gpt4 book ai didi

java - 如何为每个样本 32 位、3 个样本图像数据创建 BufferedImage

转载 作者:塔克拉玛干 更新时间:2023-11-02 19:22:43 26 4
gpt4 key购买 nike

我正在尝试从一些图像数据创建一个 BufferedImage,它是一个字节数组。图像是 RGB 格式,每个像素有 3 个样本 - R、G 和 B,每个样本 32 位(对于每个样本,不是所有 3 个样本)。

现在我想从这个字节数组创建一个 BufferedImage。这就是我所做的:

        ColorModel cm = new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_sRGB), new int[] {32, 32, 32}, false, false, Transparency.OPAQUE, DataBuffer.TYPE_INT);
Object tempArray = ArrayUtils.toNBits(bitsPerSample, pixels, samplesPerPixel*imageWidth, endian == IOUtils.BIG_ENDIAN);
WritableRaster raster = cm.createCompatibleWritableRaster(imageWidth, imageHeight);
raster.setDataElements(0, 0, imageWidth, imageHeight, tempArray);
BufferedImage bi = new BufferedImage(cm, raster, false, null);

上述代码适用于每个样本 24 位 RGB 图像,但不适用于每个样本 32 位。生成的图像是垃圾,显示在图像的右侧。它应该像图像的左侧。

注意:我的机器上唯一可以读取此图像的图像阅读器是 ImageMagick。所有其他显示的结果与下图右侧的垃圾类似。

ArrayUtils.toNBits() 只是将字节数组转换为具有正确字节顺序的 int 数组。我确定这个是正确的,因为我已经与其他方法交叉检查以生成相同的 int 数组。

我想问题可能是因为我使用所有 32 位 int 来表示包含负值的颜色。看起来我需要 long 数据类型,但是没有 long 类型的 DataBuffer。

Instances of ComponentColorModel created with transfer types DataBuffer.TYPE_BYTE, DataBuffer.TYPE_USHORT, and DataBuffer.TYPE_INT have pixel sample values which are treated as unsigned integral values.

以上引用来自 ComponentColorModel 的 Java 文档。这意味着 32 位样本确实被视为无符号整数值。那么问题可能出在其他地方。

有没有人遇到过类似的问题并找到了解决方法,或者我可能在这里做错了什么?

更新 2:“真正的”问题在于当使用 32 位样本时,ComponentColorModel 的算法将 1 向左移动 0 次 (1<<0),因为移动开启int 始终在 0~31 之间(含)。这不是预期值。要解决这个问题(实际上左移 32 次),唯一需要做的就是将 1 从 int 更改为 long 类型,如 1L,如下面的修复所示。

更新:根据 HaraldK 的回答和评论,我们最终同意问题来自 Java 的 ComponentColorModel,它没有正确处理 32 位样本。 HaraldK 提出的修复方案也适用于我的案例。以下是我的版本:

import java.awt.Transparency;
import java.awt.color.ColorSpace;
import java.awt.image.ComponentColorModel;
import java.awt.image.DataBuffer;

public class Int32ComponentColorModel extends ComponentColorModel {
//
public Int32ComponentColorModel(ColorSpace cs, boolean alpha) {
super(cs, alpha, false, alpha ? Transparency.TRANSLUCENT : Transparency.OPAQUE, DataBuffer.TYPE_INT);
}

@Override
public float[] getNormalizedComponents(Object pixel, float[] normComponents, int normOffset) {
int numComponents = getNumComponents();

if (normComponents == null || normComponents.length < numComponents + normOffset) {
normComponents = new float[numComponents + normOffset];
}

switch (transferType) {
case DataBuffer.TYPE_INT:
int[] ipixel = (int[]) pixel;
for (int c = 0, nc = normOffset; c < numComponents; c++, nc++) {
normComponents[nc] = ipixel[c] / ((float) ((1L << getComponentSize(c)) - 1));
}
break;
default: // I don't think we can ever come this far. Just in case!!!
throw new UnsupportedOperationException("This method has not been implemented for transferType " + transferType);
}

return normComponents;
}
}

enter image description here

最佳答案

更新:

这似乎是一个已知错误:ComponentColorModel.getNormalizedComponents() does not handle 32-bit TYPE_INT ,10(十!)年前针对 Java 5 的报道。

有利的一面是,Java 现在已部分开源。我们现在可以提出一个补丁,如果幸运的话,它将针对 Java 9 左右进行评估......:-P

该错误提出了以下解决方法:

Subclass ComponentColorModel and override getNormalizedComponents() to properly handle 32 bit per sample TYPE_INT data by dividing the incoming pixel value by 'Math.pow(2, 32) - 1' when dealing with this data, rather than using the erroneous bit shift. (Using a floating point value is ok, since getNormalizedComponents() converts everything to floating point anyway).

我的修复有点不同,但基本思想是相同的(随意优化您认为合适的:-)):

private static class TypeIntComponentColorModel extends ComponentColorModel {
public TypeIntComponentColorModel(final ColorSpace cs, final boolean alpha) {
super(cs, alpha, false, alpha ? TRANSLUCENT : OPAQUE, DataBuffer.TYPE_INT);
}

@Override
public float[] getNormalizedComponents(Object pixel, float[] normComponents, int normOffset) {
int numComponents = getNumComponents();

if (normComponents == null) {
normComponents = new float[numComponents + normOffset];
}

switch (transferType) {
case DataBuffer.TYPE_INT:
int[] ipixel = (int[]) pixel;
for (int c = 0, nc = normOffset; c < numComponents; c++, nc++) {
normComponents[nc] = ((float) (ipixel[c] & 0xffffffffl)) / ((float) ((1l << getComponentSize(c)) - 1));
}
break;
default:
throw new UnsupportedOperationException("This method has not been implemented for transferType " + transferType);
}

return normComponents;
}
}

考虑以下代码。如果按原样运行,对我来说,它显示的图像大部分是黑色的,右上四分之一的白色覆盖着一个黑色圆圈。如果我将数据类型更改为 TYPE_USHORT(取消注释 transferType 行),它会显示一半/一半白色和从黑色到白色的线性渐变,中间有一个橙色圆圈(应该如此)。

使用ColorConvertOp 转换为标准类型似乎没有什么区别。

public class Int32Image {
public static void main(String[] args) {
// Define dimensions and layout of the image
int w = 300;
int h = 200;
int transferType = DataBuffer.TYPE_INT;
// int transferType = DataBuffer.TYPE_USHORT;

ColorModel colorModel = new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_sRGB), false, false, Transparency.OPAQUE, transferType);
WritableRaster raster = colorModel.createCompatibleWritableRaster(w, h);
BufferedImage image = new BufferedImage(colorModel, raster, false, null);

// Start with linear gradient
if (raster.getTransferType() == DataBuffer.TYPE_INT) {
DataBufferInt buffer = (DataBufferInt) raster.getDataBuffer();
int[] data = buffer.getData();

for (int y = 0; y < h; y++) {
int value = (int) (y * 0xffffffffL / h);

for (int x = 0; x < w; x++) {
int offset = y * w * 3 + x * 3;
data[offset] = value;
data[offset + 1] = value;
data[offset + 2] = value;
}
}
}
else if (raster.getTransferType() == DataBuffer.TYPE_USHORT) {
DataBufferUShort buffer = (DataBufferUShort) raster.getDataBuffer();
short[] data = buffer.getData();

for (int y = 0; y < h; y++) {
short value = (short) (y * 0xffffL / h);

for (int x = 0; x < w; x++) {
int offset = y * w * 3 + x * 3;
data[offset] = value;
data[offset + 1] = value;
data[offset + 2] = value;
}
}
}

// Paint something (in color)
Graphics2D g = image.createGraphics();
g.setColor(Color.WHITE);
g.fillRect(0, 0, w / 2, h);
g.setColor(Color.ORANGE);
g.fillOval(100, 50, w - 200, h - 100);
g.dispose();

System.out.println("image = " + image);

// image = new ColorConvertOp(null).filter(image, new BufferedImage(image.getWidth(), image.getHeight(), BufferedImage.TYPE_INT_ARGB));

JFrame frame = new JFrame();
frame.add(new JLabel(new ImageIcon(image)));
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
}

对我来说,这似乎表明使用 transferType TYPE_INTColorModel 有问题。但我很乐意犯错。 ;-)

您可以尝试的另一件事是将值缩小到 16 位,使用 TYPE_USHORT 光栅和颜色模型,看看这是否有所不同。我打赌它会,但我懒得尝试。 ;-)

关于java - 如何为每个样本 32 位、3 个样本图像数据创建 BufferedImage,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26875429/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com