Search code examples
iosiphonemacosaccelerate-frameworkvimage

Compute the histogram of an image using vImageHistogramCalculation


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.


Solution

  • 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.