I use 16UC1 images in the context of depth cameras that produce a monochrome depth image, where typically an intensity of 1 represents a distance of 1 mm.
This small script generates a correct PNG image but incorrect JPG with the same source.
import cv2
import numpy as np
# Create a 100x100 image with uint16 type.
width = 100
height = 100
image = np.empty((height, width), dtype=np.uint16)
# Define some intensities.
intensities = [65535, 32767, 127, 0]
# Fill the regions with the corresponding values.
n_regions = 2 * (len(intensities) - 1) + 1
for i, intensity in enumerate(intensities):
h_margin = round(i * width / n_regions)
w_margin = round(i * height / n_regions)
image[h_margin:height-h_margin, w_margin:width-w_margin] = intensity
cv2.imwrite('output_image.png', image)
cv2.imwrite('output_image.jpg', image)
The correct image are 4 squares from white (large square, intensity 65535) to black (smallest square, intensity 0).
When I export the PNG to JPEG from Gimp, the result is correct.
Is it a bad conversion to JPEG from 16UC1 image or JPEG compression artifacts?
as far as I know, there’s something called Losless JPEG and that’s the only jpeg that supports 16BIT
If i try it with C, I get a [ WARN:0@0.025] global loadsave.cpp:848 cv::imwrite_ Unsupported depth image for selected encoder is fallbacked to CV_8U. warning when saving it to PNG and no warning when saving it to JPEG. But in the end, both are 8Bit
* With JPEG 2000 encoder, 8-bit unsigned (CV_8U) and 16-bit unsigned (CV_16U) images can be saved.
* With JPEG XL encoder, 8-bit unsigned (CV_8U), 16-bit unsigned (CV_16U) and 32-bit float(CV_32F) images can be saved.
//Edit: I did a mistake with the file format, this happens when using CV_16FC1 instead of CV_16U. Now the correction: With CV_16U, PNG get saved as 16Bit and I get this warning for writing JPG instead of PNG.
//Edit2: I think GIMP is simply norming the values before saving it to a 8Bit JPEG. I don’t have python but what happens if you divide your image by 256 before saving it to JPEG, does it look correct to you?
If I save the JPG image with cv2.imwrite('output_image.jpg', (image / 256).astype(np.uint8)), then the image is correct (even without .astype(np.uint8)).
I did some further tests with Gimp and MATLAB. Matlab can save 16-bit jpegs, its help says
'BitDepth' A scalar value indicating desired bitdepth;
for grayscale images this can be 8, 12, or 16;
for truecolor images this can be 8 or 12. Only
lossless mode is supported for 16-bit images.
But only Matlab can read these 16-Bit JPEGs. Gimp, IrfanView, Windows Paint, Paint.NET and OpenCV failed to read this file.
If you load a 16Bit-Mono-PNG to Gimp and save it as JPEG, your saved jpeg file will be 8Bit.
So if you need a precision of 16Bit, then better stay with PNG, it can keep the information and compared with JPEG you don’t have annoying artefacts. But don’t forget to set the IMREAD_ANYDEPTH parameter for imread, when trying to read them, or they will be 8-Bit again