【问题标题】:How to set RGB pixel in a BufferedImage to display a 16-bit-depth PNG?如何在 BufferedImage 中设置 RGB 像素以显示 16 位深度的 PNG?
【发布时间】:2013-12-14 09:52:05
【问题描述】:

我正在尝试读取和显示 PNG 文件。 我处理 8 位深度的图像没有问题。 我进行如下操作:

BufferedImage result = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);

然后我读取每个像素的 3*8=24 位,将它们保存在字节数组 data 中,并将它们放入图像中:

for (int y = 0; y < height; y++)
   for (int x = 0; x < width; x++)
     result.setRGB(x, y, ((data[x * 3 + 0] & 0xff) << 16)
                       + ((data[x * 3 + 1] & 0xff) << 8)
                       + ((data[x * 3 + 2] & 0xff)));

现在问题在于 16 位深度图像。当然data 现在更大了,它包含 48 位,分为 6 个字节,每个 RGB 三元组:来自调试器的data 具有我期望的值。 如何设置 RGB 像素?我是否必须更改 BufferedImage 声明?也许有:

BufferedImage result = new BufferedImage(width, height, BufferedImage.TYPE_USHORT_565_RGB);

非常感谢!

P.S.:按照 PNG 标准,图像的颜色类型为 2(没有 alpha 的 RGB)。


也许我必须使用http://docs.oracle.com/javase/7/docs/api/java/awt/image/ColorModel.html

【问题讨论】:

    标签: java rgb pixel bufferedimage 16-bit


    【解决方案1】:

    @haraldK 指出了正确的方向。我提供了一些来自“icafe”Java 图像库的“PNGReader”的工作代码。

    if(bitsPerPixel == 16) {
        if(interlace_method==NON_INTERLACED)
           spixels = generate16BitRGBPixels(compr_data, false);
        else {
           spixels = generate16BitRGBInterlacedPixels(compr_data, false);
                   }
        int[] off = {0, 1, 2}; //band offset, we have 3 bands
        int numOfBands = 3;
        boolean hasAlpha = false;
        int trans = Transparency.OPAQUE;
        int[] nBits = {16, 16, 16}; 
        if(alpha != null) { // Deal with single color transparency
           off = new int[] {0, 1, 2, 3}; //band offset, we have 4 bands
           numOfBands = 4;
           hasAlpha = true;
           trans = Transparency.TRANSLUCENT;
           nBits = new int[] {16, 16, 16, 16};                      
        }
        db = new DataBufferUShort(spixels, spixels.length);
        raster = Raster.createInterleavedRaster(db, width, height, width*numOfBands, numOfBands, off, null);
        cm = new ComponentColorModel(colorSpace, nBits, hasAlpha, false, trans, DataBuffer.TYPE_USHORT);
    }
    return new BufferedImage(cm, raster, false, null);
    

    这里是 generate16BitRGBPixels() 方法:

    private short[] generate16BitRGBPixels(byte[] compr_data, boolean fullAlpha) throws Exception {
         //
         int bytesPerPixel = 0;
         byte[] pixBytes;
    
         if (fullAlpha)
             bytesPerPixel = 8;
         else 
             bytesPerPixel = 6;
    
         bytesPerScanLine = width*bytesPerPixel;         
    
         // Now inflate the data.
         pixBytes = new byte[height * bytesPerScanLine];
    
         // Wrap an InflaterInputStream with a bufferedInputStream to speed up reading
         BufferedInputStream bis = new BufferedInputStream(new InflaterInputStream(new ByteArrayInputStream(compr_data)));
    
         apply_defilter(bis, pixBytes, height, bytesPerPixel, bytesPerScanLine);
    
         short[] spixels = null;
    
         if(alpha != null) { // Deal with single color transparency
             spixels = new short[width*height*4];
             short redMask = (short)((alpha[1]&0xff)|(alpha[0]&0xff)<<8);
             short greenMask = (short)((alpha[3]&0xff)|(alpha[2]&0xff)<<8);;
             short blueMask = (short)((alpha[5]&0xff)|(alpha[4]&0xff)<<8);
    
             for(int i = 0, index = 0; i < pixBytes.length; index += 4) {
                 short red = (short)((pixBytes[i++]&0xff)<<8|(pixBytes[i++]&0xff));
                 short green = (short)((pixBytes[i++]&0xff)<<8|(pixBytes[i++]&0xff));
                 short blue = (short)((pixBytes[i++]&0xff)<<8|(pixBytes[i++]&0xff));
                 spixels[index] = red;
                 spixels[index + 1] = green;
                 spixels[index + 2] = blue;
                 if(spixels[index] == redMask && spixels[index + 1] == greenMask && spixels[index + 2] == blueMask) {
                     spixels[index + 3] = (short)0x0000;                               
                 } else {
                     spixels[index + 3] = (short)0xffff;
                 }
             }
         } else
             spixels = ArrayUtils.toShortArray(pixBytes, true);
    
         return spixels;         
     }
    

    和 ArrayUtils.toShortArray() 方法:

    public static short[] toShortArray(byte[] data, int offset, int len, boolean bigEndian) {
    
        ByteBuffer byteBuffer = ByteBuffer.wrap(data, offset, len);
    
        if (bigEndian) {
            byteBuffer.order(ByteOrder.BIG_ENDIAN);
        } else {
            byteBuffer.order(ByteOrder.LITTLE_ENDIAN);
        }
    
        ShortBuffer shortBuf = byteBuffer.asShortBuffer();
        short[] array = new short[shortBuf.remaining()];
        shortBuf.get(array);
    
        return array;
    }
    

    【讨论】:

      【解决方案2】:

      如果您想创建一个 每个样本 16 位(或每个像素 48 位)的图像,则没有 BufferedImage.TYPE_... 常量。 TYPE_USHORT_565_RGB 创建一个 每像素 16 位的图像,样本分别为 5(红色)、6(绿色)和 5(蓝色)位。我认为这些 USHORT RGB 值是某些计算实际上可以选择 16 位显示(又名“数千种颜色”)时的遗留物。

      要实际创建每个样本 16 位的图像,您需要做的是:

      ColorModel cm;
      WritableRaster raster;
      
      BufferedImage result = new BufferedImage(cm, raster, cm.isAlphaPremultiplied(), null);
      

      栅格是从具有 3 个库和具有 3 个波段的 BandedSampleModel 类型的数据缓冲区创建的,或者使用单个库和具有 3 个 pixelStridePixelInterleavedSampleModelscanLineStride 3 * widthbandOffsets {0, 1, 2}

      这是一个完整的样本,使用交错样本模型:

      ColorSpace sRGB = ColorSpace.getInstance(ColorSpace.CS_sRGB)
      ColorModel cm = new ComponentColorModel(sRGB, false, false, Transparency.OPAQUE, DataBuffer.TYPE_USHORT);
      WritableRaster raster = Raster.createInterleavedRaster(DataBuffer.TYPE_USHORT, w, h, 3, null);
      BufferedImage rgb = new BufferedImage(cm, raster, cm.isAlphaPremultiplied(), null);
      

      PS:数据缓冲区暴露后,您可以直接访问short 样本,以操作像素。这比使用BufferedImage.getRGB(...)/setRGB(...) 快得多,并且将保持原始的每样本 16 位精度。 BufferedImage.getRGB(...) 会将像素值转换为每个样本 32 位像素/8 位,从而失去额外的精度。

      【讨论】:

      • 感谢您的回复。我会学习这些课程并告诉你。
      • 我在创建 ColorModel 和 WritableRaster 时遇到问题。我必须使用哪种 ColorModel? ComponentColorModel 或者我可以将其设置为 null 吗?以及如何创建 WritableRaster?正如你所说,我使用了PixelInterleavedSampleModel(DataBuffer.TYPE_USHORT, img.getWidth(), img.getHeight(), 3, 3*img.getWidth(), bandOffset)。也许问题出在我传递给方法 createWritableRaster 的 DataBufferUShort 中。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-02-22
      • 1970-01-01
      相关资源
      最近更新 更多