89744

Numpy 8 and 32 bits image data type after cvtColor() conversion to LAB colorspace

Question:

I am trying to understand how LAB work in OpenCV. Previously open up a post on <a href="https://stackoverflow.com/questions/46052359/numpy-8-16-32-bits-image-data-type-after-cvtcolor-conversion-to-hsv-colorspace" rel="nofollow">HSV</a> I was playing and testing with the datatype after the conversion to LAB

LAB standard range of for the pixel data in Lightness = 0-100, a* = -127 to +127, b* = -127 to +127. But I am confused with the data shown in openCV. Could anyone help me check if I am doing the conversion of datatype and scaling correct?

import cv2 import numpy as np im = cv2.imread(r'G:\Checkerboardfordummies.png') cv2.namedWindow('im', cv2.WINDOW_NORMAL) cv2.imshow('im', im) print(im) print(im.dtype) #Conversion from 8uint to float32 before cvtColor() im = im.astype(np.float32) #Cast Image data type im /= 255. #Scale value to float32 range 0-1 print(im) print(im.dtype) #Colour Space Conversion to LAB im = cv2.cvtColor(im, cv2.COLOR_BGR2LAB) cv2.namedWindow('Float32_Checkerboard', cv2.WINDOW_NORMAL) cv2.imshow('Float32_Checkerboard', im) cv2.imwrite('Float32_Checkerboard.png',im) #Conversion from float32 to uint8 im = im*(255.) #Scale value to uint8 range 0-255 im = im.astype(np.uint8) #Cast Image data type print(im) print(im.dtype) cv2.namedWindow('uint8_Checkerboard', cv2.WINDOW_NORMAL) cv2.imshow('uint8_Checkerboard', im) cv2.imwrite('uint8_Checkerboard.png',im)

Testing image:

<a href="https://i.stack.imgur.com/D5Wbt.png" rel="nofollow"><img alt="enter image description here" class="b-lazy" data-src="https://i.stack.imgur.com/D5Wbt.png" data-original="https://i.stack.imgur.com/D5Wbt.png" src="https://etrip.eimg.top/images/2019/05/07/timg.gif" /></a>

Testing result with conversion

<a href="https://i.stack.imgur.com/0BP0x.jpg" rel="nofollow"><img alt="enter image description here" class="b-lazy" data-src="https://i.stack.imgur.com/0BP0x.jpg" data-original="https://i.stack.imgur.com/0BP0x.jpg" src="https://etrip.eimg.top/images/2019/05/07/timg.gif" /></a>

I am not sure which one is the expected colour to observe in LAB? 8uint or the one with float32? I thought it should display same colour? something might be wrong with my scaling

This is the printed out data for reference: It seem like scaling back to 8uint is wrong looking at the value

>>> [[[ 0 0 0] [ 0 0 0] [ 0 0 0] ..., [255 255 255] [255 255 255] [255 255 255]] [[ 0 0 0] [ 0 0 0] [ 0 0 0] ..., [255 255 255] [255 255 255] [255 255 255]] [[ 0 0 0] [ 0 0 0] [ 0 0 0] ..., [255 255 255] [255 255 255] [255 255 255]] ..., [[255 255 255] [255 255 255] [255 255 255] ..., [ 0 0 0] [ 0 0 0] [ 0 0 0]] [[255 255 255] [255 255 255] [255 255 255] ..., [ 0 0 0] [ 0 0 0] [ 0 0 0]] [[255 255 255] [255 255 255] [255 255 255] ..., [ 0 0 0] [ 0 0 0] [ 0 0 0]]] uint8 [[[ 0. 0. 0.] [ 0. 0. 0.] [ 0. 0. 0.] ..., [ 1. 1. 1.] [ 1. 1. 1.] [ 1. 1. 1.]] [[ 0. 0. 0.] [ 0. 0. 0.] [ 0. 0. 0.] ..., [ 1. 1. 1.] [ 1. 1. 1.] [ 1. 1. 1.]] [[ 0. 0. 0.] [ 0. 0. 0.] [ 0. 0. 0.] ..., [ 1. 1. 1.] [ 1. 1. 1.] [ 1. 1. 1.]] ..., [[ 1. 1. 1.] [ 1. 1. 1.] [ 1. 1. 1.] ..., [ 0. 0. 0.] [ 0. 0. 0.] [ 0. 0. 0.]] [[ 1. 1. 1.] [ 1. 1. 1.] [ 1. 1. 1.] ..., [ 0. 0. 0.] [ 0. 0. 0.] [ 0. 0. 0.]] [[ 1. 1. 1.] [ 1. 1. 1.] [ 1. 1. 1.] ..., [ 0. 0. 0.] [ 0. 0. 0.] [ 0. 0. 0.]]] float32 [[[ 0 0 0] [ 0 0 0] [ 0 0 0] ..., [156 0 0] [156 0 0] [156 0 0]] [[ 0 0 0] [ 0 0 0] [ 0 0 0] ..., [156 0 0] [156 0 0] [156 0 0]] [[ 0 0 0] [ 0 0 0] [ 0 0 0] ..., [156 0 0] [156 0 0] [156 0 0]] ..., [[156 0 0] [156 0 0] [156 0 0] ..., [ 0 0 0] [ 0 0 0] [ 0 0 0]] [[156 0 0] [156 0 0] [156 0 0] ..., [ 0 0 0] [ 0 0 0] [ 0 0 0]] [[156 0 0] [156 0 0] [156 0 0] ..., [ 0 0 0] [ 0 0 0] [ 0 0 0]]] uint8 >>>

<strong>EDIT</strong>: After discussion with Danmasek...managed to display the images correctly in both 8bits and 32bits

import cv2 import numpy as np im = cv2.imread(r'G:\Checkerboardfordummies.png') cv2.namedWindow('im', cv2.WINDOW_NORMAL) cv2.imshow('im', im) print(im) print(im.dtype) #Conversion from 8uint to float32 before cvtColor() im = im.astype(np.float32) #Cast Image data type im /= 255 #Scale value to float32 range 0-1 print(im) print(im.dtype) #Colour Space Conversion to HSV im = cv2.cvtColor(im, cv2.COLOR_BGR2LAB) cv2.namedWindow('Float32_Checkerboard', cv2.WINDOW_NORMAL) cv2.imshow('Float32_Checkerboard', im) cv2.imwrite('Float32_Checkerboard.png',im) #Conversion from float32 to uint8 im[:,:,0] = im[:,:,0]*(255./100)#Scale value to uint8 range 0-255 im[:,:,1] = (im[:,:,1] + 128)/255 im[:,:,2] = (im[:,:,2] + 128)/255 im = im.astype(np.uint8) #Cast Image data type print(im) print(im.dtype) cv2.namedWindow('uint8_Checkerboard', cv2.WINDOW_NORMAL) cv2.imshow('uint8_Checkerboard', im) cv2.imwrite('uint8_Checkerboard.png',im) cv2.imwrite('uint8_Checkerboard.png',im)

Got this result :

<a href="https://i.stack.imgur.com/10S50.jpg" rel="nofollow"><img alt="enter image description here" class="b-lazy" data-src="https://i.stack.imgur.com/10S50.jpg" data-original="https://i.stack.imgur.com/10S50.jpg" src="https://etrip.eimg.top/images/2019/05/07/timg.gif" /></a>

Printed data: the conversion from 32 to 8 bits....its still printing 255 on the L channel after the scaling. Very weird

>>> [[[ 0 0 0] [ 0 0 0] [ 0 0 0] ..., [255 255 255] [255 255 255] [255 255 255]] [[ 0 0 0] [ 0 0 0] [ 0 0 0] ..., [255 255 255] [255 255 255] [255 255 255]] [[ 0 0 0] [ 0 0 0] [ 0 0 0] ..., [255 255 255] [255 255 255] [255 255 255]] ..., [[255 255 255] [255 255 255] [255 255 255] ..., [ 0 0 0] [ 0 0 0] [ 0 0 0]] [[255 255 255] [255 255 255] [255 255 255] ..., [ 0 0 0] [ 0 0 0] [ 0 0 0]] [[255 255 255] [255 255 255] [255 255 255] ..., [ 0 0 0] [ 0 0 0] [ 0 0 0]]] uint8 [[[ 0. 0. 0.] [ 0. 0. 0.] [ 0. 0. 0.] ..., [ 1. 1. 1.] [ 1. 1. 1.] [ 1. 1. 1.]] [[ 0. 0. 0.] [ 0. 0. 0.] [ 0. 0. 0.] ..., [ 1. 1. 1.] [ 1. 1. 1.] [ 1. 1. 1.]] [[ 0. 0. 0.] [ 0. 0. 0.] [ 0. 0. 0.] ..., [ 1. 1. 1.] [ 1. 1. 1.] [ 1. 1. 1.]] ..., [[ 1. 1. 1.] [ 1. 1. 1.] [ 1. 1. 1.] ..., [ 0. 0. 0.] [ 0. 0. 0.] [ 0. 0. 0.]] [[ 1. 1. 1.] [ 1. 1. 1.] [ 1. 1. 1.] ..., [ 0. 0. 0.] [ 0. 0. 0.] [ 0. 0. 0.]] [[ 1. 1. 1.] [ 1. 1. 1.] [ 1. 1. 1.] ..., [ 0. 0. 0.] [ 0. 0. 0.] [ 0. 0. 0.]]] float32 [[[ 0 0 0] [ 0 0 0] [ 0 0 0] ..., [255 0 0] [255 0 0] [255 0 0]] [[ 0 0 0] [ 0 0 0] [ 0 0 0] ..., [255 0 0] [255 0 0] [255 0 0]] [[ 0 0 0] [ 0 0 0] [ 0 0 0] ..., [255 0 0] [255 0 0] [255 0 0]] ..., [[255 0 0] [255 0 0] [255 0 0] ..., [ 0 0 0] [ 0 0 0] [ 0 0 0]] [[255 0 0] [255 0 0] [255 0 0] ..., [ 0 0 0] [ 0 0 0] [ 0 0 0]] [[255 0 0] [255 0 0] [255 0 0] ..., [ 0 0 0] [ 0 0 0] [ 0 0 0]]] uint8 >>>

Answer1:

You only have 2 distinct colours, so let's simplify it and only use an image with 2 pixels, and eliminate the tens of thousands of redundant pixels that only make the behaviour more difficult to observe.

As such, we can simplify the whole thing to this onliner:

cv2.cvtColor(np.array([[[0,0,0],[1,1,1]]], dtype=np.float32), cv2.COLOR_BGR2LAB)

which returns

array([[[ 0., 0., 0.], [ 100., 0., 0.]]], dtype=float32)

Well, look at the second pixel.

At this point, let's consult <a href="http://docs.opencv.org/3.3.0/de/d25/imgproc_color_conversions.html" rel="nofollow">the documentation</a> for this kind of colour conversion.

<blockquote>

This outputs 0≤L≤100, −127≤a≤127, −127≤b≤127 . The values are then converted to the destination data type:

<ul><li>8-bit images: L←L∗255/100,a←a+128,b←b+128</li> <li>16-bit images: (currently not supported)</li> <li>32-bit images: L, a, and b are left as is</li> </ul></blockquote>

We've got a 32 bit image, so the values are "left as is" (i.e in the ranges 0≤L≤100, −127≤a≤127, −127≤b≤127)

Hence, multiplying the result by 255 and casting it to an 8 bit unsigned integer will leave you with nonsense due to overflow.

<hr /><blockquote>

But how should i scale and convert back from float32 to 8bits in the LAB colorspace without experiencing overflow.

</blockquote>

Apply some elementary math (hinted at by the quoted documentation): L←L∗255/100,a←a+128,b←b+128

float_img = cv2.cvtColor(np.array([[[0,0,0],[1,1,1]]], dtype=np.float32), cv2.COLOR_BGR2LAB) # Channel L float_img[:,:,0] = float_img[:,:,0] * (255.0/100.0) # Channels a and b float_img[:,:,1:] = float_img[:,:,1:] + 128 result = np.uint8(float_img)

The result being

array([[[ 0, 128, 128], [255, 128, 128]]], dtype=uint8)

For comparison:

>>> cv2.cvtColor(np.array([[[0,0,0],[255,255,255]]], dtype=np.uint8), cv2.COLOR_BGR2LAB) array([[[ 0, 128, 128], [255, 128, 128]]], dtype=uint8)

Recommend

  • Lua: Working with Bit32 Library to Change States of I/O's
  • Otsu's Thresholding Implementation not working properly
  • How to convert a subset of numpy recarray to continuous array?
  • Theano version of a numpy einsum for two 3dim matrices
  • Tensorflow neural network prediction is always the same
  • What is the effect of storing a larger value in a grayscale png image?
  • group values contaning np.nan in intervals
  • create a matrix from array of elements under diagonal in numpy
  • constrain a series or array to a range of values
  • Apply a function pairwise on a pandas series
  • finding values in pandas series - Python3
  • numpy array with mpz/mpfr values
  • Different outcomes when using tf.Variable() and tf.get_variable()
  • Matlab to Python Conversion binary file read
  • Are FOR loops with char iterators in C possible?
  • TypeError: Value passed to parameter 'input' has DataType float64 not in list of allowed v
  • Cythonized function unexpectedly slow
  • Error in GGally: Error in unit(tic_pos.c, “mm”) : 'x' and 'units' must have leng
  • Numpy odd behaviour conversion to datetime64 dtype
  • RavenDB indexing errors
  • Multiple Left Join LINQ-to-entities
  • Put value at centre of bins for histogram
  • ggplot2: make the points on the line a darker color than the line color
  • Suqueries in select clause with JPA
  • Default parameter as generic type
  • Django model inheritance, filtering models
  • Make new pandas columns based on pipe-delimited column with possible repeats
  • D3 get axis values on zoom event
  • How can I enlarge video fullscreen without the affected interface project in as3?
  • Python urlparse: small issue
  • FileReader+canvas image loading problem
  • Splitting given String into two variables - php
  • Fill an image in a square container while keeping aspect ratio
  • 0x202A in filename: Why?
  • How can I get HTML syntax highlighting in my editor for CakePHP?
  • How do I configure my settings file to work with unit tests?
  • How does Linux kernel interrupt the application?
  • IndexOutOfRangeException on multidimensional array despite using GetLength check
  • python draw pie shapes with colour filled
  • Binding checkboxes to object values in AngularJs