I am using background subtraction to analyse moving objects of an outdoor scene. When the sun comes out I have a problem with shadows. I am using contours to isolate the objects. At the moment I simply analyse the top half of the contour as the shadow is normally in the bottom half.
Imagine a contour of a rubber duck, what I would like to do is to find the y position of the neck of the duck, that is where the contour is at its minimum horizontal dimension. Could someone please point me in the right direction of how to find the "neck of the duck"?
In the code, binary
is a threshold image of the moving objects, HIGHT
and WIDTH
are the height and width of the image, lab
is the same image in the LAB color space.
I would like to replace the half = int(h/2)
line with a function to find the y position of a horizontal line to cut the "ducks neck" off.
_,contours,_ = cv2.findContours(binary.copy(), cv2.RETR_EXTERNAL,
cv2.CHAIN_APPROX_SIMPLE)
# loop over the contours
for i, cnt in enumerate(contours):
# compute the bounding box for the contour
(x, y, w, h) = cv2.boundingRect(cnt)
# reject contours outside size range
if w > 250 or w < 30 or h > 250 or h < 30 :
continue
# make sure the box is inside the frame
if x <= 0 or y <= 0 or x+w >= (WIDTH -1) or y+h >= (HIGHT -1):
continue
# isolate feature
half = int(h/2)
roi = lab[y:y+half, x:x+w]
mask = binary[y:y+half, x:x+w]
# calculate the mean of the colour
mean = cv2.mean(roi, mask = mask)
# note: mean is L a b
L = int(mean[0])
a = int(mean[1])
b = int(mean[2])
print L,a,b
I am using opencv 3 and python 2.7
P.S. I have tried the background subtractor MOG2 which is said to identify shadows but it is way to noisy for my use and not viable.
Contour area is given by the function cv. contourArea() or from moments, M['m00'].
To draw the contours, cv. drawContours function is used. It can also be used to draw any shape provided you have its boundary points. Its first argument is source image, second argument is the contours which should be passed as a Python list, third argument is index of contours (useful when drawing individual contour.
OpenCV is very dynamic in which we can first find all the objects (or contours) in an image using the cv2. findContours() function. We can then find the size of each object using the cv2. contourArea() function.
You can define a mask to erode the image, so that you can break the top and bottom blobs from the valley. You can apply that to your code like the following:
# loop over the contours
for i, cnt in enumerate(contours):
# compute the bounding box for the contour
(x, y, w, h) = cv2.boundingRect(cnt)
# reject contours outside size range
if w > 250 or w < 30 or h > 250 or h < 30 :
continue
# make sure the box is inside the frame
if x <= 0 or y <= 0 or x+w >= (WIDTH -1) or y+h >= (HIGHT -1):
continue
# ---------------
# create a mask for erosion, you can play with the mask size/shape
mask = cv2.getStructuringElement(cv2.MORPH_RECT,(5,5))
# erode the original image
eroded_img = cv2.erode(binary,mask,iterations = 1)
cv2.imshow("Eroded image",eroded_img)
# find the middle of the two new contours
_,new_contours,_ = cv2.findContours(eroded_img, cv2.RETR_EXTERNAL,
cv2.CHAIN_APPROX_SIMPLE)
(_, y_t, _, h_t) = cv2.boundingRect(new_contours[0])
(_, y_b, _, h_b) = cv2.boundingRect(new_contours[1])
bottom_top_y = max(y_t, y_b) # highest y of bottom part
top_bottom_y = min(y_t+h_t, y_b+h_b) # lowest y of top part
half = top_bottom_y + (bottom_top_y - top_bottom_y)/2
# ------------
# isolate feature
roi = lab[y:y+half, x:x+w]
mask = binary[y:y+half, x:x+w]
# calculate the mean of the colour
mean = cv2.mean(roi, mask = mask)
# note: mean is L a b
L = int(mean[0])
a = int(mean[1])
b = int(mean[2])
print L,a,b
Hope it helps! For more examples on morphological operations on binary images, you can check here.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With