ROI extraction from Opencv Videoframe - python

How to extract ROI from an OpenCV Video frame?
I have developed a code for tracking and counting system. I need help in implementing some more logic in my code.
Help Required Section: Extract the images of the object when it crosses the Reference line.
I want to extract the images of the object having rectangular box ROI.
Below is my code:
import numpy as np
import cv2
import pandas as pd
cap = cv2.VideoCapture('traffic.mp4')
frames_count, fps, width, height = cap.get(cv2.CAP_PROP_FRAME_COUNT), cap.get(cv2.CAP_PROP_FPS), cap.get(
cv2.CAP_PROP_FRAME_WIDTH), cap.get(cv2.CAP_PROP_FRAME_HEIGHT)
width = int(width)
height = int(height)
# creates a pandas data frame with the number of rows the same length as frame count
df = pd.DataFrame(index=range(int(frames_count)))
df.index.name = "Frames"
framenumber = 0 # keeps track of current frame
Chocolatecrossedup = 0 # keeps track of Chocolates that crossed up
Chocolatecrosseddown = 0 # keeps track of Chocolates that crossed down
Chocolateids = [] # blank list to add Chocolate ids
Chocolateidscrossed = [] # blank list to add Chocolate ids that have crossed
totalChocolates = 0 # keeps track of total Chocolates
fgbg = cv2.createBackgroundSubtractorMOG2() # create background subtractor
# information to start saving a video file
ret, frame = cap.read() # import image
ratio = .5 # resize ratio
image = cv2.resize(frame, (0, 0), None, ratio, ratio) # resize image
width2, height2, channels = image.shape
video = cv2.VideoWriter('Chocolate_counter.avi', cv2.VideoWriter_fourcc('M', 'J', 'P', 'G'), fps, (height2, width2), 1)
while True:
ret, frame = cap.read() # import image
if ret: # if there is a frame continue with code
image = cv2.resize(frame, (0, 0), None, ratio, ratio) # resize image
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) # converts image to gray
fgmask = fgbg.apply(gray) # uses the background subtraction
# applies different thresholds to fgmask to try and isolate Chocolates
# just have to keep playing around with settings until Chocolates are easily identifiable
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5, 5)) # kernel to apply to the morphology
closing = cv2.morphologyEx(fgmask, cv2.MORPH_CLOSE, kernel)
opening = cv2.morphologyEx(closing, cv2.MORPH_OPEN, kernel)
dilation = cv2.dilate(opening, kernel)
retvalbin, bins = cv2.threshold(dilation, 220, 255, cv2.THRESH_BINARY) # removes the shadows
# creates contours
contours, hierarchy = cv2.findContours(bins, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
# use convex hull to create polygon around contours
hull = [cv2.convexHull(c) for c in contours]
# draw contours
#cv2.drawContours(image, hull, -1, (0, 255, 0), 3)
# line created to stop counting contours, needed as Chocolates in distance become one big contour
lineypos = 225
cv2.line(image, (0, lineypos), (width, lineypos), (255, 0, 0), 5)
# line y position created to count contours
lineypos2 = 250
cv2.line(image, (0, lineypos2), (width, lineypos2), (0, 255, 0), 5)
# min area for contours in case a bunch of small noise contours are created
minarea = 300
# max area for contours, can be quite large for buses
maxarea = 50000
# vectors for the x and y locations of contour centroids in current frame
cxx = np.zeros(len(contours))
cyy = np.zeros(len(contours))
for i in range(len(contours)): # cycles through all contours in current frame
if hierarchy[0, i, 3] == -1: # using hierarchy to only count parent contours (contours not within others)
area = cv2.contourArea(contours[i]) # area of contour
if minarea < area < maxarea: # area threshold for contour
# calculating centroids of contours
cnt = contours[i]
M = cv2.moments(cnt)
cx = int(M['m10'] / M['m00'])
cy = int(M['m01'] / M['m00'])
if cy > lineypos: # filters out contours that are above line (y starts at top)
# gets bounding points of contour to create rectangle
# x,y is top left corner and w,h is width and height
x, y, w, h = cv2.boundingRect(cnt)
# creates a rectangle around contour
cv2.rectangle(image, (x, y), (x + w, y + h), (255, 0, 0), 2)
# Prints centroid text in order to double check later on
cv2.putText(image, str(cx) + "," + str(cy), (cx + 10, cy + 10), cv2.FONT_HERSHEY_SIMPLEX,
.3, (0, 0, 255), 1)
cv2.drawMarker(image, (cx, cy), (0, 0, 255), cv2.MARKER_STAR, markerSize=5, thickness=1,
line_type=cv2.LINE_AA)
# adds centroids that passed previous criteria to centroid list
cxx[i] = cx
cyy[i] = cy
# eliminates zero entries (centroids that were not added)
cxx = cxx[cxx != 0]
cyy = cyy[cyy != 0]
# empty list to later check which centroid indices were added to dataframe
minx_index2 = []
miny_index2 = []
# maximum allowable radius for current frame centroid to be considered the same centroid from previous frame
maxrad = 25
# The section below keeps track of the centroids and assigns them to old Chocolateids or new Chocolateids
if len(cxx): # if there are centroids in the specified area
if not Chocolateids: # if Chocolateids is empty
for i in range(len(cxx)): # loops through all centroids
Chocolateids.append(i) # adds a Chocolate id to the empty list Chocolateids
df[str(Chocolateids[i])] = "" # adds a column to the dataframe corresponding to a Chocolateid
# assigns the centroid values to the current frame (row) and Chocolateid (column)
df.at[int(framenumber), str(Chocolateids[i])] = [cxx[i], cyy[i]]
totalChocolates = Chocolateids[i] + 1 # adds one count to total Chocolates
else: # if there are already Chocolate ids
dx = np.zeros((len(cxx), len(Chocolateids))) # new arrays to calculate deltas
dy = np.zeros((len(cyy), len(Chocolateids))) # new arrays to calculate deltas
for i in range(len(cxx)): # loops through all centroids
for j in range(len(Chocolateids)): # loops through all recorded Chocolate ids
# acquires centroid from previous frame for specific Chocolateid
oldcxcy = df.iloc[int(framenumber - 1)][str(Chocolateids[j])]
# acquires current frame centroid that doesn't necessarily line up with previous frame centroid
curcxcy = np.array([cxx[i], cyy[i]])
if not oldcxcy: # checks if old centroid is empty in case Chocolate leaves screen and new Chocolate shows
continue # continue to next Chocolateid
else: # calculate centroid deltas to compare to current frame position later
dx[i, j] = oldcxcy[0] - curcxcy[0]
dy[i, j] = oldcxcy[1] - curcxcy[1]
for j in range(len(Chocolateids)): # loops through all current Chocolate ids
sumsum = np.abs(dx[:, j]) + np.abs(dy[:, j]) # sums the deltas wrt to Chocolate ids
# finds which index carid had the min difference and this is true index
correctindextrue = np.argmin(np.abs(sumsum))
minx_index = correctindextrue
miny_index = correctindextrue
# acquires delta values of the minimum deltas in order to check if it is within radius later on
mindx = dx[minx_index, j]
mindy = dy[miny_index, j]
if mindx == 0 and mindy == 0 and np.all(dx[:, j] == 0) and np.all(dy[:, j] == 0):
# checks if minimum value is 0 and checks if all deltas are zero since this is empty set
# delta could be zero if centroid didn't move
continue # continue to next Chocolateid
else:
# if delta values are less than maximum radius then add that centroid to that specific Chocolateid
if np.abs(mindx) < maxrad and np.abs(mindy) < maxrad:
# adds centroid to corresponding previously existing Chocolateid
df.at[int(framenumber), str(Chocolateids[j])] = [cxx[minx_index], cyy[miny_index]]
minx_index2.append(minx_index) # appends all the indices that were added to previous Chocolateids
miny_index2.append(miny_index)
for i in range(len(cxx)): # loops through all centroids
# if centroid is not in the minindex list then another Chocolate needs to be added
if i not in minx_index2 and miny_index2:
df[str(totalChocolates)] = "" # create another column with total Chocolates
totalChocolates = totalChocolates + 1 # adds another total Chocolate the count
t = totalChocolates - 1 # t is a placeholder to total Chocolates
Chocolateids.append(t) # append to list of Chocolate ids
df.at[int(framenumber), str(t)] = [cxx[i], cyy[i]] # add centroid to the new Chocolate id
elif curcxcy[0] and not oldcxcy and not minx_index2 and not miny_index2:
# checks if current centroid exists but previous centroid does not
# new Chocolate to be added in case minx_index2 is empty
df[str(totalChocolates)] = "" # create another column with total Chocolates
totalChocolates = totalChocolates + 1 # adds another total Chocolate the count
t = totalChocolates - 1 # t is a placeholder to total Chocolates
Chocolateids.append(t) # append to list of Chocolate ids
df.at[int(framenumber), str(t)] = [cxx[i], cyy[i]] # add centroid to the new Chocolate id
# The section below labels the centroids on screen
currentChocolates = 0 # current Chocolates on screen
currentChocolatesindex = [] # current Chocolates on screen Chocolateid index
for i in range(len(Chocolateids)): # loops through all Chocolateids
if df.at[int(framenumber), str(Chocolateids[i])] != '':
# checks the current frame to see which Chocolate ids are active
# by checking in centroid exists on current frame for certain Chocolate id
currentChocolates = currentChocolates + 1 # adds another to current Chocolates on screen
currentChocolatesindex.append(i) # adds Chocolate ids to current Chocolates on screen
for i in range(currentChocolates): # loops through all current Chocolate ids on screen
# grabs centroid of certain Chocolateid for current frame
curcent = df.iloc[int(framenumber)][str(Chocolateids[currentChocolatesindex[i]])]
# grabs centroid of certain Chocolateid for previous frame
oldcent = df.iloc[int(framenumber - 1)][str(Chocolateids[currentChocolatesindex[i]])]
if curcent: # if there is a current centroid
# On-screen text for current centroid
cv2.putText(image, "Centroid" + str(curcent[0]) + "," + str(curcent[1]),
(int(curcent[0]), int(curcent[1])), cv2.FONT_HERSHEY_SIMPLEX, .5, (0, 255, 255), 2)
cv2.putText(image, "ID:" + str(Chocolateids[currentChocolatesindex[i]]), (int(curcent[0]), int(curcent[1] - 15)),
cv2.FONT_HERSHEY_SIMPLEX, .5, (0, 255, 255), 2)
cv2.drawMarker(image, (int(curcent[0]), int(curcent[1])), (0, 0, 255), cv2.MARKER_STAR, markerSize=5,
thickness=1, line_type=cv2.LINE_AA)
if oldcent: # checks if old centroid exists
# adds radius box from previous centroid to current centroid for visualization
xstart = oldcent[0] - maxrad
ystart = oldcent[1] - maxrad
xwidth = oldcent[0] + maxrad
yheight = oldcent[1] + maxrad
cv2.rectangle(image, (int(xstart), int(ystart)), (int(xwidth), int(yheight)), (0, 125, 0), 1)
# checks if old centroid is on or below line and curcent is on or above line
# to count Chocolates and that Chocolate hasn't been counted yet
if oldcent[1] >= lineypos2 and curcent[1] <= lineypos2 and Chocolateids[
currentChocolatesindex[i]] not in Chocolateidscrossed:
Chocolatecrossedup = Chocolatecrossedup + 1
cv2.line(image, (0, lineypos2), (width, lineypos2), (0, 0, 255), 5)
Chocolateidscrossed.append(
currentChocolatesindex[i]) # adds Chocolate id to list of count Chocolate to prevent double counting
# checks if old centroid is on or above line and curcent is on or below line
# to count Chocolates and that Chocolate hasn't been counted yet
elif oldcent[1] <= lineypos2 and curcent[1] >= lineypos2 and Chocolateids[
currentChocolatesindex[i]] not in Chocolateidscrossed:
Chocolatecrosseddown = Chocolatecrosseddown + 1
cv2.line(image, (0, lineypos2), (width, lineypos2), (0, 0, 125), 5)
Chocolateidscrossed.append(currentChocolatesindex[i])
# Top left hand corner on-screen text
cv2.rectangle(image, (0, 0), (250, 100), (255, 0, 0), -1) # background rectangle for on-screen text
cv2.putText(image, "Chocolates in Area: " + str(currentChocolates), (0, 15), cv2.FONT_HERSHEY_SIMPLEX, .5, (0, 170, 0), 1)
cv2.putText(image, "Chocolates Crossed Down: " + str(Chocolatecrosseddown), (0, 45), cv2.FONT_HERSHEY_SIMPLEX, .5,(0, 170, 0), 1)
cv2.putText(image, "Total Chocolates Detected: " + str(len(Chocolateids)), (0, 60), cv2.FONT_HERSHEY_SIMPLEX, .5, (0, 170, 0), 1)
cv2.putText(image, 'Time: ' + str(round(framenumber / fps, 2)) + ' sec of ' + str(round(frames_count / fps, 2)) + ' sec', (0, 90), cv2.FONT_HERSHEY_SIMPLEX, .5, (0, 170, 0), 1)
# displays images and transformations
cv2.imshow("countours", image)
cv2.moveWindow("countours", 0, 0)
video.write(image) # save the current image to video file from earlier
# adds to framecount
framenumber = framenumber + 1
k = cv2.waitKey(int(1000/fps)) & 0xff # int(1000/fps) is normal speed since waitkey is in ms
if k == 27:
break
else:
break
cap.release()
cv2.destroyAllWindows()
GUI:
Expecting Result
When the car passes the green line. The image of the car automatically gets saved in a directory/folder. I only need the images of the ROI extracted from the videoframe.

Related

How to get only water level contour with OpenCV python on raspberry pi

I am using raspberry pi4 (8GB) with pi camera to detect water level . I have defined a line from 0,375 to 800,375 . If top most point of water level contour goes above this line then I want to call a function. Here is my code and attached image of setup. How do I get water level contour only. Does it require canny edge detection over contours to get clear water level ? first I am getting largest contour and then defining its top most point.
import numpy as np
import cv2
import time
from datetime import datetime
#color=(255,0,0)
color=(0,255,0)
thickness=2
kernel = np.ones((2,2),np.uint8) # added 01/07/2021
picflag = 0 # set value to 1 once picture is taken
# function to take still picture when water level goes beyond threshold
def takepicture(frame):
currentTime = datetime.now()
picTime = currentTime.strftime("%d.%m.%Y-%H%M%S") # Create file name for our picture
text = currentTime.strftime("%d.%m.%Y-%H:%M:%S")
font = cv2.FONT_HERSHEY_SIMPLEX # font
org = (05, 20) # org
fontScale = 0.5 # fontScale
color = (0, 0, 255) # Red color in BGR
thickness = 1 # Line thickness of 2 px
picName = picTime + '.png'
image = cv2.putText(frame, text, org, font, fontScale, color, thickness, cv2.LINE_AA, False)
cv2.imwrite(picName , image)
picflag = 1
return
cap = cv2.VideoCapture(0)
while(True):
# Capture frame-by-frame
ret, frame = cap.read() # ret = 1 if the video is captured; frame is the image
# Our operations on the frame come here
gray = cv2.cvtColor(frame,cv2.COLOR_BGR2GRAY)
#blur = cv2.GaussianBlur(gray,(21,21),0)
gray= cv2.medianBlur(gray, 3) #to remove salt and paper noise
#ret,thresh = cv2.threshold(gray,10,20,cv2.THRESH_BINARY_INV)
ret,thresh = cv2.threshold(gray,127,127,cv2.THRESH_BINARY_INV)
thresh = cv2.morphologyEx(thresh, cv2.MORPH_GRADIENT, kernel) # get outer boundaries only added 01/07/2021
thresh = cv2.dilate(thresh,kernel,iterations = 5) # strengthen weak pixels added 01/07/2021
img1, contours, hierarchy = cv2.findContours(thresh,cv2.RETR_TREE,cv2.CHAIN_APPROX_NONE)
#img1,contours,hierarchy = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE) #added 01/07/2021
cv2.line(frame, pt1=(0,375), pt2=(800,375), color=(0,0,255), thickness=2) # added 01/07/2021
if len(contours) != 0:
c = max(contours, key = cv2.contourArea) # find the largest contour
#x,y,w,h = cv2.boundingRect(c) # get bounding box of largest contour
img2=cv2.drawContours(frame, c, -1, color, thickness) # draw largest contour
#img2=cv2.drawContours(frame, contours, -1, color, thickness) # draw all contours
#img3 = cv2.rectangle(img2,(x,y),(x+w,y+h),(0,0,255),2) # draw red bounding box in img
#center = (x, y)
#print(center)
left = tuple(c[c[:, :, 0].argmin()][0])
right = tuple(c[c[:, :, 0].argmax()][0])
top = tuple(c[c[:, :, 1].argmin()][0])
bottom = tuple(c[c[:, :, 1].argmax()][0])
# Draw dots onto frame
cv2.drawContours(frame, [c], -1, (36, 255, 12), 2)
cv2.circle(frame, left, 8, (0, 50, 255), -1)
cv2.circle(frame, right, 8, (0, 255, 255), -1)
cv2.circle(frame, top, 8, (255, 50, 0), -1)
cv2.circle(frame, bottom, 8, (255, 255, 0), -1)
#print('left: {}'.format(left))
#print('right: {}'.format(right))
#print(format(top))
top_countour_point = top[1]
print(top_countour_point)
#print('bottom: {}'.format(bottom))
#if ((top_countour_point <= 375) and (picflag == 0)): #checking if contour top point is above line
#takepicture(frame)
#continue
#if ((top_countour_point > 375) and (picflag == 0)) :
#picflag = 0
#continue
# Display the resulting image
# cv2.line(frame, pt1=(0,375), pt2=(800,375), color=(0,0,255), thickness=2) # added 01/07/2021
#cv2.imshow('Contour',img3)
#cv2.imshow('thresh' ,thresh)
cv2.imshow('Contour',frame)
if cv2.waitKey(1) & 0xFF == ord('q'): # press q to quit
break
# When everything done, release the capture
cap.release()
cv2.destroyAllWindows()
Caveats
As it was pointed out in the comments, there is very little to work with based on your post. In general, I agree with s0mbre that you'd be better of with a water level sensor, and with kavko, that if you do want to use a camera, you need to better control your environment, with lighting, camera angles, background, etc.
However, that is not to say that it is not possible with your current setup, assuming, that it is a static setup except for the water level. As such, there are some assumptions that we can make.
Here are the steps that I took to get an approximate approach:
Gather image data
You have only provided one image with lines already on it, so that's not a lot to go on. What I did is that I removed the line that you added:
Fortunately there wasn't too much of a line left afterwards.
Image processing:
I have loaded the image (this would come from the code you have posted).
Using the assumptions above, I have decided to focus on a narrow slice of the image. I selected only the middle 60 pixels (1)
slc = frame[:, 300:360]
Next, I have converted it to greyscale (2)
gray_slc = cv2.cvtColor(slc, cv2.COLOR_BGR2GRAY)
I have used Canny edge detection (see docs here) to find the edges in the image (3)
edges = cv2.Canny(gray_slc, 50, 90)
After that, I have applied a Hough Transform, to find all the edges (related Stack Overflow answer) (4)
rho = 1
theta = np.pi / 180
threshold = 15
min_line_length = 50
max_line_gap = 20
lines = cv2.HoughLinesP(edges, rho, theta, threshold, np.array([]), min_line_length, max_line_gap)
Given that I can assume that all of the top lines are the edge of the container, I averaged the y coordinates of the lines, and picked the lower most one as the water level:
y_avgs = [(line[0, 1] + line[0, 3]) / 2 for line in lines]
water_level = max(y_avgs)
Having this, I just checked, if it is over the threshold you have selected:
trigger_threshold = 375
if water_level > trigger_threshold:
print("Water level is under the selected line")
Now, keep in mind, I only had the one image to go on. Considering lighting conditions, yours results may vary.

How to confirm if a point is in a certain region

I'm making a program in OpenCV-Python that tracks an object of a certain color as it moves around the frame. I want the frame to be divided into four equal parts, with each part representing a different letter. For example, if the object is in quadrant 1, output A. My question is, how do I set up regions on the frame that, when the object is in that region, the program displays that region's letter on the frame. Like, how do I set up rectangles of regions using coordinate points?
Here's the code I have so far, and any help is appreciated.
# import the necessary packages
from collections import deque
from imutils.video import VideoStream
import numpy as np
import argparse
import cv2
import imutils
import time
# construct the argument parse and parse the arguments
ap = argparse.ArgumentParser()
ap.add_argument("-v", "--video",
help="path to the (optional) video file")
ap.add_argument("-b", "--buffer", type=int, default=32,
help="max buffer size")
args = vars(ap.parse_args())
# define the lower and upper boundaries of the "orange"
# fish in the HSV color space
orangeLower = (5, 50, 50)
orangeUpper = (15, 255, 255)
# initialize the list of tracked points, the frame counter,
# and the coordinate deltas
pts = deque(maxlen=args["buffer"])
counter = 0
(dX, dY) = (0, 0)
direction = ""
# if a video path was not supplied, grab the reference
# to the webcam
if not args.get("video", False):
vs = VideoStream(src=0).start()
# otherwise, grab a reference to the video file
else:
vs = cv2.VideoCapture(args["video"])
# allow the camera or video file to warm up
time.sleep(2.0)
# keep looping
while True:
# grab the current frame
frame = vs.read()
# handle the frame from VideoCapture or VideoStream
frame = frame[1] if args.get("video", False) else frame
# if we are viewing a video and we did not grab a frame,
# then we have reached the end of the video
if frame is None:
break
# resize the frame, blur it, and convert it to the HSV
# color space
frame = imutils.resize(frame, width=600)
blurred = cv2.GaussianBlur(frame, (11, 11), 0)
hsv = cv2.cvtColor(blurred, cv2.COLOR_BGR2HSV)
# construct a mask for the color "orange", then perform
# a series of dilations and erosions to remove any small
# blobs left in the mask
mask = cv2.inRange(hsv, orangeLower, orangeUpper)
mask = cv2.erode(mask, None, iterations=2)
mask = cv2.dilate(mask, None, iterations=2)
# find contours in the mask and initialize the current
# (x, y) center of the ball
cnts = cv2.findContours(mask.copy(), cv2.RETR_EXTERNAL,
cv2.CHAIN_APPROX_SIMPLE)
cnts = imutils.grab_contours(cnts)
center = None
# only proceed if at least one contour was found
if len(cnts) > 0:
# find the largest contour in the mask, then use
# it to compute the minimum enclosing circle and
# centroid
c = max(cnts, key=cv2.contourArea)
((x, y), radius) = cv2.minEnclosingCircle(c)
M = cv2.moments(c)
center = (int(M["m10"] / M["m00"]), int(M["m01"] / M["m00"]))
# only proceed if the radius meets a minimum size
if radius > 10:
# draw the circle and centroid on the frame,
# then update the list of tracked points
cv2.circle(frame, (int(x), int(y)), int(radius),
(0, 255, 255), 2)
cv2.circle(frame, center, 5, (0, 0, 255), -1)
pts.appendleft(center)
# loop over the set of tracked points
for i in np.arange(1, len(pts)):
# if either of the tracked points are None, ignore
# them
if pts[i - 1] is None or pts[i] is None:
continue
# check to see if enough points have been accumulated in
# the buffer
if counter >= 10 and i == 10 and pts[i-10] is not None:
# compute the difference between the x and y
# coordinates and re-initialize the direction
# text variables
dX = pts[i-10][0] - pts[i][0]
dY = pts[i-10][1] - pts[i][1]
(dirX, dirY) = ("", "")
# ensure there is significant movement in the
# x-direction
if np.abs(dX) > 20:
dirX = "East" if np.sign(dX) == 1 else "West"
# ensure there is significant movement in the
# y-direction
if np.abs(dY) > 20:
dirY = "South" if np.sign(dY) == 1 else "North"
# handle when both directions are non-empty
if dirX != "" and dirY != "":
direction = "{}-{}".format(dirY, dirX)
# otherwise, only one direction is non-empty
else:
direction = dirX if dirX != "" else dirY
# otherwise, compute the thickness of the line and
# draw the connecting lines
thickness = int(np.sqrt(args["buffer"] / float(i + 1)) * 2.5)
cv2.line(frame, pts[i - 1], pts[i], (0, 0, 255), thickness)
# show the movement deltas and the direction of movement on
# the frame
cv2.putText(frame, direction, (10, 30), cv2.FONT_HERSHEY_SIMPLEX,
0.65, (0, 0, 255), 3)
cv2.putText(frame, "dx: {}, dy: {}".format(dX, dY),
(10, frame.shape[0] - 10), cv2.FONT_HERSHEY_SIMPLEX,
0.35, (0, 0, 255), 1)
# show the frame to the screen and increment the frame counter
cv2.imshow("Frame", frame)
cv2.rectangle(img=frame, pt1=(0, 0), pt2=(300, 225), color=(0, 0, 0), thickness=3, lineType=8, shift=0)
cv2.rectangle(img=frame, pt1 = (300, 1), pt2 = (600, 225), color = (0, 0, 0), thickness = 3, lineType = 8, shift = 0)
cv2.rectangle(img=frame, pt1 = (0, 225), pt2 = (300, 550), color = (0, 0, 0), thickness = 3, lineType = 8, shift = 0)
cv2.rectangle(img=frame, pt1 = (300, 225), pt2 = (600, 550), color = (0, 0, 0), thickness = 3, lineType = 8, shift = 0)
cv2.imshow("Frame", frame)
key = cv2.waitKey(1) & 0xFF
counter += 1
# Set up contours
# if the 'q' key is pressed, stop the loop
if key == ord("q"):
break
# if we are not using a video file, stop the camera video stream
if not args.get("video", False):
vs.stop()
# otherwise, release the camera
else:
vs.release()
# close all windows
cv2.destroyAllWindows()

Object counting software using opencv

I am working on counting software. I have successfully detected the products and want to count them. For counting, I have defined two-line and if centroid is between two lines the product is counted. Two problems I am facing. First, since my product is coming very fast some product is not falling between lines so, I increased the linespace. Second, after increasing the linespace if the products are falling two times then it is counted twice. I tried to put centroid in a list and if products are counted once, I deleted elements of the list. Count is twice or wrong. Please help.
Here is the video link
https://drive.google.com/file/d/1Gqp937OlDpF4_Nx2kSOzHw3A85Xch-HX/view?usp=sharing
import cv2
import math
import numpy as np
width = 0
height = 0
EntranceCounter = 0
ExitCounter = 0
OffsetRefLines = 120
QttyOfContours = 0
area = 0
detect = []
area_min = 100
area_max = 4000
BinarizationThreshold = 70
#Check if an object in entering in monitored zone
##def CheckEntranceLineCrossing(CoordYCentroid, CoorYEntranceLine,CoorYExitLine,area):
## AbsDistance = abs(CoordYCentroid - CoorYEntranceLine)
## if ((y>CoorYEntranceLine) and (y < CoorYExitLine) ):#and area>=5000):
## return 1
## #else:
## #return 0
cap = cv2.VideoCapture('testvideo.avi')
while(cap.isOpened()):
ret, frame = cap.read()
height = np.size(frame,0)
width = np.size(frame,1)
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
_, threshold = cv2.threshold(frame, 100, 255, cv2.THRESH_BINARY)
low = np.array([0,0,0])
high = np.array([60, 60, 60])
image_mask = cv2.inRange(threshold,low, high)
contours,_= cv2.findContours(image_mask, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
cv2.drawContours(frame, contours, -1, (0,255,0), 3)
CoorYEntranceLine = int(150-OffsetRefLines)
CoorYExitLine = int(150+OffsetRefLines)
cv2.line(threshold, (0,CoorYEntranceLine), (width,CoorYEntranceLine), (0, 255, 0), 2)
cv2.line(threshold , (0,CoorYExitLine), (width,CoorYExitLine), (0, 0, 255), 2)
for (i,c) in enumerate(contours):
new = True
QttyOfContours = QttyOfContours+1
(x, y, w, h) = cv2.boundingRect(c)
cv2.rectangle(threshold, (x, y), (x + w, y + h), (0, 0, 255), 2)
area = cv2.contourArea(c)
if area< area_min:
continue
#find object's centroid
CoordXCentroid = int((x+x+w)/2)
CoordYCentroid = int((y+y+h)/2)
ObjectCentroid = (CoordXCentroid,CoordYCentroid)
#center = pega_centro(x, y, w, h)
center = (CoordXCentroid,CoordYCentroid)
detect.append(center)
for (x,y) in detect:
if ((y < CoorYExitLine) and (y>CoorYEntranceLine) and (area>area_min) and (area<area_max)):
if new ==True:
EntranceCounter += 1
detect.clear()
break
cv2.putText(frame, str(EntranceCounter), ((CoordXCentroid),(CoordYCentroid-50)), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 255, 255), 2)
cv2.putText(threshold, "Entrances: {}".format(str(EntranceCounter)), (10, 50),
cv2.FONT_HERSHEY_SIMPLEX, 0.5, (250, 0, 1), 2)
cv2.imshow('fram1',threshold)
cv2.imshow('frame',frame)
if cv2.waitKey(1)==27:
break
cap.release()
cv2.destroyAllWindows()
Just check intersection of counting line and line between current and previous object position points. And, as extra guaranty, use "counted" flag for tracked objects.

Keep the same label for a moving object using opencv

I want to track a moving fish from camera witch has located on top of a fish tank. Till now I was able to track the multiple moving objects using moving average and background subtraction methods. And I put text on each contour. But the problem is I couldn't keep the same label for same moving fish. Fish can detect from every frame but tracked object number is changing. I have attached my current Python code. What am I doing wrong here? Can someone tell me a another possible way to do this?
import cv2
import numpy as np
device = cv2.VideoCapture(0)
flag, frame = device.read()
movingaverage = np.float32(frame)
background = cv2.createBackgroundSubtractorMOG2()
font=cv2.FONT_HERSHEY_SIMPLEX
kernelOpen=np.ones((5,5))
kernelClose=np.ones((20,20))
while True:
flag, frame = device.read()
alpha = float(1.0/2.0)
cv2.accumulateWeighted(frame,movingaverage,alpha)
gaussion = background.apply(frame)
res = cv2.convertScaleAbs(movingaverage)
difference_img = cv2.absdiff(res, frame)
grey_difference_img = cv2.cvtColor(difference_img, cv2.COLOR_BGR2GRAY)
ret,th1 = cv2.threshold(grey_difference_img, 10, 255, cv2.THRESH_BINARY)
combine = cv2.bitwise_and(gaussion, gaussion, mask = grey_difference_img)
_, conts, h1 =cv2.findContours(combine.copy(), cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_NONE)
if len(conts) == 0:
cv2.putText(frame,"No moving objects found!",(50,200), font, 1,(255,255,255),2,cv2.LINE_AA)
else:
number = 0
for i in range(len(conts)):
x,y,w,h = cv2.boundingRect(conts[i])
if (w > 50) and (h > 50):
cv2.rectangle(frame,(x,y),(x+w,y+h),(0,0,255), 2)
cv2.putText(frame,str(number +1)+ "object",(x,y+h), font, 1,(255,255,255),2,cv2.LINE_AA)
number = number + 1
cv2.imshow("Gaussian",gaussion)
cv2.imshow("Track",frame)
if cv2.waitKey(1) == 27:
break
device.release()
cv2.destroyAllWindows()
I have an idea but I'm not sure about that. Now I have modified the code to detect center of each and every contour. So, can I store the information about the coordinates to an array, then check the new frame contours center point such that close to array values. Then try to guess the contour which stored in array in previous frame. I don't know the possibility of this since I'm new to Python and OpenCV.
if (w > 50) and (h > 50):
cnt = conts[i]
M = cv2.moments(cnt)
if M['m00'] != 0:
cx = int(M['m10']/M['m00'])
cy = int(M['m01']/M['m00'])
#draw a circle at center of contours.
cv2.circle(frame,(cx,cy), 2, (0,0,255), -1)
print( "(",cx,",",cy,")" )
cv2.rectangle(frame,(x,y),(x+w,y+h),(0,0,255), 2)
cv2.putText(frame,str(number +1)+ "object",(x,y+h), font, 1,(255,255,255),2,cv2.LINE_AA)
I have edited my code. Now it works fine if the moving objects are in the same horizontal plane. What I did was, create list called matched_contours and paths. Then append calculated center points into paths which in element of matched_contours. And used min_dist to check whether contours has presented in previous frame or not. If it has presented I have updated new center points in matched_contour. If not, I took it as a new matched_contour.
THIS IS THE UPDATED PART
im2, contours, hierarchy = cv2.findContours(dilation, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_TC89_L1)
for (_, contour) in enumerate(contours):
(x, y, w, h) = cv2.boundingRect(contour)
if (w >= 50) or (h >= 50): # Find targeting size moving objects
x1 = int(w / 2)
y1 = int(h / 2)
cx = x + x1
cy = y + y1
if len(matched_contours) == 0: #No previous moving objects
paths = []
paths.append((cx,cy))
object_num = object_num + 1
matched_contours.append(((x, y, w, h), (cx,cy), object_num, paths))
cv2.circle(frame,(cx,cy), 2, (0,0,255), -1)
#cv2.rectangle(frame,(x,y),(x+w,y+h),(0,0,255), 2)
rect = cv2.minAreaRect(contour)
box = cv2.boxPoints(rect)
box = np.int0(box)
cv2.drawContours(frame,[box],0,(0,255,0),2)
cv2.putText(frame,"object " + str(object_num),(x,y+h), font, 0.5,(255,255,255),1,cv2.LINE_AA)
else:
found = False
for i in range(len(matched_contours)):
ponits, center, num, path = matched_contours[i]
old_cx, old_cy = center
#Calculate euclidian distances between new center and old centers to check this contour is existing or not
euc_dist = math.sqrt(float((cx - old_cx)**2) + float((cy - old_cy)**2))
if euc_dist <= min_dist:
if len(path) == max_path_length:
for t in range(len(path)):
if t == max_path_length - 1:
path[t] = (cx,cy)
else:
path[t] = path[t+1]
else:
path.append((cx,cy))
matched_contours[i] = ((x, y, w, h), (cx,cy), num, path)
cv2.circle(frame,(cx,cy), 2, (0,0,255), -1)
#cv2.rectangle(frame,(x,y),(x+w,y+h),(0,0,255), 2)
rect = cv2.minAreaRect(contour)
box = cv2.boxPoints(rect)
box = np.int0(box)
cv2.drawContours(frame,[box],0,(0,255,0),2)
cv2.putText(frame,"object " + str(num),(x,y+h), font, 0.5,(255,255,255),1,cv2.LINE_AA)
#Draw path of the moving object
for point in range(len(path)-1):
cv2.line(frame, path[point], path[point+1], (255,0,0), 1)
cv2.circle(frame,path[point+1], 3, (0,0,255), -1)
if not found: #New moving object has found
object_num = object_num + 1
paths = []
paths.append((cx,cy))
matched_contours.append(((x, y, w, h), (cx,cy), object_num, paths))
cv2.circle(frame,(cx,cy), 2, (0,0,255), -1)
#cv2.rectangle(frame,(x,y),(x+w,y+h),(0,0,255), 2)
rect = cv2.minAreaRect(contour)
box = cv2.boxPoints(rect)
box = np.int0(box)
cv2.drawContours(frame,[box],0,(0,255,0),2)
cv2.putText(frame,"object " + str(object_num),(x,y+h), font, 0.5,(255,255,255),1,cv2.LINE_AA)

Labeled unique contours (or draw independient contours)

Im working in python and opencv library.
I can threshold a camera capture, find contours (more than one) and draw.
But I have a problem. I try to identify those contours with an unique id or tag. (for example: Id: 1 , Id:2) to track them.
I need this contours use a persistent id.
The goal is draw a line and count more than one contour and sometimes more than one near contours converts in a big one.
Note: Im working with a depth camera and my image its an array of depth.
add a piece of code.
Thanks in advance.
countours = cv2.findContours(mask, cv2.RETR_EXTERNAL,
cv2.CHAIN_APPROX_SIMPLE)[1]
# only proceed if at least one contour was found
if len(countours) > 0:
# find the largest contour in the mask, then use
# it to compute the minimum enclosing circle and
# centroid
for (i,c) in enumerate(countours):
((x, y), radius) = cv2.minEnclosingCircle(c)
M = cv2.moments(c)
if M["m00"] > 0:
center = (int(M["m10"] / M["m00"]), int(M["m01"] / M["m00"]))
centerString = str(center)
x = (int(M["m10"] / M["m00"]))
y = (int(M["m01"] / M["m00"]))
else:
center = int(x), int(y)
if radius > 10:
# draw the circle and centroid on the frame,
cv2.circle(frame, (int(x), int(y)), int(radius),
(0, 255, 255), 2)
cv2.circle(frame, center, 5, (0, 0, 255), -1)
# then update the ponter trail
if self.previous_position:
cv2.line(self.trail, self.previous_position, center,
(255, 255, 255), 2)
cv2.add(self.trail, frame, frame)
print center
self.previous_position = center
if len(countours) < 1:
center = 0
self.trail = numpy.zeros((self.cam_height, self.cam_width, 3),
numpy.uint8)
self.previous_position = None
Two options. First off, the contours are already in a Python list, so the indices of that list can be used to enumerate them. In fact you're already doing that in some sense with (i,c) in enumerate(countours). You can just use the index i to 'color' each contour with the value i drawing on a blank image, and then you'll know which contour is which just by examining the image. The other, probably better option IMO, is to use cv2.connectedComponents() to label the binary images instead of the contours of the binary image. Also pre-labeling you might try morphological operations to close up blobs.
EDIT: I follow your recommendation and I have new problems :)
Label is not persistent. When I move both contours, numbers of labels change.
Add some pictures and my code.
Hand label 0 and circle 1
Hand label 1 and circle 0
while True:
cnt += 1
if (cnt % 10) == 0:
now = time.time()
dt = now - last
fps = 10/dt
fps_smooth = (fps_smooth * smoothing) + (fps * (1.0-smoothing))
last = now
c = dev.color
cad = dev.cad
dev.wait_for_frames()
c = cv2.cvtColor(c, cv2.COLOR_RGB2GRAY)
depth = dev.depth * dev.depth_scale * 1000
print depth
#d = cv2.applyColorMap(depth.astype(np.uint8), cv2.COLORMAP_SUMMER)
print depth
res = np.float32(dev.depth)
depth = 255 * np.logical_and(depth >= 100, depth <= 500)
res2 = depth.astype(np.uint8)
kernel = np.ones((3, 3), np.uint8)
#res2 = cv2.blur(res2,(15,15))
res2 = cv2.erode(res2, kernel, iterations=4)
res2 = cv2.dilate(res2, kernel, iterations=8)
im_floodfill = res2.copy()
h, w = res2.shape[:2]
mask2 = np.zeros((h + 2, w + 2), np.uint8)
cv2.floodFill(im_floodfill, mask2, (0, 0), 255)
im_floodfill_inv = cv2.bitwise_not(im_floodfill)
im_out = res2 | im_floodfill_inv
im_out = cv2.blur(im_out,(5,5))
label = cv2.connectedComponentsWithStats(im_out)
n = label[0] - 1
cog = np.delete(label[3], 0, 0)
for i in xrange(n):
# im = cv2.circle(im,(int(cog[i][0]),int(cog[i][1])), 10, (0,0,255), -1)
cv2.putText(im_out, str(i), (int(cog[i][0]), int(cog[i][1])), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 255), 1)
cv2.imshow("Depth", res)
cv2.imshow("OUT", im_out)
cv2.imshow( "C", res2)

Categories