dragon66 / icafe

Java library for reading, writing, converting and manipulating images and metadata
Eclipse Public License 1.0
203 stars 58 forks source link

Png encoding using upng.js techniques #77

Open twoxfh opened 6 years ago

twoxfh commented 6 years ago

I created png's with this great library and thought it was very compact. I then took the output and used upng.js and was abile to save an additional 20 to 30 percent without noticable quality loss. I don't have the expertise to implement what they did in Java, however, maybe someone else does. Would be a nice addition to this or its own library. Http://GitHub.com/photopea/UPNG.js

dragon66 commented 6 years ago

@twoxfh thanks for pointing out this.

  1. Although I tired my best to create smaller PNGs, icafe is not a tool to optimize PNG images.
  2. Given there are different formats, color depths, transparency control for PNG images, without specifying the exact parameters/color depth you used to save the PNG in icafe, the comparison with other tools doesn't make much sense.
  3. That being said, icafe does include a parameter to use Adaptive method to optimize PNG image size during encoding which could be passed to the encoder.
  4. From my experience, there is no bullet-proof way to squeeze a PNG image. The above mentioned Adaptive method works for some formats/color depths but not all.
  5. Would be willing to do some test with the bench mark images UPNG provided on it's website and see if anything can be implemented here in icafe.

Wen

twoxfh commented 6 years ago

@dragon66
Thanks for your response, I am trying to be a quick study. The upng.js lib allows you to restrict colors and converts to rgba8, I'm assuming that could be done with component color palette in the code below? I'm not sure how to use/build component color palette and I can't find any examples? This might be the key I'm missing in reducing file size. The end result I would like a flash back to Windows 95 :) Your thoughts are appreciated.

`` public static void sample() throws IOException, AWTException {

               BufferedImage screen = new BufferedImage(600, 800, BufferedImage.TYPE_BYTE_GRAY);         

                 ByteArrayOutputStream image = new ByteArrayOutputStream();

                 Rectangle rect = new Rectangle(0, 0, 800, 600);                                     

                  screen = new Robot().createScreenCapture(rect);

                  ImageType imageType = ImageType.PNG;

                 com.icafe4j.image.writer.ImageWriter writer = com.icafe4j.image.ImageIO.getWriter(imageType);

                 ImageParam.ImageParamBuilder builder = ImageParam.getBuilder();

                       PNGOptions pngOptions = new PNGOptions();

                              pngOptions.setApplyAdaptiveFilter(true);

                              pngOptions.setCompressionLevel(9);

                              pngOptions.setFilterType(Filter.NONE);

                              builder.colorType(ImageColorType.INDEXED);   

                              builder.componentColorPalette(null); //How to use?

                              builder.bitsPerPixel(8);

                              builder.hasAlpha(false);

                              builder.quantMethod(QuantMethod.POPULARITY);

                              builder.quantQuanlity(QuantQuality.GOOD);

                              builder.transparent(true);

                              builder.transparentColor(16777215);

                              builder.imageOptions(pngOptions);

                              try {

                              writer.setImageParam(builder.build());

                                 writer.write(screen, image);

                         File file = new File("imagetest.png");

                               FileOutputStream fop = new FileOutputStream(file);

                               if (!file.exists()) {

                                     file.createNewFile();

                              }

                               fop.write(image.toByteArray());

                              fop.flush();

                              fop.close();                            

                               image.close();

                 }catch(Exception e) {

                       e.printStackTrace();

                 }finally {                              

                        image.close();

                 }

          }
dragon66 commented 6 years ago

@twoxfh I am not sure what you mean by "restrict colors and converts to rgba8." If you want to restrict colors for the purpose of smaller image size, why do you want to convert to rgba8?

ICAFE can save PNG images as indexed-color (with single color transparency), true-color (with or without alpha - alpha channel transparency) or grayscale. To save lossless images, use true-color; to save lossy images, use indexed-color.

The example you provided actually will save the PNG as indexed-color. There are a couple of image parameters which are not actually used at the moment by icafe. The componentColorPalette is one them. So is the rgbColorPalette. The reason for this is the color palette itself is automatically created either during the color quantization process or through the color checking process in the case that the original images contain less than 256 colors.

For the bitsPerPixel parameter, the current implementation will only reduce the colors to 256 (bitsPerPixel = 8) if the original image contains number of colors more than 256. If it contains number of colors less than 256, icafe will automatically determine the smallest bitsPerPixel required to save the image as indexed-color.

I have done a small change to actually take this parameter into effect so quantization can actually reduce colors to less than 256. You can try play with the bitsPerPixel parameter to reduce your image size while saving to indexe-color image. The effective bitsPerPixel is 1, 2, 4, 8.

Another thing you can do is actually disable quantization but that may introduce noticeable artifacts into your output images.

twoxfh commented 6 years ago

@dragon66

Thank you for your feedback, been very helpful. Not sure if you want to close this issue or keep it open for UPNG.js review. If I come up with any code worthy of a pull request, will submit.

dragon66 commented 6 years ago

Thanks! Keep it open. I am also looking and comparing the results of ICAFE and UPNG to see what I can do to improve ICAFE.

dragon66 commented 6 years ago

I have done a systematic comparison of the indexed color PNG images created by tinyPNG, UPNG and ICAFE.

The images generated by ICAFE are usually a few KB larger than tinyPNG and more larger than UPNG. It is not a very big deal to me given those tools are specifically created for PNG optimization purpose.

One thing that does need to be addressed for ICAFE is the quantization process which does not handle well full alpha transparency for indexed color images as it was intended to address single color transparency like GIF.

Another thing I noticed is that there is no dithering process for UPNG which makes it easier to handle transparency. ICAFE on the other hand, has both diffusion and ordered dither support which makes it difficult to handle transparency.

I did some experiment and it seems promising to include full alpha management. Will update and close this issue when I finish updating the code to fully address this issue.

Further optimization for ICAFE to squeeze out a few KB per image is not a priority for me at the moment.