Saurabh Dawra
Saurabh Dawra

Reputation: 21

Occupancy counter using OpenCV

I have created an Occupancy counter using OpenCV. When I import a video, the code works fine and counts the people as they pass. But when I change the data input to a webcam, the count doesn't work properly. Sometimes it counts a person and sometimes it doesn't (Though it tracks them properly) and sometimes it counts one person as multiple. I am assuming that the algorithm is not fast enough to work on real time data input and the video input frames are somewhat less than the real time video input. Any ideas on how to resolve this issue?

Please find the minimal reproducible code below.

cap = cv.VideoCapture("C:\\Users\\USER_NAME\\Downloads\\example_01.mp4")

# Print the capture properties to console

for i in range(19):
    print( i, cap.get(i))

h = 480
w = 640

frameArea = h*w
areaTH = frameArea/250
print( 'Area Threshold', areaTH)

# Entry / exit lines
line_up = int(1*(h/5))
line_down   = int(2*(h/5))

up_limit =   int(0.5*(h/5))
down_limit = int(2.5*(h/5))

#Background Subtractor

fgbg = cv.createBackgroundSubtractorMOG2(detectShadows = True)
while(cap.isOpened()):
    ret, frame = cap.read()

try:
    ret,imBin= cv.threshold(fgmask,200,255,cv.THRESH_BINARY)
    ret,imBin2 = cv.threshold(fgmask2,200,255,cv.THRESH_BINARY)
    #Opening (erode-> dilate) to remove noise.
    mask = cv.morphologyEx(imBin, cv.MORPH_OPEN, kernelOp)
    mask2 = cv.morphologyEx(imBin2, cv.MORPH_OPEN, kernelOp)
    #Closing (dilate -> erode) to join white regions.
    mask =  cv.morphologyEx(mask , cv.MORPH_CLOSE, kernelCl)
    mask2 = cv.morphologyEx(mask2, cv.MORPH_CLOSE, kernelCl)
except:
    print('EOF')enter code here
    print( 'UP:',cnt_up)
    print ('DOWN:',cnt_down)
    break

contours0, hierarchy = 
cv.findContours(mask2,cv.RETR_EXTERNAL,cv.CHAIN_APPROX_SIMPLE)
for cnt in contours0:
    area = cv.contourArea(cnt)
    if area > areaTH:
        #################
        #   TRACKING    #
        #################
        
        M = cv.moments(cnt)
        cx = int(M['m10']/M['m00'])
        cy = int(M['m01']/M['m00'])
        x,y,w,h = cv.boundingRect(cnt)

        new = True
        if cy in range(up_limit,down_limit):
            for i in persons:
                if abs(x-i.getX()) <= w and abs(y-i.getY()) <= h:
                    # the object is close to one that has already been detected before
                    new = False
                    i.updateCoords(cx,cy)   # update coordinates in the object and resets age
                    if i.going_UP(line_down,line_up) == True:
                        cnt_up += 1;
                        print( "ID:",i.getId(),'crossed going up at',time.strftime("%c"))
                        log.write("ID: "+str(i.getId())+' crossed going up at ' + time.strftime("%c") + '\n')
                    elif i.going_DOWN(line_down,line_up) == True:
                        cnt_down += 1;
                        print( "ID:",i.getId(),'crossed going down at',time.strftime("%c"))
                        log.write("ID: " + str(i.getId()) + ' crossed going down at ' + time.strftime("%c") + '\n')
                    break
                if i.getState() == '1':
                    if i.getDir() == 'down' and i.getY() > down_limit:
                        i.setDone()
                    elif i.getDir() == 'up' and i.getY() < up_limit:
                        i.setDone()
                if i.timedOut():
                    #sacar i de la lista persons
                    index = persons.index(i)
                    persons.pop(index)
                    del i     #release the memory of i
            if new == True:
                p = Person1.MyPerson(pid,cx,cy, max_p_age)
                persons.append(p)
                pid += 1     
        
        cv.circle(frame,(cx,cy), 5, (0,0,255), -1)
        img = cv.rectangle(frame,(x,y),(x+w,y+h),(0,255,0),2)

Thanks and Regards

Saurabh

Upvotes: 0

Views: 347

Answers (1)

Dirnei
Dirnei

Reputation: 169

I'm not able to run your code, but if it is a problem with the realtime video feed, I have the following solution for you:

# Define the thread that will continuously pull frames from the camera
class CameraBufferCleanerThread(threading.Thread):
    def __init__(self, camera, name='camera-buffer-cleaner-thread'):
        self.camera = camera
        self.last_frame = None
        self.cancel = False
        super(CameraBufferCleanerThread, self).__init__(name=name)
        self.start()

    def run(self):
        while not self.cancel:
            ret, self.last_frame = self.camera.read()

This will create a thread and always pulls the latest frame and stores it in the last_frame property.

To use it, simply stuff the video capture in the above class and get the latest frame in your loop.

vcap = cv2.VideoCapture("rtsp://user:[email protected]:554/s0")
# Start the cleaning thread
cam_cleaner = CameraBufferCleanerThread(vcap)

while(1):
    if cam_cleaner.last_frame is None:
        continue

    image = cam_cleaner.last_frame.copy()

I had a similar problem and this solved it for me. It looks like this will also solve it for you.

Upvotes: 2

Related Questions