I'm writing a program to identify red and green colours from the output of a camera, and my camera can't detect red colours. I've tried changing the red values but it still doesn't seem to work. Does anyone know where I went wrong? Thank you!
import numpy as np
import cv2
red_lower = np.array([156,43,46])
red_upper = np.array([180,255,255])#这里是设置颜色
green_lower=np.array([35,43,46])
green_lower=np.array([77,255,255])
cap = cv2.VideoCapture(0)
cap.set(3, 320)
cap.set(4, 240)
while 1:
ret, frame = cap.read()
frame = cv2.GaussianBlur(frame, (5, 5), 0)
hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
mask = cv2.inRange(hsv, red_lower, red_upper)
mask = cv2.inRange(hsv, green_lower, green_upper)
# 图像学膨胀腐蚀
mask = cv2.erode(mask, None, iterations=2)
mask = cv2.GaussianBlur(mask, (3, 3), 0)
res = cv2.bitwise_and(frame, frame, mask=mask)
# 寻找轮廓并绘制轮廓
cnts = cv2.findContours(mask.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)[-2]
if len(cnts) > 0:
# 寻找面积最大的轮廓并画出其最小外接圆
cnt = max(cnts, key=cv2.contourArea)
(x, y), radius = cv2.minEnclosingCircle(cnt)
cv2.circle(frame, (int(x), int(y)), int(radius), (255, 0, 255), 2)
# 找到物体的位置坐标,获得颜色物体的位置,可以来控制小车的转向
print(int(x), int(y))
else:
pass
cv2.imshow('frame', frame)
cv2.imshow('mask', mask)
cv2.imshow('res', res)
if cv2.waitKey(5) & 0xFF == 27:
break
cap.release()
cv2.destroyAllWindows()
Error codes are as follows:
Redeclared 'mask' defined above without usage
your code says
red_lower = np.array([156,43,46])
red_upper = np.array([180,255,255])
that means you only consider hue "angles" of 312 to 360 degrees, which is red with a touch of magenta.
red is defined to be around angle 0.
what about angles on the positive side of 0 degrees, red with a touch of yellow?
you'll need to widen your range of hues. you'll need to define another two bounds to catch hues on the positive side of 0 degrees, then use a "bitwise or" operation to combine both ranges of red into one mask.
(if you found this answer useful, please accept and vote on it. thanks!)
"Redeclared 'mask' defined above without usage" is not an error, but it's a hint your IDE is giving as to what's going wrong here.
The second line in
hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
mask = cv2.inRange(hsv, red_lower, red_upper)
mask = cv2.inRange(hsv, green_lower, green_upper)
practically does nothing since you immediately reassign mask = afterwards.
If you want to combine two masks, you could use bitwise_or:
hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
red_mask = cv2.inRange(hsv, red_lower, red_upper)
green_mask = cv2.inRange(hsv, green_lower, green_upper)
mask = cv2.bitwise_or(red_mask, green_mask)
Related
I have found lots of information about how to draw rectangles around the biggest blue object in the frame but I need to draw rectangles around all of the blue objects.
This is my current code
import numpy as np
import cv2
cap = cv2.VideoCapture(0)
while True:
_, frame = cap.read()
# Convert BGR to HSV
hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
# define range of blue color in HSV
lower_blue = np.array([100,50,50])
upper_blue = np.array([130,255,255])
# Threshold the HSV image to get only blue colors
mask = cv2.inRange (hsv, lower_blue, upper_blue)
bluecnts = cv2.findContours(mask.copy(),
cv2.RETR_EXTERNAL,
cv2.CHAIN_APPROX_SIMPLE)[-2]
if len(bluecnts)>0:
blue_area = max(bluecnts, key=cv2.contourArea)
print(blue_area)
(xg,yg,wg,hg) = cv2.boundingRect(blue_area)
cv2.rectangle(frame,(xg,yg),(xg+wg, yg+hg),(0,255,0),2)
result = cv2.bitwise_and(frame, frame, mask=mask)
cv2.imshow('frame',frame)
cv2.imshow('mask',mask)
cv2.imshow('blue', result)
if cv2.waitKey(1) == ord('q'):
break
cap.release()
cv2.destroyAllWindows()
And this is what it currently does, draw one rectangle around the biggest blue object but I need it around each one.
In your Python/OpenCV code, try replacing
if len(bluecnts)>0:
blue_area = max(bluecnts, key=cv2.contourArea)
print(blue_area)
(xg,yg,wg,hg) = cv2.boundingRect(blue_area)
cv2.rectangle(frame,(xg,yg),(xg+wg, yg+hg),(0,255,0),2)
with
if len(bluecnts)>0:
for cnt in bluecnts:
area = cv2.contourArea(cnt)
print(area)
(xg,yg,wg,hg) = cv2.boundingRect(cnt)
cv2.rectangle(frame,(xg,yg),(xg+wg, yg+hg),(0,255,0),2)
(Untested)
I would like to detect this gate below, ideally the entire gate. I have played around for hours with a trackbar script but I am just not finding the right color space. I found other threads that just track yellow and not even that is working.. This is my code:
def track():
cap = cv2.VideoCapture('../files/sub/gate_mission1.mp4')
while True:
_, frame = cap.read()
cv2.imshow('img', frame)
hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
lower = np.array([20, 93, 0])
upper = np.array([45, 255, 255])
mask = cv2.inRange(hsv, lower, upper)
contours, _ = cv2.findContours(mask, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
cv2.drawContours(frame, contours, -1, (0, 255, 0), 2)
Maybe there is a way to just remove all the blue/greenish too im not sure? What are my options here?
This seems to work for me by thresholding in LAB colorspace in Python/OpenCV. According to Wikipedia at https://en.wikipedia.org/wiki/CIELAB_color_space "The a* axis is relative to the green–red opponent colors, with negative values toward green and positive values toward red." So we ought to get reasonably good separation for your green and reddish colors.
Input:
import cv2
import numpy as np
# load images
img = cv2.imread('gate.jpg')
# convert to LAB
lab = cv2.cvtColor(img, cv2.COLOR_BGR2LAB)
# set black ranges
lower = (130,105,100)
upper = (170,170,160)
# threshold on black
result = cv2.inRange(lab, lower, upper)
# save output
cv2.imwrite('gate_thresh.jpg', result)
# display results
cv2.imshow('thresh',result)
cv2.waitKey(0)
cv2.destroyAllWindows()
Threshold Image
I have this image with 3 channels RGB (a result of a VARI Index computation) and I would like to draw bounding boxes (rectangles) around the plants, represented in green here. What is the best and easiest way to do it with OpenCV / python?
I guess it's an easy problem for OpenCV experts, but I could not find good tutorials online to do this for multiple objects.
The closest tutorial I found was: determining-object-color-with-opencv
The assumptions for the bounding boxes should/could be:
green is the dominant color.
it should be more than X pixels.
Thanks in advance!
Just answering my own question after stumbling upon this resource: https://docs.opencv.org/3.4/da/d0c/tutorial_bounding_rects_circles.html
May not be the best answer but it somehow solves my problem!
import cv2
import numpy as np
image = cv2.imread('vari3.png')
# https://www.pyimagesearch.com/2016/02/15/determining-object-color-with-opencv/
# https://docs.opencv.org/3.4/da/d0c/tutorial_bounding_rects_circles.html
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
lab = cv2.cvtColor(image, cv2.COLOR_BGR2LAB)
# mask: green is dominant.
thresh = np.array((image.argmax(axis=-1) == 1) * 255, dtype=np.uint8)
cnts = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)[0]
contours_poly = [None] * len(cnts)
boundRect = [None] * len(cnts)
for i, c in enumerate(cnts):
contours_poly[i] = cv2.approxPolyDP(c, 3, True)
boundRect[i] = cv2.boundingRect(contours_poly[i])
for i in range(len(cnts)):
# cv2.drawContours(image, contours_poly, i, (0, 255, 0), thickness=2)
pt1 = (int(boundRect[i][0]), int(boundRect[i][1]))
pt2 = (int(boundRect[i][0] + boundRect[i][2]), int(boundRect[i][1] + boundRect[i][3]))
if np.sqrt((pt2[1] - pt1[1]) * (pt2[0] - pt1[0])) < 30:
continue
cv2.rectangle(image, pt1, pt2, (0, 0, 0), 2)
cv2.imwrite('result.png', image)
cv2.imshow("Image", image)
cv2.waitKey(0)
You need to do HSV filtering
Change image colors from BGR to HSV (Hue Saturation Value).
Filter a certain range of saturation and hue that matches green by
thresholding.
Refer to this page for code to do the first 2
https://pythonprogramming.net/color-filter-python-opencv-tutorial/
Do some morphological operations like Erosion, Dilation, Opening,
Closing to remove the small bits of green that don't represent trees
and to connect the trees that look broken together.
Refer to https://docs.opencv.org/master/d9/d61/tutorial_py_morphological_ops.html
Detect the contours then draw the rectangles
import cv2
import numpy as np
img = cv2.imread('8FGo1.jpg',1)
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
lower_red = np.array([45,100,50])
upper_red = np.array([75,255,255])
mask = cv2.inRange(hsv, lower_red, upper_red)
kernel = np.ones((5,5),np.uint8)
mask = cv2.morphologyEx(mask, cv2.MORPH_OPEN, kernel)
mask = cv2.morphologyEx(mask, cv2.MORPH_CLOSE, kernel)
contours, hierarchy = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
for contour in contours:
x,y,w,h = cv2.boundingRect(contour)
img = cv2.rectangle(img,(x,y),(x+w,y+h),(0,255,0),2)
cv2.rectangle(img,(x,y),(x+w,y+h),(0,255,0),2)
cv2.imshow('img',img)
#cv2.imshow('mask',mask)
Output
I need to detect black objects in a real time video. I got a code in the internet for detecting blue objects. So I changed the upper and lower hsv value according to bgr colour code(am not clear about how to convert bgr to hsv), But its not detecting the black object in the video.the code am using blue colour detection is:
import cv2
import numpy as np
cap = cv2.VideoCapture(0)
while(1):
_, frame = cap.read()
hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
lower_red = np.array([110,50,50])
upper_red = np.array([130,255,255])
mask = cv2.inRange(hsv, lower_red, upper_red)
res = cv2.bitwise_and(frame,frame, mask= mask)
cv2.imshow('frame',frame)
cv2.imshow('mask',mask)
cv2.imshow('res',res)
k = cv2.waitKey(5) & 0xFF
if k == 27:
break
cv2.destroyAllWindows()
cap.release()
the output for blue color is:
original image:
The code I'm using for black is:`
import cv2
import numpy as np
cap = cv2.VideoCapture(0)
while(1):
_, frame = cap.read()
hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
lower_red = np.array([0,0,0])
upper_red = np.array([0,0,0])
mask = cv2.inRange(hsv, lower_red, upper_red)
res = cv2.bitwise_and(frame,frame, mask= mask)
cv2.imshow('frame',frame)
cv2.imshow('mask',mask)
cv2.imshow('res',res)
k = cv2.waitKey(5) & 0xFF
if k == 27:
break
cv2.destroyAllWindows()
cap.release()
Result:
Nothing is displayed in the result of black. I think the problem is in the hsv conversion but am not sure. And in the detected blue image is not at all accurate it result in noise. How to achieve black detection and reduce noise?.
The easiest way to detect black would be to do a binary threshold in greyscale. Black pixel values will always have a very low value, so therefore it would be easier to do this in a 1 channel image instead of a 3 channel. I would recommend:
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
ret, thresh = cv2.threshold(gray, 15, 255, cv2.THRESH_BINARY_INV)
change the value of 15 until you get reasonable results. Lower value would result in preserving only darker pixels. If you wanted to extract the location of the pixels you could also get the contours i.e.
image, contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)
and then draw the contour back onto the original frame with:
frame = cv2.drawContours(frame, contours, -1,(0,0,255),3)
Alternatively, you might find it easier to invert the image first so that you are trying to extract white pixels. This could lead to less confusion with the pixels you want to extract being similar to the mask pixel (0). You could do this simple with numpy subtraction, then set your thresh value to a very high value i.e:
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
gray = 255-gray
ret, thresh = cv2.threshold(gray, 225, 255, cv2.THRESH_BINARY_INV)
image, contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)
frame = cv2.drawContours(frame, contours, -1,(0,0,255),3)
black= np.array([0, 0, 0], np.uint8)
grayScale= np.array([0, 0, 29], np.uint8)
Valor (29) depends of how much "brightness" you want.
This page is where you can test your color ranges
I have a code here that detects LASER light but I'm experiencing problems in different light conditions. So I think I might solve it if I added a code that checks if that light is a circle.
The problem is I don't know how to apply it here. Here is what the laser light looks like in the mask.
I'm hoping that you can help me with my code.
Here's my code:
import cv2
import numpy as np
cap = cv2.VideoCapture(0)
while True:
ret, frame = cap.read()
hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV) convert from bgr to hsv color space
lower = np.array([0,0,255]) #range of laser light
upper = np.array([255, 255, 255])
mask = cv2.inRange(hsv, lower, upper)
maskcopy = mask.copy()
circles = cv2.HoughCircles(maskcopy, cv2.HOUGH_GRADIENT, 1, 500,
param1 = 20, param2 = 10,
minRadius = 1, maxRadius = 3)
_,cont,_ = cv2.findContours(maskcopy, cv2.RETR_LIST,
cv2.CHAIN_APPROX_SIMPLE)
if circles is not None:
circles = np.round(circles[0,:]).astype('int')
for(x,y,r) in circles:
cv2.circle(frame, (x,y), r, (0,255,0),4)
cv2.imshow('mask', mask)
cv2.imshow('frame', frame)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
cap.release()
cv2.destroyAllWindows()
Screenshot:
I tried something similar once and the best solution for me was:
(I saved your image to my hard disk and made a sample code)
import cv2
import math
img = cv2.imread('laser.jpg')
gray_image = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
ret,thresh = cv2.threshold(gray_image,100,255,cv2.THRESH_BINARY)
im2, contours, hierarchy = cv2.findContours(thresh,cv2.RETR_TREE,cv2.CHAIN_APPROX_NONE)
area = sorted(contours, key=cv2.contourArea, reverse=True)
contour = area[0]
(x,y),radius = cv2.minEnclosingCircle(contour)
radius = int(radius)
area = cv2.contourArea(contour)
circ = 4*area/(math.pi*(radius*2)**2)
cv2.drawContours(img, [contour], 0, (0,255,0), 2)
cv2.imshow('img', img)
print(circ)
So the idea is to find your contour with cv2.findContours (laser point) and enclosing circle to it so you can get the radius, then get the area with cv2.contourArea of your contour and check its circularity with the formula circ = 4*area/(math.pi*(radius*2)**2). The perfect citrcle would return the result of 1. The more it goes to 0 the less "circuar" your contour is (in pictures below). Hope it helps!
so your code should be something like this and it will return no error (tried it and it works)
import cv2
import numpy as np
import math
cap = cv2.VideoCapture(0)
while True:
try:
ret, frame = cap.read()
hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV) #convert from bgr to hsv color space
lower = np.array([0,0,255]) #range of laser light
upper = np.array([255, 255, 255])
mask = cv2.inRange(hsv, lower, upper)
im2, contours, hierarchy = cv2.findContours(mask,cv2.RETR_TREE,cv2.CHAIN_APPROX_NONE)
area = sorted(contours, key=cv2.contourArea, reverse=True)
contour = area[0]
(x,y),radius = cv2.minEnclosingCircle(contour)
radius = int(radius)
area = cv2.contourArea(contour)
circ = 4*area/(math.pi*(radius*2)**2)
print(circ)
except:
pass
cv2.imshow('mask', mask)
cv2.imshow('frame', frame)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
cap.release()
cv2.destroyAllWindows()
I came up with a solution with a different approach.
My idea was to create a circle with center in the center of the white region of the mask and with radius equal to half the width of the white region of the mask. Then I check how similar is this circle from the mask.
Here is the code:
white = np.where(mask>250) # you can also make it == 255
white = np.asarray(white)
minx = min(white[0])
maxx = max(white[0])
miny = min(white[1])
maxy = max(white[1])
radius = int((maxx-minx)/2)
cx = minx + radius
cy = miny + radius
black = mask.copy()
black[:,:]=0
cv2.circle(black, (cy,cx), radius, (255,255,255),-1)
diff = cv2.bitwise_xor(black, mask)
diffPercentage = len(diff>0)/diff.size
print (diffPercentage)
Then you have to come up with what percentage threshold is "similar" enough for you.
The code above was tested reading a mask from disk, but a video is just a sequence of images. Without your webcam input I cannot test the code with video, but it should work like this:
import cv2
import numpy as np
cap = cv2.VideoCapture(0)
while True:
ret, frame = cap.read()
hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
lower = np.array([0,0,255]) #range of laser light
upper = np.array([255, 255, 255])
mask = cv2.inRange(hsv, lower, upper)
white = np.where(mask>250) # you can also make it == 255
white = np.asarray(white)
minx = min(white[0])
maxx = max(white[0])
miny = min(white[1])
maxy = max(white[1])
radius = int((maxx-minx)/2)
cx = minx + radius
cy = miny + radius
black = mask.copy()
black[:,:]=0
cv2.circle(black, (cy,cx), radius, (255,255,255),-1)
diff = cv2.bitwise_xor(black, mask)
diffPercentage = len(diff>0)/diff.size
print (diffPercentage)
cv2.imshow('mask', mask)
cvw.imshow('diff', diff)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
cap.release()
cv2.destroyAllWindows()