基于OpenCV的车辆计数(一)
全部代碼鏈接
首先效果圖:
語言:python + OpenCV
簡單的背景減差算法實現移動物體檢測。
學習目標:
1. 理解背景減差算法
2. OpenCV圖像濾波
3. 通過連通區域檢測目標
前景 = 當前幀 - 背景
? ? ? ? 但在某些情況下,我們不能得到靜態幀,因為光可以改變,或者一些物體會被某人移動,或者總是存在運動,等等。在這種情況下,我們保存了一些幀,并試圖找出其中大多數像素是相同的,然后這個像素變為背景的一部分。一般來說,我們如何得到這個背景,以及我們用來選擇更合適的濾波器。
在這個例子中,我們使用MOG(混合高斯)進行背景減差,效果:
可以看出一些噪聲,和陰影,我們使用標準的濾波器去移除他們。
import os import logging import logging.handlers import randomimport numpy as np import skvideo.io import cv2 import matplotlib.pyplot as pltimport utils # without this some strange errors happen cv2.ocl.setUseOpenCL(False) random.seed(123)# ============================================================================ IMAGE_DIR = "./out" VIDEO_SOURCE = "input.mp4" SHAPE = (720, 1280) # HxW # ============================================================================def train_bg_subtractor(inst, cap, num=500):'''BG substractor need process some amount of frames to start giving result'''print ('Training BG Subtractor...')i = 0for frame in cap:inst.apply(frame, None, 0.001)i += 1if i >= num:return capdef main():log = logging.getLogger("main")# creting MOG bg subtractor with 500 frames in cache# and shadow detctionbg_subtractor = cv2.createBackgroundSubtractorMOG2(history=500, detectShadows=True)# Set up image source# You can use also CV2, for some reason it not working for mecap = skvideo.io.vreader(VIDEO_SOURCE)# skipping 500 frames to train bg subtractortrain_bg_subtractor(bg_subtractor, cap, num=500)frame_number = -1for frame in cap:if not frame.any():log.error("Frame capture failed, stopping...")breakframe_number += 1utils.save_frame(frame, "./out/frame_%04d.png" % frame_number)fg_mask = bg_subtractor.apply(frame, None, 0.001)utils.save_frame(frame, "./out/fg_mask_%04d.png" % frame_number)# ============================================================================if __name__ == "__main__":log = utils.init_logging()if not os.path.exists(IMAGE_DIR):log.debug("Creating image directory `%s`...", IMAGE_DIR)os.makedirs(IMAGE_DIR)main()濾波
對于這個例子,我們用到了下列濾波器:
閾值、膨脹、腐蝕、開運算、閉運算
現在我們將使用它們來消除前景掩碼上的一些噪聲。
首先,我們將使用閉運算來消除區域的空隙,然后開運算以移除1到2個PX點,然后在膨脹處理使目標更大。
前景如下:
通過檢測連通區域進行目標檢測
我們使用cv2.findContours函數實現;
cv2.CV_RETR_EXTERNAL — get only outer contours. cv2.CV_CHAIN_APPROX_TC89_L1 - use Teh-Chin chain approximation algorithm (faster) def get_centroid(x, y, w, h):x1 = int(w / 2)y1 = int(h / 2)cx = x + x1cy = y + y1return (cx, cy)def detect_vehicles(fg_mask, min_contour_width=35, min_contour_height=35):matches = []# finding external contoursim, contours, hierarchy = cv2.findContours(fg_mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_TC89_L1)# filtering by with, heightfor (i, contour) in enumerate(contours):(x, y, w, h) = cv2.boundingRect(contour)contour_valid = (w >= min_contour_width) and (h >= min_contour_height)if not contour_valid:continue# getting center of the bounding boxcentroid = get_centroid(x, y, w, h)matches.append(((x, y, w, h), centroid))return matches我們通過高度、寬度和添加質心來添加一些過濾
封裝,算法實現一般流程:
class PipelineRunner(object):'''Very simple pipline.Just run passed processors in order with passing context from one to another.You can also set log level for processors.'''def __init__(self, pipeline=None, log_level=logging.DEBUG):self.pipeline = pipeline or []self.context = {}self.log = logging.getLogger(self.__class__.__name__)self.log.setLevel(log_level)self.log_level = log_levelself.set_log_level()def set_context(self, data):self.context = datadef add(self, processor):if not isinstance(processor, PipelineProcessor):raise Exception('Processor should be an isinstance of PipelineProcessor.')processor.log.setLevel(self.log_level)self.pipeline.append(processor)def remove(self, name):for i, p in enumerate(self.pipeline):if p.__class__.__name__ == name:del self.pipeline[i]return Truereturn Falsedef set_log_level(self):for p in self.pipeline:p.log.setLevel(self.log_level)def run(self):for p in self.pipeline:self.context = p(self.context)self.log.debug("Frame #%d processed.", self.context['frame_number'])return self.contextclass PipelineProcessor(object):'''Base class for processors.'''def __init__(self):self.log = logging.getLogger(self.__class__.__name__)作為輸入構造函數將采取將按順序運行的處理器列表。每個處理器都是這項工作的一部分。讓我們創建輪廓檢測處理器。
class ContourDetection(PipelineProcessor):'''Detecting moving objects.Purpose of this processor is to subtrac background, get moving objectsand detect them with a cv2.findContours method, and then filter off-bywidth and height. bg_subtractor - background subtractor isinstance.min_contour_width - min bounding rectangle width.min_contour_height - min bounding rectangle height.save_image - if True will save detected objects mask to file.image_dir - where to save images(must exist). '''def __init__(self, bg_subtractor, min_contour_width=35, min_contour_height=35, save_image=False, image_dir='images'):super(ContourDetection, self).__init__()self.bg_subtractor = bg_subtractorself.min_contour_width = min_contour_widthself.min_contour_height = min_contour_heightself.save_image = save_imageself.image_dir = image_dirdef filter_mask(self, img, a=None):'''This filters are hand-picked just based on visual tests'''kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (2, 2))# Fill any small holesclosing = cv2.morphologyEx(img, cv2.MORPH_CLOSE, kernel)# Remove noiseopening = cv2.morphologyEx(closing, cv2.MORPH_OPEN, kernel)# Dilate to merge adjacent blobsdilation = cv2.dilate(opening, kernel, iterations=2)return dilationdef detect_vehicles(self, fg_mask, context):matches = []# finding external contoursim2, contours, hierarchy = cv2.findContours(fg_mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_TC89_L1)for (i, contour) in enumerate(contours):(x, y, w, h) = cv2.boundingRect(contour)contour_valid = (w >= self.min_contour_width) and (h >= self.min_contour_height)if not contour_valid:continuecentroid = utils.get_centroid(x, y, w, h)matches.append(((x, y, w, h), centroid))return matchesdef __call__(self, context):frame = context['frame'].copy()frame_number = context['frame_number']fg_mask = self.bg_subtractor.apply(frame, None, 0.001)# just thresholding valuesfg_mask[fg_mask < 240] = 0fg_mask = self.filter_mask(fg_mask, frame_number)if self.save_image:utils.save_frame(fg_mask, self.image_dir +"/mask_%04d.png" % frame_number, flip=False)context['objects'] = self.detect_vehicles(fg_mask, context)context['fg_mask'] = fg_maskreturn contex所以只需將BG減法、濾波和檢測部分合并在一起即可。
現在讓我們創建一個處理器,它將鏈接到不同幀上的檢測對象,并創建路徑,也將計算到達出口區域的車輛。
?
總結
以上是生活随笔為你收集整理的基于OpenCV的车辆计数(一)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: redis win10开机自启
- 下一篇: 如何用企业微信做增长裂变?企业微信和个人