裁剪UIImage

我有一些调整图像大小的代码,这样我就可以得到图像中心的缩放块 – 我使用这个来获取UIImage并返回一个图像的小方形表示,类似于专辑视图中的内容的照片应用程序。 (我知道我可以使用UIImageView并调整裁剪模式以获得相同的结果,但这些图像有时显示在UIWebViews )。

我已经开始注意到这个代码中的一些崩溃,我有点困难。 我有两个不同的理论,我想知道是否在基地。

理论1)我通过绘制到我的目标尺寸的离屏图像上下文来实现裁剪。 由于我想要图像的中心部分,因此我将传递给drawInRectCGRect参数设置为大于图像上下文边界的东西。 我希望这是犹太教,但我是否试图画出我不应该接触的其他记忆?

理论2)我在后台线程中做所有这些。 我知道有部分UIKit被限制在主线程中。 我假设/希望绘制到屏幕外视图不是其中之一。 我错了吗?

(哦,我怎么会错过NSImage's drawInRect:fromRect:operation:fraction: method。)

  • Swift PNG图像以不正确的方向保存
  • 在解散另一个之后立即呈现模态视图控制器
  • UIImagePickerController摄像头视图在iOS 8上奇怪的旋转(图片)
  • UIImagePickerController不在iOS 8中呈现
  • 调整从相机拉取的UIimages的大小也旋转的UIimage?
  • 上传后iOS iOS UIImagePickerController结果图像方向
  • presentModalViewController:Animated已在ios6中弃用
  • 将选取的图像保存到CoreData
  • 21 Solutions collect form web for “裁剪UIImage”

    更新2014年5月28日:我写了这个时,iOS 3左右是热门的新事物,我敢肯定现在有更好的方法来做到这一点,可能内置。 正如很多人所说,这种方法不考虑轮换; 阅读一些额外的答案,并传播一些upvote爱周围,以保持对这个问题的答复对每个人都有帮助。

    原始回复:

    我要复制/粘贴我对别处相同问题的回应:

    有没有一个简单的类方法来做到这一点,但有一个函数,你可以用来获得所需的结果: CGImageCreateWithImageInRect(CGImageRef, CGRect)将帮助你。

    以下是一个简短的例子:

     CGImageRef imageRef = CGImageCreateWithImageInRect([largeImage CGImage], cropRect); // or use the UIImage wherever you like [UIImageView setImage:[UIImage imageWithCGImage:imageRef]]; CGImageRelease(imageRef); 

    要在保持相同比例和方向的同时裁剪视网膜图像,请在UIImage类别(iOS 4.0及更高版本)中使用以下方法:

     - (UIImage *)crop:(CGRect)rect { if (self.scale > 1.0f) { rect = CGRectMake(rect.origin.x * self.scale, rect.origin.y * self.scale, rect.size.width * self.scale, rect.size.height * self.scale); } CGImageRef imageRef = CGImageCreateWithImageInRect(self.CGImage, rect); UIImage *result = [UIImage imageWithCGImage:imageRef scale:self.scale orientation:self.imageOrientation]; CGImageRelease(imageRef); return result; } 

    你可以创build一个UIImage类别,并在需要的地方使用它。 基于HitScans的回应和评论以下。

     @implementation UIImage (Crop) - (UIImage *)crop:(CGRect)rect { rect = CGRectMake(rect.origin.x*self.scale, rect.origin.y*self.scale, rect.size.width*self.scale, rect.size.height*self.scale); CGImageRef imageRef = CGImageCreateWithImageInRect([self CGImage], rect); UIImage *result = [UIImage imageWithCGImage:imageRef scale:self.scale orientation:self.imageOrientation]; CGImageRelease(imageRef); return result; } @end 

    你可以这样使用它:

     UIImage *imageToCrop = <yourImageToCrop>; CGRect cropRect = <areaYouWantToCrop>; //for example //CGRectMake(0, 40, 320, 100); UIImage *croppedImage = [imageToCrop crop:cropRect]; 

    这是我的UIImage裁剪实现,它遵循imageOrientation属性。 所有的方向都经过彻底的testing。

     inline double rad(double deg) { return deg / 180.0 * M_PI; } UIImage* UIImageCrop(UIImage* img, CGRect rect) { CGAffineTransform rectTransform; switch (img.imageOrientation) { case UIImageOrientationLeft: rectTransform = CGAffineTransformTranslate(CGAffineTransformMakeRotation(rad(90)), 0, -img.size.height); break; case UIImageOrientationRight: rectTransform = CGAffineTransformTranslate(CGAffineTransformMakeRotation(rad(-90)), -img.size.width, 0); break; case UIImageOrientationDown: rectTransform = CGAffineTransformTranslate(CGAffineTransformMakeRotation(rad(-180)), -img.size.width, -img.size.height); break; default: rectTransform = CGAffineTransformIdentity; }; rectTransform = CGAffineTransformScale(rectTransform, img.scale, img.scale); CGImageRef imageRef = CGImageCreateWithImageInRect([img CGImage], CGRectApplyAffineTransform(rect, rectTransform)); UIImage *result = [UIImage imageWithCGImage:imageRef scale:img.scale orientation:img.imageOrientation]; CGImageRelease(imageRef); return result; } 

    Swift 3版本

     func cropImage(imageToCrop:UIImage, toRect rect:CGRect) -> UIImage{ let imageRef:CGImage = imageToCrop.cgImage!.cropping(to: rect)! let cropped:UIImage = UIImage(cgImage:imageRef) return cropped } let imageTop:UIImage = UIImage(named:"one.jpg")! // add validation 

    在这里输入图像描述

    在这个桥接函数的帮助下CGRectMake – > CGRect (信贷给这个答案由@rob mayoff ):

      func CGRectMake(_ x: CGFloat, _ y: CGFloat, _ width: CGFloat, _ height: CGFloat) -> CGRect { return CGRect(x: x, y: y, width: width, height: height) } 

    用法是:

     if var image:UIImage = UIImage(named:"one.jpg"){ let croppedImage = cropImage(imageToCrop: image, toRect: CGRectMake( image.size.width/4, 0, image.size.width/2, image.size.height) ) } 

    输出:

    在这里输入图像描述

    单挑:所有这些答案都假设一个CGImage背景的图像对象。

    image.CGImage可以返回nil,如果UIImage是由CIImage支持的,如果使用CIFilter创build了这个图片,情况就是CIFilter

    在这种情况下,您可能必须在新的上下文中绘制图像,然后返回该图像( 较慢 )。

     UIImage* crop(UIImage *image, rect) { UIGraphicsBeginImageContextWithOptions(rect.size, false, [image scale]); [image drawAtPoint:CGPointMake(-rect.origin.x, -rect.origin.y)]; cropped_image = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); return cropped_image; } 

    这里的答案都没有正确处理所有的比例和旋转问题。 这是迄今为止所说的所有内容的综合,截至iOS7 / 8为止。 它意味着被包含在UIImage的一个类别中。

     - (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; } 
     CGSize size = [originalImage size]; int padding = 20; int pictureSize = 300; int startCroppingPosition = 100; if (size.height > size.width) { pictureSize = size.width - (2.0 * padding); startCroppingPosition = (size.height - pictureSize) / 2.0; } else { pictureSize = size.height - (2.0 * padding); startCroppingPosition = (size.width - pictureSize) / 2.0; } // WTF: Don't forget that the CGImageCreateWithImageInRect believes that // the image is 180 rotated, so x and y are inverted, same for height and width. CGRect cropRect = CGRectMake(startCroppingPosition, padding, pictureSize, pictureSize); CGImageRef imageRef = CGImageCreateWithImageInRect([originalImage CGImage], cropRect); UIImage *newImage = [UIImage imageWithCGImage:imageRef scale:1.0 orientation:originalImage.imageOrientation]; [m_photoView setImage:newImage]; CGImageRelease(imageRef); 

    我见过的大部分回复只处理(x,y)的位置(0,0)。 好的,这是一种情况,但我希望我的裁剪操作被集中。 我花了一段时间才弄清楚是WTF评论的后面。

    我们来看一个以纵向拍摄的图像的情况:

    1. 原来的图像高度比它的宽度高(喔,到目前为止并不奇怪!)
    2. CGImageCreateWithImageInRect方法在它自己的世界中想像的图像并不是一个真正的纵向,而是一个横向(这也是为什么如果您不使用imageWithCGImage构造函数中的方向参数,它将显示为180旋转)。
    3. 所以,你应该想象这是一个风景,(0,0)的位置是图像的右上angular。

    希望这是有道理的! 如果没有,请尝试不同的值,您将看到,当为您的cropRectselect正确的x,y,宽度和高度时,逻辑会反转。

    Swift Extension

     extension UIImage { func crop(var rect: CGRect) -> UIImage { rect.origin.x*=self.scale rect.origin.y*=self.scale rect.size.width*=self.scale rect.size.height*=self.scale let imageRef = CGImageCreateWithImageInRect(self.CGImage, rect) let image = UIImage(CGImage: imageRef, scale: self.scale, orientation: self.imageOrientation)! return image } } 

    swift3

     extension UIImage { func crop(rect: CGRect) -> UIImage? { var scaledRect = rect scaledRect.origin.x *= scale scaledRect.origin.y *= scale scaledRect.size.width *= scale scaledRect.size.height *= scale guard let imageRef: CGImage = cgImage?.cropping(to: scaledRect) else { return nil } return UIImage(cgImage: imageRef, scale: scale, orientation: imageOrientation) } } 

    Swift中裁剪UIImage的最佳解决scheme,在精度方面,像素缩放…:

     private func squareCropImageToSideLength(let sourceImage: UIImage, let sideLength: CGFloat) -> UIImage { // input size comes from image let inputSize: CGSize = sourceImage.size // round up side length to avoid fractional output size let sideLength: CGFloat = ceil(sideLength) // output size has sideLength for both dimensions let outputSize: CGSize = CGSizeMake(sideLength, sideLength) // calculate scale so that smaller dimension fits sideLength let scale: CGFloat = max(sideLength / inputSize.width, sideLength / inputSize.height) // scaling the image with this scale results in this output size let scaledInputSize: CGSize = CGSizeMake(inputSize.width * scale, inputSize.height * scale) // determine point in center of "canvas" let center: CGPoint = CGPointMake(outputSize.width/2.0, outputSize.height/2.0) // calculate drawing rect relative to output Size let outputRect: CGRect = CGRectMake(center.x - scaledInputSize.width/2.0, center.y - scaledInputSize.height/2.0, scaledInputSize.width, scaledInputSize.height) // begin a new bitmap context, scale 0 takes display scale UIGraphicsBeginImageContextWithOptions(outputSize, true, 0) // optional: set the interpolation quality. // For this you need to grab the underlying CGContext let ctx: CGContextRef = UIGraphicsGetCurrentContext() CGContextSetInterpolationQuality(ctx, kCGInterpolationHigh) // draw the source image into the calculated rect sourceImage.drawInRect(outputRect) // create new image from bitmap context let outImage: UIImage = UIGraphicsGetImageFromCurrentImageContext() // clean up UIGraphicsEndImageContext() // pass back new image return outImage } 

    用来调用这个函数的指令:

     let image: UIImage = UIImage(named: "Image.jpg")! let squareImage: UIImage = self.squareCropImageToSideLength(image, sideLength: 320) self.myUIImageView.image = squareImage 

    注意: 在Objective-C中编写的最初的源代码灵感来自“Cocoanetics”博客。

    看起来有点奇怪,但很好,并考虑到图像的方向:

     var image:UIImage = ... let img = CIImage(image: image)!.imageByCroppingToRect(rect) image = UIImage(CIImage: img, scale: 1, orientation: image.imageOrientation) 
     - (UIImage *)getSubImage:(CGRect) rect{ CGImageRef subImageRef = CGImageCreateWithImageInRect(self.CGImage, rect); CGRect smallBounds = CGRectMake(rect.origin.x, rect.origin.y, CGImageGetWidth(subImageRef), CGImageGetHeight(subImageRef)); UIGraphicsBeginImageContext(smallBounds.size); CGContextRef context = UIGraphicsGetCurrentContext(); CGContextDrawImage(context, smallBounds, subImageRef); UIImage* smallImg = [UIImage imageWithCGImage:subImageRef]; UIGraphicsEndImageContext(); return smallImg; } 
      (UIImage *)squareImageWithImage:(UIImage *)image scaledToSize:(CGSize)newSize { double ratio; double delta; CGPoint offset; //make a new square size, that is the resized imaged width CGSize sz = CGSizeMake(newSize.width, newSize.width); //figure out if the picture is landscape or portrait, then //calculate scale factor and offset if (image.size.width > image.size.height) { ratio = newSize.width / image.size.width; delta = (ratio*image.size.width - ratio*image.size.height); offset = CGPointMake(delta/2, 0); } else { ratio = newSize.width / image.size.height; delta = (ratio*image.size.height - ratio*image.size.width); offset = CGPointMake(0, delta/2); } //make the final clipping rect based on the calculated values CGRect clipRect = CGRectMake(-offset.x, -offset.y, (ratio * image.size.width) + delta, (ratio * image.size.height) + delta); //start a new context, with scale factor 0.0 so retina displays get //high quality image if ([[UIScreen mainScreen] respondsToSelector:@selector(scale)]) { UIGraphicsBeginImageContextWithOptions(sz, YES, 0.0); } else { UIGraphicsBeginImageContext(sz); } UIRectClip(clipRect); [image drawInRect:clipRect]; UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); return newImage; } 

    在iOS9.2SDK上,我使用下面的方法将帧从UIView转换为UIImage

     -(UIImage *)getNeedImageFrom:(UIImage*)image cropRect:(CGRect)rect { CGSize cropSize = rect.size; CGFloat widthScale = image.size.width/self.imageViewOriginal.bounds.size.width; CGFloat heightScale = image.size.height/self.imageViewOriginal.bounds.size.height; cropSize = CGSizeMake(rect.size.width*widthScale, rect.size.height*heightScale); CGPoint pointCrop = CGPointMake(rect.origin.x*widthScale, rect.origin.y*heightScale); rect = CGRectMake(pointCrop.x, pointCrop.y, cropSize.width, cropSize.height); CGImageRef subImage = CGImageCreateWithImageInRect(image.CGImage, rect); UIImage *croppedImage = [UIImage imageWithCGImage:subImage]; CGImageRelease(subImage); return croppedImage; } 

    Swift 2.0更新( CIImage兼容性)

    Maxim的答案扩展,但如果您的图像是基于CIImage的,也适用。

     public extension UIImage { func imageByCroppingToRect(rect: CGRect) -> UIImage? { if let image = CGImageCreateWithImageInRect(self.CGImage, rect) { return UIImage(CGImage: image) } else if let image = (self.CIImage)?.imageByCroppingToRect(rect) { return UIImage(CIImage: image) } return nil } } 

    这是一个基于面条的更新的Swift 3版本的答案

     func cropping(to rect: CGRect) -> UIImage? { if let cgCrop = cgImage?.cropping(to: rect) { return UIImage(cgImage: cgCrop) } else if let ciCrop = ciImage?.cropping(to: rect) { return UIImage(ciImage: ciCrop) } return nil } 

    我对其他解决scheme并不满意,因为他们要么抽出几次(使用比必要的更多的权力),要么在方向上有问题。 这是我用来从UIImage *图像缩放广场裁剪图像。

     CGFloat minimumSide = fminf(image.size.width, image.size.height); CGFloat finalSquareSize = 600.; //create new drawing context for right size CGRect rect = CGRectMake(0, 0, finalSquareSize, finalSquareSize); CGFloat scalingRatio = 640.0/minimumSide; UIGraphicsBeginImageContext(rect.size); //draw [image drawInRect:CGRectMake((minimumSide - photo.size.width)*scalingRatio/2., (minimumSide - photo.size.height)*scalingRatio/2., photo.size.width*scalingRatio, photo.size.height*scalingRatio)]; UIImage *croppedImage = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); 

    我使用下面的方法。

      -(UIImage *)getNeedImageFrom:(UIImage*)image cropRect:(CGRect)rect { CGSize cropSize = rect.size; CGFloat widthScale = image.size.width/self.imageViewOriginal.bounds.size.width; CGFloat heightScale = image.size.height/self.imageViewOriginal.bounds.size.height; cropSize = CGSizeMake(rect.size.width*widthScale, rect.size.height*heightScale); CGPoint pointCrop = CGPointMake(rect.origin.x*widthScale, rect.origin.y*heightScale); rect = CGRectMake(pointCrop.x, pointCrop.y, cropSize.width, cropSize.height); CGImageRef subImage = CGImageCreateWithImageInRect(image.CGImage, rect); UIImage *croppedImage = [UIImage imageWithCGImage:subImage]; CGImageRelease(subImage); return croppedImage; 

    }

    看看https://github.com/vvbogdan/BVCropPhoto

     - (UIImage *)裁剪图像{
         CGFloat scale = self.sourceImage.size.width / self.scrollView.contentSize.width;
    
         UIImage * finalImage = nil;
         CGRect targetFrame = CGRectMake((self.scrollView.contentInset.left + self.scrollView.contentOffset.x)* scale,
                 (self.scrollView.contentInset.top + self.scrollView.contentOffset.y)* scale,
                 self.cropSize.width * scale,
                 self.cropSize.height * scale);
    
         CGImageRef contextImage = CGImageCreateWithImageInRect([[self imageWithRotation:self.sourceImage] CGImage],targetFrame);
    
         if(contextImage!= NULL){
             finalImage = [UIImage imageWithCGImage:contextImage
                                             规模:self.sourceImage.scale
                                       取向:UIImageOrientationUp];
    
             CGImageRelease(contextImage);
         }
    
         return finalImage;
     }
    
    
     - (UIImage *)imageWithRotation:(UIImage *)image {
    
    
        如果(image.imageOrientation == UIImageOrientationUp)返回图像;
         CGAffineTransform转换= CGAffineTransformIdentity;
    
        开关(image.imageOrientation){
            情况下UIImageOrientationDown:
            大小写UIImageOrientationDownMirrored:
                 transform = CGAffineTransformTranslate(transform,image.size.width,image.size.height);
                 transform = CGAffineTransformRotate(transform,M_PI);
                打破;
    
            情况下UIImageOrientationLeft:
            情况下UIImageOrientationLeftMirrored:
                 transform = CGAffineTransformTranslate(transform,image.size.width,0);
                 transform = CGAffineTransformRotate(transform,M_PI_2);
                打破;
    
            情况下UIImageOrientationRight:
            情况下UIImageOrientationRightMirrored:
                 transform = CGAffineTransformTranslate(transform,0,image.size.height);
                 transform = CGAffineTransformRotate(transform,-M_PI_2);
                打破;
            情况下UIImageOrientationUp:
            情况UIImageOrientationUpMirrored:
                打破;
         }
    
        开关(image.imageOrientation){
            情况UIImageOrientationUpMirrored:
            大小写UIImageOrientationDownMirrored:
                 transform = CGAffineTransformTranslate(transform,image.size.width,0);
                 transform = CGAffineTransformScale(transform,-1,1);
                打破;
    
            情况下UIImageOrientationLeftMirrored:
            情况下UIImageOrientationRightMirrored:
                 transform = CGAffineTransformTranslate(transform,image.size.height,0);
                 transform = CGAffineTransformScale(transform,-1,1);
                打破;
            情况下UIImageOrientationUp:
            情况下UIImageOrientationDown:
            情况下UIImageOrientationLeft:
            情况下UIImageOrientationRight:
                打破;
         }
    
         //现在我们将底层的CGImage绘制到一个新的上下文中,应用这个变换
         //以上计算
         CGContextRef ctx = CGBitmapContextCreate(NULL,image.size.width,image.size.height,
                 CGImageGetBitsPerComponent(image.CGImage),0,
                 CGImageGetColorSpace(image.CGImage)
                 CGImageGetBitmapInfo(image.CGImage));
         CGContextConcatCTM(ctx,transform);
        开关(image.imageOrientation){
            情况下UIImageOrientationLeft:
            情况下UIImageOrientationLeftMirrored:
            情况下UIImageOrientationRight:
            情况下UIImageOrientationRightMirrored:
                 // Grr ...
                 CGContextDrawImage(ctx,CGRectMake(0,0,image.size.height,image.size.width),image.CGImage);
                打破;
    
            默认:
                 CGContextDrawImage(ctx,CGRectMake(0,0,image.size.width,image.size.height),image.CGImage);
                打破;
         }
    
         //现在我们只是从绘图上下文创build一个新的UIImage
         CGImageRef cgimg = CGBitmapContextCreateImage(ctx);
         UIImage * img = [UIImage imageWithCGImage:cgimg];
         CGContextRelease(CTX);
         CGImageRelease(cgimg);
        返回img;
    
     }
    

    按照@Arne的答案。 我只是修复类别function。 把它放在UIImage类别中。

     -(UIImage*)cropImage:(CGRect)rect{ UIGraphicsBeginImageContextWithOptions(rect.size, false, [self scale]); [self drawAtPoint:CGPointMake(-rect.origin.x, -rect.origin.y)]; UIImage* cropped_image = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); return cropped_image; }