AWa*_*inb 9 python webcam wxpython video-capture python-imaging-library
有没有人对如何在python中进行图像比较以检测图像中的变化有任何建议?我正在开发一个应用程序,用我的网络摄像头监视我的区域,我想弄清楚如何比较每帧拍摄的图像,看看是否有任何动作被检测到.从长远来看,我想设置一个灵敏度滑块,所以如果你能够指导我的方向,我相信我可以找到其余的.
我在这里看到一些关于将网络摄像头与wxPython集成的帖子,这是一个小型演示.请注意,我刚刚在昨晚开始使用,所以如果您正在寻找笔尖代码,您可能需要自己修改它(现在;):
要求:PIL和VideoCapture
#videocapturepanel.py
#Todo:
# - Fix background colour after video is stopped
# - Create image comparison method
# - Add capture function
# - Save stream to video file?
import threading, wx
from PIL import Image
from VideoCapture import Device
cam = Device(0)
buffer, width, height = cam.getBuffer()
cam.setResolution(width, height)
DEFAULT_DEVICE_INDEX = 0
DEFAULT_DEVICE_WIDTH = width
DEFAULT_DEVICE_HEIGHT = height
DEFAULT_BACKGROUND_COLOUR = wx.Colour(0, 0, 0)
class VideoCaptureThread(threading.Thread):
def __init__(self, control, width=DEFAULT_DEVICE_WIDTH, height=DEFAULT_DEVICE_HEIGHT, backColour=DEFAULT_BACKGROUND_COLOUR):
self.backColour = backColour
self.width = width
self.height = height
self.control = control
self.isRunning = True
self.buffer = wx.NullBitmap
threading.Thread.__init__(self)
def getResolution(self):
return (self.width, self.height)
def setResolution(self, width, height):
self.width = width
self.height = height
cam.setResolution(width, height)
def getBackgroundColour(self):
return self.backColour
def setBackgroundColour(self, colour):
self.backColour = colour
def getBuffer(self):
return self.buffer
def stop(self):
self.isRunning = False
def run(self):
while self.isRunning:
buffer, width, height = cam.getBuffer()
im = Image.fromstring('RGB', (width, height), buffer, 'raw', 'BGR', 0, -1)
buff = im.tostring()
self.buffer = wx.BitmapFromBuffer(width, height, buff)
x, y = (0, 0)
try:
width, height = self.control.GetSize()
if width > self.width:
x = (width - self.width) / 2
if height > self.height:
y = (height - self.height) / 2
dc = wx.BufferedDC(wx.ClientDC(self.control), wx.NullBitmap, wx.BUFFER_VIRTUAL_AREA)
dc.SetBackground(wx.Brush(self.backColour))
dc.Clear()
dc.DrawBitmap(self.buffer, x, y)
except TypeError:
pass
except wx.PyDeadObjectError:
pass
self.isRunning = False
class VideoCapturePanel(wx.Panel):
def __init__(self, parent, id=-1, pos=wx.DefaultPosition, size=wx.DefaultSize, initVideo=False, style=wx.SUNKEN_BORDER):
wx.Panel.__init__(self, parent, id, pos, size, style)
if initVideo:
self.StartVideo()
self.Bind(wx.EVT_CLOSE, self.OnClose)
def OnClose(self, event):
try:
self.Device.stop()
except:
pass
def StopVideo(self):
self.Device.stop()
self.SetBackgroundColour(self.Device.backColour)
dc = wx.BufferedDC(wx.ClientDC(self), wx.NullBitmap)
dc.SetBackground(wx.Brush(self.Device.backColour))
dc.Clear()
def StartVideo(self):
self.Device = VideoCaptureThread(self)
self.Device.start()
def GetBackgroundColour(self):
return self.Device.getBackgroundColour()
def SetBackgroundColour(self, colour):
self.Device.setBackgroundColour(colour)
class Frame(wx.Frame):
def __init__(self, parent, id=-1, title="A Frame", path="", pos=wx.DefaultPosition, size=wx.DefaultSize, style=wx.DEFAULT_FRAME_STYLE):
wx.Frame.__init__(self, parent, id, title, pos, size, style)
self.VidPanel = VideoCapturePanel(self, -1, initVideo=False)
self.StartButton = wx.ToggleButton(self, -1, "Turn On")
self.ColourButton = wx.Button(self, -1, "Change Background")
szr = wx.BoxSizer(wx.VERTICAL)
bszr = wx.BoxSizer(wx.HORIZONTAL)
bszr.Add(self.StartButton, 0, wx.ALIGN_CENTER_HORIZONTAL | wx.LEFT, 5)
bszr.Add(self.ColourButton, 0, wx.ALIGN_CENTER_HORIZONTAL)
szr.Add(self.VidPanel, 1, wx.EXPAND)
szr.Add(bszr, 0, wx.ALIGN_CENTER_HORIZONTAL)
self.SetSizer(szr)
self.StartButton.Bind(wx.EVT_TOGGLEBUTTON, self.OnToggled)
self.ColourButton.Bind(wx.EVT_BUTTON, self.OnColour)
def OnColour(self, event):
dlg = wx.ColourDialog(self)
dlg.GetColourData().SetChooseFull(True)
if dlg.ShowModal() == wx.ID_OK:
data = dlg.GetColourData()
self.VidPanel.SetBackgroundColour(data.GetColour())
dlg.Destroy()
def OnToggled(self, event):
if event.IsChecked():
self.VidPanel.StartVideo()
else:
self.VidPanel.StopVideo()
#self.VidPanel.SetBackgroundColour(data.GetColour())
if __name__ == "__main__":
# Run GUI
app = wx.PySimpleApp()
frame = Frame(None, -1, "Test Frame", size=(800, 600))
frame.Show()
app.MainLoop()
del app
Run Code Online (Sandbox Code Playgroud)
*UPDATE*
使用Paul的例子,我创建了一个类并将其实现到我的代码中:
class Images:
def __init__(self, image1, image2, threshold=98, grayscale=True):
self.image1 = image1
if type(image1) == str:
self.image1 = Image.open(self.image1)
self.image2 = image2
if type(image2) == str:
self.image2 = Image.open(image2)
self.threshold = threshold
def DoComparison(self, image1=None, image2=None):
if not image1: image1 = self.image1
if not image2: image2 = self.image2
diffs = ImageChops.difference(image1, image2)
return self.ImageEntropy(diffs)
def ImageEntropy(self, image):
histogram = image.histogram()
histlength = sum(histogram)
probability = [float(h) / histlength for h in histogram]
return -sum([p * math.log(p, 2) for p in probability if p != 0])
Run Code Online (Sandbox Code Playgroud)
然后将变量self.image = False添加到VideoCaptureThread的__init__()函数中,并在行im = Image.fromstring(...)之后将以下代码添加到VideoCaptureThread的run()函数中:
if self.image:
img = compare.Images2(im, self.image).DoComparison()
print img
self.image = im
Run Code Online (Sandbox Code Playgroud)
当我运行该示例时,它似乎工作正常,但我对我得到的结果有点困惑:
1.58496250072
5.44792407663
1.58496250072
5.44302784225
1.58496250072
5.59144486002
1.58496250072
5.37568050189
1.58496250072
Run Code Online (Sandbox Code Playgroud)
到目前为止,似乎所有其他图像都被关闭了很多,尽管变化很小?运行时的加法理论上应该捕获变量self.image下的前一个图像,并与新图像im进行比较.在比较之后,使用self.image = im将self.image更新为当前图像,那么为什么每个第二个图像都会有这样的差异?我的眼睛最多可能在两张图像中来回移动,我看不出这与我的结果有什么不同?
*更新2*
这是我到目前为止,有三个比较比较类,有三种不同的方法来检测运动.
class Images ~第一次尝试使用谷歌搜索时发现的一些代码,甚至不记得它是如何工作的.:P
class Images2 ~使用此线程的Paul代码创建,实现更新后的图像熵函数.
class Images3 ~在这里找到的DetectMotion功能的修改版本.(返回百分比已更改并且似乎考虑了照明)
说实话,我真的不知道他们在做什么,从字面上看,但我能说的是到目前为止,类Image3似乎是设置检测的最简单/准确的方法,垮台是需要更多的时间来处理其他两个班.
(请注意,为避免与scipy发生冲突,进行了一些导入更改,sys.modules ["Image"]与PIL.Image相同)
import math, sys, numpy as np
import PIL.Image, PIL.ImageChops
sys.modules["Image"] = PIL.Image
sys.modules["ImageChops"] = PIL.ImageChops
from scipy.misc import imread
from scipy.linalg import norm
from scipy import sum, average
DEFAULT_DEVICE_WIDTH = 640
DEFAULT_DEVICE_HEIGHT = 480
class Images:
def __init__(self, image1, image2, threshold=98, grayscale=True):
if type(image1) == str:
self.image1 = sys.modules["Image"].open(image1)
self.image2 = sys.modules["Image"].open(image2)
if grayscale:
self.image1 = self.DoGrayscale(imread(image1).astype(float))
self.image2 = self.DoGrayscale(imread(image2).astype(float))
else:
self.image1 = imread(image1).astype(float)
self.image2 = imread(image2).astype(float)
self.threshold = threshold
def DoComparison(self, image1=None, image2=None):
if image1: image1 = self.Normalize(image1)
else: image1 = self.Normalize(self.image1)
if image2: image2 = self.Normalize(image2)
else: image2 = self.Normalize(self.image2)
diff = image1 - image2
m_norm = sum(abs(diff))
z_norm = norm(diff.ravel(), 0)
return (m_norm, z_norm)
def DoGrayscale(self, arr):
if len(arr.shape) == 3:
return average(arr, -1)
else:
return arr
def Normalize(self, arr):
rng = arr.max()-arr.min()
amin = arr.min()
return (arr-amin)*255/rng
class Images2:
def __init__(self, image1, image2, threshold=98, grayscale=True):
self.image1 = image1
if type(image1) == str:
self.image1 = sys.modules["Image"].open(self.image1)
self.image2 = image2
if type(image2) == str:
self.image2 = sys.modules["Image"].open(image2)
self.threshold = threshold
def DoComparison(self, image1=None, image2=None):
if not image1: image1 = self.image1
if not image2: image2 = self.image2
diffs = sys.modules["ImageChops"].difference(image1, image2)
return self.ImageEntropy(diffs)
def ImageEntropy(self, image):
w,h = image.size
a = np.array(image.convert('RGB')).reshape((w*h,3))
h,e = np.histogramdd(a, bins=(16,)*3, range=((0,256),)*3)
prob = h/np.sum(h)
return -np.sum(np.log2(prob[prob>0]))
def OldImageEntropy(self, image):
histogram = image.histogram()
histlength = sum(histogram)
probability = [float(h) / histlength for h in histogram]
return -sum([p * math.log(p, 2) for p in probability if p != 0])
class Images3:
def __init__(self, image1, image2, threshold=8):
self.image1 = image1
if type(image1) == str:
self.image1 = sys.modules["Image"].open(self.image1)
self.image2 = image2
if type(image2) == str:
self.image2 = sys.modules["Image"].open(image2)
self.threshold = threshold
def DoComparison(self, image1=None, image2=None):
if not image1: image1 = self.image1
if not image2: image2 = self.image2
image = image1
monoimage1 = image1.convert("P", palette=sys.modules["Image"].ADAPTIVE, colors=2)
monoimage2 = image2.convert("P", palette=sys.modules["Image"].ADAPTIVE, colors=2)
imgdata1 = monoimage1.getdata()
imgdata2 = monoimage2.getdata()
changed = 0
i = 0
acc = 3
while i < DEFAULT_DEVICE_WIDTH * DEFAULT_DEVICE_HEIGHT:
now = imgdata1[i]
prev = imgdata2[i]
if now != prev:
x = (i % DEFAULT_DEVICE_WIDTH)
y = (i / DEFAULT_DEVICE_HEIGHT)
try:
#if self.view == "normal":
image.putpixel((x,y), (0,0,256))
#else:
# monoimage.putpixel((x,y), (0,0,256))
except:
pass
changed += 1
i += 1
percchange = float(changed) / float(DEFAULT_DEVICE_WIDTH * DEFAULT_DEVICE_HEIGHT)
return percchange
if __name__ == "__main__":
# image1 & image2 MUST be legit paths!
image1 = "C:\\Path\\To\\Your\\First\\Image.jpg"
image2 = "C:\\Path\\To\\Your\\Second\\Image.jpg"
print "Images Result:"
print Images(image1, image2).DoComparison()
print "\nImages2 Result:"
print Images2(image1, image2).DoComparison()
print "\nImages3 Result:"
print Images3(image1, image2).DoComparison()
Run Code Online (Sandbox Code Playgroud)
Pau*_*aul 10
这可能是一种天真的方法,但它是一个简单的开始.我相信你会受到相机噪音的影响,你可能想要区分灯光的变化和图像构成的变化.但这就是我想到的:
您可以使用PIL ImageChops有效地区分图像.然后,您可以获取该差异的熵以获得单值阈值.
它似乎工作:
from PIL import Image, ImageChops
import math
def image_entropy(img):
"""calculate the entropy of an image"""
# this could be made more efficient using numpy
histogram = img.histogram()
histogram_length = sum(histogram)
samples_probability = [float(h) / histogram_length for h in histogram]
return -sum([p * math.log(p, 2) for p in samples_probability if p != 0])
# testing..
img1 = Image.open('SnowCam_main1.jpg')
img2 = Image.open('SnowCam_main2.jpg')
img3 = Image.open('SnowCam_main3.jpg')
# No Difference
img = ImageChops.difference(img1,img1)
img.save('test_diff1.png')
print image_entropy(img) # 1.58496250072
# Small Difference
img = ImageChops.difference(img1,img2)
img.save('test_diff2.png')
print image_entropy(img) # 5.76452986917
# Large Difference
img = ImageChops.difference(img1,img3)
img.save('test_diff3.png')
print image_entropy(img) # 8.15698432026
Run Code Online (Sandbox Code Playgroud)
我相信这是一个更好的图像熵算法,因为它在颜色空间中三维地加入,而不是为每个波段创建单独的直方图.
编辑 - 此功能于2012年4月6日更改
import numpy as np
def image_entropy(img):
w,h = img.size
a = np.array(img.convert('RGB')).reshape((w*h,3))
h,e = np.histogramdd(a, bins=(16,)*3, range=((0,256),)*3)
prob = h/np.sum(h) # normalize
prob = prob[prob>0] # remove zeros
return -np.sum(prob*np.log2(prob))
Run Code Online (Sandbox Code Playgroud)
这些是我的测试图像:

图片1

图2

图3
| 归档时间: |
|
| 查看次数: |
7226 次 |
| 最近记录: |