1

I have a big issue trying to convert a large scientific image (7 Mb) 16bit PNG image to JPG in order to compress it and check for any eventual artifact in Python. The original image can be found at: https://postimg.cc/p5PQG8ry Reading other answers here I have tried Pillow and OpenCV without any success the only thing I obtain is a white sheet. What I'm doing wrong? The commented line was an attempt from Read 16-bit PNG image file using Python but seems not working for me generating a data type error.

import numpy as np
from PIL import Image 
import cv2
image = cv2.imread('terzafoto.png', -cv2.IMREAD_ANYDEPTH)
cv2.imwrite('terza.jpg', image)
im = Image.open('terzafoto.png').convert('RGB')
im.save('terzafoto.jpg', format='JPEG', quality=100)
#im = Image.fromarray(np.array(Image.open('terzafoto.jpg')).astype("uint16")).convert('RGB')
GBBL
  • 576
  • 1
  • 6
  • 18
  • 1
    Why is there a minus in front of `cv2.IMREAD_ANYDEPTH`? – Dan Mašek May 11 '21 at 12:55
  • I have tried with the minus and without ..... NO result. It was taken from https://stackoverflow.com/questions/48863230/python-how-to-read-in-a-16-bit-png-grayscale-image – GBBL May 11 '21 at 16:25
  • That post is just plain wrong, I've added some comments to it explaining why. There's nothing like this anywhere in the documentation, and the source code confirms it. – Dan Mašek May 11 '21 at 17:13
  • Anyway, OpenCV can only write 8 bit JPEG files (and IIRC the standard JPEG format also only does 8bit, but I may be wrong on that). The JP2 (JPEG 2000) codec in OpenCV does support 16bit. – Dan Mašek May 11 '21 at 17:23
  • OK I have red the comments, thank you very interesting. But now what I have to do ? What I really want is to read the 16bit PNG and compress it into a JPG, trying different compression option and checking that no artifacts are produced. I was able to do that wit an external program (XnWiew) but I want to do it programmatically – GBBL May 11 '21 at 18:25
  • Well, if you want to write JPEG file, you have to convert the image to 8 bit first. `img = cv2.imread('terzafoto.png', cv2.IMREAD_ANYDEPTH)` followed by `cv2.imwrite('foo.jpeg', np.uint8(img // 256))` gives me an 8 bit jpeg, that looks like the original. | Like I said, if you want to write a 16 bit image, then you have to pick a different format. Or find a Python library that can do 16bit JPEGs, but asking for which one that is is off-topic here. | It's not entirely clear what eactlly it is you want from the question itself. – Dan Mašek May 11 '21 at 18:34
  • 1
    THANKS ! this is already some light in the dark ! It works ! I rapidly generated a set of images with different compression factors using [cv2.IMWRITE_JPEG_QUALITY, 90]. Block artifacts are evident at lower quality. I presume that the default is about [cv2.IMWRITE_JPEG_QUALITY, 95] ! – GBBL May 11 '21 at 19:07
  • If you permit me I will write a complete answer that could be useful for other users now correcting im = Image.fromarray((np.array(Image.open('terzafoto.png'))//256).astype("uint8")).convert('RGB') it works also for Pillow ! – GBBL May 11 '21 at 19:17
  • Sure, go for it. – Dan Mašek May 11 '21 at 19:20

1 Answers1

1

Thanks to Dan Masek I was able to find the error in my code. I was not correctly converting the data from 16 to 8 bit. Here the updated code with the solution for OpenCV and Pillow.

import numpy as np
from PIL import Image
import cv2
im = Image.fromarray((np.array(Image.open('terzafoto.png'))//256).astype("uint8")).convert('RGB')
im.save('PIL100.jpg', format='JPEG', quality=100)
img = cv2.imread('terzafoto.png', cv2.IMREAD_ANYDEPTH)
cv2.imwrite('100.jpeg', np.uint8(img // 256),[int(cv2.IMWRITE_JPEG_QUALITY), 100])

The image quality factor can be set in function of your needs. 100 means lossless compression.

GBBL
  • 576
  • 1
  • 6
  • 18
  • Quality 100 does not mean lossless, see https://stackoverflow.com/a/48578831/2836621 If you want lossless, consider using a lossless format such as PNG. – Mark Setchell May 14 '21 at 09:18