背景消除或背景減法是這樣一種假設。我們有2個圖片,一個是靜止的,比如場景,沒有需要檢測的東西,另一個照片則包含了要檢測的對象,但他是侵入了背景裡的東西,或對象。我們就是要檢測這個東西,比如商場進入的小偷,老鼠,或者馬路上通過的車輛。
利用背景減法,我們容易找到我們感興趣的東西。先看看下面2張圖片:
右邊圖片是我們的背景,左邊圖片是我們的結果,我們找到感興趣的部分,就是框起來的部分。框起來前就是我們對比的圖片,或者叫變化的圖片。有這個教授坐在椅子上的部分。
本程序除了opencv要安裝好外,還要裝好imutils。imutils的下載和安裝在 Python下應用opencv的簡單功能演示 一文中有介紹。
本文的原始代碼來自 https://www.pyimagesearch.com/2016/11/21/raspbian-opencv-pre-configured-and-pre-installed/ 的一個教學講稿。
代碼開始部分
註釋裡介紹使用方法:
python image_sub.py --bg 背景文件名 --fg 前景文件名
然後輸入必要的包,命令行參數處理,這裡有缺省參數,你可修改default 後的文件路徑和名
- # USAGE 使用方法
- # python image_sub.py --bg images/bg.jpg --fg images/adrian.jpg
-
- # import the necessary packages 输入必要的包
- import numpy as np
- import argparse
- import imutils
- import cv2
-
- # construct the argument parser and parse the arguments
- # 命令行参数处理,2个图片都存在imges 目录里,这里提供缺省值
- # 这根据你的情况,更改default 后的文件名,当然也可命令行输入
- ap = argparse.ArgumentParser()
- ap.add_argument("-b", "--bg", default='images/bg.jpg',
- help="path to background image")
- ap.add_argument("-f", "--fg", default='images/adrian.jpg',
- help="path to foreground image")
- args = vars(ap.parse_args())
複製代碼
導入圖片並灰度化處理
- # load the background and foreground images
- # 导入背景,前景文件
- bg = cv2.imread(args["bg"])
- fg = cv2.imread(args["fg"])
-
- # convert the background and foreground images to grayscale
- # 灰度化处理
- bgGray = cv2.cvtColor(bg, cv2.COLOR_BGR2GRAY)
- fgGray = cv2.cvtColor(fg, cv2.COLOR_BGR2GRAY)
複製代碼
背景減法
做減法時,轉換為int32,這樣可以有負值。然後取絕對值,再轉成類型uint8, opencv可以識別。
- # perform background subtraction by subtracting the foreground from
- # the background and then taking the absolute value
- # 背景减法
- sub = bgGray.astype("int32") - fgGray.astype("int32")
- sub = np.absolute(sub).astype("uint8")
- cv2.imshow("sub",sub)
複製代碼
二值化處理
用Otsu 門檻法,轉換上面的減法結果為前景和背景,0為背景,255為前景。圖片效果為下面左邊圖。
然後我們erosion,再dilate消除噪聲,處理效果為下面右邊圖:
erosion dilate的詳細介紹可以看:https://opencv-python-tutroals.readthedocs.io/en/latest/py_tutorials/py_imgproc/py_morphological_ops/py_morphological_ops.html
- # find contours in the thresholded difference map and then initialize
- # 发现边界
- # our bounding box regions that contains the *entire* region of motion
- cnts = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)
- cnts = imutils.grab_contours(cnts)
-
- #给边界初始值
- (minX, minY) = (np.inf, np.inf)
- (maxX, maxY) = (-np.inf, -np.inf)
-
- # loop over the contours
- # 循环计算边界
- for c in cnts:
- # compute the bounding box of the contour
- (x, y, w, h) = cv2.boundingRect(c)
-
- # reduce noise by enforcing requirements on the bounding box size
- # 如果边界值,w 或 w 小于20 就认为是噪音
- if w > 20 and h > 20:
- # update our bookkeeping variables
- minX = min(minX, x)
- minY = min(minY, y)
- maxX = max(maxX, x + w - 1)
- maxY = max(maxY, y + h - 1)
複製代碼
繪製長方形,並輸出圖形
- # draw a rectangle surrounding the region of motion
- # 绘制长方形
- cv2.rectangle(fg, (minX, minY), (maxX, maxY), (0, 255, 0), 2)
-
- # show the output image
- # 输出图形
- cv2.imshow("Output", fg)
- cv2.imshow("bg", bg)
- cv2.waitKey(0)
複製代碼
綜合在一起的代碼:
- # USAGE 使用方法
- # python image_sub.py --bg images/bg.jpg --fg images/adrian.jpg
-
- # import the necessary packages 输入必要的包
- import numpy as np
- import argparse
- import imutils
- import cv2
-
- # construct the argument parser and parse the arguments
- # 命令行参数处理,2个图片都存在imges 目录里,这里提供缺省值
- # 这根据你的情况,更改default 后的文件名,当然也可命令行输入
- ap = argparse.ArgumentParser()
- ap.add_argument("-b", "--bg", default='images/bg.jpg', help="path to background image")
- ap.add_argument("-f", "--fg", default='images/adrian.jpg', help="path to foreground image")
- args = vars(ap.parse_args())
-
- # load the background and foreground images
- # 导入背景,前景文件
- bg = cv2.imread(args["bg"])
- fg = cv2.imread(args["fg"])
-
- # convert the background and foreground images to grayscale
- # 灰度化处理
- bgGray = cv2.cvtColor(bg, cv2.COLOR_BGR2GRAY)
- fgGray = cv2.cvtColor(fg, cv2.COLOR_BGR2GRAY)
-
- # perform background subtraction by subtracting the foreground from
- # the background and then taking the absolute value
- # 背景减法
- sub = bgGray.astype("int32") - fgGray.astype("int32")
- sub = np.absolute(sub).astype("uint8")
- cv2.imshow("sub",sub)
-
- # threshold the image to find regions of the subtracted image with
- # larger pixel differences
- thresh = cv2.threshold(sub, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1]
- cv2.imshow("thresh",thresh)
-
- # perform a series of erosions and dilations to remove noise
- # erode ,dilate 降噪处理
- thresh = cv2.erode(thresh, None, iterations=1)
- thresh = cv2.dilate(thresh, None, iterations=1)
- cv2.imshow("thresh2",thresh)
-
- # find contours in the thresholded difference map and then initialize
- # 发现边界
- # our bounding box regions that contains the *entire* region of motion
- cnts = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
- cnts = imutils.grab_contours(cnts)
-
- #给边界初始值
- (minX, minY) = (np.inf, np.inf)
- (maxX, maxY) = (-np.inf, -np.inf)
-
- # loop over the contours
- # 循环计算边界
- for c in cnts:
- # compute the bounding box of the contour
- (x, y, w, h) = cv2.boundingRect(c)
-
- # reduce noise by enforcing requirements on the bounding box size
- # 如果边界值,w 或 w 小于20 就认为是噪音
- if w > 20 and h > 20:
- # update our bookkeeping variables
- minX = min(minX, x)
- minY = min(minY, y)
- maxX = max(maxX, x + w - 1)
- maxY = max(maxY, y + h - 1)
-
- # draw a rectangle surrounding the region of motion
- # 绘制长方形
- cv2.rectangle(fg, (minX, minY), (maxX, maxY), (0, 255, 0), 2)
-
- # show the output image
- # 输出图形
- cv2.imshow("Output", fg)
- cv2.imshow("bg", bg)
- cv2.waitKey(0)
複製代碼
文章出處 |