Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ConvertBufferedImage does not always return gray images for SingleBands #28

Closed
thhart opened this issue Jun 25, 2015 · 9 comments
Closed

Comments

@thhart
Copy link
Contributor

thhart commented Jun 25, 2015

In ConvertBufferedImages.checkInputs there a gray scale image is only returned when the src image type is ImageInt16. But it should return a gray scale not by type but by src band width.

@lessthanoptimal
Copy link
Owner

Having trouble understand. Can you clarify/give an example?

@thhart
Copy link
Contributor Author

thhart commented Jun 25, 2015

Please check code below. It produces following output:

I8: 3
I16: 1
F32: 3

Either I misunderstand your concept or there is something wrong. I expect 1 for all types.

ImageBase singleBand = GeneralizedImageOps.createSingleBand(ImageDataType.I8, 10, 10); BufferedImage image = ConvertBufferedImage.convertTo(singleBand, null, false); System.err.println("I8: " + image.getSampleModel().getNumBands()); singleBand = GeneralizedImageOps.createSingleBand(ImageDataType.I16, 10, 10); image = ConvertBufferedImage.convertTo(singleBand, null, false); System.err.println("I16: " + image.getSampleModel().getNumBands()); singleBand = GeneralizedImageOps.createSingleBand(ImageDataType.F32, 10, 10); image = ConvertBufferedImage.convertTo(singleBand, null, false); System.err.println("F32: " + image.getSampleModel().getNumBands());

@lessthanoptimal
Copy link
Owner

Ok I understand. You've run into a case that was missed. I'm not sure what to do with F32 images. There really isn't an equivalent BufferedImage. Maybe TYPE_INT_RGB so that positive and negative numberse can be colorized? TYPE_BYTE_GRAY since it's just a single band? Throw an exception since there is no reasonable default?

@thhart
Copy link
Contributor Author

thhart commented Jun 25, 2015

How about this:
I8 is mapped to TYPE_BYTE_GRAY
i16 is mapped to TYPE_USHORT_GRAY
F32 is mapped to TYPE_USHORT_GRAY

IMHO TYPE_INT_RGB is not better at all because it also has "only" 8 bit per color. So it is waste of memory then.

@lessthanoptimal
Copy link
Owner

what do you do with 16bit BufferedImages? It's a bit of a weird format. Most of the time I convert a float into a BufferedImage because I want to visualize it, so having an RGB format makes more sense

@lessthanoptimal
Copy link
Owner

I'm now leaning towards an exception since it's probably application specific what you want.

@thhart
Copy link
Contributor Author

thhart commented Jun 26, 2015

Hi Peter, I have written a little test to understand the issue. As far as I can see the USHORT_GRAY is really the best option because it has 16 bit for the gray scale and really this information. INT_RGB reserves 8 bit per band so you have only a 8 bit gray scale there but three 9 times bigger data which makes it inefficient to handle/paint also.
I have created a sample gray scale gradient and pushed this into several BufferedImages as shown below.

gradient

TYPE_3BYTE_BGR, type: 5, bands: 3, bits: 24
first pixel value: 255.0
last pixel value: 0.0
TYPE_BYTE_GRAY, type: 10, bands: 1, bits: 8
first pixel value: 255.0
last pixel value: 0.0
TYPE_INT_RGB, type: 1, bands: 3, bits: 24
first pixel value: 255.0
last pixel value: 0.0
TYPE_USHORT_GRAY, type: 11, bands: 1, bits: 16
first pixel value: 65535.0
last pixel value: 0.0

    private void loadImages() throws IOException {
      BufferedImage read = ImageIO.read(new File("gradient.png"));
      printImageInfo("TYPE_3BYTE_BGR", read);
      ImageIO.write(read, "png", new File("typeCustom.png"));
      final BufferedImage gray = pushToFormat(read, BufferedImage.TYPE_BYTE_GRAY);
      printImageInfo("TYPE_BYTE_GRAY", gray);
      ImageIO.write(gray, "png", new File("typeByteGray.png"));
      final BufferedImage rgb = pushToFormat(read, BufferedImage.TYPE_INT_RGB);
      printImageInfo("TYPE_INT_RGB", rgb);
      ImageIO.write(rgb, "png", new File("typeIntRgb.png"));
      final BufferedImage ushort = pushToFormat(read, BufferedImage.TYPE_USHORT_GRAY);
      printImageInfo("TYPE_USHORT_GRAY", ushort);
      ImageIO.write(ushort, "png", new File("typeShortGray.png"));
   }
   private BufferedImage pushToFormat(BufferedImage read, int type) {
      final BufferedImage li = new BufferedImage(read.getWidth(), read.getHeight(), type);
      li.getGraphics().drawImage(read, 0,0, null);
      return li;
   }
   private void printImageInfo(String name, BufferedImage read) {
      System.err.println(name + ", type: " + read.getType() + ", bands: " + read.getSampleModel().getNumBands() + ", bits: " + read.getColorModel().getPixelSize());
      System.err.println("first pixel value: " + read.getData().getPixel(0,0, (double[])null)[0]);
      System.err.println("last pixel value: " + read.getData().getPixel(read.getWidth() - 1, 0, (double[])null)[0]);
   }

@lessthanoptimal
Copy link
Owner

But it doesn't handle negative numbers. I tend to use RGB for floats when
visualizing. I use one band for positive numbers and another for negative
numbers. Really what I'm proposing is that there's no default and an error
message will pop up with two suggestions, 16bit gray or RGB. Plus to take
advantage of that full change of numbers you would need to massage the data
first. People will often scale a gray input image to have intensities from
0 to 1 for floats. if it's feature intensity then the range is positive
and negative and has an arbitrary upper and lower limit.
ConvertBufferedImage doesn't do any of that. It's a dumb type cast.

On Fri, Jun 26, 2015 at 2:47 AM, thhart notifications@github.com wrote:

Hi Peter, I have written a little test to understand the issue. As far as
I can see the USHORT_GRAY is really the best option because it has 16 bit
for the gray scale and really this information. INT_RGB reserves 8 bit per
band so you have only a 8 bit gray scale there but three 9 times bigger
data which makes it inefficient to handle/paint also.
I have created a sample gray scale gradient and pushed this into several
BufferedImages as shown below.

[image: gradient]
https://cloud.githubusercontent.com/assets/2632538/8374781/7c6ade9e-1bf8-11e5-924d-7eea2c9dc7d1.png

TYPE_3BYTE_BGR, type: 5, bands: 3, bits: 24
first pixel value: 255.0
last pixel value: 0.0
TYPE_BYTE_GRAY, type: 10, bands: 1, bits: 8
first pixel value: 255.0
last pixel value: 0.0
TYPE_INT_RGB, type: 1, bands: 3, bits: 24
first pixel value: 255.0
last pixel value: 0.0
TYPE_USHORT_GRAY, type: 11, bands: 1, bits: 16
first pixel value: 65535.0
last pixel value: 0.0

private void loadImages() throws IOException {
  BufferedImage read = ImageIO.read(new File("gradient.png"));
  printImageInfo("TYPE_3BYTE_BGR", read);
  ImageIO.write(read, "png", new File("typeCustom.png"));
  final BufferedImage gray = pushToFormat(read, BufferedImage.TYPE_BYTE_GRAY);
  printImageInfo("TYPE_BYTE_GRAY", gray);
  ImageIO.write(gray, "png", new File("typeByteGray.png"));
  final BufferedImage rgb = pushToFormat(read, BufferedImage.TYPE_INT_RGB);
  printImageInfo("TYPE_INT_RGB", rgb);
  ImageIO.write(rgb, "png", new File("typeIntRgb.png"));
  final BufferedImage ushort = pushToFormat(read, BufferedImage.TYPE_USHORT_GRAY);
  printImageInfo("TYPE_USHORT_GRAY", ushort);
  ImageIO.write(ushort, "png", new File("/tmp/typeShortGray.png"));

}

private BufferedImage pushToFormat(BufferedImage read, int type) {
final BufferedImage li = new BufferedImage(read.getWidth(), read.getHeight(), type);
li.getGraphics().drawImage(read, 0,0, null);
return li;
}

private void printImageInfo(String name, BufferedImage read) {
System.err.println(name + ", type: " + read.getType() + ", bands: " + read.getSampleModel().getNumBands() + ", bits: " + read.getColorModel().getPixelSize());
System.err.println("first pixel value: " + read.getData().getPixel(0,0, (double[])null)[0]);
System.err.println("last pixel value: " + read.getData().getPixel(read.getWidth() - 1, 0, (double[])null)[0]);
}


Reply to this email directly or view it on GitHub
#28 (comment)
.

"Now, now my good man, this is no time for making enemies." — Voltaire
(1694-1778), on his deathbed in response to a priest asking that he
renounce Satan.

@lessthanoptimal
Copy link
Owner

Well that broke a lot of stuff. For maintaining some backwards compatibility TYPE_BYTE_GRAY seems to be the best default.

  1. It's single band like the input
  2. Has the implicit assumption that the input can be converted into a BufferedImage, which is most often the case if the pixel value range is 0 to 255
  3. TYPE_USHORT_GRAY rendered images way too dark

If you need anything special just provide your own BufferedImage instead of null. That's what everything in VisualizeImageData does, which is where I put all the application specific convert for visualization.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants