user15888214
user15888214

Reputation:

Why does cv2.imread output a matrix of zeros for a 32-bit image even when using cv.IMREAD_ANYDEPTH?

I'm using OpenCV version 4.1.1 in Python and cannot get a legitimate reading for a 32-bit image, even when I use cv.IMREAD_ANYDEPTH. Without cv.IMREAD_ANYDEPTH, it returns as None type; with it, I get a matrix of zeros. The issue persists after reinstalling OpenCV. os.path.isfile returns True. The error was replicated on another computer. The images open in ImageJ, so I wouldn't think they're corrupted. I would rather use Skimage since it reads the images just fine, but I have to use OpenCV for what I'm working on. Any advice is appreciated.

img = cv2.imread(file,cv2.IMREAD_ANYDEPTH)

Link for the image: https://drive.google.com/file/d/1IiHbemsmn2gLW12RG3i9fLYZQW2u8sQw/view?usp=sharing

Upvotes: 1

Views: 1836

Answers (1)

Dan Mašek
Dan Mašek

Reputation: 19071

It appears to be some bug in how OpenCV loads such TIFF images. Pillow seems to load the image in a sensible way. Running

from PIL import Image
import numpy as np

img_pil = Image.open('example_image.tiff')
img_pil_cv = np.array(img_pil)
print(img_pil_cv.dtype)
print(img_pil_cv.max())

I get

int32
40950

as an output, which looks reasonable enough.

When I do

import cv2
img_cv = cv2.imread('example_image.tiff', cv2.IMREAD_ANYDEPTH)
print(img_cv.dtype)
print(img_cv.max())

I get

float32
5.73832e-41

which is obviously wrong.

Nevertheless, the byte array holding the pixel data is correct, it's just not being interpreted correctly. You can use numpy.ndarray.view to reinterpret the datatype of a numpy array, so that it's treated as an array if 32bit integers instead.

img_cv = cv2.imread('example_image.tiff', cv2.IMREAD_ANYDEPTH)
img_cv = img_cv.view(np.int32)
print(img_cv.dtype)
print(img_cv.max())

Which prints out

int32
40950

Since the maximum value is small enough for 16bit integer, let's convert the array and see what it looks like

img_cv_16bit = img_cv.astype(np.uint16)
cv2.imwrite('output_cv_16bit.png', img_cv_16bit)

16 bit unscaled

OK, there are some bright spots, and a barely visible pattern. With a little adjustment, we can get something visible:

img_cv_8bit = np.clip(img_cv_16bit // 16, 0, 255).astype(np.uint8)
cv2.imwrite('output_cv_8bit.png', img_cv_8bit)

scaled down by 16 and cast to 8bit

That looks quite reasonable now.

Upvotes: 1

Related Questions