4

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.

Stephen Canon
  • 103,815
  • 19
  • 183
  • 269
Eric
  • 16,003
  • 15
  • 87
  • 139
  • The problem is here: `vImagePixelCount *histogram[4] = {0};` You're initializing an array of four null pointers to contain the histogram results. I'll provide more detail as an answer later if no one else gets to it first. – Stephen Canon Sep 02 '14 at 22:30
  • Hey @StephenCanon, looks like no else got to it apart from you. More detail on your comment above would be great! – Eric Sep 03 '14 at 20:50
  • @Eric... I tried Stephen logic and getting EXC_BAD_Access in both the cases.Can you suggest me what could have gone wrong. – Imran Oct 09 '15 at 11:50

1 Answers1

5

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.

Stephen Canon
  • 103,815
  • 19
  • 183
  • 269
  • Hey Stephen, thanks for your answer. However, I'm getting a few problems with your first bit of code. I'm getting a warning on the line where I declare `histogramPointers `, which says: `Incompatible pointer types initializing 'vImagePixelCount *' (aka 'unsigned long *') with an expression of type 'vImagePixelCount (*)[8]', and the app crashes on the call to `vImageHistogramCalculation_ARGBFFFF`, with an EXC_BAD_ACCESS. Any thoughts..? – Eric Sep 09 '14 at 15:59
  • 1
    Sorry, it seems that I posted an early version of the code on accident; try again now. – Stephen Canon Sep 09 '14 at 16:18
  • Thanks, that runs fine now. I'm a bit confused by the result though. All four channels are like this: `([0] = 727040, [1] = 0, [2] = 0, [3] = 0, [4] = 0, [5] = 0, [6] = 0, [7] = 0)`... Any thoughts? – Eric Sep 09 '14 at 16:53
  • 1
    Given that the image format is float, most likely the pixel values are normalized to [0,1], so they are all falling into the first bin (which covers [0,31.875) when min=0 and max=255). You probably want to use min=0.0, max=1.0 instead. – Stephen Canon Sep 09 '14 at 16:56
  • histogramEntries is usually a larger number like 256. It does not indicate the number of histograms, but the number of entries in each histogram. Also, does your image data have anything in it, or is it initialized to all zeros? – Ian Ollmann Sep 09 '14 at 20:58
  • @StephenCanon... I tried both the above code but EXC_BAD_Access...Can you tell what could be wrong. – Imran Oct 09 '15 at 11:26