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

Encode 16bit image #17

Closed
oeway opened this issue Mar 1, 2018 · 23 comments
Closed

Encode 16bit image #17

oeway opened this issue Mar 1, 2018 · 23 comments

Comments

@oeway
Copy link

oeway commented Mar 1, 2018

Hi,
Thanks for the nice png library. It's great that it support 16bit image decoding, will 16-bit encoding be supported? Thank you.

@photopea
Copy link
Owner

photopea commented Mar 1, 2018

Nobody needed 16-bit encoding yet, but I think it should be quite easy to do :) What is your use case?

@oeway
Copy link
Author

oeway commented Mar 1, 2018 via email

@photopea
Copy link
Owner

photopea commented Apr 1, 2018

Saving 16-bit PNGs is not that hard, but we need a nice interface for it.

The current UPNG.encode() expects 8-bit RGBA image. I could let it accept 16-bit RGBA images (iff the input ArrayBuffer is 2x bigger than it should be). Would you mind reconverting your grayscale into RGBA before encoding? (i.e. R=G=B=Gray, Alpha=0xffff , the image would become 4x larger) ?

It would require you to reconvert your grayscale and use 4x more memory, but it would let us keep using the single UPNG.encode() function. Otherwise, we would need new functions UPNG.encodeGray8(), UPNG.encodeGray16() .... or more complicated parameters.

BTW. don't worry about your output PNGs being too large. UPNG would detect the "grayness" of RGBA and encode it appropriatelly.

@oeway
Copy link
Author

oeway commented Apr 1, 2018 via email

@photopea
Copy link
Owner

photopea commented Apr 1, 2018

The UPNG.encode gets Width, Height, and array of bytes of specific length. If we know, that the input is RGBA (four channels), we can deduce the channel depth from the area (width x height) and array length.

Do you understand what I mean by "unwrapping" your grayscale into RGBA? Are your images too large to do it? How large are they?

@oeway
Copy link
Author

oeway commented Apr 1, 2018 via email

@photopea
Copy link
Owner

photopea commented Apr 1, 2018

Wow, the 25600x25600 image would have 1.3 GB uncompressed (2 bytes per pixel). How large are these TIFFs? Do they have a lossy or lossless compression? If they are lossy, maybe you should use JPG instead of PNG.

@oeway
Copy link
Author

oeway commented Apr 1, 2018 via email

@photopea
Copy link
Owner

photopea commented Apr 1, 2018

UPNG.js supports "lossy compression" - quantization by a palette of colors (e.g. just 20 colors for the whole image). But these methods expect 8-bit colors right now.

BTW. why don't you reconvert your images to 8-bits? It would be a form of "lossy compression" and then you can give it to UPNG.js

@oeway
Copy link
Author

oeway commented Apr 2, 2018 via email

@photopea
Copy link
Owner

photopea commented Apr 2, 2018

I think I can expose a low-level encode function for you (that lets you specify the number of channels and bits per channel).

PNG format does not support 32-bit color depth now (but I guess most of image viewers can not display 32-bit TIFF either). Sure, you could split bits into multiple channels, but image viewers would not display it as intended. But do you really need "regular people" to be able to display it? If you want to show it on your website, I would convert it to a smaller bit depth and resolution, so visitors don't have to wait for hours until it is loaded.

@oeway
Copy link
Author

oeway commented Apr 2, 2018 via email

@photopea
Copy link
Owner

photopea commented Apr 2, 2018

Have you tried to "tunnel" your 16-bit grayscale as 8-bit RGBA? Pass your ArrayBuffer to UPNG.encode() and set the height to a half (so the number of bytes corresponds). Later, after you decode it, multiply the height by 2 and read ArrayBuffer as Uint16Array. It should work.

E.g. Your 3x4 16-bit image has 12x2=24 bytes. Look at those bytes as the 3x2 8-bit RGBA image (6 pixels x 4 bytes per pixel = 24 bytes).

@oeway
Copy link
Author

oeway commented Apr 2, 2018 via email

@photopea
Copy link
Owner

photopea commented Apr 2, 2018

Hi, so I implemented the support for 16-bit images. Have a look at UPNG.encodeLL()

@oeway
Copy link
Author

oeway commented Apr 2, 2018

Great, thanks, I will have a try and let you know.
Just a quick thought, do you think we will need a UPNG.toGray16?

@photopea
Copy link
Owner

photopea commented Apr 2, 2018

Do you mean for decoding? it can be read directly from img.data

@oeway
Copy link
Author

oeway commented Apr 3, 2018

Hey, I just tested with my data, I manage to generate 16-bit images with 2 frames. However, I haven't figured out how to get the pixel value correct.
Here is what I tried:

      var im1 = new Uint16Array(new ArrayBuffer(2560*2560*2));
      var im2 = new Uint16Array(new ArrayBuffer(2560*2560*2));

      for(var i=0;i<2560;i++){
        for(var j=0;j<2560;j++){
          im1[j*2560+i] = j;
          im2[j*2560+i] = i;
        }
      }
      var imgFile = UPNG.encodeLL([im1.buffer, im1.buffer], 2560, 2560, 1, 0, 16, [0, 0])
      //save imgFile

I do get two frame png image, but it appears like this when I open it with imagej:
screen shot 2018-04-03 at 16 06 41

According to the code, I shouldn't see strips but only a gradient image. And the pixel value range should be 0 to 2560;
Could you tell me what went wrong?

@photopea
Copy link
Owner

photopea commented Apr 3, 2018

I think the problem is in Endianity. Uint16Array in JS is usually little-endian (I hope they will fix the endianity in the spec soon). While PNG integers are big-endian - https://www.w3.org/TR/2003/REC-PNG-20031110/#3networkByteOrder . Such problem was never noticed in UPNG.js, because endianity does not matter for pixel depths <= 8 :)

So you have to reorder two bytes within your Uint16 value.

@oeway
Copy link
Author

oeway commented Apr 4, 2018

Hi, you are right. It's the Endianity issue.
I used the following function to swap bytes, and then it works.

function swap16(val) {
    return ((val & 0xFF) << 8) | ((val >> 8) & 0xFF);
 }

Thanks very much.
I think we can close this issue now.

@photopea
Copy link
Owner

photopea commented Apr 4, 2018

ok i am glad it works :)

@photopea photopea closed this as completed Apr 4, 2018
@monschine
Copy link

monschine commented Jun 27, 2018

could you please post the code on how the swap16 function need to be called

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

3 participants