2015-08-20 7 views
107

मैं एक छवि-गहन सामाजिक ऐप बना रहा हूं जहां छवियों को सर्वर से डिवाइस पर भेजा जाता है। जब डिवाइस में छोटे स्क्रीन संकल्प होते हैं, तो मुझे अपने इच्छित डिस्प्ले आकारों से मेल खाने के लिए, डिवाइस पर बिटमैप्स का आकार बदलने की आवश्यकता होती है।एंड्रॉइड पर बिटमैप्स का आकार बदलने के लिए सबसे मेमोरी प्रभावी तरीका?

समस्या यह है कि createScaledBitmap का उपयोग कर थंबनेल चित्रों की एक भीड़ का आकार बदलने के बाद बाहर के स्मृति त्रुटियों का एक बहुत में चलाने के लिए मुझे का कारण बनता है।

Android पर बिटमैप्स का आकार बदलने का सबसे मेमोरी प्रभावी तरीका क्या है?

+7

अपने सर्वर तो आप अपने ग्राहक के रैम और बैंडविड्थ को बचा नहीं सही आकार भेज सकते हैं !? – James

+2

कि वैध अगर मैं सर्वर संसाधन स्वामित्व है, यह एक गणना घटक इसके साथ उपलब्ध था, और सभी मामलों में, यह अभी तक इसे नहीं देखा था पहलू अनुपात के लिए छवियों की सटीक आयामों का अनुमान लगा सकते। इसलिए यदि आप किसी तृतीय पक्ष सीडीएन से संसाधन सामग्री लोड कर रहे हैं (जैसे मैं हूं) यह काम नहीं करता है :( –

उत्तर

154

इस उत्तर Loading large bitmaps Efficiently जो बताते हैं एक नीचे बढ़ाया बिटमैप संस्करण लोड करने के लिए inSampleSize उपयोग करने के लिए कैसे से संक्षेप है।

विशेष रूप से Pre-scaling bitmaps विभिन्न तरीकों, उन्हें कैसे गठबंधन करने के लिए के विवरण बताते हैं, और जो सबसे अधिक स्मृति कुशल हैं।

वहाँ जो विभिन्न स्मृति गुण होते हैं Android पर एक बिटमैप आकार बदलने के लिए तीन प्रमुख तरीके हैं:

createScaledBitmap API

यह API एक मौजूदा बिटमैप में ले जाएगा, और के साथ एक नया बिटमैप बनाने आपके द्वारा चुने गए सटीक आयाम।

प्लस तरफ, आप वास्तव में उस छवि आकार को प्राप्त कर सकते हैं जिसे आप ढूंढ रहे हैं (चाहे यह कैसा दिखता है)। लेकिन नकारात्मक, यह है कि इस API को काम करने के लिए मौजूदा बिटमैप की आवश्यकता है। एक नया, छोटा संस्करण बनाने में सक्षम होने से पहले, छवि को लोड, डीकोडेड और बिटमैप बनाया जाना होगा। यह आपके सटीक आयाम प्राप्त करने के मामले में आदर्श है, लेकिन अतिरिक्त मेमोरी ओवरहेड के मामले में भयानक है। जैसे, इस जिन्होंने स्मृति के प्रति जागरूक होने के लिए

inSampleSize flag

BitmapFactory.Options एक संपत्ति inSampleSize कि आपकी छवि का आकार होगा, जबकि इसे डीकोड के रूप में उल्लेख किया गया है, से बचने के लिए करते हैं सबसे एप्लिकेशन डेवलपर के लिए एक सौदा ब्रेकर तरह-की है अस्थायी बिटमैप को डीकोड करने की आवश्यकता। यहां उपयोग किया गया यह पूर्णांक मान एक छवि को 1/x कम आकार पर लोड करेगा। उदाहरण के लिए, inSampleSize से 2 को सेट करने वाली छवि उस आधे आकार को लौटाती है, और इसे 4 पर सेट करने से उस छवि को 1/4 वां आकार मिलता है। असल में छवि आकार हमेशा आपके स्रोत आकार की तुलना में कुछ पावर-ऑफ-छोटे होंगे।

मेमोरी परिप्रेक्ष्य से, inSampleSize का उपयोग करना वास्तव में तेज़ ऑपरेशन है। प्रभावी रूप से, यह आपके परिणामस्वरूप बिटमैप में आपकी छवि के प्रत्येक Xth पिक्सेल को केवल डीकोड करेगा।

  • यह आप सटीक संकल्प नहीं देता: वहाँ inSampleSize हालांकि साथ दो मुख्य मुद्दों है। यह केवल 2 बिट की कुछ शक्ति द्वारा आपके बिटमैप के आकार को कम करता है।

  • यह सर्वोत्तम गुणवत्ता का आकार का उत्पादन नहीं करता है। अधिकांश आकार देने वाले फ़िल्टर पिक्सेल के ब्लॉक को पढ़कर अच्छी दिखने वाली छवियां उत्पन्न करते हैं, और फिर प्रश्न में आकार वाले पिक्सेल का उत्पादन करने के लिए उन्हें वज़न देते हैं।inSampleSize बस हर कुछ पिक्सल को पढ़ कर यह सब बचा जाता है। परिणाम काफी performant है, और कम स्मृति है, लेकिन गुणवत्ता ग्रस्त है।

आप केवल कुछ pow2 आकार से अपनी छवि को सिकुड़ने के साथ काम कर रहे हैं, और छानने कोई मुद्दा नहीं है, तो आप inSampleSize तुलना में एक अधिक स्मृति कुशल (या प्रदर्शन कुशल) विधि नहीं मिल रहा।

inScaled, inDensity, inTargetDensity flags

आप एक आयाम है कि दोनों में से एक शक्ति के बराबर नहीं है के लिए एक छवि पैमाने पर करने की जरूरत है, तो आप BitmapOptions की inScaled, inDensity और inTargetDensity झंडे की आवश्यकता होगी। जब inScaled ध्वज स्थापित किया गया है, प्रणाली inDensity मूल्यों से inTargetDensity विभाजित करके अपने बिटमैप को लागू करने के लिए स्केलिंग मूल्य व्युत्पन्न होगा।

mBitmapOptions.inScaled = true; 
mBitmapOptions.inDensity = srcWidth; 
mBitmapOptions.inTargetDensity = dstWidth; 

// will load & resize the image to be 1/inSampleSize dimensions 
mCurrentBitmap = BitmapFactory.decodeResources(getResources(), 
     mImageIDs, mBitmapOptions); 

इस विधि का उपयोग अपनी छवि को आकार फिर से होगा, और यह भी आकार बदलने चरण के दौरान यह करने के लिए एक 'आकार बदलने फिल्टर' लागू होते हैं, जो है, अंतिम परिणाम बेहतर दिखेगा क्योंकि कुछ अतिरिक्त गणित को ध्यान में रखा जा चुका है । लेकिन चेतावनी दी: कि अतिरिक्त फिल्टर कदम, अतिरिक्त संसाधन समय लेता है, और जल्दी से, बड़ी छवियों के लिए जोड़ सकते हैं धीमी गति से आकार बदलता है, जिसके परिणामस्वरूप और फिल्टर खुद के लिए अतिरिक्त स्मृति आवंटन।

अतिरिक्त फ़िल्टरिंग ओवरहेड के कारण, इस तकनीक को उस छवि पर लागू करने का एक अच्छा विचार नहीं है जो आपके वांछित आकार से काफी बड़ा है।

जादू संयोजन

एक स्मृति और प्रदर्शन के दृष्टिकोण से, आप सबसे अच्छा परिणाम के लिए इन विकल्पों को जोड़ सकते हैं। (inSampleSize, inScaled, inDensity और inTargetDensity झंडे की स्थापना)

inSampleSize पहले, छवि को लागू किया जाएगा यह करने के लिए हो रही है अगले अपने लक्ष्य आकार की तुलना में बिजली के- दो बड़े। फिर, inDensity & inTargetDensity परिणाम पैमाने पर करने के आयाम है कि आप चाहते हैं सटीक, छवि को साफ करने के लिए एक फिल्टर आपरेशन लागू करने के लिए उपयोग किया जाता।

इन दोनों को जोड़ना एक तेज़ ऑपरेशन है, क्योंकि inSampleSize चरण पिक्सल की संख्या को कम करेगा जिससे परिणामी घनत्व-आधारित चरण को इसके आकार बदलने वाले फ़िल्टर को लागू करने की आवश्यकता होगी।

mBitmapOptions.inScaled = true; 
mBitmapOptions.inSampleSize = 4; 
mBitmapOptions.inDensity = srcWidth; 
mBitmapOptions.inTargetDensity = dstWidth * mBitmapOptions.inSampleSize; 

// will load & resize the image to be 1/inSampleSize dimensions 
mCurrentBitmap = BitmapFactory.decodeFile(fileName, mBitmapOptions); 

आप विशिष्ट आयाम, और कुछ अच्छे छानने के लिए एक छवि फिट करने के लिए की आवश्यकता होगी, रहे हैं, तो इस तकनीक का सही आकार प्राप्त करने के लिए सबसे अच्छा पुल है, लेकिन एक तेजी से, कम स्मृति पदचिह्न में किया ऑपरेशन।

हो रही छवि आयाम

संपूर्ण चित्र डिकोडिंग के लिए अपने बिटमैप का आकार बदलने के लिए बिना छवि का आकार हो रही है, आप आने वाले आयामों की जानकारी होनी चाहिए। आप चित्र के आयाम मिलता है, डब्ल्यू/मदद करने के लिए वास्तव में पिक्सेल डेटा को डिकोड करने की आवश्यकता होगी, ओ inJustDecodeBounds ध्वज का उपयोग कर सकते हैं।

// Decode just the boundaries 
mBitmapOptions.inJustDecodeBounds = true; 
BitmapFactory.decodeFile(fileName, mBitmapOptions); 
srcWidth = mBitmapOptions.outWidth; 
srcHeight = mBitmapOptions.outHeight; 


//now go resize the image to the size you want 

आप इस फ़्लैग का उपयोग पहले आकार को डिकोड करने के कर सकते हैं और तब अपने लक्षित समाधान करने के लिए स्केलिंग के लिए उचित मूल्यों की गणना।

+1

यह अच्छा होगा अगर आप हमें बता सकें कि dstWidth क्या है? – k0sh

+0

@ k0sh dstWIdth ImageView की चौड़ाई है जहां इसकी 'यानी गंतव्य width' या dstWidth कम – tyczj

+0

जवाब के लिए धन्यवाद @tyczj के लिए करने के लिए जा रहा है, मुझे पता है यह क्या है, लेकिन कुछ पता नहीं हो सकता है देखते हैं और कोल्ट, जो वास्तव में इस सवाल का जवाब था के बाद से, शायद वह यह समझा सकता है इसलिए लोगों को भ्रमित प्राप्त नहीं है। – k0sh

11

के रूप में अच्छा (और सही) इस उत्तर के रूप में, यह भी बहुत जटिल है। के बजाय फिर से आविष्कार पहिया, Glide, Picasso, UIL, Ion, या दूसरों है कि आप के लिए इस जटिल और त्रुटि प्रवण तर्क लागू की किसी भी संख्या की तरह पुस्तकालयों पर विचार करें।

कोल्ट खुद भी Pre-scaling Bitmaps Performance Patterns Video में ग्लाइड और पिकासो पर एक नज़र लेने की सिफारिश की।

पुस्तकालयों का उपयोग करके आप कोल्ट के जवाब में बताया गया दक्षता के हर बिट मिलता है, लेकिन बेहद सरल एपीआई है कि Android के हर संस्करण में समान रूप से काम के साथ कर सकते हैं।

संबंधित मुद्दे