4 图像特征与目标检测
4.1 图像特征
图像特征是图像中独特的,易于跟踪和比较的特定模板或特定结构。图像特征主要有图像的颜色特征、纹理特征、形状特征和空间关系特
征。
4.1.1 形状特征
方向梯度直方图(Histogram of Oriented Gradient)HOG特征:其主要思想是在一副图像中,目标的形状能够被梯度或边缘的方向密度分布很好地描述。cv2.HOGDescriptor()
其计算步骤如下:
- 灰度化(避免光照等因素的影响);
- 采用Gamma校正法对输入图像进行颜色空间的标准化(归一化)
- 计算图像每个像素的梯度(包括大小和方向);
- 将图像划分成小cells,统计每个cell的梯度直方图(不同梯度的个数),得到cell的描述子;
- 将每几个cell组成一个block,得到block的描述子;
- 将图像image内的所有block的HOG特征descriptor串联起来就可以得到HOG特征,该特征向量就是用来目标检测或分类的特征。可以用SVM等分类器对图像分类。
代码实现:
import cv2
import numpy as np
# 判断矩形i是否完全包含在矩形o中
def is_inside(o, i):
ox, oy, ow, oh = o
ix, iy, iw, ih = i
return ox > ix and oy > iy and ox + ow < ix + iw and oy + oh < iy + ih
# 对人体绘制颜色框
def draw_person(image, person):
x, y, w, h = person
cv2.rectangle(image, (x, y), (x + w, y + h), (0, 255, 255), 2)
img = cv2.imread("people.jpg")
hog = cv2.HOGDescriptor() # 启动检测器对象
hog.setSVMDetector(cv2.HOGDescriptor_getDefaultPeopleDetector()) # 指定检测器类型为人体
# 输入参数:图像,阈值,步长(x,y)方向表示距离SVM超平面的距离
# 返回值:found记录box的坐标信息
found, w = hog.detectMultiScale(img,0.1,(1,1)) # 加载并检测图像
print("检测到的BOX坐标信息为:", found)
print(w)
# 丢弃某些完全被其它矩形包含在内的矩形
found_filtered = []
for ri, r in enumerate(found):
for qi, q in enumerate(found):
# 如果包含
if ri != qi and is_inside(r, q):
break
# 如果不包含
else:
found_filtered.append(r)
print('去除被包含的box后的坐标信息为:',found_filtered)
# 对不包含在内的有效矩形进行颜色框定
for person in found_filtered:
draw_person(img, person)
cv2.imshow("people detection", img)
cv2.waitKey(0)
cv2.destroyAllWindows()
结果:
Harris 角点检测:角点可以理解为物体的拐角。通常通过计算点的曲率及梯度来检测角点。cv2.cornerHarris()
计算步骤如下:
代码实现:
import cv2
import numpy as np
filename = 'harris2.png'
img = cv2.imread(filename)
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
gray = np.float32(gray)
# 输入图像必须是 float32 ,最后一个参数在 0.04 到 0.06 之间
# img : 数据类型为 float32 的输入图像
# blockSize: 角点检测中要考虑的领域大小
# ksize-Sobel: 求导中使用的窗口大小
# k-Harris: 角点检测方程中的自由参数,取值参数为 [0,04,0.06]
dst = cv2.cornerHarris(gray,2,7,0.06)
#结果进行膨胀,可有可无
dst = cv2.dilate(dst,None)
print(dst.shape,img.shape)
# 设定阈值,不同图像阈值不同
img[dst>0.01*dst.max()]=[0,0,255]
print(dst.max())
cv2.imshow('dst_img',img)
#cv2.imshow('dst',dst)
cv2.waitKey(0)
cv2.destroyAllWindows()
结果:
尺度不变特征变换算法(Scale-invariant feature transform)SIFT算法:SIFT具有尺度不变性,可在图像中检测出关键点,是一种局部特征描述子。可用于图像缝合,手势识别,动作对比等领域。具有• 旋转、缩放、平移不变性、 解决图像仿射变换,投影变换的关键的匹配、 光照影响小、 目标遮挡影响小、 噪声景物影响小。siftpute()
实现步骤:
- 尺度空间极值检测点检测;
- 关键点定位:去除一些不好的特征点,保存下来的特征点能够满足稳定性等条件;
- 关键点方向参数:获取关键点所在尺度空间的邻域,然后计算该区域的梯度和方向,根据计算得到的结果创建方向直方图,直方图的峰值为主方向的参数;
- 关键点描述符:每个关键点用一组向量(位置、尺度、方向)将这个关键点描述出来,使其不随着光照、视角等等影响而改变;
- 关键点匹配:分别对模板图和实时图建立关键点描述符集合,通过对比关键点描述符来判断两个关键点是否相同;
- 返回的关键点是一个带有很多不用属性的特殊结构体,属性当中有坐标,方向、角度等。
- 使用siftpute()函数来进行计算关键点描
述符kp,des = siftpute(gray,kp)
; - 如果未找到关键点,可使用函数
sift.detectAndCompute()
直接找到关键点并计算; - 在第二个函数中,kp为关键点列表,des为
numpy的数组,为关键点数目×128。
代码实现:(有专利保护)
import cv2
import numpy as np
img = cv2.imread('harris2.png')
gray= cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
sift = cv2.xfeatures2d.SIFT_create()
kp = sift.detect(gray,None)#找到关键点
img=cv2.drawKeypoints(gray,kp,img)#绘制关键点
cv2.imshow('sp',img)
cv2.waitKey(0)
cv2.destroyAllWindows()
4.1.2 纹理特征
LBP原理:是一种描述图像局部纹理特征的算子,具有旋转不变性,灰度不变性。计算的是中心性俗点与邻域像素点之间的差值,原理如下:
代码实现:
import cv2
import numpy as np
def LBP(src):
'''
:param src:灰度图像
:return:
'''
height = src.shape[0]
width = src.shape[1]
dst = src.copy()
lbp_value = np.zeros((1,8), dtype=np.uint8)
#print(lbp_value)
neighbours = np.zeros((1,8), dtype=np.uint8)
#print(neighbours)
for x in range(1, width-1):
for y in range(1, height-1):
neighbours[0, 0] = src[y - 1, x - 1] #左上
neighbours[0, 1] = src[y - 1, x] #上
neighbours[0, 2] = src[y - 1, x + 1] #右上
neighbours[0, 3] = src[y, x - 1] #左
neighbours[0, 4] = src[y, x + 1] #右
neighbours[0, 5] = src[y + 1, x - 1] #左下
neighbours[0, 6] = src[y + 1, x] #下
neighbours[0, 7] = src[y + 1, x + 1] #右下
center = src[y, x]
# 根据邻域值与中心值计算二进制
for i in range(8):
if neighbours[0, i] > center:
lbp_value[0, i] = 1
else:
lbp_value[0, i] = 0
# 转化为十进制值
lbp = lbp_value[0, 0] * 1 + lbp_value[0, 1] * 2 + lbp_value[0, 2] * 4 + lbp_value[0, 3] * 8 \
+ lbp_value[0, 4] * 16 + lbp_value[0, 5] * 32 + lbp_value[0, 6] * 64 + lbp_value[0, 7] * 128
#print(lbp)
dst[y, x] = lbp
return dst
img = cv2.imread('girl.jpg',0)
print(img.shape)
cv2.imshow('src',img)
cv2.waitKey(0)
new_img = LBP(img)
cv2.imshow('dst',new_img)
cv2.waitKey(0)
cv2.destroyAllWindows()
结果:
模板匹配:
代码实现:
import cv2
import numpy as np
def template_demo(tpl,target):
methods = [cv2.TM_SQDIFF_NORMED, cv2.TM_CCORR_NORMED, cv2.TM_CCOEFF_NORMED] #3种模板匹配方法
th, tw = tpl.shape[:2]
for md in methods:
result = cv2.matchTemplate(target, tpl, md)
min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(result)
if md == cv2.TM_SQDIFF_NORMED:
tl = min_loc
else:
tl = max_loc
br = (tl[0]+tw, tl[1]+th) #br是矩形右下角的点的坐标
cv2.rectangle(target, tl, br, (0, 0, 255), 2)
cv2.namedWindow("match-" + np.str(md), cv2.WINDOW_NORMAL)
cv2.imshow("match-" + np.str(md), target)
tpl =cv2.imread("sample2.jpg")
target = cv2.imread("target1.jpg")
cv2.waitKey(0)
cv2.destroyAllWindows()
cv2.namedWindow('template image', cv2.WINDOW_NORMAL)
cv2.imshow("template image", tpl)
cv2.namedWindow('target image', cv2.WINDOW_NORMAL)
cv2.imshow("target image", target)
template_demo(tpl,target)
cv2.waitKey(0)
cv2.destroyAllWindows()
结果:
4.1 人脸检测
cv2.CascadeClassifier(r'haarcascade_frontalface_default.xml')
faces为检测到人脸的坐标(矩形)
faces = face_cascade.detectMultiScale(gray, scaleFactor = 1.08, minNeighbors = 8, minSize = (2, 2))
代码:
import cv2
# 读入图像
img = cv2.imread("target1.jpg")
# 加载人脸特征,该文件在 python安装目录\Lib\site-packages\cv2\data 下
face_cascade = cv2.CascadeClassifier(r'haarcascade_frontalface_default.xml')
# 将读取的图像转为COLOR_BGR2GRAY,减少计算强度
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 检测出的人脸个数
faces = face_cascade.detectMultiScale(gray, scaleFactor = 1.08, minNeighbors = 8, minSize = (2, 2))
print("Face : {0}".format(len(faces)))
print(faces)
# 用矩形圈出人脸的位置
for(x, y, w, h) in faces:
cv2.rectangle(img, (x, y), (x + w, y + h), (0, 255, 0), 2)
cv2.namedWindow("Faces")
cv2.imshow("Faces", img)
cv2.waitKey(0)
cv2.destroyAllWindows()
结果:
5 视频目标识别
5.1 视频读取和存储
摄像头的调用
import numpy as np
import cv2
cap = cv2.VideoCapture(0)#开启摄像头
while(True):
#获取一帧帧图像
ret, frame = cap.read()
#转化为灰度图
#gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
cv2.imshow('frame',frame)
#按下“q”键停止
if cv2.waitKey(1) & 0xFF == ord('q'):
break
cap.release()
cv2.destroyAllWindows()
保存视频
更改视频格式
import numpy as np
import cv2
cap = cv2.VideoCapture('output_1.flv')
fourcc = cv2.VideoWriter_fourcc('M','J','P','G')
#视频图像帧率
fps = cap.get(cv2.CAP_PROP_FPS)
print(fps)
# 视频图像的宽度
frame_width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
# 视频图像的长度
frame_height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
print(frame_width)
print(frame_height)
#创建VideoWriter对象
out = cv2.VideoWriter('output_1_new.mp4',fourcc, fps,(frame_width,frame_height))
while(True):
ret, frame = cap.read()
if ret==True:
# 水平翻转
frame = cv2.flip(frame,1)
out.write(frame)
cv2.imshow('frame',frame)
if cv2.waitKey(25) & 0xff == ord('q'):
break
else:
break
out.release()
cap.release()
cv2.destroyAllWindows()
5.2 目标识别
帧差法
帧间差分法是通过对视频中相邻两帧图像做差分运算
来标记运动物体的方法。
当视频中存在移动物体的时候,相邻帧(或相邻三帧)之间在灰度上会有差别, 求取两帧图像灰度差的绝对值,则静止的物体在差值图像上表现出来全是0,而 移动物体特别是移动物体的轮廓处由于存在灰度变化为非0。
优点:
- 算法实现简单,程序设计复杂度低;
- 对光线等场景变化不太敏感,能够适应各种动态环境,稳定性较好;
缺点:
- 不能提取出对象的完整区域,对象内部有“空洞”;
- 只能提取出边界,边界轮廓比较粗,往往比实际物体要大;
- 对快速运动的物体,容易出现糊影的现象,甚至会被检测为两个不同的运动物体;
- 对慢速运动的物体,当物体在前后两帧中几乎完全重叠时,则检测不到物体;
# 导入必要的软件包
import cv2
# 视频文件输入初始化
filename = "move_detect.flv"
camera = cv2.VideoCapture(filename)
# 视频文件输出参数设置
out_fps = 12.0 # 输出文件的帧率
fourcc = cv2.VideoWriter_fourcc('M', 'P', '4', '2')
out1 = cv2.VideoWriter('v1.avi', fourcc, out_fps, (500, 400))
out2 = cv2.VideoWriter('v2.avi', fourcc, out_fps, (500, 400))
# 初始化当前帧的前帧
lastFrame = None
# 遍历视频的每一帧
while camera.isOpened():
# 读取下一帧
(ret, frame) = camera.read()
# 如果不能抓取到一帧,说明我们到了视频的结尾
if not ret:
break
# 调整该帧的大小
frame = cv2.resize(frame, (500, 400), interpolation=cv2.INTER_CUBIC)
# 如果第一帧是None,对其进行初始化
if lastFrame is None:
lastFrame = frame
continue
# 计算当前帧和前帧的不同
frameDelta = cv2.absdiff(lastFrame, frame)
# 当前帧设置为下一帧的前帧
lastFrame = frame.copy()
# 结果转为灰度图
thresh = cv2.cvtColor(frameDelta, cv2.COLOR_BGR2GRAY)
# 图像二值化
thresh = cv2.threshold(thresh, 25, 255, cv2.THRESH_BINARY)[1]
# #去除图像噪声,先腐蚀再膨胀(形态学开运算)
# thresh=cv2.erode(thresh,None,iterations=1)
# thresh = cv2.dilate(thresh, None, iterations=2)
# 阀值图像上的轮廓位置
# 返回两个值,一个是轮廓本身,还有一个是每条轮廓对应的属性。
cnts, hierarchy = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
# 遍历轮廓
for c in cnts:
# 忽略小轮廓,排除误差
if cv2.contourArea(c) < 150:
continue
# 计算轮廓的边界框,在当前帧中画出该框
(x, y, w, h) = cv2.boundingRect(c)
cv2.rectangle(frame, (x, y), (x + w, y + h), (0, 255, 0), 2)
# 显示当前帧
cv2.imshow("frame", frame)
cv2.imshow("frameDelta", frameDelta)
cv2.imshow("thresh", thresh)
# 保存视频
out1.write(frame)
out2.write(frameDelta)
# 如果q键被按下,跳出循环
if cv2.waitKey(20) & 0xFF == ord('q'):
break
# 清理资源并关闭打开的窗口
out1.release()
out2.release()
camera.release()
cv2.destroyAllWindows()
光流法
光流法利用图像序列中像素在时间域上的变化以及相邻帧之间的相关性,根据 上一帧与当前帧之间的对应关系,计算得到相邻帧之间物体的运动信息。
大多数的光流计算方法计算量巨大,结构复杂,且易受光照、物体遮挡或图像 噪声的影响,鲁棒性差,故一般不被对精度和实时性要求比较高的监控系统所 采用
import cv2 as cv
import numpy as np
es = cv.getStructuringElement(cv.MORPH_ELLIPSE, (10, 10))
kernel = cv.getStructuringElement(cv.MORPH_ELLIPSE, (3, 3))
cap = cv.VideoCapture("move_detect.flv")
frame1 = cap.read()[1]
prvs = cv.cvtColor(frame1, cv.COLOR_BGR2GRAY)
hsv = np.zeros_like(frame1)
hsv[..., 1] = 255
# 视频文件输出参数设置
out_fps = 12.0 # 输出文件的帧率
fourcc = cv.VideoWriter_fourcc('M', 'P', '4', '2')
sizes = (int(cap.get(cv.CAP_PROP_FRAME_WIDTH)), int(cap.get(cv.CAP_PROP_FRAME_HEIGHT)))
out1 = cv.VideoWriter('v6.avi', fourcc, out_fps, sizes)
out2 = cv.VideoWriter('v8.avi', fourcc, out_fps, sizes)
while True:
(ret, frame2) = cap.read()
next = cv.cvtColor(frame2, cv.COLOR_BGR2GRAY)
flow = cv.calcOpticalFlowFarneback(prvs, next, None, 0.5, 3, 15, 3, 5, 1.2, 0)
mag, ang = cv.cartToPolar(flow[..., 0], flow[..., 1])
hsv[..., 0] = ang * 180 / np.pi / 2
hsv[..., 2] = cv.normalize(mag, None, 0, 255, cv.NORM_MINMAX)
bgr = cv.cvtColor(hsv, cv.COLOR_HSV2BGR)
draw = cv.cvtColor(bgr, cv.COLOR_BGR2GRAY)
draw = cv.morphologyEx(draw, cv.MORPH_OPEN, kernel)
draw = cv.threshold(draw, 25, 255, cv.THRESH_BINARY)[1]
contours, hierarchy = cv.findContours(draw.copy(), cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE)
for c in contours:
if cv.contourArea(c) < 500:
continue
(x, y, w, h) = cv.boundingRect(c)
cv.rectangle(frame2, (x, y), (x + w, y + h), (255, 255, 0), 2)
cv.imshow('frame2', bgr)
cv.imshow('draw', draw)
cv.imshow('frame1', frame2)
out1.write(bgr)
out2.write(frame2)
k = cv.waitKey(20) & 0xff
if k == 27 or k == ord('q'):
break
elif k == ord('s'):
cv.imwrite('opticalfb.png', frame2)
cv.imwrite('opticalhsv.png', bgr)
prvs = next
out1.release()
out2.release()
cap.release()
cv.destroyAllWindows()
背景减除法
import numpy as np
import cv2
# read the video
cap = cv2.VideoCapture('move_detect.flv')
# create the subtractor
fgbg = cv2.createBackgroundSubtractorMOG2(
history=500, varThreshold=100, detectShadows=False)
def getPerson(image, opt=1):
# get the front mask
mask = fgbg.apply(frame)
# eliminate the noise
line = cv2.getStructuringElement(cv2.MORPH_RECT, (1, 5), (-1, -1))
mask = cv2.morphologyEx(mask, cv2.MORPH_OPEN, line)#开运算
cv2.imshow("mask", mask)
# find the max area contours
contours, hierarchy = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
for c in range(len(contours)):
area = cv2.contourArea(contours[c])
if area < 150:
continue
rect = cv2.minAreaRect(contours[c])
cv2.ellipse(image, rect, (0, 255, 0), 2, 8)
cv2.circle(image, (np.int32(rect[0][0]), np.int32(rect[0][1])), 2, (255, 255, 0), 2, 8, 0)
return image, mask
while True:
ret, frame = cap.read()
cv2.imwrite("input.png", frame)
cv2.imshow('input', frame)
result, m_ = getPerson(frame)
cv2.imshow('result', result)
k = cv2.waitKey(20)&0xff
if k == 27 or k == ord('q'):
cv2.imwrite("result.png", result)
cv2.imwrite("mask.png", m_)
break
cap.release()
cv2.destroyAllWindows()
更多推荐
【学习笔记】OpenCv图像处理基础(下)
发布评论