【问题标题】:Cropping an UIImage裁剪 UIImage
【发布时间】:2010-09-14 14:34:29
【问题描述】:

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

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

理论 1)我通过绘制到我的目标尺寸的屏幕外图像上下文来实现裁剪。因为我想要图像的中心部分,所以我将传递给drawInRectCGRect 参数设置为大于图像上下文范围的值。我希望这是 Kosher,但我是不是试图画出我不应该接触的其他记忆?

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

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

【问题讨论】:

  • 如果您尝试诊断崩溃,您应该在调试器下运行应用程序并记录崩溃时发生的情况。您甚至还没有确定是否有异常被抛出,或者由于悬空指针而获得 EXC_BAD_ACCESS。一旦你知道了这一点,那么你就可以开始制作理论了。
  • 很公平。尽管我在崩溃日志中确实有 EXC_BAD_ACCESS 消息,但我还没有在调试器下重新制作它。当我发布这个时,我假设我在我的实现中犯了一个愚蠢的错误并且有人会跳上它(比如忘记剪切路径)。
  • 尽管下面有什么值得和好的答案,在 NSHipster 上有一个很好的技术和性能调查:nshipster.com/image-resizing。我内心的纯粹主义者想使用 CIImage,但实用主义者选择了 UIKit / image context。

标签: ios objective-c cocoa-touch uiimagepickercontroller image-manipulation


【解决方案1】:

Swift 5.0 更新

public extension UIImage {
    func cropped(rect: CGRect) -> UIImage? {
        if let image = self.cgImage!.cropping(to: rect) {
            return UIImage(cgImage: image)
        } else if let image = (self.ciImage)?.cropped(to: rect) {
            return UIImage(ciImage: image)
        }
       return nil
   }
}

【讨论】:

    【解决方案2】:

    斯威夫特 5:

    extension UIImage {
        func cropped(rect: CGRect) -> UIImage? {
            guard let cgImage = cgImage else { return nil }
    
            UIGraphicsBeginImageContextWithOptions(rect.size, false, 0)
            let context = UIGraphicsGetCurrentContext()
    
            context?.translateBy(x: 0.0, y: self.size.height)
            context?.scaleBy(x: 1.0, y: -1.0)
            context?.draw(cgImage, in: CGRect(x: rect.minX, y: rect.minY, width: self.size.width, height: self.size.height), byTiling: false)
    
    
            let croppedImage = UIGraphicsGetImageFromCurrentImageContext()
            UIGraphicsEndImageContext()
    
            return croppedImage
        }
    }
    

    【讨论】:

    • 这会调整图像的大小而不是裁剪它。如果您觉得这听起来不正确,请提供有关您传入的 rect 的更多信息。
    【解决方案3】:

    下面的代码 sn-p 可能会有所帮助。

    import UIKit
    
    extension UIImage {
        func cropImage(toRect rect: CGRect) -> UIImage? {
            if let imageRef = self.cgImage?.cropping(to: rect) {
                return UIImage(cgImage: imageRef)
            }
            return nil
        }
    }
    

    【讨论】:

      【解决方案4】:

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

      【讨论】:

        【解决方案5】:

        关注@Arne 的回答。 我只是固定到类别功能。把它放在 UIImage 的类别中。

        -(UIImage*)cropImage:(CGRect)rect{
        
            UIGraphicsBeginImageContextWithOptions(rect.size, false, [self scale]);
            [self drawAtPoint:CGPointMake(-rect.origin.x, -rect.origin.y)];
            UIImage* cropped_image = UIGraphicsGetImageFromCurrentImageContext();
            UIGraphicsEndImageContext();
            return cropped_image;
        }
        

        【讨论】:

          【解决方案6】:

          这是基于Noodles 答案的更新 Swift 3 版本

          func cropping(to rect: CGRect) -> UIImage? {
          
              if let cgCrop = cgImage?.cropping(to: rect) {
                  return UIImage(cgImage: cgCrop)
              }
              else if let ciCrop = ciImage?.cropping(to: rect) {
                  return UIImage(ciImage: ciCrop)
              }
          
              return nil
          }
          

          【讨论】:

            【解决方案7】:

            Swift 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
            

            借助此桥接功能CGRectMake -> CGRect(感谢this answer@rob mayoff 回答):

             func CGRectMake(_ x: CGFloat, _ y: CGFloat, _ width: CGFloat, _ height: CGFloat) -> CGRect {
                return CGRect(x: x, y: y, width: width, height: height)
            }
            

            用法是:

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

            输出:

            【讨论】:

            • 不知道为什么它被否决,我认为这是一个很好的答案。我唯一能想到的是它不处理缩放,所以如果你有@2x/@3x 图像并且你的函数名称不匹配,它将无法工作。
            • 你需要guard第一行以防UIImage.CGImage返回nil,以及为什么在let完美工作时使用var
            • 这是正确的,但是,请仔细阅读cropping(to:) 上的文档。生成的CGImage 强烈引用了更大的CGImage,它是从中裁剪出来的。因此,如果您想保留裁剪后的图像而不需要原始图像,请查看其他解决方案。
            • 这不适用于具有方向标签的图像。裁剪区域未正确旋转。
            • @Max 这是因为 cgImage 没有方向支持。如果 UIImage 具有right,则 cgImage 应逆时针旋转 90 度,因此裁剪矩形也应旋转
            【解决方案8】:

            swift3

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

            【讨论】:

              【解决方案9】:

              https://github.com/vvbogdan/BVCropPhoto

              - (UIImage *)croppedImage { CGFloat scale = self.sourceImage.size.width / self.scrollView.contentSize.width; UIImage *finalImage = nil; CGRect targetFrame = CGRectMake((self.scrollView.contentInset.left + self.scrollView.contentOffset.x) * 比例, (self.scrollView.contentInset.top + self.scrollView.contentOffset.y) * 比例, self.cropSize.width * 比例, self.cropSize.height * 比例); CGImageRef contextImage = CGImageCreateWithImageInRect([[self imageWithRotation:self.sourceImage] CGImage], targetFrame); 如果(上下文图像!= NULL){ finalImage = [UIImage imageWithCGImage:contextImage 规模:self.sourceImage.scale 方向:UIImageOrientationUp]; CGImageRelease(contextImage); } 返回最终图像; } - (UIImage *)imageWithRotation:(UIImage *)image { if (image.imageOrientation == UIImageOrientationUp) 返回图片; CGAffineTransform 变换 = CGAffineTransformIdentity; 开关(image.imageOrientation){ 案例 UIImageOrientationDown: 案例 UIImageOrientationDownMirrored: 变换 = CGAffineTransformTranslate(变换,image.size.width,image.size.height); 变换 = CGAffineTransformRotate(变换,M_PI); 休息; 案例 UIImageOrientationLeft: 案例 UIImageOrientationLeftMirrored: 变换 = CGAffineTransformTranslate(变换, image.size.width, 0); 变换 = CGAffineTransformRotate(变换,M_PI_2); 休息; 案例 UIImageOrientationRight: 案例 UIImageOrientationRightMirrored: 变换 = CGAffineTransformTranslate(变换, 0, image.size.height); 变换 = CGAffineTransformRotate(变换,-M_PI_2); 休息; 案例 UIImageOrientationUp: 案例 UIImageOrientationUpMirrored: 休息; } 开关(image.imageOrientation){ 案例 UIImageOrientationUpMirrored: 案例 UIImageOrientationDownMirrored: 变换 = CGAffineTransformTranslate(变换, image.size.width, 0); 变换 = CGAffineTransformScale(变换, -1, 1); 休息; 案例 UIImageOrientationLeftMirrored: 案例 UIImageOrientationRightMirrored: 变换 = CGAffineTransformTranslate(变换, image.size.height, 0); 变换 = CGAffineTransformScale(变换, -1, 1); 休息; 案例 UIImageOrientationUp: 案例 UIImageOrientationDown: 案例 UIImageOrientationLeft: 案例 UIImageOrientationRight: 休息; } // 现在我们将底层的 CGImage 绘制到一个新的上下文中,应用变换 // 上面计算。 CGContextRef ctx = CGBitmapContextCreate(NULL, image.size.width, image.size.height, CGImageGetBitsPerComponent(image.CGImage), 0, CGImageGetColorSpace(image.CGImage), CGImageGetBitmapInfo(image.CGImage)); CGContextConcatCTM(ctx, 变换); 开关(image.imageOrientation){ 案例 UIImageOrientationLeft: 案例 UIImageOrientationLeftMirrored: 案例 UIImageOrientationRight: 案例 UIImageOrientationRightMirrored: // 嘎嘎... CGContextDrawImage(ctx, CGRectMake(0, 0, image.size.height, image.size.width), image.CGImage); 休息; 默认: CGContextDrawImage(ctx, CGRectMake(0, 0, image.size.width, image.size.height), image.CGImage); 休息; } // 现在我们只是从绘图上下文中创建一个新的 UIImage CGImageRef cgimg = CGBitmapContextCreateImage(ctx); UIImage *img = [UIImage imageWithCGImage:cgimg]; CGContextRelease(ctx); CGImageRelease(cgimg); 返回图片; }

              【讨论】:

                【解决方案10】:

                您可以创建一个 UIImage 类别并在任何需要的地方使用它。基于 HitScans 响应和下面的 cmets。

                @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
                

                你可以这样使用它:

                UIImage *imageToCrop = <yourImageToCrop>;
                CGRect cropRect = <areaYouWantToCrop>;   
                
                //for example
                //CGRectMake(0, 40, 320, 100);
                
                UIImage *croppedImage = [imageToCrop crop:cropRect];
                

                【讨论】:

                • 不应该[[UIScreen mainScreen] scale] 只是self.scale?图片的比例可能与屏幕的比例不同。
                • 嗨,我试过你的答案,它给了我No visible @interface for 'UIImage' declares the selector 'crop',即使我将 .h 和 .m 类别文件放在我的项目中并在我使用的类别中导入了 .h .有什么想法吗?
                • 已修复。我错过了将方法头放在 UIImage+Crop.h 文件中。
                • 我想将图像裁剪成圆形。这样我们就只能看到圆形路径,其他路径保持透明。
                • 你可以使用 CGImageCreateWithMask 或 CGImageCreateWithMaskingColors 代替 CGImageCreateWithImageInRect
                【解决方案11】:

                Swift 2.0 更新(CIImage 兼容性)

                Maxim's Answer 扩展,但如果您的图像也基于CIImage,也可以使用。

                public extension UIImage {
                    func imageByCroppingToRect(rect: CGRect) -> UIImage? {
                        if let image = CGImageCreateWithImageInRect(self.CGImage, rect) {
                            return UIImage(CGImage: image)
                        } else if let image = (self.CIImage)?.imageByCroppingToRect(rect) {
                            return UIImage(CIImage: image)
                        }
                       return nil
                   }
                }
                

                【讨论】:

                  【解决方案12】:

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

                  image.CGImage 可以返回 nil,如果 UIImageCIImage 支持,如果您使用 CIFilter 创建此图像就是这种情况。

                  在这种情况下,您可能必须在新的上下文中绘制图像,然后返回该图像(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;
                  }
                  

                  【讨论】:

                  • 这正是我所需要的,适用于所有场景,因此比任何其他解决方案都好!
                  • 第一行的image.size应该是rect.size UIGraphicsBeginImageContextWithOptions(rect.size, false, image.scale);
                  • 谢谢布雷特,肯定就在那里。我更新了代码以包含此修复程序。
                  • 为什么是 -rect.origin.x 而不仅仅是 rect.origin.x?
                  • 因为我们在裁剪;换句话说,'rect' 参数是说“我希望新图像的左上角从 [10, 10] 开始”。为此,我们绘制图像,使 [10, 10] 位于新图像的原点。
                  【解决方案13】:

                  我使用下面的方法。

                    -(UIImage *)getNeedImageFrom:(UIImage*)image cropRect:(CGRect)rect
                    {
                      CGSize cropSize = rect.size;
                      CGFloat widthScale =  
                      image.size.width/self.imageViewOriginal.bounds.size.width;
                      CGFloat heightScale = 
                      image.size.height/self.imageViewOriginal.bounds.size.height;
                      cropSize = CGSizeMake(rect.size.width*widthScale,  
                      rect.size.height*heightScale);
                      CGPoint  pointCrop = CGPointMake(rect.origin.x*widthScale, 
                      rect.origin.y*heightScale);
                      rect = CGRectMake(pointCrop.x, pointCrop.y, cropSize.width, 
                      cropSize.height);
                      CGImageRef subImage = CGImageCreateWithImageInRect(image.CGImage, rect);
                      UIImage *croppedImage = [UIImage imageWithCGImage:subImage];
                      CGImageRelease(subImage);
                      return croppedImage;
                  

                  }

                  【讨论】:

                    【解决方案14】:

                    在 iOS9.2SDK 上,我使用下面的方法将帧从 UIView 转换为 UIImage

                    -(UIImage *)getNeedImageFrom:(UIImage*)image cropRect:(CGRect)rect
                    {
                      CGSize cropSize = rect.size;
                      CGFloat widthScale = image.size.width/self.imageViewOriginal.bounds.size.width;
                      CGFloat heightScale = image.size.height/self.imageViewOriginal.bounds.size.height;
                      cropSize = CGSizeMake(rect.size.width*widthScale, 
                                  rect.size.height*heightScale);
                      CGPoint pointCrop = CGPointMake(rect.origin.x*widthScale,
                                 rect.origin.y*heightScale);
                      rect = CGRectMake(pointCrop.x, pointCrop.y, cropSize.width, cropSize.height);
                      CGImageRef subImage = CGImageCreateWithImageInRect(image.CGImage, rect);
                      UIImage *croppedImage = [UIImage imageWithCGImage:subImage];
                      CGImageRelease(subImage);
                    
                      return croppedImage;
                    }
                    

                    【讨论】:

                      【解决方案15】:

                      看起来有点奇怪,但效果很好,并且考虑到了图像方向:

                      var image:UIImage = ...
                      
                      let img = CIImage(image: image)!.imageByCroppingToRect(rect)
                      image = UIImage(CIImage: img, scale: 1, orientation: image.imageOrientation)
                      

                      【讨论】:

                      • 但它显然打破了规模
                      【解决方案16】:

                      Swift 扩展

                      extension UIImage {
                          func crop(var rect: CGRect) -> UIImage {
                              rect.origin.x*=self.scale
                              rect.origin.y*=self.scale
                              rect.size.width*=self.scale
                              rect.size.height*=self.scale
                      
                              let imageRef = CGImageCreateWithImageInRect(self.CGImage, rect)
                              let image = UIImage(CGImage: imageRef, scale: self.scale, orientation: self.imageOrientation)!
                              return image
                          }
                      }
                      

                      【讨论】:

                        【解决方案17】:

                        Swift 中裁剪 UIImage 的最佳解决方案,在精度、像素缩放方面...:

                        private func squareCropImageToSideLength(let sourceImage: UIImage,
                            let sideLength: CGFloat) -> UIImage {
                                // input size comes from image
                                let inputSize: CGSize = sourceImage.size
                        
                                // round up side length to avoid fractional output size
                                let sideLength: CGFloat = ceil(sideLength)
                        
                                // output size has sideLength for both dimensions
                                let outputSize: CGSize = CGSizeMake(sideLength, sideLength)
                        
                                // calculate scale so that smaller dimension fits sideLength
                                let scale: CGFloat = max(sideLength / inputSize.width,
                                    sideLength / inputSize.height)
                        
                                // scaling the image with this scale results in this output size
                                let scaledInputSize: CGSize = CGSizeMake(inputSize.width * scale,
                                    inputSize.height * scale)
                        
                                // determine point in center of "canvas"
                                let center: CGPoint = CGPointMake(outputSize.width/2.0,
                                    outputSize.height/2.0)
                        
                                // calculate drawing rect relative to output Size
                                let outputRect: CGRect = CGRectMake(center.x - scaledInputSize.width/2.0,
                                    center.y - scaledInputSize.height/2.0,
                                    scaledInputSize.width,
                                    scaledInputSize.height)
                        
                                // begin a new bitmap context, scale 0 takes display scale
                                UIGraphicsBeginImageContextWithOptions(outputSize, true, 0)
                        
                                // optional: set the interpolation quality.
                                // For this you need to grab the underlying CGContext
                                let ctx: CGContextRef = UIGraphicsGetCurrentContext()
                                CGContextSetInterpolationQuality(ctx, kCGInterpolationHigh)
                        
                                // draw the source image into the calculated rect
                                sourceImage.drawInRect(outputRect)
                        
                                // create new image from bitmap context
                                let outImage: UIImage = UIGraphicsGetImageFromCurrentImageContext()
                        
                                // clean up
                                UIGraphicsEndImageContext()
                        
                                // pass back new image
                                return outImage
                        }
                        

                        调用该函数的指令:

                        let image: UIImage = UIImage(named: "Image.jpg")!
                        let squareImage: UIImage = self.squareCropImageToSideLength(image, sideLength: 320)
                        self.myUIImageView.image = squareImage
                        

                        注意:the initial source code inspiration written in Objective-C has been found on "Cocoanetics" blog.

                        【讨论】:

                          【解决方案18】:
                           (UIImage *)squareImageWithImage:(UIImage *)image scaledToSize:(CGSize)newSize {
                              double ratio;
                              double delta;
                              CGPoint offset;
                          
                              //make a new square size, that is the resized imaged width
                              CGSize sz = CGSizeMake(newSize.width, newSize.width);
                          
                              //figure out if the picture is landscape or portrait, then
                              //calculate scale factor and offset
                              if (image.size.width > image.size.height) {
                                  ratio = newSize.width / image.size.width;
                                  delta = (ratio*image.size.width - ratio*image.size.height);
                                  offset = CGPointMake(delta/2, 0);
                              } else {
                                  ratio = newSize.width / image.size.height;
                                  delta = (ratio*image.size.height - ratio*image.size.width);
                                  offset = CGPointMake(0, delta/2);
                              }
                          
                              //make the final clipping rect based on the calculated values
                              CGRect clipRect = CGRectMake(-offset.x, -offset.y,
                                                           (ratio * image.size.width) + delta,
                                                           (ratio * image.size.height) + delta);
                          
                          
                              //start a new context, with scale factor 0.0 so retina displays get
                              //high quality image
                              if ([[UIScreen mainScreen] respondsToSelector:@selector(scale)]) {
                                  UIGraphicsBeginImageContextWithOptions(sz, YES, 0.0);
                              } else {
                                  UIGraphicsBeginImageContext(sz);
                              }
                              UIRectClip(clipRect);
                              [image drawInRect:clipRect];
                              UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
                              UIGraphicsEndImageContext();
                          
                              return newImage;
                          }
                          

                          【讨论】:

                            【解决方案19】:

                            这里没有一个答案可以 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;
                            }
                            

                            【讨论】:

                            • 为什么这个答案没有得到更多的支持?我尝试了上述所有答案并遇到了大量问题(特别是从 UIImage 转换为 CIImage 并丢失了正确的转换数据)。这是唯一对我有用的!
                            • 谢谢@Aggressor,很高兴这对你有用。我的答案仅在一个月前发布,而其中许多答案已经存在 5 年了。我猜这解释了缺乏可比较的赞成票。
                            • 我正在使用它在图像之间滑动过滤器(就像 snapchat 一样)并且我遇到了轻微的缩放问题。您的代码中是否有任何内容可以建议我查看可能导致此缩放错误的原因?我在这里发布了使用您的代码的解决方案:stackoverflow.com/questions/23319497/…
                            • 一个很有用的功能,谢谢。任何提示应该修改哪些内容以将焦点集中在图像的中心部分?
                            • 这应该是答案。
                            【解决方案20】:

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

                            原始回复:

                            我将在其他地方复制/粘贴我对同一问题的回答:

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

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

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

                            【讨论】:

                            • imageOrientation 设置为不向上时,CGImage 相对于UIImage 旋转,并且裁剪矩形将是错误的。您可以相应地旋转裁剪矩形,但新创建的UIImage 将具有imageOrientation,因此裁剪后的CGImage 将在其中旋转。
                            • 我想指出,在视网膜显示器上,您需要使用此方法将您的cropRect 宽度和高度加倍。只是我遇到的东西。
                            • 让它在任何方向使用:[UIImage imageWithCGImage:imageRef scale:largeImage.scale orientation:largeImage.imageOrientation];
                            • 你应该添加对 Retina 的支持!
                            • @NicosKaralis 只会将其旋转到正确的方向。但是,您从图像中裁剪出来的区域仍然是错误的。
                            【解决方案21】:

                            我对其他解决方案不满意,因为它们要么绘制多次(使用了不必要的功率),要么存在方向问题。这是我从 UIImage * 图像中用于缩放正方形croppedImage 的内容。

                            CGFloat minimumSide = fminf(image.size.width, image.size.height);
                            CGFloat finalSquareSize = 600.;
                            
                            //create new drawing context for right size
                            CGRect rect = CGRectMake(0, 0, finalSquareSize, finalSquareSize);
                            CGFloat scalingRatio = 640.0/minimumSide;
                            UIGraphicsBeginImageContext(rect.size);
                            
                            //draw
                            [image drawInRect:CGRectMake((minimumSide - photo.size.width)*scalingRatio/2., (minimumSide - photo.size.height)*scalingRatio/2., photo.size.width*scalingRatio, photo.size.height*scalingRatio)];
                            
                            UIImage *croppedImage = UIGraphicsGetImageFromCurrentImageContext();
                            
                            UIGraphicsEndImageContext();
                            

                            【讨论】:

                              【解决方案22】:

                              这是我的 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;
                              }
                              

                              【讨论】:

                              • 警告implicit declaration of function 'rad' is invalid in c99 可以通过替换来删除:rad(90), rad(-90), rad(-180) -> M_PI_2, -M_PI_2, -_M_PI
                              • 糟糕,抱歉。将 rad() 实用函数添加到源 sn-p。
                              • contains undefined reference for architecture armv7 我错过了图书馆吗? CoreGraphics 已导入。
                              • @SergiiRudchenko “所有方向都经过了彻底的测试。” - 这是否包括镜像方向?
                              • @BobSpryn 不,你没有错过图书馆。虽然我无法解释错误的含义,但像 SoundBlaster 建议的那样替换 rad() 也可以修复此错误。
                              【解决方案23】:
                              - (UIImage *)getSubImage:(CGRect) rect{
                                  CGImageRef subImageRef = CGImageCreateWithImageInRect(self.CGImage, rect);
                                  CGRect smallBounds = CGRectMake(rect.origin.x, rect.origin.y, CGImageGetWidth(subImageRef), CGImageGetHeight(subImageRef));
                              
                                  UIGraphicsBeginImageContext(smallBounds.size);
                                  CGContextRef context = UIGraphicsGetCurrentContext();
                                  CGContextDrawImage(context, smallBounds, subImageRef);
                                  UIImage* smallImg = [UIImage imageWithCGImage:subImageRef];
                                  UIGraphicsEndImageContext();
                              
                                  return smallImg;
                              }
                              

                              【讨论】:

                                【解决方案24】:

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

                                【讨论】:

                                • 你可以在这里杀死条件并且总是通过self.scale 倍增,不是吗?
                                • 你说得对,迈克尔,但我认为上述解决方案在非视网膜设备上要快一点,因为它只进行一次检查,而不是四次乘法加一次赋值。在视网膜设备上,它只是一个不必要的布尔检查,所以这是个人偏好或目标适合度的问题。
                                • 在启用 ARC 的情况下使用 CGImageRelease(imageRef); 是否正确?
                                • 实际上 4 次乘法可能比评估一个条件要快 :) 在现代 CPU 上可以很好地优化乘法,而只能预测条件。但你是对的,这是一个偏好问题。由于可读性提高,我更喜欢更短/更简单的代码。
                                【解决方案25】:
                                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);
                                

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

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

                                1. 原始图像的高度高于其宽度(哇,到目前为止并不奇怪!)
                                2. CGImageCreateWithImageInRect 方法在它自己的世界中想象的图像不是真正的肖像而是风景(这也是为什么如果你不使用 imageWithCGImage 构造函数中的方向参数,它将显示为 180 旋转) .
                                3. 因此,您应该将其想象为风景, (0, 0) 位置是图像的右上角。

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

                                【讨论】:

                                  猜你喜欢
                                  • 2018-07-08
                                  • 2011-02-07
                                  • 1970-01-01
                                  • 1970-01-01
                                  • 1970-01-01
                                  • 2011-09-25
                                  • 1970-01-01
                                  • 1970-01-01
                                  相关资源
                                  最近更新 更多