【问题标题】:Scaled Bitmap maintaining aspect ratio缩放位图保持纵横比
【发布时间】:2013-03-04 15:41:48
【问题描述】:

我想将Bitmap 缩放到与运行时相关的宽度和高度,其中保持纵横比,Bitmap 填充整个宽度并将图像垂直居中,裁剪多余部分或填充间隙具有 0 个 alpha 像素。

我目前正在自己​​重绘位图,方法是创建一个所有 0 alpha 像素的 Bitmap 并在其顶部绘制图像 Bitmap,缩放到精确的指定宽度并保持纵横比,但是,它结束了丢失/搞砸像素数据。

这是我的做法:

Bitmap background = Bitmap.createBitmap((int)width, (int)height, Config.ARGB_8888);
float originalWidth = originalImage.getWidth(), originalHeight = originalImage.getHeight();
Canvas canvas = new Canvas(background);
float scale = width/originalWidth;
float xTranslation = 0.0f, yTranslation = (height - originalHeight * scale)/2.0f;
Matrix transformation = new Matrix();
transformation.postTranslate(xTranslation, yTranslation);
transformation.preScale(scale, scale);
canvas.drawBitmap(originalImage, transformation, null);
return background;

是否有一个库或一些更好的代码可以更好地做到这一点?我希望图像看起来尽可能清晰,但我知道我的函数不会提供很好的结果。

我知道我可以通过使用整数缩放而不是浮点缩放来使图像保持正常,但我需要 100% 填充宽度。

另外,我知道ImageViewGravity.CENTER_CROP 功能,但是,它也使用整数缩放,所以它会在不应该的时候切断图像的宽度。

【问题讨论】:

    标签: android bitmap scaling


    【解决方案1】:

    这将尊重 maxWidth 和 maxHeight,这意味着生成的位图的尺寸永远不会大于这些:

     private static Bitmap resize(Bitmap image, int maxWidth, int maxHeight) {
        if (maxHeight > 0 && maxWidth > 0) {
            int width = image.getWidth();
            int height = image.getHeight();
            float ratioBitmap = (float) width / (float) height;
            float ratioMax = (float) maxWidth / (float) maxHeight;
    
            int finalWidth = maxWidth;
            int finalHeight = maxHeight;
            if (ratioMax > ratioBitmap) {
                finalWidth = (int) ((float)maxHeight * ratioBitmap);
            } else {
                finalHeight = (int) ((float)maxWidth / ratioBitmap);
            }
            image = Bitmap.createScaledBitmap(image, finalWidth, finalHeight, true);
            return image;
        } else {
            return image;
        }
    }
    

    【讨论】:

    • 解决方案的答案裁剪图像,但此解决方案工作完美。
    • @Arslan 我没有使用这种方法进行任何裁剪。你能详细说明一下吗?
    • @JoshPinter “这个问题的公认答案导致裁剪。这个答案对我来说非常有效,因为它不会导致裁剪。”
    • 如果我的宽度和高度是 1200x1200 并且我的最大宽度和高度是 700x700 rationBitmap 和 rationMax 都是 0.0 所以这是一个有效的条件或不是因为之后它将被除以零异常跨度>
    • 对我来说完美:)
    【解决方案2】:

    这个呢:

    Bitmap background = Bitmap.createBitmap((int)width, (int)height, Config.ARGB_8888);
    
    float originalWidth = originalImage.getWidth(); 
    float originalHeight = originalImage.getHeight();
    
    Canvas canvas = new Canvas(background);
    
    float scale = width / originalWidth;
    
    float xTranslation = 0.0f;
    float yTranslation = (height - originalHeight * scale) / 2.0f;
    
    Matrix transformation = new Matrix();
    transformation.postTranslate(xTranslation, yTranslation);
    transformation.preScale(scale, scale);
    
    Paint paint = new Paint();
    paint.setFilterBitmap(true);
    
    canvas.drawBitmap(originalImage, transformation, paint);
    
    return background;
    

    我添加了一个paint 来过滤缩放的位图。

    【讨论】:

    • 嗯,比例不能改变,因为我需要填充宽度,它也不能裁剪宽度。然而,油漆起到了作用。谢谢!您可以编辑答案以切换回我的缩放比例吗?
    • 但它正在裁剪图像
    • 就是这样!终于
    【解决方案3】:

    这是我的 Utils 类中的一个方法,它可以完成这项工作:

    public static Bitmap scaleBitmapAndKeepRation(Bitmap targetBmp,int reqHeightInPixels,int reqWidthInPixels)
        {
            Matrix matrix = new Matrix();
            matrix .setRectToRect(new RectF(0, 0, targetBmp.getWidth(), targetBmp.getHeight()), new RectF(0, 0, reqWidthInPixels, reqHeightInPixels), Matrix.ScaleToFit.CENTER);
            Bitmap scaledBitmap = Bitmap.createBitmap(targetBmp, 0, 0, targetBmp.getWidth(), targetBmp.getHeight(), matrix, true);
            return scaledBitmap;
        }
    

    【讨论】:

    • 注意这只是 API 17 (Jellybean) 或更高版本
    • 整洁!! .那很简单。除了变量命名约定。最好更改 TargetBmp -> targetBmp。适用于 portraitlandscape 图像
    • 当我们使用这种方法缩放位图时,位图质量会下降。不适合我。请给我另一个解决方案。
    • @RishavSingla 您正在请求解决调整图像大小的固有成本问题。它不是向量。调整大小是一项有损操作。
    【解决方案4】:

    这里我有一个经过测试的解决方案,我从位图文件中创建了一个缩放位图:

        int scaleSize =1024;
    
        public Bitmap resizeImageForImageView(Bitmap bitmap) {
            Bitmap resizedBitmap = null;
            int originalWidth = bitmap.getWidth();
            int originalHeight = bitmap.getHeight();
            int newWidth = -1;
            int newHeight = -1;
            float multFactor = -1.0F;
            if(originalHeight > originalWidth) {
                newHeight = scaleSize ;
                multFactor = (float) originalWidth/(float) originalHeight;
                newWidth = (int) (newHeight*multFactor);
            } else if(originalWidth > originalHeight) {
                newWidth = scaleSize ;
                multFactor = (float) originalHeight/ (float)originalWidth;
                newHeight = (int) (newWidth*multFactor);
            } else if(originalHeight == originalWidth) {
                newHeight = scaleSize ;
                newWidth = scaleSize ;
            }
            resizedBitmap = Bitmap.createScaledBitmap(bitmap, newWidth, newHeight, false);
            return resizedBitmap;
        }
    

    请注意,我需要最大尺寸为 4096x4096 像素的缩放位图,但在调整大小时需要保持纵横比。如果您需要其他宽度或高度值,只需替换值“4096”即可。

    这只是对 Coen 答案的补充,但他的代码中的问题是他计算比率的那一行。将两个整数相除得到一个整数,如果结果为

    【讨论】:

    • 我建议将新的大小值提取到变量或参数中以避免重复。您也可以使用新的大小值初始化 newWidth 和 newHeight 并仅重新设置更改以保留比例的变量(也不需要预先初始化 multFactor 或 resizedBitmap)。
    • 你绝对是对的@Christopher Reichel,上面的代码并没有缩放所有的尺寸,只是宽度大于高度的尺寸。这个答案更好,但@joaomgcd 的答案比这更好,因为代码优化和方法参数。
    【解决方案5】:

    以上答案都不适合我,我只是创建了一个方法,将所有尺寸设置为所需的尺寸,并将空白区域涂成黑色。这是我的方法:

    /**
     * Scale the image preserving the ratio
     * @param imageToScale Image to be scaled
     * @param destinationWidth Destination width after scaling
     * @param destinationHeight Destination height after scaling
     * @return New scaled bitmap preserving the ratio
     */
    public static Bitmap scalePreserveRatio(Bitmap imageToScale, int destinationWidth,
            int destinationHeight) {
        if (destinationHeight > 0 && destinationWidth > 0 && imageToScale != null) {
            int width = imageToScale.getWidth();
            int height = imageToScale.getHeight();
    
            //Calculate the max changing amount and decide which dimension to use
            float widthRatio = (float) destinationWidth / (float) width;
            float heightRatio = (float) destinationHeight / (float) height;
    
            //Use the ratio that will fit the image into the desired sizes
            int finalWidth = (int)Math.floor(width * widthRatio);
            int finalHeight = (int)Math.floor(height * widthRatio);
            if (finalWidth > destinationWidth || finalHeight > destinationHeight) {
                finalWidth = (int)Math.floor(width * heightRatio);
                finalHeight = (int)Math.floor(height * heightRatio);
            }
    
            //Scale given bitmap to fit into the desired area
            imageToScale = Bitmap.createScaledBitmap(imageToScale, finalWidth, finalHeight, true);
    
            //Created a bitmap with desired sizes
            Bitmap scaledImage = Bitmap.createBitmap(destinationWidth, destinationHeight, Bitmap.Config.ARGB_8888);
            Canvas canvas = new Canvas(scaledImage);
    
            //Draw background color
            Paint paint = new Paint();
            paint.setColor(Color.BLACK);
            paint.setStyle(Paint.Style.FILL);
            canvas.drawRect(0, 0, canvas.getWidth(), canvas.getHeight(), paint);
    
            //Calculate the ratios and decide which part will have empty areas (width or height)
            float ratioBitmap = (float)finalWidth / (float)finalHeight;
            float destinationRatio = (float) destinationWidth / (float) destinationHeight;
            float left = ratioBitmap >= destinationRatio ? 0 : (float)(destinationWidth - finalWidth) / 2;
            float top = ratioBitmap < destinationRatio ? 0: (float)(destinationHeight - finalHeight) / 2;
            canvas.drawBitmap(imageToScale, left, top, null);
    
            return scaledImage;
        } else {
            return imageToScale;
        }
    }
    

    例如;

    假设您有一个 100 x 100 的图像,但所需的尺寸是 300x50,那么此方法会将您的图像转换为 50 x 50 并将其绘制成尺寸为 300 x 50 的新图像(空文件将是黑色的)。

    另一个例子:假设您有一个 600 x 1000 的图像,并且所需的尺寸再次为 300 x 50,那么您的图像将被转换为 30 x 50 并绘制成一个尺寸为 300 x 50 的新创建的图像.

    我认为这就是它必须的,卢比。

    【讨论】:

    • 我的用例的完美答案
    • 很好,也适用于我的用例。有效地计算宽度和高度。
    • 我投了赞成票,因为它可以同时完成这两项工作,使位图在不丢失纵横比的情况下进行缩放,并使位图居中。
    【解决方案6】:

    更简单的解决方案:注意我们将宽度设置为 500 像素

     public void scaleImageKeepAspectRatio()
        {
            int imageWidth = scaledGalleryBitmap.getWidth();
            int imageHeight = scaledGalleryBitmap.getHeight();
            int newHeight = (imageHeight * 500)/imageWidth;
            scaledGalleryBitmap = Bitmap.createScaledBitmap(scaledGalleryBitmap, 500, newHeight, false);
    
        }
    

    【讨论】:

      【解决方案7】:

      也可以通过自己计算比例来完成,像这样。

      private Bitmap scaleBitmap(Bitmap bm) {
          int width = bm.getWidth();
          int height = bm.getHeight();
      
          Log.v("Pictures", "Width and height are " + width + "--" + height);
      
          if (width > height) {
              // landscape
              int ratio = width / maxWidth;
              width = maxWidth;
              height = height / ratio;
          } else if (height > width) {
              // portrait
              int ratio = height / maxHeight;
              height = maxHeight;
              width = width / ratio;
          } else {
              // square
              height = maxHeight;
              width = maxWidth;
          }
      
          Log.v("Pictures", "after scaling Width and height are " + width + "--" + height);
      
          bm = Bitmap.createScaledBitmap(bm, width, height, true);
          return bm;
      }
      

      【讨论】:

      • 如果你缩小比例,比率为0,它会抛出一个异常除以0
      • 是的,无论如何,它的解决方案才是正确的?秃鹰总是准备在这里投反对票....叹息:-)
      【解决方案8】:

      在 Gowrav 的回答中添加了 RESIZE_CROP。

         enum RequestSizeOptions {
          RESIZE_FIT,
          RESIZE_INSIDE,
          RESIZE_EXACT,
          RESIZE_CENTRE_CROP
      }
      static Bitmap resizeBitmap(Bitmap bitmap, int reqWidth, int reqHeight, RequestSizeOptions options) {
          try {
              if (reqWidth > 0 && reqHeight > 0 && (options == RequestSizeOptions.RESIZE_FIT ||
                      options == RequestSizeOptions.RESIZE_INSIDE ||
                      options == RequestSizeOptions.RESIZE_EXACT || options == RequestSizeOptions.RESIZE_CENTRE_CROP)) {
      
                  Bitmap resized = null;
                  if (options == RequestSizeOptions.RESIZE_EXACT) {
                      resized = Bitmap.createScaledBitmap(bitmap, reqWidth, reqHeight, false);
                  } else {
                      int width = bitmap.getWidth();
                      int height = bitmap.getHeight();
                      float scale = Math.max(width / (float) reqWidth, height / (float) reqHeight);
                      if (scale > 1 || options == RequestSizeOptions.RESIZE_FIT) {
                          resized = Bitmap.createScaledBitmap(bitmap, (int) (width / scale), (int) (height / scale), false);
                      }
                      if (scale > 1 || options == RequestSizeOptions.RESIZE_CENTRE_CROP) {
                          int smaller_side = (height-width)>0?width:height;
                          int half_smaller_side = smaller_side/2;
                          Rect initialRect = new Rect(0,0,width,height);
                          Rect finalRect = new Rect(initialRect.centerX()-half_smaller_side,initialRect.centerY()-half_smaller_side,
                                  initialRect.centerX()+half_smaller_side,initialRect.centerY()+half_smaller_side);
                          bitmap = Bitmap.createBitmap(bitmap,  finalRect.left, finalRect.top, finalRect.width(), finalRect.height(), null, true);
                          //keep in mind we have square as request for cropping, otherwise - it is useless
                          resized = Bitmap.createScaledBitmap(bitmap, reqWidth, reqHeight, false);
                      }
      
                  }
                  if (resized != null) {
                      if (resized != bitmap) {
                          bitmap.recycle();
                      }
                      return resized;
                  }
              }
          } catch (Exception e) {
              Log.w("AIC", "Failed to resize cropped image, return bitmap before resize", e);
          }
          return bitmap;
      }
      

      【讨论】:

        【解决方案9】:

        基于joaomgcd的回答的Kotlin扩展功能版本

        private fun Bitmap.resize(maxWidth: Int, maxHeight: Int): Bitmap {
            return if (maxHeight > 0 && maxWidth > 0) {
                val width = this.width
                val height = this.height
                val ratioBitmap = width.toFloat() / height.toFloat()
                val ratioMax = maxWidth.toFloat() / maxHeight.toFloat()
                var finalWidth = maxWidth
                var finalHeight = maxHeight
                if (ratioMax > ratioBitmap) {
                    finalWidth = (maxHeight.toFloat() * ratioBitmap).toInt()
                } else {
                    finalHeight = (maxWidth.toFloat() / ratioBitmap).toInt()
                }
                Bitmap.createScaledBitmap(this, finalWidth, finalHeight, true)
            } else this
        }
        

        【讨论】:

          【解决方案10】:

          This is an awesome library from ArthurHub 如果您不想重新发明轮子,可以通过编程和交互方式处理图像裁剪。

          但是如果你喜欢像我这样不臃肿的版本..,这里显示的内部函数是一个相当复杂的执行图像缩放的标准选项

          /**
           * Resize the given bitmap to the given width/height by the given option.<br>
           */
          
          enum RequestSizeOptions {
              RESIZE_FIT,
              RESIZE_INSIDE,
              RESIZE_EXACT
          }
          
          static Bitmap resizeBitmap(Bitmap bitmap, int reqWidth, int reqHeight, RequestSizeOptions options) {
              try {
                  if (reqWidth > 0 && reqHeight > 0 && (options == RequestSizeOptions.RESIZE_FIT ||
                          options == RequestSizeOptions.RESIZE_INSIDE ||
                          options == RequestSizeOptions.RESIZE_EXACT)) {
          
                      Bitmap resized = null;
                      if (options == RequestSizeOptions.RESIZE_EXACT) {
                          resized = Bitmap.createScaledBitmap(bitmap, reqWidth, reqHeight, false);
                      } else {
                          int width = bitmap.getWidth();
                          int height = bitmap.getHeight();
                          float scale = Math.max(width / (float) reqWidth, height / (float) reqHeight);
                          if (scale > 1 || options == RequestSizeOptions.RESIZE_FIT) {
                              resized = Bitmap.createScaledBitmap(bitmap, (int) (width / scale), (int) (height / scale), false);
                          }
                      }
                      if (resized != null) {
                          if (resized != bitmap) {
                              bitmap.recycle();
                          }
                          return resized;
                      }
                  }
              } catch (Exception e) {
                  Log.w("AIC", "Failed to resize cropped image, return bitmap before resize", e);
              }
              return bitmap;
          }
          

          【讨论】:

            【解决方案11】:
            public static Bitmap scaleBitmap(Bitmap bitmap, int wantedWidth, int wantedHeight) {
                float originalWidth = bitmap.getWidth();
                float originalHeight = bitmap.getHeight();
                Bitmap output = Bitmap.createBitmap(wantedWidth, wantedHeight, Config.ARGB_8888);
                Canvas canvas = new Canvas(output);
                Matrix m = new Matrix();
            
                float scalex = wantedWidth/originalWidth;
                float scaley = wantedHeight/originalHeight;
                float xTranslation = 0.0f, yTranslation = (wantedHeight - originalHeight * scaley)/2.0f;
            
                m.postTranslate(xTranslation, yTranslation);
                m.preScale(scalex, scaley);
                // m.setScale((float) wantedWidth / bitmap.getWidth(), (float) wantedHeight / bitmap.getHeight());
                Paint paint = new Paint();
                paint.setFilterBitmap(true);
                canvas.drawBitmap(bitmap, m, paint);
            
                return output;
            }
            

            【讨论】:

              【解决方案12】:

              我的解决方案是这样,它保持纵横比,并且只需要一个尺寸,例如,如果你有一个 1920*1080 和一个 1080*1920 的图像并且你想将其调整为 1280,第一个将是 1280*720第二个是 720*1280

              public static Bitmap resizeBitmap(final Bitmap temp, final int size) {
                      if (size > 0) {
                          int width = temp.getWidth();
                          int height = temp.getHeight();
                          float ratioBitmap = (float) width / (float) height;
                          int finalWidth = size;
                          int finalHeight = size;
                          if (ratioBitmap < 1) {
                              finalWidth = (int) ((float) size * ratioBitmap);
                          } else {
                              finalHeight = (int) ((float) size / ratioBitmap);
                          }
                          return Bitmap.createScaledBitmap(temp, finalWidth, finalHeight, true);
                      } else {
                          return temp;
                      }
                  }
              

              【讨论】:

                【解决方案13】:

                重新缩放图像涉及简单的数学运算,请考虑以下 sn-p 并继续操作, 1. 假设你有一个 720x1280 的 Imaan 图像,并且你想适应 420 的宽度,得到给定数学所需的减少百分比,

                originalWidth = 720;
                wP = 720/100;
                /*  wP = 7.20 is a percentage value */
                
                1. 现在从原始宽度中减去所需的宽度,然后将结果乘以wP。您将获得宽度减少的百分比。

                difference = originalWidth - 420; dP = difference/wP;

                这里dP 将是 41.66,表示您将大小减小 41.66%。因此,您必须将高度降低 41.66(dP) 以保持该图像的比例或比例。 计算如下给出的高度,

                hP = originalHeight / 100;
                //here height percentage will be 1280/100 = 12.80
                height = originalHeight - ( hp * dP);
                // here 1280 - (12.80 * 41.66) = 746.75
                

                这是您的合适比例,您可以调整 420x747 的图像/位图大小。它将返回调整大小的图像而不会丢失比例/比例。

                示例

                public static Bitmap scaleToFit(Bitmap image, int width, int height, bool isWidthReference) {
                    if (isWidthReference) {
                        int originalWidth = image.getWidth();
                        float wP = width / 100;
                        float dP = ( originalWidth - width) / wP;
                        int originalHeight = image.getHeight();
                        float hP = originalHeight / 100;
                        int height = originalHeight - (hP * dP);
                        image = Bitmap.createScaledBitmap(image, width, height, true);
                    } else {
                        int originalHeight = image.getHeight();
                        float hP = height / 100;
                        float dP = ( originalHeight - height) / hP;
                        int originalWidth = image.getWidth();
                        float wP = originalWidth / 100;
                        int width = originalWidth - (wP * dP);
                        image = Bitmap.createScaledBitmap(image, width, height, true);
                    }
                    return image;
                }
                

                在这里,您只是参考高度或宽度参数来缩放图像以适应所需的标准。

                【讨论】:

                  猜你喜欢
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 2011-08-24
                  • 2010-11-25
                  • 2014-01-04
                  • 1970-01-01
                  • 1970-01-01
                  相关资源
                  最近更新 更多