【问题标题】:Memory efficient bitmap handling in mono for android用于 android 的 mono 中的内存高效位图处理
【发布时间】:2013-07-10 21:13:35
【问题描述】:

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

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

    /*
     *  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. 丢弃所有东西,以便清除内存。

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

【问题讨论】:

  • 不能在服务器端调整大小吗?您也不使用(或处置)您的 bm 变量,这是为什么呢?
  • @Chintan 虽然你的方法是正确的,但它并不总是给出正确的 inSampleSize。在这里查看我的评论:link
  • @Cinthan,您的代码仍将位图加载到内存中。如果我这样做,它会从 ~6mb 内存飙升到 ~42mb。这是一个巨大的差异,必须有另一种处理方式。我想知道 Google+ 和 Facebook 在拍照后如何处理。

标签: c# android xamarin.android xamarin


【解决方案1】:
        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);

        }

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-11-04
    • 2012-05-04
    • 1970-01-01
    • 2023-04-08
    • 1970-01-01
    • 1970-01-01
    • 2010-10-29
    相关资源
    最近更新 更多