Getting lab colour from image

Hello

I have this code:

import cv2
import numpy as np
bgr = [126, 125, 125]
print(type(bgr))
lab = cv2.cvtColor( np.uint8([[bgr]] ), cv2.COLOR_BGR2LAB)[0][0]
print(lab)  

It seems to work takes the bgr values and prints Lab values

I also have this code:

import cv2
import numpy as np

img = cv2.imread('5.tif',cv2.IMREAD_UNCHANGED)

data = np.reshape(img, (-1,3))
print(data.shape)
data = np.float32(data)

criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 10, 1.0)
flags = cv2.KMEANS_RANDOM_CENTERS
compactness,labels,centers = cv2.kmeans(data,1,None,criteria,10,flags)

print('Dominant color is: bgr({})'.format(centers[0].astype(np.int32)))

print(centers[0])
myval = (format(centers[0].astype(np.int32)))

newlist = myval.replace(' ', ', ')

print(type(myval))
print(type(newlist))
print(type(centers))
li = list(myval.split(","))
print(type(li))
print(li)
lab = cv2.cvtColor( np.uint8([[li]] ), cv2.COLOR_BGR2LAB)[0][0]
print(lab)

which does not work.

It seems to be a mismatch between the values formatted as [126, 125, 125] vs [125 125 125]

But I can’t get the format to feed into the final command. I think it was to do with it being a list, but I convert it to list and I get this values: [‘[126 125 125]’] which is worse. Now I have square brackets and still no commas.

My goal is to read the average/dominent colour of an image patch, and determine the L value.

First off, I don’t think the code above is doing what you think it is doing. You should not be using 8 bit integers when converting from BGR to Lab. The code above returns the values: [134 128 127]. These are not proper Lab values. This is because RGB should have a lightness between the SDR range of 0 - 100 in Lab. You really need to use floating point numbers and scale the values between 0 - 1.

import cv2
import numpy as np
bgr = [126 / 255, 125 / 255, 125 / 255]
print(type(bgr))
lab = cv2.cvtColor(np.float32([[bgr]] ), cv2.COLOR_BGR2LAB)[0][0]
print(lab)

Now we get [52.264404 0.328125 -0.796875]. Let’s double-check this in the very popular Colour Science library. We’ll flip the values as Colour Science takes RGB not BGR.

>>> import colour
>>> colour.XYZ_to_Lab(colour.sRGB_to_XYZ([125/255, 125/255, 126/255]))
array([ 52.43518256,   0.20496802,  -0.54105055])

This is making much more sense now. Values are a little different as I’m not sure what cv2 is using for a white point or what precision it uses for its conversion matrices, but at least the values are now in the Lab ballpark now and seems comparable now. In case it matters, Colour Science in this instance is assuming a D65 white point.

I won’t comment on your main question as I’m still not entirely sure what you are asking. It would be helpful if you were to give a more minimal example illustrating exactly what you have a question with as I feel your entire script, which references a tif file that I do not have access to, is more information than is needed to convey what your real question is.

It would be helpful if you could narrow down a minimal example illustrating just what your real question is.

Thanks for your reply.

My goal is to get the Lab Values for a xrite colour checker (digital sg or 24 patch) and calculate gain modulation on the patches.

To do this I need to know the L value of the patches and then calculate the gain between pairs.

I see the colour package above has chart detection which looks like it could be useful. I don’t see anything to get the Lab values or analysis on the patches but perhaps it’s there in the docs. But my goal right now is to analyse the patch , detection of zones can come afterwards…

My thought was

1 get dominant colour from patch (my tifs are just the grey patches patches cropped (as above i was going to worry about detection and cropping ROI later), available here: WeTransfer - Send Large Files & Share Photos Online - Up to 2GB Free )

get the L value for the patch. do for each patch and then calculate the gain. Right now I open the image in photoshop and sample the patch but this is time consuming and needs photoshop…

I need a way to sample the average colour (dominant colour) from the image and take this and assign it to the bgr variable in your example.

I used this page Converting single value colors to LAB using python OpenCV - Stack Overflow

but could not get it to take the value I found since it needs a triplet separated by commas, but my code I found here questions/50899692/most-dominant-color-in-rgb-image-opencv-numpy-python seems to want the triplet colour values with no comma separation. Then I got confused about lists, arrays, and data types.

so I want to
1.get dominant colour of patch
2. convert to L value

Then I can loop this and hopefully
figure out how to locate and extract ROI for each patch from single image of checker and calculate gain modulation for each pair of adjacent patches.

I have some small experience with python and using it for various minor image tasks but I didn’t think this would be so tricky.

thanks

OK

I figured out and the code does seem to run correctly. Whether it’s doing what I think I want I will check tomorrow. but now:

import numpy as np



img = cv2.imread('5.tif',cv2.IMREAD_UNCHANGED)

data = np.reshape(img, (-1,3))
print(data.shape)
data = np.float32(data)

criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 10, 1.0)
flags = cv2.KMEANS_RANDOM_CENTERS
compactness,labels,centers = cv2.kmeans(data,1,None,criteria,10,flags)

print('Dominant color is: bgr({})'.format(centers[0].astype(np.int32)))
print('Dominant color is: bgr({})'.format(centers[0]))

new_List = np.divide(centers, 255)
print('new_List', new_List)

myval = new_List.tolist()
print('myval', myval)

lab2 = cv2.cvtColor(np.float32([[myval[0]]] ), cv2.COLOR_BGR2LAB)[0][0]
print(lab2)

## 126 125 125
print('hardcoded 126.268326 125.12772 125.221695')
bgr = [126.268326 / 255, 125.12772 / 255, 125.221695 / 255]
print('bgr', bgr)

lab = cv2.cvtColor(np.float32([[bgr]] ), cv2.COLOR_BGR2LAB)[0][0]
print(lab)
## 126 125 125
print('hardcoded int 126 125 125')
bgr = [126 / 255, 125 / 255, 125 / 255]
print('bgr', bgr)

lab = cv2.cvtColor(np.float32([[bgr]] ), cv2.COLOR_BGR2LAB)[0][0]
print(lab)

all the results seem to match. I needed the myval[0] since it seems I had a list in a list.

Gives output

(339200, 3)
Dominant color is: bgr([126 125 125])
Dominant color is: bgr([126.268326 125.12772  125.221695])
new_List [[0.4951699  0.49069697 0.49106547]]
myval [[0.49516990780830383, 0.4906969666481018, 0.49106547236442566]]
[52.44751  0.21875 -0.53125]
hardcoded 126.268326 125.12772 125.221695
bgr [0.49516990588235293, 0.49069694117647056, 0.4910654705882353]
[52.44751  0.21875 -0.53125]
hardcoded int 126 125 125
bgr [0.49411764705882355, 0.49019607843137253, 0.49019607843137253]
[52.264404  0.328125 -0.796875]

Lab values look sane.