2010-03-11 7 views
10

У меня проблема с использованием билинейной интерполяции для 16-битных данных. У меня есть два изображения: origImage и displayImage. Я хочу использовать AffineTransformOp для фильтрации origImage через AffineTransform в displayImage, который является размером области отображения. origImage имеет тип BufferedImage.TYPE_USHORT_GRAY и имеет растр типа sun.awt.image.ShortInterleavedRaster. Вот код, который у меня есть сейчасОшибка Java при билинейной интерполяции 16-битных данных

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 и увидел, что ошибка все еще присутствует.

+2

Это похоже на проблему с переполнением, поскольку именно три строки пикселей должны иметь цвет RGB 128-128-128, которые были изменены. (Они заменяются одной строкой 57-57-57, одной строкой 232-232-232 и одной строкой 151-151-151.) Но я понятия не имею, почему. –

+0

Спасибо за ответ. Эти изображения должны быть в оттенках серого, поэтому должен быть только один канал, почему вы предлагаете конкретные значения, которые вы опубликовали? – Jon

+0

Я открыл ваши png-изображения в gimp и посмотрел на значения цвета. Оба имеют гладкий вертикальный градиент от 0 до 255 (от 0x00 до 0xff), за исключением белых и черных полос сверху и снизу и трех проблемных рядов пикселей в середине второго изображения. Но опять же, я понятия не имею, откуда они взялись. –

ответ

0

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

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

+1

Я отправил код для создания AffineTransform, сообщите мне, если это поможет. – Jon

+0

И нет, я еще не понял решение этого вопроса. – Jon

+0

Вы могли найти что-нибудь, посмотрев источник для AffineTransform? – Jon

2

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

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

+0

Да, у меня оно не появляется при использовании ближайшего соседа. Однако, поскольку я работаю с медицинскими данными, пользователь будет иметь 16-битные данные, отображаемые на мониторах с очень высоким разрешением, где интерполяция ближайшего соседа будет недостаточной. – Jon

+0

Я использую Windows XP SP3 32 бит и Java 1.6.0_18 – Jon

+0

Это не преобразование ошибок в PNG. Я загружаю файл в файл Dicom с помощью dcm4che (www.dcm4che.org) и создаю BufferedImage.Затем я рисую этот BufferedImage на экране и вижу ошибку. Я просто решил сохранить его как PNG, чтобы показать здесь ошибку. – Jon

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 преобразуется.