如何使用 iOS 轻松调整/优化图像大小?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/612131/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to easily resize/optimize an image size with iOS?
提问by jpm
My application is downloading a set of image files from the network, and saving them to the local iPhone disk. Some of those images are pretty big in size (widths larger than 500 pixels, for instance). Since the iPhone doesn't even have a big enough display to show the image in its original size, I'm planning on resizing the image to something a bit smaller to save on space/performance.
我的应用程序正在从网络下载一组图像文件,并将它们保存到本地 iPhone 磁盘。其中一些图像的尺寸非常大(例如,宽度大于 500 像素)。由于 iPhone 甚至没有足够大的显示屏来显示原始尺寸的图像,我计划将图像调整为更小的尺寸以节省空间/性能。
Also, some of those images are JPEGs and they are not saved as the usual 60% quality setting.
此外,其中一些图像是 JPEG,它们不会保存为通常的 60% 质量设置。
How can I resize a picture with the iPhone SDK, and how can I change the quality setting of a JPEG image?
如何使用 iPhone SDK 调整图片大小,以及如何更改 JPEG 图像的质量设置?
回答by Brad Larson
A couple of suggestions are provided as answers to this question. I had suggested the technique described in this post, with the relevant code:
提供了一些建议作为这个问题的答案。我建议使用这篇文章中描述的技术,并附上相关代码:
+ (UIImage*)imageWithImage:(UIImage*)image
scaledToSize:(CGSize)newSize;
{
UIGraphicsBeginImageContext( newSize );
[image drawInRect:CGRectMake(0,0,newSize.width,newSize.height)];
UIImage* newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return newImage;
}
As far as storage of the image, the fastest image format to use with the iPhone is PNG, because it has optimizations for that format. However, if you want to store these images as JPEGs, you can take your UIImage and do the following:
就图像存储而言,与 iPhone 一起使用的最快图像格式是 PNG,因为它对该格式进行了优化。但是,如果要将这些图像存储为 JPEG,则可以使用 UIImage 并执行以下操作:
NSData *dataForJPEGFile = UIImageJPEGRepresentation(theImage, 0.6);
This creates an NSData instance containing the raw bytes for a JPEG image at a 60% quality setting. The contents of that NSData instance can then be written to disk or cached in memory.
这将创建一个 NSData 实例,其中包含 JPEG 图像的原始字节,质量设置为 60%。然后可以将该 NSData 实例的内容写入磁盘或缓存在内存中。
回答by lostInTransit
The easiest and most straightforward way to resize your images would be this
调整图像大小的最简单和最直接的方法是
float actualHeight = image.size.height;
float actualWidth = image.size.width;
float imgRatio = actualWidth/actualHeight;
float maxRatio = 320.0/480.0;
if(imgRatio!=maxRatio){
if(imgRatio < maxRatio){
imgRatio = 480.0 / actualHeight;
actualWidth = imgRatio * actualWidth;
actualHeight = 480.0;
}
else{
imgRatio = 320.0 / actualWidth;
actualHeight = imgRatio * actualHeight;
actualWidth = 320.0;
}
}
CGRect rect = CGRectMake(0.0, 0.0, actualWidth, actualHeight);
UIGraphicsBeginImageContext(rect.size);
[image drawInRect:rect];
UIImage *img = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
回答by Pulkit Goyal
The above methods work well for small images, but when you try to resize a very large image, you will quickly run out of memory and crash the app. A much better way is to use CGImageSourceCreateThumbnailAtIndex
to resize the image without completely decoding it first.
上述方法适用于小图像,但是当您尝试调整非常大的图像时,您将很快耗尽内存并使应用程序崩溃。更好的方法是使用CGImageSourceCreateThumbnailAtIndex
调整图像大小而不先完全解码它。
If you have the path to the image you want to resize, you can use this:
如果您有要调整大小的图像的路径,则可以使用以下命令:
- (void)resizeImageAtPath:(NSString *)imagePath {
// Create the image source (from path)
CGImageSourceRef src = CGImageSourceCreateWithURL((__bridge CFURLRef) [NSURL fileURLWithPath:imagePath], NULL);
// To create image source from UIImage, use this
// NSData* pngData = UIImagePNGRepresentation(image);
// CGImageSourceRef src = CGImageSourceCreateWithData((CFDataRef)pngData, NULL);
// Create thumbnail options
CFDictionaryRef options = (__bridge CFDictionaryRef) @{
(id) kCGImageSourceCreateThumbnailWithTransform : @YES,
(id) kCGImageSourceCreateThumbnailFromImageAlways : @YES,
(id) kCGImageSourceThumbnailMaxPixelSize : @(640)
};
// Generate the thumbnail
CGImageRef thumbnail = CGImageSourceCreateThumbnailAtIndex(src, 0, options);
CFRelease(src);
// Write the thumbnail at path
CGImageWriteToFile(thumbnail, imagePath);
}
More details here.
更多细节在这里。
回答by codeburn
Best way to scale images without losing the aspect ratio (i.e. without stretching the imgage) is to use this method:
在不丢失纵横比(即不拉伸图像)的情况下缩放图像的最佳方法是使用以下方法:
//to scale images without changing aspect ratio
+ (UIImage *)scaleImage:(UIImage *)image toSize:(CGSize)newSize {
float width = newSize.width;
float height = newSize.height;
UIGraphicsBeginImageContext(newSize);
CGRect rect = CGRectMake(0, 0, width, height);
float widthRatio = image.size.width / width;
float heightRatio = image.size.height / height;
float divisor = widthRatio > heightRatio ? widthRatio : heightRatio;
width = image.size.width / divisor;
height = image.size.height / divisor;
rect.size.width = width;
rect.size.height = height;
//indent in case of width or height difference
float offset = (width - height) / 2;
if (offset > 0) {
rect.origin.y = offset;
}
else {
rect.origin.x = -offset;
}
[image drawInRect: rect];
UIImage *smallImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return smallImage;
}
Add this method to your Utility class so you can use it throughout your project, and access it like so:
将此方法添加到您的 Utility 类,以便您可以在整个项目中使用它,并像这样访问它:
xyzImageView.image = [Utility scaleImage:yourUIImage toSize:xyzImageView.frame.size];
This method takes care of scaling while maintaining aspect ratio. It also adds indents to the image in case the scaled down image has more width than height (or vice versa).
此方法在保持纵横比的同时负责缩放。如果缩小的图像宽度大于高度(反之亦然),它还会向图像添加缩进。
回答by Rog
If you have control over the server, I would strongly recommend resizing the images server side with ImageMagik. Downloading large images and resizing them on the phone is a waste of many precious resources - bandwidth, battery and memory. All of which are scarce on phones.
如果您可以控制服务器,我强烈建议您使用ImageMagik调整图像服务器端的大小。在手机上下载大图像并调整它们的大小会浪费许多宝贵的资源——带宽、电池和内存。所有这些在手机上都是稀缺的。
回答by mixel
I developed an ultimate solution for image scaling in Swift.
我为 Swift 中的图像缩放开发了一个终极解决方案。
You can use it to resize image to fill, aspect fill or aspect fit specified size.
您可以使用它来调整图像大小以填充、纵横填充或纵横匹配指定大小。
You can align image to center or any of four edges and four corners.
您可以将图像对齐到中心或四个边缘和四个角中的任何一个。
And also you can trim extra space which is added if aspect ratios of original image and target size are not equal.
如果原始图像和目标尺寸的纵横比不相等,您还可以修剪额外的空间。
enum UIImageAlignment {
case Center, Left, Top, Right, Bottom, TopLeft, BottomRight, BottomLeft, TopRight
}
enum UIImageScaleMode {
case Fill,
AspectFill,
AspectFit(UIImageAlignment)
}
extension UIImage {
func scaleImage(width width: CGFloat? = nil, height: CGFloat? = nil, scaleMode: UIImageScaleMode = .AspectFit(.Center), trim: Bool = false) -> UIImage {
let preWidthScale = width.map { let compressionQuality: CGFloat = 0.75 // adjust to change JPEG quality
if let data = UIImageJPEGRepresentation(image, compressionQuality) {
// ...
}
/ size.width }
let preHeightScale = height.map { extension UIImage {
class func resizeImage(image: UIImage, newHeight: CGFloat) -> UIImage {
let scale = newHeight / image.size.height
let newWidth = image.size.width * scale
UIGraphicsBeginImageContext(CGSize(width: newWidth, height: newHeight))
image.draw(in: CGRect(x: 0, y: 0, width: newWidth, height: newHeight))
let newImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return newImage!
}
}
/ size.height }
var widthScale = preWidthScale ?? preHeightScale ?? 1
var heightScale = preHeightScale ?? widthScale
switch scaleMode {
case .AspectFit(_):
let scale = min(widthScale, heightScale)
widthScale = scale
heightScale = scale
case .AspectFill:
let scale = max(widthScale, heightScale)
widthScale = scale
heightScale = scale
default:
break
}
let newWidth = size.width * widthScale
let newHeight = size.height * heightScale
let canvasWidth = trim ? newWidth : (width ?? newWidth)
let canvasHeight = trim ? newHeight : (height ?? newHeight)
UIGraphicsBeginImageContextWithOptions(CGSizeMake(canvasWidth, canvasHeight), false, 0)
var originX: CGFloat = 0
var originY: CGFloat = 0
switch scaleMode {
case .AspectFit(let alignment):
switch alignment {
case .Center:
originX = (canvasWidth - newWidth) / 2
originY = (canvasHeight - newHeight) / 2
case .Top:
originX = (canvasWidth - newWidth) / 2
case .Left:
originY = (canvasHeight - newHeight) / 2
case .Bottom:
originX = (canvasWidth - newWidth) / 2
originY = canvasHeight - newHeight
case .Right:
originX = canvasWidth - newWidth
originY = (canvasHeight - newHeight) / 2
case .TopLeft:
break
case .TopRight:
originX = canvasWidth - newWidth
case .BottomLeft:
originY = canvasHeight - newHeight
case .BottomRight:
originX = canvasWidth - newWidth
originY = canvasHeight - newHeight
}
default:
break
}
self.drawInRect(CGRectMake(originX, originY, newWidth, newHeight))
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return image
}
}
There are examples of applying this solution below.
下面是应用此解决方案的示例。
Gray rectangle is target site image will be resized to.
Blue circles in light blue rectangle is the image (I used circles because it's easy to see when it's scaled without preserving aspect).
Light orange color marks areas that will be trimmed if you pass trim: true
.
灰色矩形是目标站点图像将被调整大小。浅蓝色矩形中的蓝色圆圈是图像(我使用圆圈是因为在不保留外观的情况下缩放时很容易看到)。浅橙色标记区域,如果您通过,将对其进行修剪trim: true
。
Aspect fitbefore and after scaling:
缩放前后的纵横匹配:
Another example of aspect fit:
另一个方面适合的例子:
Aspect fitwith top alignment:
与顶部对齐的纵横比:
Aspect fill:
方面填充:
Fill:
填写:
I used upscaling in my examples because it's simpler to demonstrate but solution also works for downscaling as in question.
我在示例中使用了放大,因为演示起来更简单,但解决方案也适用于所讨论的缩小。
For JPEG compression you should use this :
对于 JPEG 压缩,你应该使用这个:
UIImage.resizeImage(image: yourImageName, newHeight: yourImageNewHeight)
You can check out my gistwith Xcode playground.
您可以在 Xcode playground 上查看我的要点。
回答by Alexandre Lara
For Swift 3, the below code scales the image keeping the aspect ratio. You can read more about the ImageContext in Apple's documentation:
对于 Swift 3,以下代码缩放图像保持纵横比。您可以在Apple 的文档中阅读有关 ImageContext 的更多信息:
+ (UIImage *)scaleImage:(UIImage *)image toSize:(CGSize)newSize
{
CGSize actSize = image.size;
float scale = actSize.width/actSize.height;
if (scale < 1) {
newSize.height = newSize.width/scale;
}
else {
newSize.width = newSize.height*scale;
}
UIGraphicsBeginImageContext(newSize);
[image drawInRect:CGRectMake(0, 0, newSize.width, newSize.height)];
UIImage* newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return newImage;
}
To use it, call resizeImage()
method:
要使用它,请调用resizeImage()
方法:
-(UIImage *)resizeImage :(UIImage *)theImage :(CGSize)theNewSize {
UIGraphicsBeginImageContextWithOptions(theNewSize, NO, 1.0);
[theImage drawInRect:CGRectMake(0, 0, theNewSize.width, theNewSize.height)];
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return newImage;
}
回答by Amit Shelgaonkar
you can use this code to scale image in required size.
您可以使用此代码按所需大小缩放图像。
func compressTo(toSizeInMB size: Double) -> UIImage? {
let bytes = size * 1024 * 1024
let sizeInBytes = Int(bytes)
var needCompress:Bool = true
var imgData:Data?
var compressingValue:CGFloat = 1.0
while (needCompress) {
if let resizedImage = scaleImage(byMultiplicationFactorOf: compressingValue), let data: Data = UIImageJPEGRepresentation(resizedImage, compressingValue) {
if data.count < sizeInBytes || compressingValue < 0.1 {
needCompress = false
imgData = data
} else {
compressingValue -= 0.1
}
}
}
if let data = imgData {
print("Finished with compression value of: \(compressingValue)")
return UIImage(data: data)
}
return nil
}
private func scaleImage(byMultiplicationFactorOf factor: CGFloat) -> UIImage? {
let size = CGSize(width: self.size.width*factor, height: self.size.height*factor)
UIGraphicsBeginImageContext(size)
draw(in: CGRect(x: 0, y: 0, width: size.width, height: size.height))
if let newImage: UIImage = UIGraphicsGetImageFromCurrentImageContext() {
UIGraphicsEndImageContext()
return newImage;
}
return nil
}
回答by Vincent
A problem that might occur on retina displays is that the scale of the image is set by ImageCapture or so. The resize functions above will not change that. In these cases the resize will work not properly.
在 Retina 显示器上可能出现的一个问题是图像的比例是由 ImageCapture 左右设置的。上面的调整大小功能不会改变这一点。在这些情况下,调整大小将无法正常工作。
In the code below, the scale is set to 1 (not scaled) and the returned image has the size that you would expect. This is done in the UIGraphicsBeginImageContextWithOptions
call.
在下面的代码中,比例设置为 1(未缩放)并且返回的图像具有您期望的大小。这是在UIGraphicsBeginImageContextWithOptions
调用中完成的。
回答by Harry Bloom
Adding to the slew of answers here, but I have gone for a solution which resizes by file size, rather than dimensions.
在这里添加了大量答案,但我已经寻找了一种按文件大小而不是尺寸调整大小的解决方案。
This will both reduce the dimensions and quality of the image until it reaches your given size.
这将减少图像的尺寸和质量,直到达到给定的尺寸。
##代码##Credit for scaling by size answer
按尺寸缩放答案的功劳