2018.12.12—2018.12.29
此博客爲在看過毛星雲版《OpenCV3編程入門》後所總結的一本筆記,可供複習使用。
文章目錄
- OpenCV3編程入門 學習筆記
- 第一部分 快速上手OpenCV
- 第二部分 初探core組件
- 4.1 基礎圖像容器Mat
- 4.2 常用數據結構和函數
- 5.1 訪問圖像中的像素
- 5.2 ROI區域圖像疊加&圖像混合
- 5.3 分離顏色通道、多通道圖像混合
- 5.4 圖像對比度、亮度值調整
- 5.6 輸入輸出XML和YAML文件
- 第三部分 掌握imgproc組件
- 6.1 線性濾波:方框濾波、均值濾波、高斯濾波
- 6.3 形態學濾波(1):腐蝕與膨脹
- 6.4 形態學濾波(2):開運算、閉運算、形態學梯度、頂帽、黑帽
- 6.5 漫水填充
- 6.6 圖像金字塔與圖片尺寸縮放
- 6.7 閾值化
- 7.1 基於OpenCV的邊緣檢測
- 7.2 霍夫變換
- 7.3 重映射
- 7.4 仿射變換
- 7.5 直方圖均衡化
- 8.1 查找並繪製輪廓
- 8.2 尋找物體的凸包
- 8.3 使用多邊形將輪廓包圍
- 8.4 圖像的矩
- 8.5 分水嶺算法
- 8.6 圖像修補
- 9.1 圖像直方圖概述
- 9.2 直方圖的計算與繪製
- 9.3 直方圖對比
- 9.4 反向投影
- 9.5 模板匹配
- 第四部分 深入feature2d組件
第一部分 快速上手OpenCV
1.5快速上手OpenCV圖像處理
1.5.1圖像顯示
#include<opencv2/opencv.hpp>
using namespace cv;
int main()
{
Mat img = imread("E:\\imagelib\\02.jpg");
namedWindow("楊超越", WINDOW_NORMAL);
//保證讀取的圖像顯示完整
imshow("楊超越", img);
waitKey(0);
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
1.5.2圖像腐蝕
1.6 OpenCV視頻操作基礎
#include<opencv2/opencv.hpp>
using namespace cv;
int main()
{
VideoCapture capture(0);//opencv調用攝像頭讀取視頻
while (1)
{
Mat frame;
capture >> frame;
imshow("讀取視頻", frame);
if (waitKey(30) == 27)//按下Esc鍵退出讀取視頻操作
break;
}
return 0;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
Canny邊緣檢測
#include<opencv2/opencv.hpp>
using namespace cv;
int main()
{
VideoCapture capture(0);//opencv調用攝像頭讀取視頻
Mat edges;
while (1)
{
Mat frame;
capture >> frame;
cvtColor(frame, edges, CV_BGR2GRAY);
blur(edges, edges, Size(7, 7));
Canny(edges, edges, 0, 30, 3);
namedWindow("被canny後的視頻", WINDOW_NORMAL);
imshow("被canny後的視頻", edges);
if (waitKey(30) == 27)//按下Esc鍵退出讀取視頻操作
break;
}
return 0;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
3.1 圖像的載入、顯示和輸出到文件
圖像讀入
函數: Mat imread(”路徑”,int flas=g);
Mat srcImage0 = imread("E:\\imagelib\\25.jpg");//默認flags 1,彩色圖像
Mat srcImage1 = imread("E:\\imagelib\\25.jpg", 1);//彩色圖像
Mat srcImage2 = imread("E:\\imagelib\\25.jpg", 2);//深度圖像/灰度圖像(無損)
Mat srcImage3 = imread("E:\\imagelib\\25.jpg", 4);//彩色(無損)
- 1
- 2
- 3
- 4
創建窗口:
函數:void namedWindow(“名稱”,int flags=WINDOW_AUTOSIZE);
WINDOW_AUTOSIZE:窗口大小不可調。
WINDOW_NORMAL:窗口大小可調。
每顯示一幅圖片,對應新建一個窗口,可以實現多幅圖片顯示。
圖像輸出
指的是將圖像從MAT這種數據結構變成文件格式:png、jpg等等
函數:
bool imwrite(“輸出的圖像文件名+後綴(.png)”,Mat類型的圖像名稱,對特定文件的參數設置)
Mat mat(480, 640, CV_8UC4);
createAlphaMat(mat);
vectorcompression_params;
compression_params.push_back(IMWRITE_PNG_COMPRESSION);
compression_params.push_back(9);
imwrite(“透明Alpha值圖.png”, mat,compression_params);
3.2滑動條的創建和使用
滑動條的創建和使用
函數:int createTrackbar(軌跡條名字,依附的窗口名,int* value(滑塊位置),int count,TrackbarCallback onChange=0(回調函數),void* userdata=0)
回調函數void on_Trackbar(int,void*)
3.3 鼠標操作
鼠標操作
函數:void setMouseCallback(窗口名稱,MouseCallback onMouse(回調函數),void* userdata=0(用戶傳遞到回調函數的參數))
回調函數void Foo(int event,int x,int y,int flags,void* param)
第二部分 初探core組件
4.1 基礎圖像容器Mat
Mat 是一個類,有兩個數據部分組成,矩陣頭(包含矩陣尺寸、存儲方法、存儲地址等信息)和一個指向所有存儲像素值的矩陣。
Mat A, C
A = imread("01.jpg");
Mat B(A);//拷貝構造函數
C = A;//賦值運算符
Mat F = A.clone();
Mat G;
A. copyTo(G);
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
拷貝構造函數和賦值運算符只複製信息頭,與原對象共用一個矩陣,若改一個,則都受影響。
使用函數clone()或copyTo()可以複製矩陣。
使用Mat創建圖像
Mat M = (2, 2, CV_8UC3, Scalar(0, 0, 255));
2,2爲二維矩陣的大小
CV_[位數][帶符號與否][類型前綴]C[通道數]
Scalar(a,b,c,d)四個參數,是一個類。表示顏色
在RGB中,前三個依次爲B,G,R,最後一個可不寫。
輸出Mat矩陣
cout << “M =” << endl << " " << M << endl << endl;
4.2 常用數據結構和函數
注意數據類型,一般未提到的是int型
點的表示:Point類
Point point;
Point.x=10;
Point.y=8;
或者Point point(10,8);
顏色的表示:Scalar類
Scalar(0,0,255)
尺寸的表示:Size類
Size(5,5)
矩形的表示:Rect類
Rect類的成員變量有下x,y,width,height,分別爲左上角點的座標和矩形的寬和高。常用的成員函數有:Size()返回值爲Size();area()返回矩形的面積。Constrins(Point)判斷點是否在矩形內;inside(Rect)函數判斷矩形是否在矩形內;tl返回左上角點的座標;br()返回右下角點的座標。還可對矩形進行交集、並集、平移和縮放操作。
顏色空間轉換:cvtColor()函數
cvtColor(原圖,目標圖,標號,通道數(可不寫))
繪圖函數
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
using namespace cv;
//此程序對於OpenCV3版需要額外包含頭文件:
#include <opencv2/imgproc/imgproc.hpp>
//-----------------------------------【宏定義部分】--------------------------------------------
// 描述:定義一些輔助宏
//------------------------------------------------------------------------------------------------
#define WINDOW_NAME1 “【繪製圖1】” //爲窗口標題定義的宏
#define WINDOW_NAME2 “【繪製圖2】” //爲窗口標題定義的宏
#define WINDOW_WIDTH 600//定義窗口大小的宏
//--------------------------------【全局函數聲明部分】-------------------------------------
// 描述:全局函數聲明
//-----------------------------------------------------------------------------------------------
void DrawEllipse( Mat img, double angle );//繪製橢圓
void DrawFilledCircle( Mat img, Point center );//繪製圓
void DrawPolygon( Mat img );//繪製多邊形
void DrawLine( Mat img, Point start, Point end );//繪製線段
int main( void )
{
// 創建空白的Mat圖像
Mat atomImage = Mat::zeros( WINDOW_WIDTH, WINDOW_WIDTH, CV_8UC3 );
Mat rookImage = Mat::zeros( WINDOW_WIDTH, WINDOW_WIDTH, CV_8UC3 );
ShowHelpText();
// ---------------------<1>繪製化學中的原子示例圖------------------------
//【1.1】先繪製出橢圓
DrawEllipse( atomImage, 90 );
DrawEllipse( atomImage, 0 );
DrawEllipse( atomImage, 45 );
DrawEllipse( atomImage, -45 );
//【1.2】再繪製圓心
DrawFilledCircle( atomImage, Point( WINDOW_WIDTH/2, WINDOW_WIDTH/2) );
// ----------------------------<2>繪製組合圖-----------------------------
//【2.1】先繪製出凹多邊形
DrawPolygon( rookImage );
// 【2.2】繪製矩形
rectangle( rookImage,
Point( 0, 7*WINDOW_WIDTH/8 ),
Point( WINDOW_WIDTH, WINDOW_WIDTH),
Scalar( 0, 255, 255 ),
-1,
8 );
// 【2.3】繪製一些線段
DrawLine( rookImage, Point( 0, 15*WINDOW_WIDTH/16 ), Point( WINDOW_WIDTH, 15*WINDOW_WIDTH/16 ) );
DrawLine( rookImage, Point( WINDOW_WIDTH/4, 7*WINDOW_WIDTH/8 ), Point( WINDOW_WIDTH/4, WINDOW_WIDTH ) );
DrawLine( rookImage, Point( WINDOW_WIDTH/2, 7*WINDOW_WIDTH/8 ), Point( WINDOW_WIDTH/2, WINDOW_WIDTH ) );
DrawLine( rookImage, Point( 3*WINDOW_WIDTH/4, 7*WINDOW_WIDTH/8 ), Point( 3*WINDOW_WIDTH/4, WINDOW_WIDTH ) );
// ---------------------------<3>顯示繪製出的圖像------------------------
imshow( WINDOW_NAME1, atomImage );
moveWindow( WINDOW_NAME1, 0, 200 );
imshow( WINDOW_NAME2, rookImage );
moveWindow( WINDOW_NAME2, WINDOW_WIDTH, 200 );
waitKey( 0 );
return(0);
}
//-------------------------------【DrawEllipse( )函數】--------------------------------
// 描述:自定義的繪製函數,實現了繪製不同角度、相同尺寸的橢圓
//-----------------------------------------------------------------------------------------
void DrawEllipse( Mat img, double angle )
{
int thickness = 2;
int lineType = 8;
ellipse( img,
Point( WINDOW_WIDTH/2, WINDOW_WIDTH/2 ),
Size( WINDOW_WIDTH/4, WINDOW_WIDTH/16 ),
angle,
0,
360,
Scalar( 255, 129, 0 ),
thickness,
lineType );
}
//-----------------------------------【DrawFilledCircle( )函數】---------------------------
// 描述:自定義的繪製函數,實現了實心圓的繪製
//-----------------------------------------------------------------------------------------
void DrawFilledCircle( Mat img, Point center )
{
int thickness = -1;
int lineType = 8;
circle( img,
center,
WINDOW_WIDTH/32,
Scalar( 0, 0, 255 ),
thickness,
lineType );
}
//-----------------------------------【DrawPolygon( )函數】--------------------------
// 描述:自定義的繪製函數,實現了凹多邊形的繪製
//--------------------------------------------------------------------------------------
void DrawPolygon( Mat img )
{
int lineType = 8;
//創建一些點
Point rookPoints[1][20];
rookPoints[0][0] = Point( WINDOW_WIDTH/4, 7*WINDOW_WIDTH/8 );
rookPoints[0][1] = Point( 3*WINDOW_WIDTH/4, 7*WINDOW_WIDTH/8 );
rookPoints[0][2] = Point( 3*WINDOW_WIDTH/4, 13*WINDOW_WIDTH/16 );
rookPoints[0][3] = Point( 11*WINDOW_WIDTH/16, 13*WINDOW_WIDTH/16 );
rookPoints[0][4] = Point( 19*WINDOW_WIDTH/32, 3*WINDOW_WIDTH/8 );
rookPoints[0][5] = Point( 3*WINDOW_WIDTH/4, 3*WINDOW_WIDTH/8 );
rookPoints[0][6] = Point( 3*WINDOW_WIDTH/4, WINDOW_WIDTH/8 );
rookPoints[0][7] = Point( 26*WINDOW_WIDTH/40, WINDOW_WIDTH/8 );
rookPoints[0][8] = Point( 26*WINDOW_WIDTH/40, WINDOW_WIDTH/4 );
rookPoints[0][9] = Point( 22*WINDOW_WIDTH/40, WINDOW_WIDTH/4 );
rookPoints[0][10] = Point( 22*WINDOW_WIDTH/40, WINDOW_WIDTH/8 );
rookPoints[0][11] = Point( 18*WINDOW_WIDTH/40, WINDOW_WIDTH/8 );
rookPoints[0][12] = Point( 18*WINDOW_WIDTH/40, WINDOW_WIDTH/4 );
rookPoints[0][13] = Point( 14*WINDOW_WIDTH/40, WINDOW_WIDTH/4 );
rookPoints[0][14] = Point( 14*WINDOW_WIDTH/40, WINDOW_WIDTH/8 );
rookPoints[0][15] = Point( WINDOW_WIDTH/4, WINDOW_WIDTH/8 );
rookPoints[0][16] = Point( WINDOW_WIDTH/4, 3*WINDOW_WIDTH/8 );
rookPoints[0][17] = Point( 13*WINDOW_WIDTH/32, 3*WINDOW_WIDTH/8 );
rookPoints[0][18] = Point( 5*WINDOW_WIDTH/16, 13*WINDOW_WIDTH/16 );
rookPoints[0][19] = Point( WINDOW_WIDTH/4, 13*WINDOW_WIDTH/16 );
const Point* ppt[1] = { rookPoints[0] };
int npt[] = { 20 };
fillPoly( img,
ppt,
npt,
1,
Scalar( 255, 255, 255 ),
lineType );
}
//-----------------------------------【DrawLine( )函數】--------------------------
// 描述:自定義的繪製函數,實現了線的繪製
//---------------------------------------------------------------------------------
void DrawLine( Mat img, Point start, Point end )
{
int thickness = 2;
int lineType = 8;
line( img,
start,
end,
Scalar( 0, 0, 0 ),
thickness,
lineType );
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
- 126
- 127
- 128
- 129
- 130
- 131
- 132
- 133
- 134
- 135
- 136
- 137
- 138
- 139
- 140
- 141
- 142
- 143
- 144
- 145
- 146
- 147
- 148
- 149
- 150
- 151
- 152
- 153
- 154
- 155
- 156
- 157
- 158
- 159
5.1 訪問圖像中的像素
顏色空間縮減
對每個像素實施Inew=(Iold/div)*div,可將顏色空間所見div的立方倍
函數爲:colorReduce(srcImage,dstImage,div);
計時函數
double time0 = static_cast(getTickCount());
…
time0 = ((double)getTickCount() - time0) / getTickFrequency();//計算算法運行時間
訪問圖像中像素的三類方法
- 指針訪問:C操作符[]
- 迭代器 iterator
- 動態地址計算
5.2 ROI區域圖像疊加&圖像混合
感興趣區域:ROI 定義
Mat imageROI;
imageROI = image(Rect(500, 250, logo.cols, logo.rows));//使用矩形定義
imageROI = image(Range(250, 250 + logo.rows), Range(200, 200 + logo.cols));//使用Range(範圍)
線性混合操作
函數:addWeighted(輸入圖像1,佔比,輸入圖像2,佔比,整體偏移量,輸出圖像,int dtype=-1);
5.3 分離顏色通道、多通道圖像混合
通道分離:split(srcImage,channels)
通道合併:merge(channels,通道數(可以省略),srcImage)
5.4 圖像對比度、亮度值調整
g(i,j)=a*f(I,j)+b;
a稱爲增益,控制對比度;
b稱爲偏置,控制亮度。
訪問圖片中每一個像素,然後對其做如上變換,進而調整對比度和亮度值。
5.5 離散傅里葉變換
函數:void dft(srcImage,dstImage,int flags=0(可選擇做哪種傅里葉變換),int nonzeroRows=0(選擇自己想要處理的非零行,比如輸出矩陣C.rows))
其他一些可能用到的函數:
返回DFT最優尺寸大小:getOptimalDFTSize()函數
擴充圖像邊界:copyMakeBorder()函數
計算二維矢量的幅值:magnitude()函數 138
計算自然對數:log()函數
矩陣歸一化:normalize()函數
程序實例
#include "opencv2/core/core.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/highgui/highgui.hpp"
#include <iostream>
using namespace cv;
//--------------------------------------【main( )函數】-----------------------------------------
// 描述:控制檯應用程序的入口函數,我們的程序從這裏開始執行
//-------------------------------------------------------------------------------------------------
int main()
{
//【1】以灰度模式讀取原始圖像並顯示
Mat srcImage = imread("E:\\imagelib\\41.jpg", 0);
if (!srcImage.data) { printf("讀取圖片錯誤,請確定目錄下是否有imread函數指定圖片存在~! \n"); return false; }
imshow("原始圖像", srcImage);
//【2】將輸入圖像延擴到最佳的尺寸,邊界用0補充
int m = getOptimalDFTSize(srcImage.rows);
int n = getOptimalDFTSize(srcImage.cols);
//將添加的像素初始化爲0.
Mat padded;
copyMakeBorder(srcImage, padded, 0, m - srcImage.rows, 0, n - srcImage.cols, BORDER_CONSTANT, Scalar::all(0));
//【3】爲傅立葉變換的結果(實部和虛部)分配存儲空間。
//將planes數組組合合併成一個多通道的數組complexI
Mat planes[] = { Mat_<float>(padded), Mat::zeros(padded.size(), CV_32F) };
Mat complexI;
merge(planes, 2, complexI);
//【4】進行就地離散傅里葉變換
dft(complexI, complexI);
//【5】將複數轉換爲幅值,即=> log(1 + sqrt(Re(DFT(I))^2 + Im(DFT(I))^2))
split(complexI, planes); // 將多通道數組complexI分離成幾個單通道數組,planes[0] = Re(DFT(I), planes[1] = Im(DFT(I))
magnitude(planes[0], planes[1], planes[0]);// planes[0] = magnitude
Mat magnitudeImage = planes[0];
//【6】進行對數尺度(logarithmic scale)縮放
magnitudeImage += Scalar::all(1);
log(magnitudeImage, magnitudeImage);//求自然對數
//【7】剪切和重分佈幅度圖象限
//若有奇數行或奇數列,進行頻譜裁剪
magnitudeImage = magnitudeImage(Rect(0, 0, magnitudeImage.cols & -2, magnitudeImage.rows & -2));
//重新排列傅立葉圖像中的象限,使得原點位於圖像中心
int cx = magnitudeImage.cols / 2;
int cy = magnitudeImage.rows / 2;
Mat q0(magnitudeImage, Rect(0, 0, cx, cy)); // ROI區域的左上
Mat q1(magnitudeImage, Rect(cx, 0, cx, cy)); // ROI區域的右上
Mat q2(magnitudeImage, Rect(0, cy, cx, cy)); // ROI區域的左下
Mat q3(magnitudeImage, Rect(cx, cy, cx, cy)); // ROI區域的右下
//交換象限(左上與右下進行交換)
Mat tmp;
q0.copyTo(tmp);
q3.copyTo(q0);
tmp.copyTo(q3);
//交換象限(右上與左下進行交換)
q1.copyTo(tmp);
q2.copyTo(q1);
tmp.copyTo(q2);
//【8】歸一化,用0到1之間的浮點值將矩陣變換爲可視的圖像格式
//此句代碼的OpenCV2版爲:
//normalize(magnitudeImage, magnitudeImage, 0, 1, CV_MINMAX);
//此句代碼的OpenCV3版爲:
normalize(magnitudeImage, magnitudeImage, 0, 1, NORM_MINMAX);
//【9】顯示效果圖
imshow("頻譜幅值", magnitudeImage);
waitKey();
return 0;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
5.6 輸入輸出XML和YAML文件
示例程序:XML和YAML文件的寫入
右擊工程–>屬性–>配置屬性 --> C/C++ --> 命令行–>輸入"/D _CRT_SECURE_NO_WARNINGS"–>“確定”,這樣問題就可以解決了。注:不帶引號
#include "opencv2/opencv.hpp"
#include <time.h>
using namespace cv;
int main()
{
//改變console字體顏色
system(“color 5F”);
//初始化
FileStorage fs("test.yaml", FileStorage::WRITE);
//開始文件寫入
fs << "frameCount" << 5;
time_t rawtime; time(&rawtime);
fs << "calibrationDate" << asctime(localtime(&rawtime));
Mat cameraMatrix = (Mat_<double>(3, 3) << 1000, 0, 320, 0, 1000, 240, 0, 0, 1);
Mat distCoeffs = (Mat_<double>(5, 1) << 0.1, 0.01, -0.001, 0, 0);
fs << "cameraMatrix" << cameraMatrix << "distCoeffs" << distCoeffs;
fs << "features" << "[";
for (int i = 0; i < 3; i++)
{
int x = rand() % 640;
int y = rand() % 480;
uchar lbp = rand() % 256;
fs << "{:" << "x" << x << "y" << y << "lbp" << "[:";
for (int j = 0; j < 8; j++)
fs << ((lbp >> j) & 1);
fs << "]" << "}";
}
fs << "]";
fs.release();
printf("\n文件讀寫完畢,請在工程目錄下查看生成的文件~");
getchar();
return 0;
}
示例程序:XML和YAML文件的讀取
#include “opencv2/opencv.hpp”
#include <time.h>
using namespace cv;
using namespace std;
int main()
{
//改變console字體顏色
system(“color 6F”);
//初始化
FileStorage fs2("test.yaml", FileStorage::READ);
// 第一種方法,對FileNode操作
int frameCount = (int)fs2["frameCount"];
std::string date;
// 第二種方法,使用FileNode運算符> >
fs2["calibrationDate"] >> date;
Mat cameraMatrix2, distCoeffs2;
fs2["cameraMatrix"] >> cameraMatrix2;
fs2["distCoeffs"] >> distCoeffs2;
cout << "frameCount: " << frameCount << endl
<< "calibration date: " << date << endl
<< "camera matrix: " << cameraMatrix2 << endl
<< "distortion coeffs: " << distCoeffs2 << endl;
FileNode features = fs2["features"];
FileNodeIterator it = features.begin(), it_end = features.end();
int idx = 0;
std::vector<uchar> lbpval;
//使用FileNodeIterator遍歷序列
for (; it != it_end; ++it, idx++)
{
cout << "feature #" << idx << ": ";
cout << "x=" << (int)(*it)["x"] << ", y=" << (int)(*it)["y"] << ", lbp: (";
// 我們也可以使用使用filenode > > std::vector操作符很容易的讀數值陣列
(*it)["lbp"] >> lbpval;
for (int i = 0; i < (int)lbpval.size(); i++)
cout << " " << (int)lbpval[i];
cout << ")" << endl;
}
fs2.release();
//程序結束,輸出一些幫助文字
printf("\n文件讀取完畢,請輸入任意鍵結束程序~");
getchar();
return 0;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
第三部分 掌握imgproc組件
6.1 線性濾波:方框濾波、均值濾波、高斯濾波
方框濾波:鄰域像素平均值
函數:boxFilter()
均值濾波:鄰域像素平均值+歸一化
函數:blur()
高斯濾波:鄰域像素加權平均值,處理高斯噪聲
函數:GaussianBlur()
6.2 非線性濾波:中值濾波、雙邊濾波
中值濾波:領域像素中值,去除脈衝噪聲、散粒噪聲、椒鹽噪聲
函數:medianBlur()
雙邊濾波:比高斯濾波多了一個高斯方差
函數:bilateralFilter()
函數實例:
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <iostream>
using namespace std;
using namespace cv;
//-----------------------------------【全局變量聲明部分】--------------------------------------
// 描述:全局變量聲明
//-----------------------------------------------------------------------------------------------
Mat g_srcImage,g_dstImage1,g_dstImage2,g_dstImage3,g_dstImage4,g_dstImage5;
int g_nBoxFilterValue=6; //方框濾波內核值
int g_nMeanBlurValue=10; //均值濾波內核值
int g_nGaussianBlurValue=6; //高斯濾波內核值
int g_nMedianBlurValue=10; //中值濾波參數值
int g_nBilateralFilterValue=10; //雙邊濾波參數值
//-----------------------------------【全局函數聲明部分】--------------------------------------
// 描述:全局函數聲明
//-----------------------------------------------------------------------------------------------
//軌跡條回調函數
static void on_BoxFilter(int, void *); //方框濾波
static void on_MeanBlur(int, void *); //均值塊濾波器
static void on_GaussianBlur(int, void *); //高斯濾波器
static void on_MedianBlur(int, void *); //中值濾波器
static void on_BilateralFilter(int, void *); //雙邊濾波器
void ShowHelpText();
//-----------------------------------【main( )函數】--------------------------------------------
// 描述:控制檯應用程序的入口函數,我們的程序從這裏開始
//-----------------------------------------------------------------------------------------------
int main( )
{
system(“color 4F”);
// 載入原圖
g_srcImage = imread( "1.jpg", 1 );
if( !g_srcImage.data ) { printf("讀取srcImage錯誤~! \n"); return false; }
//克隆原圖到四個Mat類型中
g_dstImage1 = g_srcImage.clone( );
g_dstImage2 = g_srcImage.clone( );
g_dstImage3 = g_srcImage.clone( );
g_dstImage4 = g_srcImage.clone( );
g_dstImage5 = g_srcImage.clone( );
//顯示原圖
namedWindow("【<0>原圖窗口】", WINDOW_NORMAL);
imshow("【<0>原圖窗口】",g_srcImage);
//=================【<1>方框濾波】=========================
//創建窗口
namedWindow("【<1>方框濾波】", WINDOW_NORMAL);
//創建軌跡條
createTrackbar("內核值:", "【<1>方框濾波】",&g_nBoxFilterValue, 50,on_BoxFilter );
on_BoxFilter(g_nBoxFilterValue,0);
imshow("【<1>方框濾波】", g_dstImage1);
//=====================================================
//=================【<2>均值濾波】==========================
//創建窗口
namedWindow("【<2>均值濾波】", WINDOW_NORMAL);
//創建軌跡條
createTrackbar("內核值:", "【<2>均值濾波】",&g_nMeanBlurValue, 50,on_MeanBlur );
on_MeanBlur(g_nMeanBlurValue,0);
//======================================================
//=================【<3>高斯濾波】===========================
//創建窗口
namedWindow("【<3>高斯濾波】", WINDOW_NORMAL);
//創建軌跡條
createTrackbar("內核值:", "【<3>高斯濾波】",&g_nGaussianBlurValue, 50,on_GaussianBlur );
on_GaussianBlur(g_nGaussianBlurValue,0);
//=======================================================
//=================【<4>中值濾波】===========================
//創建窗口
namedWindow("【<4>中值濾波】", WINDOW_NORMAL);
//創建軌跡條
createTrackbar("參數值:", "【<4>中值濾波】",&g_nMedianBlurValue, 50,on_MedianBlur );
on_MedianBlur(g_nMedianBlurValue,0);
//=======================================================
//=================【<5>雙邊濾波】===========================
//創建窗口
namedWindow("【<5>雙邊濾波】", WINDOW_NORMAL);
//創建軌跡條
createTrackbar("參數值:", "【<5>雙邊濾波】",&g_nBilateralFilterValue, 50,on_BilateralFilter);
on_BilateralFilter(g_nBilateralFilterValue,0);
//=======================================================
//輸出一些幫助信息
cout<<endl<<"\t運行成功,請調整滾動條觀察圖像效果~\n\n"
<<"\t按下“q”鍵時,程序退出。\n";
while(char(waitKey(1)) != 'q') {}
return 0;
}
//-----------------------------【on_BoxFilter( )函數】------------------------------------
// 描述:方框濾波操作的回調函數
//-----------------------------------------------------------------------------------------------
static void on_BoxFilter(int, void *)
{
//方框濾波操作
boxFilter( g_srcImage, g_dstImage1, -1,Size( g_nBoxFilterValue+1, g_nBoxFilterValue+1));
//顯示窗口
imshow("【<1>方框濾波】", g_dstImage1);
}
//-----------------------------【on_MeanBlur( )函數】------------------------------------
// 描述:均值濾波操作的回調函數
//-----------------------------------------------------------------------------------------------
static void on_MeanBlur(int, void *)
{
blur( g_srcImage, g_dstImage2, Size( g_nMeanBlurValue+1, g_nMeanBlurValue+1), Point(-1,-1));
imshow("【<2>均值濾波】", g_dstImage2);
}
//-----------------------------【on_GaussianBlur( )函數】------------------------------------
// 描述:高斯濾波操作的回調函數
//-----------------------------------------------------------------------------------------------
static void on_GaussianBlur(int, void )
{
GaussianBlur( g_srcImage, g_dstImage3, Size( g_nGaussianBlurValue2+1, g_nGaussianBlurValue*2+1 ), 0, 0);
imshow("【<3>高斯濾波】", g_dstImage3);
}
//-----------------------------【on_MedianBlur( )函數】------------------------------------
// 描述:中值濾波操作的回調函數
//-----------------------------------------------------------------------------------------------
static void on_MedianBlur(int, void )
{
medianBlur ( g_srcImage, g_dstImage4, g_nMedianBlurValue2+1 );
imshow("【<4>中值濾波】", g_dstImage4);
}
//-----------------------------【on_BilateralFilter( )函數】------------------------------------
// 描述:雙邊濾波操作的回調函數
//-----------------------------------------------------------------------------------------------
static void on_BilateralFilter(int, void )
{
bilateralFilter ( g_srcImage, g_dstImage5, g_nBilateralFilterValue, g_nBilateralFilterValue2, g_nBilateralFilterValue/2 );
imshow("【<5>雙邊濾波】", g_dstImage5);
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
- 126
- 127
- 128
- 129
- 130
- 131
- 132
- 133
- 134
- 135
- 136
- 137
- 138
- 139
- 140
- 141
- 142
- 143
- 144
- 145
- 146
- 147
- 148
- 149
- 150
- 151
- 152
6.3 形態學濾波(1):腐蝕與膨脹
對於一幅二值化圖片,認爲前景是高亮部分(灰度值較高),背景是灰暗部分(灰度值較低)。
膨脹:尋找鄰域內灰度最大值代替該點灰度值,相當於高亮部分增多,所以稱爲膨脹。
函數:dilate()
腐蝕:尋找鄰域內灰度最小值代替該點灰度值,相當於灰暗部分增多,所以稱爲腐蝕。
函數:erode()
6.4 形態學濾波(2):開運算、閉運算、形態學梯度、頂帽、黑帽
開運算:先腐蝕,後膨脹
閉運算:先膨脹、後腐蝕
形態學梯度:膨脹圖與腐蝕圖之差
頂帽:原圖與開運算之差
黑帽:閉運算與結果之差
上述形態學濾波函數均可由morphologyEx()函數實現。
實例:
#include <opencv2/opencv.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
using namespace std;
using namespace cv;
//-----------------------------------【全局變量聲明部分】-----------------------------------
// 描述:全局變量聲明
//-----------------------------------------------------------------------------------------------
Mat g_srcImage, g_dstImage;//原始圖和效果圖
int g_nElementShape = MORPH_RECT;//元素結構的形狀
//變量接收的TrackBar位置參數
int g_nMaxIterationNum = 10;
int g_nOpenCloseNum = 0;
int g_nErodeDilateNum = 0;
int g_nTopBlackHatNum = 0;
//-----------------------------------【全局函數聲明部分】--------------------------------------
// 描述:全局函數聲明
//-----------------------------------------------------------------------------------------------
static void on_OpenClose(int, void*);//回調函數
static void on_ErodeDilate(int, void*);//回調函數
static void on_TopBlackHat(int, void*);//回調函數
//-----------------------------------【main( )函數】--------------------------------------------
// 描述:控制檯應用程序的入口函數,我們的程序從這裏開始
//-----------------------------------------------------------------------------------------------
int main( )
{
//改變console字體顏色
system(“color 2F”);
//載入原圖
g_srcImage = imread("1.jpg");
if( !g_srcImage.data ) { printf("Oh,no,讀取srcImage錯誤~! \n"); return false; }
//顯示原始圖
namedWindow("【原始圖】");
imshow("【原始圖】", g_srcImage);
//創建三個窗口
namedWindow("【開運算/閉運算】",1);
namedWindow("【腐蝕/膨脹】",1);
namedWindow("【頂帽/黑帽】",1);
//參數賦值
g_nOpenCloseNum=9;
g_nErodeDilateNum=9;
g_nTopBlackHatNum=2;
//分別爲三個窗口創建滾動條
createTrackbar("迭代值", "【開運算/閉運算】",&g_nOpenCloseNum,g_nMaxIterationNum*2+1,on_OpenClose);
createTrackbar("迭代值", "【腐蝕/膨脹】",&g_nErodeDilateNum,g_nMaxIterationNum*2+1,on_ErodeDilate);
createTrackbar("迭代值", "【頂帽/黑帽】",&g_nTopBlackHatNum,g_nMaxIterationNum*2+1,on_TopBlackHat);
//輪詢獲取按鍵信息
while(1)
{
int c;
//執行回調函數
on_OpenClose(g_nOpenCloseNum, 0);
on_ErodeDilate(g_nErodeDilateNum, 0);
on_TopBlackHat(g_nTopBlackHatNum,0);
//獲取按鍵
c = waitKey(0);
//按下鍵盤按鍵Q或者ESC,程序退出
if( (char)c == 'q'||(char)c == 27 )
break;
//按下鍵盤按鍵1,使用橢圓(Elliptic)結構元素結構元素MORPH_ELLIPSE
if( (char)c == 49 )//鍵盤按鍵1的ASII碼爲49
g_nElementShape = MORPH_ELLIPSE;
//按下鍵盤按鍵2,使用矩形(Rectangle)結構元素MORPH_RECT
else if( (char)c == 50 )//鍵盤按鍵2的ASII碼爲50
g_nElementShape = MORPH_RECT;
//按下鍵盤按鍵3,使用十字形(Cross-shaped)結構元素MORPH_CROSS
else if( (char)c == 51 )//鍵盤按鍵3的ASII碼爲51
g_nElementShape = MORPH_CROSS;
//按下鍵盤按鍵space,在矩形、橢圓、十字形結構元素中循環
else if( (char)c == ' ' )
g_nElementShape = (g_nElementShape + 1) % 3;
}
return 0;
}
//-----------------------------------【on_OpenClose( )函數】----------------------------------
// 描述:【開運算/閉運算】窗口的回調函數
//-----------------------------------------------------------------------------------------------
static void on_OpenClose(int, void*)
{
//偏移量的定義
int offset = g_nOpenCloseNum - g_nMaxIterationNum;//偏移量
int Absolute_offset = offset > 0 ? offset : -offset;//偏移量絕對值
//自定義核
Mat element = getStructuringElement(g_nElementShape, Size(Absolute_offset2+1, Absolute_offset2+1), Point(Absolute_offset, Absolute_offset) );
//進行操作
if( offset < 0 )
//此句代碼的OpenCV2版爲:
//morphologyEx(g_srcImage, g_dstImage, CV_MOP_OPEN, element);
//此句代碼的OpenCV3版爲:
morphologyEx(g_srcImage, g_dstImage, MORPH_OPEN, element);
else
//此句代碼的OpenCV2版爲:
//morphologyEx(g_srcImage, g_dstImage, CV_MOP_CLOSE, element);
//此句代碼的OpenCV3版爲:
morphologyEx(g_srcImage, g_dstImage, MORPH_CLOSE, element);
//顯示圖像
imshow("【開運算/閉運算】",g_dstImage);
}
//-----------------------------------【on_ErodeDilate( )函數】----------------------------------
// 描述:【腐蝕/膨脹】窗口的回調函數
//-----------------------------------------------------------------------------------------------
static void on_ErodeDilate(int, void*)
{
//偏移量的定義
int offset = g_nErodeDilateNum - g_nMaxIterationNum; //偏移量
int Absolute_offset = offset > 0 ? offset : -offset;//偏移量絕對值
//自定義核
Mat element = getStructuringElement(g_nElementShape, Size(Absolute_offset2+1, Absolute_offset2+1), Point(Absolute_offset, Absolute_offset) );
//進行操作
if( offset < 0 )
erode(g_srcImage, g_dstImage, element);
else
dilate(g_srcImage, g_dstImage, element);
//顯示圖像
imshow("【腐蝕/膨脹】",g_dstImage);
}
//-----------------------------------【on_TopBlackHat( )函數】--------------------------------
// 描述:【頂帽運算/黑帽運算】窗口的回調函數
//----------------------------------------------------------------------------------------------
static void on_TopBlackHat(int, void*)
{
//偏移量的定義
int offset = g_nTopBlackHatNum - g_nMaxIterationNum;//偏移量
int Absolute_offset = offset > 0 ? offset : -offset;//偏移量絕對值
//自定義核
Mat element = getStructuringElement(g_nElementShape, Size(Absolute_offset2+1, Absolute_offset2+1), Point(Absolute_offset, Absolute_offset) );
//進行操作
if( offset < 0 )
morphologyEx(g_srcImage, g_dstImage, MORPH_TOPHAT , element);
else
morphologyEx(g_srcImage, g_dstImage, MORPH_BLACKHAT, element);
//顯示圖像
imshow("【頂帽/黑帽】",g_dstImage);
}
//輸出一些幫助信息
printf("\n\t請調整滾動條觀察圖像效果\n\n");
printf( “\n\t按鍵操作說明: \n\n”
“\t\t鍵盤按鍵【ESC】或者【Q】- 退出程序\n”
“\t\t鍵盤按鍵【1】- 使用橢圓(Elliptic)結構元素\n”
“\t\t鍵盤按鍵【2】- 使用矩形(Rectangle )結構元素\n”
“\t\t鍵盤按鍵【3】- 使用十字型(Cross-shaped)結構元素\n”
“\t\t鍵盤按鍵【空格SPACE】- 在矩形、橢圓、十字形結構元素中循環\n” );
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
- 126
- 127
- 128
- 129
- 130
- 131
- 132
- 133
- 134
- 135
- 136
- 137
- 138
- 139
- 140
- 141
- 142
- 143
- 144
- 145
- 146
- 147
- 148
- 149
- 150
- 151
- 152
- 153
- 154
- 155
- 156
- 157
- 158
6.5 漫水填充
漫水填充是一種用特定的顏色填充連通區域,通過設置可連通像素的上下限以及連通方式來達到不同的填充效果的辦法。
函數:floodFill();
使用空範圍的漫水填充
使用漸變、固定範圍的漫水填充
使用漸變、浮動範圍的漫水填充
操作標誌符的低八位使用4位的連接模式
操作標誌符的低八位使用8位的連接模式
6.6 圖像金字塔與圖片尺寸縮放
圖像金字塔,通常指高斯採樣(高斯金字塔)
從下到上,層數逐漸增加,越向上圖像越小
拉格朗日金字塔:原圖像利用高斯採樣經先縮小再增大
向上採樣:擴大尺寸
函數:pyrUp()
向下採樣:減小尺寸
函數:pyrDown()
尺寸調整:resize();
可選擇增大或縮小倍數,或直接給出輸出圖像大小。
也可選擇插值方式,通常選擇效率較高的線性插值。
6.7 閾值化
固定閾值操作:Threshold()函數
5種操作。0-4
0:二進制閾值
1:反二進制閾值
2:截斷閾值
3:閾值化爲0
4:反閾值化爲0
自適應閾值操作:adaptiveThreshold()函數
7.1 基於OpenCV的邊緣檢測
邊緣檢測幾種方法:
canny算子:帶兩個閾值,內部使用Sobel算子。
sobel算子: 分別求x,y方向的梯度,然後融合,實現邊緣檢測。
Laplacian 算子:使用sobel算子二階微分。
scharr濾波器:內核爲3不能變。
程序實例:
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
using namespace cv;
//-----------------------------------【全局變量聲明部分】--------------------------------------
// 描述:全局變量聲明
//-----------------------------------------------------------------------------------------------
//原圖,原圖的灰度版,目標圖
Mat g_srcImage, g_srcGrayImage,g_dstImage;
//Canny邊緣檢測相關變量
Mat g_cannyDetectedEdges;
int g_cannyLowThreshold=1;//TrackBar位置參數
//Sobel邊緣檢測相關變量
Mat g_sobelGradient_X, g_sobelGradient_Y;
Mat g_sobelAbsGradient_X, g_sobelAbsGradient_Y;
int g_sobelKernelSize=1;//TrackBar位置參數
//Scharr濾波器相關變量
Mat g_scharrGradient_X, g_scharrGradient_Y;
Mat g_scharrAbsGradient_X, g_scharrAbsGradient_Y;
//-----------------------------------【全局函數聲明部分】--------------------------------------
// 描述:全局函數聲明
//-----------------------------------------------------------------------------------------------
static void ShowHelpText( );
static void on_Canny(int, void*);//Canny邊緣檢測窗口滾動條的回調函數
static void on_Sobel(int, void*);//Sobel邊緣檢測窗口滾動條的回調函數
void Scharr( );//封裝了Scharr邊緣檢測相關代碼的函數
//-----------------------------------【main( )函數】--------------------------------------------
// 描述:控制檯應用程序的入口函數,我們的程序從這裏開始
//-----------------------------------------------------------------------------------------------
int main( int argc, char** argv )
{
//改變console字體顏色
system(“color 2F”);
//載入原圖
g_srcImage = imread("1.jpg");
if( !g_srcImage.data ) { printf("Oh,no,讀取srcImage錯誤~! \n"); return false; }
//顯示原始圖
namedWindow("【原始圖】");
imshow("【原始圖】", g_srcImage);
// 創建與src同類型和大小的矩陣(dst)
g_dstImage.create( g_srcImage.size(), g_srcImage.type() );
// 將原圖像轉換爲灰度圖像
cvtColor( g_srcImage, g_srcGrayImage, COLOR_BGR2GRAY );
// 創建顯示窗口
namedWindow( "【效果圖】Canny邊緣檢測", WINDOW_AUTOSIZE );
namedWindow( "【效果圖】Sobel邊緣檢測", WINDOW_AUTOSIZE );
// 創建trackbar
createTrackbar( "參數值:", "【效果圖】Canny邊緣檢測", &g_cannyLowThreshold, 120, on_Canny );
createTrackbar( "參數值:", "【效果圖】Sobel邊緣檢測", &g_sobelKernelSize, 3, on_Sobel );
// 調用回調函數
on_Canny(0, 0);
on_Sobel(0, 0);
//調用封裝了Scharr邊緣檢測代碼的函數
Scharr( );
//輪詢獲取按鍵信息,若按下Q,程序退出
while((char(waitKey(1)) != 'q')) {}
return 0;
}
//-----------------------------------【on_Canny( )函數】----------------------------------
// 描述:Canny邊緣檢測窗口滾動條的回調函數
//-----------------------------------------------------------------------------------------------
void on_Canny(int, void*)
{
// 先使用 3x3內核來降噪
blur( g_srcGrayImage, g_cannyDetectedEdges, Size(3,3) );
// 運行我們的Canny算子
Canny( g_cannyDetectedEdges, g_cannyDetectedEdges, g_cannyLowThreshold, g_cannyLowThreshold*3, 3 );
//先將g_dstImage內的所有元素設置爲0
g_dstImage = Scalar::all(0);
//使用Canny算子輸出的邊緣圖g_cannyDetectedEdges作爲掩碼,來將原圖g_srcImage拷到目標圖g_dstImage中
g_srcImage.copyTo( g_dstImage, g_cannyDetectedEdges);
//顯示效果圖
imshow( "【效果圖】Canny邊緣檢測", g_dstImage );
}
//-----------------------------------【on_Sobel( )函數】----------------------------------
// 描述:Sobel邊緣檢測窗口滾動條的回調函數
//-----------------------------------------------------------------------------------------
void on_Sobel(int, void*)
{
// 求 X方向梯度
Sobel( g_srcImage, g_sobelGradient_X, CV_16S, 1, 0, (2*g_sobelKernelSize+1), 1, 1, BORDER_DEFAULT );
convertScaleAbs( g_sobelGradient_X, g_sobelAbsGradient_X );//計算絕對值,並將結果轉換成8位
// 求Y方向梯度
Sobel( g_srcImage, g_sobelGradient_Y, CV_16S, 0, 1, (2*g_sobelKernelSize+1), 1, 1, BORDER_DEFAULT );
convertScaleAbs( g_sobelGradient_Y, g_sobelAbsGradient_Y );//計算絕對值,並將結果轉換成8位
// 合併梯度
addWeighted( g_sobelAbsGradient_X, 0.5, g_sobelAbsGradient_Y, 0.5, 0, g_dstImage );
//顯示效果圖
imshow("【效果圖】Sobel邊緣檢測", g_dstImage);
}
//-----------------------------------【Scharr( )函數】---------------------------------
// 描述:封裝了Scharr邊緣檢測相關代碼的函數
//-----------------------------------------------------------------------------------------
void Scharr( )
{
// 求 X方向梯度
Scharr( g_srcImage, g_scharrGradient_X, CV_16S, 1, 0, 1, 0, BORDER_DEFAULT );
convertScaleAbs( g_scharrGradient_X, g_scharrAbsGradient_X );//計算絕對值,並將結果轉換成8位
// 求Y方向梯度
Scharr( g_srcImage, g_scharrGradient_Y, CV_16S, 0, 1, 1, 0, BORDER_DEFAULT );
convertScaleAbs( g_scharrGradient_Y, g_scharrAbsGradient_Y );//計算絕對值,並將結果轉換成8位
// 合併梯度
addWeighted( g_scharrAbsGradient_X, 0.5, g_scharrAbsGradient_Y, 0.5, 0, g_dstImage );
//顯示效果圖
imshow("【效果圖】Scharr濾波器", g_dstImage);
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
- 126
- 127
- 128
- 129
- 130
- 131
- 132
- 133
- 134
- 135
- 136
- 137
- 138
- 139
- 140
- 141
7.2 霍夫變換
霍夫變換主要用來快速準確地檢測出直線或者圓。
須先進行邊緣檢測:
標準霍夫變換:HoughLines()函數
累計概率霍夫變換:HoughLinesP()函數
霍夫圓變換:HoughCircles()函數
程序實例:
#include <opencv2/opencv.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
using namespace std;
using namespace cv;
//-----------------------------------【全局變量聲明部分】--------------------------------------
// 描述:全局變量聲明
//-----------------------------------------------------------------------------------------------
Mat g_srcImage, g_dstImage,g_midImage;//原始圖、中間圖和效果圖
vector<Vec4i> g_lines;//定義一個矢量結構g_lines用於存放得到的線段矢量集合
//變量接收的TrackBar位置參數
int g_nthreshold=100;
//-----------------------------------【全局函數聲明部分】--------------------------------------
// 描述:全局函數聲明
//-----------------------------------------------------------------------------------------------
static void on_HoughLines(int, void*);//回調函數
static void ShowHelpText();
//-----------------------------------【main( )函數】--------------------------------------------
// 描述:控制檯應用程序的入口函數,我們的程序從這裏開始
//-----------------------------------------------------------------------------------------------
int main( )
{
//改變console字體顏色
system(“color 4F”);
//載入原始圖和Mat變量定義
Mat g_srcImage = imread("1.jpg"); //工程目錄下應該有一張名爲1.jpg的素材圖
//顯示原始圖
imshow("【原始圖】", g_srcImage);
//創建滾動條
namedWindow("【效果圖】",1);
createTrackbar("值", "【效果圖】",&g_nthreshold,200,on_HoughLines);
//進行邊緣檢測和轉化爲灰度圖
Canny(g_srcImage, g_midImage, 50, 200, 3);//進行一次canny邊緣檢測
cvtColor(g_midImage,g_dstImage, COLOR_GRAY2BGR);//轉化邊緣檢測後的圖爲灰度圖
//調用一次回調函數,調用一次HoughLinesP函數
on_HoughLines(g_nthreshold,0);
HoughLinesP(g_midImage, g_lines, 1, CV_PI/180, 80, 50, 10 );
//顯示效果圖
imshow("【效果圖】", g_dstImage);
waitKey(0);
return 0;
}
//-----------------------------------【on_HoughLines( )函數】--------------------------------
// 描述:【頂帽運算/黑帽運算】窗口的回調函數
//----------------------------------------------------------------------------------------------
static void on_HoughLines(int, void*)
{
//定義局部變量儲存全局變量
Mat dstImage=g_dstImage.clone();
Mat midImage=g_midImage.clone();
//調用HoughLinesP函數
vector<Vec4i> mylines;
HoughLinesP(midImage, mylines, 1, CV_PI/180, g_nthreshold+1, 50, 10 );
//循環遍歷繪製每一條線段
for( size_t i = 0; i < mylines.size(); i++ )
{
Vec4i l = mylines[i];
line( dstImage, Point(l[0], l[1]), Point(l[2], l[3]), Scalar(23,180,55), 1, LINE_AA);
}
//顯示圖像
imshow("【效果圖】",dstImage);
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
7.3 重映射
實現重映射:remap()函數
重映射:就是把一幅圖像中某位置的像素放置到另一個圖片指定位置的過程。可進行X方向鏡面,Y方向鏡面,等多重映射。通過改變參數map.x以及map.y。
程序實例:
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include <iostream>
using namespace cv;
using namespace std;
//-----------------------------------【宏定義部分】--------------------------------------------
// 描述:定義一些輔助宏
//------------------------------------------------------------------------------------------------
#define WINDOW_NAME “【程序窗口】” //爲窗口標題定義的宏
//-----------------------------------【全局變量聲明部分】--------------------------------------
// 描述:全局變量的聲明
//-----------------------------------------------------------------------------------------------
Mat g_srcImage, g_dstImage;
Mat g_map_x, g_map_y;
//-----------------------------------【全局函數聲明部分】--------------------------------------
// 描述:全局函數的聲明
//-----------------------------------------------------------------------------------------------
int update_map( int key);
static void ShowHelpText( );//輸出幫助文字
//-----------------------------------【main( )函數】--------------------------------------------
// 描述:控制檯應用程序的入口函數,我們的程序從這裏開始執行
//-----------------------------------------------------------------------------------------------
int main( int argc, char** argv )
{
//改變console字體顏色
system(“color 5F”);
//顯示幫助文字
ShowHelpText();
//【1】載入原始圖
g_srcImage = imread( "1.jpg", 1 );
if(!g_srcImage.data ) { printf("讀取圖片錯誤,請確定目錄下是否有imread函數指定的圖片存在~! \n"); return false; }
imshow("原始圖",g_srcImage);
//【2】創建和原始圖一樣的效果圖,x重映射圖,y重映射圖
g_dstImage.create( g_srcImage.size(), g_srcImage.type() );
g_map_x.create( g_srcImage.size(), CV_32FC1 );
g_map_y.create( g_srcImage.size(), CV_32FC1 );
//【3】創建窗口並顯示
namedWindow( WINDOW_NAME, WINDOW_AUTOSIZE );
imshow(WINDOW_NAME,g_srcImage);
//【4】輪詢按鍵,更新map_x和map_y的值,進行重映射操作並顯示效果圖
while( 1 )
{
//獲取鍵盤按鍵
int key = waitKey(0);
//判斷ESC是否按下,若按下便退出
if( (key & 255) == 27 )
{
cout << "程序退出...........\n";
break;
}
//根據按下的鍵盤按鍵來更新 map_x & map_y的值. 然後調用remap( )進行重映射
update_map(key);
//此句代碼的OpenCV2版爲:
//remap( g_srcImage, g_dstImage, g_map_x, g_map_y, CV_INTER_LINEAR, BORDER_CONSTANT, Scalar(0,0, 0) );
//此句代碼的OpenCV3版爲:
remap( g_srcImage, g_dstImage, g_map_x, g_map_y, INTER_LINEAR, BORDER_CONSTANT, Scalar(0,0, 0) );
//顯示效果圖
imshow( WINDOW_NAME, g_dstImage );
}
return 0;
}
//-----------------------------------【update_map( )函數】--------------------------------
// 描述:根據按鍵來更新map_x與map_x的值
//----------------------------------------------------------------------------------------------
int update_map( int key )
{
//雙層循環,遍歷每一個像素點
for( int j = 0; j < g_srcImage.rows;j++)
{
for( int i = 0; i < g_srcImage.cols;i++)
{
switch(key)
{
case ‘1’: // 鍵盤【1】鍵按下,進行第一種重映射操作
if( i > g_srcImage.cols0.25 && i < g_srcImage.cols0.75 && j > g_srcImage.rows0.25 && j < g_srcImage.rows0.75)
{
g_map_x.at<float>(j,i) = static_cast<float>(2*( i - g_srcImage.cols0.25 ) + 0.5);
g_map_y.at<float>(j,i) = static_cast<float>(2( j - g_srcImage.rows*0.25 ) + 0.5);
}
else
{
g_map_x.at<float>(j,i) = 0;
g_map_y.at<float>(j,i) = 0;
}
break;
case ‘2’😕/ 鍵盤【2】鍵按下,進行第二種重映射操作
g_map_x.at<float>(j,i) = static_cast<float>(i);
g_map_y.at<float>(j,i) = static_cast<float>(g_srcImage.rows - j);
break;
case ‘3’😕/ 鍵盤【3】鍵按下,進行第三種重映射操作
g_map_x.at<float>(j,i) = static_cast<float>(g_srcImage.cols - i);
g_map_y.at<float>(j,i) = static_cast<float>(j);
break;
case ‘4’😕/ 鍵盤【4】鍵按下,進行第四種重映射操作
g_map_x.at<float>(j,i) = static_cast<float>(g_srcImage.cols - i);
g_map_y.at<float>(j,i) = static_cast<float>(g_srcImage.rows - j);
break;
}
}
}
return 1;
}
//-----------------------------------【ShowHelpText( )函數】----------------------------------
// 描述:輸出一些幫助信息
//----------------------------------------------------------------------------------------------
static void ShowHelpText()
{
//輸出歡迎信息和OpenCV版本
printf("\n\n\t\t\t非常感謝購買《OpenCV3編程入門》一書!\n");
printf("\n\n\t\t\t此爲本書OpenCV3版的第66個配套示例程序\n");
printf("\n\n\t\t\t 當前使用的OpenCV版本爲:" CV_VERSION );
printf("\n\n ----------------------------------------------------------------------------\n");
//輸出一些幫助信息
printf("\n\t歡迎來到重映射示例程序~\n\n");
printf( “\n\t按鍵操作說明: \n\n”
“\t\t鍵盤按鍵【ESC】- 退出程序\n”
“\t\t鍵盤按鍵【1】- 第一種映射方式\n”
“\t\t鍵盤按鍵【2】- 第二種映射方式\n”
“\t\t鍵盤按鍵【3】- 第三種映射方式\n”
“\t\t鍵盤按鍵【4】- 第四種映射方式\n” );
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
- 126
- 127
- 128
- 129
- 130
- 131
- 132
- 133
- 134
- 135
- 136
- 137
- 138
7.4 仿射變換
對圖像進行旋轉、平移、縮放。
進行仿射變換:warpAffine()函數 ,需要輸入仿射變換矩陣M (2*3)
getAffineTransform(),通過原圖像與目標圖像各三點求取仿射變換矩陣M
計算二維旋轉變換矩陣:getRotationMatrix2D()函數,通過繞點,角度,縮放尺度來計算旋轉矩陣M
7.5 直方圖均衡化
實現直方圖均衡化:equalizeHist(src,dst)函數
直方圖均衡化是通過拉伸像素強度分佈範圍來增強圖像對比度的一種方法。
8.1 查找並繪製輪廓
從二值圖像中查找輪廓。
尋找輪廓:findContours()函數
繪製輪廓:drawContours()函數
8.2 尋找物體的凸包
凸包:給定二維平面上的點集,凸包就是將最外層的點連接起來構成的凸多邊形。
尋找凸包:convexHull()函數
存在hull中,可使用line函數或者drawContours()函數將其繪製出來。
8.3 使用多邊形將輪廓包圍
返回外部矩形邊界:boundingRect()函數
尋找最小包圍矩形:minAreaRect()函數 (可以是斜着的)
尋找最小包圍圓形:minEnclosingCircle()函數
用橢圓擬合二維點集:fitEllipse()函數
逼近多邊形曲線:approxPolyDP()函數 ,使用方法類似凸包,結果存在counters_poly中,需使用line或者drawContours()繪製出來。
8.4 圖像的矩
矩用來計算形狀的重心、面積,主軸和其他形狀特徵。
矩的計算:moments()函數
計算輪廓面積:contourArea()函數
計算輪廓長度:arcLength()函數
輪廓面積也可以矩的.m00計算,結果與contourArea()一致。
8.5 分水嶺算法
實現分水嶺算法:watershed()函數
圖像分割的一種,分割前需要大致勾畫出需要分割的區域。
8.6 圖像修補
圖像修復主要用來解決圖象被噪聲腐蝕的問題,比如鏡頭上的水滴、灰塵或者是舊照片的劃痕。
主要是利用那些已經被破壞的區域的邊緣,即邊緣的顏色和結構,繁殖和混合到損壞的圖像中,以達到圖像修補的目的。
實現圖像修補:inpaint()函數
9.1 圖像直方圖概述
圖像直方圖是用以表示數字圖像中亮度分佈的直方圖,標繪了圖像中每個亮度值的像素數。
9.2 直方圖的計算與繪製
計算直方圖:calcHist()函數
計算出來存在MatND類型的hist中,如需繪圖,還需進行歸一化,繪製。
找尋最值:minMaxLoc()函數 ,可以返回最大值指針,最小值指針,最大值對應位置指針,最小值對應位置指針。
9.3 直方圖對比
對比直方圖:compareHist()函數
有四種方式進行對比。
9.4 反向投影
反像投影中存儲的數值反應的是該像素對應亮度所在直方圖中的bin區間中的值的概率。
反向投影的作用:反像投影可用於在輸入圖像(通常較大)中查找與特定圖像(通常較小或者僅一個像素,也就是模板圖像)最匹配的點或者區域,也就是定位模板圖像出現在輸入圖像的位置。
計算反向投影:calcBackProject()函數
使用反像投影匹配:cvCalcBackProjectPatch()函數
通道複製:mixChannels()函數
可實現通道分離,合併,交換,重新分配
9.5 模板匹配
模板匹配是一項在一幅圖像中尋找與另一幅模板圖像最匹配(相似)部分的技術。
實現模板匹配:matchTemplate()函數
有六種方法進行匹配。
第四部分 深入feature2d組件
10.1 Harris角點檢測
角點定義:如果某一點在任意方向的一個微小變動都會引起灰度很大的變化,那麼我們就把它稱之爲角點。
實現Harris角點檢測:cornerHarris()函數
將檢測到的存入圖像中,通過閾值化操作將其顯示出來。
10.2 Shi-Tomasi角點檢測
確定圖像強角點:goodFeaturesToTrack()函數
不需要比較閾值,只需給出最大檢測角點處,以及角點間允許最小距離,檢測出的角點存在vector中。
10.3 亞像素級角點檢測
goodFeaturesToTrack()函數只能提供像素座標的整數值,有時需要實數座標值,
這時需要cornerSubPix()函數。
尋找亞像素角點:cornerSubPix()函數
11.1 SURF特徵點檢測
SURF是尺度不變特徵變換算法(SIFT)的加速版。一般來說,標準的SURF算子比SIFT算子快好幾倍,並且在多幅圖片下具有更好的穩定性。
SurfFeatureDetector detector
detector.detect進行檢測特徵點。
繪製關鍵點:drawKeypoints()函數
KeyPoint類用於存儲特徵點。
11.2 SURF特徵提取
步驟:
-
檢測特徵點:SurfFeatureDetector detector -> detector.detect
-
計算描述子(特徵向量)SurfDescriptorExtractor extractor -> extractor.compute
-
特徵匹配:BruteForceMatcher< L2 > matcher –> matcher.match
-
繪製匹配點:drawMatches()函數
程序實例:#include "opencv2/core/core.hpp" #include "opencv2/features2d/features2d.hpp" #include "opencv2/highgui/highgui.hpp" #include <opencv2/nonfree/nonfree.hpp> #include<opencv2/legacy/legacy.hpp> #include <iostream> using namespace cv; using namespace std;
int main( )
{
//【0】改變console字體顏色
system(“color 1F”);
//【1】載入素材圖
Mat srcImage1 = imread(“1.jpg”,1);
Mat srcImage2 = imread(“2.jpg”,1);
if( !srcImage1.data || !srcImage2.data )
{ printf(“讀取圖片錯誤,請確定目錄下是否有imread函數指定的圖片存在~! \n”); return false; }
//【2】使用SURF算子檢測關鍵點
int minHessian = 700;//SURF算法中的hessian閾值
SurfFeatureDetector detector( minHessian );//定義一個SurfFeatureDetector(SURF) 特徵檢測類對象
std::vector<KeyPoint> keyPoint1, keyPoints2;//vector模板類,存放任意類型的動態數組
//【3】調用detect函數檢測出SURF特徵關鍵點,保存在vector容器中
detector.detect( srcImage1, keyPoint1 );
detector.detect( srcImage2, keyPoints2 );
//【4】計算描述符(特徵向量)
SurfDescriptorExtractor extractor;
Mat descriptors1, descriptors2;
extractor.compute( srcImage1, keyPoint1, descriptors1 );
extractor.compute( srcImage2, keyPoints2, descriptors2 );
//【5】使用BruteForce進行匹配
// 實例化一個匹配器
BruteForceMatcher< L2<float> > matcher;
std::vector< DMatch > matches;
//匹配兩幅圖中的描述子(descriptors)
matcher.match( descriptors1, descriptors2, matches );
//【6】繪製從兩個圖像中匹配出的關鍵點
Mat imgMatches;
drawMatches( srcImage1, keyPoint1, srcImage2, keyPoints2, matches, imgMatches );//進行繪製
//【7】顯示效果圖
imshow(“匹配圖”, imgMatches );
waitKey(0);
return 0;
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
}
11.3 使用FLANN進行特徵點匹配
另一種特徵匹配方法,可實現快速高效匹配。
FlannBasedMatcher類
DescriptorMatcher::match
11.4 尋找已知物體
在FLANN特徵匹配基礎上,還可以進一步利用Homography映射找出已知物體。
尋找透視變換:findHomography()函數
進行透視矩陣變換:perspectiveTransform()函數
步驟:
- 使用findHomography()函數尋找匹配上的關鍵點的變換。
- 使用perspectiveTransform()函數來映射點。
11.5 ORB特徵提取
ORB算法是brief算法的改進版。
ORB算法概述算法比sift算法效率高兩個數量級,而在計算速度上,ORB是sift的100倍,是surf的10倍。ORB算法綜合性能在各種測評裏相較於其他提取算法是最好的。
BRIEF的優點在於速度,缺點也很明顯。
不具備旋轉不變性。
對噪聲敏感。
不具備尺度不變性。
ORB算法解決了上述缺點的1.2,但未解決3.但後續如果用在視頻處理,可通過跟蹤等策略解決。
OrbFeatureDetector featureDetector 特徵檢測
OrbDescriptorExtractor featureExtractor; 計算描述子
基於FLANN的描述符對象匹配
程序實例:
#include <opencv2/opencv.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/nonfree/features2d.hpp>
#include <opencv2/features2d/features2d.hpp>
using namespace cv;
using namespace std;
int main( )
{
//【0】改變console字體顏色
system(“color 2F”);
//【0】顯示幫助文字
//【0】載入源圖,顯示並轉化爲灰度圖
Mat srcImage = imread("E:\\imagelib\\78.jpg");
imshow("原始圖",srcImage);
Mat grayImage;
cvtColor(srcImage, grayImage, CV_BGR2GRAY);
//------------------檢測SIFT特徵點並在圖像中提取物體的描述符----------------------
//【1】參數定義
OrbFeatureDetector featureDetector;
vector<KeyPoint> keyPoints;
Mat descriptors;
//【2】調用detect函數檢測出特徵關鍵點,保存在vector容器中
featureDetector.detect(grayImage, keyPoints);
//【3】計算描述符(特徵向量)
OrbDescriptorExtractor featureExtractor;
featureExtractor.compute(grayImage, keyPoints, descriptors);
//【4】基於FLANN的描述符對象匹配
flann::Index flannIndex(descriptors, flann::LshIndexParams(12, 20, 2), cvflann::FLANN_DIST_HAMMING);
//【5】初始化視頻採集對象
VideoCapture cap(0);
unsigned int frameCount = 0;//幀數
//【6】輪詢,直到按下ESC鍵退出循環
Mat captureImage = imread("E:\\imagelib\\79.jpg");
imshow("原始圖2", captureImage);
Mat captureImage_gray;//定義兩個Mat變量,用於視頻採集
cvtColor(captureImage, captureImage_gray, CV_BGR2GRAY);
vector<KeyPoint> captureKeyPoints;
Mat captureDescription;
//【8】調用detect函數檢測出特徵關鍵點,保存在vector容器中
featureDetector.detect(captureImage_gray, captureKeyPoints);
//【9】計算描述符
featureExtractor.compute(captureImage_gray, captureKeyPoints, captureDescription);
//【10】匹配和測試描述符,獲取兩個最鄰近的描述符
Mat matchIndex(captureDescription.rows, 2, CV_32SC1), matchDistance(captureDescription.rows, 2, CV_32FC1);
flannIndex.knnSearch(captureDescription, matchIndex, matchDistance, 2, flann::SearchParams());//調用K鄰近算法
//【11】根據勞氏算法(Lowe's algorithm)選出優秀的匹配
vector<DMatch> goodMatches;
for(int i = 0; i < matchDistance.rows; i++)
{
if(matchDistance.at<float>(i, 0) < 0.6 * matchDistance.at<float>(i, 1))
{
DMatch dmatches(i, matchIndex.at<int>(i, 0), matchDistance.at<float>(i, 0));
goodMatches.push_back(dmatches);
}
}
//【12】繪製並顯示匹配窗口
Mat resultImage;
drawMatches( captureImage, captureKeyPoints, srcImage, keyPoints, goodMatches, resultImage);
imshow("匹配窗口", resultImage);
return 0;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
</div>
<link href="https://csdnimg.cn/release/phoenix/mdeditor/markdown_views-b6c3c6d139.css" rel="stylesheet">
</div>