Чтобы масштабировать изображение, вам нужно создать новое изображение и рисовать в нем. Один из способов — использовать метод filter()
для AffineTransferOp
, как предлагается здесь. Это позволяет выбрать метод интерполяции.
private static BufferedImage scale1(BufferedImage before, double scale) {
int w = before.getWidth();
int h = before.getHeight();
// Create a new image of the proper size
int w2 = (int) (w * scale);
int h2 = (int) (h * scale);
BufferedImage after = new BufferedImage(w2, h2, BufferedImage.TYPE_INT_ARGB);
AffineTransform scaleInstance = AffineTransform.getScaleInstance(scale, scale);
AffineTransformOp scaleOp
= new AffineTransformOp(scaleInstance, AffineTransformOp.TYPE_BILINEAR);
scaleOp.filter(before, after);
return after;
}
Другой способ — просто нарисовать исходное изображение в новом изображении, используя операцию масштабирования для выполнения масштабирования. Этот метод очень похож, но он также иллюстрирует, как вы можете нарисовать все, что захотите, на конечном изображении. (Я ставлю пустую строку там, где два метода начинают различаться.)
private static BufferedImage scale2(BufferedImage before, double scale) {
int w = before.getWidth();
int h = before.getHeight();
// Create a new image of the proper size
int w2 = (int) (w * scale);
int h2 = (int) (h * scale);
BufferedImage after = new BufferedImage(w2, h2, BufferedImage.TYPE_INT_ARGB);
AffineTransform scaleInstance = AffineTransform.getScaleInstance(scale, scale);
AffineTransformOp scaleOp
= new AffineTransformOp(scaleInstance, AffineTransformOp.TYPE_BILINEAR);
Graphics2D g2 = (Graphics2D) after.getGraphics();
// Here, you may draw anything you want into the new image, but we're
// drawing a scaled version of the original image.
g2.drawImage(before, scaleOp, 0, 0);
g2.dispose();
return after;
}
Дополнение: результаты
Чтобы проиллюстрировать различия, я сравнил результаты пяти приведенных ниже методов. Вот как выглядят результаты в увеличенном и уменьшенном масштабе вместе с данными о производительности. (Производительность варьируется от одного прогона к другому, поэтому принимайте эти цифры только как приблизительные ориентиры.) Верхнее изображение — оригинал. Я масштабирую его в двойном и половинном размере.
Как видите, AffineTransformOp.filter()
, используемый в scaleBilinear()
, быстрее, чем стандартный метод рисования Graphics2D.drawImage()
в scale2()
. Также интерполяция BiCubic является самой медленной, но дает наилучшие результаты при расширении изображения. (По производительности его следует сравнивать только с scaleBilinear()
и scaleNearest().
) Билинейный режим кажется лучше для сжатия изображения, хотя это сложный выбор. А NearestNeighbor — самый быстрый, с худшими результатами. Билинейность кажется лучшим компромиссом между скоростью и качеством. Image.getScaledInstance()
, вызванный в методе questionable()
, работал очень плохо и возвращал то же самое низкое качество, что и NearestNeighbor. (Числа производительности даны только для расширения изображения.)
![введите описание изображения здесь](https://i.stack.imgur.com/Rg5m4.png)
public static BufferedImage scaleBilinear(BufferedImage before, double scale) {
final int interpolation = AffineTransformOp.TYPE_BILINEAR;
return scale(before, scale, interpolation);
}
public static BufferedImage scaleBicubic(BufferedImage before, double scale) {
final int interpolation = AffineTransformOp.TYPE_BICUBIC;
return scale(before, scale, interpolation);
}
public static BufferedImage scaleNearest(BufferedImage before, double scale) {
final int interpolation = AffineTransformOp.TYPE_NEAREST_NEIGHBOR;
return scale(before, scale, interpolation);
}
@NotNull
private static
BufferedImage scale(final BufferedImage before, final double scale, final int type) {
int w = before.getWidth();
int h = before.getHeight();
int w2 = (int) (w * scale);
int h2 = (int) (h * scale);
BufferedImage after = new BufferedImage(w2, h2, before.getType());
AffineTransform scaleInstance = AffineTransform.getScaleInstance(scale, scale);
AffineTransformOp scaleOp = new AffineTransformOp(scaleInstance, type);
scaleOp.filter(before, after);
return after;
}
/**
* This is a more generic solution. It produces the same result, but it shows how you
* can draw anything you want into the newly created image. It's slower
* than scaleBilinear().
* @param before The original image
* @param scale The scale factor
* @return A scaled version of the original image
*/
private static BufferedImage scale2(BufferedImage before, double scale) {
int w = before.getWidth();
int h = before.getHeight();
// Create a new image of the proper size
int w2 = (int) (w * scale);
int h2 = (int) (h * scale);
BufferedImage after = new BufferedImage(w2, h2, before.getType());
AffineTransform scaleInstance = AffineTransform.getScaleInstance(scale, scale);
AffineTransformOp scaleOp
= new AffineTransformOp(scaleInstance, AffineTransformOp.TYPE_BILINEAR);
Graphics2D g2 = (Graphics2D) after.getGraphics();
// Here, you may draw anything you want into the new image, but we're just drawing
// a scaled version of the original image. This is slower than
// calling scaleOp.filter().
g2.drawImage(before, scaleOp, 0, 0);
g2.dispose();
return after;
}
/**
* I call this one "questionable" because it uses the questionable getScaledImage()
* method. This method is no longer favored because it's slow, as my tests confirm.
* @param before The original image
* @param scale The scale factor
* @return The scaled image.
*/
private static Image questionable(final BufferedImage before, double scale) {
int w2 = (int) (before.getWidth() * scale);
int h2 = (int) (before.getHeight() * scale);
return before.getScaledInstance(w2, h2, Image.SCALE_FAST);
}
person
MiguelMunoz
schedule
14.09.2017