我有一个相当大的,几乎全屏的图像,我将在iPad上显示。图像大约80%透明。我需要在客户端上确定不透明像素的边界框,然后裁剪到该边界框。
在StackOverflow上扫描其他问题并阅读一些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 :(得分: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;
}
答案 1 :(得分: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;
}
答案 2 :(得分:0)
没有聪明的作弊可以让设备完成工作,但有一些方法可以加速任务,或最大限度地减少对用户界面的影响。
首先,考虑加速这项任务的必要性。通过此字节数组的简单迭代可能足够快。如果应用程序每次运行只计算一次,或者对用户的选择做出反应,选择之间至少需要几秒钟,则可能没有必要投资优化此任务。
如果图像可用后一段时间内不需要边界框,则可以在单独的线程中启动此迭代。这样计算不会阻止主接口线程。 Grand Central Dispatch可以更轻松地使用单独的线程来完成此任务。
如果必须加速任务,也许这是视频图像的实时处理,那么数据的并行处理可能会有所帮助。 Accelerate框架可以帮助设置数据的SIMD计算。或者,为了真正获得此次迭代的性能,使用NEON SIMD操作的ARM汇编语言代码可以通过大量的开发工作获得很好的结果。
最后一个选择是研究更好的算法。在检测图像中的特征方面有大量工作要做。边缘检测算法可能比通过字节阵列的简单迭代更快。也许Apple将来会为Core Graphics添加边缘检测功能,这可以应用于这种情况。 Apple实现的图像处理功能可能不是这种情况的完全匹配,但Apple的实现应该进行优化,以使用iPad的SIMD或GPU功能,从而提高整体性能。