Ошибка Java при билинейной интерполяции 16-битных данных - PullRequest
10 голосов
/ 11 марта 2010

У меня проблема с использованием билинейной интерполяции для 16-битных данных. У меня есть два изображения, origImage и displayImage. Я хочу использовать AffineTransformOp для фильтрации origImage через AffineTransform в 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 битное изображение alt text

16-битное изображение alt text

Эти два изображения были созданы одинаковой модой и должны выглядеть одинаково, но обратите внимание на линию в середине 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);

Я до сих пор понятия не имею, что здесь происходит. Буду очень признателен за любую помощь, которая может быть оказана. Я также приложил пример ошибки, возникающей в реальном изображении, с которой я сталкиваюсь для дополнительной справки.

Вот ошибка, возникающая на рентгеновском снимке руки alt text

Вот увеличенная версия, сфокусированная на области между большим пальцем и первым пальцем. alt text

Еще раз обратите внимание, что ошибка возникает не на очень белых областях, а на значениях в середине динамического диапазона, как на градиентном изображении.

Я обнаружил больше информации. Я настраивал некоторые из преобразований и обнаружил, что ошибка не возникает, если я просто фильтрую матрицу тождеств. Это также не происходит, если я перевожу на целое число. Это происходит, если я перевожу не целым числом. Это также происходит, если я изменяю масштаб на любое значение, кроме 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 и увидел, что ошибка все еще присутствует.

Ответы [ 3 ]

2 голосов
/ 20 марта 2010

Какую версию java (java -version) и ОС вы используете? Это может быть ошибка в преобразовании (которая с тех пор была исправлена) или ошибка при рендеринге в PNG.

Вы пытались использовать фильтр NEAREST_NEIGHBOR вместо фильтра BILINEAR?

2 голосов
/ 27 октября 2010

Вы можете обойти это, применив преобразование в 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_ROTATION, TYPE_FLIP или TYPE_IDENTITY.

0 голосов
/ 16 марта 2010

Ты это решил? Вероятно, это вызвано неправильным использованием AffineTransformOp. Как вы создали AffineTransform ATX? Если у меня есть это, я должен быть в состоянии копировать, чтобы помочь отладке.

Возможно, вы захотите взглянуть и на этот сайт . Он содержит много полезной информации о AffineTransformOp

...