5

I know the opencv got a BGR order, but in my experiment, not only the order but also the values are totally messed

import cv2 as cv
import tifffile as tiff
import skimage.io

img_path = r"C:\test\pics\t100r50s16_1_19.tif"
c = cv.imread(img_path,cv.IMREAD_UNCHANGED)
t = tiff.imread(img_path)
s = skimage.io.imread(img_path)

print("c:", c.shape, "t:", t.shape, "s:", s.shape)
print("c:", c.dtype, "t:", t.dtype, "s:", s.dtype)

print(c[0, 0], c[1023, 0], c[0, 1023], c[1023, 1023])
print(t[0, 0], t[1023, 0], t[0, 1023], t[1023, 1023])
print(s[0, 0], s[1023, 0], s[0, 1023], s[1023, 1023])

print(c.sum())
print(t.sum())
print(s.sum())

And the outputs like this:

c: (1024, 1024, 4) t: (1024, 1024, 4) s: (1024, 1024, 4)
c: uint8 t: uint8 s: uint8
[ 50  63  56 182] [131 137 140 193] [29 28 27 94] [123 130 134 190]
[ 79  88  70 182] [185 181 173 193] [74 77 80 94] [180 174 165 190]
[ 79  88  70 182] [185 181 173 193] [74 77 80 94] [180 174 165 190]
# Here seems that opencv only read the alpha channel right, 
# the values of first three channels are much different than other package

539623146
659997127
659997127

The image i use can be download here. So, here is my question, how open cv handle 4 channel tiff file? Because when i test on 3-channel image, everything looks alright.

Ziyi He
  • 51
  • 3
  • 1
    The order is "BGR", not "GBR". – lightalchemist Sep 26 '18 at 02:30
  • 2
    This is apparently a known issue, at least for JPEG. It is probably due to the different decoder these two libraries use to decode the image. See [this link](https://github.com/scikit-image/scikit-image/issues/2293) for a discussion. – lightalchemist Sep 26 '18 at 02:41
  • Thank you for pointing my mistake and providing reference @lightalchemist – Ziyi He Sep 26 '18 at 02:47
  • And actually i have looked into the link you provide yesterday, in that discussion, the differences seems caused by "round" issue(probably caused by dtype), because the value diff in minor(like 49 and 50, 101 and 100). But in my situation the gap is so big(130 and 174, 28 and 77 in G channel). And this only happens in 4-channel image, when i changed to 3-channel everything works fine. So that's why i open a new question here@lightalchemist – Ziyi He Sep 26 '18 at 02:55
  • Can you supply a 3-channel TIFF that works correctly too please - I think I have something... – Mark Setchell Sep 26 '18 at 07:09

1 Answers1

6

I don't buy it for a minute that there is a rounding error or some error related to JPEG decoding like the linked article suggests.

Firstly because your image is integer, specifically uint8 so there is no rounding of floats, and secondly because the compression of your TIF image is not JPEG - in fact there is no compression. You can see that for yourself if you use ImageMagick and do:

identify -verbose a.tif

or if you use tiffinfo that ships with libtiff, like this:

tiffinfo -v a.tif

So, I did some experiments by generating sample images with ImageMagick like this:

# Make 8x8 pixel TIF full of RGBA(64,128,192) with full opacity
convert -depth 8 -size 8x8 xc:"rgba(64,128,192,1)" a.tif

# Make 8x8 pixel TIFF with 4 rows per strip
convert -depth 8 -define tiff:rows-per-strip=4 -size 8x8 xc:"rgba(64,128,192,1)" a.tif

And OpenCV was able to read all those correctly, however, when I did the following it went wrong.

# Make 8x8 pixel TIFF with RGB(64,128,192) with 50% opacity
convert -depth 8 -define tiff:rows-per-strip=1 -size 8x8 xc:"rgba(64,128,192,0.5)" a.tif

And the values came out in OpenCV as 32, 64, 96 - yes, exactly HALF the correct values - like OpenCV is pre-multiplying the alpha. So I tried with an opacity of 25% and the values came out at 1/4 of the correct ones. So, I suspect there is a bug in OpenCV that premultiplies the alpha.

If you look at your values, you will see that tifffile and skimage read the first pixel as:

[ 79  88  70 182 ]

if you look at the alpha of that pixel, it is 0.713725 (182/255), and if you multiply each of those values by that, you will get:

[ 50  63  56 182 ]

which is exactly what OpenCV did.

As a workaround, I guess you could divide by the alpha to scale correctly.


In case the argument is that OpenCV intentionally pre-multiplies the alpha, then that begs the question why it does that for TIFF files but NOT for PNG files:

# Create 8x8 PNG image full of rgb(64,128,192) with alpha=0.5
convert -depth 8 size 8x8 xc:"rgba(64,128,192,0.5)" a.png

Check with OpenCV:

import cv2
c = cv2.imread('a.png',cv2.IMREAD_UNCHANGED)

In [4]: c.shape
Out[4]: (8, 8, 4)

In [5]: c
Out[5]: 
array([[[192, 128,  64, 128],
        [192, 128,  64, 128],
...
...

In case anyone thinks that the values in the TIF file are as OpenCV reports them, I can only say that I wrote rgb(64,128,192) at 50% opacity and I tested each of the following and found that they all agree, with the sole exception of OpenCV that that is exactly what the file contains:

  • ImageMagick v7
  • libvips v8
  • Adobe Photoshop CC 2017
  • PIL/Pillow v5.2.0
  • GIMP v2.8
  • scikit-image v0.14
Mark Setchell
  • 191,897
  • 31
  • 273
  • 432
  • I have no idea how to raise an issue with the OpenCV developers, so if anyone is familiar with the process and feels like raising it, please do, maybe adding an explanatory link to this answer - which can be obtained by clicking the `share` button and copying the URL. – Mark Setchell Sep 26 '18 at 10:07
  • @MarkSetchell I apologize for being too hasty in adding my comment. If what you observed is true, this behavior is not necessarily a bug per se. Pre-multiplying by alpha is a standard procedure to facilitate [image compositing](https://en.wikipedia.org/wiki/Alpha_compositing) so OpenCV might be doing this by default but skimage and the other libraries are not. The [docs for imread](https://docs.opencv.org/3.1.0/d4/da8/group__imgcodecs.html#ga288b8b3da0892bd651fce07b3bbd3a56) is not clear that this is performed though. – lightalchemist Sep 27 '18 at 03:31
  • 1
    @lightalchemist I have no issues whatsoever with your thoughts - it's a free world! Say as you please - I do! I did check what happens with a semi-transparent PNG file and found that OpenCV does NOT pre-multiply that format (I have updated my answer above) - so my feeling is either that the TIFF pre-multiplying behaviour is a bug, or OpenCV is inconsistent. – Mark Setchell Sep 27 '18 at 13:11
  • @MarkSetchell I added a "answer" (actually more of a speculation) as to why OpenCV behaved the way it did in this situation. I wrongly implied that pre-multiplied alpha is the way image formats that handle alpha store the data. Actually what I was trying to suggest is why premultiplying with alpha should not come across as surprising given that we do that in graphics work. It seems that it is not OpenCV that is doing the premultiplying but that it is *not* undoing it because the `cv2.IMREAD_UNCHANGED` flag was passed. Running out of chars but I explained why PNG was loaded properly – lightalchemist Sep 27 '18 at 15:28