Pergunta

Eu tenho um open-source aplicativo que carrega fotos para Facebook.Para economizar largura de banda, as fotos são redimensionadas automaticamente antes de enviar (Facebook impõe um limite de tamanho máximo).Algumas pessoas se queixaram da qualidade da foto, e na verdade, você pode ver a diferença (ver esse problema para alguns penteados imagens).

Então a minha pergunta é, qual é a "melhor" forma de reduzir imagens (i.e.as fotos) em Java, sem perder a qualidade, ou pelo menos, com o mínimo de perda de qualidade / artefatos?

Você pode ver o código atual que eu tenho aqui (código de redimensionamento através de nesta página).

Foi útil?

Solução

Eu tentei tudo - incluindo os truques aqui, e tudo o que posso dizer que você é melhor usar o ImageMagick com qualquer interface, as bibliotecas de imagens JAVAS não estão prontas para o Snuff quando se trata disso. Você precisa suportar tantos formatos e algoritmos para acertar.

Outras dicas

Phil, eu não sei com qual solução você acabou, mas escalar imagens em java pode parecer muito bom se você:

  • Evite tipos de bufferImage que não são bem suportados pelo JDK.
  • Use escala incremental
  • Atenha -se a Bicubic ao usar a escala incremental

Eu fiz uma parcela justa dos testes com esses métodos e a escala incremental, além de aderir a tipos de imagem bem suportados, é a chave - vejo que Alexander mencionou que ele ainda não teve boa sorte com isso, o que é uma chatice.

Eu liberei o Biblioteca IMGSCALR (Apache 2) Cerca de 6 meses atrás para abordar a questão de "Quero cópias em escala de boa aparência desta imagem, faça agora!" Depois de ler algo como 10 perguntas como essa.

O uso padrão se parece:

BufferedImage img = ImageIO.read(...); // load image
BufferedImage scaledImg = Scalr.resize(img, 640);

O segundo argumento é a largura e a altura que o IMGSCALR usará para escalar a imagem - mantendo suas proporções corretas mesmo se você passou em dimensões inválidas - existem muitos Métodos mais detalhados, mas esse é o uso mais simples.

O caso de uso que você deseja, por exemplo, se o Facebook limitou imagens a 800x600 pixels, se faria assim:

BufferedImage img = ImageIO.read(...); // load image
BufferedImage scaledImg = Scalr.resize(img, Method.QUALITY, 800, 600);

Isso garantirá que a imagem permaneça no tipo de imagem mais suportada e dimensionada com o método da mais alta qualidade que o Java pode reunir.

Em meus próprios testes de alta resolução, não notei discrepâncias abertas com imagens em escala usando esta biblioteca/esses métodos, exceto quando sua imagem é colocada em um tipo de imagem mal suportado pelo carregador de imagem-por exemplo, isso acontece muito com gifs . Se você os deixar assim e não os tira desses tipos mal apoiados, ele acaba parecendo realmente escavado e terrível.

A razão para isso é que a equipe Java2D na verdade possui diferentes dutos acelerados de hardware para todos os diferentes tipos de estimativas buffers que o JDK pode processar - um subconjunto desses tipos de imagem que são menos comuns que voltam a usar o mesmo pipeline de renderização de software sob o Capas em Java2D, resultando em imagens pobres e às vezes totalmente incorretas. Essa foi uma PIA para explicar e tentar descobrir que acabei de escrever essa lógica diretamente na biblioteca.

Os dois melhores tipos suportados são bufferImage.type_int_rgb e _argb se você estiver curioso.

Os dois mais populares open source libs especializada em redimensionamento de imagens em java atualmente são:

Adição de lá é o JDK forma com Java Graphics2D (consulte esta pergunta como fazer isso) que é notório para criar maus resultados, especialmente com o escalonamento.Há também um Interface Java para o ImageMagick qual será omitida aqui, porque exige uma ferramenta externa.

Visual De Qualidade

Aqui está uma comparação dos resultados de redimensionamento/processando um 580x852 png para 145x213.Como referência Photoshop CS5 "salvar para web" redimensionamento é usado. Nota:os resultados são:1: 1 o que as libs criado apenas copiados juntos.O zoom não use qualquer tipo de filtragem, apenas um simples algoritmo do vizinho mais próximo. Aqui você pode encontrar a imagem original.

comparison

  1. Thumbnailator 0.4.8 com as configurações padrão, sem a dimensão ajustes
  2. O Photoshop CS5 com bicubic algoritmo
  3. imgscalr 4.2 com ULTRA_QUALITY definição, sem a dimensão ajustes
  4. Graphics2D (Java 8) com a render dicas VALUE_INTERPOLATION_BICUBIC, VALUE_RENDER_QUALITY, VALUE_ANTIALIAS_ON

Deixo para o leitor a selecionar o melhor resultado como isso é subjetivo.Em geral, todos têm boa saída, exceto Graphics2D.Thumbnailator gera imagens mais nítidas, muito semelhante ao Photoshop saída, considerando que imgscalr de saída é consideravelmente mais suave.Para os ícones e o texto etc.você quer uma imagem mais nítida de saída, para imagens que você pode querer mais suave de saída.

Tempo De Processamento

Aqui é não-científicas de benchmark usando este ferramenta e 114 imagens com dimensão de cerca de 96x96 até 2560x1440 tratá-lo como 425% criação de imagens:100%, 150%, 200%, 300% e a 400% em escala versões (para 114 * 5 operações de escala).Todas as libs usar as mesmas configurações como na comparação de qualidade (para maior qualidade possível).Os tempos estão só não escala o todo o processo.Feito em um i5-2520M com 8GB de Ram e 5 execuções.

  • Thumbnailator:7003.0 ms | 6581.3 ms | 6019.1 ms | 6375.3 ms | ms 8700.3
  • imgscalr:25218.5 ms | 25786.6 ms | 25095.7 ms | 25790.4 ms | ms 29296.3
  • Graphics2D:7387.6 ms | 7177.0 ms | 7048.2 ms | 7132.3 ms | ms 7510.3

Aqui está o código utilizado neste benchmark.

Curiosamente Thumbnailator também é o mais rápido, com um tempo médio de 6,9 sec seguido por Java2D com 7,2 sec deixando imgscalr para trás com um pobre 26.2 sec.Provavelmente, este não é justo, pois imgscalr é definido para ULTRA_QUALITY o que parece ser extremamente caro;com o QUALITY definição a média é de mais competitiva 11.1 sec.

Para redimensionar a imagem com a qualidade personalizada, use thumbnailator.jar.

Código de exemplo http://code.google.com/p/thumbnailator/wiki/examples

Qual dica de renderização você está usando? Geralmente, a reamostragem bicúbica será a melhor. Nas fotos que você está vinculando, elas são muito irregulares, o que me faz pensar que você está usando o vizinho mais próximo como sua dica.

No Picturescaler classe que você vincula, no paintComponent Método, ele usa seis meios diferentes de redimensionar a imagem. Você já tentou todos os seis para ver o que dá o melhor resultado?

After a few frustrating experiments I found the following resize evaluation, and employed the multi-pass approach in my project.

To do that I copied the getScaledInstance() method into my thumbnail generator class, changed my image read approach to use ImageIO (that one returns a BufferedImage) and am now very happy!

I compared the result with a resize done in Photoshop CS3 and the result is very much the same.

I wanted highest quality resize with aspect ratio preserved. Tried few things and read several entries. Lost two days and in the end I got the best result with plain Java method (tried also ImageMagick and java-image-scaling libraries):

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();
    }
  }
}
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top