用于android的单声道内存高效位图处理

时间:2013-07-12 10:37:51

标签: c# android xamarin.android xamarin

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

无论如何,我用来将位图加载到内存然后操作它的代码似乎占用了大量内存。目前正在使用此代码:

    /*
     *  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. 处理初始位图。 (但永远不要删除内存中的缩放位图)。
  6. 我真正应该做的事情:

    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;
      
      }
      
    2.   

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

      1. 现在我知道了真正的尺寸。所以我可以在将其加载到内存之前对其进行下采样。<​​/ li>
      2. (在我的例子中)将位图转换为base64字符串并发送它。
      3. 处理所有内容,以便清除内存。
      4. 我目前无法测试这个,因为我不在我的开发机器上。如果这是正确的方法,任何人都可以给我一些反馈吗?我们将不胜感激。

1 个答案:

答案 0 :(得分: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);

        }