处理大图像

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 属性 - 并查看基于文档的其他内存优化技术:

https://developer.android.com/training/articles/memory.html

正如你所说:

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,然后使用libjpeglib解码图片显示出来。接下来是一些示例代码,调用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;
}

希望能帮到你!