我正在尝试将24位RGB格式的灰度图像转换为8位格式的灰度图像。换句话说,输入和输出在视觉上应该是相同的,只是通道数会改变。这是输入图像:
用于将其转换为8位的代码:
File input = new File("input.jpg");
File output = new File("output.jpg");
// Read 24-bit RGB input JPEG.
BufferedImage rgbImage = ImageIO.read(input);
int w = rgbImage.getWidth();
int h = rgbImage.getHeight();
// Create 8-bit gray output image from input.
BufferedImage grayImage = new BufferedImage(w, h, BufferedImage.TYPE_BYTE_GRAY);
int[] rgbArray = rgbImage.getRGB(0, 0, w, h, null, 0, w);
grayImage.setRGB(0, 0, w, h, rgbArray, 0, w);
// Save output.
ImageIO.write(grayImage, "jpg", output);
这是输出图像:
如您所见,有一些细微的差别。但是它们应该是相同的。对于看不到它的人,这里是the difference between the two images(当在Gimp中使用“差异混合”模式查看时,全黑表示没有差异)。如果我使用PNG代替输入和输出,则会发生相同的问题。
grayImage.setRGB
之后,我尝试比较两个图像中相同像素的颜色值:
int color1 = rgbImage.getRGB(230, 150); // This returns 0xFF6D6D6D.
int color2 = grayImage.getRGB(230, 150); // This also returns 0xFF6D6D6D.
两种颜色相同。但是,如果我对Gimp中的图像进行相同的比较,则会分别得到0xFF6D6D6D
和0xFF272727
……巨大的差异。
这里发生了什么?有什么办法可以从24位灰度图像中获得相同的8位图像?我正在使用Oracle JDK 1.8作记录。
我测试的前两件事,我打印了两个图像。
BufferedImage @ 544fa968:类型= 5 ColorModel:#pixelBits = 24 numComponents = 3色彩空间= java.awt.color.ICC_ColorSpace@68e5eea7透明度= 1具有alpha = false isAlphaPre = false ByteInterleavedRaster:宽度= 400高度= 400 #numDataElements 3个dataOff [0] = 2
BufferedImage @ 11fc564b:类型= 10 ColorModel:#pixelBits = 8 numComponents = 1颜色空间= java.awt.color.ICC_ColorSpace@394a2528透明度= 1具有alpha = false isAlphaPre = false ByteInterleavedRaster:宽度= 400高度= 400 #numDataElements 1个数据关闭[0] = 0
我们可以看到图像具有不同的色彩空间,并且数据偏移量也不同。
而且我使用图形在输出上绘制原始图像。
Graphics g = grayImage.getGraphics();
g.drawImage(rgbImage, 0, 0, null);
这很好。我将图片另存为png,并不是因为它会改变您看到的效果,并且当我在两张图片之间进行区别时,它们是相同的。
底线是,两种不同图像类型的rgb值不同。因此,尽管使用get rgb看到相同的值,但显示它们时,它们将被解释为不同的值。
使用图形的速度较慢,但是可以输出正确的图像。
我认为这里的区别是setRGB / getRGB以非直观的方式对数据进行操作。
DataBuffer rgbBuffer = rgbImage.getRaster().getDataBuffer();
DataBuffer grayBuffer = grayImage.getRaster().getDataBuffer();
System.out.println(grayBuffer.size() + ", " + rgbBuffer.size() );
for(int i = 0; i<10; i++){
System.out.println(
grayBuffer.getElem(i) + "\t"
+ rgbBuffer.getElem(3*i) + ", "
+ rgbBuffer.getElem(3*i+1) + ", "
+ rgbBuffer.getElem(3*i + 2) );
}
显示我们期望的数据。 rgb缓冲区的大小是其3倍,像素直接对应。
160000,480000255 255、255、255255 255、255、255254254、254、254253253、253、253252252、252、252252252、252、252251251、251、251251251、251、251250250、250、250250250、250、250
当我们检查相应的rgb值时。
for(int i = 0; i<10; i++){
System.out.println(
Integer.toHexString( grayImage.getRGB(i, 0) ) + ", "
+ Integer.toHexString( rgbImage.getRGB(i, 0) ) + " " );
}
ffffffff,ffffffffffffffff,ffffffffffffffff,ffffeefefffefefe,fffdfdfdfffefefe,fffffcfcfffefefe,fffffcfcfffdfdfd,fffbfbfbfffdfdfd,fffbfbfbfffdfdfd,fffafafafffdfdfd,fffafafa
因此,要使图像正确,它必须具有不同的rgb值。