2009-03-02 192 views
184

现在,我一直抨击我的脸,直到现在,尽管我总是觉得自己处于启示边缘,但我无法实现自己的目标。UIImage:调整大小,然后裁剪

我在设计的概念阶段提前想到,从iPhone的相机或库中抓取图像,将其缩小到指定高度,使用相当于方面填充选项的UIImageView(完全在代码中),然后裁剪任何不符合通过CGRect。

从相机或图书馆获取原始图像,是微不足道的。我对其他两个步骤证明有多困难感到震惊。

附图显示了我正在努力实现的目标。请人友善地握住我的手?我迄今为止发现的每个代码示例都似乎粉碎了图像,颠倒过来,看起来像废话,画出了界限,或者只是不能正常工作。

+7

的链接断开,以你的形象。 – 2012-09-12 09:58:44

+1

有一件事,如果我们感到震惊的是为什么苹果没有把它添加到UIImagePickerController - 它太沉闷的困难;-) – Tim 2015-03-15 12:23:16

回答

244

我需要同样的东西 - 在我的情况下,选择适合缩放后的尺寸,然后裁剪每一端以适应宽度。 (我在横向工作,所以可能没有注意到纵向模式中的任何缺陷。)这里是我的代码 - 它是UIImage上的一个关于categeory的部分。我的代码中的目标大小始终设置为设备的全屏大小。

@implementation UIImage (Extras) 

#pragma mark - 
#pragma mark Scale and crop image 

- (UIImage*)imageByScalingAndCroppingForSize:(CGSize)targetSize 
{ 
    UIImage *sourceImage = self; 
    UIImage *newImage = nil;  
    CGSize imageSize = sourceImage.size; 
    CGFloat width = imageSize.width; 
    CGFloat height = imageSize.height; 
    CGFloat targetWidth = targetSize.width; 
    CGFloat targetHeight = targetSize.height; 
    CGFloat scaleFactor = 0.0; 
    CGFloat scaledWidth = targetWidth; 
    CGFloat scaledHeight = targetHeight; 
    CGPoint thumbnailPoint = CGPointMake(0.0,0.0); 

    if (CGSizeEqualToSize(imageSize, targetSize) == NO) 
    { 
     CGFloat widthFactor = targetWidth/width; 
     CGFloat heightFactor = targetHeight/height; 

     if (widthFactor > heightFactor) 
     { 
      scaleFactor = widthFactor; // scale to fit height 
     } 
     else 
     { 
      scaleFactor = heightFactor; // scale to fit width 
     } 

     scaledWidth = width * scaleFactor; 
     scaledHeight = height * scaleFactor; 

     // center the image 
     if (widthFactor > heightFactor) 
     { 
      thumbnailPoint.y = (targetHeight - scaledHeight) * 0.5; 
     } 
     else 
     { 
      if (widthFactor < heightFactor) 
      { 
       thumbnailPoint.x = (targetWidth - scaledWidth) * 0.5; 
      } 
     } 
    } 

    UIGraphicsBeginImageContext(targetSize); // this will crop 

    CGRect thumbnailRect = CGRectZero; 
    thumbnailRect.origin = thumbnailPoint; 
    thumbnailRect.size.width = scaledWidth; 
    thumbnailRect.size.height = scaledHeight; 

    [sourceImage drawInRect:thumbnailRect]; 

    newImage = UIGraphicsGetImageFromCurrentImageContext(); 

    if(newImage == nil) 
    { 
     NSLog(@"could not scale image"); 
    } 

    //pop the context to get back to the default 
    UIGraphicsEndImageContext(); 

    return newImage; 
} 
+1

好奇地它在模拟器中工作,但在设备上我收到ExecBadAccess .. – 2009-09-02 08:17:04

+0

在这里相同。一旦移动到设备,我收到间歇性错误。下面的代码由Brad Lawson提供,工作方式类似,没有错误。 – mmc 2009-09-18 23:33:12

+5

由于UIImage函数不是线程安全的,所以发生错误的exec问题。这就是为什么它sometemes崩溃,有时不 – Erik 2010-03-16 12:59:55

77

较旧的帖子包含调整UIImage大小的方法的代码。有关部分如下:

+ (UIImage*)imageWithImage:(UIImage*)image 
       scaledToSize:(CGSize)newSize; 
{ 
    UIGraphicsBeginImageContext(newSize); 
    [image drawInRect:CGRectMake(0,0,newSize.width,newSize.height)]; 
    UIImage* newImage = UIGraphicsGetImageFromCurrentImageContext(); 
    UIGraphicsEndImageContext(); 

    return newImage; 
} 

至于裁剪推移,我相信,如果你改变使用不同大小的缩放比上下文的方法,你的结果图像应该被裁剪到指定边界的背景下。

+3

不知道为什么这会修复图像的方向,但它确实,因此它解决了我的问题与相机没有在`originalImage`中返回正确的方向。谢谢。 – Brenden 2009-08-13 21:48:50

+9

我发现调整视网膜设备上的图像大小显得模糊。为了保持清晰,我将第一行修改为以下内容:`UIGraphicsBeginImageContextWithOptions(newSize,1.0f,0.0f);`。 (在这里解释:http://stackoverflow.com/questions/4334233/how-to-capture-uiview-to-uiimage-without-loss-of-quality-on-retina-display) – johngraham 2012-01-25 19:18:13

+0

它旋转图像 - 但doesn不适当地裁剪!为什么它有这么多票? – Dejell 2013-01-21 08:10:13

17
+ (UIImage *)scaleImage:(UIImage *)image toSize:(CGSize)targetSize { 
    //If scaleFactor is not touched, no scaling will occur  
    CGFloat scaleFactor = 1.0; 

    //Deciding which factor to use to scale the image (factor = targetSize/imageSize) 
    if (image.size.width > targetSize.width || image.size.height > targetSize.height) 
     if (!((scaleFactor = (targetSize.width/image.size.width)) > (targetSize.height/image.size.height))) //scale to fit width, or 
      scaleFactor = targetSize.height/image.size.height; // scale to fit heigth. 

    UIGraphicsBeginImageContext(targetSize); 

    //Creating the rect where the scaled image is drawn in 
    CGRect rect = CGRectMake((targetSize.width - image.size.width * scaleFactor)/2, 
          (targetSize.height - image.size.height * scaleFactor)/2, 
          image.size.width * scaleFactor, image.size.height * scaleFactor); 

    //Draw the image into the rect 
    [image drawInRect:rect]; 

    //Saving the image, ending image context 
    UIImage *scaledImage = UIGraphicsGetImageFromCurrentImageContext(); 
    UIGraphicsEndImageContext(); 

    return scaledImage; 
} 

我提出这个我来到这个周围。她不是美人吗? ;)

7

在这里,你去。这是一个完美的;-)

编辑:见下面的评论 - “不能与某些图像工作,失败:CGContextSetInterpolationQuality:无效的情况下为0x0错误”

// Resizes the image according to the given content mode, taking into account the image's orientation 
- (UIImage *)resizedImageWithContentMode:(UIViewContentMode)contentMode imageToScale:(UIImage*)imageToScale bounds:(CGSize)bounds interpolationQuality:(CGInterpolationQuality)quality { 
    //Get the size we want to scale it to 
    CGFloat horizontalRatio = bounds.width/imageToScale.size.width; 
    CGFloat verticalRatio = bounds.height/imageToScale.size.height; 
    CGFloat ratio; 

    switch (contentMode) { 
     case UIViewContentModeScaleAspectFill: 
      ratio = MAX(horizontalRatio, verticalRatio); 
      break; 

     case UIViewContentModeScaleAspectFit: 
      ratio = MIN(horizontalRatio, verticalRatio); 
      break; 

     default: 
      [NSException raise:NSInvalidArgumentException format:@"Unsupported content mode: %d", contentMode]; 
    } 

    //...and here it is 
    CGSize newSize = CGSizeMake(imageToScale.size.width * ratio, imageToScale.size.height * ratio); 


    //start scaling it 
    CGRect newRect = CGRectIntegral(CGRectMake(0, 0, newSize.width, newSize.height)); 
    CGImageRef imageRef = imageToScale.CGImage; 
    CGContextRef bitmap = CGBitmapContextCreate(NULL, 
               newRect.size.width, 
               newRect.size.height, 
               CGImageGetBitsPerComponent(imageRef), 
               0, 
               CGImageGetColorSpace(imageRef), 
               CGImageGetBitmapInfo(imageRef)); 

    CGContextSetInterpolationQuality(bitmap, quality); 

    // Draw into the context; this scales the image 
    CGContextDrawImage(bitmap, newRect, imageRef); 

    // Get the resized image from the context and a UIImage 
    CGImageRef newImageRef = CGBitmapContextCreateImage(bitmap); 
    UIImage *newImage = [UIImage imageWithCGImage:newImageRef]; 

    // Clean up 
    CGContextRelease(bitmap); 
    CGImageRelease(newImageRef); 

    return newImage; 
} 
2

我修改布拉德·拉尔森的代码。它将在方面填充图像。

-(UIImage*) scaleAndCropToSize:(CGSize)newSize; 
{ 
    float ratio = self.size.width/self.size.height; 

    UIGraphicsBeginImageContext(newSize); 

    if (ratio > 1) { 
     CGFloat newWidth = ratio * newSize.width; 
     CGFloat newHeight = newSize.height; 
     CGFloat leftMargin = (newWidth - newHeight)/2; 
     [self drawInRect:CGRectMake(-leftMargin, 0, newWidth, newHeight)]; 
    } 
    else { 
     CGFloat newWidth = newSize.width; 
     CGFloat newHeight = newSize.height/ratio; 
     CGFloat topMargin = (newHeight - newWidth)/2; 
     [self drawInRect:CGRectMake(0, -topMargin, newSize.width, newSize.height/ratio)]; 
    } 

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

    return newImage; 
} 
1

以下简单代码适用于我。

[imageView setContentMode:UIViewContentModeScaleAspectFill]; 
[imageView setClipsToBounds:YES]; 
1
scrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(0.0,0.0,ScreenWidth,ScreenHeigth)]; 
    [scrollView setBackgroundColor:[UIColor blackColor]]; 
    [scrollView setDelegate:self]; 
    [scrollView setShowsHorizontalScrollIndicator:NO]; 
    [scrollView setShowsVerticalScrollIndicator:NO]; 
    [scrollView setMaximumZoomScale:2.0]; 
    image=[image scaleToSize:CGSizeMake(ScreenWidth, ScreenHeigth)]; 
    imageView = [[UIImageView alloc] initWithImage:image]; 
    UIImageView* imageViewBk = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"background.png"]]; 
    [self.view addSubview:imageViewBk]; 
    CGRect rect; 
    rect.origin.x=0; 
    rect.origin.y=0; 
    rect.size.width = image.size.width; 
    rect.size.height = image.size.height; 

    [imageView setFrame:rect]; 

    [scrollView setContentSize:[imageView frame].size]; 
    [scrollView setMinimumZoomScale:[scrollView frame].size.width/[imageView frame].size.width]; 
    [scrollView setZoomScale:[scrollView minimumZoomScale]]; 
    [scrollView addSubview:imageView]; 

    [[self view] addSubview:scrollView]; 

,那么你可以通过这个

float zoomScale = 1.0/[scrollView zoomScale]; 
CGRect rect; 
rect.origin.x = [scrollView contentOffset].x * zoomScale; 
rect.origin.y = [scrollView contentOffset].y * zoomScale; 
rect.size.width = [scrollView bounds].size.width * zoomScale; 
rect.size.height = [scrollView bounds].size.height * zoomScale; 

CGImageRef cr = CGImageCreateWithImageInRect([[imageView image] CGImage], rect); 

UIImage *cropped = [UIImage imageWithCGImage:cr]; 

CGImageRelease(cr); 
2

Xamarin采取截屏图像。IOS版本为accepted answer on how to resize and then crop UIImage (Aspect Fill)是 下面

public static UIImage ScaleAndCropImage(UIImage sourceImage, SizeF targetSize) 
    { 
     var imageSize = sourceImage.Size; 
     UIImage newImage = null; 
     var width = imageSize.Width; 
     var height = imageSize.Height; 
     var targetWidth = targetSize.Width; 
     var targetHeight = targetSize.Height; 
     var scaleFactor = 0.0f; 
     var scaledWidth = targetWidth; 
     var scaledHeight = targetHeight; 
     var thumbnailPoint = PointF.Empty; 
     if (imageSize != targetSize) 
     { 
      var widthFactor = targetWidth/width; 
      var heightFactor = targetHeight/height; 
      if (widthFactor > heightFactor) 
      { 
       scaleFactor = widthFactor;// scale to fit height 
      } 
      else 
      { 
       scaleFactor = heightFactor;// scale to fit width 
      } 
      scaledWidth = width * scaleFactor; 
      scaledHeight = height * scaleFactor; 
      // center the image 
      if (widthFactor > heightFactor) 
      { 
       thumbnailPoint.Y = (targetHeight - scaledHeight) * 0.5f; 
      } 
      else 
      { 
       if (widthFactor < heightFactor) 
       { 
        thumbnailPoint.X = (targetWidth - scaledWidth) * 0.5f; 
       } 
      } 
     } 
     UIGraphics.BeginImageContextWithOptions(targetSize, false, 0.0f); 
     var thumbnailRect = new RectangleF(thumbnailPoint, new SizeF(scaledWidth, scaledHeight)); 
     sourceImage.Draw(thumbnailRect); 
     newImage = UIGraphics.GetImageFromCurrentImageContext(); 
     if (newImage == null) 
     { 
      Console.WriteLine("could not scale image"); 
     } 
     //pop the context to get back to the default 
     UIGraphics.EndImageContext(); 

     return newImage; 
    } 
0
- (UIImage*)imageScale:(CGFloat)scaleFactor cropForSize:(CGSize)targetSize 
{ 
    targetSize = !targetSize.width?self.size:targetSize; 
    UIGraphicsBeginImageContext(targetSize); // this will crop 

    CGRect thumbnailRect = CGRectZero; 

    thumbnailRect.size.width = targetSize.width*scaleFactor; 
    thumbnailRect.size.height = targetSize.height*scaleFactor; 
    CGFloat xOffset = (targetSize.width- thumbnailRect.size.width)/2; 
    CGFloat yOffset = (targetSize.height- thumbnailRect.size.height)/2; 
    thumbnailRect.origin = CGPointMake(xOffset,yOffset); 

    [self drawInRect:thumbnailRect]; 

    UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext(); 

    if(newImage == nil) 
    { 
     NSLog(@"could not scale image"); 
    } 

    UIGraphicsEndImageContext(); 

    return newImage; 
} 

下面工作的例子: 左图像 - (原点图像);与比例×右图像

enter image description here

如果要缩放图像,但保留其框架(比例),调用方法是这样的:

[yourImage imageScale:2.0f cropForSize:CGSizeZero]; 
0

斯威夫特版本:

static func imageWithImage(image:UIImage, newSize:CGSize) ->UIImage { 
    UIGraphicsBeginImageContextWithOptions(newSize, true, UIScreen.mainScreen().scale); 
    image.drawInRect(CGRectMake(0, 0, newSize.width, newSize.height)) 

    let newImage = UIGraphicsGetImageFromCurrentImageContext(); 

    UIGraphicsEndImageContext(); 
    return newImage 
} 
0

这个问题似乎已经被解决了,但是当我寻求一个我可以更容易理解(并且用Swift编写)的解决方案时,我到达了这个地方(也发布到:How to crop the UIImage?


我希望能够从基于的纵横比的区域,并且规模裁剪到基于一个外边界程度的尺寸。这里是我的变化:

import AVFoundation 
import ImageIO 

class Image { 

    class func crop(image:UIImage, crop 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)。

一个警告是,这种方法有效地假定你正在绘制的图像大于图像上下文。我没有测试过这个,但我认为你可以使用这段代码来处理裁剪/缩放,但是明确地将scale参数定义为前述的比例参数。默认情况下,UIKit会根据屏幕分辨率应用一个乘数。

最后,应该指出的是,这种UIKit方法比CoreGraphics/Quartz和Core Image方法更高级,并且似乎可以处理图像方向问题。这也是值得一提的是,它是相当快的,第二ImageIO,根据这个职位在这里:http://nshipster.com/image-resizing/

2

我转换Sam Wirch's guide to swift它运作良好,虽然有一些非常轻微的“压扁”在最后的形象,我无法解决。

func resizedCroppedImage(image: UIImage, newSize:CGSize) -> UIImage { 
    var ratio: CGFloat = 0 
    var delta: CGFloat = 0 
    var offset = CGPointZero 
    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) 
    } 
    let clipRect = CGRectMake(-offset.x, -offset.y, (ratio * image.size.width) + delta, (ratio * image.size.height) + delta) 
    UIGraphicsBeginImageContextWithOptions(newSize, true, 0.0) 
    UIRectClip(clipRect) 
    image.drawInRect(clipRect) 
    let newImage = UIGraphicsGetImageFromCurrentImageContext() 
    UIGraphicsEndImageContext() 
    return newImage 
} 

如果有人想要客观的c版本,它在他的网站上。

6

这是Jane Sales在Swift中的一个答案。干杯!

public func resizeImage(image: UIImage, size: CGSize) -> UIImage? { 
    var returnImage: UIImage? 

    var scaleFactor: CGFloat = 1.0 
    var scaledWidth = size.width 
    var scaledHeight = size.height 
    var thumbnailPoint = CGPointMake(0, 0) 

    if !CGSizeEqualToSize(image.size, size) { 
     let widthFactor = size.width/image.size.width 
     let heightFactor = size.height/image.size.height 

     if widthFactor > heightFactor { 
      scaleFactor = widthFactor 
     } else { 
      scaleFactor = heightFactor 
     } 

     scaledWidth = image.size.width * scaleFactor 
     scaledHeight = image.size.height * scaleFactor 

     if widthFactor > heightFactor { 
      thumbnailPoint.y = (size.height - scaledHeight) * 0.5 
     } else if widthFactor < heightFactor { 
      thumbnailPoint.x = (size.width - scaledWidth) * 0.5 
     } 
    } 

    UIGraphicsBeginImageContextWithOptions(size, true, 0) 

    var thumbnailRect = CGRectZero 
    thumbnailRect.origin = thumbnailPoint 
    thumbnailRect.size.width = scaledWidth 
    thumbnailRect.size.height = scaledHeight 

    image.drawInRect(thumbnailRect) 
    returnImage = UIGraphicsGetImageFromCurrentImageContext() 

    UIGraphicsEndImageContext() 

    return returnImage 
} 
0

这里是一个斯威夫特3版本的发布Sam Wirch's guide to swift由William T.

extension UIImage { 

    static func resizedCroppedImage(image: UIImage, newSize:CGSize) -> UIImage? { 
     var ratio: CGFloat = 0 
     var delta: CGFloat = 0 
     var offset = CGPoint.zero 

     if image.size.width > image.size.height { 
      ratio = newSize.width/image.size.width 
      delta = (ratio * image.size.width) - (ratio * image.size.height) 
      offset = CGPoint(x: delta/2, y: 0) 
     } else { 
      ratio = newSize.width/image.size.height 
      delta = (ratio * image.size.height) - (ratio * image.size.width) 
      offset = CGPoint(x: 0, y: delta/2) 
     } 

     let clipRect = CGRect(x: -offset.x, y: -offset.y, width: (ratio * image.size.width) + delta, height: (ratio * image.size.height) + delta) 
     UIGraphicsBeginImageContextWithOptions(newSize, true, 0.0) 
     UIRectClip(clipRect) 
     image.draw(in: clipRect) 
     let newImage = UIGraphicsGetImageFromCurrentImageContext() 
     UIGraphicsEndImageContext() 

     return newImage 
    } 

}