Как обнаружить движение между двумя изображениями PIL? (включая пример интеграции веб-камеры wxPython)

Есть ли у кого-нибудь какие-либо предложения относительно того, как я мог бы сравнивать изображения в 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 

* ОБНОВЛЕНИЕ *

используя пример Павла, я создал класс и внедрил его в свой код:

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

а затем добавила переменную self.image = False в функцию __init__() VideoCaptureThread и добавила ниже код в функцию run () VideoCaptureThread после строки im = Image.fromstring (…) :

  if self.image: img = compare.Images2(im, self.image).DoComparison() print img self.image = im 

Когда я запускаю образец, он работает нормально, но я немного смущен результатами, которые получаю:

 1.58496250072 5.44792407663 1.58496250072 5.44302784225 1.58496250072 5.59144486002 1.58496250072 5.37568050189 1.58496250072 

До сих пор кажется, что любое другое изображение отключено совсем немного, хотя изменения минимальны? Добавление к запуску должно теоретически захватить предыдущее изображение под переменной self.image и сравнить с новым изображением im . После сравнения self.image обновляется до текущего изображения, используя self.image = im , так почему же такая разница в каждом втором изображении? В лучшем случае мои глаза могли бы сдвинуться назад / вперед в пределах двух изображений, и я не могу видеть, что вызывает такой различие с моими результатами?

* ОБНОВЛЕНИЕ 2 *

Вот что я до сих пор, есть три класса сравнения сравнения с тремя различными методами для обнаружения движения.

class Images ~ Первая попытка использования некоторого кода, который я нашел во время поиска в Google, даже не помню, как это работает tbh. :П

class Images2 ~ Создан с использованием кода Пола из этой темы , реализуя его обновленную функцию энтропии изображения.

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() 

One Solution collect form web for “Как обнаружить движение между двумя изображениями PIL? (включая пример интеграции веб-камеры wxPython)”

Это может быть наивный подход, но это простое место для начала. Я уверен, что на вас повлияет шум камеры, и вы можете отличить изменения в освещении от изменений в составе изображения. Но вот что мне пришло в голову:

Вы можете использовать PIL ImageChops для эффективной разницы между изображениями. Затем вы можете взять энтропию этого diff, чтобы получить порог с одним значением.

Кажется, что это работает:

 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 

Это, я считаю, гораздо лучший алгоритм для энтропии изображения, поскольку он помещает 3-мерное пространство в цветовом пространстве, а не создает отдельную гистограмму для каждой группы.

EDIT – эта функция была изменена 6 апреля 2012 г.

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

Это мои тестовые изображения:

введите описание изображения здесь

Изображение 1

введите описание изображения здесь

Изображение 2

введите описание изображения здесь

Изображение 3

  • вишневый и wxpython
  • Невозможно импортировать wx (Python) в Mac OS X
  • Использование wxPython для ввода данных от пользователя
  • Как сохранить ввод текстового поля пользователя, когда пользователь нажимает «ok» на python?
  • Уведомления о всплывающих окнах wxPython
  • Какое событие я должен привязать до включения кнопки в wxPython?
  • Блокнот wxpython внутри коробки
  • Чтение изображения из буфера обмена с помощью wxPython
  • Как перенаправить регистратор на wxPython textCtrl с помощью специального обработчика ведения журнала?
  • Лучший способ получить название кнопки, которая называется событием?
  • wxPython: Как узнать, какой виджет имеет фокус?
  •  
    Interesting Posts for Van-Lav

    pd.get_dummies () медленно на больших уровнях

    Почему в Python отсутствует явная проверка пустоты (например, `is Empty`)

    Как я могу заменить определенную строку в строке в Python?

    Найти и sub в одной строке, используя Python re

    Как получить URL-адрес неудачных попыток?

    Многопоточный веб-скребок с использованием urlretrieve на сайте с поддержкой cookie

    python итерации над словарями

    Преобразование имен файлов python в unicode

    Является ли Python 'sys.argv' ограниченным в максимальном количестве аргументов?

    есть ли способ запустить приложение для бутылок в режиме демона

    Объединить несколько csv с помощью python

    imaplib.select на большой почтовый ящик: слишком много аргументов для команды

    Последовательный порт не работает в перезаписанном коде Python

    Преобразовать \ r текст в \ n, так что readlines () работает по назначению

    Попытайтесь поместить несколько элементов из начала и конца списка

    Python - лучший язык программирования в мире.