如何将从CGImageRef创建的UIImage对象转换为原始数据并再次返回

Mik*_*ike 5 objective-c uiimage nsdata ios cgimageref

假设你从一个UIImage开始,你想要裁剪它.最常见的方法是使用CGImageCreateWithImageInRect来获取CGImageRef并从中创建一个新图像,如下所示:

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

现在让我们说你以后需要将这个UIImage转换为NSData对象.例如,如果您希望使用NSKeyedArchiver将映像存储在磁盘上,则会发生这种情况.或者,您可以通过以下显式执行以下操作来获取NSData对象:

NSData *imageData = UIImagePNGRepresentation(croppedImage);
Run Code Online (Sandbox Code Playgroud)

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

UIImage *image = [UIImage imageWithData:imageData];
Run Code Online (Sandbox Code Playgroud)

我为一系列48张图片做了这个.他们中的大多数都很好,但是在每4张图片之后,接下来的两张图片都被破坏了,每张图片的顶部和底部都被交换了.如果我将转换转换为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);
    }
}
Run Code Online (Sandbox Code Playgroud)

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];
    }
Run Code Online (Sandbox Code Playgroud)

这一切在iPhone 6上运行良好,但在iPhone 5上不起作用.当图像网格很长时,它似乎也只是一个问题.例如,对于我使用self.animationObject.image的示例,numInY为3和numInX 2,图像的总大小为1536x3072(每个迷你图像为768x1024).底部两个图像的顶部和底部翻转.

And*_*rea 0

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

此片段摘自此处,归功于 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;
}
Run Code Online (Sandbox Code Playgroud)