如何制作一个与从UIImagePickerController返回的UIImage完全相同的副本?

8
我对从iPhone相机返回的图像有不同的需求。 我的应用程序将图像缩小以便上传和显示,最近,我添加了将图像保存到“照片”应用程序的功能。
起初,我将返回值分配给了两个变量,但结果发现它们正在共享同一个对象,因此我得到了两张缩小的图像,而不是一张完整比例的图像。
在弄清楚无法执行` UIImage *copyImage = [myImage copy]; `后,我通过下面的方式使用` imageWithCGImage `进行了复制。不幸的是,这并不奏效,因为复制品(此处为` croppedImage `)最终会与原始图像旋转90度。
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info
{   
    // Resize, crop, and correct orientation issues
    self.originalImage = [info valueForKey:@"UIImagePickerControllerOriginalImage"];
    UIImageWriteToSavedPhotosAlbum(originalImage, nil, nil, nil);
    UIImage *smallImage = [UIImage imageWithCGImage:[originalImage CGImage]]; // UIImage doesn't conform to NSCopy

    // This method is from a category on UIImage based on this discussion:
    // http://discussions.apple.com/message.jspa?messageID=7276709
    // It doesn't rotate smallImage, though: while imageWithCGImage returns
    // a rotated CGImage, the UIImageOrientation remains at UIImageOrientationUp!
    UIImage *fixedImage = [smallImage scaleAndRotateImageFromImagePickerWithLongestSide:480];
    ...
}

有没有一种方法可以在不修改UIImagePickerControllerOriginalImage图像的情况下复制它?

2
你最终解决了这个问题吗?如果是的话,能否编辑问题并附上解决方案?谢谢! - pws5068
抱歉,我已经有一段时间没有机会处理那段代码了。一旦我回到那个项目中,我一定会更新它...这应该是在今年的某个时候。 - clozach
UIImage是不可变的。因此,两个指针指向同一个对象或不同的对象都无关紧要。 - user102008
是的。如果时间戳不明显,我从未回到那个项目,并且据我所知,它已被原始所有者搁置,因此我不太可能选择获胜答案,因为我无法测试更新的响应。 - clozach
只是提供信息:我遇到了类似的问题,并尝试了建议在另一个线程中的深度复制,它满足了我的需求。下面的建议答案对我没有用,因为两个副本仍然被修改,就好像它们共享相同的数据一样(我使用GLKTextureLoader,有时使用底部左侧原点等选项修改UIImage中的数据)。 - HuaTham
5个回答

17

这似乎可以工作,但根据您对newImage的使用方式,您可能会遇到一些内存问题:

CGImageRef newCgIm = CGImageCreateCopy(oldImage.CGImage);
UIImage *newImage = [UIImage imageWithCGImage:newCgIm scale:oldImage.scale orientation:oldImage.imageOrientation];

我认为你还需要在此之后添加CGImageRelease(newCgIm);。我在绘图应用中没有使用CGImageRelease,导致了内存警告和崩溃。 - tagirkaZ

9

这应该是可行的:

UIImage *newImage = [UIImage imageWithCGImage:oldImage.CGImage];

2
如果 oldImage.scale > 1,这将导致质量损失。最好使用 imageWithCGImage:scale:orientation: - Aaron Brager

5

复制后备数据并旋转图片

这个问题以稍微不同的方式询问了关于 UIImage 的常见问题。本质上,您有两个相关的问题 —— 深层复制和旋转。一个 UIImage 只是一个容器,具有用于显示的方向属性。一个 UIImage 可以将其备份数据保存为 CGImageCIImage ,但大多数情况下采用 CGImageCGImage 是包含指向底层数据的指针的信息结构体,如果您阅读文档,复制该结构体不会复制数据。所以...

深层复制

如下一段中所述,深度复制数据会导致图像旋转,因为图像在底层数据中被旋转。

UIImage *newImage = [UIImage imageWithData:UIImagePNGRepresentation(oldImage)];

这将复制数据,但在将其传输到类似于UIImageView的东西进行正确显示之前,需要设置方向属性。
另一种深度复制的方法是绘制到上下文中并获取结果。假设有一只斑马。
UIGraphicsBeginImageContext(zebra!.size)
zebra!.drawInRect(CGRectMake(0, 0, zebra!.size.width, zebra!.size.height))
let copy = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()

深拷贝和旋转

如何旋转CGImage的问题已经有了解答。此外,这个旋转后的图像是一个新的CGImage,可用来创建UIImage


1
谢谢,卡梅伦。如上所述,我无法确认这是否解决了我5年前遇到的问题,但既然你花时间教授而不仅仅是演示,那就归你了! - clozach
我在不同的时间里分别遇到了这两个信息。对于UIImage的用户来说,它并不立即显而易见,它只是一个包装器,底层数据可能是上下颠倒的。当有人说出来时,我想这是有道理的,但这就是魔法。 - Cameron Lowell Palmer

3
UIImage *newImage = [UIImage imageWithData:UIImagePNGRepresentation(oldImage)];

一个快速的测试显示,当使用imageWithData:而不是imageWithCGImage:时出现了同样的问题。:( - clozach
1
这实际上是一种工作方法,它复制基础数据并生成一个带有新CGImage支持的新UIImage。如果图像被旋转了,那么是因为底层数据被旋转了,UIImage需要设置方向值来纠正旋转。 - Cameron Lowell Palmer

0

我认为您需要创建一个图像上下文(CGContextRef)。使用方法CGContextDrawImage(...)将UIImage.CGImage绘制到上下文中,然后使用CGBitmapContextCreateImage(...)从上下文中获取图像。 通过这样的例程,我相信您可以获得所需的真实图像副本。希望这能对您有所帮助。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接