J'essayais de télécharger un fichier image sur Parse après avoir pris une photo directement sur le téléphone. Mais cela lève une exception:
Arrêt de l'application en raison d'une exception non interceptée 'NSInvalidArgumentException', raison: 'PFFile ne peut pas être plus grand que 10485760 octets'
Voici mon code:
Dans le contrôleur de première vue:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if (segue.identifier == "getImage")
{
var svc = segue.destinationViewController as! ClothesDetail
svc.imagePassed = imageView.image
}
}
Dans le contrôleur de vue qui télécharge l'image:
let imageData = UIImagePNGRepresentation(imagePassed)
let imageFile = PFFile(name: "\(picName).png", data: imageData)
var userpic = PFObject(className:"UserPic")
userpic["picImage"] = imageFile`
Mais j'ai encore besoin de télécharger cette photo sur Parse. Existe-t-il un moyen de réduire la taille ou la résolution de l'image?
ios
swift
parse-platform
uiimage
elvislkm
la source
la source
Réponses:
Oui, vous pouvez utiliser à la
UIImageJPEGRepresentation
place deUIImagePNGRepresentation
pour réduire la taille de votre fichier image. Vous pouvez simplement créer une extension UIImage comme suit: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) } }
modifier / mettre à jour:
Xcode 10 Swift 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) } }
Usage:
if let imageData = image.jpeg(.lowest) { print(imageData.count) }
la source
UIImageJPEGRepresentation(yourImage, 1.0)
au lieu de simplement taper.jp
et laisser xcode compléter automatiquement la méthode pour vous. la même chose pour l'énumération de compression.whatever
.Si vous souhaitez limiter la taille de l'image à une valeur concrète, vous pouvez procéder comme suit:
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 } }
la source
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!)! }
la source
Correction de jus pour Xcode 7, testé le 21/09/2015 et fonctionne bien:
Créez simplement une extension
UIImage
comme suit: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)! } }
Ensuite, vous pouvez l'utiliser comme ceci:
let imageData = imagePassed.lowestQualityJPEGNSData
la source
Approche binaire Swift 4 pour compresser l'image
Je pense qu'il est assez tard pour répondre à cette question mais voici ma solution à la question qui est optimisée J'utilise la recherche binaire pour trouver la valeur optimale. Ainsi, par exemple, disons que par une approche de soustraction normale pour atteindre 62%, il faudrait 38 tentatives de compression, l'approche de * recherche binaire ** atteindrait la solution requise en log max (100) = environ 7 tentatives.
Cependant, je tiens également à vous informer que la
UIImageJPEGRepresentation
fonction ne se comporte pas de manière linéaire surtout lorsque le nombre atteint près de 1. Voici la capture d'écran où nous pouvons voir que l'image cesse de se compresser après que la valeur flottante est> 0,995. Le comportement est assez imprévisible, il est donc préférable d'avoir un tampon delta qui gère de tels cas.Voici le code pour cela
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)! } }
la source
for i in 0...13
.C'est très simple avec l'
UIImage
extensionextension 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)!) } }
utiliser
// max 300kb image.resizeByByte(maxByte: 300000) { (resizedData) in print("image size: \(resizedData.count)") }
la source
Mise à jour Swift 4.2 . J'ai créé cette extension pour réduire la taille d'UIImage.
Ici, vous avez deux méthodes, la première prend un pourcentage et la seconde réduit l'image à 1 Mo.
Bien sûr, vous pouvez changer la deuxième méthode pour devenir 1 Ko ou n'importe quelle taille que vous voulez.
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 } }
la source
Dans Swift 5, répondez @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)! } }
Et vous pouvez appeler comme ceci:
let imageData = imagePassed.lowestQualityJPEGNSData
la source
Dans 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! }
la source
L'utilisation
func jpegData(compressionQuality: CGFloat) -> Data?
fonctionne bien si vous n'avez pas besoin de compresser à une taille spécifique. Cependant, pour certaines images, je trouve utile de pouvoir compresser en dessous d'une certaine taille de fichier. Dans ce cas,jpegData
n'est pas fiable et la compression itérative d'une image entraîne un plafonnement de la taille du fichier (et peut être très coûteuse). Au lieu de cela, je préfère réduire la taille de l'UIImage elle-même, puis la convertir en jpegData et vérifier si la taille réduite est inférieure à la valeur que j'ai choisie (dans une marge d'erreur que j'ai définie). J'ajuste le multiplicateur de pas de compression en fonction du rapport entre la taille de fichier actuelle et la taille de fichier souhaitée pour accélérer les premières itérations qui sont les plus coûteuses.Swift 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() } }
Et l'utilisation:
let data = image.compress(to: 300)
L'
resized
extension UIImage provient de: Comment redimensionner l'UIImage pour réduire la taille de l'image de téléchargementla source
Swift 3
@ réponse leo-dabus révisée pour 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) } }
la source
Dans Swift 4, j'ai créé cette extension qui recevra la taille attendue pour essayer de l'atteindre.
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) } }
Utiliser
do { try UiImageView.image?.compressImage(100, completion: { (image, compressRatio) in print(image.size) imageData = UIImageJPEGRepresentation(image, compressRatio) base64data = imageData?.base64EncodedString() }) } catch { print("Error") }
la source