Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to combine an image with a mask into one single UIImage with Accelerate Framework?

This code combines an image and a grayscale mask image into one UIImage. It works but it is slow.

+ (UIImage*) maskImage:(UIImage *)image withMask:(UIImage *) mask
{
    CGImageRef imageReference = image.CGImage;
    CGImageRef maskReference = mask.CGImage;

    CGImageRef imageMask = CGImageMaskCreate(CGImageGetWidth(maskReference),
                                             CGImageGetHeight(maskReference),
                                             CGImageGetBitsPerComponent(maskReference),
                                             CGImageGetBitsPerPixel(maskReference),
                                             CGImageGetBytesPerRow(maskReference),
                                             CGImageGetDataProvider(maskReference),
                                             NULL, // Decode is null
                                             YES // Should interpolate
                                             );

    CGImageRef maskedReference = CGImageCreateWithMask(imageReference, imageMask);
    CGImageRelease(imageMask);

    UIImage *maskedImage = [UIImage imageWithCGImage:maskedReference];
    CGImageRelease(maskedReference);

    return maskedImage;
}

I think that Accelerate Framework can help. But I am not sure. There is vImage and it can do alpha compositing. Or maybe what I look for is called "vImage Transform". Not like CATransform3D but "transforming" image.

But what I need is make a photo into a transparent JPEG based on a mask.

Can Accelerate Framework be used for this? Or is there an alternative?

like image 776
iamjustaprogrammer Avatar asked Oct 20 '22 14:10

iamjustaprogrammer


1 Answers

VImageOverwriteChannels_ARGB8888 is probably the API you want, provided that the image JPEG is opaque to start with. You can use vImageBuffer_InitWithCGImage to extract out the source image as 8 bpc, 32 bpp, kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Little. This will give you a BGRA8888 image with opaque alpha. Get out the mask as a 8bpc, 8bpp, kCGImageAlphaNone image. Use vImageOverwriteChannels_ARGB8888 to overwrite the BGRA alpha with the new alpha channel. Then make a new CGImage with vImageCreateCGImageFromBuffer, modifying the format slightly to kCGImageAlphaFirst | kCGBitmapByteOrder32Little.

You can also try flattening the mask into the image by taking maskedReference above and decoding it directly to BGRA kCGImageAlphaFirst. This only really works well if the image and the mask are the same size. Otherwise some resampling occurs, which is time consuming.

I don't know whether either of these is really going to be faster or not. It would be useful to look at an instruments time profile of where your time is going. vImageOverwriteChannels_ARGB8888 is probably only a tiny bit of the work to be done here. Depending on the format of the original image, quite a lot of work for colorspace conversion and image format conversion can occur behind the scenes in vImageBuffer_InitWithCGImage and vImageCreateCGImageFromBuffer. The key to speed here (and with the competing CG path) is to minimize the workload by making intelligent choices.

Sometimes, trying some stuff, then filing a bug against apple if nothing works well can yield an informed response. A trivially reproducible example is usually key.

like image 50
Ian Ollmann Avatar answered Nov 15 '22 03:11

Ian Ollmann