ViBe运动目标检测python实现

代码实现参考官方开源C代码,仅实现了对灰度图的运动目标检测,即对应C代码中三个核心函数:

libvibeModel_Sequential_AllocInit_8u_C1R
libvibeModel_Sequential_Segmentation_8u_C1R
libvibeModel_Sequential_Update_8u_C1R

其中,Segmentation部分循环进行了相应的修改,没有对像素进行遍历逐一修改,而是通过获取mask,直接对图像矩阵进行修改;Update部分完全照着C代码改的python,判断语句太多了。

官网截图

全部代码:https://github.com/232525/ViBe.python

ViBe类文件

# -*- coding: utf-8 -*-
"""
Created on Sat Jul 18 17:43:58 2020
@filename: lib_vibe.py
@author: curya
"""

import numpy as np
import time

class vibe_gray:
    def __init__(self, ):
        self.width = 0
        self.height = 0
        self.numberOfSamples = 20
        self.matchingThreshold = 20
        self.matchingNumber = 2
        self.updateFactor = 16
        
        # Storage for the history
        self.historyImage = None
        self.historyBuffer = None
        self.lastHistoryImageSwapped = 0
        self.numberOfHistoryImages = 2
        
        # Buffers with random values
        self.jump = None
        self.neighbor = None
        self.position = None
        
    def AllocInit(self, image):
        print('AllocInit!')
        height, width = image.shape[:2]
        # set the parametors
        self.width = width
        self.height = height
        print(self.height, self.width)
        
        # create the historyImage
        self.historyImage = np.zeros((self.height, self.width, self.numberOfHistoryImages), np.uint8)
        for i in range(self.numberOfHistoryImages):
            self.historyImage[:, :, i] = image
            
        # create and fill the historyBuffer
        self.historyBuffer = np.zeros((self.height, self.width, self.numberOfSamples-self.numberOfHistoryImages), np.uint8)
        for i in range(self.numberOfSamples-self.numberOfHistoryImages):
            image_plus_noise = image + np.random.randint(-10, 10, (self.height, self.width))
            image_plus_noise[image_plus_noise > 255] = 255
            image_plus_noise[image_plus_noise < 0] = 0
            self.historyBuffer[:, :, i] = image_plus_noise.astype(np.uint8)
        
        # fill the buffers with random values
        size = 2 * self.width + 1 if (self.width > self.height) else 2 * self.height + 1
        self.jump = np.zeros((size), np.uint32)
        self.neighbor = np.zeros((size), np.int)
        self.position = np.zeros((size), np.uint32)
        for i in range(size):
            self.jump[i] = np.random.randint(1, 2*self.updateFactor+1)
            self.neighbor[i] = np.random.randint(-1, 3-1) + np.random.randint(-1, 3-1) * self.width
            self.position[i] = np.random.randint(0, self.numberOfSamples)
        
    
    def Segmentation(self, image):
        # segmentation_map init
        segmentation_map = np.zeros((self.height, self.width)) + (self.matchingNumber - 1)
        
        # first history image
        mask = np.abs(image - self.historyImage[:, :, 0]) > self.matchingThreshold
        segmentation_map[mask] = self.matchingNumber
        
        # next historyImages
        for i in range(1, self.numberOfHistoryImages):
            mask = np.abs(image - self.historyImage[:, :, i]) <= self.matchingThreshold
            segmentation_map[mask] = segmentation_map[mask] - 1
        
        # for swapping
        self.lastHistoryImageSwapped = (self.lastHistoryImageSwapped + 1) % self.numberOfHistoryImages
        swappingImageBuffer = self.historyImage[:, :, self.lastHistoryImageSwapped]
        
        # now, we move in the buffer and leave the historyImage
        numberOfTests = self.numberOfSamples - self.numberOfHistoryImages
        mask = segmentation_map > 0
        for i in range(numberOfTests):
            mask_ = np.abs(image - self.historyBuffer[:, :, i]) <= self.matchingThreshold
            mask_ = mask * mask_
            segmentation_map[mask_] = segmentation_map[mask_] - 1
            
            # Swapping: Putting found value in history image buffer
            temp = swappingImageBuffer[mask_].copy()
            swappingImageBuffer[mask_] = self.historyBuffer[:, :, i][mask_].copy()
            self.historyBuffer[:, :, i][mask_] = temp
        
        # simulate the exit inner loop
        mask_ = segmentation_map <= 0
        mask_ = mask * mask_
        segmentation_map[mask_] = 0
        
        # Produces the output. Note that this step is application-dependent
        mask = segmentation_map > 0
        segmentation_map[mask] = 255
        return segmentation_map.astype(np.uint8)
    
    def Update(self, image, updating_mask):
        numberOfTests = self.numberOfSamples - self.numberOfHistoryImages
        
        inner_time = 0
        t0 = time.time()
        # updating
        for y in range(1, self.height-1):
            t1 = time.time()
            shift = np.random.randint(0, self.width)
            indX = self.jump[shift]
            t2 = time.time()
            inner_time += (t2 - t1)
            #""" too slow
            while indX < self.width - 1:
                t3 = time.time()
                index = indX + y * self.width
                t4 = time.time()
                inner_time += (t4 - t3)
                if updating_mask[y, indX] == 255:
                    t5 = time.time()
                    value = image[y, indX]
                    index_neighbor = index + self.neighbor[shift]
                    y_, indX_ = int(index_neighbor / self.width), int(index_neighbor % self.width)
                    
                    if self.position[shift] < self.numberOfHistoryImages:
                        self.historyImage[y, indX, self.position[shift]] = value
                        self.historyImage[y_, indX_, self.position[shift]] = value
                    else:
                        pos = self.position[shift] - self.numberOfHistoryImages
                        self.historyBuffer[y, indX, pos] = value
                        self.historyBuffer[y_, indX_, pos] = value
                    t6 = time.time()
                    inner_time += (t6 - t5)
                t7 = time.time()
                shift = shift + 1
                indX = indX + self.jump[shift]
                t8 = time.time()
                inner_time += (t8 - t7)
            #"""
        t9 = time.time()
        # print('update: %.4f, inner time: %.4f' % (t9 - t0, inner_time))
        
        # First row
        y = 0
        shift = np.random.randint(0, self.width)
        indX = self.jump[shift]
        
        while indX <= self.width - 1:
            index = indX + y * self.width
            if updating_mask[y, indX] == 0:
                if self.position[shift] < self.numberOfHistoryImages:
                    self.historyImage[y, indX, self.position[shift]] = image[y, indX]
                else:
                    pos = self.position[shift] - self.numberOfHistoryImages
                    self.historyBuffer[y, indX, pos] = image[y, indX]
            
            shift = shift + 1
            indX = indX + self.jump[shift]
            
        # Last row
        y = self.height - 1
        shift = np.random.randint(0, self.width)
        indX = self.jump[shift]
        
        while indX <= self.width - 1:
            index = indX + y * self.width
            if updating_mask[y, indX] == 0:
                if self.position[shift] < self.numberOfHistoryImages:
                    self.historyImage[y, indX, self.position[shift]] = image[y, indX]
                else:
                    pos = self.position[shift] - self.numberOfHistoryImages
                    self.historyBuffer[y, indX, pos] = image[y, indX]
            
            shift = shift + 1
            indX = indX + self.jump[shift]
        
        # First column
        x = 0
        shift = np.random.randint(0, self.height)
        indY = self.jump[shift]
        
        while indY <= self.height - 1:
            index = x + indY * self.width
            if updating_mask[indY, x] == 0:
                if self.position[shift] < self.numberOfHistoryImages:
                    self.historyImage[indY, x, self.position[shift]] = image[indY, x]
                else:
                    pos = self.position[shift] - self.numberOfHistoryImages
                    self.historyBuffer[indY, x, pos] = image[indY, x]
            
            shift = shift + 1
            indY = indY + self.jump[shift]
            
        # Last column
        x = self.width - 1
        shift = np.random.randint(0, self.height)
        indY = self.jump[shift]
        
        while indY <= self.height - 1:
            index = x + indY * self.width
            if updating_mask[indY, x] == 0:
                if self.position[shift] < self.numberOfHistoryImages:
                    self.historyImage[indY, x, self.position[shift]] = image[indY, x]
                else:
                    pos = self.position[shift] - self.numberOfHistoryImages
                    self.historyBuffer[indY, x, pos] = image[indY, x]
            
            shift = shift + 1
            indY = indY + self.jump[shift]
            
        # The first pixel
        if np.random.randint(0, self.updateFactor) == 0:
            if updating_mask[0, 0] == 0:
                position = np.random.randint(0, self.numberOfSamples)
                
                if position < self.numberOfHistoryImages:
                    self.historyImage[0, 0, position] = image[0, 0]
                else:
                    pos = position - self.numberOfHistoryImages
                    self.historyBuffer[0, 0, pos] = image[0, 0]

# TO DO
class vibe_rgb:
    def __init__(self, ):
        pass
    
    def init(self, ):
        pass
    
    def segmentation(self, ):
        pass
    
    def update(self, ):
        pass

调用主函数文件

# -*- coding: utf-8 -*-
"""
Created on Sat Jul 18 17:43:58 2020
@filename: vibe_demo.py
@author: curya
"""

import numpy as np
import cv2
import time
from lib_vibe import vibe_gray

cap = cv2.VideoCapture('./TownCentreXVID.avi')
vibe = vibe_gray()


frame_index = 0
segmentation_time = 0
update_time = 0
t1 = time.time()
while True:
    ret, frame = cap.read()
    if not ret:
        break
    gray_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
    if frame_index % 100 == 0:
        print('Frame number: %d' % frame_index)
        
    if frame_index == 0:
        vibe.AllocInit(gray_frame)
    
    t2 = time.time()
    segmentation_map = vibe.Segmentation(gray_frame)
    t3 = time.time()
    vibe.Update(gray_frame, segmentation_map)
    t4 = time.time()
    segmentation_time += (t3-t2)
    update_time += (t4-t3)
    print('Frame %d, segmentation: %.4f, updating: %.4f' % (frame_index, t3-t2, t4-t3))
    segmentation_map = cv2.medianBlur(segmentation_map, 3)
    
    cv2.imshow('Actual Frame!', frame)  
    cv2.imshow('Gray Frame!', gray_frame)
    cv2.imshow('Segmentation Frame!', segmentation_map)
    frame_index += 1
    if cv2.waitKey(1) & 0xFF == ord('q'):                                 # Break while loop if video ends
        break
t5 = time.time()    
print('All time cost %.3f' % (t5-t1))
print('segmentation time cost: %.3f, update time cost: %.3f' % (segmentation_time, update_time))

cap.release()
cv2.waitKey()
cv2.destroyAllWindows()

运行

python vibe_demo.py

问题

和C代码比较,效率就是个弟弟,跑分辨率不是太高的视频勉强凑合,分辨率太高的视频就真的干看着了,不过和我找的几份别人用python实现的ViBe代码比较,感觉还算是快的。

其次就是发现效率的硬伤之后,也就没去检查代码逻辑是不是和C代码一致,反正是可以检测目标。

还是想办法用Cython复用C代码?我太难了。。。。。。好不容易照着别人的东西造了个轮子,结果轮子不是很圆。:-( Sad

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,874评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,102评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,676评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,911评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,937评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,935评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,860评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,660评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,113评论 1 308
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,363评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,506评论 1 346
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,238评论 5 341
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,861评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,486评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,674评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,513评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,426评论 2 352

推荐阅读更多精彩内容