在iOS上找到UIImage的非透明像素的最快,最有效的方法

时间:2016-01-01 15:36:51

标签: ios image-processing opengl-es alpha-transparency

我想问一下图像处理机制。我开发了一个iOS应用程序,它使用OpenGLES在视图上手写。我有一个函数保存,将所有绘图的视图转换为图像并保存到照片库。

我可以使用下面的代码轻松地将视图内容轻松转换为图像 (注意:下面的代码不是问题。它的目的只是将视图内容转换为图像并且它工作得很完美,但我在此处显示以供参考)

// Get the size of the backing CAEAGLLayer

    NSInteger x = 0, y = 0, width = backingWidth, height = backingHeight;
    NSInteger dataLength = width * height * 4;
    GLubyte *data = (GLubyte*)malloc(dataLength * sizeof(GLubyte));

    // Read pixel data from the framebuffer
    glPixelStorei(GL_PACK_ALIGNMENT, 4);
    glReadPixels(x, y, width, height, GL_RGBA, GL_UNSIGNED_BYTE, data);

    // Create a CGImage with the pixel data
    // If your OpenGL ES content is opaque, use kCGImageAlphaNoneSkipLast to ignore the alpha channel
    // otherwise, use kCGImageAlphaPremultipliedLast
    CGDataProviderRef ref = CGDataProviderCreateWithData(NULL, data, dataLength, NULL);
    CGColorSpaceRef colorspace = CGColorSpaceCreateDeviceRGB();
    CGImageRef iref = CGImageCreate(width, height, 8, 32, width * 4, colorspace, kCGBitmapByteOrder32Big | kCGImageAlphaPremultipliedLast,
                                    ref, NULL, true, kCGRenderingIntentDefault);

    // OpenGL ES measures data in PIXELS
    // Create a graphics context with the target size measured in POINTS
    NSInteger widthInPoints, heightInPoints;
    if (NULL != &UIGraphicsBeginImageContextWithOptions) {
        // On iOS 4 and later, use UIGraphicsBeginImageContextWithOptions to take the scale into consideration
        // Set the scale parameter to your OpenGL ES view's contentScaleFactor
        // so that you get a high-resolution snapshot when its value is greater than 1.0
        CGFloat scale = self.contentScaleFactor;
        widthInPoints = width / scale;
        heightInPoints = height / scale;
        UIGraphicsBeginImageContextWithOptions(CGSizeMake(widthInPoints, heightInPoints), NO, scale);
    } else {
        // On iOS prior to 4, fall back to use UIGraphicsBeginImageContext

    }

    CGContextRef cgcontext = UIGraphicsGetCurrentContext();

    // UIKit coordinate system is upside down to GL/Quartz coordinate system
    // Flip the CGImage by rendering it to the flipped bitmap context
    // The size of the destination area is measured in POINTS
    CGContextSetBlendMode(cgcontext, kCGBlendModeCopy);
    CGContextDrawImage(cgcontext, CGRectMake(0.0, 0.0, widthInPoints, heightInPoints), iref);

    // Retrieve the UIImage from the current context
    UIImage *image = UIGraphicsGetImageFromCurrentImageContext();

    UIGraphicsEndImageContext();

    // Clean up
    free(data);
    CFRelease(ref);
    CFRelease(colorspace);
    CGImageRelease(iref);

    return image;

问题是我想确定视图是否有任何绘图。如果没有绘图 - >无法保存,因为保存空白图像是没用的,所以我的想法是检查图像是否有任何不透明的像素

我的解决方案

  • 将我的绘图视图转换为图像(其像素具有Alpha通道)

  • 检查图像是否有任何非零alpha通道像素

    • 如果是,用户正确绘制了一些内容 - >可以保存
    • 如果不是,用户不会绘制任何内容或用户删除所有内容 - >不保存

我知道BruteForce算法可以遍历所有像素,但它似乎是最糟糕的方式,只有在没有其他有效方法的情况下才能实现

有没有有效的方法来检查它

1 个答案:

答案 0 :(得分:0)

我发现BruteForce算法并不像我那样慢。只需不到200毫秒的时间就可以查看具有iPad Pro和iPad mini 2尺寸的图像的所有像素数据

所以我虽然使用BruteForce是可以接受的

以下是检查的代码

    CGImageRef imageRef = [selfImage CGImage];
    NSUInteger width = CGImageGetWidth(imageRef);
    NSUInteger height = CGImageGetHeight(imageRef);

    float total = width * height * 4;

    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
    unsigned char *rawData = (unsigned char*) calloc(total, sizeof(unsigned char));
    NSUInteger bytesPerPixel = 4;
    NSUInteger bytesPerRow = bytesPerPixel * width;
    NSUInteger bitsPerComponent = 8;
    CGContextRef tempContext = CGBitmapContextCreate(rawData, width, height,
                                                     bitsPerComponent, bytesPerRow, colorSpace,
                                                     kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big);
    CGColorSpaceRelease(colorSpace);

    CGContextDrawImage(tempContext, CGRectMake(0, 0, width, height), imageRef);
    CGContextRelease(tempContext);

    // Now your rawData contains the image data in the RGBA8888 pixel format
    BOOL empty = YES;
    for (int i = 0 ; i < total ;) {
        CGFloat alpha = ((CGFloat) rawData[i + 3] ) / 255.0f;
        //        CGFloat red   = ((CGFloat) rawData[i]     ) / alpha;
        //        CGFloat green = ((CGFloat) rawData[i + 1] ) / alpha;
        //        CGFloat blue  = ((CGFloat) rawData[i + 2] ) / alpha;
        i += bytesPerPixel;

        if (alpha != 0) {
            empty = NO;
            break;
        }
    }

    if (empty) {
        //Do something
    } else {
        //Do other thing
    }

如果有任何改进或其他有效的算法,请在这里发帖,我真的很感激