วิธีที่มีประสิทธิภาพหน่วยความจำมากที่สุดในการปรับขนาดบิตแมปบน Android?


115

ฉันกำลังสร้างแอปโซเชียลที่เน้นรูปภาพมากซึ่งจะส่งรูปภาพจากเซิร์ฟเวอร์ไปยังอุปกรณ์ เมื่ออุปกรณ์มีความละเอียดหน้าจอเล็กลงฉันจำเป็นต้องปรับขนาดบิตแมปบนอุปกรณ์เพื่อให้ตรงกับขนาดการแสดงผลที่ต้องการ

ปัญหาคือการใช้createScaledBitmapทำให้ฉันพบข้อผิดพลาดหน่วยความจำจำนวนมากหลังจากปรับขนาดภาพขนาดย่อจำนวนมาก

วิธีใดที่มีประสิทธิภาพมากที่สุดในการปรับขนาดบิตแมปบน Android


7
เซิร์ฟเวอร์ของคุณไม่สามารถส่งขนาดที่ถูกต้องเพื่อที่คุณจะประหยัด RAM และแบนด์วิดท์ของลูกค้าได้หรือไม่!?
James

2
จะใช้ได้ก็ต่อเมื่อฉันเป็นเจ้าของทรัพยากรเซิร์ฟเวอร์มีส่วนประกอบการคำนวณที่พร้อมใช้งานและในทุกกรณีสามารถคาดเดาขนาดของภาพที่แน่นอนสำหรับอัตราส่วนภาพที่ยังไม่เคยเห็น ดังนั้นหากคุณกำลังโหลดเนื้อหาทรัพยากรจาก CDN ของบุคคลที่สาม (เช่นฉัน) มันไม่ทำงาน :(
Colt McAnlis

คำตอบ:


168

คำตอบนี้สรุปได้จากการโหลดบิตแมปขนาดใหญ่อย่างมีประสิทธิภาพ ซึ่งอธิบายถึงวิธีการใช้ inSampleSize เพื่อโหลดเวอร์ชันบิตแมปที่ลดขนาดลง

โดยเฉพาะอย่างยิ่งPre-scaling bitmapsจะอธิบายรายละเอียดของวิธีการต่างๆวิธีการรวมเข้าด้วยกันและวิธีใดเป็นหน่วยความจำที่มีประสิทธิภาพมากที่สุด

มีสามวิธีที่โดดเด่นในการปรับขนาดบิตแมปบน Android ซึ่งมีคุณสมบัติหน่วยความจำที่แตกต่างกัน:

createScaledBitmap API

API นี้จะใช้บิตแมปที่มีอยู่และสร้างบิตแมปใหม่พร้อมมิติข้อมูลที่คุณเลือกไว้

ในด้านบวกคุณจะได้ขนาดภาพที่ต้องการ (ไม่ว่าจะมีลักษณะอย่างไร) แต่ข้อเสียคือ API นี้ต้องการบิตแมปที่มีอยู่เพื่อให้ทำงานได้ หมายความว่ารูปภาพจะต้องโหลดถอดรหัสและสร้างบิตแมปก่อนจึงจะสามารถสร้างเวอร์ชันใหม่ที่เล็กกว่าได้ สิ่งนี้เหมาะอย่างยิ่งในแง่ของการได้ขนาดที่แน่นอนของคุณ แต่น่ากลัวในแง่ของค่าใช้จ่ายหน่วยความจำเพิ่มเติม ด้วยเหตุนี้นี่จึงเป็นตัวทำลายข้อตกลงสำหรับนักพัฒนาแอปส่วนใหญ่ที่มักจะคำนึงถึงความจำ

ธง inSampleSize

BitmapFactory.Optionsมีคุณสมบัติที่ระบุinSampleSizeว่าจะปรับขนาดภาพของคุณในขณะที่ถอดรหัสเพื่อหลีกเลี่ยงความจำเป็นในการถอดรหัสเป็นบิตแมปชั่วคราว ค่าจำนวนเต็มที่ใช้ที่นี่จะโหลดรูปภาพที่มีขนาดลดลง 1 / x ตัวอย่างเช่นการตั้งค่าinSampleSizeเป็น 2 จะส่งคืนรูปภาพที่มีขนาดครึ่งหนึ่งและการตั้งค่าเป็น 4 จะส่งคืนรูปภาพที่มีขนาด 1/4 โดยทั่วไปขนาดภาพจะมีขนาดเล็กกว่าขนาดต้นฉบับของคุณเสมอ

จากมุมมองของหน่วยความจำการใช้inSampleSizeเป็นการดำเนินการที่รวดเร็วมาก อย่างมีประสิทธิภาพมันจะถอดรหัสทุกพิกเซล Xth ของภาพของคุณเป็นบิตแมปที่ได้ มีสองประเด็นหลักinSampleSizeดังนี้:

  • มันไม่ได้ให้คุณความละเอียดที่แน่นอน มันจะลดขนาดของบิตแมปของคุณด้วยกำลัง 2 เท่านั้น

  • มันไม่ได้ผลิตปรับขนาดคุณภาพที่ดีที่สุด ฟิลเตอร์ปรับขนาดส่วนใหญ่จะสร้างภาพที่ดูดีโดยการอ่านบล็อคพิกเซลจากนั้นจึงถ่วงน้ำหนักเพื่อสร้างพิกเซลที่ปรับขนาดดังกล่าว inSampleSizeหลีกเลี่ยงทั้งหมดนี้ด้วยการอ่านทุกๆสองสามพิกเซล ผลลัพธ์ที่ได้ค่อนข้างมีประสิทธิภาพและหน่วยความจำต่ำ แต่คุณภาพแย่ลง

หากคุณกำลังเพียงการจัดการกับการหดตัวภาพของคุณด้วยขนาด pow2 บางส่วนและการกรองจะไม่เป็นปัญหาแล้วคุณไม่สามารถหาหน่วยความจำมีประสิทธิภาพมากขึ้น (หรือมีประสิทธิภาพ) inSampleSizeวิธีการมากกว่า

แฟล็ก inScaled, inDensity, inTargetDensity

หากคุณจำเป็นต้องขนาดภาพไปยังมิติที่ไม่ได้เท่ากับอำนาจของทั้งสองแล้วคุณจะต้อง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ก่อนอื่นจะถูกนำไปใช้กับรูปภาพโดยทำให้มันมีกำลังสองต่อไปที่ใหญ่กว่าขนาดเป้าหมายของคุณ จากนั้น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

@ k0sh dstWIdth คือความกว้างของ ImageView สำหรับตำแหน่งที่จะไป ie destination widthหรือ dstWidth สำหรับระยะสั้น
tyczj

@tyczj ขอบคุณสำหรับคำตอบฉันรู้ว่ามันคืออะไร แต่มีบางคนอาจไม่รู้และเนื่องจาก Colt ที่ตอบคำถามนี้จริงบางทีเขาอาจอธิบายได้เพื่อให้ผู้คนไม่สับสน
k0sh

โพสต์ที่ดี ... ไม่รู้เกี่ยวกับแฟล็ก inScaled, inDensity, inTargetDensity ก่อนหน้านี้ ...
maveroid

ฉันได้ดูซีรีส์รูปแบบการทำงานของ Android และฉันได้เรียนรู้มากมาย!
Anis

13

คำตอบนี้ดี (และถูกต้อง) แต่ก็ซับซ้อนมากเช่นกัน แทนที่จะประดิษฐ์วงล้อขึ้นมาใหม่ให้พิจารณาไลบรารีเช่นGlide , Picasso , UIL , Ionหรืออื่น ๆ อีกมากมายที่ใช้ตรรกะที่ซับซ้อนและผิดพลาดนี้ให้กับคุณ

Colt เองยังแนะนำให้ดู Glide และ Picasso ในวิดีโอรูปแบบประสิทธิภาพบิตแมปล่วงหน้า

เมื่อใช้ไลบรารีคุณจะได้รับประสิทธิภาพทุกอย่างที่กล่าวถึงในคำตอบของ Colt แต่ด้วย API ที่ง่ายกว่ามากซึ่งทำงานได้อย่างสม่ำเสมอใน Android ทุกเวอร์ชัน

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.