1

我有一个应用程序,它允许用户拍照。拍照后,用户可以将其发送到我的网络服务器。但在我这样做之前,它需要调整位图的大小,因为我喜欢将一致的大小发送到我的网络服务器。

无论如何,我用来将位图加载到内存中然后对其进行操作的代码似乎确实占用了大量内存。当前正在使用此代码:

    /*
     *  This method is used to calculate image size.
     *  And also resize/scale image down to 1600 x 1200
     */
    private void ResizeBitmapAndSendToWebServer(string album_id) {

        Bitmap bm = null;
                    // This line is taking up to much memory each time..
        Bitmap bitmap = MediaStore.Images.Media.GetBitmap(Android.App.Application.Context.ApplicationContext.ContentResolver,fileUri);

                    /*
                     * My question is : Could i do the next image manipulation
                     * before i even load the bitmap into memory?
                     */
        int width = bitmap.Width;
        int height = bitmap.Height;

        if (width >= height) { // <-- Landscape picture

            float scaledWidth = (float)height / width;

            if (width > 1600) {
                bm = Bitmap.CreateScaledBitmap (bitmap, 1600, (int)(1600 * scaledWidth), true);
            } else {
                bm = bitmap;
            }
        } else {

            float scaledHeight = (float)width / height;

            if (height > 1600) {
                bm = Bitmap.CreateScaledBitmap (bitmap, (int)(1600 * scaledHeight), 1600 , true);
            } else {
                bm = bitmap;
            }

        }
                    // End of question code block.

        MemoryStream stream = new MemoryStream ();
        bitmap.Compress (Bitmap.CompressFormat.Jpeg, 80, stream);
        byte[] bitmapData = stream.ToArray ();
        bitmap.Dispose ();

        app.api.SendPhoto (Base64.EncodeToString (bitmapData, Base64Flags.Default), album_id);

    }

解决此类内存问题的好方法是什么?

编辑 1:

在阅读了其他帖子之后,我很清楚我正在用我的代码做一些低效的事情。这就是我一直在做的事情:

  1. 将完整的位图加载到内存中。
  2. 决定它是否是风景。
  3. 然后创建具有正确尺寸的新位图。
  4. 然后将此位图转换为字节数组
  5. 处理初始位图。(但永远不要从内存中删除缩放的位图)。

我真正应该做的是:

  1. 确定实际位图尺寸而不将其加载到内存中:

    private void FancyMethodForDeterminingImageDimensions() {
    
        BitmapFactory.Options options = new BitmapFactory.Options();
        options.InJustDecodeBounds = true;
    
        BitmapFactory.DecodeFile(fileUri.Path, options);
    
        // Now the dimensions of the bitmap are known without loading
        // the bitmap into memory.
        // I am not further going to explain this, i think the purpose is
        // explaining enough.
        int outWidth = options.OutWidth;
        int outHeight = options.OutHeight;
    
    }
    

如果设置为 true,解码器将返回 null(无位图),但 out... 字段仍将设置,允许调用者查询位图而无需为其像素分配内存。

  1. 现在我知道真正的维度了。所以我可以在将其加载到内存之前对其进行下采样。
  2. (在我的情况下)将位图转换为 base64 字符串并发送。
  3. 处理所有东西,以便清除内存。

我目前无法对此进行测试,因为我不在我的开发机器上。如果这是正确的方法,任何人都可以给我一些反馈吗?将不胜感激。

4

1 回答 1

2
        private void ResizeBitmapAndSendToWebServer(string album_id) {

            BitmapFactory.Options options = new BitmapFactory.Options ();
            options.InJustDecodeBounds = true; // <-- This makes sure bitmap is not loaded into memory.
            // Then get the properties of the bitmap
            BitmapFactory.DecodeFile (fileUri.Path, options);
            Android.Util.Log.Debug ("[BITMAP]" , string.Format("Original width : {0}, and height : {1}", options.OutWidth, options.OutHeight) );
            // CalculateInSampleSize calculates the right aspect ratio for the picture and then calculate
            // the factor where it will be downsampled with.
            options.InSampleSize = CalculateInSampleSize (options, 1600, 1200);
            Android.Util.Log.Debug ("[BITMAP]" , string.Format("Downsampling factor : {0}", CalculateInSampleSize (options, 1600, 1200)) );
            // Now that we know the downsampling factor, the right sized bitmap is loaded into memory.
            // So we set the InJustDecodeBounds to false because we now know the exact dimensions.
            options.InJustDecodeBounds = false;
            // Now we are loading it with the correct options. And saving precious memory.
            Bitmap bm = BitmapFactory.DecodeFile (fileUri.Path, options);
            Android.Util.Log.Debug ("[BITMAP]" , string.Format("Downsampled width : {0}, and height : {1}", bm.Width, bm.Height) );
            // Convert it to Base64 by first converting the bitmap to
            // a byte array. Then convert the byte array to a Base64 String.
            MemoryStream stream = new MemoryStream ();
            bm.Compress (Bitmap.CompressFormat.Jpeg, 80, stream);
            byte[] bitmapData = stream.ToArray ();
            bm.Dispose ();

            app.api.SendPhoto (Base64.EncodeToString (bitmapData, Base64Flags.Default), album_id);

        }
于 2013-07-14T11:10:20.660 回答