Próbowałem przesłać plik obrazu do Parse po zrobieniu zdjęcia bezpośrednio na telefonie. Ale rzuca wyjątek:
Zakończenie aplikacji z powodu nieprzechwyconego wyjątku „NSInvalidArgumentException”, przyczyna: „PFFile nie może być większa niż 10485760 bajtów”
Oto mój kod:
W pierwszym widoku kontroler:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if (segue.identifier == "getImage")
{
var svc = segue.destinationViewController as! ClothesDetail
svc.imagePassed = imageView.image
}
}
W widoku kontrolera, który przesyła obraz:
let imageData = UIImagePNGRepresentation(imagePassed)
let imageFile = PFFile(name: "\(picName).png", data: imageData)
var userpic = PFObject(className:"UserPic")
userpic["picImage"] = imageFile`
Ale nadal muszę przesłać to zdjęcie do Parse. Czy istnieje sposób na zmniejszenie rozmiaru lub rozdzielczości obrazu?
ios
swift
parse-platform
uiimage
elvislkm
źródło
źródło
Odpowiedzi:
Tak, możesz użyć
UIImageJPEGRepresentation
zamiast,UIImagePNGRepresentation
aby zmniejszyć rozmiar pliku obrazu. Możesz po prostu utworzyć rozszerzenie UIImage w następujący sposób: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) } }
edytuj / aktualizuj:
Xcode 10 Swift 4.2.0 Łatka
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) } }
Stosowanie:
if let imageData = image.jpeg(.lowest) { print(imageData.count) }
źródło
UIImageJPEGRepresentation(yourImage, 1.0)
zamiast po prostu pisać.jp
i pozwolić xcode na autouzupełnianie metody za Ciebie. to samo dla wyliczenia kompresji.whatever
.Jeśli chcesz ograniczyć rozmiar obrazu do określonej wartości, możesz wykonać następujące czynności:
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 } }
źródło
if let data = bits.representation(using: .jpeg, properties: [.compressionFactor:compressingValue])
//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!)! }
źródło
Jus Fixing for Xcode 7, testowane 21.09.2015 i działa poprawnie:
Po prostu utwórz rozszerzenie
UIImage
w następujący sposób: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)! } }
Następnie możesz go użyć w ten sposób:
let imageData = imagePassed.lowestQualityJPEGNSData
źródło
Podejście binarne Swift 4 do kompresji obrazu
Uważam, że jest już dość późno, aby odpowiedzieć na to pytanie, ale oto moje rozwiązanie na pytanie, które jest zoptymalizowane. Używam wyszukiwania binarnego, aby znaleźć optymalną wartość. Na przykład, powiedzmy, że przy normalnym podejściu odejmowania osiągnięcie 62% wymagałoby 38 prób kompresji, podejście * Wyszukiwanie binarne ** zapewniłoby wymagane rozwiązanie w maksymalnym logu (100) = około 7 prób.
Jednak chciałbym również poinformować, że
UIImageJPEGRepresentation
funkcja nie zachowuje się liniowo, zwłaszcza gdy liczba zbliża się do 1. Oto zrzut ekranu, na którym widać, że obraz przestaje się kompresować po tym, jak wartość float> 0,995. Zachowanie jest dość nieprzewidywalne, więc lepiej mieć bufor delta, który obsługiwałby takie przypadki.Oto kod
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)! } }
źródło
for i in 0...13
.To bardzo proste z
UIImage
rozszerzeniemextension 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)!) } }
używać
// max 300kb image.resizeByByte(maxByte: 300000) { (resizedData) in print("image size: \(resizedData.count)") }
źródło
Aktualizacja Swift 4.2 . Stworzyłem to rozszerzenie, aby zmniejszyć rozmiar UIImage.
Tutaj masz dwie metody, pierwsza pobiera procent, a druga zmniejsza obraz do 1 MB.
Oczywiście możesz zmienić drugą metodę na 1KB lub dowolny rozmiar.
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 } }
źródło
W Swift 5 jako @Thiago Arreguy odpowiedz:
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)! } }
Możesz zadzwonić w ten sposób:
let imageData = imagePassed.lowestQualityJPEGNSData
źródło
W Swift
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! }
źródło
Używanie
func jpegData(compressionQuality: CGFloat) -> Data?
działa dobrze, jeśli nie musisz kompresować do określonego rozmiaru. Jednak w przypadku niektórych obrazów przydatna jest możliwość kompresji pliku poniżej określonego rozmiaru. W takim przypadkujpegData
jest zawodne, a iteracyjna kompresja obrazu skutkuje wyhamowaniem rozmiaru pliku (i może być naprawdę kosztowna). Zamiast tego wolę zmniejszyć rozmiar samego UIImage, a następnie przekonwertować go na jpegData i sprawdzić, czy zmniejszony rozmiar jest poniżej wybranej przeze mnie wartości (w granicach ustawionego przeze mnie marginesu błędu). Dostosowuję mnożnik kroku kompresji na podstawie stosunku rozmiaru bieżącego pliku do żądanego rozmiaru, aby przyspieszyć pierwsze iteracje, które są najdroższe.Szybki 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() } }
I zastosowanie:
let data = image.compress(to: 300)
resized
Rozszerzenie UIImage pochodzi z: Jak zmienić rozmiar UIImage, aby zmniejszyć rozmiar przesyłanego obrazuźródło
Szybki 3
Odpowiedź @ leo-dabus poprawiona pod kątem szybkiej 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) } }
źródło
W Swift 4 stworzyłem to rozszerzenie, które otrzyma oczekiwany rozmiar i spróbuje do niego dotrzeć.
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) } }
Używać
do { try UiImageView.image?.compressImage(100, completion: { (image, compressRatio) in print(image.size) imageData = UIImageJPEGRepresentation(image, compressRatio) base64data = imageData?.base64EncodedString() }) } catch { print("Error") }
źródło