I have a large image A and another image B which has an alpha channel that I would like to paste into A. I want to apply an affine transform to B before I stick it on to A. What are the steps to doing this in c++ using vImage in iOS?
-
I regret there is a lot of missing detail here. Are the images CGImageRefs? vImage_Buffers? JPEGs? UIImages? How many channels are in B. Do you intend to overwrite the alpha already in A or blend it in? Is A premultiplied? It may be that the simplest answer if you are unfamiliar with low level graphics details is to prepare a CGImageMask and attach it to the CGImageRef. – Ian Ollmann Jan 25 '15 at 06:34
-
vInage_buffers, alpha exists in both images, premultiplied. – twerdster Jan 25 '15 at 12:38
2 Answers
I doubt you were able to get any of the answers posted here to work; there are just not enough specifics in them to help someone who had a question like this to begin with.
Here's your working answer:
- (CVPixelBufferRef)copyRenderedPixelBuffer:(CVPixelBufferRef)pixelBuffer {
CVPixelBufferLockBaseAddress( pixelBuffer, 0 );
unsigned char *base = (unsigned char *)CVPixelBufferGetBaseAddress( pixelBuffer );
size_t width = CVPixelBufferGetWidth( pixelBuffer );
size_t height = CVPixelBufferGetHeight( pixelBuffer );
size_t stride = CVPixelBufferGetBytesPerRow( pixelBuffer );
//size_t extendedWidth = stride / sizeof( uint32_t ); // each pixel is 4 bytes/32 bits
vImage_Buffer _img = {
.data = base,
.height = height,
.width = width,
.rowBytes = stride
};
size_t pixelBufferSize = (stride * height) / sizeof(uint8_t);
void* gBuffer = malloc(pixelBufferSize);
vImage_Buffer _dstG = {
.data = gBuffer,
.height = height / sizeof(uint8_t),
.width = width / sizeof(uint8_t),
.rowBytes = stride / sizeof(uint8_t)
};
vImage_Error err;
const uint8_t map[4] = { 3, 2, 1, 0 };
err = vImagePermuteChannels_ARGB8888(&_img, &_img, map, kvImageNoFlags);
if (err != kvImageNoError)
NSLog(@"Error: %ld", err);
err = vImageExtractChannel_ARGB8888(&_img, &_dstG, 2, kvImageNoError);
if (err != kvImageNoError)
NSLog(@"Error: %ld", err);
err = vImageEqualization_Planar8(&_dstG, &_dstG, kvImageNoError);
if (err != kvImageNoError)
NSLog(@"Error: %ld", err);
err = vImageContrastStretch_Planar8( &_dstG, &_dstG, kvImageNoError );
if (err != kvImageNoError)
NSLog(@"Error: %ld", err);
err = vImageOverwriteChannels_ARGB8888(&_dstG, &_img, &_img, 0x2, kvImageNoError);
if (err != kvImageNoError)
NSLog(@"Error: %ld", err);
err = vImagePermuteChannels_ARGB8888(&_img, &_img, map, kvImageNoFlags);
if (err != kvImageNoError)
NSLog(@"Error: %ld", err);
CVPixelBufferUnlockBaseAddress( pixelBuffer, 0 );
free(gBuffer);
return (CVPixelBufferRef)CFRetain( pixelBuffer );
}

- 1,485
- 14
- 19
Assuming 8-bits per component, 4 channel data:
- Unpremultiply A -- vImageUnpremultiplyData_ARGB8888
- extract alpha channel from B -- vImageExtractChannel_ARGB8888
- transform B -- vImageAffineWarp_Planar8
- extract alpha channel from A -- vImageExtractChannel_ARGB8888
- multiply the two alpha channels together -- vImagePremultiplyData_Planar8
- write the result into A -- vImageOverwriteChannels_ARGB8888
- premultiply A -- vImagePremultiplyData_ARGB8888
If you really wanted to replace the alpha of A with B rather than composite them together, skip steps 4 and 5.
The whole thing will run quite a bit faster if you do all 7 steps on one scanline, before moving on to the next scanline. kvImageDoNotTile and dispatch_apply() can be used to multithread it fairly simply.
On premultiplication: You may have a choice as to whether the images are premultiplied or not. The performance benefits for compositing of premultiplied images is usually overstated. It is only marginally more work to composite a non-premultiplied image into a premultiplied surface than it is to composite a premultiplied one. Premultiplication causes problems for most image filters, resulting in a bunch of work unpremultiplying and premultiplying again. It also causes some precision loss. As you can see above, if the images are not premultiplied, then the operations you want to do become much simpler. It could be a simple as just steps 2 and 6, or a single pass with vImageSelectChannels_ARGB8888 / vImagePermuteChannelsWithMaskedInsert_ARGB8888().

- 1,592
- 9
- 16