裁剪UIImage
我有一些调整图像大小的代码,这样我就可以得到图像中心的缩放块 – 我使用这个来获取UIImage
并返回一个图像的小方形表示,类似于专辑视图中的内容的照片应用程序。 (我知道我可以使用UIImageView
并调整裁剪模式以获得相同的结果,但这些图像有时显示在UIWebViews
)。
我已经开始注意到这个代码中的一些崩溃,我有点困难。 我有两个不同的理论,我想知道是否在基地。
理论1)我通过绘制到我的目标尺寸的离屏图像上下文来实现裁剪。 由于我想要图像的中心部分,因此我将传递给drawInRect
的CGRect
参数设置为大于图像上下文边界的东西。 我希望这是犹太教,但我是否试图画出我不应该接触的其他记忆?
理论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评论的后面。
我们来看一个以纵向拍摄的图像的情况:
- 原来的图像高度比它的宽度高(喔,到目前为止并不奇怪!)
- CGImageCreateWithImageInRect方法在它自己的世界中想像的图像并不是一个真正的纵向,而是一个横向(这也是为什么如果您不使用imageWithCGImage构造函数中的方向参数,它将显示为180旋转)。
- 所以,你应该想象这是一个风景,(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; }