I'm new to image processing and I'm struggling a bit, I'm making my own diy security software and I made a function to detect some movement in order to start recording and notify me.
The idea of this function is to take two images and diff them in order to find some movement, the problem I have is that either :
- The detection is working really fine but at night there's some noise on the image and even daily in the shadows which trigger a positive detection wrongly
- The function is not wrongly triggered but it miss some detections
The way I tried the option 2 is through the commented code, main ideas was to
- divide gray images and blur
- replace the basic threshold by an adaptative one (gaussian and mean)
Here is my code :
import cv2
import numpy as np
from skimage.metrics import structural_similarity as ssim
def count_diff_nb(img_1, img_2):
# resize images
img_1_height, img_1_width = img_1.shape[:2]
new_height = int((600 / img_1_width) * img_1_height)
img_1 = cv2.resize(img_1, (600,new_height))
img_2 = cv2.resize(img_2, (600,new_height))
# convert to gray scale
gray_image1 = cv2.cvtColor(img_1, cv2.COLOR_BGR2GRAY)
gray_image2 = cv2.cvtColor(img_2, cv2.COLOR_BGR2GRAY)
# Gaussian blur in order to remove some noise
blur1 = cv2.GaussianBlur(gray_image1, (5,5), 0)
blur2 = cv2.GaussianBlur(gray_image2, (5,5), 0)
# divide (bad idea)
#divide1 = cv2.divide(gray_image1, blur1, scale=255)
#divide2 = cv2.divide(gray_image2, blur2, scale=255)
# Compute SSIM between two images
#ssim_value, diff = ssim(gray_image1, gray_image2, full=True)
ssim_value, diff = ssim(blur1, blur2, full=True)
#ssim_value, diff = ssim(divide1, divide2, full=True)
diff_percent = (1 - ssim_value) * 100
# The diff image contains the actual image differences between the two images
# and is represented as a floating point data type so we must convert the array
# to 8-bit unsigned integers in the range [0,255] before we can use it with OpenCV
diff = (diff * 255).astype("uint8")
# Adaptative threshold (bad idea too)
#thresh = cv2.adaptiveThreshold(diff, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY_INV, 11, 2)
#thresh = cv2.adaptiveThreshold(diff, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY_INV, 3, 10)
# Threshold the difference image
thresh = cv2.threshold(diff, 0, 255, cv2.THRESH_BINARY_INV | cv2.THRESH_OTSU)[1]
# followed by finding contours to
# obtain the regions that differ between the two images
contours = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
contours = contours[0] if len(contours) == 2 else contours[1]
# Highlight differences
mask = np.zeros(img_1.shape, dtype='uint8')
filled = img_2.copy()
contours_nb = 0
for c in contours:
# limit is an area so sqrt of size
area = cv2.contourArea(c)
# 72000 is 1/3 of global img area
if area > 2000 and area < 72000:
contours_nb = contours_nb + 1
x,y,w,h = cv2.boundingRect(c)
cv2.rectangle(img_1, (x, y), (x + w, y + h), (36,255,12), 2)
cv2.rectangle(img_2, (x, y), (x + w, y + h), (36,255,12), 2)
cv2.drawContours(mask, [c], 0, (0,255,0), -1)
cv2.drawContours(filled, [c], 0, (0,255,0), -1)
return contours_nb, diff_percent, img_2, filled
Do you have any ideas or things I'm missing in order to be able to find the sweetspot between sensibility (not miss detections) and ignoring random noise due to the darkness ?
I thought to ignore the dark colors before converting to grayscale but if the moving thing is black then .. it's a bad idea I think.
Thanks a lot !
Edit :
I changed the whole thing by implementing this solution suggested by @pippo1980. I use BackgroundSubtractorMOG2 which works the best in my case. (I tested the different options).
So it works almost perfectly, the last pain point is now at the sunrise and sunset, when my cheap webcam is struggling with noise and the image is a little blur / randomly noised.
I'm searching how to deal with this but I'm not sure.
Here's when it's working fine, you can see that the mask is really sharp :
And at sunset with the blur / noise on image :












I don't have any idea about what you are doing wrong but googling a bit you could find a lot of approaches. For example stolen from Moving Object Detection with OpenCV using Contour Detection and Background Subtraction, you could find a nice Flowchart of Object Detection Pipeline using OpenCV:
That mentions Background subtraction, not described in your algorithm, but I could be wrong I can't read OpenCV by earth. In the docs they describe one of this methods as:
And you could actually find about this method on OpenCV docs :
Apparently there are two of them
BackgroundSubtractorMOGandBackgroundSubtractorMOG2.They are actually 3 described here on SO too:
Differences between MOG, MOG2, and GMG