使用自定义ColorSpace将java.awt.image.BufferedImage 24位RGB转换为8位灰度图像

4

我想使用 java.awt.image.BufferedImage 进行简单的彩色图像转灰度处理。我在图像处理领域是个初学者,如果我混淆了什么,请见谅。

我的输入图像是RGB 24位图像(没有α通道),我希望输出一个8位灰度BufferedImage,这意味着我有一个类似于这样的类(为了清晰起见,详情被省略):

public class GrayscaleFilter {
    private BufferedImage colorFrame;
    private BufferedImage grayFrame = 
        new BufferedImage(width, height, BufferedImage.TYPE_BYTE_GRAY);

到目前为止,我已经成功尝试了两种转换方法,第一种是:

    private BufferedImageOp grayscaleConv = 
        new ColorConvertOp(ColorSpace.getInstance(ColorSpace.CS_GRAY), null);

    protected void filter() {
        grayscaleConv.filter(colorFrame, grayFrame);
    }

第二点是:
    protected void filter() {       
        WritableRaster raster = grayFrame.getRaster();

        for(int x = 0; x < raster.getWidth(); x++) {
            for(int y = 0; y < raster.getHeight(); y++){
                int argb = colorFrame.getRGB(x,y);
                int r = (argb >> 16) & 0xff;
                int g = (argb >>  8) & 0xff;
                int b = (argb      ) & 0xff;

                int l = (int) (.299 * r + .587 * g + .114 * b);
                raster.setSample(x, y, 0, l);
            }
        }
    }

第一种方法速度更快,但生成的图像非常暗,这意味着我正在失去带宽,这是不可接受的(在灰度和sRGB ColorModel之间使用了一些颜色转换映射,称为tosRGB8LUT,对我来说效果不佳,我只是猜测这些值被使用)。第二种方法速度较慢,但效果非常好。
有没有一种方法可以将这两种方法结合起来,例如使用自定义索引的ColorSpace用于ColorConvertOp?如果有,请给我一个例子。
提前致谢。
5个回答

6
public BufferedImage getGrayScale(BufferedImage inputImage){
    BufferedImage img = new BufferedImage(inputImage.getWidth(), inputImage.getHeight(), BufferedImage.TYPE_BYTE_GRAY);
    Graphics g = img.getGraphics();
    g.drawImage(inputImage, 0, 0, null);
    g.dispose();
    return img;
}

5

这里有一个例子与您的第一个示例略有不同,即ColorConvertOp的参数。请尝试这个:

protected void filter() {
   BufferedImageOp grayscaleConv = 
      new ColorConvertOp(colorFrame.getColorModel().getColorSpace(), 
                         grayFrame.getColorModel().getColorSpace(), null);
   grayscaleConv.filter(colorFrame, grayFrame);
}

2
这种建议的方法也会产生较暗的图像。我想知道是否有人解决了这个问题。 - ecem
@ecem 可能是和这个问题一样的原因:https://dev59.com/MWUp5IYBdhLWcg3wRF6c - Mark Ransom

2
尝试修改您的第二种方法。不要单独处理每个像素,而是获取一个 ARGB 整数值数组,对其进行转换,然后将其设置回去。

0

这里有一个在某些情况下对我有效的解决方案。

取图像高度y,图像宽度x,图像颜色深度m和整数位数n。仅当(2^m)/(x*y*2^n) >= 1时才有效。 在处理初始灰度值时,为每个颜色通道保留一个n位整数总数。将每个总数除以(x*y)得到每个通道的平均值avr[channel]。为每个通道的每个像素添加(192-avr[channel])。

请记住,这种方法可能不会像标准亮度方法那样具有相同的质量水平,但如果您正在寻找速度和质量之间的折衷,并且不想处理昂贵的浮点运算,那么它可能适合您。


0
第二种方法基于像素的亮度,因此它可以获得更有利的视觉效果。通过使用查找数组或哈希表计算l时优化昂贵的浮点算术运算,可以稍微加快速度。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接