Java error on bilinear interpolation of 16 bit data
I'm having an issue using bilinear interpolation for 16 bit data. I have two images, origImage and displayImage. I want to use AffineTransformOp to filter origImage through an AffineTransform into displayImage which is the size of the display area. origImage is of type BufferedImage.TYPE_USHORT_GRAY and has a raster of type sun.awt.image.ShortInterleavedRaster. Here is the code I have right now
displayImage = new BufferedImage(getWidth(), getHeight(), origImage.getType());
try {
op = new AffineTransformOp(atx, AffineTransformOp.TYPE_BILINEAR);
op.filter(origImage, displayImage);
}
catch (Exception e) {
e.printStackTrace();
}
In order to show the error I have created 2 gradient images. One has values in the 15 bit range (max of 32767) and one in the 16 bit range (max of 65535). Below are the two images
15 bit image
16 bit image
These two images were created in identical fashions and should look identical, but notice the line across the middle of the 16 bit image. At first I thought that this was an overflow problem however, it is weird that it's manifesting itself in the center of the gradient instead of at the end where the pixel values are higher. Also, if it was an overflow issue than I would suspect that the 15 bit image would have been affected as well.
Any help on this would be greatly appreciated.
I was just wondering why no one is answering, did I provide enough information? Is more info needed?
Below is the code I use to generate the AffineTransform. All of the referenced variables are calculated based off of user input (mouse movement) and should be correct (it's been tested by a lot of people including myself). Hopefully this can help with the error.
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.concatenat开发者_运维知识库e(flipMatrixTransform);
atx.concatenate(scaleTransform);
I still have no idea what's going on here. I'd really appreciate any help that can be provided. I've also attached an example of the bug happening in a real image that I encounter for more reference.
Here is the bug happening in an X-ray of the hand
Here is a zoomed up version focused on the area between the thumb and first finger.
Note again how the bug doesn't occur on the extremely white areas, but on the values in the middle of the dynamic range, just like in the gradient image.
I've discovered more information. I was adjusting some of the transforms and found that the bug does not occur if I just filter through an identity matrix. It also doesn't occur if I translate by an integer amount. It does occur if I translate by a non integer amount. It also occurs if I zoom by any amount other than 1 (integer or not). Hopefully this helps.
After more experimenting, the bug definitely manifests itself at the boundary pixels between half the max intensity (65535/2 = 32767.5). It also ONLY occurs at this value. I hope this might help diagnosis!!
At the request of AlBlue here is code that is completely independent of my application that can generate the bug. Note that in the original post I included an image gradient generated with the below code however I zoomed in on one of the gradients to better show the effect. You should see the effect four times on the 0.5 translated image and not on either of the other two images. Also note that this bug appears while scaling by any amount other than 1. Just replace AffineTransform.getTranslateInstance() with AffineTransform.getScaleInstance(0.9, 0.9) to see the bug also.
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);
}
As another update, I just tried this on Fedora 10 and saw the bug is still present.
What version of java (java -version) and OS are you using? It might be a bug in the transform (which has since been fixed) or it might be an error in the rendering to PNG.
Have you tried using a NEAREST_NEIGHBOR filter instead of the BILINEAR one?
You can work around it by applying the transform in a Graphics2D
instead of an 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);
}
I don't know why this would give different output, but it does.
Note: useG2D
could be a constant or it could be set based on the result of tx.getType()
. The bug does not occur with TYPE_QUADRANT_ROTATION
, TYPE_FLIP
or TYPE_IDENTITY
transforms.
Did you solve this? It is likely a being caused by not using the AffineTransformOp correctly. How did you create the AffineTransform atx ? If I have that I should be able to replicate to help debug.
You may wish to have a look at this site too. It contains lots of useful information about AffineTransformOp
精彩评论