【问题标题】:Java error on bilinear interpolation of 16 bit data16位数据双线性插值的Java错误
【发布时间】:2011-01-26 13:16:04
【问题描述】:

我在对 16 位数据使用双线性插值时遇到问题。我有两个图像,origImage 和 displayImage。我想使用 AffineTransformOp 通过 AffineTransform 将 origImage 过滤为 displayImage ,这是显示区域的大小。 origImage 的类型为 BufferedImage.TYPE_USHORT_GRAY,并具有 sun.awt.image.ShortInterleavedRaster 类型的栅格。这是我现在的代码

displayImage = new BufferedImage(getWidth(), getHeight(), origImage.getType());
try {
    op = new AffineTransformOp(atx, AffineTransformOp.TYPE_BILINEAR);
    op.filter(origImage, displayImage);
}
catch (Exception e) {
    e.printStackTrace();
}

为了显示错误,我创建了 2 个渐变图像。一个具有 15 位范围内的值(最大值为 32767),一个具有 16 位范围内的值(最大值为 65535)。下面是两张图

15 位图像

16 位图像

这两个图像以相同的方式创建并且看起来应该相同,但请注意横跨 16 位图像中间的线。起初我认为这是一个溢出问题,但奇怪的是它出现在渐变的中心而不是像素值较高的末端。另外,如果是溢出问题,我会怀疑 15 位图像也会受到影响。

对此的任何帮助将不胜感激。

我只是想知道为什么没有人回答,我是否提供了足够的信息?需要更多信息吗?

下面是我用来生成 AffineTransform 的代码。所有引用的变量都是根据用户输入(鼠标移动)计算得出的,并且应该是正确的(包括我自己在内的很多人都对其进行了测试)。希望这可以帮助解决错误。

AffineTransform panTranslate = new AffineTransform();
panTranslate.translate(imagePanOffset.x, imagePanOffset.y);

AffineTransform rotateCenterTranslate = new AffineTransform();
rotateCenterTranslate.translate(imageRotateCTR.x, imageRotateCTR.y);
AffineTransform rotateTransform = new AffineTransform();
rotateTransform.rotate(Math.toRadians(rotateValue));
AffineTransform rotateAntiCenterTranslate = new AffineTransform();
rotateAntiCenterTranslate.translate(-imageRotateCTR.x, -imageRotateCTR.y);

AffineTransform translateTransform = new AffineTransform();
translateTransform.translate(imageMagOffset.x, imageMagOffset.y);

AffineTransform flipMatrixTransform = new AffineTransform();

switch (flipState) {
    case ENV.FLIP_NORMAL: // NORMAL
        break;

    case ENV.FLIP_TOP_BOTTOM: // FLIP
        flipMatrixTransform.scale(1.0, -1.0);
        flipMatrixTransform.translate(0.0, -h);
        break;

    case ENV.FLIP_LEFT_RIGHT: // MIRROR
        flipMatrixTransform.scale(-1.0, 1.0);
        flipMatrixTransform.translate(-w, 0.0);
        break;

    case ENV.FLIP_TOP_BOTTOM_LEFT_RIGHT: // FLIP+MIRROR
        flipMatrixTransform.scale(-1.0, -1.0);
        flipMatrixTransform.translate(-w, -h);
        break;
}

scaleTransform = new AffineTransform();
scaleTransform.scale(magFactor, magFactor);

AffineTransform atx = new AffineTransform();
atx.concatenate(panTranslate);
atx.concatenate(rotateCenterTranslate);
atx.concatenate(rotateTransform);
atx.concatenate(rotateAntiCenterTranslate);
atx.concatenate(translateTransform);
atx.concatenate(flipMatrixTransform);
atx.concatenate(scaleTransform);

我仍然不知道这里发生了什么。我真的很感激可以提供的任何帮助。我还附上了我遇到的真实图像中发生的错误示例,以供更多参考。

这是手部 X 光片中发生的错误

这是一个放大版本,重点放在拇指和食指之间的区域。

再次注意该错误不会出现在极白的区域,而是出现在动态范围中间的值上,就像在渐变图像中一样。

我发现了更多信息。我正在调整一些转换,发现如果我只是通过单位矩阵进行过滤,则不会发生该错误。如果我翻译一个整数,它也不会发生。如果我翻译一个非整数量,它确实会发生。如果我缩放 1 以外的任何数量(整数或非整数),也会发生这种情况。希望这会有所帮助。

经过更多试验,该错误肯定会在最大强度一半 (65535/2 = 32767.5) 之间的边界像素处出现。它也只发生在这个值上。我希望这可能有助于诊断!

应 AlBlue 的要求,这里的代码完全独立于我的应用程序,它可以生成错误。请注意,在原始帖子中,我包含了使用以下代码生成的图像渐变,但是我放大了其中一个渐变以更好地显示效果。您应该在 0.5 转换后的图像上看到四次效果,而不是在其他两个图像中的任何一个上。另请注意,在缩放除 1 以外的任何数量时都会出现此错误。只需将 AffineTransform.getTranslateInstance() 替换为 AffineTransform.getScaleInstance(0.9, 0.9) 即可查看该错误。

private static class MyJPanel extends JPanel {
    BufferedImage displayImage = null;
    public MyJPanel(double translateValue) {
        super();
        BufferedImage bi = new BufferedImage(1024, 1024, BufferedImage.TYPE_USHORT_GRAY);

        int dataRange = (int)Math.pow(2, 16);
        double step = dataRange/(bi.getRaster().getDataBuffer().getSize()/4.0);
        double value = 0;
        for (int i=0; i<bi.getRaster().getDataBuffer().getSize(); i++) {
            bi.getRaster().getDataBuffer().setElem(i, (int)value);
            if (value >= dataRange)
                value = 0;
            else
                value += step;
        }
        displayImage = new BufferedImage(bi.getWidth(), bi.getHeight(), bi.getType());
        AffineTransform tx = AffineTransform.getTranslateInstance(translateValue, translateValue);
        AffineTransformOp op = new AffineTransformOp(tx, AffineTransformOp.TYPE_BILINEAR);
        op.filter(bi, displayImage);
    }

    public void paint(Graphics g) {
        super.paint(g);
        g.drawImage(displayImage, 0, 0, this);
    }
}

private static void showDisplayError() {
    JDialog dialog1 = new JDialog();
    dialog1.setTitle("No Translation");
    MyJPanel panel1 = new MyJPanel(0);
    dialog1.getContentPane().add(panel1);
    dialog1.setSize(1024, 1024);
    dialog1.setVisible(true);

    JDialog dialog2 = new JDialog();
    dialog2.setTitle("Translation of 0.5");
    MyJPanel panel2 = new MyJPanel(0.5);
    dialog2.getContentPane().add(panel2);
    dialog2.setSize(1024, 1024);
    dialog2.setVisible(true);

    JDialog dialog3 = new JDialog();
    dialog3.setTitle("Translation of 1.0");
    MyJPanel panel3 = new MyJPanel(1.0);
    dialog3.getContentPane().add(panel3);
    dialog3.setSize(1024, 1024);
    dialog3.setVisible(true);
}

作为另一个更新,我刚刚在 Fedora 10 上尝试过,发现错误仍然存​​在。

【问题讨论】:

  • 它看起来确实是某种溢出问题,因为正是三行像素应该具有 RGB 颜色 128-128-128 已更改。 (它们被替换为一排 57-57-57、一排 232-232-232 和一排 151-151-151。)但我不知道为什么。
  • 感谢您的回复。这些图片应该是灰度的,所以应该只有一个频道,为什么你建议你发布的特定值?
  • 我在 gimp 中打开了您的 png 图像并查看了颜色值。两者都有从 0 到 255(0x00 到 0xff)的平滑垂直渐变,除了顶部和底部的白色和黑色带,以及第二张图像中间的三个有问题的像素行。但同样,我不知道它们来自哪里。
  • 好的。我只是选择使用 ImageIO.write() 将它们保存为 .png 格式,然后将它们发布到此处。如果我以不同的格式保存它们,是否可以显示更多信息?
  • 我正在寻找解决方法,有人吗?在 NM 医学图像上遇到了完全相同的问题。

标签: java image-processing bufferedimage 16-bit affinetransform


【解决方案1】:

您使用的是什么版本的 java (java -version) 和操作系统?这可能是转换中的错误(现已修复),也可能是渲染为 PNG 时出现的错误。

您是否尝试过使用 NEAREST_NEIGHBOR 过滤器而不是 BILINEAR 过滤器?

【讨论】:

  • 是的,我有,使用最近邻居时它不会出现。但是,由于我正在处理医疗数据,因此用户将在非常高分辨率的监视器上显示 16 位数据,而最近邻插值将不够。
  • 我使用的是 Windows XP SP3 32 位和 Java 1.6.0_18
  • 这不是向 PNG 渲染的错误。我使用 dcm4che (www.dcm4che.org) 将文件作为 Dicom 文件加载并创建一个 BufferedImage。然后我将此 BufferedImage 绘制到屏幕上并查看错误。我只是选择将其保存为 PNG 以在此处显示错误。
  • 好的,所以我可以在我的 Mac (java -version 1.6.0_17) 上运行它并获得与您类似的结果。无翻译和 1 翻译没有可见的人工制品; 0.5 的翻译在中间有一个伪影。奇怪的是,在图像的左侧,它是纯白色的,而图像的右侧是纯黑色的。无论哪种方式,它似乎都是一个 Java 错误,这意味着目前,您的选择是 (a) 向 Sun 提出一个错误,以及 (b) 实施您自己的作为解决方法。
  • 假设你有一个测试用例(尽管请在一个类和一个 main 方法中提交它以使其更容易),他们应该很容易调查。但是,对于快速响应,我不会抱太大希望。您是否尝试过为您的数据使用错误缓冲区大小,如 TYPE_UINT 或浮点值之一,如 DOUBLE?它们不仅精度会提高 5 倍,而且双精度值可能较少受到非积分变换的影响。
【解决方案2】:

您可以通过在 Graphics2D 而不是 AffineTransformOp 中应用转换来解决此问题:

if (useG2D) {
    Graphics2D g = displayImage.createGraphics();
    g.transform(tx);
    g.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
                       RenderingHints.VALUE_INTERPOLATION_BILINEAR);
    g.drawImage(bi, null, 0, 0);
} else {
    AffineTransformOp op = new AffineTransformOp(tx, AffineTransformOp.TYPE_BILINEAR);
    op.filter(bi, displayImage);
}

我不知道为什么这会给出不同的输出,但确实如此。

注意:useG2D 可以是常数,也可以根据tx.getType() 的结果设置。 TYPE_QUADRANT_ROTATIONTYPE_FLIPTYPE_IDENTITY 转换不会出现该错误。

【讨论】:

    【解决方案3】:

    你解决了吗?这可能是由于未正确使用 AffineTransformOp 造成的。您是如何创建 AffineTransform atx 的?如果我有,我应该能够复制以帮助调试。

    您也不妨看看这个site。它包含很多关于 AffineTransformOp 的有用信息

    【讨论】:

    • 我已经发布了生成 AffineTransform 的代码,如果有帮助请告诉我。
    • 不,我还没有找到解决方案。
    • 您是否能够通过查看 AffineTransform 的源代码找到任何东西?
    • 看来转换确实会影响错误的存在。有关详细信息,请参阅我的最新编辑。
    猜你喜欢
    • 2021-05-23
    • 1970-01-01
    • 1970-01-01
    • 2018-07-25
    • 1970-01-01
    • 2010-10-22
    • 1970-01-01
    • 2018-04-12
    • 1970-01-01
    相关资源
    最近更新 更多