पार्स को PFFile के रूप में अपलोड करने से पहले एक छवि के आकार को कम करने के लिए कैसे संपीड़ित करें? (स्विफ्ट)


88

मैं सीधे फोन पर फोटो लेने के बाद पार्से में एक छवि फ़ाइल अपलोड करने की कोशिश कर रहा था। लेकिन यह एक अपवाद फेंकता है:

अनकैप्ड अपवाद '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`

लेकिन मुझे अभी भी उस तस्वीर को पार्स पर अपलोड करने की आवश्यकता है। क्या छवि के आकार या रिज़ॉल्यूशन को कम करने का कोई तरीका है?


मैंने अंत में gbk और fount के अंतिम प्रस्ताव की कोशिश की कि अगर मैं newData = UIImageJPEGRepresentation (UIImage (data: data) कहूं, तो 1) newData.count data.count के बराबर नहीं है और वास्तव में अधिक से अधिक के फैक्टर के साथ बड़ा है। 2. जो मेरे लिए वास्तव में आश्चर्यजनक है! किसी भी तरह, कोड के लिए धन्यवाद!
निकोर्ड

जवाबों:


188

हां आप अपनी छवि फ़ाइल आकार को कम करने के UIImageJPEGRepresentationबजाय उपयोग कर सकते हैं UIImagePNGRepresentation। आप अनुसरण के रूप में केवल एक एक्सटेंशन UIImage बना सकते हैं:

Xcode 8.2 • स्विफ्ट 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)
}

1
अघोषित प्रकार का उपयोग UIImage। यह मुझे मिलने वाली त्रुटि है
उमित काया

1
मैं 22-25MB के आकार के साथ छवियों को वापस कर रहा था, अब इसका एक अंश। आपको बहुत - बहुत धन्यवाद! महान विस्तार!
ऑक्टेवियो एंटोनियो केडेनो

3
सिंटैक्स के अलावा कोई अंतर नहीं है। बेशक, आप मैन्युअल रूप से कोड UIImageJPEGRepresentation(yourImage, 1.0)को केवल टाइप करने के बजाय लिख सकते हैं .jpऔर xcode को आपके लिए विधि स्वतः पूर्ण कर सकते हैं। संपीड़न गणना के लिए एक ही है .whatever
सिंह Dabus

1
@ यूमिट आपको UIKit
एलन

1
'jpegData (संपीडन गुण) :) का नाम बदलकर' UIImageJPEGRepresentation ( : :) :
5uper_0leh

53

यदि आप छवि के आकार को कुछ ठोस मान तक सीमित करना चाहते हैं, तो u निम्नानुसार कर सकते हैं:

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

यह अधिक व्यापक समाधान है।
आइडल अशरफ

स्विफ्ट 3.1if let data = bits.representation(using: .jpeg, properties: [.compressionFactor:compressingValue])
जैक्सनसोक्स

16
यह बहुत महंगा है आप थोड़ी देर और हर बार इतना भारी काम कर रहे हैं !! कोई सीमित स्थिति नहीं ..
अंबर के

व्यापक लेकिन वास्तव में स्मृति पर कठिन। यह स्मृति समस्याओं के साथ पुराने उपकरणों को क्रैश करता है। ऐसा करने का एक सस्ता तरीका होना चाहिए।
टोफू योद्धा

1
यह एक बहुत बुरा विचार है, यह भयानक किनारे के मामलों से भरा है। 1) यह 1.0 के compressingValue के साथ शुरू होता है जिसका मतलब है कि शायद ही कोई संपीड़न। यदि छवि आयाम छोटे हैं, तो छवियां बहुत अधिक केबी होने की तुलना में समाप्त हो जाएंगी, जिनकी उन्हें आवश्यकता है। 2) यदि चित्र बड़े हैं, तो यह धीमी गति से होगा क्योंकि यह लक्ष्य आकार के तहत प्राप्त करने के लिए कई बार पुनः प्राप्त कर सकता है। 3) यदि चित्र बहुत बड़े हैं, तो यह उस बिंदु तक संकुचित हो सकता है जहां छवि कचरा दिखती है। उन मामलों में छवि को बचाने में विफल रहने और उपयोगकर्ता को यह बताना बहुत बेहतर होगा कि यह बहुत बड़ा है।
ओरियन एडवर्ड्स

10
  //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!)!
}

9

Xcode 7 के लिए Jus फिक्सिंग, 09/21/2015 को परीक्षण किया गया और ठीक काम किया गया:

बस 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

स्वामी श्री थियागो और संपादक श्री कुलदीप
अभिमन्यु राठौर

5

छवि को संपीड़ित करने के लिए 4 बाइनरी दृष्टिकोण को स्विफ्ट करें

मेरा मानना ​​है कि इस प्रश्न का उत्तर देने में काफी देर हो चुकी है लेकिन यहाँ मेरे प्रश्न का समाधान है जो अनुकूलित है मैं बाइनरी खोज का उपयोग कर रहा हूं इष्टतम मूल्य खोजने के लिए । इसलिए, उदाहरण के लिए, सामान्य घटाव दृष्टिकोण से कहें कि 62% तक पहुंचने के लिए 38 संपीड़न प्रयासों की आवश्यकता होगी, * बाइनरी सर्च ** दृष्टिकोण अधिकतम लॉग (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)!
    }
}

क्यों आप एक लूप लूप का उपयोग कर रहे हैं और मैन्युअल रूप से एक चर बढ़ा रहे हैं? बस उपयोग करें for i in 0...13
पीटर शोर्न

3

वह 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)!)
    }
}

उपयोग करने के लिए

// max 300kb
image.resizeByByte(maxByte: 300000) { (resizedData) in
    print("image size: \(resizedData.count)")
}

4
बहुत धीमा और समान
iman kazemayni

1
ऐसा हो सकता है कि आपकी आकार की छवि कभी भी 300kB से कम न हो और आपको इस मामले में कोई कमी न हो
slxl

कम से कम (compressQuality> = 0) अतिरिक्त और& स्थिति के रूप में लूप में जोड़ा जा सकता है
slxl

2

स्विफ्ट 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
    }
}

2

स्विफ्ट 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

2

स्विफ्ट में

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!
    }

2

func jpegData(compressionQuality: CGFloat) -> Data?यदि आपको किसी विशिष्ट आकार को संपीड़ित करने की आवश्यकता नहीं है, तो अच्छी तरह से उपयोग करना । हालाँकि, कुछ छवियों के लिए, मुझे यह उपयोगी लगता है कि एक निश्चित फ़ाइल आकार के नीचे संपीड़ित करने में सक्षम हो। उस मामले में, jpegDataअविश्वसनीय है, और एक छवि के पुनरावृत्त compressing फ़ाइलों पर बाहर पठार में परिणाम (और वास्तव में महंगा हो सकता है)। इसके बजाय, मैं खुद 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एक्सटेंशन से आता है: मैं अपलोड छवि का आकार कम करने के लिए UIImage का आकार कैसे बदलूं


1

स्विफ्ट 3

@ leo-dabus उत्तर को स्विफ्ट 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)  }
}

1

स्विफ्ट 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")
        }
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.