alecail
alecail

Reputation: 4062

How to compile vImage emboss effect sample code?

Here is the code found in the documentation:

int myEmboss(void *inData,
unsigned int inRowBytes,
void *outData,
unsigned int outRowBytes,
unsigned int height,
unsigned int width,
void *kernel,
unsigned int kernel_height,
unsigned int kernel_width,
int divisor ,
vImage_Flags flags ) {
   uint_8 kernel = {-2, -2, 0, -2, 6, 0, 0, 0, 0}; // 1
   vImage_Buffer src = { inData, height, width, inRowBytes }; // 2
   vImage_Buffer dest = { outData, height, width, outRowBytes }; // 3
   unsigned char bgColor[4] = { 0, 0, 0, 0 }; // 4
   vImage_Error err; // 5
   err = vImageConvolve_ARGB8888(    &src,     //const vImage_Buffer *src
                                     &dest,    //const vImage_Buffer *dest,
                                      NULL,
                                      0,    //unsigned int srcOffsetToROI_X,
                                      0,    //unsigned int srcOffsetToROI_Y,
                                      kernel,    //const signed int *kernel,
                                      kernel_height,     //unsigned int
                                      kernel_width,    //unsigned int
                                      divisor,    //int
                                      bgColor,
                                      flags | kvImageBackgroundColorFill
                                      //vImage_Flags flags
                                    );


   return err;
}

Here is the problem: the kernel variable seems to refer to three different types:

  1. void * kernel in the formal parameter list
  2. an undefined unsigned int uint_8 kernel, as a new variable which presumably would shadow the formal parameter
  3. a const signed int *kernel when calling vImageConvolve_ARGB8888.

Is this actual code ? How may I compile this function ?

Upvotes: 1

Views: 710

Answers (3)

Sten
Sten

Reputation: 3864

This is the way I am using it to process frames read from a video with AVAssetReader. This is a blur, but you can change the kernel to suit your needs. 'imageData' can of course be obtained by other means, e.g. from an UIImage.

     CMSampleBufferRef sampleBuffer = [asset_reader_output copyNextSampleBuffer];
     CVImageBufferRef imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer);            
     CVPixelBufferLockBaseAddress(imageBuffer,0);
     void *imageData = CVPixelBufferGetBaseAddress(imageBuffer);        

     int16_t kernel[9];
     for(int i = 0; i < 9; i++) {
        kernel[i] = 1;
     }
     kernel[4] = 2;

     unsigned char *newData= (unsigned char*)malloc(4*currSize);

     vImage_Buffer  inBuff = { imageData, height, width, 4*width };
     vImage_Buffer  outBuff = { newData, height, width, 4*width };

     vImage_Error err=vImageConvolve_ARGB8888 (&inBuff,&outBuff,NULL, 0,0,kernel,3,3,10,nil,kvImageEdgeExtend);
     if (err != kvImageNoError) NSLog(@"convolve error %ld", err);
     CVPixelBufferUnlockBaseAddress(imageBuffer, 0);

     //newData holds the processed image

Upvotes: 2

Ken Thomases
Ken Thomases

Reputation: 90671

You are correct that that function is pretty messed up. I recommend using the Provide Feedback widget to let Apple know.

I think you should remove the kernel, kernel_width, and kernel_height parameters from the function signature. Those seem to be holdovers from a function that applies a caller-supplied kernel, but this example is about applying an internally-defined kernel.

Fixed the declaration of the kernel local variable to make it an array of uint8_t, like so:

    uint8_t kernel[] = {-2, -2, 0, -2, 6, 0, 0, 0, 0}; // 1

Then, at the call to vImageConvolve_ARGB8888(), replace kernel_width and kernel_height by 3. Since the kernel is hard-coded, the dimensions can be as well.

Upvotes: 2

Jack
Jack

Reputation: 16865

The kernel is just the kernel used in the convolution. In mathematical terms, it is the matrix that is convolved with your image, to achieve blur/sharpen/emboss or other effects. This function you provided is just a thin wrapper around the vimage convolution function. To actually perform the convolution you can follow the code below. The code is all hand typed so not necessarily 100% correct but should point you in the right direction.

To use this function, you first need to have pixel access to your image. Assuming you have a UIImage, you do this:

//image is a UIImage
CGImageRef img = image.CGImage;
CGDataProviderRef dataProvider = CGImageGetDataProvider(img); 
CFDataRef cfData = CGDataProviderCopyData(dataProvider);
void * dataPtr = (void*)CFDataGetBytePtr(cfData);

Next, you construct the vImage_Buffer that you will pass to the function

vImage_Buffer inBuffer, outBuffer;
inBuffer.data = dataPtr;
inBuffer.width = CGImageGetWidth(img);
inBuffer.height = CGImageGetHeight(img);
inBuffer.rowBytes = CGImageGetBytesPerRow(img);

Allocate the outBuffer as well

outBuffer.data = malloc(inBuffer.height * inBuffer.rowBytes)
// Setup width, height, rowbytes equal to inBuffer here

Now we create the Kernel, the same one in your example, which is a 3x3 matrix Multiply the values by a divisor if they are float (they need to be int)

int divisor = 1000;
CGSize kernalSize = CGSizeMake(3,3);
int16_t *kernel = (int16_t*)malloc(sizeof(int16_t) * 3 * 3);
// Assign kernel values to the emboss kernel
// uint_8 kernel = {-2, -2, 0, -2, 6, 0, 0, 0, 0} // * 1000 ;

Now perform the convolution on the image!

//Use a background of transparent black as temp
Pixel_8888 temp = 0;
vImageConvolve_ARGB8888(&inBuffer, &outBuffer, NULL, 0, 0, kernel, kernelSize.width, kernelSize.height, divisor, temp, kvImageBackgroundColorFill);

Now construct a new UIImage out of outBuffer and your done!

Remember to free the kernel and the outBuffer data.

Upvotes: 2

Related Questions