Изменение размера изображения без потери качества - PullRequest
13 голосов
/ 31 октября 2011

Я сделал этот код для изменения размера изображения с двумя факторами. Это работает, но качество изображения очень плохое после его изменения! Вы можете мне помочь?

Это код

public class ImageTest {

private static final int factor1 = 3;
private static final int factor2 = 4;
public static void main(String [] args){

    JFileChooser cs = new JFileChooser();
    cs.setFileSelectionMode(cs.DIRECTORIES_ONLY);
    int i = cs.showOpenDialog(null);
    if(i==cs.APPROVE_OPTION){
        File f = cs.getSelectedFile();
        File[] ff = f.listFiles();
        for(int j=0;j<ff.length;j++){
            String end = ff[j].getName().substring(ff[j].getName().indexOf(".")+1);
            System.out.println(end);
            try{
                BufferedImage originalImage = ImageIO.read(ff[j]);
                int type = originalImage.getType() == 0? BufferedImage.TYPE_INT_ARGB : originalImage.getType();
                BufferedImage resizeImageJpg = resizeImageWithHint(originalImage, type);
                ImageIO.write(resizeImageJpg, end, new File("pr/"+ff[j].getName()));
            }catch(IOException e){
                e.printStackTrace();
            }
        }
    }


}
private static BufferedImage resizeImageWithHint(BufferedImage originalImage, int type){
    int IMG_WIDTH = (originalImage.getWidth()*factor1)/factor2;
    int IMG_HEIGHT = (originalImage.getHeight()*factor1)/factor2;
    BufferedImage resizedImage = new BufferedImage(IMG_WIDTH, IMG_HEIGHT, type);
    Graphics2D g = resizedImage.createGraphics();
    g.drawImage(originalImage, 0, 0, IMG_WIDTH, IMG_HEIGHT, null);
    g.dispose();    
    g.setComposite(AlphaComposite.Src);

    g.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
            RenderingHints.VALUE_INTERPOLATION_BILINEAR);
    g.setRenderingHint(RenderingHints.KEY_RENDERING,
            RenderingHints.VALUE_RENDER_QUALITY);
    g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
            RenderingHints.VALUE_ANTIALIAS_ON);

    return resizedImage;
}   
   }

Я видел в сети, что resizeImageWithHint делается в рамках, чтобы не потерять качество ... но это так! Зачем? Вы можете помочь мне с этим?

Ответы [ 8 ]

30 голосов
/ 31 октября 2011

Лучшая статья, которую я когда-либо читал на эту тему: Опасности Image.getScaledInstance () (веб-архив).

Короче говоря: вам нужно выполнить несколько шагов по изменению размера, чтобы получить хорошее изображение. Вспомогательный метод из статьи:

public BufferedImage getScaledInstance(BufferedImage img,
                                       int targetWidth,
                                       int targetHeight,
                                       Object hint,
                                       boolean higherQuality)
{
    int type = (img.getTransparency() == Transparency.OPAQUE) ?
        BufferedImage.TYPE_INT_RGB : BufferedImage.TYPE_INT_ARGB;
    BufferedImage ret = (BufferedImage)img;
    int w, h;
    if (higherQuality) {
        // Use multi-step technique: start with original size, then
        // scale down in multiple passes with drawImage()
        // until the target size is reached
        w = img.getWidth();
        h = img.getHeight();
    } else {
        // Use one-step technique: scale directly from original
        // size to target size with a single drawImage() call
        w = targetWidth;
        h = targetHeight;
    }

    do {
        if (higherQuality && w > targetWidth) {
            w /= 2;
            if (w < targetWidth) {
                w = targetWidth;
            }
        }

        if (higherQuality && h > targetHeight) {
            h /= 2;
            if (h < targetHeight) {
                h = targetHeight;
            }
        }

        BufferedImage tmp = new BufferedImage(w, h, type);
        Graphics2D g2 = tmp.createGraphics();
        g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, hint);
        g2.drawImage(ret, 0, 0, w, h, null);
        g2.dispose();

        ret = tmp;
    } while (w != targetWidth || h != targetHeight);

    return ret;
}
10 голосов
/ 31 мая 2013

Следующий код дал мне самое высокое качество при сохранении пропорций. Перепробовал несколько вещей и прочитал несколько представленных здесь записей в других ответах. Потерял два дня и в итоге я получил лучший результат с простым Java-методом (пробовал также библиотеки ImageMagick и java-image-scale):

public static boolean resizeUsingJavaAlgo(String source, File dest, int width, int height) throws IOException {
  BufferedImage sourceImage = ImageIO.read(new FileInputStream(source));
  double ratio = (double) sourceImage.getWidth()/sourceImage.getHeight();
  if (width < 1) {
    width = (int) (height * ratio + 0.4);
  } else if (height < 1) {
    height = (int) (width /ratio + 0.4);
  }

  Image scaled = sourceImage.getScaledInstance(width, height, Image.SCALE_AREA_AVERAGING);
  BufferedImage bufferedScaled = new BufferedImage(scaled.getWidth(null), scaled.getHeight(null), BufferedImage.TYPE_INT_RGB);
  Graphics2D g2d = bufferedScaled.createGraphics();
  g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
  g2d.drawImage(scaled, 0, 0, width, height, null);
  dest.createNewFile();
  writeJpeg(bufferedScaled, dest.getCanonicalPath(), 1.0f);
  return true;
}


/**
* Write a JPEG file setting the compression quality.
*
* @param image a BufferedImage to be saved
* @param destFile destination file (absolute or relative path)
* @param quality a float between 0 and 1, where 1 means uncompressed.
* @throws IOException in case of problems writing the file
*/
private static void writeJpeg(BufferedImage image, String destFile, float quality)
      throws IOException {
  ImageWriter writer = null;
  FileImageOutputStream output = null;
  try {
    writer = ImageIO.getImageWritersByFormatName("jpeg").next();
    ImageWriteParam param = writer.getDefaultWriteParam();
    param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
    param.setCompressionQuality(quality);
    output = new FileImageOutputStream(new File(destFile));
    writer.setOutput(output);
    IIOImage iioImage = new IIOImage(image, null, null);
    writer.write(null, iioImage, param);
  } catch (IOException ex) {
    throw ex;
  } finally {
    if (writer != null) {
      writer.dispose();
    }
    if (output != null) {
      output.close();
    }
  }
}
6 голосов
/ 14 декабря 2015

Знаете, вопрос устарел ... Я пробовал разные решения для серфинга, а не для веба, я получил лучший результат, используя getScaledInstance(), предоставив Image.SCALE_SMOOTH в качестве аргумента. На самом деле качество получаемого изображения было действительно лучше. Мой код ниже:

final int THUMB_SIDE = 140;
try {
    BufferedImage masterImage = ImageIO.read(startingImage);
    BufferedImage thumbImage = new BufferedImage(THUMB_SIDE, THUMB_SIDE, BufferedImage.TYPE_INT_ARGB);
    Graphics2D g2d = thumbImage.createGraphics();
    g2d.drawImage(masterImage.getScaledInstance(THUMB_SIDE, THUMB_SIDE, Image.SCALE_SMOOTH), 0, 0, THUMB_SIDE, THUMB_SIDE, null);
    g2d.dispose();
    String thumb_path = path.substring(0, path.indexOf(".png")) + "_thumb.png";
    ImageIO.write(thumbImage, "png", new File(thumb_path));
} catch (IOException e) {
    e.printStackTrace();
}
3 голосов
/ 03 сентября 2014

Если ваш источник изображения png, используйте следующее:

Image imgSmall = imgBig.getScaledInstance(
        targetWidth, targetHeight, Image.SCALE_SMOOTH);

Если вы хотите изменить размер JPEG или GIF без потери качества, я сделал библиотеку в 2010 году для этого:1005 * beautylib на github , который использует внутренне эту другую библиотеку: java-image-scaling .Вы можете увидеть непосредственно исходный код, чтобы найти что-то полезное: https://github.com/felipelalli/beautylib/blob/master/src/br/eti/fml/beautylib/ResizeImage.java

2 голосов
/ 09 апреля 2015

Ни один из ответов не поможет вам достичь желаемого качества.Включите thumbailator.jar в свой проект (загрузите его здесь):

https://code.google.com/p/thumbnailator/

https://code.google.com/p/thumbnailator/wiki/Downloads?tm=2

Затем сначала загрузите изображение (в виде файла, без Thumbnailator -он используется для создания больших пальцев, но вы, конечно, можете создавать большие изображения с ним) и изменять его размер в соответствии с любым размером (например, с Thumbnailator 800x600).Качество будет очень хорошим.Я искал это долгое время, этот .jar помог мне достичь того, чего я хочу.

1 голос
/ 31 октября 2011

Да, у меня были те же проблемы, и я их решил, прочитайте мой вопрос (ответ включен в вопрос).Я попробовал imgscalr и java-image-scaling библиотеки и обнаружил, что второе намного лучше.Подойдите ближе к монитору, чтобы оценить разницу между примерами с миниатюрами.

Несмотря на мои первоначальные мысли, изменение размера изображения кажется очень сложной задачей, вы не хотите делать это самостоятельно.Например, я говорю java-image-scaling , чтобы использовать ResampleFilters.getLanczos3Filter() для получения лучшего результата.

В нем также описывается, как сохранить JPG с качеством, превышающим стандарт 75, который производитплохой результат, особенно для миниатюр.

Я также написал небольшой класс, который называется MyImage, чтобы помочь с общими задачами, такими как чтение изображения из байтового массива из файламасштабирование путем указания только ширины или только высоты, масштабирование путем указания ограничивающей рамки, масштабирование путем указания ширины и высоты и добавление белой полосы, чтобы изображение не искажалось, и запись в файл JPG.

0 голосов
/ 12 мая 2017

Все опубликованные методы не работают для меня, мне нужно уменьшить размер QrCode, но с помощью вышеперечисленных методов качество плохое, и сканер не работает, если я беру оригинальное изображение и изменяю его размер в краске, сканер работает.

0 голосов
/ 31 октября 2016

Ответ: Удалить подсказки VALUE_INTERPOLATION_BILINEAR и VALUE_RENDERING_QUALITY .

Пример: * 1 013 ** ** 1014 1015 * public static BufferedImage resizeImage(BufferedImage image, int width, int height) { // Temporary image BufferedImage tmp = image; // Result image BufferedImage result = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); // Graphics object Graphics2D graphics = (Graphics2D)result.createGraphics(); // Add rendering hints graphics.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY); graphics.setRenderingHint(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_ENABLE); // Draw tmp graphics.drawImage(tmp, 0, 0, width, height, null); // Dispose of graphics object graphics.dispose(); // Return image result return result; } Примечание: По некоторым причинам намеки VALUE_INTERPOLATION_BILINEAR и VALUE_RENDERING_QUALITY размытие изображения с изменяемым размером.

...