处理大图像
Handling large images
我正在构建一个应用程序来处理 超大图像,例如 10000X5000 像素。当我尝试在 ImageView 上显示此类图像时,该应用程序崩溃了。
我无法进行位图采样,因为我无法更改图像的大小。
一个简单的工作就是select一张10000X5000像素的图片,显示在一个ImageView中。
目前,我为此目的使用了简单的代码,但应用却崩溃了。
Uri selectedImage = data.getData();
String[] filePathColumn = { MediaStore.Images.Media.DATA };
// Get the cursor
Cursor cursor = getContentResolver().query(selectedImage,
filePathColumn, null, null, null);
// Move to first row
cursor.moveToFirst();
int columnIndex = cursor.getColumnIndex(filePathColumn[0]);
imgDecodableString = cursor.getString(columnIndex);
cursor.close();
ImageView imgView = (ImageView) findViewById(R.id.imgView);
// Set the Image in ImageView after decoding the String
imgView.setImageBitmap(BitmapFactory
.decodeFile(imgDecodableString));
异常是 OutOfMemory
E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.example.star.largeiamgesdemo,
PID: 21155
java.lang.OutOfMemoryError: Failed to allocate a 200000012 byte
allocation with 4194208 free bytes and 123MB until OOM
MemoryMapFile
之类的东西可能会奏效,但它是针对 FileChannel
中的图像没有得到任何信息的文件
更新
我无法在显示时缩小图像,因为我必须对其进行编辑(即用户可以在图像上写一些东西,或将其他图像作为水印放在它们上面)
谢谢
很明显,您必须减小图像大小,否则 phone 无法渲染图像以在 ImageView
中显示。因此,您需要缩小图像,以便图像可以显示在您的 ImageView
.
int imageScaleWidth = 480; // Change the width
Bitmap bitmapImage = BitmapFactory.decodeFile(imgDecodableString); // imgDecodableString is your image path.
int imageScaleHeigth = (int) ( bitmapImage.getHeight() * (imageScaleWidth / bitmapImage.getWidth()) );
Bitmap scaled = Bitmap.createScaledBitmap(bitmapImage, imageScaleWidth , imageScaleHeigth , true);
imgView.setImageBitmap(scaled);
您应该阅读 "Loading Large Bitmaps Efficiently"
上的 Android 文档
特别是,您提到您不能为 "bitmap sampling" 但请注意 Android 允许您读取图像文件大小,然后计算适用于可用大小的采样。 Android 然后将只加载 sampled 位图,这很好,因为屏幕分辨率无论如何都不足以正确显示这样的图像。
另外,一定要考虑缩略图的方法decodeSampledBitmapFromResource
。
https://developer.android.com/training/displaying-bitmaps/load-bitmap.html#load-bitmap
最后,如果需要,请务必使用 largeHeap
Manifest
属性 - 并查看基于文档的其他内存优化技术:
正如你所说:
I can't go for bitmap sampling as I can't change the size of images.
所以,缩小图像尺寸或在展示前对其进行采样是不行的。我认为下面是一个解决方案。
因为 Android 的 dalvik 限制,您可以使用最大堆大小。当超过它时,崩溃发生。
为了绕过dalvik限制,可以使用jni从native heap申请内存。你必须在 jni 中使用 libjpeg
。可以选择在SurfaceView上显示图片,然后通过holder.getSurface()
得到一个Surface Object,传递给jni,然后使用libjpeg
lib解码图片显示出来。接下来是一些示例代码,调用showJPG()
函数显示大图。
JNIEXPORT void JNICALL Java_com_example_photoprocessing_activity_SurfaceProcessingActivity_showJPG(
JNIEnv * env, jobject activity, jobject surface, jstring img) {
const char * imgChar;
jboolean * isCopy;
imgChar = env->GetStringUTFChars(img, 0);
ANativeWindow_Buffer nwBuffer;
LOGI("img path : %s ",imgChar);
LOGI("ANativeWindow_fromSurface ");
ANativeWindow * mANativeWindow = ANativeWindow_fromSurface(env, surface);
if (mANativeWindow == NULL) {
LOGE("ANativeWindow_fromSurface error");
return;
}
LOGI("ANativeWindow_lock ");
if (0 != ANativeWindow_lock(mANativeWindow, &nwBuffer, 0)) {
LOGE("ANativeWindow_lock error");
return;
}
read_jpeg_file_show(imgChar, nwBuffer);
if (nwBuffer.format == WINDOW_FORMAT_RGBA_8888) {
LOGI("nwBuffer->format == WINDOW_FORMAT_RGBA_8888 ");
}
LOGI("ANativeWindow_unlockAndPost ");
if (0 != ANativeWindow_unlockAndPost(mANativeWindow)) {
LOGE("ANativeWindow_unlockAndPost error");
return;
}
env->ReleaseStringUTFChars(img,imgChar);
ANativeWindow_release(mANativeWindow);
LOGI("ANativeWindow_release ");
return;
}
int read_jpeg_file_show(const char *input_filename,
ANativeWindow_Buffer& nwBuffer) {
struct jpeg_decompress_struct cinfo;
struct jpeg_error_mgr jerr;
FILE *input_file;
JSAMPARRAY buffer;
int row_width;
unsigned char *buffertmp;
cinfo.err = jpeg_std_error(&jerr);
if ((input_file = fopen(input_filename, "rb")) == NULL) {
fprintf(stderr, "can't open %s\n", input_filename);
LOGI("can't open jpg1");
return -1;
}
jpeg_create_decompress(&cinfo);
/* Specify data source for decompression */
jpeg_stdio_src(&cinfo, input_file);
/* Read file header, set default decompression parameters */
(void) jpeg_read_header(&cinfo, TRUE);
/* Start decompressor */
(void) jpeg_start_decompress(&cinfo);
row_width = cinfo.output_width * cinfo.output_components;
buffer = (*cinfo.mem->alloc_sarray)((j_common_ptr) &cinfo, JPOOL_IMAGE,
row_width, 1);
buffertmp = (unsigned char *) malloc(row_width);
memset(buffertmp, 0, row_width);
LOGI("malloc and memset");
/* Process data */
int get8h5 = 248, get8h6 = 252;
__uint16_t * line = (__uint16_t *) nwBuffer.bits;
int wheight = 0;
int scalew = 1, scaleh = 1;
if (cinfo.output_width > nwBuffer.width) {
scalew = cinfo.output_width / nwBuffer.width;
}
LOGI(" scale of img = %d", scalew);
for (int i = 0, choosehNum = 0; i < cinfo.output_height; i++) {
jpeg_read_scanlines(&cinfo, buffer, 1);
buffertmp = *buffer;
if (i % scalew == 0 && choosehNum++ < nwBuffer.height) {
//LOGI("nwBuffer->format == WINDOW_FORMAT_RGB_565");
for (int j = 0, choosewNum = 0; j < cinfo.output_width; j++) {
if (j % scalew == 0) {
if (nwBuffer.format == WINDOW_FORMAT_RGB_565) {
line[choosewNum] = ((__uint16_t ) buffertmp[3 * j + 0]
& get8h5) << 8
| ((__uint16_t ) (buffertmp[3 * j + 1] & get8h6)
<< 3)
| ((__uint16_t ) (buffertmp[3 * j + 2] & get8h6)
>> 3);
choosewNum++;
}
}
}
line = line + nwBuffer.stride;
}
}
(void) jpeg_finish_decompress(&cinfo);
LOGI("jpeg_finish_decompress !!");
jpeg_destroy_decompress(&cinfo);
LOGI("jpeg_destroy_decompress !!");
/* Close files, if we opened them */
fclose(input_file);
return 0;
}
希望能帮到你!
我正在构建一个应用程序来处理 超大图像,例如 10000X5000 像素。当我尝试在 ImageView 上显示此类图像时,该应用程序崩溃了。 我无法进行位图采样,因为我无法更改图像的大小。
一个简单的工作就是select一张10000X5000像素的图片,显示在一个ImageView中。
目前,我为此目的使用了简单的代码,但应用却崩溃了。
Uri selectedImage = data.getData();
String[] filePathColumn = { MediaStore.Images.Media.DATA };
// Get the cursor
Cursor cursor = getContentResolver().query(selectedImage,
filePathColumn, null, null, null);
// Move to first row
cursor.moveToFirst();
int columnIndex = cursor.getColumnIndex(filePathColumn[0]);
imgDecodableString = cursor.getString(columnIndex);
cursor.close();
ImageView imgView = (ImageView) findViewById(R.id.imgView);
// Set the Image in ImageView after decoding the String
imgView.setImageBitmap(BitmapFactory
.decodeFile(imgDecodableString));
异常是 OutOfMemory
E/AndroidRuntime: FATAL EXCEPTION: main Process: com.example.star.largeiamgesdemo,
PID: 21155 java.lang.OutOfMemoryError: Failed to allocate a 200000012 byte allocation with 4194208 free bytes and 123MB until OOM
MemoryMapFile
之类的东西可能会奏效,但它是针对 FileChannel
更新
我无法在显示时缩小图像,因为我必须对其进行编辑(即用户可以在图像上写一些东西,或将其他图像作为水印放在它们上面)
谢谢
很明显,您必须减小图像大小,否则 phone 无法渲染图像以在 ImageView
中显示。因此,您需要缩小图像,以便图像可以显示在您的 ImageView
.
int imageScaleWidth = 480; // Change the width
Bitmap bitmapImage = BitmapFactory.decodeFile(imgDecodableString); // imgDecodableString is your image path.
int imageScaleHeigth = (int) ( bitmapImage.getHeight() * (imageScaleWidth / bitmapImage.getWidth()) );
Bitmap scaled = Bitmap.createScaledBitmap(bitmapImage, imageScaleWidth , imageScaleHeigth , true);
imgView.setImageBitmap(scaled);
您应该阅读 "Loading Large Bitmaps Efficiently"
上的 Android 文档特别是,您提到您不能为 "bitmap sampling" 但请注意 Android 允许您读取图像文件大小,然后计算适用于可用大小的采样。 Android 然后将只加载 sampled 位图,这很好,因为屏幕分辨率无论如何都不足以正确显示这样的图像。
另外,一定要考虑缩略图的方法decodeSampledBitmapFromResource
。
https://developer.android.com/training/displaying-bitmaps/load-bitmap.html#load-bitmap
最后,如果需要,请务必使用 largeHeap
Manifest
属性 - 并查看基于文档的其他内存优化技术:
正如你所说:
I can't go for bitmap sampling as I can't change the size of images.
所以,缩小图像尺寸或在展示前对其进行采样是不行的。我认为下面是一个解决方案。
因为 Android 的 dalvik 限制,您可以使用最大堆大小。当超过它时,崩溃发生。
为了绕过dalvik限制,可以使用jni从native heap申请内存。你必须在 jni 中使用 libjpeg
。可以选择在SurfaceView上显示图片,然后通过holder.getSurface()
得到一个Surface Object,传递给jni,然后使用libjpeg
lib解码图片显示出来。接下来是一些示例代码,调用showJPG()
函数显示大图。
JNIEXPORT void JNICALL Java_com_example_photoprocessing_activity_SurfaceProcessingActivity_showJPG(
JNIEnv * env, jobject activity, jobject surface, jstring img) {
const char * imgChar;
jboolean * isCopy;
imgChar = env->GetStringUTFChars(img, 0);
ANativeWindow_Buffer nwBuffer;
LOGI("img path : %s ",imgChar);
LOGI("ANativeWindow_fromSurface ");
ANativeWindow * mANativeWindow = ANativeWindow_fromSurface(env, surface);
if (mANativeWindow == NULL) {
LOGE("ANativeWindow_fromSurface error");
return;
}
LOGI("ANativeWindow_lock ");
if (0 != ANativeWindow_lock(mANativeWindow, &nwBuffer, 0)) {
LOGE("ANativeWindow_lock error");
return;
}
read_jpeg_file_show(imgChar, nwBuffer);
if (nwBuffer.format == WINDOW_FORMAT_RGBA_8888) {
LOGI("nwBuffer->format == WINDOW_FORMAT_RGBA_8888 ");
}
LOGI("ANativeWindow_unlockAndPost ");
if (0 != ANativeWindow_unlockAndPost(mANativeWindow)) {
LOGE("ANativeWindow_unlockAndPost error");
return;
}
env->ReleaseStringUTFChars(img,imgChar);
ANativeWindow_release(mANativeWindow);
LOGI("ANativeWindow_release ");
return;
}
int read_jpeg_file_show(const char *input_filename,
ANativeWindow_Buffer& nwBuffer) {
struct jpeg_decompress_struct cinfo;
struct jpeg_error_mgr jerr;
FILE *input_file;
JSAMPARRAY buffer;
int row_width;
unsigned char *buffertmp;
cinfo.err = jpeg_std_error(&jerr);
if ((input_file = fopen(input_filename, "rb")) == NULL) {
fprintf(stderr, "can't open %s\n", input_filename);
LOGI("can't open jpg1");
return -1;
}
jpeg_create_decompress(&cinfo);
/* Specify data source for decompression */
jpeg_stdio_src(&cinfo, input_file);
/* Read file header, set default decompression parameters */
(void) jpeg_read_header(&cinfo, TRUE);
/* Start decompressor */
(void) jpeg_start_decompress(&cinfo);
row_width = cinfo.output_width * cinfo.output_components;
buffer = (*cinfo.mem->alloc_sarray)((j_common_ptr) &cinfo, JPOOL_IMAGE,
row_width, 1);
buffertmp = (unsigned char *) malloc(row_width);
memset(buffertmp, 0, row_width);
LOGI("malloc and memset");
/* Process data */
int get8h5 = 248, get8h6 = 252;
__uint16_t * line = (__uint16_t *) nwBuffer.bits;
int wheight = 0;
int scalew = 1, scaleh = 1;
if (cinfo.output_width > nwBuffer.width) {
scalew = cinfo.output_width / nwBuffer.width;
}
LOGI(" scale of img = %d", scalew);
for (int i = 0, choosehNum = 0; i < cinfo.output_height; i++) {
jpeg_read_scanlines(&cinfo, buffer, 1);
buffertmp = *buffer;
if (i % scalew == 0 && choosehNum++ < nwBuffer.height) {
//LOGI("nwBuffer->format == WINDOW_FORMAT_RGB_565");
for (int j = 0, choosewNum = 0; j < cinfo.output_width; j++) {
if (j % scalew == 0) {
if (nwBuffer.format == WINDOW_FORMAT_RGB_565) {
line[choosewNum] = ((__uint16_t ) buffertmp[3 * j + 0]
& get8h5) << 8
| ((__uint16_t ) (buffertmp[3 * j + 1] & get8h6)
<< 3)
| ((__uint16_t ) (buffertmp[3 * j + 2] & get8h6)
>> 3);
choosewNum++;
}
}
}
line = line + nwBuffer.stride;
}
}
(void) jpeg_finish_decompress(&cinfo);
LOGI("jpeg_finish_decompress !!");
jpeg_destroy_decompress(&cinfo);
LOGI("jpeg_destroy_decompress !!");
/* Close files, if we opened them */
fclose(input_file);
return 0;
}
希望能帮到你!