OpenCV color identification - python

Hi I'm trying to built simple color identifying program. I have taken a image (yellow & pink) with and convert it in HSV color space. Then used threshold to identify yellow color region. I getting the output (black image). I want yellow region to be filled with while color and rest with black.
IplImage *imgRead= cvLoadImage("yellow.jpeg",CV_LOAD_IMAGE_COLOR);
if(!imgRead) {
fprintf(stderr, "Error in reading image\n");
exit(1);
}
IplImage *imgHsv = cvCreateImage(cvGetSize(imgRead),8, 3);
cvCvtColor(imgRead, imgHsv,CV_BGR2HSV);
IplImage *imgThreshold = cvCreateImage(cvGetSize(imgRead),8, 1);
cvInRangeS(imgHsv, cvScalar(25, 80, 80,80), cvScalar(34, 255, 255,255), imgThreshold);
cvShowImage("image",imgThreshold);
cvWaitKey(0);
In above code I had calculated HSV value for yellow as 30. (In gimp hsv value for yellow color is 60). In cvInRangeS, except for hue value I'm not sure how to specify other values for cvScalar.
What values I need to put? Am I missing anything?

I think the problem you are having is due to the scaling of the HSV data to fit in 8-bits. Normally, as I'm sure you noticed from using GIMP that HSV scales are as follows:
H -> [0, 360]
S -> [0, 100]
V -> [0, 100]
But, OpenCV remaps these values as follows:
(H / 2) -> [0, 180] (so that the H values can be stored in 8-bits)
S -> [0, 255]
V -> [0, 255]
This is why your calculated Hue value is 30 instead of 60. So, to filter out all colors except for yellow your cvInRangeS call would look something like this:
cvInRangeS(imgHsv, cvScalar(25, 245, 245, 0), cvScalar(35, 255, 255, 255), imgThreshold);
The fourth channel is unused for HSV. This call would give you 10-counts of noise in your color detector threshold for each dimension.
As mentioned by, SSteve your threshold should work, but you may need to expand your threshold boundaries to capture the yellow-ish color in your image.
Hope that helps!

I ran your code and it worked fine. Perhaps the yellow in your image isn't as yellow as you think.
Edit: The other potential difference is that I'm using OpenCV 2.3. Which version are you using?
Ok, one more edit: Have you tried looking at your yellow values? That would give you a definitive answer as to what values you should use in cvInRangeS. Add these two lines after the call to cvCvtColor:
uchar* ptr = (uchar*)(imgHsv->imageData);
printf("H: %d, S:%d, V:%d\n", ptr[0], ptr[1], ptr[2]);
For my image, I got:
H: 30, S:109, V:255
That's why your code worked for me.

Related

Extract plot lines from chart

I am trying to extract some plot lines from the image below. As you can see one line is quite thin. My idea was to remove the noise around the lines (grid lines and text).
So far I came up with this code to remove the grid:
import numpy as np
import cv2
gray = cv2.imread('test.png')
edges = cv2.Canny(gray,50,150,apertureSize = 3)
lines = cv2.HoughLinesP(image=edges,rho=0.01,theta=np.pi/90, threshold=100,lines=np.array([]), minLineLength=100,maxLineGap=80)
a,b,c = lines.shape
for i in range(a):
cv2.line(gray, (lines[i][0][0], lines[i][0][1]), (lines[i][0][2], lines[i][0][3]), (255, 255, 255), 3, cv2.LINE_AA)
cv2.imwrite('result.png',gray)
After that i get the following result:
As you can see, there is still some noise around the plots and they are a bit cut off (doesn't have to be perfectly). Has anyone a better solution or some tips how i can improve this? Maybe remove the words first? Maybe detect the lines directly instead of removing the grid etc.?
You can segment both plots using the HSV color space and looking for the blue and orange color. This results on a pretty clean binary mask. Let's check out the code:
# Imports:
import numpy as np
import cv2
# Set image path
path = "D://opencvImages//"
fileName = "graphs.png"
# Reading an image in default mode:
inputImage = readImage(path + fileName)
# BGR to HSV:
hsvImage = cv2.cvtColor(inputImage, cv2.COLOR_BGR2HSV)
The first portion of the script converts the BGR image to the HSV color space. Next you need the color values to apply a simple binary thresholding. The tricky part is to get correct HSV values. For the blue graph, a proper Hue threshold seems to be from [85, 179] while leaving the rest of the channels opened, from [0, 255]. For the orange color, a possible Hue range could be [11, 30]. I create a list with these two thresholds:
# Array with HSV values:
hsvValues = []
# Blue range:
lowThreshold = [85, 0, 0]
highThreshold = [179, 255, 255]
# Into the list:
hsvValues.append((lowThreshold, highThreshold))
# Orange range:
lowThreshold = [11, 0, 0]
highThreshold = [30, 255, 255]
# Into the list:
hsvValues.append((lowThreshold, highThreshold))
Now, let's create the mask. Just iterate over the HSV list and apply the thresholding using the cv2.inRange function:
# Create mask:
for i in range(len(hsvValues)):
# Get current thresholds:
currentLowThres = np.array(hsvValues[i][0])
currentHighThres = np.array(hsvValues[i][2])
# Apply HSV threshold:
hsvMask = cv2.inRange(hsvImage, currentLowThres, currentHighThres)
cv2.imshow("Hsv Mask", hsvMask)
cv2.waitKey(0)
These are the two masks:
Blue plot:
Orange plot:
Now, do you want to create actual line models using this information? That's another problem. I'd be cautious to use Hough's line transform/detection. Although the masks are pretty clean, Hough's line parameter tuning is notoriously capricious (i.e., difficult and non-scalable) if you attempt to run not-so similar images through the algorithm, Additionally, I guess you could be more interested in multiple lines segments per plot instead of one continuous line, so I'd be on the lookout for a more ad-hoc approach.

PIL - Change multiple pixels values in a "mode P" image

I would like to know how to change more than one pixel value in a mode P image with PIL.
In my case I have 3 pixels values: 0, 1 , 2. I would map them respectively to 255, 76, 124.
I tried:
Image.open(my_image).point(lambda p: 255 if p==0 else (76 if p==1 else 124))
When I run the code above, I get an image with all black pixels. Why? Sholud I use a different function rather than point()?
Update:
.getpalette() returns {0, 255}
If all pixels in your image with the same value mapping to the same output value sounds fine, then Image.point() is definitely the right way to go.
Now why you're getting a black image depends on the color values defined in the palette. You can check the image's palette by calling Image.getpalette(). If your palette defines only 3 color values and anything beyond index 9 in the palette data is 0, then you should map your pixel data in that range, anything other than that will map to the default black.
If you want to use other color values than these defined in your palette, then consider converting to other color modes before calling Image.point():
Image.open(my_image).convert('L').point(lambda p: 255 if p==0 else 76 if p==1 else 124)

How to replace color for colored objects in image?

I am trying to detect edges in images of a video, but edge detection methods such as canny does not work very well might be due to in similarity between boxes's color and floor color or brightness so I want to find a way to make all red and blue boxes look as white as possible, or may be the best way to detect edges as perfect as possible for every frame since that is the ultimate goal.
I recommend you using color tracking then.
Convert to HSV
cv2.bgr2hsv
Why hsv? eventhough the brightness change, u can still detect that color
Filtering
You can use cv2.inrange
Noise cancelling
Use cv2.Gaussianblur
Contouring
use cv2.findContours
Find the edge
use ur method
Repeat this step for every color of your box
Hope this help
Just to complete my comment in your question. One can use HSV/HLS colorspaces and use inRanges with the Hue channel. For example:
import numpy as np
import cv2
# load image and threshold it
original = cv2.imread("a.jpg")
hsvframe = cv2.cvtColor(original, cv2.COLOR_BGR2HLS)
mask = cv2.inRange(hsvframe, (160,40,40), (180, 255, 255))
mask = mask + cv2.inRange(hsvframe, (0,40,40), (12, 255, 255)) # color red is at the beginning and end of the hue wheel
original[mask==255] = (0,255,0)
cv2.imshow("image", original)
cv2.waitKey(0)
cv2.destroyAllWindows()
Things to remember, Hue goes from 0-180 in np.uint8. This means if you need hue 300-360 the limits will be 150-180. The other two values are 0-255 where 255 = 100%.
The result of this small code is:
It is not perfect, but one can refine it using the methods suggested by the other answer. I hope this helps.

How to find the RED color regions using OpenCV? [duplicate]

This question already has answers here:
Finding red color in image using Python & OpenCV
(3 answers)
Closed 10 months ago.
I am trying to make a program where I detect red. However sometimes it is darker than usual so I can't just use one value.
What is a good range for detecting different shades of red?
I am currently using the range 128, 0, 0 - 255, 60, 60 but sometimes it doesn't even detect a red object I put in front of it.
RGBis not a good color space for specific color detection. HSV will be a good choice.
For RED, you can choose the HSV range (0,50,20) ~ (5,255,255) and (175,50,20)~(180,255,255)using the following colormap. Of course, the RED range is not that precise, but it is just ok.
The code taken from my another answer: Detect whether a pixel is red or not
#!/usr/bin/python3
# 2018.07.08 10:39:15 CST
# 2018.07.08 11:09:44 CST
import cv2
import numpy as np
## Read and merge
img = cv2.imread("ColorChecker.png")
img_hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
## Gen lower mask (0-5) and upper mask (175-180) of RED
mask1 = cv2.inRange(img_hsv, (0,50,20), (5,255,255))
mask2 = cv2.inRange(img_hsv, (175,50,20), (180,255,255))
## Merge the mask and crop the red regions
mask = cv2.bitwise_or(mask1, mask2 )
croped = cv2.bitwise_and(img, img, mask=mask)
## Display
cv2.imshow("mask", mask)
cv2.imshow("croped", croped)
cv2.waitKey()
Related answers:
Choosing the correct upper and lower HSV boundaries for color detection with`cv::inRange` (OpenCV)
How to define a threshold value to detect only green colour objects in an image :Opencv
How to detect two different colors using `cv2.inRange` in Python-OpenCV?
Detect whether a pixel is red or not
Of course, for the specific question, maybe other color space is also OK.
How to read utility meter needle with opencv?
You could check that the red component is the maximum and others are both clearly lower:
def red(r, g, b):
threshold = max(r, g, b)
return (
threshold > 8 # stay away from black
and r == threshold # red is biggest component
and g < threshold*0.5 # green is much smaller
and b < threshold*0.5 # so is b
)
This can be implemented very efficiently using numpy.
The "right way" would be doing a full conversion to HSV and check there, but it's going to be slower and somewhat trickier (hue is an angle so you cannot just take the absolute value of the difference, moreover colors like (255, 254, 254) are going to be qualified as "red" even if they're considered white for a human).
Note also that human visual system tends to compensate for average, so something could be seen as "blue" even if indeed the biggest component is red, but everything in the image is red, so that "doesn't count" for our brain.
In the image below if you ask a human what color is the part in the circle area most would say "blue" while indeed the biggest component is red:
Please, use HSV or HSL (hue, saturation, luminance) instead of RGB, in HSV the red color can be easily detected using the value of hue within some threshold.
Red Color means Red value is higher than Blue and Green.
So you can check the differences between Red and Blue, Red and Green.
You can simply split RGB into individual channels and apply threshold like this.
b,g,r = cv2.split(img_rgb)
rg = r - g
rb = r - b
rg = np.clip(rg, 0, 255)
rb = np.clip(rb, 0, 255)
mask1 = cv2.inRange(rg, 50, 255)
mask2 = cv2.inRange(rb, 50, 255)
mask = cv2.bitwise_and(mask1, mask2)
Hope it can be a solution for your problem.
Thank you.

what are recommended color spaces for detecting orange color in open cv?

I need a suitable color-space to detect orange color above very similar colors as red and yellow.I have already tried some color-spaces as :RGB HSV & YUV but object i want to detect , changes its position which means the environmental light changes over time & this is my main problem .
HSV is a good color space for color detection.
This is a hsv colormap for reference:
The x-axis represents Hue in [0,180), the y-axis1 represents Saturation in [0,255], the y-axis2 represents S = 255, while keep V = 255.
To find a color, usually just look up for the range of H and S, and set v in range(20, 255).
For example:
detect orange
Details from my another answer: Choosing the correct upper and lower HSV boundaries for color detection with`cv::inRange` (OpenCV)
To find the orange color, we look up for the map, and find the best range: H :[10, 25], S: [100, 255], and V: [20, 255]. So the mask is cv2.inRange(hsv,(10, 100, 20), (25, 255, 255) )
#!/usr/bin/python3
# 2018.01.21 20:46:41 CST
import cv2
img = cv2.imread("test.jpg")
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
mask = cv2.inRange(hsv,(10, 100, 20), (25, 255, 255) )
cv2.imshow("orange", mask);cv2.waitKey();cv2.destroyAllWindows()
The result:
detect green / yellow/ blue
How to define a threshold value to detect only green colour objects in an image :Opencv
detect two different colors
How to detect two different colors using `cv2.inRange` in Python-OpenCV?
Randomly choosing a color system might not be the best approach.
A more systematic approach could be by looking at a color histogram such as below, which shows all image pixels in the RGB cube.
Then you populate this histogram with orange color samples taken from various images, in such a way to cover all the "oranges" you are thinking of.
This will delimit a region in RGB space and the shape of the region will tell you the most suitable color system, knowing how the other color systems map to to the cube. For example, HLS can be represented as a bicone or bipyramid with it axs along the main diagonal of the cube.
Admittedly, this is a difficult task.

Categories