@[TOC]
一森瘪、黑白圖片
來(lái)自:輕舞飛揚(yáng)
在了解色彩空間前牡属,先了解一下黑白圖片的形成。
用光線對(duì)著傳感器扼睬,從傳感器的立場(chǎng)上來(lái)看的話逮栅,一部分接收到了光線,那么這一部分就是明亮的窗宇,一部分沒有接收到光線措伐,那么這一部分將會(huì)是黑暗的,我們可以視為傳感器存在兩個(gè)通道军俊。
如果傳感器存在多個(gè)通道的話侥加,意思就是說我們將光線進(jìn)行分類,高能量的粪躬,中等能量的担败,低能量的等等多種分類,傳感器對(duì)這多種能力進(jìn)行區(qū)分顯示出不同的顏色镰官。
不同的顏色有不同的能量分布提前,一般來(lái)說紅色(R)在600-700nm波長(zhǎng)處能量最高,綠色在534-555nm處能量最高泳唠,藍(lán)色(B)在420-440nm處能力最高狈网。
二、HSV顏色空間
?色調(diào)H
用角度度量笨腥,取值范圍為0°~360°拓哺,從紅色開始按逆時(shí)針方向計(jì)算,紅色為0°脖母,綠色為120°,藍(lán)色為240°士鸥。它們的補(bǔ)色是:黃色為60°,青色為180°,品紅為300°镶奉;
?飽和度S
飽和度S表示顏色接近光譜色的程度础淤。一種顏色,可以看成是某種光譜色與白色混合的結(jié)果哨苛。其中光譜色所占的比例愈大鸽凶,顏色接近光譜色的程度就愈高,顏色的飽和度也就愈高建峭。飽和度高玻侥,顏色則深而艷。光譜色的白光成分為0亿蒸,飽和度達(dá)到最高南用。通常取值范圍為0%~100%瞬浓,值越大,顏色越飽和。
?明度V
明度表示顏色明亮的程度封救,對(duì)于光源色,明度值與發(fā)光體的光亮度有關(guān)酱畅;對(duì)于物體色弄喘,此值和物體的透射比或反射比有關(guān)。通常取值范圍為0%(黑)到100%(白)贡蓖。
HSV模型的三維表示從RGB立方體演化而來(lái)曹鸠。設(shè)想從RGB沿立方體對(duì)角線的白色頂點(diǎn)向黑色頂點(diǎn)觀察,就可以看到立方體的六邊形外形斥铺。六邊形邊界表示色彩彻桃,水平軸表示純度,明度沿垂直軸測(cè)量晾蜘。<center>
</center>
三邻眷、OpenCV中的HSV
HSV顏色空間與人眼較為接近,一般以HSV為顏色檢測(cè)和識(shí)別笙纤。
opencv H范圍(0-180) S(0-255) V(0-255)耗溜。
1. HSV二值化處理的函數(shù):
-
說明
檢查數(shù)組元素是否位于其他兩個(gè)數(shù)組的元素之間。
該功能檢查范圍如下:?? 對(duì)于單通道輸入數(shù)組的每個(gè)元素:
?? 對(duì)于兩通道陣列:
也就是說省容,如果src(I)在指定的1D抖拴,2D,3D等區(qū)域內(nèi)腥椒,則dst(I)設(shè)置為255(全1位)阿宅,否則設(shè)置為0。
==高低閾值指的是hsv的高低閾值,當(dāng)圖像的hsv在高低閾值之間那么輸出圖像為255(白)笼蛛,否則為0(黑色)洒放。==
當(dāng)下邊界參數(shù)和/或上邊界參數(shù)為標(biāo)量時(shí),應(yīng)忽略上式中在lowerb和upperb處的索引(I)滨砍。
- 聲明
void inRange(InputArray src, InputArray lowerb,InputArray upperb, OutputArray dst);
- 參數(shù)
src 輸入圖像 lowerb 低閾值 upperb 高閾值往湿。 dst 輸出圖像(大小與輸入圖像一樣妖异,類型是8U)相當(dāng)于:Mat dstImage = Mat::zeros(srcImage.size(),CV_8U);
2. HSV顏色范圍的選取:
內(nèi)容來(lái)自: 阿卡蒂奧
比如我們選擇某個(gè)偏紅色的范圍:
?? 1. 色環(huán)圖中這個(gè)區(qū)間即BGR(0,128,255)到BGR(255,0,213)领追;
?? 2. B他膳、G、R這三個(gè)通道的范圍分別為0-255绒窑,0-128棕孙,213-255。
?? 3. 因此閾值下限lowerb=Scalar(0,0,213)些膨,閾值上限upperb=Scalar(255,128,255)蟀俊。
四、顏色直方圖的獲取與目標(biāo)跟蹤
1. 顏色直方圖的獲取
顏色直方圖是對(duì)運(yùn)動(dòng)目標(biāo)表面顏色分布的統(tǒng)計(jì)订雾,不受目標(biāo)的形狀肢预、姿態(tài)等變化的影響。所以用直方圖作為目標(biāo)的特征洼哎,依據(jù)顏色分布進(jìn)行匹配误甚,具有穩(wěn)定性好、抗部分遮擋谱净、計(jì)算方法簡(jiǎn)單和計(jì)算量小的特點(diǎn)窑邦,是比較理想的目標(biāo)顏色特征。為了抵抗光照亮暗帶來(lái)的影響壕探,一般的顏色直方圖均在HSV色系下提取冈钦。
對(duì)HSV3個(gè)分量按照對(duì)顏色變化的敏感程度不同分別進(jìn)行量化。設(shè)量化后李请,3個(gè)分量的取值范圍分別為瞧筛,
,
按照
的形式排列成一個(gè)矢量,則其范圍為:
設(shè)顏色i的像素點(diǎn)個(gè)數(shù)為
,圖像的像素點(diǎn)的總數(shù)為:<center>
則顏色i的出現(xiàn)概率白翻,即被定義為顏色直方圖乍炉,即<center>
</center>
2.基于顏色直方圖的目標(biāo)跟蹤
因?yàn)轭伾狈綀D是矢量,以此作為特征進(jìn)行目標(biāo)跟蹤時(shí)滤馍,即基于顏色直方圖的目標(biāo)跟蹤時(shí)岛琼,可采用Bhattacharyya距離作為兩直方圖相似度的度量。計(jì)算公式為:<center></center>
其中:為兩直方圖的Bhattacharyya系數(shù)巢株;p為候選目標(biāo)直方圖分布槐瑞;
為模板直方圖分布;
為兩直方圖的Bhattacharyya距離阁苞,其值越小困檩,表明兩直方圖的相似度越高祠挫;反之,兩直方圖相似度越低悼沿。
五茸歧、camshift算法原理
camshift就是利用目標(biāo)的顏色直方圖模型將圖像轉(zhuǎn)換為顏色概率分布圖,初始化一個(gè)搜索窗的大小和位置显沈,并根據(jù)上一幀得到的結(jié)果自適應(yīng)調(diào)整搜索窗口的位置和大小,從而定位出當(dāng)前圖像中目標(biāo)的中心位置逢唤。
分為三個(gè)部分:
1. 色彩投影圖(反向投影):
(1) RGB顏色空間對(duì)光照亮度變化較為敏感拉讯,為了減少此變化對(duì)跟蹤效果的影響,==首先將圖像從RGB空間轉(zhuǎn)換到HSV空間==鳖藕。
(2) 然后==對(duì)其中的H分量作直方圖魔慷,在直方圖中代表了不同H分量值出現(xiàn)的概率或者像素個(gè)數(shù)==,就是說可以查找出H分量大小為h的概率或者像素個(gè)數(shù)著恩,即得到了顏色概率查找表院尔。
(3) ==將圖像中每個(gè)像素的值用其顏色出現(xiàn)的概率對(duì)替換,就得到了顏色概率分布圖==喉誊。這個(gè)過程就叫反向投影邀摆,顏色概率分布圖是一個(gè)灰度圖像。
2. meanshift
meanshift算法是一種密度函數(shù)梯度估計(jì)的非參數(shù)方法伍茄,通過迭代尋優(yōu)找到概率分布的極值來(lái)定位目標(biāo)栋盹。
算法過程:
??1). 在顏色概率分布圖中選取搜索窗W
??2). 計(jì)算階距:
- ?????? 計(jì)算零階矩
![]()
- ?????? 計(jì)算一階矩:
![]()
- ?????? 計(jì)算搜索窗的質(zhì)心:
![]()
??3)調(diào)整搜索窗的大小
??4)移動(dòng)搜索窗的中心到質(zhì)心敷矫,如果移動(dòng)距離大于預(yù)設(shè)的固定閾值例获,則重復(fù)2)3)4),直到搜索窗的中心與質(zhì)心間的移動(dòng)距離小于預(yù)設(shè)的固定閾值曹仗,或者循環(huán)運(yùn)算的次數(shù)達(dá)到某一最大值榨汤,停止計(jì)算。關(guān)于meanshift的收斂性證明可以google相關(guān)文獻(xiàn)怎茫。
3. camshift
將meanshift算法擴(kuò)展到連續(xù)圖像序列收壕,就是camshift算法。==它將視頻的所有幀做meanshift運(yùn)算轨蛤,并將上一幀的結(jié)果啼器,即搜索窗的大小和中心,作為下一幀meanshift算法搜索窗的初始值==俱萍。如此迭代下去端壳,就可以實(shí)現(xiàn)對(duì)目標(biāo)的跟蹤。
算法過程
(1).初始化搜索窗
(2).計(jì)算搜索窗的顏色概率分布(反向投影)
(3).運(yùn)行meanshift算法枪蘑,獲得搜索窗新的大小和位置损谦。
(4).在下一幀視頻圖像中用(3)中的值重新初始化搜索窗的大小和位置岖免,再跳轉(zhuǎn)到(2)繼續(xù)進(jìn)行。
camshift能有效解決目標(biāo)變形和遮擋的問題照捡,對(duì)系統(tǒng)資源要求不高颅湘,時(shí)間復(fù)雜度低,在簡(jiǎn)單背景下能夠取得良好的跟蹤效果栗精。但當(dāng)背景較為復(fù)雜闯参,或者有許多與目標(biāo)顏色相似像素干擾的情況下,會(huì)導(dǎo)致跟蹤失敗悲立。因?yàn)樗鼏渭兊目紤]顏色直方圖鹿寨,忽略了目標(biāo)的空間分布特性,所以這種情況下需加入對(duì)跟蹤目標(biāo)的預(yù)測(cè)算法薪夕。
4. OpenCV中相關(guān)API
1. 直方圖
前面已經(jīng)提過:
OpenCV--017:圖像直方圖
OpenCV--018:圖像直方圖均衡化
OpenCV--020:圖像直方圖反向投影
OpenCV--021:直方圖規(guī)定化
2. CamShift函數(shù)
說明
查找對(duì)象的中心脚草,大小和方向。-
聲明
RotatedRect cv::CamShift( InputArray probImage, Rect &window, TermCriteria criteria ) Python: retval, window=cv.CamShift( probImage, window, criteria )
-
參數(shù)
probImage 對(duì)象直方圖的反向投影原献。 window 初始搜索窗口馏慨。 criteria 底層meanShift的停止條件。 criterial:
OpenCV--TermCriteria類
返回(在舊接口中)CAMSHIFT用于收斂函數(shù)的迭代次數(shù)姑隅,實(shí)現(xiàn)CAMSHIFT對(duì)象跟蹤算法写隶。首先,它使用meanShift找到一個(gè)對(duì)象中心讲仰,然后調(diào)整窗口大小樟澜,找到最佳旋轉(zhuǎn)。該函數(shù)返回旋轉(zhuǎn)后的矩形結(jié)構(gòu)叮盘,其中包括對(duì)象位置秩贰、大小和方向。通過RotatedRect::boundingRect()可以獲得搜索窗口的下一個(gè)位置柔吼。
六毒费、基于顏色特征的目標(biāo)跟蹤
//用HSV中的Hue分量進(jìn)行跟蹤
Mat image;
//表示是否要進(jìn)入反向投影模式,true則表示要進(jìn)入反向投影模式
bool backprogectMode = false;
//表示在選中要跟蹤的初始目標(biāo)愈魏,true表示正在用鼠標(biāo)選擇要跟蹤的目標(biāo)
bool selectObject = false;
//跟蹤目標(biāo)的個(gè)數(shù)
int trackObject = 0;
//是否顯示HUE分量直方圖
bool showHist = true;
//用于保存鼠標(biāo)選擇第一次單擊時(shí)點(diǎn)的位置
Point origin;
//用于保存鼠標(biāo)選擇的矩形框
Rect selection;
int vmin = 10, vmax = 256, smin = 30;
//鼠標(biāo)事件,該函數(shù)用鼠標(biāo)進(jìn)行跟蹤目標(biāo)的選擇
static void onMouse(int event,int x,int y,int ,void*) {
//鼠標(biāo)左鍵按下時(shí)觅玻,則條件為true,在用鼠標(biāo)進(jìn)行目標(biāo)選擇
//if里面的代碼塊就是確定所選擇的矩形區(qū)域selection
if (selectObject) {
//確定鼠標(biāo)點(diǎn)擊的矩形左上角頂點(diǎn)的坐標(biāo)
selection.x = MIN(x, origin.x);
cout << "origin: " << origin.x <<",";
selection.y = MIN(y, origin.y);
cout << origin.y << endl;
selection.width = std::abs(x - origin.x);//矩形寬
selection.height = std::abs(y - origin.y);//矩形高
//用于確保所選的矩形區(qū)域在圖片區(qū)域內(nèi)
selection &= Rect(0, 0, image.cols, image.rows);
}
switch (event)
{
//鼠標(biāo)按下,開始點(diǎn)擊選擇跟蹤物體
case EVENT_LBUTTONDOWN:
//記錄鼠標(biāo)第一次按下的位置
origin = Point(x, y);
//鼠標(biāo)剛按下去時(shí),初始化了一個(gè)矩形區(qū)域
selection = Rect(x, y, 0, 0);
selectObject = true;
break;
//鼠標(biāo)松開,完成選擇跟蹤物體
case EVENT_LBUTTONUP:
selectObject = false;
//如果選擇物體有效培漏,則打開跟蹤功能
if (selection.width > 0 && selection.height > 0)
trackObject = -1;
break;
}
}
static void help() {
cout << "\n這是一個(gè)基于meanShift算法的demo:\n"
" 你可以選擇一個(gè)跟蹤的顏色目標(biāo)溪厘,比如你的臉。\n";
cout << "\n\nHot keys: \n"
"\tESC - quit the program\n"
"\tc - stop the tracking\n"
"\tb - switch to/from backprojection view\n"
"\th - show/hide object histogram\n"
"To initialize tracking, select the object with mouse: \n";
}
static void camshiftTest() {
help();
VideoCapture cap;
Rect trackWindow;
int hsize = 16;
float hranges[] = { 0,180 };
const float* phranges = hranges;
//打開默認(rèn)的攝像頭
cap.open(0);
//判斷攝像頭是否成功打開
if (!cap.isOpened())
{
help();
cout << "***could not initialize capturing...***\n";
return ;
}
//直方圖窗口:用于顯示直方圖
namedWindow("Histogram", 0);
//CamShift :用于顯示視頻
namedWindow("CamShift Demo", 0);
//設(shè)置鼠標(biāo)回調(diào)函數(shù)
setMouseCallback("CamShift Demo", onMouse, 0);
//createTrackbar函數(shù)的功能是在對(duì)應(yīng)的CamShift Demo窗口創(chuàng)建滑動(dòng)條Vmin,Vmax,Smin
//滑動(dòng)條的值最大為256,最后一個(gè)參數(shù)為0代表沒有調(diào)用滑動(dòng)拖動(dòng)的響應(yīng)函數(shù)
//vmin,vmax,smin初始值分別為10牌柄,256畸悬,30
createTrackbar("Vmin", "CamShift Demo", &vmin, 256, 0);
createTrackbar("Vmax", "CamShift Demo", &vmax, 256, 0);
createTrackbar("Smin", "CamShift Demo", &smin, 256, 0);
Mat frame, hsv, hue, mask, hist, histimg = Mat::zeros(200, 320, CV_8UC3), backproj;
bool paused = false;
for (;;) {
if (!paused)
{
//從攝像頭中讀取的一幀存在frame中
cap >> frame;
//若讀取失敗,停止循環(huán)
if (frame.empty())
{
break;
}
}
frame.copyTo(image);
if (!paused)
{
//將BGR顏色空間轉(zhuǎn)化成HSV顏色空間
cvtColor(image, hsv, COLOR_BGR2HSV);
//判斷是否存在需要追蹤的目標(biāo),非0即有需要跟蹤的目標(biāo)
if (trackObject)
{
//獲取進(jìn)度條中設(shè)置的值
int _vmin = vmin, _vmax = vmax;
//inRange函數(shù)的功能是檢查輸入數(shù)組每個(gè)元素大小是否在2個(gè)給定數(shù)值之間珊佣,
//如果3個(gè)通道都在對(duì)應(yīng)的范圍內(nèi)蹋宦,則mask對(duì)應(yīng)的那個(gè)點(diǎn)的值全為1(0xff)披粟,否則為0(0x00).
//可以是多通道的,mask保存0通道的最小值,也就是h分量
//這里利用了hsv的3個(gè)通道冷冗,比較h:0~180,s:smin~256,v:min(vmin,vmax),max(vmin,vmax)守屉。
inRange(hsv, Scalar(0, smin, MIN(_vmin, _vmax)), Scalar(180, 256, MAX(_vmin, _vmax)), mask);
int ch[] = { 0,0 };
//hue初始化為與hsv大小深度一樣的矩陣,色調(diào)的度量是用角度表示的蒿辙,紅綠藍(lán)之間相差120度拇泛,反色相差180度
hue.create(hsv.size(), hsv.depth());
//將hsv第一個(gè)通道(也就是色調(diào))的數(shù)復(fù)制到hue中,0索引數(shù)組
mixChannels(&hsv, 1, &hue, 1, ch, 1);
//表示已經(jīng)選擇了有效地待追蹤區(qū)域
if (trackObject < 0)
{
//此處的構(gòu)造函數(shù)roi用的是Mat hue的矩陣頭思灌,且roi的數(shù)據(jù)指針指向hue俺叭,即共用相同的數(shù)據(jù),selection為其感興趣的區(qū)域
Mat roi(hue, selection);
//設(shè)置掩膜板選擇框?yàn)镽OI
Mat maskroi(mask, selection);
//得到選擇框內(nèi)且滿足掩膜板內(nèi)的直方圖
//calcHist()函數(shù):第1個(gè)參數(shù):輸入矩陣序列
// 第2個(gè)參數(shù):表示輸入的矩陣數(shù)目习瑰,
// 第3個(gè)參數(shù):表示將被計(jì)算直方圖維數(shù)通道的列表,
// 第4個(gè)參數(shù):表示可選的掩碼函數(shù)
// 第5個(gè)參數(shù):表示輸出直方圖秽荤,
// 第6個(gè)參數(shù): 表示直方圖的維數(shù)甜奄,
// 第7個(gè)參數(shù): 為每一維直方圖數(shù)組的大小,
// 第8個(gè)參數(shù): 為每一維直方圖bin的邊界
calcHist(&roi, 1, 0, maskroi, hist, 1, &hsize, &phranges);
//將hist矩陣進(jìn)行歸一化窃款,都?xì)w一化到0-255
normalize(hist, hist, 0, 255, NORM_MINMAX);
trackWindow = selection;
//置trackObject為1,表明屬性提取完成
//只要鼠標(biāo)選完區(qū)域松開后课兄,且沒有按鍵盤清0鍵'c',則trackObject一直保持為1晨继,因此該if函數(shù)只能執(zhí)行一次烟阐,除非重新選擇跟蹤區(qū)域
trackObject = 1;
//與按下‘c’鍵是一樣的,這里的all(0)表示的是標(biāo)量全部清0
histimg = Scalar::all(0);
//histing是一個(gè)200*300的矩陣紊扬,hsize應(yīng)該是每一個(gè)bin的寬度蜒茄,也就是histing矩陣能分出幾個(gè)bin出來(lái)
int binW = histimg.cols / hsize;
//定義一個(gè)緩沖單bin矩陣
Mat buf(1, hsize, CV_8UC3);
//saturate_case函數(shù)為從一個(gè)初始類型準(zhǔn)確變換到另一個(gè)初始類型
for (int i = 0; i < hsize; i++)
{
buf.at<Vec3b>(i) = Vec3b(saturate_cast<uchar>(i * 180. / hsize), 255, 255);
}
cvtColor(buf, buf, COLOR_HSV2BGR);
for (int i = 0; i < hsize; i++)
{
//at函數(shù)為返回一個(gè)指定數(shù)組元素的參考值
//畫直方圖到圖像空間,指定左上角和右下角餐屎,并定義顏色檀葛,大小,線型等
int val = saturate_cast<int>(hist.at<float>(i) * histimg.rows / 255);
rectangle( histimg,
Point(i * binW, histimg.rows),
Point((i + 1) * binW, histimg.rows - val),
Scalar(buf.at<Vec3b>(i)),-1, 8);
}
}
//計(jì)算直方圖的反向投影
//計(jì)算hue圖像0通道直方圖hist的反向投影腹缩,并讓入backproj中
calcBackProject(&hue, 1, 0, hist, backproj, & phranges);
backproj &= mask;
//得到掩膜內(nèi)的反向投影
//trackWindow為鼠標(biāo)選擇的區(qū)域屿聋,TermCriteria為確定迭代終止的準(zhǔn)則
//使用MeanShift算法對(duì)backproj中的內(nèi)容進(jìn)行搜索,返回跟蹤結(jié)果
RotatedRect trackBox = CamShift(backproj, trackWindow,TermCriteria(1 | 2, 10, 1));
//如果鼠標(biāo)選擇的區(qū)域trackWindow小于等于1,重置trackWindow區(qū)域
if (trackWindow.area() <= 1)
{
int cols = backproj.cols, rows = backproj.rows, r = (MIN(cols, rows) + 5) / 6;
trackWindow = Rect(trackWindow.x - r, trackWindow.y - r,
trackWindow.x + r, trackWindow.y + r) &
Rect(0, 0, cols, rows);
}
if (backprogectMode)
cvtColor(backproj, image, COLOR_GRAY2BGR);
//畫出跟蹤結(jié)果的位置:以橢圓為代表
ellipse(image, trackBox, Scalar(0, 0, 255), 3);
}
}
else if (trackObject < 0) {
paused = false;
}
//如果正處于物體選擇藏鹊,畫出選擇框
if (selectObject && selection.width > 0 && selection.height > 0)
{
Mat roi(image, selection);
bitwise_not(roi, roi);// bitwise_not為將每一個(gè)bit位取反
}
imshow("CamShift Demo", image);
imshow("Histogram", histimg);
char c = (char)waitKey(10);
if (c == 27)//退出鍵
break;
switch (c)
{
case 'b'://反向投影模型交替
backprogectMode = !backprogectMode;
break;
case 'c'://清零跟蹤目標(biāo)對(duì)象
trackObject = 0;
histimg = Scalar::all(0);
break;
case 'h'://顯示直方圖交替
showHist = !showHist;
if (!showHist)
destroyWindow("Histogram");
else
namedWindow("Histogram", 1);
break;
case 'p'://暫停跟蹤交替
paused = !paused;
break;
default:
;
}
}
}
int main(){
//Mat src=imread("D:/test/sh.png");
//inRangeTest(src);
camshiftTest();
waitKey(0);
}
學(xué)習(xí)資料
-
雷霄驊: Camshift算法原理及其Opencv實(shí)現(xiàn)
ps:雖然不認(rèn)識(shí)润讥,但是每次看到都想哭,一想到他就會(huì)想起對(duì)我很重要的一個(gè)人盘寡。愿他們?cè)谔焐夏芸炜鞓窐返摹?/p>