[英]How to compress of reduce the size of an image before uploading to Parse as PFFile? (Swift)
直接在手機上拍照后,我試圖將圖像文件上傳到 Parse。 但它拋出一個異常:
由於未捕獲的異常“NSInvalidArgumentException”而終止應用程序,原因:“PFFile 不能大於 10485760 字節”
這是我的代碼:
在第一個視圖控制器中:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if (segue.identifier == "getImage")
{
var svc = segue.destinationViewController as! ClothesDetail
svc.imagePassed = imageView.image
}
}
在上傳圖像的視圖控制器中:
let imageData = UIImagePNGRepresentation(imagePassed)
let imageFile = PFFile(name: "\(picName).png", data: imageData)
var userpic = PFObject(className:"UserPic")
userpic["picImage"] = imageFile`
但我仍然需要將該照片上傳到 Parse。 有沒有辦法減小圖像的大小或分辨率?
是的,您可以使用UIImageJPEGRepresentation
而不是UIImagePNGRepresentation
來減小圖像文件的大小。 您可以創建一個擴展 UIImage 如下:
Xcode 8.2 • Swift 3.0.2
extension UIImage {
enum JPEGQuality: CGFloat {
case lowest = 0
case low = 0.25
case medium = 0.5
case high = 0.75
case highest = 1
}
/// Returns the data for the specified image in JPEG format.
/// If the image object’s underlying image data has been purged, calling this function forces that data to be reloaded into memory.
/// - returns: A data object containing the JPEG data, or nil if there was a problem generating the data. This function may return nil if the image has no data or if the underlying CGImageRef contains data in an unsupported bitmap format.
func jpeg(_ quality: JPEGQuality) -> Data? {
return UIImageJPEGRepresentation(self, quality.rawValue)
}
}
編輯/更新:
Xcode 10 斯威夫特 4.2
extension UIImage {
enum JPEGQuality: CGFloat {
case lowest = 0
case low = 0.25
case medium = 0.5
case high = 0.75
case highest = 1
}
/// Returns the data for the specified image in JPEG format.
/// If the image object’s underlying image data has been purged, calling this function forces that data to be reloaded into memory.
/// - returns: A data object containing the JPEG data, or nil if there was a problem generating the data. This function may return nil if the image has no data or if the underlying CGImageRef contains data in an unsupported bitmap format.
func jpeg(_ jpegQuality: JPEGQuality) -> Data? {
return jpegData(compressionQuality: jpegQuality.rawValue)
}
}
用法:
if let imageData = image.jpeg(.lowest) {
print(imageData.count)
}
如果您想將圖像的大小限制為某個具體值,您可以執行以下操作:
import UIKit
extension UIImage {
// MARK: - UIImage+Resize
func compressTo(_ expectedSizeInMb:Int) -> UIImage? {
let sizeInBytes = expectedSizeInMb * 1024 * 1024
var needCompress:Bool = true
var imgData:Data?
var compressingValue:CGFloat = 1.0
while (needCompress && compressingValue > 0.0) {
if let data:Data = UIImageJPEGRepresentation(self, compressingValue) {
if data.count < sizeInBytes {
needCompress = false
imgData = data
} else {
compressingValue -= 0.1
}
}
}
if let data = imgData {
if (data.count < sizeInBytes) {
return UIImage(data: data)
}
}
return nil
}
}
//image compression
func resizeImage(image: UIImage) -> UIImage {
var actualHeight: Float = Float(image.size.height)
var actualWidth: Float = Float(image.size.width)
let maxHeight: Float = 300.0
let maxWidth: Float = 400.0
var imgRatio: Float = actualWidth / actualHeight
let maxRatio: Float = maxWidth / maxHeight
let compressionQuality: Float = 0.5
//50 percent compression
if actualHeight > maxHeight || actualWidth > maxWidth {
if imgRatio < maxRatio {
//adjust width according to maxHeight
imgRatio = maxHeight / actualHeight
actualWidth = imgRatio * actualWidth
actualHeight = maxHeight
}
else if imgRatio > maxRatio {
//adjust height according to maxWidth
imgRatio = maxWidth / actualWidth
actualHeight = imgRatio * actualHeight
actualWidth = maxWidth
}
else {
actualHeight = maxHeight
actualWidth = maxWidth
}
}
let rect = CGRectMake(0.0, 0.0, CGFloat(actualWidth), CGFloat(actualHeight))
UIGraphicsBeginImageContext(rect.size)
image.drawInRect(rect)
let img = UIGraphicsGetImageFromCurrentImageContext()
let imageData = UIImageJPEGRepresentation(img!,CGFloat(compressionQuality))
UIGraphicsEndImageContext()
return UIImage(data: imageData!)!
}
Jus Fixing for Xcode 7,於 2015 年 9 月 21 日測試,工作正常:
只需創建一個擴展UIImage
如下:
extension UIImage
{
var highestQualityJPEGNSData: NSData { return UIImageJPEGRepresentation(self, 1.0)! }
var highQualityJPEGNSData: NSData { return UIImageJPEGRepresentation(self, 0.75)!}
var mediumQualityJPEGNSData: NSData { return UIImageJPEGRepresentation(self, 0.5)! }
var lowQualityJPEGNSData: NSData { return UIImageJPEGRepresentation(self, 0.25)!}
var lowestQualityJPEGNSData: NSData { return UIImageJPEGRepresentation(self, 0.0)! }
}
然后你可以像這樣使用它:
let imageData = imagePassed.lowestQualityJPEGNSData
使用func jpegData(compressionQuality: CGFloat) -> Data?
如果您不需要壓縮到特定大小,則效果很好。 但是,對於某些圖像,我發現能夠壓縮到特定文件大小以下很有用。 在這種情況下, jpegData
是不可靠的,並且圖像的迭代壓縮會導致文件大小趨於平穩(並且可能非常昂貴)。 相反,我更喜歡減小 UIImage 本身的大小,然后轉換為 jpegData 並檢查減小的大小是否低於我選擇的值(在我設置的誤差范圍內)。 我根據當前文件大小與所需文件大小的比率調整壓縮步長乘數,以加速最昂貴的第一次迭代。
斯威夫特 5
extension UIImage {
func resized(withPercentage percentage: CGFloat, isOpaque: Bool = true) -> UIImage? {
let canvas = CGSize(width: size.width * percentage, height: size.height * percentage)
let format = imageRendererFormat
format.opaque = isOpaque
return UIGraphicsImageRenderer(size: canvas, format: format).image {
_ in draw(in: CGRect(origin: .zero, size: canvas))
}
}
func compress(to kb: Int, allowedMargin: CGFloat = 0.2) -> Data {
let bytes = kb * 1024
var compression: CGFloat = 1.0
let step: CGFloat = 0.05
var holderImage = self
var complete = false
while(!complete) {
if let data = holderImage.jpegData(compressionQuality: 1.0) {
let ratio = data.count / bytes
if data.count < Int(CGFloat(bytes) * (1 + allowedMargin)) {
complete = true
return data
} else {
let multiplier:CGFloat = CGFloat((ratio / 5) + 1)
compression -= (step * multiplier)
}
}
guard let newImage = holderImage.resized(withPercentage: compression) else { break }
holderImage = newImage
}
return Data()
}
}
和用法:
let data = image.compress(to: 300)
UIImage resized
擴展來自: How do I resize the UIImage to reduce upload image size
Swift 4 二進制壓縮圖像的方法
我相信現在回答這個問題已經很晚了,但這是我對優化問題的解決方案我正在使用二進制搜索來找到最佳值。 因此,例如,假設通過正常減法方法達到 62% 需要 38 次壓縮嘗試,*二元搜索**方法將在 max log(100) = 大約 7 次嘗試中達到所需的解決方案。
但是,還要通知您UIImageJPEGRepresentation
函數的行為不是線性的,尤其是當數字接近 1 時。這是屏幕截圖,我們可以看到在浮點值 > 0.995 后圖像停止壓縮。 這種行為是非常不可預測的,所以最好有一個可以處理這種情況的增量緩沖區。
這是它的代碼
extension UIImage {
func resizeToApprox(sizeInMB: Double, deltaInMB: Double = 0.2) -> Data {
let allowedSizeInBytes = Int(sizeInMB * 1024 * 1024)
let deltaInBytes = Int(deltaInMB * 1024 * 1024)
let fullResImage = UIImageJPEGRepresentation(self, 1.0)
if (fullResImage?.count)! < Int(deltaInBytes + allowedSizeInBytes) {
return fullResImage!
}
var i = 0
var left:CGFloat = 0.0, right: CGFloat = 1.0
var mid = (left + right) / 2.0
var newResImage = UIImageJPEGRepresentation(self, mid)
while (true) {
i += 1
if (i > 13) {
print("Compression ran too many times ") // ideally max should be 7 times as log(base 2) 100 = 6.6
break
}
print("mid = \(mid)")
if ((newResImage?.count)! < (allowedSizeInBytes - deltaInBytes)) {
left = mid
} else if ((newResImage?.count)! > (allowedSizeInBytes + deltaInBytes)) {
right = mid
} else {
print("loop ran \(i) times")
return newResImage!
}
mid = (left + right) / 2.0
newResImage = UIImageJPEGRepresentation(self, mid)
}
return UIImageJPEGRepresentation(self, 0.5)!
}
}
UIImage
擴展非常簡單
extension UIImage {
func resizeByByte(maxByte: Int, completion: @escaping (Data) -> Void) {
var compressQuality: CGFloat = 1
var imageData = Data()
var imageByte = UIImageJPEGRepresentation(self, 1)?.count
while imageByte! > maxByte {
imageData = UIImageJPEGRepresentation(self, compressQuality)!
imageByte = UIImageJPEGRepresentation(self, compressQuality)?.count
compressQuality -= 0.1
}
if maxByte > imageByte! {
completion(imageData)
} else {
completion(UIImageJPEGRepresentation(self, 1)!)
}
}
快速 5
extension UIImage {
func resizeByByte(maxByte: Int, completion: @escaping (Data) -> Void) {
var compressQuality: CGFloat = 1
var imageData = Data()
var imageByte = self.jpegData(compressionQuality: 1)?.count
while imageByte! > maxByte {
imageData = self.jpegData(compressionQuality: compressQuality)!
imageByte = self.jpegData(compressionQuality: compressQuality)?.count
compressQuality -= 0.1
}
if maxByte > imageByte! {
completion(imageData)
} else {
completion(self.jpegData(compressionQuality: 1)!)
}
}
}
使用
// max 300kb
image.resizeByByte(maxByte: 300000) { (resizedData) in
print("image size: \(resizedData.count)")
}
在斯威夫特
func ResizeImageFromOriginalSize(targetSize: CGSize) -> UIImage {
var actualHeight: Float = Float(self.size.height)
var actualWidth: Float = Float(self.size.width)
let maxHeight: Float = Float(targetSize.height)
let maxWidth: Float = Float(targetSize.width)
var imgRatio: Float = actualWidth / actualHeight
let maxRatio: Float = maxWidth / maxHeight
var compressionQuality: Float = 0.5
//50 percent compression
if actualHeight > maxHeight || actualWidth > maxWidth {
if imgRatio < maxRatio {
//adjust width according to maxHeight
imgRatio = maxHeight / actualHeight
actualWidth = imgRatio * actualWidth
actualHeight = maxHeight
}
else if imgRatio > maxRatio {
//adjust height according to maxWidth
imgRatio = maxWidth / actualWidth
actualHeight = imgRatio * actualHeight
actualWidth = maxWidth
}
else {
actualHeight = maxHeight
actualWidth = maxWidth
compressionQuality = 1.0
}
}
let rect = CGRect(x: 0.0, y: 0.0, width: CGFloat(actualWidth), height: CGFloat(actualHeight))
UIGraphicsBeginImageContextWithOptions(rect.size, false, CGFloat(compressionQuality))
self.draw(in: rect)
let newImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return newImage!
}
斯威夫特 4.2更新。 我創建了這個擴展來減少 UIImage 的大小。
這里有兩種方法,第一種是取百分比,第二種是將圖像縮小到 1MB。
當然,您可以將第二種方法更改為 1KB 或您想要的任何大小。
import UIKit
extension UIImage {
func resized(withPercentage percentage: CGFloat) -> UIImage? {
let canvasSize = CGSize(width: size.width * percentage, height: size.height * percentage)
UIGraphicsBeginImageContextWithOptions(canvasSize, false, scale)
defer { UIGraphicsEndImageContext() }
draw(in: CGRect(origin: .zero, size: canvasSize))
return UIGraphicsGetImageFromCurrentImageContext()
}
func resizedTo1MB() -> UIImage? {
guard let imageData = self.pngData() else { return nil }
let megaByte = 1000.0
var resizingImage = self
var imageSizeKB = Double(imageData.count) / megaByte // ! Or devide for 1024 if you need KB but not kB
while imageSizeKB > megaByte { // ! Or use 1024 if you need KB but not kB
guard let resizedImage = resizingImage.resized(withPercentage: 0.5),
let imageData = resizedImage.pngData() else { return nil }
resizingImage = resizedImage
imageSizeKB = Double(imageData.count) / megaByte // ! Or devide for 1024 if you need KB but not kB
}
return resizingImage
}
}
在 Swift 5 中,@Thiago Arreguy 回答:
extension UIImage {
var highestQualityJPEGNSData: Data { return self.jpegData(compressionQuality: 1.0)! }
var highQualityJPEGNSData: Data { return self.jpegData(compressionQuality: 0.75)!}
var mediumQualityJPEGNSData: Data { return self.jpegData(compressionQuality: 0.5)! }
var lowQualityJPEGNSData: Data { return self.jpegData(compressionQuality: 0.25)!}
var lowestQualityJPEGNSData: Data { return self.jpegData(compressionQuality: 0.0)! }
}
你可以這樣調用:
let imageData = imagePassed.lowestQualityJPEGNSData
斯威夫特 3
@leo-dabus 為 swift 3 修改了答案
extension UIImage {
var uncompressedPNGData: Data? { return UIImagePNGRepresentation(self) }
var highestQualityJPEGNSData: Data? { return UIImageJPEGRepresentation(self, 1.0) }
var highQualityJPEGNSData: Data? { return UIImageJPEGRepresentation(self, 0.75) }
var mediumQualityJPEGNSData: Data? { return UIImageJPEGRepresentation(self, 0.5) }
var lowQualityJPEGNSData: Data? { return UIImageJPEGRepresentation(self, 0.25) }
var lowestQualityJPEGNSData:Data? { return UIImageJPEGRepresentation(self, 0.0) }
}
在 Swift 4 中,我創建了這個擴展,它將接收預期的大小並嘗試達到它。
extension UIImage {
enum CompressImageErrors: Error {
case invalidExSize
case sizeImpossibleToReach
}
func compressImage(_ expectedSizeKb: Int, completion : (UIImage,CGFloat) -> Void ) throws {
let minimalCompressRate :CGFloat = 0.4 // min compressRate to be checked later
if expectedSizeKb == 0 {
throw CompressImageErrors.invalidExSize // if the size is equal to zero throws
}
let expectedSizeBytes = expectedSizeKb * 1024
let imageToBeHandled: UIImage = self
var actualHeight : CGFloat = self.size.height
var actualWidth : CGFloat = self.size.width
var maxHeight : CGFloat = 841 //A4 default size I'm thinking about a document
var maxWidth : CGFloat = 594
var imgRatio : CGFloat = actualWidth/actualHeight
let maxRatio : CGFloat = maxWidth/maxHeight
var compressionQuality : CGFloat = 1
var imageData:Data = UIImageJPEGRepresentation(imageToBeHandled, compressionQuality)!
while imageData.count > expectedSizeBytes {
if (actualHeight > maxHeight || actualWidth > maxWidth){
if(imgRatio < maxRatio){
imgRatio = maxHeight / actualHeight;
actualWidth = imgRatio * actualWidth;
actualHeight = maxHeight;
}
else if(imgRatio > maxRatio){
imgRatio = maxWidth / actualWidth;
actualHeight = imgRatio * actualHeight;
actualWidth = maxWidth;
}
else{
actualHeight = maxHeight;
actualWidth = maxWidth;
compressionQuality = 1;
}
}
let rect = CGRect(x: 0.0, y: 0.0, width: actualWidth, height: actualHeight)
UIGraphicsBeginImageContext(rect.size);
imageToBeHandled.draw(in: rect)
let img = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
if let imgData = UIImageJPEGRepresentation(img!, compressionQuality) {
if imgData.count > expectedSizeBytes {
if compressionQuality > minimalCompressRate {
compressionQuality -= 0.1
} else {
maxHeight = maxHeight * 0.9
maxWidth = maxWidth * 0.9
}
}
imageData = imgData
}
}
completion(UIImage(data: imageData)!, compressionQuality)
}
}
使用
do {
try UiImageView.image?.compressImage(100, completion: { (image, compressRatio) in
print(image.size)
imageData = UIImageJPEGRepresentation(image, compressRatio)
base64data = imageData?.base64EncodedString()
})
} catch {
print("Error")
}
我發現的最簡單的方法是
extension UIImage {
func compressImage(with maxSizeInBytes: Int ) -> UIImage? {
if maxSizeInBytes < 0 {
return nil
}
var currentImage:UIImage? = self
var divideQuality:CGFloat = 1.0
var imageData = self.jpegData(compressionQuality:divideQuality )
while imageData!.count > maxSizeInBytes {
divideQuality = divideQuality/2
imageData = currentImage?.jpegData(compressionQuality: divideQuality)
}
guard let data = imageData else {
return nil
}
currentImage = UIImage(data: data)
return UIImage(data: (currentImage?.jpegData(compressionQuality: divideQuality)) as! Data)
}
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.