0

Is there some really simple and basic code for making preview for HDR images (like getting 2D BufferedImage output or something)?

I am using this HDR image.

I tried this (it uses TwelveMonkeys), but no success at all (it simply stuck/frozen at ImageReader reader = readers.next();)

I edited it a bit to suit my needs like this, testing where it got broken/stuck/frozen...and it always happen after TEST 1, that is TEST 2 is never reached, tho no IllegalArgumentException is thrown - if I remove the if() section, then TEST 3 is never reached (I am using NetBeansIDE v12.4, Win7 x64):

public BufferedImage hdrToBufferedImage(File hdrFile) throws IOException {
    BufferedImage bi = null;

    // Create input stream
    // I WROTE DOWN THE STRING FOR THIS EXAMPLE, normally it is taken from the hdrFile
    // HDR image size is 23.7MB if it matters at all?
    ImageInputStream input = ImageIO.createImageInputStream(new File("Z:/HDR/spiaggia_di_mondello_4k.hdr"));

    try {
        // Get the reader
        Iterator<ImageReader> readers = ImageIO.getImageReaders(input);
        System.err.println("=====>>> TEST 1");

        if (!readers.hasNext()) {
            throw new IllegalArgumentException("No reader for: " + hdrFile);
        }
        System.err.println("=====>>> TEST 2");

        ImageReader reader = readers.next();
        System.err.println("=====>>> TEST 3");

        try {
            reader.setInput(input);

            // Disable default tone mapping
            HDRImageReadParam param = (HDRImageReadParam) reader.getDefaultReadParam();
            param.setToneMapper(new NullToneMapper());

            // Read the image, using settings from param
            bi = reader.read(0, param);
        } finally {
            // Dispose reader in finally block to avoid memory leaks
            reader.dispose();
        }
    } finally {
        // Close stream in finally block to avoid resource leaks
        input.close();
    }

    // Get float data
    float[] rgb = ((DataBufferFloat) bi.getRaster().getDataBuffer()).getData();

    // Convert the image to something easily displayable
    BufferedImage converted = new ColorConvertOp(null).filter(bi, new BufferedImage(bi.getWidth(), bi.getHeight(), BufferedImage.TYPE_INT_RGB));

    return converted;
}
fafa
  • 77
  • 8
  • Unless you actually plan on doing custom tone mapping, you could just use `return ImageIO.read(hdrFile);`. Anyway, the above code works for me (Java 8/11/17). I suggest you use a debugger to step *into* the `next()` or `hasNext()` methods to figure out the exact spot where it freezes. – Harald K May 30 '22 at 14:37
  • if I do `BufferedImage bi = ImageIO.read(hdrFile); System.err.println("=====>>> TEST A");` it passes to TEST A, but as soon as I do `BufferedImage bi = ImageIO.read(hdrFile); System.err.println("=====>>> TEST B: " + bi.getHeight());` it stuck once again - no test CMD output, besides it looks like it produces no actual BufferedImage content, just empty `BufferedImage` or something – fafa May 30 '22 at 14:49
  • As I said, use a debugger, and find your answer. – Harald K May 30 '22 at 14:52

3 Answers3

1

Well, if you don't mind occasional extreme halucinogenic oversaturation of some colors here and there (I was unable solving the issue - if anyone knows how to, please, feel free to update my code), you can try this (it is using JavaHDR) + I also added a bit of brightness and contrast to it as all HDR I tested looked too dark for the preview, so if you do not like that you can remove that part from the code:

public int rgbToInteger(int r, int g, int b) {
    int rgb = r;
    rgb = (rgb << 8) + g;
    rgb = (rgb << 8) + b;
    return rgb;
}

public BufferedImage hdrToBufferedImage(File hdrFile) throws IOException {
    HDRImage hdr = HDREncoder.readHDR(hdrFile, true);
    int width = hdr.getWidth();
    int height = hdr.getHeight();
    BufferedImage bi = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
    for (int x = 0; x < width; x++) {
        for (int y = 0; y < height; y++) {
            int r = (int) (hdr.getPixelValue(x, y, 0) * 255);
            int g = (int) (hdr.getPixelValue(x, y, 1) * 255);
            int b = (int) (hdr.getPixelValue(x, y, 2) * 255);
            bi.setRGB(x, y, rgbToInteger(r, g, b));
        }
    }

    //***** YOU CAN REMOVE THIS SMALL SECTION IF YOU FEEL THE IMAGE IS TOO BRIGHT FOR YOU
    float brightness = 2f;
    float contrast = 20f;
    RescaleOp rescaleOp = new RescaleOp(brightness, contrast, null);
    rescaleOp.filter(bi, bi);
    //***** 

    return bi;
}
qraqatit
  • 492
  • 4
  • 14
  • I accept this as correct answer although those saturated colors are quite annoying, but still it serves my simple needs - the image is clearly visible and that's basically what I needed... Tho it'd be great if someone could solve the issue! – fafa May 31 '22 at 01:01
  • 1
    To normalize the values in this code, I think `int r = (int) ((hdr.getPixelValue(x, y, 0) / (0.75 + hdr.getPixelValue(x, y, 0)) * 255)` (and same for the rest of the components) should work the same as using `DefaultToneMapper(0.75f)` (and no `RescaleOp` later). Assuming `hdr.getPixelValue(...)` returns the floating point value in the HDR file for that pixel/sample. – Harald K Jun 01 '22 at 12:03
  • yes, that made the trick! – fafa Jun 01 '22 at 12:48
1

I can compile and run the code you posted (changing the path obviously) without problems on my two macOS machines, testing on all the LTS Java versions (8, 11 and 17). In addition, I run code similar to this as part of the CI/CD pipeline of my project that tests on Windows and Linux as well. I think there is something wrong with the setup in your IDE or Java on your computer. I am not able to reproduce the "freeze"-situation you describe...

Here is the output of running the program (I also printed the resulting BufferedImage for verification):

=====>>> TEST 1
=====>>> TEST 2
=====>>> TEST 3
image = BufferedImage@5a42bbf4: type = 1 DirectColorModel: rmask=ff0000 gmask=ff00 bmask=ff amask=0 IntegerInterleavedRaster: width = 1024 height = 512 #Bands = 3 xOff = 0 yOff = 0 dataOffset[0] 0

Running with the code as-is (with the NullToneMapper and no post-processing), the image looks like this, due to unnormalized values:

enter image description here

Running with the default/built-in tone mapper, or simply reading the image with ImageIO.read(hdrFile) as suggested in the comments, the image will look like this:

enter image description here

Finally, playing a bit with the code using a custom global tone mapper; param.setToneMapper(new DefaultToneMapper(0.75f)), I get a result like this:

enter image description here

Harald K
  • 26,314
  • 7
  • 65
  • 111
  • If your code would work for me I would definitely set your answer as the correct one, but unfortunately I do not know how to find out what is wrong with my NetBeansIDE or Java installation on my Win7 x64 OS (how to run that debugging you suggested). BTW the answer I set as the correct one makes the BufferedImage output EXACTLY AS YOURS 1st ONE with those hallucinogenic-like oversaturated hallos, which makes me think maybe I need just some sort of "finetuning" as you have in your code,which makes me think there actually is nothing wrong with my IDE as it produces the image just wrongly colored. – fafa Jun 01 '22 at 11:42
  • ...that is how to normalize the values having the code that works for me? + as I said before, in my case it simply does not even pass that simple line of `ImageIO.read(hdrFile);` Are there some sorts of characters that cannot occur in the file path? Like _, numbers or so? Cos that is the only thing I can think of, for exmaple in my real file path I have folder like NameOfMyApp_1_000 - can that be a problem? – fafa Jun 01 '22 at 11:44
  • 1
    I don't use NetBeans, so I can't help you there, unfortunately... I'm sure you'll find a tutorial. In IntelliJ IDEA, you just put a breakpoint (by clicking the line number) at the line *before* it freezes, and then hit "Debug" (next to the "Run" button). Then step through the code from there. You really should learn this technique as it is invaluable for learning how code works. – Harald K Jun 01 '22 at 11:53
  • For the normalization, you can find the code for [`DefaultToneMapper` here](https://github.com/haraldk/TwelveMonkeys/blob/master/imageio/imageio-hdr/src/main/java/com/twelvemonkeys/imageio/plugins/hdr/tonemap/DefaultToneMapper.java). – Harald K Jun 01 '22 at 11:59
  • OK, I finally LEARNED SOME NEW STUFF - thanks, but back to my problem: the debugging simply says it stop working at that breakpoint, that is no problem reported whatsoever at all. this is the window message: `Listening on javadebug User program running LineBreakpoint UtilitiesImages.java : 539 successfully submitted. Breakpoint hit at line 539 in class myapp.utils.UtilitiesImages by thread ForkJoinPool.commonPool-worker-1. Thread ForkJoinPool.commonPool-worker-1 stopped at UtilitiesImages.java:539.` That line 539 is your simple suggested code that you said work: `ImageIO.read(hdrFile);` – fafa Jun 01 '22 at 12:24
  • My NetBeansIDE runs on JAVA v1.8.0_181, 64-bit – fafa Jun 01 '22 at 12:29
  • Yes, the debugger will stop (pause) at the breakpoint. That's the basic idea... Then you must use the debugger to continue running or step over/into the next line and so on. You want to step *into* as deep as you can to figure out where the code *freezes*. – Harald K Jun 01 '22 at 12:29
  • what? wait, but I do know where it stops/freezes: at the ImageIO.read() - there is no way I can move next or somehting, right? How can I got into the compiled jar code of ImageIO??? As I understand it now it does not add any new funcitonality toi my simplistic TEST1, TEST2 - or am I wrong once again here? Sorry I am still Java newbie. Maybe the best thing for me would be to actually know how to change resulting rgb int for setRGB() to make the color tone right, but how? I look at your code, but it does not helped me at all - I tried add that rgb/(rgb+const) with my vals but img was black – fafa Jun 01 '22 at 12:30
  • I'm sure you can, I do it all the time using IntelliJ IDEA. You just have to figure out how to do this in NetBeans. Downloading the sources of the JDK will also make things easier to follow. – Harald K Jun 01 '22 at 12:35
  • AH, unfortunately, this sound way further than my newbie Java skills, unfortunately - what I needed was some simple solution how to easily modify rgb integer to set the tone correctly, nothing else. Maybe I make new post about it? But I am afraid I would be banned or something for posting the same question differently? – fafa Jun 01 '22 at 12:36
  • You can buy fish at the supermarket or learn how to fish. Up to you. – Harald K Jun 01 '22 at 12:37
  • I wish I knew why the code does not work for you, I really do. But I just can't reproduce the behavior you describe. I know it's possible to dig further into how and why, but as it does not happen for me, and I use different tooling I can't help you there. – Harald K Jun 01 '22 at 12:50
  • I just see you actually DID PROVIDE THE ANSWER as a simple reply...sorry then, sorry - your suggestion works perfectly, thank you. I will post it once again corrected by your suggestion mentioning you. – fafa Jun 01 '22 at 12:50
  • PS: For the future... I see in your comment above that you have a `ForkJoinPool`... But that's not part of the code you posted. Could it be that the code does not freeze, but throws an exception that kills the thread, but is swallowed (not logged)? It would be interesting to see the rest of that code... – Harald K Jun 02 '22 at 07:48
  • that Fork-thingie is part of NetBeansIDE debug process :-)))) ...or at least it looks like that, as it only shows up during the debug in the debug window. But what I thought is this: you say you run on MacOS and testing on Win ONLY WITH SOME SORT OF VIRTUAL MACHINE, right? So, could it be that in fact on real Windows machine your approach would not work? Just guessing, you see - newbie here, but do you get some feedback about funcitonality of yer code form real win users? – fafa Jun 02 '22 at 12:00
0

After a long discussion with @HaraldK and his code addition, I am posting the final correct code for this problem, that is in fact mix of @qraqatit code updated a bit with the @HaraldK addition that corrects wrong color tone mapping, here it is:

public int rgbToInteger(int r, int g, int b) {
    int rgb = r;
    rgb = (rgb << 8) + g;
    rgb = (rgb << 8) + b;
    return rgb;
}

public BufferedImage hdrToBufferedImage(File hdrFile) throws IOException {
    HDRImage hdr = HDREncoder.readHDR(hdrFile, true);
    int width = hdr.getWidth();
    int height = hdr.getHeight();
    BufferedImage bi = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
    float colorToneCorrection = 0.75f;
    for (int x = 0; x < width; x++) {
        for (int y = 0; y < height; y++) {
            float r = hdr.getPixelValue(x, y, 0);
            int red = (int) ((r / (colorToneCorrection + r)) * 255);
            float g = hdr.getPixelValue(x, y, 1);
            int green = (int) ((g / (colorToneCorrection + g)) * 255);
            float b = hdr.getPixelValue(x, y, 2);
            int blue = (int) (int) ((b / (colorToneCorrection + b)) * 255);
            bi.setRGB(x, y, rgbToInteger(red, green, blue));
        }
    }

    //MAKE THE RESULTING IMAGE A BIT BRIGHTER
    float brightness = 1.35f;
    float contrast = 0f;
    RescaleOp rescaleOp = new RescaleOp(brightness, contrast, null);
    rescaleOp.filter(bi, bi);

    return bi;
}
fafa
  • 77
  • 8