/*2017-1-14*/ /*視頻的讀取。。。*/ int g_n=0; void on_change(int pos,void *)//看來void*不能省! { printf("g_n id %d\n",pos); } int main() { //結構體第一個字母都是大寫,函數小寫 //打開視頻文件:其實就是創建一個VideoCapture結構 VideoCapture capture("E:\\vedio\\01.avi"); //檢測是否正常打開:成功打開時,isOpened返回ture if(!capture.isOpened()) cout<<"fail to open!"<<endl; //獲取整個幀數 //capture.get(宏)能夠獲取各類屬性信息 long total = capture.get(CV_CAP_PROP_FRAME_COUNT); cout<<"視頻"<<total<<"幀"<<endl; namedWindow("視頻",1); createTrackbar("position","視頻",&g_n,total,&on_change); on_change(g_n,0); Mat frame; while(capture.read(frame)){ //frame 保存當前讀取幀文件,會保存讀取信息,讀到結尾會返回NULL imshow("car",frame); char c = waitKey(1);//這一步很重要,否則看不到視頻內容,延時30ms(每秒30幀) while( c ==' '){//可使用空格暫停 c=waitKey(0); } } capture.release(); waitKey(0); return 0; } /* Mat這個類有兩部分數據。 一個是matrixheader,這部分的大小是固定的,包含矩陣的大小,存儲的方式,矩陣存儲的地址等等。 另外一個部分是一個指向矩陣包含像素值的指針。 */ /*,使用矩陣爲Mat賦值的時候,位數必定要匹配,CV_8U是8位,而一個int有4個字節,*/ int main() { char data[]={1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18}; //尼瑪,我說怎麼搜到的程序上有使用char定義數組給Mat賦值的,這TMD位數不匹配,因此用char Mat M = Mat(1, 18, CV_8U, data); //CV_8U是8位,而一個int有4個字節,這是否是說明, //這樣成立的話,是否是說明,即便是64位的編譯器,一個字節也是8位(廢話!!) //Mat M(3,3,CV_8UC2); //M=(Mat_<int>(3,3) <<); // for(int y=0;y<M.rows;y++){ // for(int x=0;x<M.cols;x++){ // int value = M.at<int>(y,x); // printf("%2d",value); // } // cout<<endl; // } cout<<sizeof(int)<<endl; cout<<M<<endl; waitKey(0); return 0; } /* 函數 M.channels() M.ptr() 分別返回M的通道數和行地址 儘管M是以兩個通道類型保存的數據,而且二維矩陣,可是,返回指針任然是一維,行式存儲 */ char* p =(char *)M.ptr(0);//ptr<>()函數返回第i行的地址 cout<<(int)++data[2]<<endl; //儘管M是以兩個通道類型保存的數據,而且二維矩陣,可是,返回指針任然是一維,行式存儲 //只能經過逐步移動指針,訪問同一元素的不一樣通道的值 //不作強制轉換,就會輸出字符,看來上面是自動強制了 /*2017-1-16*/ /*分離通道,看來,使用*/char* p =(char *)M.ptr(0)/*返回的行地址,增長也是以單個字節爲單位*/ int main() { Mat M=imread("E:\\visio\\1.jpg",1); Mat rtemp=Mat::zeros(M.size(),CV_8UC1); Mat gtemp=Mat::zeros(M.size(),CV_8UC1); Mat btemp=Mat::zeros(M.size(),CV_8UC1); for(int i=0;i<M.rows;i++){ char* data = M.ptr<char>(i); for(int j=0;j<M.cols;j++){ rtemp.at<uchar>(i,j) = (data+3*j)[0]; gtemp.at<uchar>(i,j) = (data+3*j)[1]; btemp.at<uchar>(i,j) = (data+3*j)[2]; } } imshow("Rtongdao",rtemp); imshow("Gtongdao",gtemp); imshow("Btongdao",btemp); waitKey(0); } //彩色版本 而且使用 Vec3b //template<typename_Tp, int n> class Vec : public Matx<_Tp, n, 1> {...}; typedef Vec<uchar, 3> Vec3b; //其實這句就是定義一個uchar類型的數組,長度爲3 Mat rtemp=Mat::zeros(M.size(),CV_8UC3); Mat gtemp=Mat::zeros(M.size(),CV_8UC3); Mat btemp=Mat::zeros(M.size(),CV_8UC3); for(int i=0;i<M.rows;i++){ char* data = M.ptr<char>(i); char* bdata = btemp.ptr<char>(i); char* gdata = gtemp.ptr<char>(i); char* rdata = rtemp.ptr<char>(i); for(int j=0;j<M.cols;j++){ (bdata+3*j)[0] = (data+3*j)[0]; gtemp.at<Vec3b>(i,j)[1]=(data+3*j)[1]; ////其實這句就是定義一個uchar類型的數組,長度爲3 (rdata+3*j)[2] = (data+3*j)[2]; } } /*繪製一維直方圖*/ int main() { //【1】載入原圖並顯示 Mat srcImage = imread("E:\\visio\\1.jpg", 1); imshow("原圖",srcImage); if(!srcImage.data) {cout << "fail to load image" << endl; return 0;} Mat btemp(srcImage.size(),CV_8UC1); Mat gtemp(srcImage.size(),CV_8UC1); Mat rtemp(srcImage.size(),CV_8UC1); vector<Mat> channel; //建立mat爲元素的可變數組; split(srcImage,channel); //使用split函數分離通道爲三個元素,存放到channles[] btemp = channel.at(0); gtemp = channel.at(1); rtemp = channel.at(2); imshow("b",btemp); imshow("g",gtemp); imshow("r",rtemp); //【2】定義變量 MatND bdstHist,gdstHist,rdstHist; int dims = 1;//幾維直方圖 float hranges[] = {0, 255};//範圍數組 const float *ranges[] = {hranges};// 這裏須要爲const類型,幾維,從這取幾個範圍,元素是數組 int size[] = {256};//每一維度尺寸的大小,幾維,就有幾個尺寸,裏面的元素是數 int channels = 0; //【3】計算圖像的直方圖 calcHist(&btemp, 1, &channels, Mat(), bdstHist, dims, size, ranges); calcHist(>emp, 1, &channels, Mat(), gdstHist, dims, size, ranges); calcHist(&rtemp, 1, &channels, Mat(), rdstHist, dims, size, ranges); int scale = 5;//能夠根據用戶的需求調節顯示圖像 Mat bdstImage(256 , 64*scale, CV_8U, Scalar(0));//保存白色高度,8位的,單通道圖像 Mat gdstImage(256 , 64*scale, CV_8U, Scalar(0)); Mat rdstImage(256 , 64*scale, CV_8U, Scalar(0)); //【4】獲取最大值和最小值 double bminValue = 0,gminValue = 0,rminValue = 0; double bmaxValue = 0,gmaxValue = 0,rmaxValue = 0; minMaxLoc(bdstHist,&bminValue, &bmaxValue, 0, 0); minMaxLoc(gdstHist,&gminValue, &gmaxValue, 0, 0); minMaxLoc(rdstHist,&rminValue, &rmaxValue, 0, 0); //【5】繪製出直方圖 int hpt = 64 ; for(int i = 0; i < 256; i++)//有256組,因此要畫256次 { float binValue = bdstHist.at<float>(i);//獲取對應直方柱的值 //感受當Mat.at<>()[],,若是對象的元素是單通道,不須要[],當元素爲數組vec3b,須要[] int realValue = saturate_cast<int>(binValue * hpt/bmaxValue); //是加是減,乘除,都會超出一個像素灰度值的範圍 //當運算完以後,結果爲負,則轉爲0,結果超出255,則爲255。 rectangle(bdstImage,//對角兩個點,畫矩形 Point(i*scale, 64 - 1), //原點在左上角,因此點在下水平軸上 Point((i+1)*scale - 3, 64 - realValue), // -3,應該是柱與柱之間測間隔。 Scalar(255));// scalar(255),邊框填充白色 } for(int i = 0; i < 256; i++) { float binValue = gdstHist.at<float>(i); int realValue = saturate_cast<int>(binValue * hpt/gmaxValue); rectangle(gdstImage, Point(i*scale, 64 - 1), Point((i+1)*scale - 3, 64 - realValue), Scalar(255)); } for(int i = 0; i < 256; i++) { float binValue = rdstHist.at<float>(i); int realValue = saturate_cast<int>(binValue * hpt/rmaxValue); rectangle(rdstImage, Point(i*scale, 64 - 1), Point((i+1)*scale - 3, 64 - realValue), Scalar(255)); } imshow("一維直方圖b", bdstImage); imshow("一維直方圖g", gdstImage); imshow("一維直方圖r", rdstImage); waitKey(0); return 0; } /*以灰度圖像加載圖像是經過公式Y=0.2..*b+0.5*g+0.1..*r=R=G=B計算*/ /*RGB顏色空間中,對角線上,沒有顏色,只有亮度,即當R=G=B*/ /*色相(Hue):指物體傳導或反射的波長。定義各類顏色,取值範圍0度-360度,直徑兩端,是互補色(混在一塊兒是白色) 飽和度(Saturation):又稱色度,是指色彩的強度或純度。越靠近內軸,彷佛就摻了白色,變得淡了 亮度 (brightness/Intensity):是指顏色的相對明暗度(能夠理解爲光照),一般以 0% (黑色) 到 100% (白色) 的百分比來衡量。 */ /*不必value非得定義成char,定義成int徹底能覆蓋,CV-8U全部數值 */ /*當Mat型數據的元素是,三通道時,使用(vec3b)訪問時可使用下標定位 hsvImage.at<Vec3b>(i,j)[1]*/ int value; for(int i=0;i<hsvImage.rows;i++){ for(int j=0;j<hsvImage.cols;j++) { value = hsvImage.at<Vec3b>(i,j)[1];//當char被賦值170時可能已經發生了溢出, cout<<value<<" "; //因此以後不管怎麼處理都挽回不了, } } // imshow("hsv",hsvImage); cout<<endl<<format(srcImage,"python")<<endl<<endl; cout<<format(hsvImage,"python"); /*srcImage.copyTo(hsvImage);//這個函數應該是把數據類型也改了 */ /*copy的使用*/ C++: void Mat::copyTo(OutputArray m) const C++: void Mat::copyTo(OutputArray m, InputArray mask) const /*通常狀況下使用第一種複製的方法,目的矩陣和數據矩陣大小須要相同 (一半設置爲圖像的感興趣區域,用來賦值LOGO) 第二種使用方式,有一個mask(只須要單通道), mask須要與數據矩陣等大,其非零區域代表須要使用的地方,零的不會用來複制,因此目的矩陣的對應區域不變 */ /*2016-1-18*/ /* cvConvertScale(IplImage*,IplImage*,1.0,0)函數彷佛只能使用IplImage=cvLoadImage加載的圖像才能運行; 在Mat數據類型中有個函數convertTo()函數,以下,也能夠轉換數據類型 */ /* 使用cout顯示的方式,不如使用循環指針訪問獲得的真實 */ /* 保存HSV格式的圖像,圖像數據只能用char大小的數據 */ int main(int argc, char** argv) { char data[]={1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18}; Mat src2 = Mat(3, 2, CV_8UC3, data); Mat src3; src2.convertTo(src3,CV_32FC3,1.0,0.1); float value[3]; for(int i=0;i<src3.rows;i++){ for(int j=0;j<src3.cols;j++) { value[0] = src3.at<Vec3f>(i,j)[0]; value[1] = src3.at<Vec3f>(i,j)[1]; value[2] = src3.at<Vec3f>(i,j)[2]; cout<<"<"<<value[0]<<" "<<value[1]<<" "<<value[2]<<endl; } } //保存HSV格式的圖像,圖像數據只能用char大小的數據 hsvImage.convertTo(hsvImage,CV_8UC3,(1.0/360)*255,0); imshow("hsv",hsvImage); cout<<endl<<format(src3,"python"); imshow("jieguo",src3); getchar(); return 0; } /* 自定義結構體元素,進行腐蝕操做:若是B的原點平移到點(x,y),那麼B將徹底包含於X中。輸入的二值圖像只包含黑色(0)和白色(255)像素,所以結構元素覆蓋的圖像區域中有黑色像素,則錨點所在像素(x,y)將會被替換成黑色0,不然替換成白色255。而物體的邊界一般會有黑色像素,因此腐蝕至關於收縮邊界。 另外一種理解,腐蝕就是把當前像素替換成所定義的像素集中的最小像素值。 */ int main() { char data[20][20]={0}; for(int i=3;i<8;i++){ for(int j=4;j<10;j++){ data[i][j]=255; } } for(int i=8;i<16;i++){ for(int j=2;j<8;j++){ data[i][j]=255; } } Mat srcImage(20,20, CV_8UC1, data); cout<<format(srcImage,"python"); //使用自定義結構元素進行腐蝕的正確姿式, //網上說本身定義麻煩,確實本身賦值結構元素是有點麻煩, //自定義一個有「形狀」的mat,erode 自動能夠判斷其餘參數 char vaule[16]={ 0,0,0,0, 0,0,1,0, 0,1,1,0, 0,0,0,0}; Mat element(4,4,CV_8UC1,vaule); Mat result(srcImage.size(),CV_8UC1,Scalar::all(0)); erode(srcImage,result,element);//默認腐蝕操做一次 cout<<endl<<endl<<format(result,"python"); waitKey(0); return 0; } /*2016-1-17*/ /*輪廓提取 一、提取前應該先使用邊緣算子處理,才能提取到單根輪廓 二、看來轉爲灰度圖,保存的通道也是 cvtColor 三、vector<Vec4i> hierarchy;保存輪廓關係一維數組,元素是4個數 vector< vector<Point> > contours;保存輪廓數據的「二維數組」,元素是點 四、//hierarchy[][]後一個輪廓、前一個輪廓、內嵌輪廓,父輪廓的contours數組索引編號(行) 五、反過來用(y,x)srcImage.at<Vec3b>(y,x)[2]=255;//對對,這樣使用是先返回行,對應於座標是y */ int main() { Mat srcImage=imread("E:/visio/lunkuo.jpg"); //cvtColor(srcImage,srcImage,CV_BGR2GRAY,0); //這就是單通道了,看來轉爲灰度圖,保存的通道也是 1 imshow("jiegou",srcImage); // cout<<format(srcImage,"python")<<endl; Mat gimage; GaussianBlur(srcImage,gimage,Size(3,3),0);//高斯模糊 // imshow("GAUSS Image",gimage);//這裏仍是彩色的 Mat cimage; Canny(gimage,cimage,100,250); //圖像處理之Canny 邊緣檢測 1 // imshow("Cann Image",cimage); // cout<<format(image,"python")<<endl;//這裏已經變成單通道了 Mat lunkuo=Mat::zeros(srcImage.size(),CV_8UC3); //繪製 vector<Vec4i> hierarchy; vector< vector<Point> > contours; //第二個參數:contours,定義爲「vector<vector<Point>> contours」, //是一個向量,而且是一個雙重向量,向量 //內每一個元素保存了一組由連續的Point點構成的點的集合的向量,每一組Point點集就是一個輪廓。 //有多少輪廓,向量contours就有多少元素。 //使用cvFindContours的返回值爲int型,指檢測出來的輪廓的個數 findContours(cimage,contours,hierarchy,1,CV_CHAIN_APPROX_NONE);//沒有返回值 //只有以第四種模式尋找輪廓,hierarchy的數組,才能徹底被附上應有的值! int idx = 0; int i=0; for( ; idx<contours.size(); idx++){ for(int j=0;j<contours[idx].size();j++){ int x=contours[idx][j].x,y=contours[idx][j].y; if(idx % 2 == 0) lunkuo.at<Vec3b>(y,x)[2]=255; if(idx % 2 != 0) lunkuo.at<Vec3b>(y,x)[1]=255; //除了模式0以外, //1,2,3模式每個輪廓都有兩個存儲數組,僅有幾個點有差異,8個輪廓共16個數組 // Scalar color(0,0,255 ); // drawContours( lunkuo, contours, idx, color, 1, 8, hierarchy ); // 使用drawContours,能夠順便給線復顏色值,idx是contour數組的行變量, // 應該還不至於操做父子結構。。以後的參數掠過 } imshow("lunkuo",lunkuo); } // imshow("lunkuo",lunkuo); /* 模式2-1 查找全部輪廓,所有輸出 for(i=0;i<contours.size();i++) { //contours[i]表明的是第i個輪廓, //contours[i].size()表明的是第i個輪廓上全部的像素點數 for(int j=0;j<contours[i].size();j++) { //繪製出contours向量內全部的像素點 //這裏要想畫在原圖上,要畫gimage,由於gimage已經與原圖像不同了 //而且原圖像畫邊界的部分是白色,都是255,因此會有截斷彷佛 //還有一種辦法,更改原圖像 int x=contours[i][j].x,y=contours[i][j].y; //還有一種辦法,更改原圖像 ,數組的話還不能總體賦值呢,就先一步一步操做吧 //這裏竟然要反過來用,像素點(x,y),,這裏要輸入(y,x) //否則就使用Point point(x,y),,srcImage.at<Vec3b>(p)[] srcImage.at<Vec3b>(y,x)[2]=255;//對對,這樣使用是先返回行,對應於座標是y srcImage.at<Vec3b>(y,x)[0]=0; //那麼使用的時候,也得反着用 srcImage.at<Vec3b>(y,x)[1]=0; } //輸出hierarchy向量內容 cout<<"向量hierarchy的第" <<i<<" 個元素內容爲:"<<endl <<hierarchy[i]<<endl<<endl; } imshow("lunkuo",srcImage); */ /* 模式 RETR_EXTERNAL==0 只查找最外層輪廓, 輪廓保存數組,仍然是二維,儘管只保存一個輪廓 沒有使用層次關係 hierarchy for(int i=0;i<contours.size();i++) { //contours[i]表明的是第i個輪廓,contours[i].size()表明的是第i個輪廓上全部的像素點數 for(int j=0;j<contours[i].size();j++) { //繪製出contours向量內全部的像素點 Point P=Point(contours[i][j].x,contours[i][j].y); lunkuo.at<uchar>(P)=255; } //輸出hierarchy向量內容 char ch[256]; sprintf(ch,"%d",i); string str=ch; //hierarchy[][]後一個輪廓、前一個輪廓、內嵌輪廓的索引編號,父輪廓、應該是contours的行號 cout<<"向量hierarchy的第" <<str<<" 個元素內容爲:"<<endl<<hierarchy[i]<<endl<<endl; } imshow("lunkuo",lunkuo); */ waitKey(0); return 0; } /*2017-1-19*/ //圖像的細化,先有原圖獲得灰度圖cvtColor //在有灰度圖獲得二值化threshold //由程序中判斷邊緣點的方法將其記錄到edg,再由索引表八連通圖的方法判斷是否爲刪除點,記錄到delete,以後刪除 //以後再有邊緣函數fincontours試試.......... void deletecontours(Mat &srcImage) { unsigned char deletemark[256] = { 0,0,0,0,0,0,0,1, 0,0,1,1,0,0,1,1, 0,0,0,0,0,0,0,0, 0,0,1,1,1,0,1,1, 0,0,0,0,0,0,0,0, 1,0,0,0,1,0,1,1, 0,0,0,0,0,0,0,0, 1,0,1,1,1,0,1,1, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 1,0,0,0,1,0,1,1, 1,0,0,0,0,0,0,0, 1,0,1,1,1,0,1,1, 0,0,1,1,0,0,1,1, 0,0,0,1,0,0,1,1, 0,0,0,0,0,0,0,0, 0,0,0,1,0,0,1,1, 1,1,0,1,0,0,0,1, 0,0,0,0,0,0,0,0, 1,1,0,1,0,0,0,1, 1,1,0,0,1,0,0,0, 0,1,1,1,0,0,1,1, 0,0,0,1,0,0,1,1, 0,0,0,0,0,0,0,0, 0,0,0,0,0,1,1,1, 1,1,1,1,0,0,1,1, 1,1,0,0,1,1,0,0, 1,1,1,1,0,0,1,1, 1,1,0,0,1,1,0,0 };//索引表 Mat image; Mat copyImage; cvtColor(srcImage,image,CV_BGR2GRAY); threshold(image, copyImage, 128, 1, THRESH_BINARY);//這樣,像素點就變成0 1 //image = image * 255 顯示圖像 // cout<<endl<<endl<<format(copyImage,"python"); //僅使用最外一層輪廓,不用作預處理,而且只有一層 vector<Point> edg; vector<Point> detele; bool flag = true; int times = 0; int sum; char vaule[8] = { 0 }; while( flag ) { times++; flag = false; int number0=0; //記得以前使用一種函數,返回的是行號 注意像素點的表示(行號,列號) //若想以後正確使用,一開始就得按照規矩來 for(int i=1;i<copyImage.rows-1;i++){ for(int j=1;j<copyImage.cols-1;j++){ int x=i,y=j; if( copyImage.at<char>(x,y) == 1 ){ vaule[0] = copyImage.at<char>(x-1,y-1); vaule[1] = copyImage.at<char>(x-1,y); vaule[2] = copyImage.at<char>(x-1,y+1); vaule[3] = copyImage.at<char>(x,y+1); vaule[4] = copyImage.at<char>(x+1,y+1); vaule[5] = copyImage.at<char>(x+1,y); vaule[6] = copyImage.at<char>(x+1,y-1); vaule[7] = copyImage.at<char>(x,y-1); sum = vaule[0] & vaule[1] & vaule[2] & vaule[3] & vaule[4] & vaule[5] & vaule[6] & vaule[7]; if(sum == 0) { edg.push_back(Point (x,y)); } } } } for(int i=0 ; i<edg.size() ; i++){ int x,y; x = edg[i].x; y = edg[i].y; vaule[0] = copyImage.at<char>(x-1,y-1); vaule[1] = copyImage.at<char>(x-1,y); vaule[2] = copyImage.at<char>(x-1,y+1); vaule[3] = copyImage.at<char>(x,y+1); vaule[4] = copyImage.at<char>(x+1,y+1); vaule[5] = copyImage.at<char>(x+1,y); vaule[6] = copyImage.at<char>(x+1,y-1); vaule[7] = copyImage.at<char>(x,y-1); //因爲字符轉換int使得數值變成0了估計!!!!!!!!!!!!! vaule[1]*=2; vaule[2]*=4; vaule[3]*=8; vaule[4]*=16; vaule[5]*=32; vaule[6]*=64; vaule[7]*=128; sum = vaule[0] + vaule[1] + vaule[2] + vaule[3] + vaule[4] + vaule[5] + vaule[6] + vaule[7] ; //若發現該點是可刪除點,即當即刪除.刪除會影響到之後的判斷,故先作標記 if(deletemark[sum] == 1){ detele.push_back(Point (x,y));//vertor的插入 } } if(detele.size() > 0)//vector的成員函數.size() flag = true; for(int i=0;i<detele.size();i++){ int x,y; x=detele[i].x,y=detele[i].y; copyImage.at<char>(x,y)=0; } cout<<endl<<"time"<<times<<endl; detele.clear(); edg.clear(); } imshow("jieguo",copyImage*255); } /*2017-2-2*/ /* hu矩 質量中心的簡稱,它同做用於質點繫上的力系無關。設?n個質點組成的質點系?,其各質點的質量分別爲m1,m2,…,mn。若用?r1?,r2,……,rn分別表示質點系中各質點相對某固定點的矢徑,rc?表示質心的矢徑,則有rc=(m1r1+m2r2+……+mnrn)/(m1+m2+……+mn)。當物體具備連續分佈的質量時,質心C的矢徑?rc=∫ρrdτ/∫ρdτ,式中ρ爲體(或面、線)密度;dτ爲至關於ρ的體(或面?、線)元?;積分在具備分佈密度ρ的整個物質體(或面、線)上進行。由牛頓運動定律或質點系的動量定理,可推導出質心運動定理:質心的運動和一個位於質心的質點的運動相同, 中心矩具備平移不變性,它與中心作差了 歸一化中心矩,應該具備縮放平移不變性 Hu矩具備平移,縮放,旋轉不變性,應該是綜合了好多矩的向量 中心矩是20 11 02開始,,由於00就是p+q 00 ;01 10中心矩不存在 */ int main() { char data[100]={0}; for(int i=3;i<6;i++){ for(int j=3;j<6;j++) data[10*i+j]=255; } Mat srcImage= Mat(10, 10, CV_8U, data); Moments mts=moments(srcImage,2);//2是非零的意思,會將圖像中大於零的都認爲1 //這應該是庫中的函數,能夠直接使用,計算各階矩(原點矩,中心矩)0,1,2 double hu[7]; HuMoments(mts, hu);//只須要接受各階矩,計算獲得hu矩,存入hu數組 for (int i=0; i<7; i++) { cout << log(abs(hu[i])) <<endl; } } /*2017-2-3*/ /* resylt = matchShapes(contours[],contours [],1 ) 函數的使用方法,輸入比較的邊緣數組,比較方法1 2 三、 返回值是個浮點數,顯示比較距離,越小越類似 其內部仍是先計算出兩個圖像的hu矩,而後經過一些數學方法計算兩個hu矩之間的差距 */ int main() { Mat srcImage0=imread("E:/visio/dev/xin.JPG"); Canny(srcImage0,srcImage0,100,250); //即便看似很規整,仍是直接檢測最外層輪廓,仍是會檢測到前一個後一個的關係,不是繼承 Mat srcImage1=imread("E:\\visio\\dev\\youxin.JPG"); Canny(srcImage1,srcImage1,100,250); Mat lunkuo0=Mat::zeros(srcImage0.size(),CV_8U); //繪製 Mat lunkuo1=Mat::zeros(srcImage1.size(),CV_8U); //繪製 vector<Vec4i> hierarchy; vector< vector<Point> > contours0; vector< vector<Point> > contours1; findContours(srcImage0,contours0,hierarchy,0,CV_CHAIN_APPROX_NONE);//沒有返回值 findContours(srcImage1,contours1,hierarchy,0,CV_CHAIN_APPROX_NONE);//沒有返回值 drawContours( lunkuo0, contours0, 0, 255, 1, 8 ); drawContours( lunkuo1, contours1, 0, 255, 1, 8 ); //經過上面的邊緣檢測以後,一幅圖才能尋找出一個邊緣。 imshow("lunkuo0",lunkuo0); imshow("lunkuo1",lunkuo1); double result; result=matchShapes(contours0[0],contours1[0],1,0); //比較方法的宏定義有三個,只是這裏居然輸入的是邊緣數組,而不是二值化圖像,1,2,3 //使用這種方法,比較結果不能提錢限定只能從總的比較結果中選取一個最小的 cout<<result<<endl; waitKey(0); } /*matchTempate()模版匹配到的使用*/ //計算一個二維矩陣中最大值最小值,以及他們的位置 minMaxLoc( //取出矩陣中最大最小值 const CvArr* arr,//目標矩陣 double* min_val,//最小值 double* max_val,//最大值 CvPoint* min_loc = NULL,//最小值位置 CvPoint* max_loc = NULL,//最大值位置 const CvArr* mask = NULL//掩膜,以便作區域操做 ); int main() { Mat srcImage=imread("zong.jpg"); Mat temp=imread("ling.jpg"); int srcw=srcImage.rows,srch=srcImage.cols,tempw=temp.rows,temph=temp.rows; int resultw=srcw-tempw+1;//儲存匹配結果的矩陣,按照上述原理,應該的長度和寬度就是這樣 int resulth=srch-temph+1; Mat result(resultw,resulth,CV_8U,Scalar::all(0)); //結果矩陣,保存着匹配信息,估計以後須要可以對矩陣計算的相關函數 matchTemplate(srcImage,temp,result,1); //這個函數類型爲void, //使用一個與temp大小的框,遍歷原圖像,而後對比temple(對比方法6中) //有6種比較計算方法,估計也是計算矩,不一樣的方法,result中值的大小意義不一樣 double min,max; Point minpoint,maxpoint,rightpoint; minMaxLoc(result,&min,&max,&minpoint,&maxpoint,Mat()); //查找矩陣中最大值最小值,及其餘們的位置 rightpoint=minpoint;//使用了第一種方法,最小值表示最類似 rectangle(srcImage,rightpoint,Point(rightpoint.x+tempw,rightpoint.y+temph),Scalar(0,0,255)); //畫矩形函數,輸入圖像,座上,右下點,顏色(使用了構造函數,構造了一個每名的量) imshow("jieguo",srcImage); waitKey(0); } //上面的版本只能實如今衆多圖像中匹配一個最類似的結果,並不能識別 //比模版大的類似對象(由匹配原理),後者,多個與模版大小相同(只能圈出一個最類似的,識別多個須要設計,視頻的最後一節) //因爲這個方法並不能很好的實現刀閘的識別,因此,識別多個的項目就先放一放 /*2017-2-13*/ /* 仿照houghcircles的思想改寫的檢測圓形曲線,但結果彷佛並很差,發現大多說的圖像通過處理後 半徑很小的圓,每每峯值很大,這也學是由於半徑越小,其邊緣越能近似到同一個(半徑)圓心 半徑大的,卻能近似到幾個有那麼一點點差異的圓心,從而致使峯值分散,不能與半徑小的圓相比 */ int main() { Mat src=imread(".//刀閘圖片//data.jpg"); int radius=50;//定義半徑範圍 float interval=1;//定義角度範圍 Mat result; src.copyTo(result); cvtColor(src,src,CV_RGB2GRAY); GaussianBlur(src,src,Size(3,3),0); Canny(src,src,100,250); imshow("src",src); //三維矩陣,下標爲各類圓(不一樣半徑時以各個像素爲圓心),值表明有多少像素點在它上面 vector<vector<vector<int> > > count(radius,vector<vector<int> >(src.rows,vector<int>(src.cols))); //只知道這樣建立不知道爲何? int x0,y0; for(int k=1;k<radius;k++){ for(int i=0;i<src.rows;i++){ for(int j=0;j<src.cols;j++){ unsigned char color = src.at<uchar>(i,j); if(color > 0){//若是是前景點 for (int theta = 0; theta < (1 / interval) * 360; theta++){ //計算圓心的方法也是獨特,就是圖中轉換後的圓(以點代圓) double t = ( (1 / interval) * theta * CV_PI ) / 180; x0 = (int)cvRound(j - k * cos(t));//對一個double型的數進行四捨五入 y0 = (int)cvRound(i - k * sin(t)); //計算圓心。k是半徑,這裏是根據r計算a,b(圓心),由於r有界可作外層循環。 if (x0 < src.cols && x0 > 0 && y0 < src.rows && y0 > 0){ count[k][y0][x0]++; } } } } } } int max = 0; int r = 0; int x = 0, y = 0; for(int k=1;k<50;k++){ for(int i=0;i<src.rows;i++){ for(int j=0;j<src.cols;j++){ if (count[k][i][j] > max){ max = count[k][i][j]; x = j; y = i; r = k; } } } } cout << x << endl; cout << y << endl; cout << r << endl; cout<<"峯值"<<max<<endl; Point point; point.x = x; point.y = y; //畫圓 circle(result, point, r, Scalar(0, 0, 255)); imshow("yuan",result); waitKey(0); return 0; } /*霍夫圓檢測函數*/ HoughCircles(src,circles,CV_HOUGH_GRADIENT,1,3,100,45,0,0); //8位圖,存儲變量,檢測方法,步長(肯定累加器的大小,越大,累加器空間越小) //圓心之間可合併的最大距離,canny檢測因子,判斷累加器閾值大小已肯定那些點是圓心 //半徑的最小,最大 //總之效果並很差用 vector<Vec3f> circles; HoughCircles(src,circles,CV_HOUGH_GRADIENT,1,3,100,45,0,0); for(int i=0;i<circles.size();i++){ Point center(cvRound(circles[i][0]),cvRound(circles[i][1])); int radius = cvRound(circles[i][2]); //繪製圓心 circle(result,center,3,Scalar(0,0,255),-1,8,0); //繪製輪廓 circle(result,center,radius,Scalar(155,50,255),3,8,0); } cout<<circles.size()<<endl; /*直線檢測*/ Mat src=imread(".//刀閘圖片//01.jpg",1);//"./"此文件夾 Mat edg; //霍夫直線檢測,能夠檢測出直線,,,,,轉換了參數空間 Canny(src,edg,50,100); imshow("edg",edg); vector<Vec4i> lines; HoughLinesP(edg,lines,1,CV_PI/180,150,50,10); //輸入二值化圖像,存儲空間,(轉化後)半徑的步長,角度的步長,閾值(待定直線), //最小直線閾值(小於該值,則不被認爲是一條直線),閾值(兩條直線間隙大於該值,則被認爲是兩條線段,不然是一條。) for(int i=0;i<lines.size();i++){ //這種使用方法要學會 Vec4i l=lines[i]; line(src,Point(l[0],l[i]),Point(l[2],l[3]),Scalar(0,0,255)); } imshow("jieguo",src); /*2017-2-15*/ /*Moravec角點檢測算法*/ //輸入二值化圖像,存儲角點座標指針,角點閾值(有計算方式肯定) int getMoravec(Mat& src,Point* corners , float threshold) { int winsize=5;//窗口的大小 即 5*5 win[5,5] int halfwinsize=winsize/2; Mat diffDst(src.size(),CV_32FC1,Scalar::all(0)); int conersCount=0; int x,y; for (y=halfwinsize ; y<src.rows-halfwinsize ; y++)//先y(行),每次移動一個像素 { for (x=halfwinsize;x<src.cols-halfwinsize; x++)//後x(列),從第一個浮動框的中點開始 { float reaction[4],minVal; reaction[0]=0; reaction[1]=0; reaction[2]=0; reaction[3]=0;//應該是記錄四個方向的差值 int i; //在咱們的算法中,(u,v)的取值是((1,0),(1,1),(0,1),(-1,1).//上半平面,每隔45度,取一個角 //固然,你本身能夠改爲(1,0),(1,1),(0,1),(-1,1),(-1,0),(-1,-1),(0,-1),(1,-1) 8種狀況 //0 for (i=-halfwinsize;i<halfwinsize;i++)//-2 -1 0 -1 {//固然要加,要計算全部框內的差值平方和 reaction[0]+= pow(src.at<uchar>(y,x+i)-src.at<uchar>(y,x+i+1),2); } //45 for (i=-halfwinsize;i<halfwinsize;i++)//-2 -1 0 -1 { reaction[1]+= pow(src.at<uchar>(y+i,x+i)-src.at<uchar>(y+i+1,x+i+1),2); } //90 for (i=-halfwinsize;i<halfwinsize;i++)//-2 -1 0 -1 { reaction[2]+= pow(src.at<uchar>(y+i,x)-src.at<uchar>(y+i+1,x),2); } //-45 for (i=-halfwinsize;i<halfwinsize;i++)//-2 -1 0 -1 { reaction[3]+= pow(src.at<uchar>(y+i,x-i)-src.at<uchar>(x-i-1),2); } //----------------------------- //取最小值 minVal=reaction[0]; minVal=reaction[1]<minVal?reaction[1]:minVal; minVal=reaction[2]<minVal?reaction[2]:minVal; minVal=reaction[3]<minVal?reaction[3]:minVal; //將最小值保存在準備好的模版中,且與原圖對應像素 diffDst.at<float>(y,x)=minVal; } } //獲取角點座標 判斷當前點的局部極大值!!! 對對 局部區域最大值 for (y=halfwinsize;y<src.rows-halfwinsize;y+=halfwinsize){ for (x=halfwinsize;x<src.cols-halfwinsize;x+=halfwinsize){ float max=0; int flag=0; Point maxLoc;//保存最大值的位置 maxLoc.x=-1;//初始化 maxLoc.y=-1; //在局部區域裏面找最大值! for (int winy=-halfwinsize;winy<halfwinsize;winy++){ for (int winx=-halfwinsize;winx<halfwinsize;winx++){ float value; value= diffDst.at<float>(y+winy,x+winx);//取像素點的值 if (value>max){ max=value; flag=1;//存在候選角點,還沒設置閾值 maxLoc.x=x+winx; maxLoc.y=y+winy; } } } if (flag==1 && max>threshold){ corners[conersCount].x=maxLoc.x; corners[conersCount].y=maxLoc.y; conersCount++; } } } return conersCount; } /*2017-2-17*/ /* .h文件,與相應的.cpp文件的使用 感受只能在項目文件中使用這種方法兩節兩個.cpp文件(主文件經過.h使用另外一個.cpp ) 一、原理上並不要求這兩個文件(.h .cpp)同名,但爲了使用方便,一半設置爲同名 二、.cpp裏能夠只用各類函數,沒有主函數,由於在一個總體中只要又一個函數入口就好了 三、可是即便是非主的.cpp文件也須要他本身編譯經過所需的庫的頭文件引用 四、根據文本替代遠離的.h文件在主文件的位置也有講究,例如若.h文件中使用了Mat, 那麼他在主文件中的位置須在命名空間的後面, 否則,根據文本替代原理,編譯器不會識別.h文件中的陌生符號 */ /*輸出字符到文本文件*/ #include <fstream> ofstream outfile("data.txt");//打開 outfile<<"下標從小到大"; for(int i=0;i<256;i++){ outfile<<","<<(int)result[i]; } outfile.close();關閉 /*可視化編程,滑動小框*/ for(int i=0;i<contours.size();i++){ for(int j=0;j<contours[i].size();j++){ int x,y; x=contours[i][j].x; y=contours[i][j].y; int sum[2]; //則以此點爲中心,考察一個框 Mat temp; src.copyTo(temp); circle(temp, Point(x,y),round,Scalar(255)); imshow("temp",temp); waitKey(100); } } //Point像素點座標訪問,X是橫向的座標軸是 //at(i,j),矩陣數據訪問,i是行號 Point point(10,100); srcImage.at<uchar>(point.x,point.y)=255;//x,y==>行,列 cout<<point.x<<" "<<point.y<<endl; srcImage.at<uchar>(50,100)=255;//同上,先行後列 //這種用法很特殊,,彷佛本身作了轉換 point.x=20; point.y=100; srcImage.at<uchar>(point)=255;//這樣使用x,y就表明 列,行了 Mat m=Mat::zeros(200,200,CV_8UC3); Point point(10,100); circle(m,point,1,Scalar(0,0,255));//使用point的數據,x列,y行 circle(m,Point(20,100),1,Scalar(0,0,255));//同上,x列,y行 cout<<point.x;//或者說對於Point來說,point.x就是cols m.at<Vec3b>(10,100)[2]=255; //由於這裏(i,j)並非表明像素點的座標,而是cols號 imshow("jieguo",m); //多邊形逼近 Mat lunkuo=Mat::zeros(srcImage.size(),CV_8UC3); //繪製 vector<Vec4i> hierarchy; vector< vector<Point> > contours; findContours(srcImage,contours,hierarchy,0,CV_CHAIN_APPROX_NONE);//沒有返回值 vector<vector<Point> > approxPoint(contours.size()); for (int i = 0; i < (int)contours.size(); i++){ approxPolyDP(contours[i], approxPoint[i], 3, true); } for(int i=0;i<approxPoint.size();i++){ drawContours(lunkuo, approxPoint, i, Scalar(255, 255, 255), 1); } imshow("black",lunkuo); //角度計算 double=atan(y/x); //計算在不在圓裏面,是帶入公式 {x1-x}^2+{y1-y}^2==r /*2017-2-18*/ /*卷積*/ //有的圖像處理方法是把象素的顏色(灰度)值變換爲周圍圖像顏色(灰度)值的調和(周圍象素顏色(灰度)值乘以一個權重值求和,效果會使得圖像效果變得朦朧),這個過程也符合卷積的物理意義(一組值乘以他們相應的「權重」係數的和),因此這個處理也被稱爲卷積。 /*2017-2-19*/ /* 一、關於h文件的引用,沒主函數的cpp文件也須要引入相關的系統庫,才能經過編譯 or*///將全部須要的引入庫都寫在這裏h文件中是能夠的,,但,, //不將全部的庫都寫在這裏的緣由是, //並不知道那個函數所須要引入那個庫, //倒不如,每一個函數的庫,他本身負責引入 //這裏這樣寫只是證實這樣寫是能夠的,並不建議這樣寫! /**.h文件*/ #include <iostream> #include <string> #include <list> #include <vector> #include <map> #include <stack> #include <opencv2/opencv.hpp> #include <iostream> using namespace std; using namespace cv; Scalar icvprGetRandomColor(); void icvprCcaByTwoPass(const Mat& _binImg, Mat& _lableImg); void icvprCcaBySeedFill(const Mat& _binImg, Mat& _lableImg); void icvprLabelColor(const Mat& _labelImg, Mat& _colorLabelImg); /* 二、防止h文件引用本身 */ /*2017-2-20*/ //goodFeatureToTrack()函數 /* 一、//eig是最小特徵值矩陣,並非二值化矩陣,對其膨脹效果不一樣 //默認用3*3的核膨脹,膨脹以後,除了局部最大值點和原來相同,其它非局部最大值點被 //3*3鄰域內的最大值點取代,如不理解,可看一下灰度圖像的膨脹原理 dilate( eig, tmp, Mat() ); 二、Size imgsize = image.size(); //?還有這個類型?! 三、vector<const float*> tmpCorners; //這裏使用指針,是要訪問他在內存中的真正地址,?指向浮點數的地址? 四、//size_t類型 //而數組下標的正確類型則是size_t,是標準C庫中定義的一種類型,應爲unsigned int size_t i, j 五、eig.data 返回eig內存塊的首地址?還有eig.data這樣的使用法? 角點在原圖像中的行?eig.step也許是一行的大小吧? */ oid cv::goodFeaturesToTrack(InputArray _image, OutputArray _corners, int maxCorners, double qualityLevel, double minDistance, InputArray _mask, int blockSize, bool useHarrisDetector, double harrisK ) { //若是須要對_image全圖操做,則給_mask傳入cv::Mat(),不然傳入感興趣區域 Mat image = _image.getMat(), mask = _mask.getMat(); CV_Assert( qualityLevel > 0 && minDistance >= 0 && maxCorners >= 0 ); //對參數有一些基本要求 CV_Assert( mask.empty() || (mask.type() == CV_8UC1 && mask.size() == image.size()) ); Mat eig, tmp; //eig存儲每一個像素協方差矩陣的最小特徵值,tmp用來保存經膨脹後的eig //?協方差矩陣 大概就是能夠表徵角的特色的一個矩陣吧? //?注意計算完以後,eig就變成float類型的了? if( useHarrisDetector ) cornerHarris( image, eig, blockSize, 3, harrisK ); //blockSize是計算2*2協方差矩陣的窗口大小,sobel算子窗口爲3,harrisK是計算Harris角點時須要的值 else cornerMinEigenVal( image, eig, blockSize, 3 ); //計算每一個像素對應的協方差矩陣的最小特徵值,保存在eig中 double maxVal = 0; minMaxLoc( eig, 0, &maxVal, 0, 0, mask ); //只有一個最大值(最小值)沒保存,maxVal保存了eig的最大值 threshold( eig, eig, maxVal*qualityLevel, 0, THRESH_TOZERO ); //閾值設置爲 maxVal乘以qualityLevel(圖像質量水平), //大於此閾值的保持不變,小於此閾值的都設爲0 //eig是最小特徵值矩陣,並非二值化矩陣,對其膨脹效果不一樣 //默認用3*3的核膨脹,膨脹以後,除了局部最大值點和原來相同,其它非局部最大值點被 //3*3鄰域內的最大值點取代,如不理解,可看一下灰度圖像的膨脹原理 dilate( eig, tmp, Mat() ); //tmp中保存了膨脹以後的eig,?保存膨脹以後的幹什麼? Size imgsize = image.size(); //?還有這個類型?! vector<const float*> tmpCorners; //存放粗選出的角點地址,?指向浮點數的地址? //這裏使用指針,是要訪問他在內存中的真正地址 for( int y = 1; y < imgsize.height - 1; y++ )//從第二行開始,到倒數第二行 { const float* eig_data = (const float*)eig.ptr(y); //得到eig第y行的首地址 const float* tmp_data = (const float*)tmp.ptr(y); //得到tmp第y行的首地址 const uchar* mask_data = mask.data ? mask.ptr(y) : 0; for( int x = 1; x < imgsize.width - 1; x++ ){ float val = eig_data[x]; if( val != 0 && val == tmp_data[x] && (!mask_data || mask_data[x]) ) //val == tmp_data[x]說明這是局部極大值 tmpCorners.push_back(eig_data + x); //保存其位置 ?eig_data已是y行首地址了? } } //-----------此分割線以上是根據特徵值粗選出的角點,咱們稱之爲弱角點----------// //----------此分割線如下還要根據minDistance進一步篩選角點,仍然能存活下來的咱們稱之爲強角點----------// //tmpCorners,數組 保存弱角點的 內存像素地址 sort( tmpCorners, greaterThanPtr<float>() ); //按特徵值降序排列,注意這一步很重要,後面的不少編程思路都是創建在這個降序排列的基礎上 vector<Point2f> corners; //size_t類型 //而數組下標的正確類型則是size_t,是標準C庫中定義的一種類型,應爲unsigned int size_t i, j, total = tmpCorners.size(), ncorners = 0; //下面的程序有點稍微難理解,須要本身仔細想一想 //minDistance是傳入參數 if(minDistance >= 1){ int w = image.cols; int h = image.rows; const int cell_size = cvRound(minDistance); //向最近的整數取整 /* 這裏根據cell_size構建了一個矩形窗口grid(雖然下面的grid定義的是vector<vector>,而並非咱們這裏說的矩形窗口,但爲了便於理解,仍是將grid想象成一個 grid_width*grid_height 的矩形窗口比較好), 除以cell_size說明grid窗口裏相差一個像素至關於_image裏相差minDistance個像素,至於爲何加上cell_size - 1後面會講 */ //w,h是整個圖像的,因此grid_width的大小也是對於整個圖像的 const int grid_width = (w + cell_size - 1) / cell_size; const int grid_height = (h + cell_size - 1) / cell_size; //vector裏面是vector,grid用來保存得到的強角點座標,?注意grid中保存的是座標,但爲何是Point2f? std::vector<std::vector<Point2f> > grid(grid_width*grid_height); minDistance *= minDistance;//平方,方面後面計算,省的開根號 for( i = 0; i < total; i++ ) // 剛剛粗選的弱角點,都要到這裏來接收新一輪的考驗 { int ofs = (int)( (const uchar*)tmpCorners[i] - eig.data ); //tmpCorners中保存了角點的地址,eig.data返回eig內存塊的首地址?還有eig.data這樣的使用法? int y = (int)(ofs / eig.step); //角點在原圖像中的行?eig.step也許是一行的大小吧? int x = (int)((ofs - y*eig.step)/sizeof(float)); //在原圖像中的列 bool good = true;//先認爲當前角點就是強角點 int x_cell = x / cell_size;//x_cell,y_cell是角點(y,x)在grid中的對應座標 int y_cell = y / cell_size;//所有圖像分紅若干個 grid,,這個x_cell,y_cell處於的grid的座標 //如今知道爲何前面grid_width定義時要加上cell_size - 1了吧,這是爲了使得(y,x)在grid中的4鄰域像素都存在,也就是說(y_cell,x_cell)不會成爲邊界像素 // (y_cell,x_cell)的4鄰域像素 int x1 = x_cell - 1; int y1 = y_cell - 1; int x2 = x_cell + 1; int y2 = y_cell + 1; // boundary check,再次確認x1,y1,x2或y2不會超出grid邊界 x1 = std::max(0, x1); //比較0和x1的大小 y1 = std::max(0, y1); x2 = std::min(grid_width-1, x2); y2 = std::min(grid_height-1, y2); //記住grid中相差一個像素,至關於_image中相差了minDistance個像素 for( int yy = y1; yy <= y2; yy++ ){ // 行 for( int xx = x1; xx <= x2; xx++ ){ //列 vector <Point2f> &m = grid[yy*grid_width + xx]; //grid中元素的是Point2f引用 //若是(y_cell,x_cell)的4鄰域像素, //也就是(y,x)的minDistance鄰域像素中已有被保留的強角點 if( m.size() ){//m指向一個座標點 for(j = 0; j < m.size(); j++){ //當前角點周圍的強角點都拉出來跟當前角點比一比 float dx = x - m[j].x; float dy = y - m[j].y; //注意若是(y,x)的minDistance鄰域像素中已有被保留的強角點,則說明該強角點是在(y,x)以前就被測試過的,又由於tmpCorners中已按照特徵值降序排列(特徵值越大說明角點越好),這說明先測試的必定是更好的角點,也就是已保存的強角點必定好於當前角點,因此這裏只要比較距離,若是距離知足條件,能夠立馬扔掉當前測試的角點 //也就是在必定範圍內,只保留一個角點,就是強角點,只不過這裏用的轉變思想很好啊 if( dx*dx + dy*dy < minDistance ){ good = false; goto break_out; } } } } // 列 } //行 break_out: if(good) { // printf("%d: %d %d -> %d %d, %d, %d -- %d %d %d %d, %d %d, c=%d\n", // i,x, y, x_cell, y_cell, (int)minDistance, cell_size,x1,y1,x2,y2, grid_width,grid_height,c); grid[y_cell*grid_width + x_cell].push_back(Point2f((float)x, (float)y)); //再從grid中轉變會原來圖像中的座標點 corners.push_back(Point2f((float)x, (float)y)); ++ncorners; if( maxCorners > 0 && (int)ncorners == maxCorners ) //因爲前面已按降序排列,當ncorners超過maxCorners的時候跳出循環直接忽略tmpCorners中剩下的角點,反正剩下的角點愈來愈弱 break; } } } else //除了像素自己,沒有哪一個鄰域像素能與當前像素知足minDistance < 1,所以直接保存粗選的角點 { for( i = 0; i < total; i++ ) { int ofs = (int)((const uchar*)tmpCorners[i] - eig.data); int y = (int)(ofs / eig.step); //粗選的角點在原圖像中的行 int x = (int)((ofs - y*eig.step)/sizeof(float)); //在圖像中的列 corners.push_back(Point2f((float)x, (float)y)); ++ncorners; if( maxCorners > 0 && (int)ncorners == maxCorners ) break; } } Mat(corners).convertTo(_corners, _corners.fixedType() ? _corners.type() : CV_32F); } /* 使用Harris()函數找角點 一、仍是使用輪庫圖查找效果明顯一點,只是爲何平滑區域,檢測出來的值這麼大 二、首先使用normlize()函數,將float的矩陣歸一化到0-255 float(), convertScaleAbs在轉化到CV8U,減小了以後的計算量 三、根據賦值 = -數 的原理想 +=纔是另外一種 */ #include "harris2.h" /* 對輸入的圖像使用Harris檢測,src 以後對於設置的局部框進行抑制,thresh 減小了連續角點的出現 */ void cornerHarris_demo( Mat& src,int size, int thresh) { Mat dst, dst_norm; dst = Mat::zeros( src.size(), CV_32FC1 ); //harris函數的參數設置 int blockSize = 3; int apertureSize = 3; double k = 0.04; /// Detecting corners cornerHarris( src, dst, blockSize, apertureSize, k, BORDER_DEFAULT ); ///這一步僅僅是爲了好讓其可以轉換成到CV8U normalize( dst, dst_norm, 0, 255, NORM_MINMAX, CV_32FC1, Mat() ); //歸一化函數,結果還是float型 convertScaleAbs(dst_norm,dst_norm,1,0); imshow("dst_norm",dst_norm); Mat show=Mat::zeros(src.size(),CV_8UC3); vector<vector<Point> > contours; vector<Vec4i> hierarchy; findContours(src, contours, hierarchy, 0, CV_CHAIN_APPROX_NONE); drawContours(show, contours, -1, Scalar(255,255,255), 1, 8, hierarchy); //根據Harris函數計算獲得的矩陣,進行閾值判斷 // for( int j = 0; j < dst_norm.rows ; j++ ){ // for( int i = 0; i < dst_norm.cols; i++ ){ // if( (int) dst_norm.at<float>(j,i) > thresh ){ // circle( show, Point( i, j ), 1, Scalar(0,0,255)); // } // } // } //另外一個版本的閾值抑制 //(20*20)的窗口 //設定局部抑制時的窗口,也就是21的窗口有一個極大值點而且設定,變相的減小連續點 //同時進行閾值抑制 for( int i = size ; i < dst_norm.rows-size ; i+=size ){ for( int j = size ; j < dst_norm.cols-size ; j+=size ){ int max=0; Point point; for(int u = -size;u<size;u++){//根據賦值 = -數 的原理想 +=纔是另外一種 for(int v =-size;v<size;v++){ if(max < dst_norm.at<uchar>(i+u,j+v)){ max = dst_norm.at<uchar>(i+u,j+v); point.y = i+u; point.x = j+v; } } } if(max > thresh) circle( show, point, 1, Scalar(0,0,255) ); } } //畫出角點 imshow( "result", show ); } /*2017-3-17*/ //加一個括號 初始化爲零 bufferGray2= new uchar[m_width*m_height](); /*2017-3-28*/ /*CvTermCriteria 迭代算法的終止準則 */ #define CV_TERMCRIT_ITER 1 #define CV_TERMCRIT_NUMBER CV_TERMCRIT_ITER #define CV_TERMCRIT_EPS 2 typedef struct CvTermCriteria { int type; /* CV_TERMCRIT_ITER 和CV_TERMCRIT_EPS二值之一,或者兩者的組合 */ int max_iter; /* 最大迭代次數 */ double epsilon; /* 結果的精確性 */ } //得儘快將opencv 的矩陣的使用方法熟悉 //一、指定隨機數種子 RNG rng(100); rng.fill //二、從整個數據集中取出前[0,89]行 //注:*Range的範圍是[a,b) Mat trainClass = trainData.rowRange(0,nLinearSamples); //取出第一列 Mat c = trainClass.colRange(0,1); //2017-4-6 //使用canny算子,在查找輪廓,會形成輪廓數據混亂,每一個輪廓會走兩遍,而且有差異 Canny(srcImage,srcImage,100,250); //2017-12-12 積分直方圖:http://blog.csdn.net/linear_luo/article/details/52725358 梯度直方圖:https://www.leiphone.com/news/201708/ZKsGd2JRKr766wEd.html