基于颜色阈值分割的视觉跟踪

本次试验可以分成两个步骤完成。

  1. 利用颜色阈值取出跟踪目标。
  2. 使用均移(Meanshift and Camshift)算法进行目标跟踪。

首先,什么是均移(Meanshift and Camshift)算法?
均移首先是建立一个窗口(window),然后不断地将目标在窗口内移动搜索,并计算相关位置的最大值(也可以说是窗口内概率密度)。如图,实正方形是窗口的中心,实圆形是目标的质心。目标移动,其质心也会移动,我们的目的就是计算出目标的质心然后将窗口中心移动到质心位置这样就完成了对目标的跟踪。opencv的给的例子meanshif窗口大小固定,camshift窗口实自适应的。
详细请查看python-opencv手册
https://opencv-python-tutroals.readthedocs.io/en/latest/py_tutorials/py_video/py_meanshift/py_meanshift.html#meanshift

meanshift_basics.jpg

其次,什么是颜色阈值分割?
目标的颜色是目标的一个非常明显的特征,也是比较容易察觉,彩色图像的三基色是红(RED),绿(GREEN),蓝(BLUE),在此基础上建立的模型是RGB模型,然而我们常常将RGB模型 转换成HSV模型,H(明色调),S(饱和度),V(明度)。色度决定图像色彩,饱和度是颜色的深浅,明度是图像的明亮程度。图像阈值分割就是利用HSV这三个特性,设置一个范围将我们所需要(区别于背景)的目标颜色提取出来。基于颜色阈值分割的优点在于易于理解,很直观。缺点在于阈值的选取比较麻烦,且容易受到背景颜色的干扰。


3801213fb80e7beceb58f65c2f2eb9389a506b89.jpg

python+opencv的方式给图像处理带来的极大的简易性,python是脚本语言所以在图像处理操作时可将代码写成脚本方便理解。
接下来开始进行试验。

import numpy as np#导入numpy库
import cv2#导入opencv库
cap = cv2.VideoCapture(0)#开启摄像头
ret,frame = cap.read()#读取第一帧视频
# take first frame of the video
lower_blue = np.array([20,0,0]) #设置颜色阈值下限
upper_blue = np.array([50,200,200])#设置颜色阈值上限

在这段代码中读取摄像头捕捉的第一帧作为预处理图像。并设置我们所需要捕捉目标颜色的范围,这里我捕捉的是黄色,范围大概是(20-50)。根据HSV模型,彩色图像的色调(H)在8bit下是(0-180)三基色的值大概是红(0),黄(30),绿(60),青(90),蓝(120)。饱和度(H)的范围是(0-255),明度(V)的范围是(0-255)。所以我给定的黄色杯盖的HSV范围如下H(20-50),S(0-200),V(0-200)。具体的opencv颜色阈值选取详细内容参见
https://opencv-python-tutroals.readthedocs.io/en/latest/py_tutorials/py_imgproc/py_colorspaces/py_colorspaces.html#converting-colorspaces

mask = cv2.inRange(frame, lower_blue, upper_blue)#建立一个遮掩mask
kernel = np.ones((5,5),np.uint8)#建立一个核心
opening = cv2.morphologyEx(mask, cv2.MORPH_OPEN, kernel)#开启
dilation = cv2.dilate(opening,kernel,iterations = 1)#膨胀

在这段代码中主要是对我们摄像头获取的第一帧图像进行了取遮掩,取遮掩的目的是提取出第一帧图像中的目标。opening(开启)和dilation(膨胀)都是形态学处理,开启用于把结构元素(可以理解为每个像素)小的突刺滤掉,切断细长的搭接而起到分离作用。膨胀用于扩张比背景亮的区域,压缩比背景暗的区域。其中kernel创建的就是大小为5 * 5的结构元素。关于形态学的知识详见
https://opencv-python-tutroals.readthedocs.io/en/latest/py_tutorials/py_imgproc/py_morphological_ops/py_morphological_ops.html

image, contours, hierarchy = cv2.findContours(dilation,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)#取轮廓
cnt = contours[1]#轮廓集合中第二个轮廓
M = cv2.moments(cnt)#图像矩
#重心
cx = int(M['m10']/M['m00'])
cy = int(M['m01']/M['m00'])

这段代码目的是对开启和膨胀后的目标图像进行取轮廓操作,并对取到的轮廓取图像矩取重心和取中心点坐标。轮廓contours是包含着目标的轮廓点。因为获得的目标图像可能会有空洞所以因该会有很多的轮廓点集,这里cnt = contours[1]取的是取第二个点集。图像中计算出来的矩通常描述了图像不同种类的几何特征如:大小、灰度、方向、形状、重心等特征。这里cx和cy是轮廓的重心。关于opencv提取轮廓参见[findContours]https://docs.opencv.org/3.0-last-rst/modules/imgproc/doc/structural_analysis_and_shape_descriptors.html?highlight=cv2.findcontour#cv2.findContours 关于轮廓特性如图像矩参见[CONTOUR_FEATURES]https://opencv-python-tutroals.readthedocs.io/en/latest/py_tutorials/py_imgproc/py_contours/py_contour_features/py_contour_features.html

r,h,c,w = np.int(np.abs(cy-250)),250,np.int(np.abs(cx-250)),250# 根据获得的重心建立一个窗口(window)
track_window = (c,r,w,h)
roi = frame[r:r+h, c:c+w]#取出图像中的目标图像
hsv_roi = cv2.cvtColor(roi, cv2.COLOR_BGR2HSV)#RGB模型转换成HSV模型
mask = cv2.inRange(hsv_roi, lower_blue, upper_blue)#创建遮掩mask
roi_hist = cv2.calcHist([hsv_roi],[0],mask,[30],[0,30])#获取灰度直方图
cv2.normalize(roi_hist,roi_hist,0,30,cv2.NORM_MINMAX)#归一化处理
# Setup the termination criteria, either 10 iteration or move by atleast 1 pt
term_crit = ( cv2.TERM_CRITERIA_EPS | cv2.TERM_CRITERIA_COUNT, 10, 1 )#设置起始条件

这段代码主要就是以获取到的轮廓重心为中心建立一个矩形窗口,并对这个窗口取灰度直方图,进行归一化处理,以及设置起始条件为直方图反向映射做准备。直方图反向映射是什么?它用于图像分割或在图像中查找感兴趣的对象。简单地说,它创建的图像大小(但单通道)与我们的输入图像,其中每个像素对应的概率,该像素属于我们的对象。在更简单的世界中,与其余部分相比,输出图像将使我们感兴趣的对象更白。关于直方图反向映射的原理参见
https://blog.csdn.net/michaelhan3/article/details/73550643

while(1):
    ret ,frame = cap.read()#读取图片
    if ret == True:
        hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)

        dst = cv2.calcBackProject([hsv],[0],roi_hist,[0,30],1)#直方图反向映射
# apply meanshift to get the new location
        ret, track_window = cv2.CamShift(dst, track_window, term_crit)#自适应均移算法
        # Draw it on image
        pts = cv2.boxPoints(ret)
        pts = np.int0(pts)
        img2 = cv2.polylines(frame, [pts], True, 255, 2)#绘制跟踪窗口

        cv2.imshow('img2', img2)#显示图像
        k = cv2.waitKey(60) & 0xff#设置按下esc退出程序
        if k == 27:
            break
cv2.destroyAllWindows()#摧毁窗口
cap.release()#卸载摄像头

以上实现了一个获取黄色的目标并进行跟踪的程序。结果如下gif图。

1544081556881.gif

代码托管在码云https://gitee.com/capone/testt/attach_files

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

推荐阅读更多精彩内容