【问题标题】:How to crop the UIImage?如何裁剪 UIImage?
【发布时间】:2011-02-07 18:53:20
【问题描述】:

我开发了一个应用程序,在该应用程序中我使用其像素处理图像,但在该图像处理中需要大量时间。因此我想裁剪 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 的中间部分?????????

【问题讨论】:

    标签: iphone 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 你确定吗? stackoverflow.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/

            【讨论】:

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