34

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

代码:

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

纵向结果(非方形):

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

景观结果(正方形):

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

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

4

19 回答 19

28

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.

- (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;
}
于 2015-05-26T07:22:18.613 回答
25

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

- (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;
}
于 2015-01-06T06:52:25.523 回答
23

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

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
}

斯威夫特 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
}
于 2015-11-23T12:25:53.100 回答
13

尝试这样的事情:

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

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

于 2013-01-07T21:29:36.807 回答
8

检查解决方案有快速版本:

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
    }
于 2015-09-23T16:02:34.183 回答
5

从@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;
}
于 2015-04-15T07:02:21.040 回答
5

这是基于上面@Alonzo 的 Swift 3 答案,简化和简化了代码,使其更易于理解和简洁。如果图像已经是方形的,它也会退出。

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)
    }
}
于 2017-06-08T14:20:58.790 回答
4

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

因此,您可以计算纵向中心,然后您可以将以下变换应用于图像,以便裁剪在正确的位置。

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

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

于 2015-06-15T18:07:44.360 回答
4

斯威夫特 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
于 2016-09-15T15:34:43.897 回答
4

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
    }
}
于 2016-03-17T19:56:40.450 回答
1

从 UIImageView 获得帮助怎么样

+ (UIImage *)laodSquareImage:(UIImage *)image withDiameter:(CGFloat)diameter  
{
    CGRect frame = CGRectMake(0.0f, 0.0f, diameter, diameter);
    UIImageView *imageView = [[UIImageView alloc] initWithFrame:frame];
    imageView.contentMode = UIViewContentModeScaleAspectFill;
    imageView.clipsToBounds = YES;
    imageView.image = image;
    CALayer *layer = imageView.layer;


    layer.masksToBounds = YES;
    UIGraphicsBeginImageContextWithOptions(imageView.bounds.size,NO, [UIScreen mainScreen].scale);
    CGContextRef context = UIGraphicsGetCurrentContext();
    [layer renderInContext:context];


    UIImage *roundedImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();

    return roundedImage;
}

您也可以通过仅添加一条线来制作图像圆圈。

layer.cornerRadius =MAX( imageView.frame.size.height,imageView.frame.size.width)/2;
于 2017-06-13T08:55:33.963 回答
1

斯威夫特 5

extension UIImage {

    var squared: UIImage? {
        guard let cgImage = cgImage else {
            return nil
        }

        let length = min(cgImage.width, cgImage.height)

        let x = cgImage.width / 2 - length / 2
        let y = cgImage.height / 2 - length / 2
        let cropRect = CGRect(x: x, y: y, width: length, height: length)

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

        return UIImage(cgImage: croppedCGImage, scale: scale, orientation: imageOrientation)
    }
}
于 2021-06-22T13:52:53.013 回答
1

已经有很多解决方案,但我想发布我的:

  extension UIImage {

    var cgImageWidth: Int { return cgImage?.width ?? 0 }
    var cgImageheight: Int { return cgImage?.height ?? 0 }
    var blance: CGFloat { return min(size.width, size.height)}
    var blanceSize: CGSize { return CGSize(width: blance, height: blance) }
    var blanceRect: CGRect { return CGRect(origin: .zero, size: blanceSize) }

    var roundedImage: UIImage? {
        UIGraphicsBeginImageContextWithOptions(blanceSize, false, scale)
        defer { UIGraphicsEndImageContext() }
        guard let cgImage = cgImage?.cropping(to: CGRect(origin: CGPoint(x: max(0, CGFloat(cgImageWidth) - blance)/2.0, y: max(0, CGFloat(cgImageheight) - blance)/2.0), size: blanceSize)) else { return nil }
        UIBezierPath(ovalIn: blanceRect).addClip()

        UIImage(cgImage: cgImage, scale: 1.0, orientation: self.imageOrientation).draw(in: blanceRect)
        return UIGraphicsGetImageFromCurrentImageContext()
    }

}
于 2017-07-20T06:53:58.440 回答
1

Xamarin:

UIImage ImageByCroppingImage(UIImage image, CGSize size)
{
    double newCropWidth, newCropHeight;

    if (image.Size.Width < image.Size.Height)
    {
        newCropWidth = image.Size.Width < size.Width ? size.Width : image.Size.Width;
        newCropHeight = (newCropWidth * size.Height) / size.Width;
    }
    else
    {
        newCropHeight = image.Size.Height < size.Height ? size.Height : 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 = new CGRect(x, y, newCropWidth, newCropHeight);
    var imageRef = image.CGImage.WithImageInRect(cropRect);

    var cropped = new UIImage(imageRef);

    return cropped;
}
于 2018-02-27T04:23:13.657 回答
1

Swift 5 最干净的版本。

func cropImage() -> UIImage? {
    let size = min(self.size.width, self.size.height)
    UIGraphicsBeginImageContextWithOptions(CGSize(width: size, height: size), true, 1.0)
    defer { UIGraphicsEndImageContext() }
    self.draw(in: CGRect(x: (size - self.size.width) / 2.0,
                          y: (size - self.size.height) / 2.0,
                          width: self.size.width,
                          height: self.size.height))
    return UIGraphicsGetImageFromCurrentImageContext()
}
于 2020-08-12T03:38:24.180 回答
0

这是根据给定尺寸从中心裁剪图像的 Swift 5 版本。

extension UIImage {

     func imageByCroppingImage(size : CGSize) -> UIImage{
        let refWidth : CGFloat = CGFloat(self.cgImage!.width)
        let refHeight : CGFloat = CGFloat(self.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 = self.cgImage!.cropping(to: cropRect)
        let cropped : UIImage = UIImage(cgImage: imageRef!, scale: 0, orientation: self.imageOrientation)
        return cropped
    }
    }
于 2020-04-10T22:44:32.940 回答
0

我正在处理以下问题

    extension UIImage {

    var cgImageWidth: Int { return cgImage?.width ?? 0 }
    var cgImageheight: Int { return cgImage?.height ?? 0 }
    var blance: CGFloat { return min(size.width, size.height)}
    var blanceSize: CGSize { return CGSize(width: blance, height: blance) }
    var blanceRect: CGRect { return CGRect(origin: .zero, size: blanceSize) }

    var roundedImage: UIImage? {
        UIGraphicsBeginImageContextWithOptions(blanceSize, false, scale)
        defer { UIGraphicsEndImageContext() }
        guard let cgImage = cgImage?.cropping(to: CGRect(origin: CGPoint(x: max(0, CGFloat(cgImageWidth) - blance)/2.0, y: max(0, CGFloat(cgImageheight) - blance)/2.0), size: blanceSize)) else { return nil }
        UIBezierPath(ovalIn: blanceRect).addClip()

        UIImage(cgImage: cgImage, scale: 1.0, orientation: self.imageOrientation).draw(in: blanceRect)
        return UIGraphicsGetImageFromCurrentImageContext()
    }

}
于 2017-07-20T07:07:33.687 回答
0

基于 Swift 2 扩展的解决方案。由@Geoffrey 和@Nirav Dangi 即兴创作。请注意,当 newCropWidth 和 newCropHeight 小于给定的宽度或高度时,我们将它们设置为图像的宽度或高度。

extension UIImage {
    func imageByCroppingImage(size: CGSize) -> UIImage {
        let newCropWidth, newCropHeight : CGFloat;

        if(self.size.width < self.size.height) {
            if (self.size.width < size.width) {
                newCropWidth = self.size.width;
            }
            else {
                newCropWidth = size.width;
            }
            newCropHeight = (newCropWidth * size.height)/size.width;
        } else {
            if (self.size.height < size.height) {
                newCropHeight = self.size.height;
            }
            else {
                newCropHeight = size.height;
            }
            newCropWidth = (newCropHeight * size.width)/size.height;
        }

        let x = self.size.width / 2 - newCropWidth / 2;
        let y = self.size.height / 2 - newCropHeight / 2;

        let cropRect = CGRectMake(x, y, newCropWidth, newCropHeight);
        let imageRef = CGImageCreateWithImageInRect(self.CGImage, cropRect);

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

        return croppedImage;
    }
}
于 2016-05-12T04:56:43.593 回答
-3

我能够通过以下方式完成它:

   UIImage *thisImage = [[UIImage alloc] initWithContentsOfFile:imagePath];
                double x = (thisImage.size.width )/2.0;
                double y = (thisImage.size.height)/2.0;
                mediaImage = [self imageByCropping:thisImage toRect:CGRectMake(x, y, 60, 60)];
                [thisImage release];
于 2013-02-25T17:36:33.300 回答