Подтвердить что ты не робот

Ошибка Java при билинейной интерполяции 16-битных данных

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

4b9b3361

Ответ 1

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

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

Ответ 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_ROTATION, TYPE_FLIP или TYPE_IDENTITY.

Ответ 3

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

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