如何裁剪 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?的主要内容,如果未能解决你的问题,请参考以下文章

如何获得正确的区域来裁剪 UIImage?

如何在 ImageView 中裁剪 UIImage

像IOS 5一样裁剪UIImage时如何制作视图效果?

如何在“方面填充”模式下将 UIImageView 裁剪为新的 UIImage?

如何在“方面填充”模式下将 UIImageView 裁剪为新的 UIImage?

像 camscanner 一样裁剪 UIImage