1

Given a CGRect (rect) and a UIImage (image), how do you obtain the alpha values (only) of the image pixels in the rectangle?

The following code always returns 0 alpha, no matter the image or rectangle.

CGContextRef context = CGBitmapContextCreate(NULL,
                                             rect.size.width,
                                             rect.size.height,
                                             8,
                                             rect.size.width,
                                             NULL,
                                             kCGImageAlphaOnly);

UIGraphicsPushContext(context);
[image drawInRect:rect];
char *pixels = CGBitmapContextGetData(context);
NSUInteger pixelCount = rect.size.width * rect.size.height;
for (int i = 0; i < pixelCount; i++)
{
    char pixel = pixels[i];
    CGFloat alpha = pixel / 255.0;
    NSLog(@"%f", alpha);
}
UIGraphicsPopContext();
CGContextRelease(context);
Jano
  • 62,815
  • 21
  • 164
  • 192
hpique
  • 119,096
  • 131
  • 338
  • 476
  • curious if you tried my recommendation below. Wondering if it solved the problem? – Khaled Barazi Feb 27 '13 at 13:55
  • @Spectravideo328 Nope. – hpique Mar 02 '13 at 19:29
  • I wonder if the colorspace matters when you are capturing a picture from the context. Have you tried creating a CGColorSpaceRef colorSpace=CGColorSpaceCreateDeviceRGB(); and assigning it to the 6th argument (replacing NULL). – Khaled Barazi Mar 02 '13 at 20:00
  • @Spectravideo328 According to this http://developer.apple.com/library/mac/#qa/qa1037/_index.html , the color space should be NULL. – hpique Mar 03 '13 at 06:28
  • Where is your image coming from and are you sure it has 1 bit only of non-zero alpha? – Khaled Barazi Mar 03 '13 at 14:46
  • @Spectravideo328 Yup. I managed to get this working using CGContextDrawImage instead of drawInRect:, as stated in one of the answers. – hpique Mar 03 '13 at 17:02

5 Answers5

2

In Swift 4 the code for getting only the alpha channel would looks like this:

import UIKit

extension UIImage {
    func alphaData() -> [UInt8]? {
        let size = self.size
        var alphaData = [UInt8](repeating: 0, count: Int(size.width) * Int(size.height))
        let colorSpace = CGColorSpaceCreateDeviceGray()
        let context = CGContext(
            data: &alphaData,
            width: Int(size.width),
            height: Int(size.height),
            bitsPerComponent: 8,
            bytesPerRow: Int(size.width),
            space: colorSpace,
            bitmapInfo: CGImageAlphaInfo.alphaOnly.rawValue
        );

        guard let cgImage = self.cgImage else { return nil }
        context?.draw(cgImage, in: CGRect(x: 0, y: 0, width: size.width, height: size.height))

        return alphaData
    }
}
Mastergalen
  • 4,289
  • 3
  • 31
  • 35
0

pixelData[index + 3] will be a value for Alpha. pixelData[0-2] are RGB respectively

CFDataRef theData;

    theData = CGDataProviderCopyData(CGImageGetDataProvider(sourceImage));

    CFMutableDataRef mutableData = CFDataCreateMutableCopy(0, 0, theData);

    UInt8 *pixelData = (UInt8 *) CFDataGetBytePtr(mutableData);

    int dataLength = CFDataGetLength(mutableData);

    for (int index = 0; index < dataLength; index += 4) {
       //code here for just alpha
       pixelData[index + 3]
    }
Eric
  • 4,063
  • 2
  • 27
  • 49
  • Doesn't `kCGImageAlphaOnly` return just one byte, as indicated here: http://developer.apple.com/library/mac/#qa/qa1037/_index.html ? – hpique Feb 22 '13 at 00:58
  • You can still manipulate the data or reset that pixel in the for statement. Ex. if I want to change the alpha from 1 to 255, multiply it by 255. – Eric Feb 22 '13 at 13:43
  • I don't understand your solution. You not using the input rectangle, and loading the whole image into memory. – hpique Mar 02 '13 at 19:29
  • my code uses an image, but I believe you can get the same RGBA sample from any view. [index + 0(0) -> 0(3)] gives you RGBA values in 0-3 respectively. – Eric Mar 04 '13 at 14:50
0

In the past, I have had issues getting CGBitmapContext working properly. I wouldn't be surprised if there was an issue with kCGImageAlphaOnly. You might do some sanity checking with some of the more 'mainstream' pixel formats like kCGImageAlphaPremultipliedFirst.

You might also try drawing the image using CGImageDrawImage:

CGContextRef context = CGBitmapContextCreate(NULL,
                                         rect.size.width,
                                         rect.size.height,
                                         8,
                                         rect.size.width,
                                         NULL,
                                         kCGImageAlphaOnly);

CGContextDrawImage( context, rect, image.CGImage);

//etc...

CGContextRelease(context);
Jon Brooks
  • 2,472
  • 24
  • 32
  • I tried with CGContextDrawImage and kCGImageAlphaOnly works as advertised. I'd say I'm using drawInRect: incorrectly. Any idea why? – hpique Mar 02 '13 at 19:41
  • No idea. Perhaps UIGraphic has an undocumented problem with kCGImageAlphaOnly pixel formats, or maybe calling UIGraphicsPopContext() before trying to harvest the data would work (just wild guesses). However, since you're doing everything else at the CG level, it seems unnecessary to jump up to the UIGraphics level to do your drawing. – Jon Brooks Mar 04 '13 at 22:44
0

In my experience with RGBA values is that they all have to be unsigned char. I am guessing this might have caused your problem since a signed char would stop at 127 and never attain the full 255 value if this image had an opacity of 1 - and possibly worse not being properly aligned (read).

Khaled Barazi
  • 8,681
  • 6
  • 42
  • 62
  • Changing to unsigned char didn't change the result. That said, it should be unsigned char anyway so I edited the original question. – hpique Mar 02 '13 at 19:27
0

I think this should help you.. https://github.com/OgreSwamp/ObjFloodFill

iphonic
  • 12,615
  • 7
  • 60
  • 107