光流跟蹤演算法-ag真人国际官网
a. 如何使用opencv實現金字塔光流lk跟蹤演算法
#include
#include
#include "cv.h"
#include "cxcore.h"
#include "highgui.h"
#include
using namespace cv;
static const double pi = 3.14159265358979323846;
inline static double square(int a)
{
return a * a;
}
/*該函數目的:給img分配內存空間,並設定format,如位深以及channel數*/
inline static void allocateondemand(iplimage **img, cvsize size, int depth, int channels)
{
if (*img != null) return;
*img = cvcreateimage(size, depth, channels);
if (*img == null)
{
fprintf(stderr, "error: couldn't allocate image. out of memory?\n");
exit(-1);
}
}
/*主函數,原程序是讀取avi視頻文件,然後處理,我簡單改成從攝像頭直接讀取數據*/
int main(int argc, char *argv[])
{
//讀取攝像頭
videocapture cap(0);
//讀取視頻文件
//videocapture cap; cap.open("optical_flow_input.avi");
if (!cap.isopened())
{
return -1;
}
mat frame;
/*
bool stop = false;
while (!stop)
{
cap >> frame;
// cvtcolor(frame, edges, cv_rgb2gray);
// gaussianblur(edges, edges, size(7, 7), 1.5, 1.5);
// canny(edges, edges, 0, 30, 3);
// imshow("當前視頻", edges);
imshow("當前視頻", frame);
if (waitkey(30) >= 0)
stop = true;
}
*/
//cvcapture *input_video = cvcapturefromfile( "optical_flow_input.avi" );
//cv::videocapture cap = *(cv::videocapture *) userdata;
//if (input_video == null)
// {
// fprintf(stderr, "error: can't open video device.\n");
// return -1;
// }
/*先讀取一幀,以便得到幀的屬性,如長、寬等*/
//cvqueryframe(input_video);
/*讀取幀的屬性*/
cvsize frame_size;
frame_size.height = cap.get(cv_cap_prop_frame_height);
frame_size.width = cap.get(cv_cap_prop_frame_width);
/*********************************************************/
/*用於把結果寫到文件中去,非必要
int framew = frame_size.height; // 744 for firewire cameras
int frameh = frame_size.width; // 480 for firewire cameras
videowriter writer("videotest.avi", -1, 25.0, cvsize(framew, frameh), true);
/*開始光流法*/
//videowriter writer("videotest.avi", cv_fourcc('d', 'i', 'v', 'x'), 25.0, size(640, 480), true);
while (true)
{
static iplimage *frame = null, *frame1 = null, *frame1_1c = null,
*frame2_1c = null, *eig_image = null, *temp_image = null,
*pyramid1 = null, *pyramid2 = null;
mat framet;
/*獲取第一幀*/
// cap >> framet;
cap.read(framet);
mat edges;
//黑白抽象濾鏡模式
// cvtcolor(framet, edges, cv_rgb2gray);
// gaussianblur(edges, edges, size(7, 7), 1.5, 1.5);
// canny(edges, edges, 0, 30, 3);
//轉換mat格式到lpiimage格式
frame = &iplimage(framet);
if (frame == null)
{
fprintf(stderr, "error: hmm. the end came sooner than we thought.\n");
return -1;
}
/*由於opencv的光流函數處理的是8位的灰度圖,所以需要創建一個同樣格式的
iplimage的對象*/
allocateondemand(&frame1_1c, frame_size, ipl_depth_8u, 1);
/* 把攝像頭圖像格式轉換成opencv慣常處理的圖像格式*/
cvconvertimage(frame, frame1_1c, 0);
/* 我們需要把具有全部顏色信息的原幀保存,以備最後在屏幕上顯示用*/
allocateondemand(&frame1, frame_size, ipl_depth_8u, 3);
cvconvertimage(frame, frame1, 0);
/* 獲取第二幀 */
//cap >> framet;
cap.read(framet);
// cvtcolor(framet, edges, cv_rgb2gray);
// gaussianblur(edges, edges, size(7, 7), 1.5, 1.5);
// canny(edges, edges, 0, 30, 3);
frame = &iplimage(framet);
if (frame == null)
{
fprintf(stderr, "error: hmm. the end came sooner than we thought.\n");
return -1;
}
/*原理同上*/
allocateondemand(&frame2_1c, frame_size, ipl_depth_8u, 1);
cvconvertimage(frame, frame2_1c, 0);
/*********************************************************
開始shi-tomasi演算法,該演算法主要用於feature selection,即一張圖中哪些是我
們感興趣需要跟蹤的點(interest point)
input:
* "frame1_1c" 輸入圖像.
* "eig_image" and "temp_image" 只是給該演算法提供可操作的內存區域.
* 第一個".01" 規定了特徵值的最小質量,因為該演算法要得到好的特徵點,哪就
需要一個選擇的閾值
* 第二個".01" 規定了像素之間最小的距離,用於減少運算復雜度,當然也一定
程度降低了跟蹤精度
* "null" 意味著處理整張圖片,當然你也可以指定一塊區域
output:
* "frame1_features" 將會包含fram1的特徵值
* "number_of_features" 將在該函數中自動填充上所找到特徵值的真實數目,
該值<= 400
**********************************************************/
/*開始准備該演算法需要的輸入*/
/* 給eig_image,temp_image分配空間*/
allocateondemand(&eig_image, frame_size, ipl_depth_32f, 1);
allocateondemand(&temp_image, frame_size, ipl_depth_32f, 1);
/* 定義存放frame1特徵值的數組,400隻是定義一個上限 */
cvpoint2d32f frame1_features[400];
int number_of_features = 400;
/*開始跑shi-tomasi函數*/
cvgoodfeaturestotrack(frame1_1c, eig_image, temp_image,
frame1_features, &number_of_features, .01, .01, null);
/**********************************************************
開始金字塔lucas kanade光流法,該演算法主要用於feature tracking,即是算出
光流,並跟蹤目標。
input:
* "frame1_1c" 輸入圖像,即8位灰色的第一幀
* "frame2_1c" 第二幀,我們要在其上找出第一幀我們發現的特徵點在第二幀
的什麼位置
* "pyramid1" and "pyramid2" 是提供給該演算法可操作的內存區域,計算中間
數據
* "frame1_features" 由shi-tomasi演算法得到的第一幀的特徵點.
* "number_of_features" 第一幀特徵點的數目
* "optical_flow_termination_criteria" 該演算法中迭代終止的判別,這里是
epsilon<0.3,epsilon是兩幀中對應特徵窗口的光度之差的平方,這個以後的文
章會講
* "0" 這個我不知道啥意思,反正改成1就出不來光流了,就用作者原話解釋把
means disable enhancements. (for example, the second array isn't
pre-initialized with guesses.)
output:
* "frame2_features" 根據第一幀的特徵點,在第二幀上所找到的對應點
* "optical_flow_window" lucas-kanade光流演算法的運算窗口,具體lucas-kanade
會在下一篇詳述
* "5" 指示最大的金字塔層數,0表示只有一層,那就是沒用金字塔演算法
* "optical_flow_found_feature" 用於指示在第二幀中是否找到對應特徵值,
若找到,其值為非零
* "optical_flow_feature_error" 用於存放光流誤差
**********************************************************/
/*開始為pyramid lucas kanade光流演算法輸入做准備*/
cvpoint2d32f frame2_features[400];
/* 該數組相應位置的值為非零,如果frame1中的特徵值在frame2中找到 */
char optical_flow_found_feature[400];
/* 數組第i個元素表對應點光流誤差*/
float optical_flow_feature_error[400];
/*lucas-kanade光流法運算窗口,這里取3*3的窗口,可以嘗試下5*5,區別就是5*5
出現aperture problem的幾率較小,3*3運算量小,對於feature selection即shi-tomasi演算法來說足夠了*/
cvsize optical_flow_window = cvsize(5, 5);
// cvsize optical_flow_window = cvsize(5, 5);
/* 終止規則,當完成20次迭代或者當epsilon<=0.3,迭代終止,可以嘗試下別的值*/
cvtermcriteria optical_flow_termination_criteria= cvtermcriteria(cv_termcrit_iter | cv_termcrit_eps, 20, .3);
/*分配工作區域*/
allocateondemand(&pyramid1, frame_size, ipl_depth_8u, 1);
allocateondemand(&pyramid2, frame_size, ipl_depth_8u, 1);
/*開始跑該演算法*/
cvcalcopticalflowpyrlk(frame1_1c, frame2_1c, pyramid1, pyramid2,frame1_features, frame2_features, number_of_features,
optical_flow_window, 5, optical_flow_found_feature,optical_flow_feature_error, optical_flow_termination_criteria, 0);
/*畫光流場,畫圖是依據兩幀對應的特徵值,
這個特徵值就是圖像上我們感興趣的點,如邊緣上的點p(x,y)*/
for (int i = 0; i< number_of_features; i )
{
/* 如果沒找到對應特徵點 */
if (optical_flow_found_feature[i] == 0)
continue;
int line_thickness;
line_thickness = 1;
/* cv_rgb(red, green, blue) is the red, green, and blue components
* of the color you want, each out of 255.
*/
cvscalar line_color;
line_color = cv_rgb(255, 0, 0);
/*畫箭頭,因為幀間的運動很小,所以需要縮放,不然看不見箭頭,縮放因子為3*/
cvpoint p, q;
p.x = (int)frame1_features[i].x;
p.y = (int)frame1_features[i].y;
q.x = (int)frame2_features[i].x;
q.y = (int)frame2_features[i].y;
double angle;
angle = atan2((double)p.y - q.y, (double)p.x - q.x);
double hypotenuse;
hypotenuse = sqrt(square(p.y - q.y) square(p.x - q.x));
/*執行縮放*/
q.x = (int)(p.x - 5 * hypotenuse * cos(angle));
q.y = (int)(p.y - 5 * hypotenuse * sin(angle));
/*畫箭頭主線*/
/* "frame1"要在frame1上作畫.
* "p" 線的開始點.
* "q" 線的終止點.
* "cv_aa" 反鋸齒.
* "0" 沒有小數位.
*/
cvline(frame1, p, q, line_color, line_thickness, cv_aa, 0);
/* 畫箭的頭部*/
p.x = (int)(q.x 9 * cos(angle pi / 4));
p.y = (int)(q.y 9 * sin(angle pi / 4));
cvline(frame1, p, q, line_color, line_thickness, cv_aa, 0);
p.x = (int)(q.x 9 * cos(angle - pi / 4));
p.y = (int)(q.y 9 * sin(angle - pi / 4));
cvline(frame1, p, q, line_color, line_thickness, cv_aa, 0);
}
/*顯示圖像*/
/*創建一個名為optical flow的窗口,大小自動改變*/
cvnamedwindow("optical flow", cv_window_normal);
cvflip(frame1, null, 2);
cvshowimage("optical flow", frame1);
/*延時,要不放不了*/
cvwaitkey(33);
/*寫入到文件中去*/
// cv::mat m = cv::cvarrtomat(frame1);//轉換lpimgae到mat格式
// writer << m;//opencv3.0 version writer
}
cap.release();
cvwaitkey(33);
system("pause");
}
b. 目標跟蹤檢測演算法(一)——傳統方法
姓名:劉帆;學號:20021210609;學院:電子工程學院
https://blog.csdn.net/qq_34919792/article/details/89893214
【嵌牛導讀】目標跟蹤演算法研究難點與挑戰在於實際復雜的應用環境 、背景相似干擾、光照條件的變化、遮擋等外界因素以及目標姿態變化,外觀變形,尺度變化、平面外旋轉、平面內旋轉、出視野、快速運動和運動模糊等。而且當目標跟蹤演算法投入實際應用時,不可避免的一個問題——實時性問題也是非常的重要。正是有了這些問題,才使得演算法研究充滿著難點和挑戰。
【嵌牛鼻子】目標跟蹤演算法,傳統演算法
【嵌牛提問】利用目標跟蹤檢測演算法要達到何目的?第一階段的單目標追蹤演算法包括什麼?具體步驟有哪些?它們有何特點?
【嵌牛正文】
第一階段
目標跟蹤分為兩個部分,一個是對指定目標尋找可以跟蹤的特徵,常用的有顏色,輪廓,特徵點,軌跡等,另一個是對目標特徵進行跟蹤。
1、靜態背景
1)背景差: 對背景的光照變化、雜訊干擾以及周期性運動等進行建模。通過當前幀減去背景圖來捕獲運動物體的過程。
2)幀差: 由於場景中的目標在運動,目標的影像在不同圖像幀中的位置不同。該類演算法對時間上連續的兩幀或三幀圖像進行差分運算,不同幀對應的像素點相減,判斷灰度差的絕對值,當絕對值超過一定閾值時,即可判斷為運動目標,從而實現目標的檢測功能。
與二幀差分法不同的是,三幀差分法(交並運算)去除了重影現象,可以檢測出較為完整的物體。幀間差分法的原理簡單,計算量小,能夠快速檢測出場景中的運動目標。但幀間差分法檢測的目標不完整,內部含有「空洞」,這是因為運動目標在相鄰幀之間的位置變化緩慢,目標內部在不同幀圖像中相重疊的部分很難檢測出來。幀間差分法通常不單獨用在目標檢測中,往往與其它的檢測演算法結合使用。
3)codebook
演算法為圖像中每一個像素點建立一個碼本,每個碼本可以包括多個碼元(對應閾值范圍),在學習階段,對當前像素點進行匹配,如果該像素值在某個碼元的學習閾值內,也就是說與之前出現過的某種歷史情況偏離不大,則認為該像素點符合背景特徵,需要更新對應點的學習閾值和檢測閾值。
如果新來的像素值與每個碼元都不匹配,則可能是由於動態背景導致,這種情況下,我們需要為其建立一個新的碼元。每個像素點通過對應多個碼元,來適應復雜的動態背景。
在應用時,每隔一段時間選擇k幀通過更新演算法建立codebook背景模型,並且刪除超過一段時間未使用的碼元。
4)gmm
混合高斯模型(gaussian of micture models,gmm)是較常用的背景去除方法之一(其他的還有均值法、中值法、滑動平均濾波等)。
首先我們需要了解單核高斯濾波的演算法步驟:
混合高斯建模gmm(gaussian mixture model)作為單核高斯背景建模的擴展,是目前使用最廣泛的一種方法,gmm將背景模型描述為多個分布,每個像素的r、g、b三個通道像素值的變化分別由一個混合高斯模型分布來刻畫,符合其中一個分布模型的像素即為背景像素。作為最常用的一種背景建模方法,gmm有很多改進版本,比如利用紋理復雜度來更新差分閾值,通過像素變化的劇烈程度來動態調整學習率等。
5)vibe(2011)
vibe演算法主要特點是隨機背景更新策略,這和gmm有很大不同。其步驟和gmm類似。具體的思想就是為每個像素點存儲了一個樣本集,樣本集中采樣值就是該像素點過去的像素值和其鄰居點的像素值,然後將每一個新的像素值和樣本集進行比較來判斷是否屬於背景點。
其中pt(x)為新幀的像素值,r為設定值,p1、p2、p3….為樣本集中的像素值,以pt(x)為圓心r為半徑的圓被認為成一個集,當樣本集與此集的交集大於設定的閾值#min時,可認為此為背景像素點(交集越大,表示新像素點與樣本集越相關)。我們可以通過改變#min的值與r的值來改變模型的靈敏度。
step1:初始化單幀圖像中每個像素點的背景模型。假設每一個像素和其鄰域像素的像素值在空域上有相似的分布。基於這種假設,每一個像素模型都可以用其鄰域中的像素來表示。為了保證背景模型符合統計學規律,鄰域的范圍要足夠大。當輸入第一幀圖像時,即t=0時,像素的背景模型。其中,ng(x,y)表示空域上相鄰的像素值,f(xi,yi)表示當前點的像素值。在n次的初始化的過程中,ng(x,y)中的像素點(xi,yi)被選中的可能次數為l=1,2,3,…,n。
step2:對後續的圖像序列進行前景目標分割操作。當t=k時,像素點(x,y)的背景模型為bkm(x,y),像素值為fk(x,y)。按照下面判斷該像素值是否為前景。這里上標r是隨機選的;t是預先設置好的閾值。當fk(x,y)滿足符合背景#n次時,我們認為像素點fk(x,y)為背景,否則為前景。
step3:vibe演算法的更新在時間和空間上都具有隨機性。每一個背景點有1/ φ的概率去更新自己的模型樣本值,同時也有1/ φ的概率去更新它的鄰居點的模型樣本值。更新鄰居的樣本值利用了像素值的空間傳播特性,背景模型逐漸向外擴散,這也有利於ghost區域的更快的識別。同時當前景點計數達到臨界值時將其變為背景,並有1/ φ的概率去更新自己的模型樣本值(為了減少緩慢移動物體的影響和攝像機的抖動)。
可以有如下總結,vibe中的每一個像素點在更新的時候都有一個時間和空間上隨機影響的范圍,這個范圍很小,大概3x3的樣子,這個是考慮到攝像頭抖動時會有坐標的輕微來回變化,這樣雖然由於vibe的判別方式仍認為是背景點,但是也會對後面的判別產生影響,為了保證空間的連續性,隨機更新減少了這個影響。而在樣本值保留在樣本集中的概率隨著時間的增大而變小,這就保證了像素模型在時間上面的延續特性。
6)光流
光流是由物體或相機的運動引起的圖像對象在兩個連續幀之間的視在運動模式。它是2d矢量場,其中每個矢量是一個位移矢量,顯示點從第一幀到第二幀的移動。
光流實際上是一種特徵點跟蹤方法,其計算的為向量,基於三點假設:
1、場景中目標的像素在幀間運動時亮度(像素值或其衍生值)不發生變化;2、幀間位移不能太大;3、同一表面上的鄰近點都在做相同的運動;
光流跟蹤過程:1)對一個連續視頻幀序列進行處理;2)對每一幀進行前景目標檢測;3)對某一幀出現的前景目標,找出具有代表性的特徵點(harris角點);4)對於前後幀做像素值比較,尋找上一幀在當前幀中的最佳位置,從而得到前景目標在當前幀中的位置信息;5)重復上述步驟,即可實現目標跟蹤
2、運動場(分為相機固定,但是視角變化和相機是運動的)
1)運動建模(如視覺里程計運動模型、速度運動模型等)
運動學是對進行剛性位移的相機進行構型,一般通過6個變數來描述,3個直角坐標,3個歐拉角(橫滾、俯仰、偏航)。
ⅰ、對相機的運動建模
由於這個不是我們本次所要討論的重點,但是在《概率機器人》一書中提出了很多很好的方法,相機的運動需要對圖像內的像素做位移矩陣和旋轉矩陣的坐標換算。除了對相機建立傳統的速度運動模型外,也可以用視覺里程計等通關過置信度的更新來得到概率最大位置。
ⅱ、對於跟蹤目標的運動建模
該方法需要提前通過先驗知識知道所跟蹤的目標對象是什麼,比如車輛、行人、人臉等。通過對要跟蹤的目標進行建模,然後再利用該模型來進行實際的跟蹤。該方法必須提前知道要跟蹤的目標對象是什麼,然後再去跟蹤指定的目標,這是它的局限性,因而其推廣性相對比較差。(比如已知跟蹤的物體是羽毛球,那很容易通過前幾幀的取點,來建立整個羽毛球運動的拋物線模型)
2)核心搜索演算法(常見的預測演算法有kalman(卡爾曼)濾波、擴展卡爾曼濾波、粒子濾波)
ⅰ、kalman 濾波
kalman濾波器是通過前一狀態預測當前狀態,並使用當前觀測狀態進行校正,從而保證輸出狀態平穩變化,可有效抵抗觀測誤差。因此在運動目標跟蹤中也被廣泛使用。
在視頻處理的運動目標跟蹤里,每個目標的狀態可表示為(x,y,w,h),x和y表示目標位置,w和h表示目標寬高。一般地認為目標的寬高是不變的,而其運動速度是勻速,那麼目標的狀態向量就應該擴展為(x,y,w,h,dx,dy),其中dx和dy是目標當前時刻的速度。通過kalman濾波器來估計每個時刻目標狀態的大致過程為:
對視頻進行運動目標檢測,通過簡單匹配方法來給出目標的第一個和第二個狀態,從第三個狀態開始,就先使用kalman濾波器預測出當前狀態,再用當前幀圖像的檢測結果作為觀測值輸入給kalman濾波器,得到的校正結果就被認為是目標在當前幀的真實狀態。(其中,zt為測量值,為預測值,ut為控制量,kt為增益。)
ⅱ、擴展卡爾曼濾波(ekf)和無跡卡爾曼濾波(ukf)
由於卡爾曼濾波的假設為線性問題,無法直接用在非線性問題上,ekf和ukf解決了這個問題(這個線性問題體現在用測量量來計算預測量的過程中)。ekf是通過構建線性函數g(x),與非線性函數相切,並對每一時刻所求得的g(x)做kf,如下圖所示。
ukf與ekf去求解雅可比矩陣擬合線性方程的方法不同,通過對那個先驗分布中的採集點,來線性化隨機變數的非線性函數。與ekf所用的方法不同,ukf產生的高斯分布和實際高斯分布更加接近,其引起的近似誤差也更小。
ⅲ、粒子濾波
1、初始狀態:基於粒子濾波的目標追蹤方法是一種生成式跟蹤方法,所以要有一個初始化的階段。對於第一幀圖像,人工標定出待檢測的目標,對該目標區域提出特徵;
2、搜索階段:現在已經知道了目標的特徵,然後就在目標的周圍撒點(particle), 如:a)均勻的撒點;b)按高斯分布撒點,就是近的地方撒得多,遠的地方撒的少。論文里使用的是後一種方法。每一個粒子都計算所在區域內的顏色直方圖,如初始化提取特徵一樣,然後對所有的相似度進行歸一化。文中相似性使用的是巴氏距離;
3、重采樣:根據粒子權重對粒子進行篩選,篩選過程中,既要大量保留權重大的粒子,又要有一小部分權重小的粒子;
4、狀態轉移:將重采樣後的粒子帶入狀態轉移方程得到新的預測粒子;
5、測量及更新:對目標點特徵化,並計算各個粒子和目標間的巴氏距離,更新粒子的權重;
6、決策階段:每個粒子都獲得一個和目標的相似度,相似度越高,目標在該范圍出現的可能性越高,將保留的所有粒子通過相似度加權後的結果作為目標可能的位置。
3)meanshift演算法
meanshift演算法屬於核密度估計法,它不需要任何先驗知識而完全依靠特徵空間中樣本點的計算其密度函數值。對於一組采樣數據,直方圖法通常把數據的值域分成若干相等的區間,數據按區間分成若干組,每組數據的個數與總參數個數的比率就是每個單元的概率值;核密度估計法的原理相似於直方圖法,只是多了一個用於平滑數據的核函數。採用核函數估計法,在采樣充分的情況下,能夠漸進地收斂於任意的密度函數,即可以對服從任何分布的數據進行密度估計。
meanshift演算法步驟
1、通過對初始點(或者上一幀的目標點)為圓心,繪制一個半徑為r的圓心,尋找特徵和該點相似的點所構成的向量;
2、所有向量相加,可以獲得一個向量疊加,這個向量指向特徵點多的方向;
3、取步驟二的向量終點為初始點重復步驟一、二,直到得到的向量小於一定的閾值,也就是說明當前位置是特徵點密度最密集的地方,停止迭代,認為該點為當前幀的目標點;
4)camshift演算法
camshift演算法是meanshift演算法的改進,稱為連續自適應的meanshift演算法。camshift 是由meanshift 推導而來 meanshift主要是用在單張影像上,但是獨立一張影像分析對追蹤而言並無意義,camshift 就是利用meanshift的方法,對影像串列進行分析。
1、首先在影像串列中選擇目標區域。
2、計算此區域的顏色直方圖(特徵提取)。
3、用meanshift演演算法來收斂欲追蹤的區域。
4、通過目標點的位置和向量信息計算新的窗口大小,並標示之。
5、以此為參數重復步驟三、四。
camshift 關鍵就在於當目標的大小發生改變的時候,此演算法可以自適應調整目標區域繼續跟蹤。
3、小結
第一階段的單目標追蹤演算法基本上都是傳統方法,計算量小,在嵌入式等設備中落地較多,opencv中也預留了大量的介面。通過上面的兩節的介紹,我們不難發現,目標檢測演算法的步驟分為兩部分,一部分是對指定目標尋找可以跟蹤的特徵,常用的有顏色,輪廓,特徵點,軌跡等,另一部分是對目標特徵進行跟蹤,如上文所提及的方法。所以目標檢測方法的發展,也可總結為兩個方面,一個是如何去獲得更加具有區分性的可跟蹤的穩定特徵,另一個是如何建立幀與幀之間的數據關聯,保證跟蹤目標是正確的。
隨著以概率為基礎的卡爾曼濾波、粒子濾波或是以meanshift為代表向量疊加方法在目標檢測的運用,使得目標檢測不再需要假設自身的一個狀態為靜止的,而是可以是運動的,更加符合復雜場景中的目標跟蹤。
c. 光線跟蹤演算法的例子
為了說明光線跟蹤所用的基本原理,我們來看計算一個光線與球體交點的例子。用 i 表示球面上的點,c 表示球心,r 表示半徑,那麼球面的公式為 . 如果定義一條線的起點即光線起點是 s,方向是 d,那麼線上的每個點都可以表示為
其中 t 是定義線上與起點距離的常數,為了簡化起見,通常 d 定義為單位矢量。那麼,在這種情況下已知 s、d、c 以及 r,於是代入 i 得到:
簡化 ,那麼
那麼二次方程的解是
這只是直線光線與球體交點的所用的數學公式,當然對於通用的光線跟蹤來說是遠遠不夠的,但是它至少表示了這個演算法如何使用的一個實例。
d. 計算機圖形學, 光線跟蹤演算法的過程是什麼
光線跟蹤思路:從視點出發,通過圖像平面上每個像素中心向場景發出一條光線,光線的起點為視點,方向為像素中心和視點連線單位向量。光線與離視點最近的場景物體表面交點有三種可能:
當前交點所在的物體表面為理想漫射面,跟蹤結束。
當前交點所在的物體表面為理想鏡面,光線沿其鏡面發射方向繼續跟蹤。
當前交點所在的物體表面為規則透射面,光線沿其規則透射方向繼續跟蹤。
偽代碼:
void traceray(const vec3& start, const vec3& direction, int depth, color& color)
{
vec3 intersectionpoint, reflecteddirection, transmitteddirection;
color localcolor, reflectedcolor, transmittedcolor;
if (depth >= max_depth) {
color = black; //#000
}
else {
ray ray(start, direction); //取start起點,方向direction為跟蹤射線;
if ( !scene->hasintersection(ray) )
color = backgroundcolor;
else {
計算理起始點start最近的交點intersectionpoint,
記錄相交物體intersectionobject,
// #1
shade(intersectionobject, intersectionpoint, localcolor);
// #2
if ( intersectionpoint所在面為鏡面 ) {
計算跟蹤光想s在intersectionpoint處的反射光線方向reflecteddirection,
traceray(intersectionpoint, reflecteddirection, depth 1, reflectedcolor);
}
// #3
if ( intersectionpoint所在的表面為透明面 ) {
計算跟蹤光線s在intersectionpoint處的規則透射光線方向transmitteddirection,
traceray(intersectionpoint, transmitteddirection, depth 1, transmittedcolor);
}
// #summarize
color = localcolor ks * reflectedcolor kt * transmittedcolor;
}// else
} //else
}
// 局部光照模型計算交點intersectionpoint處的局部光亮度localcolor
void shade(const object& intersectionobj, const vec3& intersectionpoint, color& localcolor)
{
確定intersectionobj在intersectionpoint處的單位法向量n,
漫反射系數kd,
鏡面反射系數ks,
環境反射系數ka;
localcolor = ka * ia; //ia為環境光亮度
for ( 每一個點光源pointlight ) {
計算入射光線單位向量l和虛擬鏡面法向單位向量h,
// 由phong模型計算光源pointlight在intersectionpoint處的漫反射和鏡面反射光亮度
localcolor = ( ipointlight * ( kd * (n.dot(l)) ks * (n.dot(h))^n ) );
}
}
e. 光流(optical flow)
光流是由觀察者和場景之間的[相對運動]引起的視覺場景中物體、表面和邊緣的運動模式。一般而言,光流是由於場景中前景目標本身的移動、觀測者運動,或者兩者的共同運動所產生的。
光流在很多領域中都被用到,例如視頻中的運動目標檢測,視頻壓縮等等。
在分析光流時,需要用到兩個重要假設: 1.對象的像素強度在連續幀之間不會改變。2.相鄰像素具有相似的運動。 下面我們運用這兩個假設來推導光流公式。
光流法實際是通過檢測圖像像素點的強度隨時間的變化進而推斷出物體移動速度及方向的方法。假設該移動很小,假設該移動很小,那麼可以根據泰勒級數得出:
根據假設1,由於移動很小,此時這時對象位移不會改變對象的像素強度:
進一步得出:
最終得出光流公式:
這里的 , 是x, y方向上的速率,或稱為i(x, y, t)的光流。而 , , 則是圖像(x, y, t)在對應方向上的偏導數。可以看到光流公式中有兩個未知量 , , 無法直接求解。因此我們需要更多的外部條件來求解方程。
lucas-kanade 方法
求解光流方程有很多方法,其中最著名的便是 lucas-kanade方法。它應用了之前提到的第二個假設,即所有相鄰像素都將具有相似的運動。對於每一個像素,lucas-kanade 方法選取與它相鄰的8個像素進行分析。根據假設,所有 9 個像素都有相同的運動。所以現在我們的問題求解 只有2個未知變數的9個方程組。
......
這樣的方程組沒有唯一解,這里我們使用最 最小二乘擬合 方法獲得一個最優近似解。
lucas-kanade方法的局限性
由於上述假設和分析都是針對較小的運動,光流演算法會受到突然移動的影響。如果兩幀圖像之間的運動太大,光流法可能會失效。對於這個問題我們可以通過圖像金字塔來解決,當我們使用更高層的金字塔圖像時,小的運動被去除,大的運動變成小運動。然後再應用 lucas-kanade,就可以得到正確的光流。
opencv 光流演示:https://docs.opencv.org/3.4/d4/dee/tutorial_optical_flow.html