如何对相机胶卷中的照片进行方形切割?

Posted

技术标签:

【中文标题】如何对相机胶卷中的照片进行方形切割?【英文标题】:How to perform square cut to the photos in camera roll? 【发布时间】:2012-06-27 19:04:47 【问题描述】:

我想像 instagram 一样在 iPhone 上尝试一些图像过滤功能。 我使用 imagePickerController 从相机胶卷中获取照片。我了解 imagePickerController 返回的图像已减少以节省内存。将原始图像加载到 UIImage 是不明智的。但是如何处理图像然后将其保存为原始像素? 我使用 iPhone 4S 作为我的开发设备。

相机胶卷中的原始照片为 3264 * 2448。

UIImagePickerControllerOriginalImage 返回的图片是1920 * 1440

UIImagePickerControllerEditedImage 返回的图片是640 * 640

imageViewOld(使用 UIImagePickerControllerCropRect [80,216,1280,1280] 裁剪 UIImagePickerControllerOriginalImage 返回的图片) 为 1280 * 1224

imageViewNew(使用双倍大小的 UIImagePickerControllerCropRect [80,216,2560,2560] 裁剪 UIImagePickerControllerOriginalImage 返回的图像) 为 1840 * 1224。

我通过instagram查看同一张照片是1280 * 1280

我的问题是:

    为什么 UIImagePickerControllerOriginalImage 不返回“原始”照片?为什么要缩小到 1920 * 1440?

    为什么 UIImagePickerControllerEditedImage 不返回 1280 * 1280 的图像?作为 UIImagePickerControllerCropRect 显示它被 1280 * 1280 正方形切割?

    如何对原始照片进行方形切割以成为 2448 * 2448 的图像?

提前致谢。 以下是我的代码:

  - (void)imagePickerController:(UIImagePickerController *)picker  didFinishPickingMediaWithInfo:(NSDictionary *)info
  

   NSString *mediaType = [info objectForKey:UIImagePickerControllerMediaType];
   if ([mediaType isEqualToString:@"public.image"])
   

    UIImage *imageEdited = [info objectForKey:UIImagePickerControllerEditedImage];
    UIImage *imagePicked = [info objectForKey:UIImagePickerControllerOriginalImage];

    CGRect cropRect;
    cropRect = [[info valueForKey:@"UIImagePickerControllerCropRect"] CGRectValue];

    NSLog(@"Original width = %f height= %f ",imagePicked.size.width, imagePicked.size.height);
    //Original width = 1440.000000 height= 1920.000000

    NSLog(@"imageEdited width = %f height = %f",imageEdited.size.width, imageEdited.size.height);
    //imageEdited width = 640.000000 height = 640.000000

    NSLog(@"corpRect %f %f %f %f", cropRect.origin.x, cropRect.origin.y , cropRect.size.width, cropRect.size.height);
    //corpRect 80.000000 216.000000 1280.000000 1280.000000

    CGRect rectNew = CGRectMake(cropRect.origin.x, cropRect.origin.y , cropRect.size.width*2, cropRect.size.height*2);

    CGRect rectOld = CGRectMake(cropRect.origin.x, cropRect.origin.y , cropRect.size.width, cropRect.size.height);

    CGImageRef imageRefNew = CGImageCreateWithImageInRect([imagePicked CGImage], rectNew);
    CGImageRef imageRefOld = CGImageCreateWithImageInRect([imagePicked CGImage], rectOld);

    UIImageView *imageViewNew = [[UIImageView alloc] initWithImage:[UIImage imageWithCGImage:imageRefNew]];
    CGImageRelease(imageRefNew);

    UIImageView *imageViewOld = [[UIImageView alloc] initWithImage:[UIImage imageWithCGImage:imageRefOld]];
    CGImageRelease(imageRefOld);


    NSLog(@"imageViewNew width = %f height = %f",imageViewNew.image.size.width, imageViewNew.image.size.height);
    //imageViewNew width = 1840.000000 height = 1224.000000

    NSLog(@"imageViewOld width = %f height = %f",imageViewOld.image.size.width, imageViewOld.image.size.height);
    //imageViewOld width = 1280.000000 height = 1224.000000

     UIImageWriteToSavedPhotosAlbum(imageEdited, nil, nil, NULL);

     UIImageWriteToSavedPhotosAlbum([imageViewNew.image imageRotatedByDegrees:90.0], nil, nil, NULL);
     UIImageWriteToSavedPhotosAlbum([imageViewOld.image imageRotatedByDegrees:90.0], nil, nil, NULL);


    //assign the image to an UIImage Control
    self.imageV.contentMode = UIViewContentModeScaleAspectFit;
    self.imageV.frame = CGRectMake(0, 0, self.view.bounds.size.width, self.view.bounds.size.width);
    self.imageV.image = imageEdited;


  

  [self dismissModalViewControllerAnimated:YES];

 

【问题讨论】:

【参考方案1】:

正如您所观察到的,UIImagePickerController 将返回缩小的编辑图像,有时为 640x640,有时为 320x320(取决于设备)。

你的问题:

如何对原始照片进行方形切割以成为 2448 * 2448 的图像?

为此,您需要首先使用UIImagePickerControllerCropRect 从使用信息字典的UIImagePickerControllerOriginalImage 键获得的原始图像创建一个新图像。使用 Quartz Core 方法,CGImageCreateWithImageInRect 您可以创建一个新图像,该图像仅包含由传递的 rect 限定的像素;在这种情况下,作物矩形。您需要考虑方向才能使其正常工作。然后,您只需要将图像缩放到您想要的大小。重要的是要注意,裁剪矩形在正确定向后是相对于原始图像的,而不是从相机或照片库中出来的。这就是为什么当我们开始使用 Quartz 方法创建新图像等时,我们需要转换裁剪矩形以匹配方向。

我采用了您上面的代码并将其设置为根据裁剪矩形从原始图像创建 1280x1280 图像。这里仍然存在一些边缘情况,即考虑到裁剪矩形有时可能具有负值(代码假定为方形裁剪矩形)尚未解决。

    首先转换裁剪矩形以考虑传入图像的方向和大小。这个transformCGRectForUIImageOrientation 函数来自NiftyBean 创建一个裁剪为转换后的裁剪矩形的图像。 将图像缩放(和旋转)到所需大小。即1280x1280。 从具有正确比例和方向的 CGImage 创建 UIImage。

以下是您的代码进行了更改:更新已在此下方添加了新代码,应处理丢失的情况。

- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info 
NSString *mediaType = [info objectForKey:UIImagePickerControllerMediaType];
if ([mediaType isEqualToString:@"public.image"])


    UIImage *imageEdited = [info objectForKey:UIImagePickerControllerEditedImage];
    UIImage *imagePicked = [info objectForKey:UIImagePickerControllerOriginalImage];

    CGRect cropRect;
    cropRect = [[info valueForKey:@"UIImagePickerControllerCropRect"] CGRectValue];

    NSLog(@"Original width = %f height= %f ",imagePicked.size.width, imagePicked.size.height);
    //Original width = 1440.000000 height= 1920.000000

    NSLog(@"imageEdited width = %f height = %f",imageEdited.size.width, imageEdited.size.height);
    //imageEdited width = 640.000000 height = 640.000000

    NSLog(@"corpRect %@", NSStringFromCGRect(cropRect));
    //corpRect 80.000000 216.000000 1280.000000 1280.000000

    CGSize finalSize = CGSizeMake(1280,1280); 
    CGImageRef imagePickedRef = imagePicked.CGImage;

    CGRect transformedRect = transformCGRectForUIImageOrientation(cropRect, imagePicked.imageOrientation, imagePicked.size);
    CGImageRef cropRectImage = CGImageCreateWithImageInRect(imagePickedRef, transformedRect);
    CGColorSpaceRef colorspace = CGImageGetColorSpace(imagePickedRef);
    CGContextRef context = CGBitmapContextCreate(NULL, 
                                                 finalSize.width, 
                                                 finalSize.height,
                                                 CGImageGetBitsPerComponent(imagePickedRef),
                                                 CGImageGetBytesPerRow(imagePickedRef),
                                                 colorspace,
                                                 CGImageGetAlphaInfo(imagePickedRef));
    CGContextSetInterpolationQuality(context, kCGInterpolationHigh); //Give the context a hint that we want high quality during the scale
    CGContextDrawImage(context, CGRectMake(0, 0, finalSize.width, finalSize.height), cropRectImage);
    CGImageRelease(cropRectImage);

    CGImageRef instaImage = CGBitmapContextCreateImage(context);
    CGContextRelease(context);

    //assign the image to an UIImage Control
    UIImage *image = [UIImage imageWithCGImage:instaImage scale:imagePicked.scale orientation:imagePicked.imageOrientation];
    self.imageView.contentMode = UIViewContentModeScaleAspectFit;
    self.imageView.image = image;
    CGImageRelease(instaImage);

    UIImageWriteToSavedPhotosAlbum(image, nil, nil, nil);


[self dismissModalViewControllerAnimated:YES];

CGRect transformCGRectForUIImageOrientation(CGRect source, UIImageOrientation orientation, CGSize imageSize) 
switch (orientation) 
    case UIImageOrientationLeft:  // EXIF #8
        CGAffineTransform txTranslate = CGAffineTransformMakeTranslation(imageSize.height, 0.0);
        CGAffineTransform txCompound = CGAffineTransformRotate(txTranslate,M_PI_2);
        return CGRectApplyAffineTransform(source, txCompound);
    
    case UIImageOrientationDown:  // EXIF #3
        CGAffineTransform txTranslate = CGAffineTransformMakeTranslation(imageSize.width, imageSize.height);
        CGAffineTransform txCompound = CGAffineTransformRotate(txTranslate,M_PI);
        return CGRectApplyAffineTransform(source, txCompound);
    
    case UIImageOrientationRight:  // EXIF #6
        CGAffineTransform txTranslate = CGAffineTransformMakeTranslation(0.0, imageSize.width);
        CGAffineTransform txCompound = CGAffineTransformRotate(txTranslate,M_PI + M_PI_2);
        return CGRectApplyAffineTransform(source, txCompound);
    
    case UIImageOrientationUp: // EXIF #1 - do nothing
    default: // EXIF 2,4,5,7 - ignore
        return source;


更新我已经做了几个方法来处理其余的情况。 步骤基本相同,只是稍作修改。

    第一个修改是正确转换和缩放上下文以处理传入图像的方向, 第二个是支持您可以从中获得的非方形作物 UIImagePickerController。在这些情况下,方形图像是 填充您选择的颜色。

新代码

// CropRect is assumed to be in UIImageOrientationUp, as it is delivered this way from the UIImagePickerController when using AllowsImageEditing is on.
// The sourceImage can be in any orientation, the crop will be transformed to match
// The output image bounds define the final size of the image, the image will be scaled to fit,(AspectFit) the bounds, the fill color will be
// used for areas that are not covered by the scaled image. 
-(UIImage *)cropImage:(UIImage *)sourceImage cropRect:(CGRect)cropRect aspectFitBounds:(CGSize)finalImageSize fillColor:(UIColor *)fillColor 

CGImageRef sourceImageRef = sourceImage.CGImage;

//Since the crop rect is in UIImageOrientationUp we need to transform it to match the source image.
CGAffineTransform rectTransform = [self transformSize:sourceImage.size orientation:sourceImage.imageOrientation];
CGRect transformedRect = CGRectApplyAffineTransform(cropRect, rectTransform);

//Now we get just the region of the source image that we are interested in.
CGImageRef cropRectImage = CGImageCreateWithImageInRect(sourceImageRef, transformedRect);

//Figure out which dimension fits within our final size and calculate the aspect correct rect that will fit in our new bounds
CGFloat horizontalRatio = finalImageSize.width / CGImageGetWidth(cropRectImage);
CGFloat verticalRatio = finalImageSize.height / CGImageGetHeight(cropRectImage);
CGFloat ratio = MIN(horizontalRatio, verticalRatio); //Aspect Fit
CGSize aspectFitSize = CGSizeMake(CGImageGetWidth(cropRectImage) * ratio, CGImageGetHeight(cropRectImage) * ratio);


CGContextRef context = CGBitmapContextCreate(NULL,
                                             finalImageSize.width,
                                             finalImageSize.height,
                                             CGImageGetBitsPerComponent(cropRectImage),
                                             0,
                                             CGImageGetColorSpace(cropRectImage),
                                             CGImageGetBitmapInfo(cropRectImage));

if (context == NULL) 
    NSLog(@"NULL CONTEXT!");


//Fill with our background color
CGContextSetFillColorWithColor(context, fillColor.CGColor);
CGContextFillRect(context, CGRectMake(0, 0, finalImageSize.width, finalImageSize.height));

//We need to rotate and transform the context based on the orientation of the source image.
CGAffineTransform contextTransform = [self transformSize:finalImageSize orientation:sourceImage.imageOrientation];
CGContextConcatCTM(context, contextTransform);

//Give the context a hint that we want high quality during the scale
CGContextSetInterpolationQuality(context, kCGInterpolationHigh); 

//Draw our image centered vertically and horizontally in our context.
CGContextDrawImage(context, CGRectMake((finalImageSize.width-aspectFitSize.width)/2, (finalImageSize.height-aspectFitSize.height)/2, aspectFitSize.width, aspectFitSize.height), cropRectImage);

//Start cleaning up..
CGImageRelease(cropRectImage);

CGImageRef finalImageRef = CGBitmapContextCreateImage(context);
UIImage *finalImage = [UIImage imageWithCGImage:finalImageRef];

CGContextRelease(context);
CGImageRelease(finalImageRef);
return finalImage;


//Creates a transform that will correctly rotate and translate for the passed orientation.
//Based on code from niftyBean.com
- (CGAffineTransform) transformSize:(CGSize)imageSize orientation:(UIImageOrientation)orientation 

CGAffineTransform transform = CGAffineTransformIdentity;
switch (orientation) 
    case UIImageOrientationLeft:  // EXIF #8
        CGAffineTransform txTranslate = CGAffineTransformMakeTranslation(imageSize.height, 0.0);
        CGAffineTransform txCompound = CGAffineTransformRotate(txTranslate,M_PI_2);
        transform = txCompound;
        break;
    
    case UIImageOrientationDown:  // EXIF #3
        CGAffineTransform txTranslate = CGAffineTransformMakeTranslation(imageSize.width, imageSize.height);
        CGAffineTransform txCompound = CGAffineTransformRotate(txTranslate,M_PI);
        transform = txCompound;
        break;
    
    case UIImageOrientationRight:  // EXIF #6
        CGAffineTransform txTranslate = CGAffineTransformMakeTranslation(0.0, imageSize.width);
        CGAffineTransform txCompound = CGAffineTransformRotate(txTranslate,-M_PI_2);
        transform = txCompound;
        break;
    
    case UIImageOrientationUp: // EXIF #1 - do nothing
    default: // EXIF 2,4,5,7 - ignore
        break;

return transform;


【讨论】:

是的!它确实有效!你能告诉我为什么 UIImagePickerController 没有给我相机胶卷中的原始图像吗? 我不确定背后的原因是什么。我观察到用相机拍照时肯定会得到全分辨率版本。如果您需要获得完整分辨率版本,我认为您可以使用 UIImagePickerControllerReferenceURL 结合 ALAssetLibrary 类来检索它。使用此框架将要求您的用户允许访问他们的位置,因为原始图像将被地理标记。 另外.. 更新了代码以更合理地处理旋转,并且即使在裁剪矩形不是方形的情况下也能处理获得方形图像。希望对您有所帮助。 你的代码解决了我的问题,尝试了很多没有正确结果的解决方案,你的“新代码”做得很好,旋转和缩放。谢谢! 这适用于所有 iPhone,不幸的是 iPad 不适用

以上是关于如何对相机胶卷中的照片进行方形切割?的主要内容,如果未能解决你的问题,请参考以下文章

从 Swift 中的相机胶卷连拍中删除单张照片

科尔多瓦,从相机胶卷中获取所有照片

删除相机胶卷中的照片

如何检测 ALAssetLibrary 中的最后一项是照片还是视频?

如何使用 Photos 框架从 iOS 上的相机胶卷中获取最新照片?

如何允许用户从他的相机胶卷或照片库中挑选照片?