如何裁剪 UIImage?
Posted
技术标签:
【中文标题】如何裁剪 UIImage?【英文标题】:How to crop the UIImage? 【发布时间】:2010-04-14 06:45:08 【问题描述】:我开发了一个应用程序,在该应用程序中我使用其像素处理图像,但在该图像处理中需要大量时间。因此我想裁剪 UIImage(仅图像的中间部分,即删除/裁剪图像的边框部分)。我有开发代码,
- (NSInteger) processImage1: (UIImage*) image
CGFloat width = image.size.width;
CGFloat height = image.size.height;
struct pixel* pixels = (struct pixel*) calloc(1, image.size.width * image.size.height * sizeof(struct pixel));
if (pixels != nil)
// Create a new bitmap
CGContextRef context = CGBitmapContextCreate(
(void*) pixels,
image.size.width,
image.size.height,
8,
image.size.width * 4,
CGImageGetColorSpace(image.CGImage),
kCGImageAlphaPremultipliedLast
);
if (context != NULL)
// Draw the image in the bitmap
CGContextDrawImage(context, CGRectMake(0.0f, 0.0f, image.size.width, image.size.height), image.CGImage);
NSUInteger numberOfPixels = image.size.width * image.size.height;
NSMutableArray *numberOfPixelsArray = [[[NSMutableArray alloc] initWithCapacity:numberOfPixelsArray] autorelease];
我如何拍摄(裁剪外边框)UIImage 的中间部分?????????
【问题讨论】:
【参考方案1】:试试这样的:
CGImageRef imageRef = CGImageCreateWithImageInRect([largeImage CGImage], cropRect);
image = [UIImage imageWithCGImage:imageRef];
CGImageRelease(imageRef);
注意:cropRect 是图像中间部分的较小矩形...
【讨论】:
mihirpmehta,现在我尝试做类似的事情,- (UIImage *)cropedImage: (UIImage *) image CGFloat width = image.size.width; CGFloat 高度 = image.size.height; UIImage *cropedImage = [[UIImage alloc] init]; CGFloat widthCrop = (image.size.width)/2; CGFloat heightCrop = (image.size.height)/2; 此后我无法想象该怎么做? 获取新的 X 和新的 Y 作为 OldX + OldX/4 和 OldY + OldY/4 并制作具有新宽度高度 NewX 和 NewY 的矩形并将其用作cropRect @mihirpmehta,您上面的方法只是在cropRect矩形中绘制原始图像的镜像。无法处理 UIImage。 sorry... 将 XY 坐标更改为 X+(Oldwidth/4) 和 y+(OldHeight/4)... 我的错误.. @mihirpmehta,我想我需要使用您给定坐标的像素处理来裁剪图像。但是它是怎么做的?【参考方案2】:我一直在寻找一种方法来获得 UIImage 的任意矩形裁剪(即子图像)。
如果图像的方向不是 UIImageOrientationUp,我尝试的大多数解决方案都不起作用。
例如:
http://www.hive05.com/2008/11/crop-an-image-using-the-iphone-sdk/
通常,如果您使用 iPhone 相机,您将有其他方向,例如 UIImageOrientationLeft,并且您将无法通过上述方式获得正确的裁剪。这是因为使用了 CGImageRef/CGContextDrawImage,它们在 UIImage 的坐标系中有所不同。
下面的代码使用 UI* 方法(没有 CGImageRef),我已经用上/下/左/右方向的图像对此进行了测试,它似乎工作得很好。
// get sub image
- (UIImage*) getSubImageFrom: (UIImage*) img WithRect: (CGRect) rect
UIGraphicsBeginImageContext(rect.size);
CGContextRef context = UIGraphicsGetCurrentContext();
// translated rectangle for drawing sub image
CGRect drawRect = CGRectMake(-rect.origin.x, -rect.origin.y, img.size.width, img.size.height);
// clip to the bounds of the image context
// not strictly necessary as it will get clipped anyway?
CGContextClipToRect(context, CGRectMake(0, 0, rect.size.width, rect.size.height));
// draw image
[img drawInRect:drawRect];
// grab image
UIImage* subImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return subImage;
【讨论】:
请注意,您只能在主线程上运行上述代码。避免UIGraphicsBeginImageContext
等的好处是绕过这个限制。
@William Denniss 你确定吗? ***.com/questions/11528803/…
@Klass,你是对的,因为 ios 4 你可以在任何线程上调用它。但是,代码仍然不是线程安全的,您一次只能在一个线程上使用UIGraphicsBeginImageContext
。文档状态:“创建基于位图的图形上下文并使其成为当前上下文。”我相信这个“当前上下文”是全局的,因此不是线程安全的。我的应用程序一次处理多个图像,并且在多个线程中使用 UIGraphicsBeginImageContext
时发生了崩溃,因此我切换到使用线程安全的 CGContextRef
。
完美!干杯伙伴!【参考方案3】:
因为我刚才需要它,这里是 Swift 4 中 M-V 的代码:
func imageWithImage(image: UIImage, croppedTo rect: CGRect) -> UIImage
UIGraphicsBeginImageContext(rect.size)
let context = UIGraphicsGetCurrentContext()
let drawRect = CGRect(x: -rect.origin.x, y: -rect.origin.y,
width: image.size.width, height: image.size.height)
context?.clip(to: CGRect(x: 0, y: 0,
width: rect.size.width, height: rect.size.height))
image.draw(in: drawRect)
let subImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return subImage!
【讨论】:
【参考方案4】:如果您不仅可以设置 UIImageView 的图像,还可以设置要在该 UIImage 中显示的左上角偏移量,最终会更快,从精灵图集创建的图像要少得多。也许这是可能的。这肯定会省去很多努力!
同时,我在我的应用程序中使用的实用程序类中创建了这些有用的功能。它从另一个 UIImage 的一部分创建一个 UIImage,并使用标准 UIImageOrientation 值指定旋转、缩放和翻转选项。保留原始图像的像素缩放比例。
我的应用在初始化过程中创建了很多 UIImage,这必然需要时间。但是在选择某个选项卡之前不需要某些图像。为了让加载看起来更快,我可以在启动时产生的单独线程中创建它们,然后等到它在选择该选项卡时完成。
此代码也发布在Most efficient way to draw part of an image in iOS
+ (UIImage*)imageByCropping:(UIImage *)imageToCrop toRect:(CGRect)aperture
return [ChordCalcController imageByCropping:imageToCrop toRect:aperture withOrientation:UIImageOrientationUp];
// Draw a full image into a crop-sized area and offset to produce a cropped, rotated image
+ (UIImage*)imageByCropping:(UIImage *)imageToCrop toRect:(CGRect)aperture withOrientation:(UIImageOrientation)orientation
// convert y coordinate to origin bottom-left
CGFloat orgY = aperture.origin.y + aperture.size.height - imageToCrop.size.height,
orgX = -aperture.origin.x,
scaleX = 1.0,
scaleY = 1.0,
rot = 0.0;
CGSize size;
switch (orientation)
case UIImageOrientationRight:
case UIImageOrientationRightMirrored:
case UIImageOrientationLeft:
case UIImageOrientationLeftMirrored:
size = CGSizeMake(aperture.size.height, aperture.size.width);
break;
case UIImageOrientationDown:
case UIImageOrientationDownMirrored:
case UIImageOrientationUp:
case UIImageOrientationUpMirrored:
size = aperture.size;
break;
default:
assert(NO);
return nil;
switch (orientation)
case UIImageOrientationRight:
rot = 1.0 * M_PI / 2.0;
orgY -= aperture.size.height;
break;
case UIImageOrientationRightMirrored:
rot = 1.0 * M_PI / 2.0;
scaleY = -1.0;
break;
case UIImageOrientationDown:
scaleX = scaleY = -1.0;
orgX -= aperture.size.width;
orgY -= aperture.size.height;
break;
case UIImageOrientationDownMirrored:
orgY -= aperture.size.height;
scaleY = -1.0;
break;
case UIImageOrientationLeft:
rot = 3.0 * M_PI / 2.0;
orgX -= aperture.size.height;
break;
case UIImageOrientationLeftMirrored:
rot = 3.0 * M_PI / 2.0;
orgY -= aperture.size.height;
orgX -= aperture.size.width;
scaleY = -1.0;
break;
case UIImageOrientationUp:
break;
case UIImageOrientationUpMirrored:
orgX -= aperture.size.width;
scaleX = -1.0;
break;
// set the draw rect to pan the image to the right spot
CGRect drawRect = CGRectMake(orgX, orgY, imageToCrop.size.width, imageToCrop.size.height);
// create a context for the new image
UIGraphicsBeginImageContextWithOptions(size, NO, imageToCrop.scale);
CGContextRef gc = UIGraphicsGetCurrentContext();
// apply rotation and scaling
CGContextRotateCTM(gc, rot);
CGContextScaleCTM(gc, scaleX, scaleY);
// draw the image to our clipped context using the offset rect
CGContextDrawImage(gc, drawRect, imageToCrop.CGImage);
// pull the image from our cropped context
UIImage *cropped = UIGraphicsGetImageFromCurrentImageContext();
// pop the context to get back to the default
UIGraphicsEndImageContext();
// Note: this is autoreleased
return cropped;
【讨论】:
谢谢。如果我从旋转图像的 AVFoundation 相机获取图像,我发送的图像方向应该是什么?【参考方案5】:使用函数
CGContextClipToRect(context, CGRectMake(0, 0, size.width, size.height));
这是一个示例代码,用于不同的目的,但可以剪辑。
- (UIImage *)aspectFillToSize:(CGSize)size
CGFloat imgAspect = self.size.width / self.size.height;
CGFloat sizeAspect = size.width/size.height;
CGSize scaledSize;
if (sizeAspect > imgAspect) // increase width, crop height
scaledSize = CGSizeMake(size.width, size.width / imgAspect);
else // increase height, crop width
scaledSize = CGSizeMake(size.height * imgAspect, size.height);
UIGraphicsBeginImageContextWithOptions(size, NO, 0.0f);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextClipToRect(context, CGRectMake(0, 0, size.width, size.height));
[self drawInRect:CGRectMake(0.0f, 0.0f, scaledSize.width, scaledSize.height)];
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return image;
【讨论】:
【参考方案6】:#非常小/简单的 Swift 5 版本,
您不应该混合 UI 和 CG 对象,它们有时具有非常不同的坐标空间。这会让你难过。
注意?:self.draw(at:)
@inlinable private prefix func - (right: CGPoint) -> CGPoint
return CGPoint(x: -right.x, y: -right.y)
extension UIImage
public func cropped(to cropRect: CGRect) -> UIImage?
let renderer = UIGraphicsImageRenderer(size: cropRect.size)
return renderer.image
_ in
self.draw(at: -cropRect.origin)
【讨论】:
【参考方案7】:如果您想在每张照片的中心裁剪肖像。
使用@M-V 解决方案,并替换cropRect。
CGFloat height = imageTaken.size.height;
CGFloat width = imageTaken.size.width;
CGFloat newWidth = height * 9 / 16;
CGFloat newX = abs((width - newWidth)) / 2;
CGRect cropRect = CGRectMake(newX,0, newWidth ,height);
【讨论】:
【参考方案8】:我希望能够根据纵横比从区域裁剪,并根据外部边界范围缩放到大小。这是我的变体:
import AVFoundation
import ImageIO
class Image
class func crop(image:UIImage, source:CGRect, aspect:CGSize, outputExtent:CGSize) -> UIImage
let sourceRect = AVMakeRectWithAspectRatioInsideRect(aspect, source)
let targetRect = AVMakeRectWithAspectRatioInsideRect(aspect, CGRect(origin: CGPointZero, size: outputExtent))
let opaque = true, deviceScale:CGFloat = 0.0 // use scale of device's main screen
UIGraphicsBeginImageContextWithOptions(targetRect.size, opaque, deviceScale)
let scale = max(
targetRect.size.width / sourceRect.size.width,
targetRect.size.height / sourceRect.size.height)
let drawRect = CGRect(origin: -sourceRect.origin * scale, size: image.size * scale)
image.drawInRect(drawRect)
let scaledImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return scaledImage
有几件事让我感到困惑,裁剪和调整大小的不同问题。裁剪由您传递给 drawInRect 的矩形的原点处理,缩放由大小部分处理。在我的情况下,我需要将源上的裁剪矩形的大小与相同纵横比的输出矩形相关联。然后输出/输入比例因子,这需要应用于drawRect(传递给drawInRect)。
需要注意的是,这种方法有效地假设您正在绘制的图像大于图像上下文。我没有对此进行测试,但我认为您可以使用此代码来处理裁剪/缩放,但将比例参数明确定义为上述比例参数。默认情况下,UIKit 根据屏幕分辨率应用乘数。
最后,应该注意的是,这种 UIKit 方法比 CoreGraphics / Quartz 和 Core Image 方法级别更高,并且似乎可以处理图像方向问题。还值得一提的是,它的速度非常快,仅次于 ImageIO,根据这里的这篇文章:http://nshipster.com/image-resizing/
【讨论】:
以上是关于如何裁剪 UIImage?的主要内容,如果未能解决你的问题,请参考以下文章
如何在“方面填充”模式下将 UIImageView 裁剪为新的 UIImage?