2011-06-29 117 views
9

我有一个相当大的几乎全屏的图像,我将在iPad上显示。图像大约80%透明。我需要在客户端上确定不透明像素的边界框,然后裁剪到该边界框。裁剪UIImage到alpha

在计算器上扫描其他的问题在这里和阅读一些CoreGraphics中的文档,我想我能做到这一点:

CGBitmapContextCreate(...) // Use this to render the image to a byte array 

.. 
    - iterate through this byte array to find the bounding box 
.. 

CGImageCreateWithImageInRect(image, boundingRect); 

这只是似乎非常低效和笨重。有没有什么聪明的我可以用CGImage的面具或使用设备的图形加速来做到这一点?

+0

你在设备上试过这个吗?我敢打赌它会比你想象的要快。 –

+0

这是真的 - 一旦我真正坐下来实施它,处理时间比我想象的要快得多! – MikeQ

回答

0

有没有聪明的骗子让设备做的工作,但有一些方法可以加快任务,或最大限度地减少对用户界面的影响。

首先,考虑是否需要加速这项任务。通过这个字节数组的简单迭代可能足够快。如果应用程序每次运行只计算一次,或者对用户的选择作出反应(选择之间至少需要几秒钟),则可能无需投资优化此任务。

如果在图像可用后一段时间内不需要边界框,则可以在单独的线程中启动该迭代。这样计算不会阻塞主界面线程。 Grand Central Dispatch可能更容易使用单独的线程执行此任务。

如果任务必须加速,也许这是视频图像的实时处理,那么并行处理数据可能会有所帮助。 Accelerate框架可能有助于设置数据的SIMD计算。或者,为了真正获得这次迭代的性能,使用NEON SIMD操作的ARM汇编语言代码可以通过大量的开发工作获得很好的结果。

最后的选择是调查一个更好的算法。在检测图像中的特征方面有大量工作。边缘检测算法可能比通过字节数组的简单迭代更快。也许Apple会在未来为Core Graphics增加边缘检测功能,这可以应用于这种情况。苹果公司实施的图像处理能力可能并不完全符合这种情况,但苹果的实施应该优化使用iPad的SIMD或GPU功能,从而提高整体性能。

8

我创建了执行此,如果任何一个需要它的uImage类别...

+ (UIImage *)cropTransparencyFromImage:(UIImage *)img { 

    CGImageRef inImage = img.CGImage;   
    CFDataRef m_DataRef; 
    m_DataRef = CGDataProviderCopyData(CGImageGetDataProvider(inImage)); 
    UInt8 * m_PixelBuf = (UInt8 *) CFDataGetBytePtr(m_DataRef); 

    int width = img.size.width; 
    int height = img.size.height; 

    CGPoint top,left,right,bottom; 

    BOOL breakOut = NO; 
    for (int x = 0;breakOut==NO && x < width; x++) { 
     for (int y = 0; y < height; y++) { 
      int loc = x + (y * width); 
      loc *= 4; 
      if (m_PixelBuf[loc + 3] != 0) { 
       left = CGPointMake(x, y); 
       breakOut = YES; 
       break; 
      } 
     } 
    } 

    breakOut = NO; 
    for (int y = 0;breakOut==NO && y < height; y++) { 

     for (int x = 0; x < width; x++) { 

      int loc = x + (y * width); 
      loc *= 4; 
      if (m_PixelBuf[loc + 3] != 0) { 
       top = CGPointMake(x, y); 
       breakOut = YES; 
       break; 
      } 

     } 
    } 

    breakOut = NO; 
    for (int y = height-1;breakOut==NO && y >= 0; y--) { 

     for (int x = width-1; x >= 0; x--) { 

      int loc = x + (y * width); 
      loc *= 4; 
      if (m_PixelBuf[loc + 3] != 0) { 
       bottom = CGPointMake(x, y); 
       breakOut = YES; 
       break; 
      } 

     } 
    } 

    breakOut = NO; 
    for (int x = width-1;breakOut==NO && x >= 0; x--) { 

     for (int y = height-1; y >= 0; y--) { 

      int loc = x + (y * width); 
      loc *= 4; 
      if (m_PixelBuf[loc + 3] != 0) { 
       right = CGPointMake(x, y); 
       breakOut = YES; 
       break; 
      } 

     } 
    } 


    CGRect cropRect = CGRectMake(left.x, top.y, right.x - left.x, bottom.y - top.y); 

    UIGraphicsBeginImageContextWithOptions(cropRect.size, 
              NO, 
              0.); 
    [img drawAtPoint:CGPointMake(-cropRect.origin.x, -cropRect.origin.y) 
      blendMode:kCGBlendModeCopy 
       alpha:1.]; 
    UIImage *croppedImage = UIGraphicsGetImageFromCurrentImageContext(); 
    UIGraphicsEndImageContext(); 
    return croppedImage; 
} 
+1

感谢这个脚本。我在我的项目中使用它,但我注意到内存泄漏问题。我想我们需要调用“CFRelease(m_DataRef);”之前“return croppedImage;” –

+0

感谢您的有用答复user404。你是否知道在你获得了对他们进行变换的点之后有没有办法? –

+0

只是在别人正在观看的情况下重复我的评论:我刚刚在我的程序中实现了这一点 - 这正是我需要的!但我得到和第一个if(m_PixelBuf [loc + 3]!= 0){行EXC_BAD_ACCESS {行。任何想法为什么?谢谢 – Smikey

13

感谢user404709制作所有的辛勤工作。 下面的代码可以处理视网膜图像并释放CFDataRef。

- (UIImage *)trimmedImage { 

    CGImageRef inImage = self.CGImage; 
    CFDataRef m_DataRef; 
    m_DataRef = CGDataProviderCopyData(CGImageGetDataProvider(inImage)); 

    UInt8 * m_PixelBuf = (UInt8 *) CFDataGetBytePtr(m_DataRef); 

    size_t width = CGImageGetWidth(inImage); 
    size_t height = CGImageGetHeight(inImage); 

    CGPoint top,left,right,bottom; 

    BOOL breakOut = NO; 
    for (int x = 0;breakOut==NO && x < width; x++) { 
     for (int y = 0; y < height; y++) { 
      int loc = x + (y * width); 
      loc *= 4; 
      if (m_PixelBuf[loc + 3] != 0) { 
       left = CGPointMake(x, y); 
       breakOut = YES; 
       break; 
      } 
     } 
    } 

    breakOut = NO; 
    for (int y = 0;breakOut==NO && y < height; y++) { 

     for (int x = 0; x < width; x++) { 

      int loc = x + (y * width); 
      loc *= 4; 
      if (m_PixelBuf[loc + 3] != 0) { 
       top = CGPointMake(x, y); 
       breakOut = YES; 
       break; 
      } 

     } 
    } 

    breakOut = NO; 
    for (int y = height-1;breakOut==NO && y >= 0; y--) { 

     for (int x = width-1; x >= 0; x--) { 

      int loc = x + (y * width); 
      loc *= 4; 
      if (m_PixelBuf[loc + 3] != 0) { 
       bottom = CGPointMake(x, y); 
       breakOut = YES; 
       break; 
      } 

     } 
    } 

    breakOut = NO; 
    for (int x = width-1;breakOut==NO && x >= 0; x--) { 

     for (int y = height-1; y >= 0; y--) { 

      int loc = x + (y * width); 
      loc *= 4; 
      if (m_PixelBuf[loc + 3] != 0) { 
       right = CGPointMake(x, y); 
       breakOut = YES; 
       break; 
      } 

     } 
    } 


    CGFloat scale = self.scale; 

    CGRect cropRect = CGRectMake(left.x/scale, top.y/scale, (right.x - left.x)/scale, (bottom.y - top.y)/scale); 
    UIGraphicsBeginImageContextWithOptions(cropRect.size, 
              NO, 
              scale); 
    [self drawAtPoint:CGPointMake(-cropRect.origin.x, -cropRect.origin.y) 
      blendMode:kCGBlendModeCopy 
       alpha:1.]; 
    UIImage *croppedImage = UIGraphicsGetImageFromCurrentImageContext(); 
    UIGraphicsEndImageContext(); 
    CFRelease(m_DataRef); 
    return croppedImage; 
} 
+0

我刚刚在我的程序中实现了这一点 - 这正是我需要的!但是,如果(m_PixelBuf [loc + 3]!= 0),我会在第一个 上得到EXC_BAD_ACCESS。任何想法为什么?谢谢。 – Smikey

+0

@Smikey,正试图在从相机捕获的图像上使用它?如果是这样,根据user1702121的[http://stackoverflow.com/a/12614015/1541893],“从捕获的示例缓冲区创建的上下文的''CGBitmapContextGetData()'不会返回它的位图”。他提供了一个解决方案。 – Dan