Vire uma matriz de pixels em um objeto de imagem com ImageIO do Java?
-
02-07-2019 - |
Pergunta
Atualmente estou virando uma matriz de valores de pixel (originalmente criados com um objeto java.awt.image.PixelGrabber) em um objeto de imagem usando o seguinte código:
public Image getImageFromArray(int[] pixels, int width, int height) {
MemoryImageSource mis = new MemoryImageSource(width, height, pixels, 0, width);
Toolkit tk = Toolkit.getDefaultToolkit();
return tk.createImage(mis);
}
É possível alcançar o mesmo resultado usando classes do pacote ImageIO (s), então eu não tenho que usar o AWT Toolkit?
Toolkit.getDefaultToolkit () parece não ser 100% confiável e às vezes vai lançar uma AWTError, enquanto as classes ImageIO deve estar sempre disponível, que é por isso que estou interessado em mudar o meu método.
Solução
Você pode criar a imagem sem usar ImageIO. Basta criar um BufferedImage usando um tipo de imagem combinando o conteúdo do conjunto de pixels.
public static Image getImageFromArray(int[] pixels, int width, int height) {
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
WritableRaster raster = (WritableRaster) image.getData();
raster.setPixels(0,0,width,height,pixels);
return image;
}
Ao trabalhar com o PixelGrabber, não se esqueça de extrair a informação RGBA da matriz de pixels antes de chamar getImageFromArray
. Há um exemplo disto na handlepixelmethod no javadoc PixelGrabber. Depois de fazer isso, certifique-se o tipo de imagem no construtor BufferedImage para BufferedImage.TYPE_INT_ARGB
.
Outras dicas
Usando o raster recebi um ArrayIndexOutOfBoundsException
mesmo quando eu criei o BufferedImage
com TYPE_INT_ARGB
. No entanto, usando o método setRGB(...)
de BufferedImage
funcionou para mim.
JavaDoc em BufferedImage.getData () diz: "um Raster que é um cópia dos dados de imagem"
Esse código funciona para mim, mas eu duvido nele de eficiência:
// Получаем картинку из массива.
int[] pixels = new int[width*height];
// Рисуем диагональ.
for (int j = 0; j < height; j++) {
for (int i = 0; i < width; i++) {
if (i == j) {
pixels[j*width + i] = Color.RED.getRGB();
}
else {
pixels[j*width + i] = Color.BLUE.getRGB();
//pixels[j*width + i] = 0x00000000;
}
}
}
BufferedImage pixelImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
pixelImage.setRGB(0, 0, width, height, pixels, 0, width);
Eu tive um bom sucesso usando java.awt.Robot para pegar uma captura de tela (ou um segmento da tela), mas para trabalhar com ImageIO, você vai precisar para armazená-lo em um BufferedImage em vez da imagem de memória fonte. Depois, você pode chamar um método estático de ImageIO e salve o arquivo. Tente algo como:
// Capture whole screen
Rectangle region = new Rectangle(Toolkit.getDefaultToolkit().getScreenSize());
BufferedImage capturedImage = new Robot().createScreenCapture(region);
// Save as PNG
File imageFile = new File("capturedImage.png");
ImageIO.write(capturedImage, "png", imageFile);
Como este é um dos mais votado questão marcados com ImageIO no SO, eu acho que ainda há espaço para uma solução melhor, mesmo que a questão é antiga. : -)
Tenha um olhar para o BufferedImageFactory.java classe do meu projeto ImageIO open source no GitHub.
Com ele, você pode simplesmente escrever:
BufferedImage image = new BufferedImageFactory(image).getBufferedImage();
A outra coisa boa é que essa abordagem, como um pior caso, tem aproximadamente o mesmo desempenho (tempo) como os exemplos baseados em PixelGrabber
já em esta discussão. Para a maioria dos casos comuns (tipicamente JPEG), é duas vezes mais rápido. Em qualquer caso, ele usa menos memória.
Como um bônus lado, o modelo de cores e layout de pixel da imagem original é mantido, em vez de traduzidos para int ARGB com o modelo de cor padrão. Isso pode economizar memória adicional.
(PS: A fábrica também suporta subamostragem, e de progresso ouvintes se alguém interessado juros região-de-: -).
Eu tive o mesmo problema de todo mundo tentando aplicar a resposta correta da questão, minha matriz int realmente obter uma OutOfboundException onde eu fixa-lo adicionando mais um índice porque o comprimento da matriz tem que ser Largura * height * 3 depois disto, eu não poderia obter a imagem assim que eu fixa-lo definindo o raster para a imagem
public static Image getImageFromArray(int[] pixels, int width, int height) {
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
WritableRaster raster = (WritableRaster) image.getData();
raster.setPixels(0,0,width,height,pixels);
image.setData(raster);
return image;
}
E você pode ver a imagem se u mostrá-lo em uma etiqueta em um JFrame como este
JFrame frame = new JFrame();
frame.getContentPane().setLayout(new FlowLayout());
frame.getContentPane().add(new JLabel(new ImageIcon(image)));
frame.pack();
frame.setVisible(true);
definir a imagem na ImageIcon (). Última conselho que você pode tentar mudar o Bufferedimage.TYPE_INT_ARGB para outra coisa que corresponde à imagem que você tem a matriz deste tipo é muito importante que eu tinha um conjunto de 0 e -1, então eu usei este tipo BufferedImage.TYPE_3BYTE_BGR