2009-03-02 139 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 
    } 

}