ios 裁剪 UIImage

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/158914/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-30 15:18:13  来源:igfitidea点击:

Cropping an UIImage

iosobjective-ccocoa-touchuiimagepickercontrollerimage-manipulation

提问by Jablair

I've got some code that resizes an image so I can get a scaled chunk of the center of the image - I use this to take a UIImageand return a small, square representation of an image, similar to what's seen in the album view of the Photos app. (I know I could use a UIImageViewand adjust the crop mode to achieve the same results, but these images are sometimes displayed in UIWebViews).

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

I've started to notice some crashes in this code and I'm a bit stumped. I've got two different theories and I'm wondering if either is on-base.

我已经开始注意到这段代码中有一些崩溃,我有点难住了。我有两种不同的理论,我想知道是否有一种是基于基础的。

Theory 1) I achieve the cropping by drawing into an offscreen image context of my target size. Since I want the center portion of the image, I set the CGRectargument passed to drawInRectto something that's larger than the bounds of my image context. I was hoping this was Kosher, but am I instead attempting to draw over other memory that I shouldn't be touching?

理论 1)我通过绘制到目标尺寸的屏幕外图像上下文来实现裁剪。因为我想要图像的中心部分,所以我将CGRect传递给的参数设置为drawInRect大于图像上下文边界的东西。我希望这是犹太洁食,但我是否试图勾勒出我不应该触及的其他记忆?

Theory 2) I'm doing all of this in a background thread. I know there are portions of UIKit that are restricted to the main thread. I was assuming / hoping that drawing to an offscreen view wasn't one of these. Am I wrong?

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

(Oh, how I miss NSImage's drawInRect:fromRect:operation:fraction:method.)

(哦,我多么想念NSImage's drawInRect:fromRect:operation:fraction:方法。)

回答by HitScan

Update 2014-05-28: I wrote this when iOS 3 or so was the hot new thing, I'm certain there are better ways to do this by now, possibly built-in. As many people have mentioned, this method doesn't take rotation into account; read some additional answers and spread some upvote love around to keep the responses to this question helpful for everyone.

2014 年 5 月 28 日更新:当 iOS 3 左右是热门新事物时,我写了这篇文章,我确信现在有更好的方法可以做到这一点,可能是内置的。正如很多人提到的,这种方法没有考虑轮换;阅读一些其他答案并传播一些赞誉,以使对这个问题的回答对每个人都有帮助。

Original response:

原回复:

I'm going to copy/paste my response to the same question elsewhere:

我将把我对同一问题的回答复制/粘贴到别处:

There isn't a simple class method to do this, but there is a function that you can use to get the desired results: CGImageCreateWithImageInRect(CGImageRef, CGRect)will help you out.

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

Here's a short example using it:

这是一个使用它的简短示例:

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

回答by Arne

To crop retina images while keeping the same scale and orientation, use the following method in a UIImage category (iOS 4.0 and above):

要在保持相同比例和方向的同时裁剪视网膜图像,请在 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;
}

回答by Vilém Kurz

You can make a UIImage category and use it wherever you need. Based on HitScans response and comments bellow it.

您可以创建一个 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

You can use it this way:

你可以这样使用它:

UIImage *imageToCrop = <yourImageToCrop>;
CGRect cropRect = <areaYouWantToCrop>;   

//for example
//CGRectMake(0, 40, 320, 100);

UIImage *croppedImage = [imageToCrop crop:cropRect];

回答by Maxim Shoustin

Swift 3 version

斯威夫特 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

enter image description here

在此处输入图片说明

with help of this bridge function CGRectMake-> CGRect(credits to this answeranswered by @rob mayoff):

在此桥接功能的帮助下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)
}

The usage is:

用法是:

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)
    )
}

Output:

输出:

enter image description here

在此处输入图片说明

回答by Sergii Rudchenko

Here is my UIImage crop implementation which obeys the imageOrientation property. All orientations were thoroughly tested.

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

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

回答by colinta

Heads up: all these answers assume a CGImage-backed image object.

注意:所有这些答案都假设有一个CGImage支持的图像对象。

image.CGImagecan return nil, if the UIImageis backed by a CIImage, which would be the case if you created this image using a CIFilter.

image.CGImage如果UIImage由 a 支持,则可以返回 nil,如果CIImage您使用CIFilter.

In that case, you might have to draw the image in a new context, and return that image (slow).

在这种情况下,您可能需要在新的上下文中绘制图像,然后返回该图像(slow)。

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

回答by awolf

None of the answers here handle all of the scale and rotation issues 100% correctly. Here's a synthesis of everything said so far, up-to-date as of iOS7/8. It's meant to be included as a method in a category on UIImage.

这里的答案都没有 100% 正确地处理所有缩放和旋转问题。这是迄今为止所有内容的综合,截至 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;
}

回答by Mark Leonard

Swift version of awolf's answer, which worked for me:

awolf的答案的Swift 版本,对我有用:

public extension UIImage {
    func croppedImage(inRect rect: CGRect) -> UIImage {
        let rad: (Double) -> CGFloat = { deg in
            return CGFloat(deg / 180.0 * .pi)
        }
        var rectTransform: CGAffineTransform
        switch imageOrientation {
        case .left:
            let rotation = CGAffineTransform(rotationAngle: rad(90))
            rectTransform = rotation.translatedBy(x: 0, y: -size.height)
        case .right:
            let rotation = CGAffineTransform(rotationAngle: rad(-90))
            rectTransform = rotation.translatedBy(x: -size.width, y: 0)
        case .down:
            let rotation = CGAffineTransform(rotationAngle: rad(-180))
            rectTransform = rotation.translatedBy(x: -size.width, y: -size.height)
        default:
            rectTransform = .identity
        }
        rectTransform = rectTransform.scaledBy(x: scale, y: scale)
        let transformedRect = rect.applying(rectTransform)
        let imageRef = cgImage!.cropping(to: transformedRect)!
        let result = UIImage(cgImage: imageRef, scale: scale, orientation: imageOrientation)
        return result
    }
}

回答by Jordan

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);

Most of the responses I've seen only deals with a position of (0, 0) for (x, y). Ok that's one case but I'd like my cropping operation to be centered. What took me a while to figure out is the line following the WTF comment.

我见过的大多数响应只处理 (x, y) 的 (0, 0) 位置。好的,这是一种情况,但我希望我的裁剪操作居中。我花了一段时间才弄清楚是 WTF 评论后面的那一行。

Let's take the case of an image captured with a portrait orientation:

让我们以纵向拍摄的图像为例:

  1. The original image height is higher than its width (Woo, no surprise so far!)
  2. The image that the CGImageCreateWithImageInRect method imagines in its own world is not really a portrait though but a landscape (That is also why if you don't use the orientation argument in the imageWithCGImage constructor, it will show up as 180 rotated).
  3. So, you should kind of imagine that it is a landscape, the (0, 0) position being the top right corner of the image.
  1. 原始图像的高度高于其宽度(哇,到目前为止并不奇怪!)
  2. CGImageCreateWithImageInRect 方法在它自己的世界中想象的图像并不是真正的肖像,而是风景(这也是为什么如果你不在 imageWithCGImage 构造函数中使用方向参数,它会显示为 180 旋转)。
  3. 所以,你应该想象它是一个风景,(0, 0) 位置是图像的右上角。

Hope it makes sense! If it does not, try different values you'll see that the logic is inverted when it comes to choosing the right x, y, width, and height for your cropRect.

希望这是有道理的!如果没有,请尝试不同的值,您会发现在为cropRect 选择正确的x、y、宽度和高度时,逻辑是颠倒的。

回答by neoneye

swift3

迅捷3

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)
    }
}