एंड्रॉइड पर बिटमैप को आकार देने के लिए अधिकांश मेमोरी कुशल तरीका?


115

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

समस्या यह है कि createScaledBitmap का उपयोग करने से मुझे थंबनेल छवियों के एक समूह का आकार बदलने के बाद बहुत सारी आउट-ऑफ-मेमोरी त्रुटियों में भाग लेना पड़ता है।

एंड्रॉइड पर बिटमैप को आकार देने के लिए सबसे स्मृति कुशल तरीका क्या है?


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

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

जवाबों:


168

इस उत्तर को लोड किए गए बड़े बिटमैप से संक्षेप में प्रस्तुत किया गया है जो कुशलता से बताता है कि डाउन-स्केल किए गए बिटमैप संस्करण को लोड करने के लिए इनसम्प्लीसाइज़ का उपयोग कैसे करें।

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

एंड्रॉइड पर बिटमैप को आकार देने के तीन प्रमुख तरीके हैं जिनमें विभिन्न मेमोरी गुण हैं:

createScaledBitmap API

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

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

नमूना चिह्नित करें

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

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

  • यह आपको सटीक संकल्प नहीं देता है । यह केवल 2 की शक्ति से आपके बिटमैप के आकार को घटाता है।

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

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

इनकल्ड, इनडेंसिटी, इनरगेट डेंसिटी फ्लैग

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

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पहले छवि पर लागू किया जाएगा, इसे अपने लक्ष्य आकार की तुलना में अगले दो LARGER को प्राप्त होगा। फिर, 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छवि के आयाम प्राप्त करने में सहायता के लिए ध्वज का उपयोग कर सकते हैं , वास्तव में पिक्सेल डेटा को डीकोड करने के लिए w / o की आवश्यकता है।

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

@ k0sh dstWIdth ImageView की चौड़ाई है, जहां इसके लिए जा रहा है destination widthया लघु के लिए
dstWidth

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

अच्छी पोस्ट ... पहले से असंगत, असमानता, inTgetgetDensity झंडे के बारे में नहीं पता था ...
मॉवरॉयड

मैं Android प्रदर्शन पैटर्न श्रृंखला देख रहा हूं, और मैंने बहुत कुछ सीखा है!
अनीस

13

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

कोल्ट खुद भी पूर्व-स्केलिंग बिटमैप प्रदर्शन प्रदर्शन वीडियो में ग्लाइड और पिकासो को देखने की सलाह देते हैं ।

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

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.