裁剪UIImage

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

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

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

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

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

更新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; }