【问题标题】:How to convert a UIImage object created from a CGImageRef into raw data and back again如何将从 CGImageRef 创建的 UIImage 对象转换为原始数据并再次返回
【发布时间】:2015-06-09 01:57:08
【问题描述】:

假设您从 UIImage 开始,并且希望裁剪它。最常见的方法是使用 CGImageCreateWithImageInRect 获取 CGImageRef 并从中创建新图像,如下所示:

CGRect cropRect = CGRectMake(x*width, y*height, width, height);
CGImageRef imageRef = CGImageCreateWithImageInRect([image CGImage], cropRect);
UIImage *croppedImage = [UIImage imageWithCGImage:imageRef];

现在假设您稍后需要将此 UIImage 转换为 NSData 对象。例如,如果您希望使用 NSKeyedArchiver 将图像存储在磁盘上,就会发生这种情况。或者,您可以通过显式执行以下操作来获取 NSData 对象:

NSData *imageData = UIImagePNGRepresentation(croppedImage);

我这样做了,后来我尝试通过这样做来重建一个新的 UIImage 对象:

UIImage *image = [UIImage imageWithData:imageData];

我为一系列 48 张图片执行此操作。它们中的大多数都很好,但是在每 4 张图像之后,接下来的 2 张图像被损坏,每张图像的顶部和底部都被交换了。如果我取出转换为 NSData 并再次返回,它工作正常。如果我使用 NSKeyedArchiver 将图像数组写入磁盘(我猜在其编码例程中调用 UIImagePNGRepresentation 之类的东西),我也会遇到完全相同的问题。我的理论是这一定与我如何从 CGImageRefs 构建图像有关。

解决方法是什么?我似乎无法弄清楚如何将 CGImageRefs 放入 NSData 对象中,我更喜欢正确构建 UIImage 以便它可以被序列化的解决方案。

编辑:

有些人要求实际运行的代码,所以我在这里添加它

    UIImage *image = self.animationObject.image;
for (int y=0; y<[self.animationInfoObject.numInY intValue]; y++) {
    for (int x=0; x<[self.animationInfoObject.numInX intValue]; x++) {
        CGRect cropRect = CGRectMake(x*width, y*height, width, height);
        CGImageRef imageRef = CGImageCreateWithImageInRect([image CGImage], cropRect);
        UIImage *croppedImage = [UIImage imageWithCGImage:imageRef];
        NSData *imageData = UIImageJPEGRepresentation(croppedImage, 1.0);
        [self addImageToArray:imageData];
        CFRelease(imageRef);
    }
}

self.animationObject.image 中的图像是一个图像网格,每个方向都有 numInX 和 numInY 个图像。我从 2D 网格中裁剪出每个单独的图像并保存它们。

稍后,我将图像放在图像视图中以对其进行动画处理。代码如下:

        NSMutableArray *animationArray = [[NSMutableArray alloc] init];
    for (int frame=0; frame<[[photoObject frameArray] count]; frame++) {
        NSData *imageData =[[photoObject imageArray] objectAtIndex:[[[photoObject frameArray] objectAtIndex:frame] integerValue]-1];
        UIImage *image = [UIImage imageWithData:imageData];
        [animationArray addObject:image];
    }

这一切在 iPhone 6 上都可以正常工作,但在 iPhone 5 上却不行。这似乎也只是在图像网格很长时才会出现问题。例如,对于我使用 self.animationObject.image 的示例,numInY 为 3,numInX 为 2,图像的总大小为 1536x3072(每个迷你图像为 768x1024)。顶部和底部翻转的是底部的两张图像。

【问题讨论】:

  • 一些更有用的细节。我有合成图像,每个图像在 2x3 网格中包含 6 个较小的图像。每张图像的大小为 768x1024,因此合成图像的总大小为 1536x3072。这是每个合成中的第 5 和第 6 个图像,它们的下半部分和上半部分交换了。因此,裁剪到大图像底部的 CGImage 有一些奇怪的地方,也许?再次,我应该提到,如果 UIImage 没有转换为 NSData 然后再返回,这将非常有效。
  • 所以在上面的这个例子中,宽度是 768,高度是 1024,当 x 为 0 或 1 并且 y 为 2 时它会中断。当 y 为 0 或 1 时它工作正常。这是一种情况CGImageCreateWithImageInRect 就不能处理那么大的图片?
  • 很难看出是什么原因造成的。我建议将其打包为测试应用程序并在 bugreporter.apple.com 上提交雷达。或者,如果您有 DTS 事件空闲,请由他们运行它以希望得到解决。
  • @Mike 您能否分享更多代码并分享一些测试图像,以便我们在这里进行测试?
  • 我同意@Mike,你能用代码说明你是如何在 48 张图片上循环的吗?

标签: ios objective-c uiimage nsdata cgimageref


【解决方案1】:

您的问题是由于图像方向。
每个 UIImage 都包含一个 -imageOrientation 属性,该属性定义了在渲染到屏幕后应该应用于 UIImage 的方向,当您请求 CGImage 时,您会丢失该信息。
如果您从提供的元数据字典中的相机获取图像,则还有 EXIF 方向,因此它取决于您的数据来自哪里。
在我的博客上,我写了一个 article 关于这个以及如果图像来自相机时如何正确处理它们。
一个快速的解决方案是读取 CGImage 属性并根据-imageOrientation 属性应用转换。 你可以找到不同的方法,here 和 here

此 sn-p 取自 here,归功于 awolf

- (UIImage *)croppedImageInRect:(CGRect)rect
{
    double (^rad)(double) = ^(double deg) {
        return deg / 180.0 * M_PI;
    };

    CGAffineTransform rectTransform;
    switch (self.imageOrientation) {
        case UIImageOrientationLeft:
            rectTransform = CGAffineTransformTranslate(CGAffineTransformMakeRotation(rad(90)), 0, -self.size.height);
            break;
        case UIImageOrientationRight:
            rectTransform = CGAffineTransformTranslate(CGAffineTransformMakeRotation(rad(-90)), -self.size.width, 0);
            break;
        case UIImageOrientationDown:
            rectTransform = CGAffineTransformTranslate(CGAffineTransformMakeRotation(rad(-180)), -self.size.width, -self.size.height);
            break;
        default:
            rectTransform = CGAffineTransformIdentity;
    };
    rectTransform = CGAffineTransformScale(rectTransform, self.scale, self.scale);

    CGImageRef imageRef = CGImageCreateWithImageInRect([self CGImage], CGRectApplyAffineTransform(rect, rectTransform));
    UIImage *result = [UIImage imageWithCGImage:imageRef scale:self.scale orientation:self.imageOrientation];
    CGImageRelease(imageRef);

    return result;
}

【讨论】:

  • 我不确定为什么图像方向会成为问题?我看到的所有图像都没有被旋转过。一切都在正确的方向。只是其中的一部分被交换了。我会测试你的解决方案,看看它是否有帮助。
  • 我也不确定,但在将图像保存在磁盘上时,我从来没有经历过旋转的变化,这不是由于这个原因。你说你的图像是旋转的,这是有根据的猜测。如果 UIimageView 发现与默认的 UIImageRotationUp 不同,则它会考虑旋转,因此会在屏幕上看到所有正确的内容,但 CGImage 没有旋转属性,这就是为什么您会看到一些不同方向的旋转。更简单的方法是打印每个 UIImage 的 -imageOrientationProperty,如果你看到与 UIImageOrientationUp 不同的东西,这就是问题所在。
猜你喜欢
  • 2012-10-26
  • 1970-01-01
  • 2013-11-20
  • 2013-11-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-11-13
相关资源
最近更新 更多