当前位置:网站首页>Detailed explanation of bitmap optimization
Detailed explanation of bitmap optimization
2022-06-23 22:22:00 【Great inventor】
Why? Bitmap It can lead to OOM?
1. Each model is compiling ROM An application heap memory has been set VM Value limit dalvik.vm.heapgrowthlimit, Used to limit the maximum memory available for each application , Exceeding this maximum value will result in OOM. This threshold , Generally according to the mobile phone screen dpi Increasing size ,dpi Smaller phones , The lower the maximum memory available per application . So when the number of loaded images is large , It is easy to exceed this threshold , cause OOM.
2. The higher the resolution of the image , The more memory consumed , When loading high resolution pictures , It will take up a lot of memory , If it is not handled properly, it will OOM. for example , The resolution of one is :1920x1080 Pictures of the . If Bitmap Use ARGB_8888 32 Bits to tile , Occupied memory is 1920x1080x4 Bytes , Take up nearly 8M Memory , As one can imagine , If the picture is not processed , will OOM.
Bitmap Basic knowledge of
A picture Bitmap Memory used = Picture length x Image width x The number of bytes a pixel occupies undefined and Bitmap.Config, It is an important parameter to specify the number of bytes per pixel .
among ,A On behalf of transparency ;R For red ;G Representing green ;B Representing blue .
ALPHA_8
Express 8 position Alpha Bitmap , namely A=8, One pixel occupied 1 Bytes , It has no color , Transparency only
ARGB_4444
Express 16 position ARGB Bitmap , namely A=4,R=4,G=4,B=4, One pixel takes up 4+4+4+4=16 position ,2 Bytes
ARGB_8888
Express 32 position ARGB Bitmap , namely A=8,R=8,G=8,B=8, One pixel takes up 8+8+8+8=32 position ,4 Bytes
RGB_565
Express 16 position RGB Bitmap , namely R=5,G=6,B=5, It has no transparency , One pixel takes up 5+6+5=16 position ,2 Bytes
A picture Bitmap Memory used = Picture length x Image width x The number of bytes a pixel occupies
According to the above algorithm , You can calculate the memory occupied by the picture , With 100*100 Pixel picture as an example
image.png
Now let's start learning Bitmap Optimization scheme
One 、Bitmap Mass compression
adopt Bitmap.compress(Bitmap.CompressFormat.JPEG, options, baos); Way to reduce picture quality
public static Bitmap compressImage(Bitmap bitmap){ ByteArrayOutputStream baos = new ByteArrayOutputStream();
// Mass compression method , here 100 Means no compression , Store the compressed data in baos in
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, baos);
int options = 100;
// Loop to determine if the compressed image is larger than 50kb, Greater than continue to compress
while ( baos.toByteArray().length / 1024>50) { // Empty baos
baos.reset();
bitmap.compress(Bitmap.CompressFormat.JPEG, options, baos);
options -= 10;// Every time it's reduced 10
}
// Put the compressed data baos Store in ByteArrayInputStream in
ByteArrayInputStream isBm = new ByteArrayInputStream(baos.toByteArray());
// hold ByteArrayInputStream Data generated images
Bitmap newBitmap = BitmapFactory.decodeStream(isBm, null, null);
return newBitmap;
}
Two 、 Zoom compression
int ratio = 8;
// Create a new bitmap based on parameters
Bitmap result = Bitmap.createBitmap(bmp.getWidth() / ratio, bmp.getHeight() / ratio, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(result);
Rect rect = new Rect(0, 0, bmp.getWidth() / ratio, bmp.getHeight() / ratio);
canvas.drawBitmap(bmp, null, rect, null);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
result.compress(Bitmap.CompressFormat.JPEG, 100, baos);
try {FileOutputStream fos = new FileOutputStream(file);
fos.write(baos.toByteArray());
fos.flush();
fos.close();
} catch (Exception e) {e.printStackTrace();
}
3、 ... and 、 Sample rate compression ( Size compression )
Bitmap The core idea of optimized loading is to use BitmapFactory.Options To load images of the required size .
Such as through ImageView To show pictures , A lot of times ImageView It's not as big as the original size of the picture , If you load the whole picture , Then set to ImageView,ImageView Is unable to display the original picture . adopt BitmapFactory.Options You can load the reduced image at a certain sampling rate , Put the reduced picture in ImageView It shows that , This will reduce the memory consumption and avoid OOM, Improved Bitmap Load time performance .BitmapFactory The four class methods provided for loading images support BitmapFactory.Options Parameters , It is very convenient to sample and zoom an image .
for fear of OOM abnormal , It's best to parse each picture , First check the size of the picture , Then you can decide whether to load the whole picture into memory or compress the picture and load it into memory . The following factors need to be considered :
1. Estimate the amount of memory required to load the entire image
2. How much memory would you like to provide to load an image
3. The actual size of the control used to display this picture
4. Current device screen size and resolution
adopt BitmapFactory.Options To zoom the image , Mainly used its inSampleSize Parameters , I.e. sampling rate . When inSampleSize by 1 when , The size of the sampled image is the original size of the image ; When inSampleSize Greater than 1 when , such as 2, Then the width and height of the sampled image are all the same as the original image 1/2, The number of pixels is... Of the original image 1/4, The memory size occupied by it is the same as that of the original picture 1/4.
The sampling rate must be greater than 1 The integer of , The picture will have the effect of shrinking , And the sampling rate affects both width and height , The scale is 1/(inSampleSize Of 2 Power ), such as inSampleSize by 4, So the zoom ratio is 1/16. Official documents point out that ,inSampleSize The values for 2 The index of :1、2、4、8、16 wait .
How to obtain the sampling rate ?
1. take BitmapFactory.Options Of inJustDecodeBounds The parameter is set to true And load the picture ;undefined 2. from BitmapFactory.Options Take out the original width and height information of the picture , They correspond to outWidth and outHeight Parameters ;undefined 3. According to the rules of sampling rate and the target View To calculate the sample rate inSampleSize;undefined 4. take BitmapFactory.Options Of inJustDecodeBounds The parameter is set to false, Then reload the picture .
/**
* Compress by picture size Parameter is bitmap
* @param bitmap
* @param pixelW
* @param pixelH
* @return
*/
public static Bitmap compressImageFromBitmap(Bitmap bitmap, int pixelW, int pixelH) {
ByteArrayOutputStream os = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, os);
if( os.toByteArray().length / 1024>512) {// If the picture is larger than 0.5M, Compression to avoid generating images (BitmapFactory.decodeStream) Time overflows
os.reset();
bitmap.compress(Bitmap.CompressFormat.JPEG, 50, os);// Here compression 50%, Store the compressed data in baos in
}
ByteArrayInputStream is = new ByteArrayInputStream(os.toByteArray());
BitmapFactory.Options options = new BitmapFactory.Options();
// First sampling
options.inJustDecodeBounds = true;// Load only bitmap The border , Take up part of the memory
options.inPreferredConfig = Bitmap.Config.RGB_565;// Set color mode
BitmapFactory.decodeStream(is, null, options);// Configure preferences
// Second sampling
options.inJustDecodeBounds = false;
options.inSampleSize = computeSampleSize(options , pixelH > pixelW ? pixelW : pixelH ,pixelW * pixelH );
is = new ByteArrayInputStream(os.toByteArray());
// Configure the final preferences for the new bitmap object
Bitmap newBitmap = BitmapFactory.decodeStream(is, null, options);
return newBitmap;
}/**
* Dynamically calculate the... Of the picture inSampleSize
* @param options
* @param minSideLength
* @param maxNumOfPixels
* @return
*/
public static int computeSampleSize(BitmapFactory.Options options, int minSideLength, int maxNumOfPixels) {
int initialSize = computeInitialSampleSize(options, minSideLength, maxNumOfPixels);
int roundedSize;
if (initialSize <= 8) {
roundedSize = 1;
while (roundedSize < initialSize) {
roundedSize <<= 1;
}
} else {
roundedSize = (initialSize + 7) / 8 * 8;
}
return roundedSize;
} private static int computeInitialSampleSize(BitmapFactory.Options options, int minSideLength, int maxNumOfPixels) {double w = options.outWidth;
double h = options.outHeight;
int lowerBound = (maxNumOfPixels == -1) ? 1 : (int) Math.ceil(Math.sqrt(w * h / maxNumOfPixels));
int upperBound = (minSideLength == -1) ? 128 :(int) Math.min(Math.floor(w / minSideLength), Math.floor(h / minSideLength));
if (upperBound < lowerBound) {return lowerBound;
}
if ((maxNumOfPixels == -1) && (minSideLength == -1)) {return 1;
} else if (minSideLength == -1) {return lowerBound;
} else {return upperBound;
}
}
}
Four 、Bitmap Color mode compression
Android The default is to use ARGB8888 Configure to handle color , Occupy 4 byte , change to the use of sth. RGB565, Will only occupy 2 byte , The price is that there will be relatively few colors , It is suitable for scenes with low requirements for color richness .
BitmapFactory.Options options = new BitmapFactory.Options();
options.inPreferredConfig = Bitmap.Config.RGB_565;// Set color mode
5、 ... and 、libjpeg.so Library compression
libjpeg Is a widely used open source JPEG Image library , Android also relies on libjpeg To compress pictures . But Android is not directly encapsulated libjpeg, It is based on another name Skia As an image processing engine .Skia It is a large and comprehensive engine maintained by Google itself , Various image processing functions are realized in it , And it is widely used in the products of Google itself and other companies ( Such as :Chrome、Firefox、 Android etc. ).Skia Yes libjpeg It's well packaged , Based on this engine, it can be very convenient for the operating system 、 Browser and other development image processing functions .
**Java The local method of is as follows :
public static native String compressBitmap(Bitmap bit, int w, int h, int
quality, byte[] fileNameBytes, boolean optimize);**
following C The code steps are as follows :
1、 take Android Of bitmap Decode and convert to RGB data undefined 2、 by JPEG Object allocates space and initializes undefined 3、 Specify the compressed data source undefined 4、 Get file information undefined 5、 Set parameters for compression , Including image size , Color space undefined 6、 Start compressing undefined 7、 Compression complete undefined 8、 Release resources
#include <string.h>
#include <bitmap.h>
#include <log.h>
#include "jni.h"
#include <stdio.h>
#include <setjmp.h>
#include <math.h>
#include <stdint.h>
#include <time.h>
#include "jpeg/android/config.h"
#include "jpeg/jpeglib.h"
#include "jpeg/cdjpeg.h" /* Common decls for cjpeg/djpeg applications */
#define LOG_TAG "jni"
//#define LOGW(...) __android_log_write(ANDROID_LOG_WARN,LOG_TAG,__VA_ARGS__)
//#define LOGI(...) __android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__)
#define true 1
#define false 0
typedef uint8_t BYTE;
char *error;
struct my_error_mgr {struct jpeg_error_mgr pub;
jmp_buf setjmp_buffer;
};
typedef struct my_error_mgr * my_error_ptr;
METHODDEF(void)
my_error_exit (j_common_ptr cinfo)
{my_error_ptr myerr = (my_error_ptr) cinfo->err;
(*cinfo->err->output_message) (cinfo);
error=(char*)myerr->pub.jpeg_message_table[myerr->pub.msg_code];
LOGE("jpeg_message_table[%d]:%s", myerr->pub.msg_code,myerr->pub.jpeg_message_table[myerr->pub.msg_code]);// LOGE("addon_message_table:%s", myerr->pub.addon_message_table);// LOGE("SIZEOF:%d",myerr->pub.msg_parm.i[0]);// LOGE("sizeof:%d",myerr->pub.msg_parm.i[1]);longjmp(myerr->setjmp_buffer, 1);
}
int generateJPEG(BYTE* data, int w, int h, int quality,
const char* outfilename, jboolean optimize) {int nComponent = 3;
// jpeg The structure of the body , Keep it wide, for example 、 high 、 A deep 、 Picture format and other information
struct jpeg_compress_struct jcs;
struct my_error_mgr jem;
jcs.err = jpeg_std_error(&jem.pub);
jem.pub.error_exit = my_error_exit;
if (setjmp(jem.setjmp_buffer)) {return 0;
}
jpeg_create_compress(&jcs);
// Open the output file wb: Can write byte
FILE* f = fopen(outfilename, "wb");
if (f == NULL) {return 0;
}
// Set the file path of the structure
jpeg_stdio_dest(&jcs, f);
jcs.image_width = w;
jcs.image_height = h;
// Set Huffman code
jcs.arith_code = false;
jcs.input_components = nComponent;
if (nComponent == 1)
jcs.in_color_space = JCS_GRAYSCALE;
else
jcs.in_color_space = JCS_RGB;
jpeg_set_defaults(&jcs);
jcs.optimize_coding = optimize;
jpeg_set_quality(&jcs, quality, true);
// Start compressing , Write all pixels
jpeg_start_compress(&jcs, TRUE);
JSAMPROW row_pointer[1];
int row_stride;
row_stride = jcs.image_width * nComponent;
while (jcs.next_scanline < jcs.image_height) {row_pointer[0] = &data[jcs.next_scanline * row_stride];
jpeg_write_scanlines(&jcs, row_pointer, 1);
}
jpeg_finish_compress(&jcs);
jpeg_destroy_compress(&jcs);
fclose(f);
return 1;
}
typedef struct {uint8_t r;
uint8_t g;
uint8_t b;
} rgb;
char* jstrinTostring(JNIEnv* env, jbyteArray barr) {char* rtn = NULL;
jsize alen = (*env)->GetArrayLength(env, barr);
jbyte* ba = (*env)->GetByteArrayElements(env, barr, 0);
if (alen > 0) {rtn = (char*) malloc(alen + 1);
memcpy(rtn, ba, alen);
rtn[alen] = 0;
}
(*env)->ReleaseByteArrayElements(env, barr, ba, 0);
return rtn;
}
jstring Java_com_effective_bitmap_utils_EffectiveBitmapUtils_compressBitmap(JNIEnv* env,
jobject thiz, jobject bitmapcolor, int w, int h, int quality,
jbyteArray fileNameStr, jboolean optimize) {AndroidBitmapInfo infocolor;
BYTE* pixelscolor;
int ret;
BYTE * data;
BYTE *tmpdata;
char * fileName = jstrinTostring(env, fileNameStr);
if ((ret = AndroidBitmap_getInfo(env, bitmapcolor, &infocolor)) < 0) { LOGE("AndroidBitmap_getInfo() failed ! error=%d", ret);return (*env)->NewStringUTF(env, "0");;
}
if ((ret = AndroidBitmap_lockPixels(env, bitmapcolor, (void**)&pixelscolor)) < 0) { LOGE("AndroidBitmap_lockPixels() failed ! error=%d", ret);}
BYTE r, g, b;
data = NULL;
data = malloc(w * h * 3);
tmpdata = data;
int j = 0, i = 0;
int color;
for (i = 0; i < h; i++) { for (j = 0; j < w; j++) {color = *((int *) pixelscolor);
r = ((color & 0x00FF0000) >> 16);
g = ((color & 0x0000FF00) >> 8);
b = color & 0x000000FF;
*data = b;
*(data + 1) = g;
*(data + 2) = r;
data = data + 3;
pixelscolor += 4;
}
}
AndroidBitmap_unlockPixels(env, bitmapcolor);
int resultCode= generateJPEG(tmpdata, w, h, quality, fileName, optimize);
free(tmpdata);
if(resultCode==0){jstring result=(*env)->NewStringUTF(env, error);
error=NULL;
return result;
}
return (*env)->NewStringUTF(env, "1"); //success
}
6、 ... and 、 Three level cache (LruCache and DiskLruCache Realization )
After first loading pictures from the network , Cache pictures in memory and sd In the card . such , We don't have to load pictures on the Internet frequently , For very good memory control , Will consider using LruCache As Bitmap A storage container in memory , stay sd Card is used DiskLruCache To uniformly manage the image cache on the disk .
SoftReference and inBitmap Combination of parameters
Store in this way as LruCache Obsolete reuse pool
Adopt LruCache As storage Bitmap The container of , And in the LruCache There is a method worth noting in , That's it entryRemoved, According to the statement given in the document , stay LruCache This method will be called when the container is full and it is necessary to eliminate the stored objects to make room ( Be careful . It's just that the object is eliminated LruCache Containers , But it doesn't mean that the memory of the object will be Dalvik The virtual machine is recycled ), At this point, it is possible to Bitmap Use SoftReference Wrap it up , And use a prepared one HashSet Containers to hold these things that are going to be recycled Bitmap. Someone will ask. . What's the point of storing like this ? The reason for this storage , It must be mentioned again inBitmap Parameters ( stay Android3.0 Just started , Please refer to API Medium BitmapFactory.Options Parameter information ). This parameter is mainly provided for us to reuse the memory Bitmap.
When the above conditions are met . The system carries out decoder It will check if there is any reusable in memory Bitmap. Avoid us going to SD The card is loaded with pictures and the system performance is degraded , After all, reusing directly from memory is better than reusing directly from memory SD On the card IO The efficiency of operation should be improved dozens of times .
边栏推荐
- Tencent News's practice based on Flink pipeline model
- Activiti practice
- JWT implementation
- Question: how to understand the network protocol and why the OSI reference model is divided into seven layers
- How does the national standard gb28181 security video platform easygbs download device video through the interface?
- Digital transformation solution for supply chain platform of mechanical equipment industry
- Micro build low code tutorial - variable definition
- Using h5ai to build Download Station
- [tutorial] build a personal email system using Tencent lightweight cloud
- Assembly deployment process
猜你喜欢

应用实践 | Apache Doris 整合 Iceberg + Flink CDC 构建实时湖仓一体的联邦查询分析架构

从CVPR 2022看域泛化(Domain Generalization)最新研究进展

北大、加州伯克利大学等联合| Domain-Adaptive Text Classification with Structured Knowledge from Unlabeled Data(基于未标记数据的结构化知识的领域自适应文本分类)

万字长文!一文搞懂InheritedWidget 局部刷新机制

SLSA: 成功SBOM的促进剂

Leetcode algorithm interview sprint sorting algorithm theory (32)

使用 Provider 改造屎一样的代码,代码量降低了2/3!

University of North China, Berkeley University of California, etc. | Domain Adaptive Text Classification with structural Knowledge from unlabeled data

Error running PyUIC: Cannot start process, the working directory ‘-m PyQt5. uic. pyuic register. ui -o

ICML2022 | 基于对比学习的离线元强化学习的鲁棒任务表示
随机推荐
JWT implementation
Summary of redis Functions PHP version
[log service CLS] one click to start the efficient operation and maintenance journey of Tencent E-Sign
Object declaration
Digital transformation solution for supply chain platform of mechanical equipment industry
How to defend the security importance of API gateway
How do I install the API gateway? What should I pay attention to?
Icml2022 | robust task representation for off-line meta reinforcement learning based on contrastive learning
Devops sharing: how to hold the meeting?
Judge whether the target class conforms to the section rule
Assembly deployment process
从CVPR 2022看域泛化(Domain Generalization)最新研究进展
H264_ AVC analysis
2021-12-19: find the missing numbers in all arrays. Give you an n
In the new easygbs kernel version, the intranet mapping to the public network cannot be played. How to troubleshoot?
How to transfer files from the local fortress server
The time deviation is more than 15 hours (54000 seconds), and the time cannot be automatically calibrated
Advantages of micro service registry Nacos over Eureka
Benchclock: a benchmark for evaluating semantic analysis language models
How to set the life cycle of API gateway