how to threshold LAB image - python

I am doing a project in python for hand gesture recognition. So the usage of LAB color space will help to improve the accuracy of recognition because as we know that our skin color mainly comprises a ratio of red and yellow color and in case of Lαβ color space, the α component represents the pixel components position between red and green while the β component represents between yellow and blue making it less vulnerable to noise.
But the problem is that, when i tried to convert the Lab image into binary using threshold function provided in opencv it returned some errors, because the input of threshold function should be a gray scale image. Anybody know how to solve this problem?
lab = cv2.cvtColor(img,cv2.COLOR_BGR2LAB)
blur = cv2.GaussianBlur(gray,(5,5),0)
ret,thresh1 = cv2.threshold(blur,70,255,cv2.THRESH_BINARY_INV+cv2.THRESH_OTSU)
The error returned is Assertion Failed.
Anybody know how to threshold an LAB image?

The simplest thresholding methods replace each pixel in an image with a black pixel if the image intensity is less than some fixed constant T, or a white pixel if the image intensity is greater than that constant. Hence, for doing thresholding it is recommended to use gray scale images.
In opencv, cv2.threshold takes two arguments, First argument is the
source image, which should be a grayscale image. Second argument is the threshold value which is used to classify
the pixel values.
But in Wikipedia, there is a reference that we can threshold color images by designating a separate threshold for each of the RGB components of the image and then combine them with an AND operation.

Opencv threshold Documentation:
input array (single-channel, 8-bit or 32-bit floating point).
You can't threshold a color image. And where did grey come from? You never use the lab converted image.

Input image should be a single channel 8-bit or 32-bit float like M4rtini said. However, an RGB, Lab, HSV are all images build up from 3 8-bit channels. If you split the channels
L, a, b = cv2.split(lab)
the result will be 3 single channel images. These you can input into the function
ret,thresh_L = cv2.threshold(L,70,255,cv2.THRESH_BINARY_INV+cv2.THRESH_OTSU)
ret,thresh_a = cv2.threshold(a,70,255,cv2.THRESH_BINARY_INV+cv2.THRESH_OTSU)
ret,thresh_b = cv2.threshold(b,70,255,cv2.THRESH_BINARY_INV+cv2.THRESH_OTSU)
basically, you can input any 2d numpy array into the threshold function as long as its 8-bit or 32-bit float. OpenCv scales Lab colorspace to 0-256.

Related

Increasing Contrast | Multiplying an 8 bit image to increase image intensities | 8bit to 16 bit?

I am working on an Image Processing Project. I know that the grayscale intensities for an image I have processed in Python has values greater than 256 color intensities. However, it seems like I am not able to view the intensities greater than the 8-bit color depth(256) that is a setting on my laptop display. Is this the case that my laptop's 8-bit color depth is the limitation here?
As you can see in the image attached, the LHS image is the original image(intensity values between 0-255), RHS image has its intensity values multiplied by 2(goal being to increase contrast).

Get pixel location of binary image with intensity 255 in python opencv

I want to get the pixel coordinates of the blue dots in an image.
To get it, I first converted it to gray scale and use threshold function.
import numpy as np
import cv2
img = cv2.imread("dot.jpg")
img_g = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
ret1,th1 = cv2.threshold(img_g,127,255,cv2.THRESH_BINARY_INV)
What to do next if I want to get the pixel location with intensity 255? Please tell if there is some simpler method to do the same.
I don't think this is going to work as you would expect.
Usually, in order to get a stable tracking over a shape with a specific color, you do that in RGB/HSV/HSL plane, you could start with HSV which is more robust in terms of lighting.
1-Convert to HSV using cv2.cvtColor()
2-Use cv2.inRagne(blue_lower, blue_upper) to "filter" all un-wanted colors.
Now you have a good-looking binary image with only blue color in it (assuming you have a static background or more filters should be added).
3-Now if you want to detect dots (which is usually more than one pixel) you could try cv2.findContours
4- You can get x,y pixel of contours using many methods(depends on the shape of what you want to detect) like this cv2.boundingRect()

Creating ROI mask by using drawContours in OpenCV

I am using drawContours to make a mask for extracting ROI.
I have already defined four points and a zero mask for drawing the contour.
The output mask of the drawContours function is sort of a trapezoid shape which is what I want.
However, when I use this mask to do bitwise_and with the image,
the result isn't really the same shape with the mask.
The edge of the shape is obviously jagged.
Here is my python code snippet:
hull2 = cv2.convexHull(crop1)
mask10 = np.zeros(image.shape[:2], dtype = "uint8")
print(len(hull2))
cv2.drawContours(mask10, [hull2], -1,255, -1,cv2.LINE_AA)
cv2.imshow("mask10",mask10)
cv2.waitKey(0)
crop = cv2.bitwise_and(image, image, mask=mask10)
cv2.imshow("crop",crop)
cv2.waitKey(0)
cv2.drawContours(image, [hull2], -1, (0, 255, 0), -1,cv2.LINE_AA)
cv2.imshow("mask+img",image)
cv2.waitKey(0)
And here is a picture showing the result: "crop" is the ROI result image
Thanks for anyone trying to help.
The reason you are getting jagged edges while your mask looks like it has smooth edges is because you are using the anti-aliasing flag on drawContours( - ,cv2.LINE_AA) which fills in the surrounding of the jagged edges with darker pixels creating a gradient that fools your eye into thinking its a smooth edge.
Why does this matter? when you use bitwise_and with a mask, any value in the mask greater than 0 is evaluated as "True" and the corresponding pixel in the image will be selected.
So those extra AA pixels despite being a smaller gray value than 255, are expanding the edge of the mask, creating your jagged edge in crop. To emulate this, do mask10[mask10 > 0] = 255; cv2.imshow('mask10', mask10) and it should have the same shape as crop.
Now as a possible solution to your problem, you could use alpha blending to use the gradient (darkened intensity) of those extra AA pixels to darken the crop image edge pixels.
mask_float = cv2.cvtColor(mask10, cv2.COLOR_GRAY2BGR).astype('float32') / 255
image_float = image.astype('float32')
crop = cv2.multiply(image_float, mask_float).astype('uint8')
First we convert mask10 to a 3 channel array so that we can apply the alpha blending to all 3 BGR channels of the image.
Then we normalize the mask to a [0-1] range as we will need to multiply the values in the next step and dtype uint8 doesnt allow greater than 255. So first converting to float32 then dividing by 255. (we could potentially use cv2.normalize() but numpy should be alot faster)
we then convert the image to float32 to allow for multiplication with the mask.
then we multiply the image with the mask to get an alpha blended image of foreground to a black background and convert it back to uint8 for opencv.
Now since the BGR values are converted from float32 to uint8, it will discard the decimal values which will cause a negligible change in color. Also, I'm not 100% sure but there might be a small change in color due to multiplying each channel individually by the same value (eg: 20%) or it could be fine and im just overthinking it? But that only applies to those darkened AA pixels, the effect should also be negligible and we are already modifying it from the original anyways so it should be fine!
As an alternative, you could also convert the image to HLS and multiply the mask to the L-channel only. I believe that should be more true to the image's colors on those edges if that is very important, and the slower speed is permissible

Increase PNG masked area on a JPG with Python Pillow (or PIL)

I'm trying to mask a jpg image using a png black/transparent mask, but due to aliasing and border blur, i always have in output a contour line of the original jpg.
Since graphical precision is not required by the task, this could be easily solved by increasing the masked area by a few pixels.
So for example if the masked area allows a centered circle of 100px, simply "extending" the circle by some pixel, would solve the problem.
Is there a way to achieve this with Pillow ?
I found a solution; i write it down so that others may benefit if needed:
1) apply a gaussian blur to the mask. this will "expand" the borders with a shade
1b) convert in black/white colors only if needed
2) apply a transformation that converts each pixel in black or white based on a threshold. no other colors allowed
so something similar:
blackThreshold = 128.0
img = img.filter(ImageFilter.GaussianBlur(radius=3))
r,g,b,a = img.split() # supposing to have a RGBA PNG
gray = Image.merge('L',(a,)) #
gray = gray.point(lambda x: 0 if x<blackThreshold else 255)

What is intensity conversion in image processing?

I want to select the green channel of an image and perform intensity conversion. I have selected the green channel of image. I would like to know how to do intensity conversion. I am currently working in python.
By selecting the green channel, you're technically already doing an intensity conversion. This is represented as a grayscale image which denotes how much green is experienced at each pixel in the image.
However, #MarkSetchell is correct where the canonical approach to convert from colour images to intensity is a weighted combination of each colour. Some people average all of them, other people exaggerate on the green channel more because we perceive that colour more clearly, but the SMPTE Rec. 709 standard is amongst the most popular: Y' = 0.299 R' + 0.587 G' + 0.114 B'.
Take a look at these informative links for more details on the conversion:
https://en.wikipedia.org/wiki/Luma_(video)
http://www.johndcook.com/blog/2009/08/24/algorithms-convert-color-grayscale/
https://en.wikipedia.org/wiki/Grayscale
However, since you are using OpenCV, you can simply call cv2.cvtColor with the correct flag to convert an image from colour to grayscale:
import numpy as np
import cv2
im = cv2.imread('...') # Place filename here
im = cv2.cvtColor(im, cv2.COLOR_BGR2GRAY)
Alternatively, you can specify 0 as the extra flag to cv2.imread to automatically convert any image into grayscale without having the need to call cv2.cvtColor:
im = cv2.imread('...', 0)
You need to be more precise. The "green channel" probably means you have green luma, a correlate of green intensity. They are related via a "transfer function", e.g. as defined as a part of sRGB:
https://en.wikipedia.org/wiki/SRGB
This will allow you to flip between luminous intensity of green and luma of green.
Equally likely, you are interested in luminance (CIE Y) or luma. Google for "Gamma FAQ" if that is the case.

Categories

Resources