Aleksandar Aleksandar - 18 days ago 5
Python Question

OpenCV detect movement in python

My goal is to detect movement in specific region on IP camera stream. I managed to write working code, but it's based on my personal understanding.

import cv2
import numpy as np
import os
import time
import datetime
import urllib
import pynotify

stream=urllib.urlopen('http://user:pass@192.168.198.120/video.mjpg')
bytes=''
fgbg = cv2.createBackgroundSubtractorMOG2()

while True:
bytes+=stream.read(16384)
a = bytes.find('\xff\xd8')
b = bytes.find('\xff\xd9')
if a!=-1 and b!=-1:
jpg = bytes[a:b+2]
bytes= bytes[b+2:]
img = cv2.imdecode(np.fromstring(jpg, dtype=np.uint8),cv2.IMREAD_COLOR)
rows,cols,c = img.shape
mask = np.zeros(img.shape, dtype=np.uint8)
roi_corners = np.array([[(940,220),(1080,240), (1080,310), (940,290)]], dtype=np.int32)
channel_count = img.shape[2]
ignore_mask_color = (255,)*channel_count
cv2.fillPoly(mask, roi_corners, ignore_mask_color)
masked_image = cv2.bitwise_and(img, mask)

fgmask = fgbg.apply(masked_image)
iii = fgmask[220:310,940:1080]

hist,bins = np.histogram(iii.ravel(),256,[0,256])

black, white, cnt1, cnt2 = 0,0,0,0


for i in range(0,127):
black += hist[i]
cnt1+=1
bl = float(black / cnt1)

for i in range(128,256):
white += hist[i]
cnt2+=1
wh = float(white / cnt2)

finalResult = ((bl+1) / (wh+1))/10

if finalResult < 1.0:
pynotify.init("cv2alert")
notice = pynotify.Notification('Alert', 'Alert text')
try:
notice.show()
except gio.Error:
print "Error"


This code works, but as I don't understand histograms so well, I didn't managed to get values directly, but with some "hacks" like left side of histogram is black, right is white, and
black / white
gives the results I want. I know that this is not quite right, but it gives me the result of 4-9 when none is in ROI and result of 0.5-2.0 when someone enters this ROI.

My question here is:
Is there some other way to read histogram and compare data, or some other method? Reading documentation does not helps me.

Answer

Differential Images are the result of the subtraction of two images

So differential image shows the difference between two images. With those images you can make movement visible.

In following script we use a differential image calculated from three consecutive images , and . The advantage of this is that the uninteresting background is removed from the result.

OpenCV offers the possibility to subtract two images from each other using absdiff(). Also logical operations on two images is already implemented. We use the method bitwise_and() to achieve the final differential image. In python it looks like this:

def diffImg(t0, t1, t2):
  d1 = cv2.absdiff(t2, t1)
  d2 = cv2.absdiff(t1, t0)
  return cv2.bitwise_and(d1, d2)

The last thing we have to do is bringing the differential image function into our previous script. Before the loop starts we read the first three images t_minus, t and t_plus and convert them into greyscale images as we dont need color information. With those images it is possible to start calculating differential images. After showing the differential image, we just have to get rid of the oldest image and read the next one. The final script looks like this:

import cv2

def diffImg(t0, t1, t2):
  d1 = cv2.absdiff(t2, t1)
  d2 = cv2.absdiff(t1, t0)
  return cv2.bitwise_and(d1, d2)

cam = cv2.VideoCapture(0)

winName = "Movement Indicator"
cv2.namedWindow(winName, cv2.CV_WINDOW_AUTOSIZE)

# Read three images first:
t_minus = cv2.cvtColor(cam.read()[1], cv2.COLOR_RGB2GRAY)
t = cv2.cvtColor(cam.read()[1], cv2.COLOR_RGB2GRAY)
t_plus = cv2.cvtColor(cam.read()[1], cv2.COLOR_RGB2GRAY)

while True:
  cv2.imshow( winName, diffImg(t_minus, t, t_plus) )

  # Read next image
  t_minus = t
  t = t_plus
  t_plus = cv2.cvtColor(cam.read()[1], cv2.COLOR_RGB2GRAY)

  key = cv2.waitKey(10)
  if key == 27:
    cv2.destroyWindow(winName)
    break

print "Goodbye"

Here you will find more elaborative answer, for what you are looking for.