I’m working on motion detection using video frames background subtraction. I already reached a good result by filtering detected motions area using thresholds where I keep only reasonable motions by their areas.
As below code shows, only contours with areas from 500 till 5,000 are passed.
backSub = cv2.createBackgroundSubtractorMOG2() capture = cv2.VideoCapture('./vtest.avi') log =  while True: _, frame = capture.read() if frame is None: break fgMask = backSub.apply(frame) kernel = np.ones((3,3),np.uint8) fgMask = cv2.erode(((fgMask>150)*255).astype(np.uint8),kernel,iterations = 1) _fgMask = np.zeros_like(frame) contours, hierarchy = cv2.findContours(fgMask,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_NONE)[-2:] for i, cnt in enumerate(contours): log.append(area) area =cv2.contourArea(cnt) if(area > 500 and _area < 5000): cv2.rectangle(_fgMask,(x,y),(x+w,y+h),(200,0,0),2) cv2.drawContours(_fgMask, contours, i, (0,0,255), -1) zkey = cv2.waitKey(1) & 0xFF if key == ord("q"): break capture.release() cv2.destroyAllWindows()
My challenge is to make those parameters (500 and 5,000) adaptive to different environments where the project will be applied on different locations, thus different parameters are needed. I tried having all areas as a list and apply a z-score noise removal using 3 and -3 thresholds, yet I didn’t get what I expected. The code below shows the area histogram where a noticeable peak placed between 500 and 4,000.
log = pd.DataFrame(log, columns=['area']) log['area_z'] = stats.zscore(log['area']) _log = log[(log['area_z']<25) & (log['area']>25)] _log = _log[['area']] _log['area'] = _log['area'] - (_log['area']%10) _log['cnt'] = 1 _log = _log.groupby(['area']).sum().reset_index() plt.plot(_log['area'], _log['cnt'])
My question is, how to get those area thresholds in an automated way to keep moving objects only.