Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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:[email protected]/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.

like image 743
Aleksandar Avatar asked Nov 09 '16 19:11

Aleksandar


Video Answer


2 Answers

One way to detect movement is to keep a running average of your scene using cv2.accumulateWeighted. Then, compare every new frame to the average using cv2.absdiff to get the image that indicates changes in the scene.

I did exactly this in a video processing project of mine. Check out the main loop in file diffavg1.py where I run the accumulator and perform the diff.

(The research of the project was to achieve realtime video processing utilizing multi-core CPU architecture, so the later versions diffavg2.py, diffavg3.py and diffavg4.py are progressively higher performance implementations, but the underlying accumulate-diff algorithm is the same.)

like image 51
Velimir Mlaker Avatar answered Oct 14 '22 05:10

Velimir Mlaker


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.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.

like image 40
U.Swap Avatar answered Oct 14 '22 06:10

U.Swap