Question

How can i "extract" only one channel from an image using vImage? My approach right now is:

vImage_Buffer redBuffer;
redBuffer.data = (void*)redImageData.bytes;
redBuffer.width = size.width;
redBuffer.height = size.height;
redBuffer.rowBytes = [redImageData length]/size.height;
vImage_Buffer redBuffer2;
redBuffer2.width = size.width;
redBuffer2.height = size.height;
redBuffer2.rowBytes = size.width;
vImageConvert_RGB888toPlanar8(&redBuffer, &redBuffer2, nil, nil, kvImageNoFlags);

redBuffer is a working image, no errors with it, but vImageConvert gives me an EXC_BAD_ACCESS, i've tried many options and some lead to an EXC_BAD_ACCESS and some to a broken output image. What am i doing wrong?

So thanks to Rob Keniger i've managed to extract the channels, but if i try to put them back together with other channels, the images get stretched horizontally and have RGB stripes in it. Why aren't they put back together correctly? Here an example with extracting the channels and then putting them back together:

    vImage_Buffer blueBuffer;
    blueBuffer.data = (void*)blueImageData.bytes;
    blueBuffer.width = size.width;
    blueBuffer.height = size.height;
    blueBuffer.rowBytes = [blueImageData length]/size.height;

    vImage_Buffer rBuffer;
    rBuffer.width = size.width;
    rBuffer.height = size.height;
    rBuffer.rowBytes = size.width;
    void *rPixelBuffer = malloc(size.width * size.height);
    if(rPixelBuffer == NULL)
    {
        NSLog(@"No pixelbuffer");
    }
    rBuffer.data = rPixelBuffer;

    vImage_Buffer gBuffer;
    gBuffer.width = size.width;
    gBuffer.height = size.height;
    gBuffer.rowBytes = size.width;
    void *gPixelBuffer = malloc(size.width * size.height);
    if(gPixelBuffer == NULL)
    {
        NSLog(@"No pixelbuffer");
    }
    gBuffer.data = gPixelBuffer;

    vImage_Buffer bBuffer;
    bBuffer.width = size.width;
    bBuffer.height = size.height;
    bBuffer.rowBytes = size.width;
    void *bPixelBuffer = malloc(size.width * size.height);
    if(bPixelBuffer == NULL)
    {
        NSLog(@"No pixelbuffer");
    }
    bBuffer.data = bPixelBuffer;

    vImageConvert_RGB888toPlanar8(&blueBuffer, &rBuffer, &gBuffer, &bBuffer, kvImageNoFlags);

    size_t destinationImageBytesLength = size.width*size.height*3;
    const void* destinationImageBytes = valloc(destinationImageBytesLength);
    NSData* destinationImageData = [[NSData alloc] initWithBytes:destinationImageBytes length:destinationImageBytesLength];
    vImage_Buffer destinationBuffer;
    destinationBuffer.data = (void*)destinationImageData.bytes;
    destinationBuffer.width = size.width;
    destinationBuffer.height = size.height;
    destinationBuffer.rowBytes = [destinationImageData length]/size.height;

    vImage_Error result = vImageConvert_Planar8toRGB888(&rBuffer, &gBuffer, &bBuffer, &destinationBuffer, 0);
    NSImage* image = nil;
    if(result == kvImageNoError)
    {
        //TODO: If you need color matching, use an appropriate colorspace here
        CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
        CGDataProviderRef dataProvider = CGDataProviderCreateWithCFData((__bridge CFDataRef)(destinationImageData));
        CGImageRef finalImageRef = CGImageCreate(size.width, size.height, 8, 24, destinationBuffer.rowBytes, colorSpace, kCGBitmapByteOrder32Big|kCGImageAlphaNone, dataProvider, NULL, NO, kCGRenderingIntentDefault);
        CGColorSpaceRelease(colorSpace);
        CGDataProviderRelease(dataProvider);
        image = [[NSImage alloc] initWithCGImage:finalImageRef size:NSMakeSize(size.width, size.height)];
        CGImageRelease(finalImageRef);
    }
    free((void*)destinationImageBytes);
    return image;
Était-ce utile?

La solution 2

You're not initialising the redBuffer2 output buffer, so you don't have anywhere to store the output of the function.

//allocate a buffer for the output image and check it exists
void *pixelBuffer = malloc(size.width * size.height);
if(pixelBuffer == NULL)
{
    NSLog(@"No pixelbuffer"); 
    //handle this somehow
}

//assign the allocated buffer to the vImage buffer struct
redBuffer2.data = pixelBuffer;

Autres conseils

A solution (or, at least the beginnings of one):

err = vImageExtractChannel_ARGB8888(&_src, &_blu, 2, kvImageNoError);
...
uint8_t copyMask = 1;
vImageOverwriteChannels_ARGB8888(&_blu, &_src, &_dst, copyMask, kvImageNoFlags);

This worked for me, except in the OpenCV version of my in-development iOS camera app; the preview window displays a white stripe on one side, which it does everytime OpenCV doesn't like the way I do something. I don't think that will be your issue, and seeing as it is only three lines of code to your multiplicity of lines, it's worth trying.

vImageConvert_RGB888toPlanar8 does not accept NULL pointers for the vImage_Buffer structs. The function is tagged with VIMAGE_NON_NULL(1,2,3,4) which should be causing your compiler to complain about this usage.

On OS X.10 or iOS 8 vImageExtractChannel_ARGB8888 was added, but there is no RGB888 equivalent. If you need that, you should file a feature request. http://bugreporter.apple.com In the mean time, you can make three planar8 output buffers and throw two away.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top