Compute the histogram of an image using vImageHistogramCalculation

1.6k Views Asked by At

I'm trying to compute the histogram of an image using vImage's vImageHistogramCalculation_ARGBFFFF, but I'm getting a vImage_Error of type kvImageNullPointerArgument (error code a -21772).

Here's my code:

- (void)histogramForImage:(UIImage *)image {

    //setup inBuffer
    vImage_Buffer inBuffer;

    //Get CGImage from UIImage
    CGImageRef img = image.CGImage;

    //create vImage_Buffer with data from CGImageRef
    CGDataProviderRef inProvider = CGImageGetDataProvider(img);
    CFDataRef inBitmapData = CGDataProviderCopyData(inProvider);

    //The next three lines set up the inBuffer object
    inBuffer.width = CGImageGetWidth(img);
    inBuffer.height = CGImageGetHeight(img);
    inBuffer.rowBytes = CGImageGetBytesPerRow(img);

    //This sets the pointer to the data for the inBuffer object
    inBuffer.data = (void*)CFDataGetBytePtr(inBitmapData);

    //Prepare the parameters to pass to vImageHistogramCalculation_ARGBFFFF
    vImagePixelCount *histogram[4] = {0};
    unsigned int histogram_entries = 4;
    Pixel_F minVal = 0;
    Pixel_F maxVal = 255;
    vImage_Flags flags = kvImageNoFlags;

    vImage_Error error = vImageHistogramCalculation_ARGBFFFF(&inBuffer,
                                                             histogram,
                                                             histogram_entries,
                                                             minVal,
                                                             maxVal,
                                                             flags);
    if (error) {
        NSLog(@"error %ld", error);
    }

    //clean up
    CGDataProviderRelease(inProvider);
}

I suspect it has something to do with my histogram parameter, which, according to the docs, is supposed to be "a pointer to an array of four histograms". Am I declaring it correctly?

Thanks.

1

There are 1 best solutions below

6
On BEST ANSWER

The trouble is that you’re not allocating space to hold the computed histograms. If you are only using the histograms locally, you can put them on the stack like so [note that I’m using eight bins instead of four, to make the example more clear]:

// create an array of four histograms with eight entries each.
vImagePixelCount histogram[4][8] = {{0}};
// vImageHistogramCalculation requires an array of pointers to the histograms.
vImagePixelCount *histogramPointers[4] = { &histogram[0][0], &histogram[1][0], &histogram[2][0], &histogram[3][0] };
vImage_Error error = vImageHistogramCalculation_ARGBFFFF(&inBuffer, histogramPointers, 8, 0, 255, kvImageNoFlags);
// You can now access bin j of the histogram for channel i as histogram[i][j].
// The storage for the histogram will be cleaned up when execution leaves the
// current lexical block.

If you need the histograms to stick around outside the scope of your function, you’ll need to allocate space for them on the heap instead:

vImagePixelCount *histogram[4];
unsigned int histogramEntries = 8;
histogram[0] = malloc(4*histogramEntries*sizeof histogram[0][0]);
if (!histogram[0]) { // handle error however is appropriate }
for (int i=1; i<4; ++i) { histogram[i] = &histogram[0][i*histogramEntries]; }
vImage_Error error = vImageHistogramCalculation_ARGBFFFF(&inBuffer, histogram, 8, 0, 255, kvImageNoFlags);
// You can now access bin j of the histogram for channel i as histogram[i][j].
// Eventually you will need to free(histogram[0]) to release the storage.

Hope this helps.