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.

enter image description here

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:
    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):
        area =cv2.contourArea(cnt)
        if(area > 500 and _area < 5000):
            cv2.drawContours(_fgMask, contours, i, (0,0,255), -1)
        zkey = cv2.waitKey(1) & 0xFF
        if key == ord("q"):

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'])

enter image description here

My question is, how to get those area thresholds in an automated way to keep moving objects only.


Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.