ios 裁剪 UIImage 的中心正方形

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/14203951/
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 21:45:18  来源:igfitidea点击:

Cropping center square of UIImage

iosimageuiimage

提问by Uxonith

So here's where I've made it so far. I am using a UIImage captured from the camera and can crop the center square when in landscape. For some reason this doesn't translate to portrait mode as expected. I'll post my code and logs just for reference.

所以这就是我到目前为止所做的。我正在使用从相机捕获的 UIImage 并且可以在横向时裁剪中心正方形。出于某种原因,这不会按预期转换为纵向模式。我将发布我的代码和日志仅供参考。

Code:

代码:

CGRect squareRect = CGRectMake(offsetX, offsetY, newWidth, newHeight);
CGImageRef imageRef = CGImageCreateWithImageInRect([image CGImage], squareRect);
image = [UIImage imageWithCGImage:imageRef scale:1 orientation:image.imageOrientation];

Portrait results (not square):

纵向结果(非方形):

original image size: {1536, 2048}, with orientation: 3
squareRect: {{0, 256}, {1536, 1536}}
new image size: {1280, 1536}, with orientation: 3 <--- not expected

Landscape results (square):

景观结果(正方形):

original image size: {2048, 1536}, with orientation: 1
squareRect: {{256, 0}, {1536, 1536}}
new image size: {1536, 1536}, with orientation: 1

Is this a bug within CGImageCreateWithImageInRect() or am I missing something here?

这是 CGImageCreateWithImageInRect() 中的错误还是我在这里遗漏了什么?

采纳答案by Nirav Dangi

I think here would be the perfect solution!
It is NOT good idea to crop image basis on the toSize's size. It will look weird when the image resolution (size) is very large.
Following code will crop the image as per the toSize's ratio.
Improved from @BlackRider's answer.

我认为这将是完美的解决方案!
根据toSize大小裁剪图像不是一个好主意。当图像分辨率(尺寸)非常大时,它看起来会很奇怪。
下面的代码将裁剪图像具体根据toSize比率
改进了@BlackRider的回答。

- (UIImage *)imageByCroppingImage:(UIImage *)image toSize:(CGSize)size
{
    double newCropWidth, newCropHeight;

    //=== To crop more efficently =====//
    if(image.size.width < image.size.height){
         if (image.size.width < size.width) {
                 newCropWidth = size.width;
          }
          else {
                 newCropWidth = image.size.width;
          }
          newCropHeight = (newCropWidth * size.height)/size.width;
    } else {
          if (image.size.height < size.height) {
                newCropHeight = size.height;
          }
          else {
                newCropHeight = image.size.height;
          }
          newCropWidth = (newCropHeight * size.width)/size.height;
    }
    //==============================//

    double x = image.size.width/2.0 - newCropWidth/2.0;
    double y = image.size.height/2.0 - newCropHeight/2.0;

    CGRect cropRect = CGRectMake(x, y, newCropWidth, newCropHeight);
    CGImageRef imageRef = CGImageCreateWithImageInRect([image CGImage], cropRect);

    UIImage *cropped = [UIImage imageWithCGImage:imageRef];
    CGImageRelease(imageRef);

    return cropped;
}

回答by Elmundo

This works with different orientations. Both portrait and landscape orientations works correctly.

这适用于不同的方向。纵向和横向都可以正常工作。

- (UIImage *)imageByCroppingImage:(UIImage *)image toSize:(CGSize)size
{
    // not equivalent to image.size (which depends on the imageOrientation)!
    double refWidth = CGImageGetWidth(image.CGImage);
    double refHeight = CGImageGetHeight(image.CGImage);

    double x = (refWidth - size.width) / 2.0;
    double y = (refHeight - size.height) / 2.0;

    CGRect cropRect = CGRectMake(x, y, size.height, size.width);
    CGImageRef imageRef = CGImageCreateWithImageInRect([image CGImage], cropRect);

    UIImage *cropped = [UIImage imageWithCGImage:imageRef scale:0.0 orientation:self.imageOrientation];
    CGImageRelease(imageRef);

    return cropped;
}

回答by Alonzo

Based on the checked answer by Elmundo and its swift version by Imbrue, here is the same solution that automatically calculates the size of the image's center (taking orientation into account), and with error consideration:

根据 Elmundo 的检查答案和 Imbrue 的 swift 版本,这里是自动计算图像中心大小(考虑方向)并考虑错误的相同解决方案:

func cropImageToSquare(image: UIImage) -> UIImage? {
    var imageHeight = image.size.height
    var imageWidth = image.size.width

    if imageHeight > imageWidth {
        imageHeight = imageWidth
    }
    else {
        imageWidth = imageHeight
    }

    let size = CGSize(width: imageWidth, height: imageHeight)

    let refWidth : CGFloat = CGFloat(CGImageGetWidth(image.CGImage))
    let refHeight : CGFloat = CGFloat(CGImageGetHeight(image.CGImage))

    let x = (refWidth - size.width) / 2
    let y = (refHeight - size.height) / 2

    let cropRect = CGRectMake(x, y, size.height, size.width)
    if let imageRef = CGImageCreateWithImageInRect(image.CGImage, cropRect) {
        return UIImage(CGImage: imageRef, scale: 0, orientation: image.imageOrientation)
    }

   return nil
}


Swift 3version

斯威夫特 3版本

func cropImageToSquare(image: UIImage) -> UIImage? {
    var imageHeight = image.size.height
    var imageWidth = image.size.width

    if imageHeight > imageWidth {
        imageHeight = imageWidth
    }
    else {
        imageWidth = imageHeight
    }

    let size = CGSize(width: imageWidth, height: imageHeight)

    let refWidth : CGFloat = CGFloat(image.cgImage!.width)
    let refHeight : CGFloat = CGFloat(image.cgImage!.height)

    let x = (refWidth - size.width) / 2
    let y = (refHeight - size.height) / 2

    let cropRect = CGRect(x: x, y: y, width: size.height, height: size.width)
    if let imageRef = image.cgImage!.cropping(to: cropRect) {
        return UIImage(cgImage: imageRef, scale: 0, orientation: image.imageOrientation)
    }

    return nil
}

回答by Macondo2Seattle

Try something like this:

尝试这样的事情:

- (UIImage *)imageByCroppingImage:(UIImage *)image toSize:(CGSize)size
{
    double x = (image.size.width - size.width) / 2.0;
    double y = (image.size.height - size.height) / 2.0;

    CGRect cropRect = CGRectMake(x, y, size.height, size.width);
    CGImageRef imageRef = CGImageCreateWithImageInRect([image CGImage], cropRect);

    UIImage *cropped = [UIImage imageWithCGImage:imageRef];
    CGImageRelease(imageRef);

    return cropped;
}

As you see, I don't specify orientation in the call to UIImage imageWithCGImage:. I wonder if that's the problem in your code.

如您所见,我没有在调用中指定方向UIImage imageWithCGImage:。我想知道这是否是您代码中的问题。

回答by Kevin ABRIOUX

There is the swift version for the checked solution :

检查的解决方案有 swift 版本:

private func imageByCroppingImage(image : UIImage, size : CGSize) -> UIImage{
        var refWidth : CGFloat = CGFloat(CGImageGetWidth(image.CGImage))
        var refHeight : CGFloat = CGFloat(CGImageGetHeight(image.CGImage))

        var x = (refWidth - size.width) / 2
        var y = (refHeight - size.height) / 2

        let cropRect = CGRectMake(x, y, size.height, size.width)
        let imageRef = CGImageCreateWithImageInRect(image.CGImage, cropRect)

        let cropped : UIImage = UIImage(CGImage: imageRef, scale: 0, orientation: image.imageOrientation)!


        return cropped
    }

回答by srjohnhuang

improved from @Elmundo's answer

从@Elmundo 的回答中改进

+(UIImage *)getCenterMaxSquareImageByCroppingImage:(UIImage *)image withOrientation:(UIImageOrientation)imageOrientation

{
    CGSize centerSquareSize;
    double oriImgWid = CGImageGetWidth(image.CGImage);
    double oriImgHgt = CGImageGetHeight(image.CGImage);
    NSLog(@"oriImgWid==[%.1f], oriImgHgt==[%.1f]", oriImgWid, oriImgHgt);
    if(oriImgHgt <= oriImgWid) {
        centerSquareSize.width = oriImgHgt;
        centerSquareSize.height = oriImgHgt;
    }else {
        centerSquareSize.width = oriImgWid;
        centerSquareSize.height = oriImgWid;
    }

    NSLog(@"squareWid==[%.1f], squareHgt==[%.1f]", centerSquareSize.width, centerSquareSize.height);

    double x = (oriImgWid - centerSquareSize.width) / 2.0;
    double y = (oriImgHgt - centerSquareSize.height) / 2.0;
    NSLog(@"x==[%.1f], x==[%.1f]", x, y);

    CGRect cropRect = CGRectMake(x, y, centerSquareSize.height, centerSquareSize.width);
    CGImageRef imageRef = CGImageCreateWithImageInRect([image CGImage], cropRect);

    UIImage *cropped = [UIImage imageWithCGImage:imageRef scale:0.0 orientation:imageOrientation];
    CGImageRelease(imageRef);


    return cropped;
}

回答by Geoffrey

Swift 2 using UIImage Extension

Swift 2 使用 UIImage 扩展

extension UIImage
{    
    func imageByCroppingImage(size : CGSize) -> UIImage
    {
        let refWidth : CGFloat = CGFloat(CGImageGetWidth(self.CGImage))
        let refHeight : CGFloat = CGFloat(CGImageGetHeight(self.CGImage))

        let x = (refWidth - size.width) / 2
        let y = (refHeight - size.height) / 2

        let cropRect = CGRectMake(x, y, size.width, size.height)
        let imageRef = CGImageCreateWithImageInRect(self.CGImage, cropRect)

        let cropped : UIImage = UIImage(CGImage: imageRef!, scale: 0, orientation: self.imageOrientation)


        return cropped
    }
}

回答by mikeho

This is an old question, but none of the answers are really correct (even the accepted answer). The important thing to understand is that UIImageOrientation(image.imageOrientation) is actually correct, but it's definition of UPand our definition of UPare different. For us, UPis the top of the device (where the power button is). For UIImageOrientation, UPis the side oppositeof the volume control buttons. So if the device takes a picture with the volume controls down, this is UIImageOrientationUp. If you take a picture in portrait mode (with the home button down), this is UIImageOrientationLeft.

这是一个老问题,但没有一个答案是真正正确的(即使是公认的答案)。重要的是要理解UIImageOrientation( image.imageOrientation) 实际上是正确的,但它对UP的定义和我们对UP的定义不同。对我们来说,UP是设备的顶部(电源按钮所在的位置)。对于UIImageOrientationUP是音量控制按钮的对面。因此,如果设备在音量控制调低的情况下拍照,则为UIImageOrientationUp. 如果您在人像模式下(按下主页按钮)拍照,这是UIImageOrientationLeft.

So you can calculate the center in portrait, then you can apply the following transform to the image so that the crop is in the correct place.

所以你可以计算纵向的中心,然后你可以对图像应用以下变换,使裁剪在正确的位置。

- (UIImage *)cropImage:(UIImage*)image toRect:(CGRect)rect {
    CGFloat (^rad)(CGFloat) = ^CGFloat(CGFloat deg) {
        return deg / 180.0f * (CGFloat) M_PI;
    };

    // determine the orientation of the image and apply a transformation to the crop rectangle to shift it to the correct position
    CGAffineTransform rectTransform;
    switch (image.imageOrientation) {
        case UIImageOrientationLeft:
            rectTransform = CGAffineTransformTranslate(CGAffineTransformMakeRotation(rad(90)), 0, -image.size.height);
            break;
        case UIImageOrientationRight:
            rectTransform = CGAffineTransformTranslate(CGAffineTransformMakeRotation(rad(-90)), -image.size.width, 0);
            break;
        case UIImageOrientationDown:
            rectTransform = CGAffineTransformTranslate(CGAffineTransformMakeRotation(rad(-180)), -image.size.width, -image.size.height);
            break;
        default:
            rectTransform = CGAffineTransformIdentity;
    };

    // adjust the transformation scale based on the image scale
    rectTransform = CGAffineTransformScale(rectTransform, image.scale, image.scale);

    // apply the transformation to the rect to create a new, shifted rect
    CGRect transformedCropSquare = CGRectApplyAffineTransform(rect, rectTransform);
    // use the rect to crop the image
    CGImageRef imageRef = CGImageCreateWithImageInRect(image.CGImage, transformedCropSquare);
    // create a new UIImage and set the scale and orientation appropriately
    UIImage *result = [UIImage imageWithCGImage:imageRef scale:image.scale orientation:image.imageOrientation];
    // memory cleanup
    CGImageRelease(imageRef);

    return result;
}

This code shifts the crop square so that it is in the correct relative position.

此代码移动裁剪方块,使其处于正确的相对位置。

回答by jbonsant

Swift 3

斯威夫特 3

    let refWidth : CGFloat = CGFloat(image.cgImage!.width)
    let refHeight : CGFloat = CGFloat(image.cgImage!.height)

    let x = (refWidth - size.width) / 2
    let y = (refHeight - size.height) / 2

    let cropRect = CGRect(x: x, y: y, width: size.width, height: size.height)
    let imageRef = image.cgImage!.cropping(to: cropRect)

    let cropped : UIImage = UIImage(cgImage: imageRef!, scale: 0, orientation: image.imageOrientation)


    return cropped

回答by phatmann

This is based on @Alonzo's Swift 3 answer above, with the code simplified and streamlined to make it more understandable and concise. It also bails out if the image is already square.

这是基于@Alonzo's Swift 3 answer 以上,代码经过简化和精简,使其更易于理解和简洁。如果图像已经是方形的,它也会退出。

extension UIImage {
    func square() -> UIImage? {
        if size.width == size.height {
            return self
        }

        let cropWidth = min(size.width, size.height)

        let cropRect = CGRect(
            x: (size.width - cropWidth) * scale / 2.0,
            y: (size.height - cropWidth) * scale / 2.0,
            width: cropWidth * scale,
            height: cropWidth * scale
        )

        guard let imageRef = cgImage?.cropping(to: cropRect) else {
            return nil
        }

        return UIImage(cgImage: imageRef, scale: scale, orientation: imageOrientation)
    }
}