UIImage: 调整大小,然后裁剪

几天来,我一直在用我的脸撞击这张脸,尽管我经常感到自己正处于启示的边缘,但我就是无法实现我的目标。

在我设计的概念阶段,我提前想到,从 iPhone 的摄像头或库中抓取一张图片,把它缩小到一个指定的高度,使用一个相当于 UIImageView 的 填充物选项(完全在代码中)的函数,然后使用 掉了,任何不符合通过的 CGRect 的东西,这将是一件微不足道的事情。

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

所附的图片显示了我正在努力实现的目标。有没有人能握住我的手?到目前为止,我发现的每一个代码示例似乎都破坏了图像,颠倒了,看起来像垃圾,画出了界限,或者其他方面只是不能正常工作。

123062 次浏览

一篇较早的文章包含了一个调整 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;
}

就裁剪而言,我相信如果您改变方法,使用不同的大小来缩放比例,您的结果图像应该被裁剪到上下文的边界。

我需要同样的东西-在我的情况下,选择尺寸适合一次缩放,然后裁剪每一端,以适应其余的宽度。(我从事的是景观工作,所以可能没有注意到任何肖像模式的缺陷。)这是我的代码-它是 UIImage 类别的一部分。代码中的目标大小总是设置为设备的全屏大小。

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

有一大段代码是关于图片大小调整和其他操作的。当我试图弄明白如何调整图像大小时,我想到了这个问题。 Http://vocaro.com/trevor/blog/2009/10/12/resize-a-uiimage-the-right-way/

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

我推荐这个,她是不是很漂亮? ;)

给你。这个很完美; -)

编辑: 见下面的评论-“无法处理某些图像,失败: 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;
}

我修改了布拉德拉森的代码。它将方面填充在给定的正确的形象。

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

下面这些简单的代码对我很有用。

[imageView setContentMode:UIViewContentModeScaleAspectFill];
[imageView setClipsToBounds:YES];
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);

用于 接受关于如何调整大小然后裁剪 UIImage (方面填充)的答案的 Xamarin.iOS 版本是 下面

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

下面是工作的例子: 左图-(原始图像) ; 右图,比例尺 x2

enter image description here

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

[yourImage imageScale:2.0f cropForSize:CGSizeZero];

快速版:

    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
}

这个问题似乎已经被搁置了,但是在我寻找一个更容易理解的解决方案(并且是用 Swift 写的)的过程中,我发现了这个问题(也发布到: 如何裁剪 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
}
}

有一些事情我感到困惑,裁剪和调整大小的单独关注。裁剪是通过传递给 draInRect 的 rect 的原点来处理的,缩放是通过 size 部分来处理的。在我的情况下,我需要关联的大小裁剪直线对源,我的输出直线相同的纵横比。然后输出/输入缩放因子,这需要应用到 draRect (传递给 draInRect)。

需要注意的是,这种方法有效地假设您正在绘制的图像大于图像上下文。我还没有对此进行测试,但是我认为您可以使用这段代码来处理裁剪/缩放,但是可以明确地将 scale 参数定义为前面提到的 scale 参数。默认情况下,UIKit 应用基于屏幕分辨率的乘法器。

最后,应该指出的是,这种 UIKit 方法比 CoreGraphics/Quartz 和 Core Image 方法的级别更高,似乎可以处理图像定位问题。同样值得一提的是,根据这篇文章: http://nshipster.com/image-resizing/,它非常快,仅次于 ImageIO

我转换了 Sam Wirch 的斯威夫特指南,它工作得很好,虽然有一些非常轻微的“挤压”在最后的图像,我不能解决。

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 版本,就在他的网站上。

这是简 · 赛尔斯在《雨燕》中的回答,干杯!

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
}

这里是一个由威廉 T 发布的 Sam Wirch 的斯威夫特指南的 Swift 3版本。

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
}


}

我发现 Evgenii Kanvets 发布的 Swift 3并没有统一缩放图像。

下面是我的 Swift 4版本的不会挤压图像的函数:

static func resizedCroppedImage(image: UIImage, newSize:CGSize) -> UIImage? {


// This function returns a newImage, based on image
// - image is scaled uniformaly to fit into a rect of size newSize
// - if the newSize rect is of a different aspect ratio from the source image
//     the new image is cropped to be in the center of the source image
//     (the excess source image is removed)


var ratio: CGFloat = 0
var delta: CGFloat = 0
var drawRect = CGRect()


if newSize.width > newSize.height {


ratio = newSize.width / image.size.width
delta = (ratio * image.size.height) - newSize.height
drawRect = CGRect(x: 0, y: -delta / 2, width: newSize.width, height: newSize.height + delta)


} else {


ratio = newSize.height / image.size.height
delta = (ratio * image.size.width) - newSize.width
drawRect = CGRect(x: -delta / 2, y: 0, width: newSize.width + delta, height: newSize.height)


}


UIGraphicsBeginImageContextWithOptions(newSize, true, 0.0)
image.draw(in: drawRect)
let newImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()


return newImage
}