stackoverflowuser2010
stackoverflowuser2010

Reputation: 40909

Problems converting image data to unsigned int types for use with OpenCV

I have an 8-bit .gif image that I'm able to read into a Numpy array of dtype uint8. However, when I try to convert the dtype to a larger type of integer, such as uint32 and uint64, for use in OpenCV, I get weird errors.

I'm using Google Colab and am running Python 3.6.

I downloaded this 8-bit color gif image:

!wget https://www.cosy.sbg.ac.at/~pmeerw/Watermarking/lena_color.gif

And here's my code below where I try to convert the image to each of [ np.uint8, np.uint16, np.uint32, np.uint64 ] and then draw a rectangle.

import numpy as np
import cv2
import matplotlib

print(np.__version__)          # 1.19.5
print(cv2.__version__)         # 4.1.2
print(matplotlib.__version__)  # 3.2.2

import matplotlib.pyplot as plt
import matplotlib.image as mpimg


img_orig = mpimg.imread('lena_color.gif')
print('img_orig dtype: %s' % img_orig.dtype) # img_orig dtype: uint8

# Coordinates for rectangle to be drawn.
TL = (100, 100)
BR = (400, 400)

# Convert to different integer dtypes.
dtypes = [ np.uint8, np.uint16, np.uint32, np.uint64 ]

for dtype in dtypes:

    print('Changing type to %s' % (dtype.__name__))

    img = img_orig.astype(dtype)
    try:
        cv2.rectangle(img, TL, BR, (0, 0, 255), 2)
        _ = plt.imshow(img)
        _ = plt.show()
    except Exception as ex:
        print(ex)

I get the following results:

Changing type to uint8

enter image description here

Changing type to uint16

enter image description here

Changing type to uint32
an integer is required (got type tuple)
Changing type to uint64

enter image description here

Specific questions:

  1. Why do I get an exception when I change the dtype to uint32 ?

  2. I drew the rectangle with blue (0, 0, 255). Why is the rectangle coming out as kind of white-purple?

  3. Why does the rectangle not appear when the dtype is uint64 ?

Upvotes: 0

Views: 3853

Answers (2)

stateMachine
stateMachine

Reputation: 5805

There are a couple of things that are happening here. First, you are using OpenCV and matplotlib. These are different imaging libraries that are not 100% compatible among themselves. OpenCV uses BGR color channel ordering while matplotlib uses RGB. If you process the image using a function from OpenCV (such as cv2.rectangle) expect to operate on reversed channels, if you loaded or are showing the image with matplotlib.

Now, for your first question. Image types are normally encoded using 8 unsigned bits per channel. That is 8 x 3 bits for a BGR (or RGB) image, although you can also have 8 x 1 bits for greyscale images, or 8 x 4 bits for a BGRA (the last channel being the Alpha channel - the transparency channel. Every type outside of those cases could be unsupported for a set of functions. In the case of OpenCV, cv2.rectangle does not support arrays of the type uint32 nor uint64. In fact, I get an exception using BOTH formats.

Now, the rectangle. You draw the rectangle using OpenCV and show it using matplotlib, so expect the color you set on the rectangle function to not match what you see on the screen. This image file, in particular, is a .gif with an extra transparency channel, making it a RGBA (4 channels) image. You are setting the color with three parameters, however, cv2.rectangle only appears to be affecting the alpha channel, setting it to 255. The solution is to convert the RGBA image to an RGB image using cv2.cvtColor:

# Include this line just before drawing the rectangle:
# Conversion from RGBA to RGB:
img = cv2.cvtColor(img, cv2.COLOR_RGBA2RGB)

So you will see the correct result on screen:

enter image description here

Advices:

  1. If you are going to use multiple imaging libraries be aware of their differences and possible compatibility issues

  2. Operate and show BGR or RGB images only, other types are usually mid-operation temporal results

  3. If processing .gifs, check out how many channels they are using. Images (at least as OpenCV requires them) are represented as numpy arrays. You can check the dimensions of a numpy array using shape

Like this:

(height, width, channels) = img.shape
print((height, width, channels))

For the Lena image, this returns:

(512, 512, 4)

Upvotes: 1

Tim Roberts
Tim Roberts

Reputation: 54698

Your image has an alpha channel. That means each pixel has 4 components (R,G,B,A), not just three. You should print the image arrays to see what they contain. When you specify (0,0,255), it defaults to 0 for the alpha. That means you're seeing the background show through. The rectangle will be whatever color the background window was at that point. If you specify (0,0,255,255), you will see the blue you expect.

Also note that it really serves no purpose to change the datatype of the numpy arrays. The components are taken as values from 0 to 255, regardless of the datatype of the components.

Upvotes: 1

Related Questions