第1章 基礎(chǔ)知識(shí)
1.1 基礎(chǔ)結(jié)構(gòu)介紹
作者博客https://blog.csdn.net/shuiyixin?type=blog
https://blog.csdn.net/shuiyixin/article/details/106046827
1.1.1 Mat類
https://blog.csdn.net/shuiyixin/article/details/106014341
Mat src, src_roi;
src = imread("./image/cat.jpg");
if (!src.data)
{
cout << "ERROR : could not load image.\n";
waitKey(0);
return -1;
}
imshow("input image", src);
src_roi = Mat(src, Rect(100, 100, 300, 200));//很常用的構(gòu)建mat類型的方法
imshow("roi image", src_roi);
1.1.2 Rect_結(jié)構(gòu)
https://blog.csdn.net/shuiyixin/article/details/106085233
1.1.3 Scalar_結(jié)構(gòu)
Scalar其實(shí)是一個(gè)從Vec派生得到的四元向量的模板類
Scalar_(_Tp v0, _Tp v1, _Tp v2=0, _Tp v3=0);
Scalar(0, 0, 255); // 紅色
Scalar(0, 255, 0); // 綠色
Scalar(255, 0, 0); // 藍(lán)色
一般情況,我們只賦值前三個(gè),第一個(gè)參數(shù)表示藍(lán)色,第二個(gè)參數(shù)表示綠色,第三個(gè)參數(shù)表示紅色,也就是BGR:
https://blog.csdn.net/shuiyixin/article/details/106111460
1.1.4 RNG類
https://blog.csdn.net/weixin_43588171/article/details/104810295
1.2 基礎(chǔ)函數(shù)
1.2.1 saturate_cast()
https://blog.csdn.net/qq_27278957/article/details/88648737
在opencv中,saturate_cast的作用是防止數(shù)據(jù)溢出,作出保護(hù)。原理可理解為如下代碼功能:
if(data < 0)
data = 0;
else if(data > 255)
data = 255;
1.2.2 reshape()
C++: Mat Mat::reshape(int cn, int rows=0) const
cn: 表示通道數(shù)(channels), 如果設(shè)為0,則表示保持通道數(shù)不變,否則則變?yōu)樵O(shè)置的通道數(shù)。
rows: 表示矩陣行數(shù)。 如果設(shè)為0,則表示保持原有的行數(shù)不變,否則則變?yōu)樵O(shè)置的行數(shù)。
1.2.3 Create()
函數(shù)原型:
inline void Mat::create(int _rows, int _cols, int _type)
inline void Mat::create(Size _sz, int _type)
void Mat::create(int ndims, const int* sizes, int type)
函數(shù)功能:
1)如果需要,分配新的數(shù)組數(shù)據(jù)
2)創(chuàng)建一個(gè)圖像矩陣的矩陣體
1.3 程序
1.3.1 退出程序
char key = (char)waitKey();
if (key == 27 || key == 'q' || key == 'Q')
{
break;
}
第2章 數(shù)據(jù)載入、顯示和保存
2.1 圖像存儲(chǔ)容器
數(shù)字圖像在計(jì)算機(jī)中是以矩陣形式存儲(chǔ)的,矩陣中的每一個(gè)元素都描述一定的圖像信息,如亮度、顏色等
OpenCV利用Mat 類用于存儲(chǔ)數(shù)據(jù),利用自動(dòng)內(nèi)存管理技術(shù)很好地解決了內(nèi)存自動(dòng)釋放的問題,當(dāng)變量不再需要時(shí), 立即釋放內(nèi)存.
CV_8U是 unsign 的8位/像素-即一個(gè)像素的值在0-255區(qū)間,這是大多數(shù)圖像和視頻格式的正常范圍。
CV_32F是 float -像素是在0-1.0之間的任意值,這對(duì)于一些數(shù)據(jù)集的計(jì)算很有用,但是它必須通過將每個(gè)像素乘以255來(lái)轉(zhuǎn)換成8位來(lái)保存或顯示。
2.1.2 Mat類構(gòu)造與賦值
1.Mat類的構(gòu)造
cv::Mat::Mat(3,3,CV_8S);//對(duì)應(yīng)代碼清單2-5
cv::Mat::Mat(cv::Size(3, 3), CV_8UC1);//對(duì)應(yīng)代碼清單2-6
cv::Mat A = cv::Mat_<double>(3, 3);
cv::Mat f = cv::Mat::Mat(3,3,CV_8S);
cv::Mat g = cv::Mat::Mat(cv::Size(3, 3), CV_8UC1);
cv::Mat h(cv::Size(3, 3), CV_8UC1);
cv::Mat i = g;
cv::Mat j(g);//修改j會(huì)修改g的值
cv::Mat k(g, cv::Range(2, 5), cv::Range::all());//range表示下標(biāo)索引,,從0開始,0是第一行,這個(gè)意思是表示為下標(biāo)為2的第3行到下標(biāo)為5的第6行前(不包括下標(biāo)為5的第6行)
cv::Mat k(g, cv::Range(2, 5), cv::Range::all());//修改k會(huì)修改g的值
cv::Mat::Mat(g, cv::Range(2, 5), cv::Range::all());//修改xx會(huì)修改g的值
2.Mat類的賦值
cv::Mat g = cv::Mat::Mat(cv::Size(3, 3), CV_8UC1, cv::Scalar(255));
cv::Mat g = (cv::Mat_<int>(3, 3) << 1, 2, 3, 4, 5, 6, 7, 8, 9);
cv::Mat c = cv::Mat_<int>(3, 3);
for (int i = 0; i < c.rows; i++)
{
for (int j = 0; j < c.cols; j++)
{
c.at<int>(i, j) = i + j;//與cv::Mat_<int>(3, 3);的int必須一致
}
}
//生成單位矩陣
cv::Mat a(cv::Mat::eye(3, 3, CV_8UC1));
//生成特定對(duì)角矩陣
cv::Mat b = (cv::Mat_<int>(1, 3) << 1, 2, 3);
cv::Mat c = cv::Mat::diag(b);
//生成1矩陣、零矩陣
cv::Mat d = cv::Mat::ones(3, 3, CV_8UC1);
cv::Mat e = cv::Mat::zeros(3, 3, CV_8UC3);
//矩陣運(yùn)算
cv::Mat a = (cv::Mat_<int>(3, 3) << 1, 2, 3, 4, 5, 6, 7, 8, 9);
cv::Mat b = (cv::Mat_<int>(3, 3) << 1, 2, 3, 4, 5, 6, 7, 8, 9);
cv::Mat c = (cv::Mat_<double>(3, 3) << 1.0, 2.1, 3.2, 4.0, 5.1, 6.2, 2, 2, 2);
cv::Mat d = (cv::Mat_<double>(3, 3) << 1.0, 2.1, 3.2, 4.0, 5.1, 6.2, 2, 2, 2);
cv::Mat e, f, g, h, i;
e = a + b;//四則運(yùn)算超了最大值按照最大值算
f = c - d;
g = 2 * a;
h = d / 2.0;
i = a - 1;
cv::Mat j, m;/*Mat 類中的數(shù)據(jù)類型必須是CV_32FC I 、CV_64FCI 、CV _32FC2 , CV 64FC2
這4 種中的一種,也就是對(duì)于一個(gè)二維的Mat 類矩陣, 其保存的數(shù)據(jù)類型必、須是float類型或者
double 類型*/
double k;
j = c * d;
k = a.dot(b);//相當(dāng)于向量運(yùn)算中的點(diǎn)成,也叫向量的內(nèi)積、數(shù)量積
//Mat矩陣的dot方法擴(kuò)展了一維向量的點(diǎn)乘操作,把整個(gè)Mat矩陣擴(kuò)展成一個(gè)行(列)向量,之后執(zhí)行向量的點(diǎn)乘運(yùn)算,仍然要求參與dot運(yùn)算的兩個(gè)Mat矩陣的行列數(shù)完全一致。
//dot操作不對(duì)參與運(yùn)算的矩陣A、B的數(shù)據(jù)類型做要求
//若參與dot運(yùn)算的兩個(gè)Mat矩陣是多通道的,則計(jì)算結(jié)果是所有通道單獨(dú)計(jì)算各自.dot之后,再累計(jì)的和,結(jié)果仍是一個(gè)double類型數(shù)據(jù)。
m = a.mul(b);//對(duì)應(yīng)位相乘,放在原位
// 對(duì)數(shù)據(jù)類型沒有要求,但A、B必須一致
// 產(chǎn)生的數(shù)據(jù)類型默認(rèn)與A、B一致
/*在圖像處理領(lǐng)域, 常用的數(shù)據(jù)類型是CV_8U ,其范圍是0-255 ,當(dāng)兩個(gè)比較大的整數(shù)相乘時(shí),
就會(huì)產(chǎn)生結(jié)果溢出的現(xiàn)象, 輸出結(jié)果為255 ,因此,在使用mu1 0方法時(shí), 需要防止出現(xiàn)數(shù)據(jù)溢出
的問題.*/
2.1.4 Mat元素的讀取
cv::Mat b(3, 4, CV_8UC3, cv::Scalar(0, 0, 1));
cv::Vec3b vc3 = b.at<cv::Vec3b>(0, 0);//cv::Vec3x對(duì)應(yīng)三通道數(shù)據(jù),cv::Vec2x對(duì)應(yīng)二通道數(shù)據(jù),cv::Vec4x對(duì)應(yīng)四通道數(shù)據(jù)
int first = (int)vc3.val[0];
int second = (int)vc3.val[1];
int third = (int)vc3.val[2];
std::cout << first << " " << second << " " << third << "" << std::endl;
for (int i = 0; i < b.rows; i++)
{
uchar* ptr = b.ptr<uchar>(i);
for (int j = 0; j < b.cols*b.channels(); j++)
{
std::cout << (int)ptr[j] << " ";
}
std::cout << std::endl;
}
//當(dāng)讀取第2行數(shù)據(jù)中第3個(gè)數(shù)據(jù)時(shí), 可以用b.ptr<uchar>(1)[2]的形式來(lái)直接訪問.
cv::MatIterator_<cv::Vec3b> it = b.begin<cv::Vec3b>();//mat是什么類型,就要用什么類型的iterator接受指針,不然會(huì)報(bào)錯(cuò)
cv::MatIterator_<cv::Vec3b> it_end = b.end<cv::Vec3b>();
for (int i = 0; it != it_end; it++)
{
std::cout << *it << " ";//可以直接輸出vec3b類型的值,,,*it
if ((++i % b.cols) == 0)
{
std::cout << std::endl;
}
}
std::cout << (int)(*(b.data + b.step[0] * 1 + b.step[1] * 1 + 1)) << std::endl;//https://blog.csdn.net/baoxiao7872/article/details/80210021講解了data和step的用法
image.at<uchar>(i, j)
其中有一個(gè)要注意的地方是i對(duì)應(yīng)的是點(diǎn)的y坐標(biāo),j對(duì)應(yīng)的是點(diǎn)的x坐標(biāo),而不是我們習(xí)慣的(x,y)
2.2 圖像的讀取與顯示
cv::Mat cv::imread(const string & filename, int flags = IMREAD_COLOR)//這些標(biāo)志參數(shù)在功能不沖突的前提下可以同時(shí)聲明多個(gè),不同參數(shù)之間用" 1 " 隔開。
//在默認(rèn)情況下,讀取圖像的像章數(shù)目必須小于2^30,可以通過修改革統(tǒng)變量中的OPENCV 10 MAX IMAGE: PIXELS 參數(shù)調(diào)整能夠讀取的最大像章數(shù)目.
//empty()函數(shù)是否為真來(lái)判斷是否成功讀取圖像,如果讀取圖像失敗,那么data 屬性返回值為0. emptyO函數(shù)返回值為1
void cv::namedWindow(const String & winname, int flags = WINDOW_AUTOSIZE)//namedWindow()窗口函數(shù)屬性標(biāo)志餐數(shù)
void cv::imshow(const string & winname, InputArray mat)//如果后面程序執(zhí)行完直接退出,那么顯;示的圖像有可能閃一下就消失,因此在需要顯示圖像的程序中,往往會(huì)在imshow()函數(shù)后跟有cv::waitKey()函數(shù),用于將程序暫停一段時(shí)間.cv::waitKey()函數(shù)是以毫秒計(jì)的等待時(shí)長(zhǎng),如果參數(shù)默認(rèn)或者為"0" ,那么表示等待用戶按鍵結(jié)束該函數(shù).
2.3 視頻加載與攝像頭調(diào)用
//這是打開視頻
cv::VideoCapture::VideoCapture();
cv::VideoCapture::VideoCapture(const String & filename, int apiPreference = CAP_ANY);
//需要通過自O(shè)pened()函數(shù)進(jìn)行判斷.如果讀取成功, 則返回值為true; 如果讀取失敗,則返回值為false .
//這事調(diào)用攝像頭
cv::VideoCapture::VideoCapture(int index, int apiPreference = CAP_ANY);//攝像頭號(hào)從0開始
2.4 數(shù)據(jù)保存
bool cv::imwrite(const String & filename, InputArray img, const std::vector<int>& params = std::vectro<int>());
cv::VideoWriter::VideoWriter(const String & filename, int fourcc, double fps, Size frameSize, bool isColor=true);
cv::FileStroage::FileStorage();//需要用open()函數(shù)操作
cv::FileStroage::FileStorage(const String & filename, int flags, const String & encoding = String());
cv::FielStorage::open(const String & filename, int flags, const String & encoding = String());
cv::FielStorage::write(const String & name, int val);//存在多個(gè)重載,可以寫多種類型的變量值
#include <opencv2/opencv.hpp>
#include <iostream>
#include <string>
using namespace std;
using namespace cv;
int main(int argc, char** argv)
{
system("color F0"); //修改運(yùn)行程序背景和文字顏色
//string fileName = "datas.xml"; //文件的名稱
string fileName = "datas.yaml"; //文件的名稱
//以寫入的模式打開文件
cv::FileStorage fwrite(fileName, cv::FileStorage::WRITE);
//存入矩陣Mat類型的數(shù)據(jù)
Mat mat = Mat::eye(3, 3, CV_8U);
fwrite.write("mat", mat); //使用write()函數(shù)寫入數(shù)據(jù)
//存入浮點(diǎn)型數(shù)據(jù),節(jié)點(diǎn)名稱為x
float x = 100;
fwrite << "x" << x;
//存入字符串型數(shù)據(jù),節(jié)點(diǎn)名稱為str
String str = "Learn OpenCV 4";
fwrite << "str" << str;
//存入數(shù)組,節(jié)點(diǎn)名稱為number_array
fwrite << "number_array" << "[" <<4<<5<<6<< "]";
//存入多node節(jié)點(diǎn)數(shù)據(jù),主名稱為multi_nodes
fwrite << "multi_nodes" << "{" << "month" << 8 << "day" << 28 << "year"
<< 2019 << "time" << "[" << 0 << 1 << 2 << 3 << "]" << "}";
//關(guān)閉文件
fwrite.release();
//以讀取的模式打開文件
cv::FileStorage fread(fileName, cv::FileStorage::READ);
//判斷是否成功打開文件
if (!fread.isOpened())
{
cout << "打開文件失敗,請(qǐng)確認(rèn)文件名稱是否正確!" << endl;
return -1;
}
//讀取文件中的數(shù)據(jù)
float xRead;
fread["x"] >> xRead; //讀取浮點(diǎn)型數(shù)據(jù)
cout << "x=" << xRead << endl;
//讀取字符串?dāng)?shù)據(jù)
string strRead;
fread["str"] >> strRead;
cout << "str=" << strRead << endl;
//讀取含多個(gè)數(shù)據(jù)的number_array節(jié)點(diǎn)
FileNode fileNode = fread["number_array"];
cout << "number_array=[";
//循環(huán)遍歷每個(gè)數(shù)據(jù)
for (FileNodeIterator i = fileNode.begin(); i != fileNode.end(); i++)
{
float a;
*i >> a;
cout << a<<" ";
}
cout << "]" << endl;
//讀取Mat類型數(shù)據(jù)
Mat matRead;
fread["mat="] >> matRead;
cout << "mat=" << mat << endl;
//讀取含有多個(gè)子節(jié)點(diǎn)的節(jié)點(diǎn)數(shù)據(jù),不使用FileNode和迭代器進(jìn)行讀取
FileNode fileNode1 = fread["multi_nodes"];
int month = (int)fileNode1["month"];
int day = (int)fileNode1["day"];
int year = (int)fileNode1["year"];
cout << "multi_nodes:" << endl
<< " month=" << month << " day=" << day << " year=" << year;
cout << " time=[";
for (int i = 0; i < 4; i++)
{
int a = (int)fileNode1["time"][i];
cout << a << " ";
}
cout << "]" << endl;
cout << " time=[";
for (FileNodeIterator i = fileNode1["time"].begin(); i != fileNode1["time"].end(); i++)
{
float a;
*i >> a;
cout << a << " ";
}
cout << "]" << endl;//兩種讀取方法都行,一種使用迭代器,一種使用地址。
system("pause");
//關(guān)閉文件
fread.release();
return 0;
}
第3章 圖像基本操作
3.1 圖像顏色空間
3.1.1 顏色模型與轉(zhuǎn)換——converTo()
RGB模型再OpenCV中是BGR的順序。
RGB模型增加透明度就是RGBA模型
HSV、YUV、Lab、GRAY
void cv::Mat::convertTo(OutputArray m, int rtype, double alpha =1, double beta = 0);//rtype轉(zhuǎn)換圖像的數(shù)據(jù)類型,alpha轉(zhuǎn)換過程中的縮放因子,beta轉(zhuǎn)換過程中的偏置因子
alpha和beta兩個(gè)餐數(shù)用于聲明兩個(gè)數(shù)據(jù)類型間的轉(zhuǎn)換關(guān)系,形式如m(x,y)=saturate_cast(α(*this)(x,y)+β),該轉(zhuǎn)換方式就是將原有數(shù)據(jù)進(jìn)行線性轉(zhuǎn)換
3.1.2 多通道分離與合并——split(), merge()
//多通道分離
void cv::split(const Mat & src, Mat * mvbegin);//需要定義mvbegin的長(zhǎng)度
void cv::split(InputArray m, OutputArrayofArrays mv);//mv是vector形式,不需要提前定義數(shù)組長(zhǎng)度
//多通道融合
void cv::merge(const Mat *mv, size_t count, OutputArray dst);//mv需要合并的圖像數(shù)組,每個(gè)圖像必須擁有相同的尺寸和數(shù)據(jù)類型.count輸入的圖像數(shù)組長(zhǎng)度
void cv::merge(InputArrayofArrays mv, OutputArray dst);//mv需要合并的圖像向量vector,每個(gè)圖像必須擁有相同的尺寸和數(shù)據(jù)類型
//多通道融合可以超過4個(gè)通道,image watch上每個(gè)像素點(diǎn)內(nèi)不顯示4之后的通道值,但是上面會(huì)顯示每個(gè)通道的值
下面這個(gè)圖是把1個(gè)lena的img拆分了三個(gè)通道,分別放在一個(gè)mat[6]內(nèi),使用merge合并出來(lái)的6通道圖片
3.2 圖像像素操作處理
3.2.1 圖像像素統(tǒng)計(jì)
1.尋找像素最大值與最小值——minMaxLoc()
void cv::minMaxLoc(InputArray src,
double * minVal,
double * maxVal = 0,
Point * minLoc = 0,
Point * maxLoc =0,
InputArray mask = noArray());//src輸入的單通道矩陣
數(shù)據(jù)類型Point用于表示圖像的像素坐標(biāo),
圖像的像素坐標(biāo)軸以左上角為坐標(biāo)原點(diǎn), 水平方向?yàn)閤 軸, 垂直方向?yàn)閥 軸
Point(x.y)對(duì)應(yīng)于圖像的行和列表示為Point (列數(shù),行數(shù))
在OpenCV 中對(duì)于二維坐標(biāo)和三維坐標(biāo)都設(shè)置了多種數(shù)據(jù)類型,針對(duì)二維坐標(biāo)數(shù)據(jù)類型, 定義了整型坐標(biāo)cv::Point2i (或者cv: :Point ) 、double 型坐標(biāo)cv : :Point2d、浮點(diǎn)型坐標(biāo)cv: :Point2f. 對(duì)于三維坐標(biāo),同樣定義了上述的坐標(biāo)數(shù)據(jù)類型,只需要將其中的數(shù)字" 2" 變成"3 氣對(duì)于坐標(biāo)中x、y、z 軸的具體數(shù)據(jù), 可以通過變量的x、y、z 屬性進(jìn)行訪問, 例如Point.x可以讀取坐標(biāo)的x 軸數(shù)據(jù).
尋找多通道矩陣需要使用cv::Mat::reshape()將多通道變成單通道
cv::Mat MatA_reshape = MatA.reshape(int cn, int rows = 0)//cn指通道數(shù),rows指轉(zhuǎn)換后矩陣的行數(shù),默認(rèn)0與轉(zhuǎn)換前相同
[外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機(jī)制,建議將圖片保存下來(lái)直接上傳(img-SPdtTvkr-1666660001256)(https://gitee.com/lyz-npu/typora-image/raw/master/img/%E5%9B%BE3-7.png)]
2.計(jì)算圖像的平均值和標(biāo)準(zhǔn)差——mean, meanStdDev
圖像的平均值表示圖像整體的亮暗程度, 圖像的平均值越大,則圖像整體越亮。
標(biāo)準(zhǔn)差表示圖像中明暗變化的對(duì)比程度, 標(biāo)準(zhǔn)差越大, 表示圖像中明暗變化越明顯。
//求平均值
cv::Scalar cv::mean(InputArray src, InputArray mask = noArray())//用scalar結(jié)構(gòu)接收
//求平均值和標(biāo)準(zhǔn)差
void cv::meanStdDev(InputArray src, OutputArray mean, OutputArray stddev, InputArray mask = noArray())//用mat結(jié)構(gòu)接收
3.2.2 圖像間的像素操作
1.兩幅圖像的比較運(yùn)算——max(), min()
void cv::max(InputArray src1,InputArray src2,OutputArray dst)
void cv::min(InputArray src1,InputArray src2,OutputArray dst)
這種比較運(yùn)算主要用在對(duì)矩陣類型數(shù)據(jù)的處理上. 與掩模圖像進(jìn)行比較運(yùn)算可以實(shí)現(xiàn)摳圖或者選擇通道的效果.
//對(duì)兩張彩色圖像進(jìn)行比較運(yùn)算
Mat img0 = imread("E:/graduate/learn/OpenCV/《OpenCV 4快速入門》數(shù)據(jù)集/data/lena.png");
Mat img1 = imread("E:/graduate/learn/OpenCV/《OpenCV 4快速入門》數(shù)據(jù)集/data/noobcv.jpg");
if (img0.empty() || img1.empty())
{
cout << "請(qǐng)確認(rèn)圖像文件名稱是否正確" << endl;
return -1;
}
Mat comMin, comMax;
max(img0, img1, comMax);
min(img0, img1, comMin);
imshow("comMin", comMin);
imshow("comMax", comMax);
//與掩模進(jìn)行比較運(yùn)算
Mat src1 = Mat::zeros(Size(512, 512), CV_8UC3);//生成一個(gè)512*512的0矩陣
Rect rect(100, 100, 300, 300);//創(chuàng)建一個(gè)坐標(biāo)點(diǎn)(100,100)開始的300*300的矩形
src1(rect) = Scalar(255, 255, 255); //生成一個(gè)低通300*300的掩模,,,,把rect矩形疊加進(jìn)src1矩陣中,rect中的值用scalar修改為255,255,255
Mat comsrc1, comsrc2;
min(img0, src1, comsrc1);
imshow("comsrc1", comsrc1);
Mat src2 = Mat(512, 512, CV_8UC3, Scalar(0, 0, 255)); //生成一個(gè)顯示紅色通道的低通掩模
min(img0, src2, comsrc2);
imshow("comsrc2", comsrc2);
[外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機(jī)制,建議將圖片保存下來(lái)直接上傳(img-UUdc5njU-1666660001256)(https://gitee.com/lyz-npu/typora-image/raw/master/img/%E5%9B%BE3-11.png)]
用黑框圖片作為一個(gè)低通掩膜和lena圖片求min(),就可以把外面的值剪掉。紅色掩膜同理
2.兩幅圖片的邏輯運(yùn)算——bitwise_()
//邏輯與
void cv::bitwise_and(InputArray src1, Input Array src2, OutputArray dst, InputArray mask = noArray())
//邏輯與
void cv::bitwise_or(InputArray src1, Input Array src2, OutputArray dst, InputArray mask = noArray())
//邏輯異或
void cv::bitwise_xor(InputArray src1, Input Array src2, OutputArray dst, InputArray mask = noArray())
//邏輯非
void cv::bitwise_not(InputArray src, OutputArray dst, InputArray mask = noArray())
3.2.3 圖像二值化——threshold()
double cv::threshold(InputArray src,
OutputArray dst,
double thresh,
double maxval,
int type
)
//thresh二值化的閾值
//maxval二值化過程的最大值,只有THRESH_BINARY和THRESH_BINARY_INV兩種二值化方法才使用,
//type二值化方法
THRESH_OTSU和THRESH_TRIANGLE這兩種標(biāo)志是獲取閾值的方法,并不是閾值比較方法的標(biāo)志, 這兩個(gè)標(biāo)志可以與前面5 種標(biāo)志一起使用, 例如" THRESH_BINARY | THRESH_OTSU" . 前面5 種標(biāo)志在調(diào)用函數(shù)時(shí)都需要人為地設(shè)置閾值,如果對(duì)圖像不了解,設(shè)置的閾值不合理, 就會(huì)對(duì)處理后的效果造成嚴(yán)重的影響.這兩個(gè)標(biāo)志分別表示利用大津法(OTSU)和三角形法(TRlANGLE)結(jié)合國(guó)像灰度值分布特性獲取二值化的閾值,并將閾值以函數(shù)返回值的形式給出.因此,如果該函數(shù)最后一個(gè)參數(shù)設(shè)置了這兩個(gè)標(biāo)志中的任何一個(gè),那么該函數(shù)第三個(gè)參數(shù)thresh將由系統(tǒng)自動(dòng)給出,但是在調(diào)用函數(shù)時(shí)仍然不能默認(rèn),只是程序不會(huì)使用這個(gè)數(shù)值.需要注意的是,到目前為止. OpenCV 4 中針對(duì)這兩個(gè)標(biāo)志只支持輸入CV_8UC1類型的圖像.
大津法——最大類間方差法:https://blog.csdn.net/yxswhy/article/details/77838622?locationNum=10&fps=1
三角形法——適用于單峰直方圖:https://blog.csdn.net/qq_45769063/article/details/107102117
由threshold()函數(shù)全局只使用一個(gè)閾值,在實(shí)際情況中, 由于光照不均勻以及陰影的存在, 全局只有一個(gè)閾值會(huì)使得在陰影處的白色區(qū)域也會(huì)被函數(shù)二值化成黑色, 因此adaptiveThreshold() 函數(shù)提供了兩種局部自適應(yīng)閾值的二值化方法,基本的思路就是以目標(biāo)像素點(diǎn)為中心選擇一個(gè)塊,然后對(duì)塊區(qū)域里面的像素點(diǎn)進(jìn)行高斯或者均值計(jì)算,將得到的平均值或者高斯值作為目標(biāo)像素點(diǎn)的閾值,以此來(lái)對(duì)目標(biāo)像素格進(jìn)行二值化。對(duì)圖像每一個(gè)像素格進(jìn)行如此操作就完成了對(duì)整個(gè)圖像的二值化處理。
void cv::adaptiveThreshold(InputArray src,
OutputArray dst,
double maxValue,
int adaptiveMehtod,
int threshType,
int blockSize,
double C
)
//maxValue二值化的最大值
//adaptiveMethod自適應(yīng)確定閾值的方法,均值法ADAPTIVE_THRESH_MEAN_C和高斯法ADAPTIVE_GAUSSIAN_MEAN_C兩種.
//thresholdType選擇圖像二值化的方法,只能是THRESH_BINARY和THRESH_BINARY_INV兩種二值化方法
//blockSize自適應(yīng)確定閾值的像素鄰域大小,一般為3,5,7的奇數(shù)
//從平均值或者加權(quán)平均值中減去的常數(shù)
3.2.4 查找表——LUT()
查找表,Look-Up-Table
void cv::LUT(InputArray src,//只能輸入CV_8U類型
InputArray lut,//256 個(gè)像素灰度值的查找表,單通道或者與src 通道數(shù)相同.
OutputArray dst//輸出圖像矩陣,尺寸與src 相同,數(shù)據(jù)類型與lut 相同.
)
函數(shù)輸出圖像的數(shù)據(jù)類型與LUT的數(shù)據(jù)類型保持一致,不與原圖像保持一致。
如果第二個(gè)參數(shù)是單通道,則輸入變量中的每個(gè)通道都按照一個(gè)LUT進(jìn)行映射,如果第二個(gè)參數(shù)是多通道,則輸入變量中的第i個(gè)通道按照第二個(gè)參數(shù)的第i個(gè)通道LUT進(jìn)行映射.
3.4 圖像變換
3.3.1 圖像連接——cv::vconcat, cv::hconcat
void cv::vconcat(const Mat * src, size_t nsrc, OutputArray dst)//src是Mat矩陣類型的數(shù)組,nsrc數(shù)組中Mat類型數(shù)據(jù)的數(shù)目
void cv::vconcat(InputArray src1, InputArray src2, OutputArray dst)
vconcat是縱向連接**,兩者需要具有相同的寬度、數(shù)據(jù)類型及通道數(shù)**
void cv::hconcat(const Mat * src, size_t nsrc, OutputArray dst)//src是Mat矩陣類型的數(shù)組,nsrc數(shù)組中Mat類型數(shù)據(jù)的數(shù)目
void cv::hconcat(InputArray src1, InputArray src2, OutputArray dst)
hconcat是縱向連接**,兩者需要具有相同的高度、數(shù)據(jù)類型及通道數(shù)**
3.3.2 圖像尺寸變換——resize()
void cv::resize(InputArray src,
OutputArray dst,
Size dsize, //輸出圖像的尺寸
double fx = 0,//水平軸的比例因子
double fy = 0,//垂直軸的比例因子
int interpolation = INTER_LINEAR//插值方法的標(biāo)志
)
該函數(shù)的dsize 和fx ( fy ) 同時(shí)可以調(diào)整輸出圖像的參數(shù),因此兩類參數(shù)在實(shí)際使用時(shí)只需要使用一類,當(dāng)根據(jù)兩個(gè)參數(shù)計(jì)算出來(lái)的輸出圖像尺寸不一致時(shí),以dsize 設(shè)置的圖像尺寸為準(zhǔn).
dsize=Size(round(fx*src.cols), round(fy*src.rows))
縮小圖像,通常使用INTER_AREA標(biāo)志會(huì)有較好的效果
放大圖像,采用INTER_CUBIC和INTER_LINEAR標(biāo)志通常會(huì)有比較好的效果
標(biāo)志參數(shù) | 簡(jiǎn)記 | 作用 |
---|---|---|
INTER_NEAREAST | 0 | 最近鄰插值法 |
INTER_LINEAR | 1 | 雙線性插值法 |
INTER_CUBIC | 2 | 雙三次插值 |
INTER_AREA | 3 | 使用像素區(qū)域關(guān)系重新采樣,首選用于圖像縮小,圖像放大時(shí)效果與INTER_NEAREST相似 |
INTER_LANCZOS4 | 4 | Lanczos插值法 |
INTER_LINEAR_EXACT | 5 | 位精確雙線性插值法 |
INTER_MAX | 7 | 用掩碼進(jìn)行插值 |
最鄰近插值法、雙線性插值:https://www.cnblogs.com/wanghui-garcia/p/11171954.html
雙三次插值:https://blog.csdn.net/u013185349/article/details/84529982?
3.3.3 圖像翻轉(zhuǎn)——flip()
void cv::flip(InputArray src,
OutputArray dst,
int flipCode// >0,繞y軸翻轉(zhuǎn);
// =O,繞x軸翻轉(zhuǎn);
// <O,繞x,y軸翻轉(zhuǎn)
)
3.3.4 圖像仿射變換——getRotationMatrix2D(), warpAffine(), getAffineTransform()
OpenCV4 提供了getRotationMatrix2D()函數(shù)用于計(jì)算旋轉(zhuǎn)矩陣, 提供了warpAffine()函數(shù)用于實(shí)現(xiàn)圖像的仿射變換.
Mat cv::getRotationMatrix2D(Point2f center,
double angle,
double scale)//獲取旋轉(zhuǎn)矩陣
確定了旋轉(zhuǎn)矩陣后,通過warpAffine()函數(shù)進(jìn)行放射變換可以實(shí)現(xiàn)矩陣的旋轉(zhuǎn)。
void cv::warpAffine(InputArray src,
OutputArray dst,
InputArray M, //2*3的變換矩陣
Size dsize, //輸出圖像的尺寸
int flags = INTER_LEANER, //插值方法
int borderMode = BORDER_CONSTANT, //像素邊界外推方法的標(biāo)志
const Scalar & borderValue = Scalar()//填充邊界使用的數(shù)值, 默認(rèn)情況下為0.
)
相比于圖像尺寸變換增加了兩個(gè)插值方法,可以與其他插值方法一起用
仿射變換又稱為三點(diǎn)變換
Mat cv::getAffineTransform(const Point2f src[], const Point2f dst[]);//利用3個(gè)對(duì)應(yīng)點(diǎn)倆去欸的那個(gè)變換矩陣M
3.3.5 圖像透視變換——getPerspectiveTransform(), warpPerspective()
透視變換又叫做四點(diǎn)變換。
Mat cv::getPerspectiveTransform(const Point2f src[], //四個(gè)像素坐標(biāo)
const Point2f dst[]
int solveMethod = DECOMP_LU//計(jì)算透視變換矩陣方法的標(biāo)志,默認(rèn)選擇最佳主軸元素的高斯消元法DECOMP_LU
);
void cv::warpPerspective(InputArray src,
OutputArray dst,
InputArray M, //
Size dsize, //輸出圖像的尺寸
int flags = INTER_LEANER, //插值方法
int borderMode = BORDER_CONSTANT, //像素邊界外推方法的標(biāo)志
const Scalar & borderValue = Scalar()//填充邊界使用的數(shù)值, 默認(rèn)情況下為0.
)
3.3.6 極坐標(biāo)變換——warpPolar()
極坐標(biāo)變換就是將圖像在直角坐標(biāo)系與極坐標(biāo)系中互相變換,
void cv::warpPolar(InputArray src,
OutputArray dst,
Size dsize, //輸出圖像的尺寸
Point2f center,
double maxRadius,
int flags//插值方法與極坐標(biāo)映射方法標(biāo)志, 兩個(gè)方法之間通過"+"或者'"|" 號(hào)連接.
)
3.4 在圖像上繪制幾何圖形
繪制圓形
繪制直線
繪制橢圓
繪制橢圓多邊形
繪制多邊形
3.5 感興趣區(qū)域(ROI), 深拷貝與淺拷貝
Rect_(_Tp _x, _Tp _y, _Tp _width, _Tp _height)
cv::Range(int start, int end)
在img中截取圖像
img(Rect(p.x, p.y, width, height))
img(Range(rows_start, rows_end), Range(cols_start,cols_end))
OpenCV中提到的圖像截取以及通過=賦值,都是淺拷貝
深拷貝方式, OpenCV 4 通過**copyTo()**函數(shù)實(shí)現(xiàn)兩類方法
void cv::Mat::copyTo(OutputArray m)const
void cv::Mat::copyTo(OutputArray m, InputArray mask)const//掩膜矩陣只能是CV_8U,尺寸相同,但是對(duì)通道數(shù)沒有要求
//當(dāng)掩模矩陣中某一位置不為0 時(shí),表示復(fù)制原圖像中相同位置的元素到新的圖像中,
void cv::copyTo(InputArray src, OutputArray dst, InputArray mask)
3.6 圖像金字塔
https://blog.csdn.net/zhu_hongji/article/details/81536820
圖像"金字塔"的底部是待處理圖像的高分辨率表示, 頂部是低分辨率的表示.
圖像金字塔在特征檢測(cè)中時(shí)基礎(chǔ)理論和技術(shù)
高斯金字塔 ( Gaussian pyramid): 用來(lái)向下/降采樣,主要的圖像金字塔
拉普拉斯金字塔(Laplacian pyramid): 用來(lái)從金字塔低層圖像重建上層未采樣圖像,在數(shù)字圖像處理中也即是預(yù)測(cè)殘差,可以對(duì)圖像進(jìn)行最大程度的還原,配合高斯金字塔一起使用。
這里的向下與向上采樣,是對(duì)圖像的尺寸而言的(和金字塔的方向相反),**向上就是圖像尺寸加倍,向下就是圖像尺寸減半。**而如果我們按上圖中演示的金字塔方向來(lái)理解,金字塔向上圖像其實(shí)在縮小,這樣剛好是反過來(lái)了。
3.6.1 高斯“金字塔”——解決尺度不確定性
尺度:通俗理解,一格圖像中包含的內(nèi)容,,,縮小的 圖像,一格中包含的信息多,是大尺度;一格中包含的信息少,是小尺度。
[外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機(jī)制,建議將圖片保存下來(lái)直接上傳(img-9V1hBRqL-1666660001260)(https://gitee.com/lyz-npu/typora-image/raw/master/img/%E5%9B%BE3-30.png)]
⑴對(duì)圖像的向下取樣
為了獲取層級(jí)為 G_i+1 的金字塔圖像,我們采用如下方法:
**<1>**對(duì)圖像G_i進(jìn)行高斯內(nèi)核卷積
**<2>**將所有偶數(shù)行和列去除
每往上一層就會(huì)通過下來(lái)樣縮小一次圖像的尺寸, 通常情況下,尺寸會(huì)縮小為原來(lái)的一半
OpenCV 4 中提供了pyrDown() 函數(shù)專門用于圖像的下來(lái)樣計(jì)算
卷積:
https://www.cnblogs.com/geeksongs/p/11132692.html——講的比較易懂
https://www.cnblogs.com/skyofbitbit/p/4471675.html——講的比較仔細(xì)
//向下采樣
void cv::pyrDown(InputArray src,
OutputArray dst,
const Size & dstsize = Size(),
int borderType = BORDER_DEFAULT
)
高斯圖像“金字塔”用到了卷積核
高斯分布https://blog.csdn.net/weixin_39124778/article/details/78411314
//向上采樣
void cv::pyrUp(InputArray src,
OutputArray dst,
const Size & dstsize = Size(),
int borderType = BORDER_DEFAULT
)
2.拉普拉斯金字塔
拉普拉斯金字塔是向上采樣,重建上層未采樣圖像,在圖像處理中預(yù)測(cè)殘差。得到的圖像比原來(lái)圖像的尺寸大。向上采樣如下:
(1)將圖像在每個(gè)方向上擴(kuò)大為原來(lái)的兩倍,新增的行和列以0填充
(2)使用先前同樣的內(nèi)核(乘以4)與放大后的圖像卷積,或得”新增像素”的近似值
乘4是因?yàn)橛迷瓉?lái)的一個(gè)像素高斯卷積核操作了新加的三個(gè)0像素,,,總共四個(gè)像素塊,,用一個(gè)像素操作總共四個(gè)像素
3.7 窗口操作
3.7.1 窗口交互操作
int cv::createTracker(const String & trackbarname,//滑動(dòng)條名
const String & winname,//滑動(dòng)條窗口名
int * value,//指向整數(shù)變量的指針
int count,//最大值
TrackerbarCallback onChange = 0,//每次滑塊位置更改時(shí)調(diào)用的函數(shù)指針,如果回調(diào)時(shí)NULL指針,不會(huì)調(diào)用任何回調(diào),只跟新數(shù)值
void * userdata = 0//傳給回調(diào)函數(shù)的可選參數(shù)
)
回調(diào)函數(shù)是什么
https://www.zhihu.com/question/19801131
3.7.2 鼠標(biāo)響應(yīng)
void cv::setMouseCallback(const String & winname,
MouseCallback onMouse,//鼠標(biāo)響應(yīng)的回調(diào)函數(shù)
void * userdata = 0
)
typedef void(* cv::MouseCallback)(int event,//鼠標(biāo)響應(yīng)事件標(biāo)志,參數(shù)為EVENT*_
int x,
int y,
int flags,
void *userdata
)
if (event == EVENT_MOUSEMOVE && (flags & EVENT_FLAG_LBUTTON))
// 提取flags的CV_EVENT_FLAG_LBUTTON 標(biāo)志位
第4章 圖像直方圖與模板匹配
4.1 圖像直方圖的繪制(一維)
同一物體無(wú)論是旋轉(zhuǎn)還是平移,在圖像中都具有相同的灰度值,因此直方因具有平移不變性、放縮不變性等優(yōu)點(diǎn)
圖像直方閣就是統(tǒng)計(jì)圖像中每個(gè)灰度值的個(gè)數(shù),之后將圖像灰度值作為橫軸,以灰度值個(gè)數(shù)或者灰度值所占比率作為縱軸繪制的統(tǒng)計(jì)圖.
void cv::calcHist(const Mat * images,//數(shù)組中所有的圖像應(yīng)具有相同的尺寸和數(shù)據(jù)類型,數(shù)據(jù)類型只能是CV_8U 、CV_16U和CV_32F這3種中的一種
int nimages,
const int * channels,
InputArray mask,
OutputArray hist,
int dims,//需要計(jì)算直方圖的維度,必須是整數(shù),并且不能大于CV_MAX_DIMS = 32
const int * histSize,
const float ** ranges,
bool uniform = true,
bool accumulate = false
)
calcHist可以生成二維直方圖
https://www.bilibili.com/video/BV1i54y1m7tw?p=26
4.2 直方圖操作
4.2.1 直方圖歸一化——normalize()
另一種常用的歸一化方式是尋找統(tǒng)計(jì)結(jié)果中的最大數(shù)值,把所有結(jié)果除以這個(gè)最大的數(shù)值,以實(shí)現(xiàn)將所有數(shù)據(jù)歸一化為0~1 .
如果不進(jìn)行歸一化,每一個(gè)灰度值下的像素?cái)?shù)目統(tǒng)計(jì)會(huì)隨著圖像尺寸的變大而變化
void cv::normalize(InputArray src,
OutputArray dst,
double alpha = 1,//歸一化下邊界的標(biāo)準(zhǔn)值
double beta =0,//歸一化的上線范圍
int norm_type = NORM_L2,//歸一化過程中數(shù)據(jù)范數(shù)種類標(biāo)志
in dtype=-1,//輸出數(shù)據(jù)類型選擇標(biāo)志,負(fù)數(shù)為輸出數(shù)據(jù)與src擁有相同類型,否則與src擁有相同的通道數(shù)
InputArray mask = noArray()
)
偏移歸一化:
數(shù)組的數(shù)值被平移或縮放到一個(gè)指定的范圍,線性歸一化。
4.2.2 直方圖比較——compareHist()
double cv::compareHist(InputArray H1,InputArray H2,int method)//method比較方法標(biāo)志
通過不同的方法計(jì)算相關(guān)系數(shù),代表的含義不同
4.3 直方圖應(yīng)用
4.3.1 直方圖均衡化——equalizeHist()
通過映射關(guān)系,將圖像中灰度值的范圍擴(kuò)大,增加原來(lái)兩個(gè)灰度值之間的差值,就可以提高圖像的對(duì)比度,進(jìn)而將圖像中的紋理突出顯現(xiàn)出來(lái),這個(gè)過程稱為圖像直方圖均衡化.
對(duì)原圖直方圖進(jìn)行均衡化之后,雖然不是很平坦,但是比原圖直方圖平坦(每個(gè)顏色出現(xiàn)的次數(shù)相等)的多;擴(kuò)展了動(dòng)態(tài)范圍,以前很窄,現(xiàn)在把其拉開;對(duì)于對(duì)比度出現(xiàn)很暗或者很亮的地方,沒有什么對(duì)比度或集中在一塊;通過均衡化一下就能把圖像給拉開;
https://blog.csdn.net/schwein_van/article/details/84336633直方圖均衡化操作
用r的累積分布函數(shù)作為變換函數(shù),可產(chǎn)生一幅灰度級(jí)分布均勻概率密度的圖像
void cv::equalizeHist(InputArray src,OutputArray dst)//但是該函數(shù)不能指定均衡化后的直方圖分布形式.
4.3.2 直方圖匹配(直方圖規(guī)定化)
將直方圖映射成指定分布形式的算法
直方圖匹配操作能夠有目的地增強(qiáng)某個(gè)灰度區(qū)間, 相比于直方圖均衡化操作, 該算法雖然多了一個(gè)輸入, 但是變換后
的結(jié)呆也更靈活.
直方圖規(guī)定化是在直方圖均衡化的基礎(chǔ)上完成的(從結(jié)果來(lái)看跳過了圖像均衡化的過程)
https://blog.csdn.net/superjunenaruto/article/details/80037777
按照累積概率插值最小原則進(jìn)行映射,,,,例如原直方圖5像素0.94累積概率到目標(biāo)直方圖的6像素0.84的距離大于到目標(biāo)直方圖7像素1的距離,因此原直方圖5像素映射到目標(biāo)直方圖7像素的位置。
4.3.3 直方圖反向投影——calcBackProject()
void cv::calcBackProject(const Mat * images,
int nimages,
const int * channels,
InputArray hist,
OutputArray backProject,
const float ** ranges,
double scale =1,
bool uniform = true
)
void cv::applyColorMap(InputArray src, OutputArray dst, int COLORMAP_*);
4.3.4 限制對(duì)比度的自適應(yīng)直方圖均衡化——CLANE()
直方圖與增強(qiáng)對(duì)比度 - 程序員阿德的文章 - 知乎 https://zhuanlan.zhihu.com/p/98541241
4.4 圖像的模板匹配
void cv::matchTemplate(InputArray image,
OutputArray templ,
OutputArray result,
int method, //模板匹配方法
InputArray mask = noArray())
第5章 圖像濾波
5.1 圖像卷積
https://www.cnblogs.com/geeksongs/p/11132692.html
圖像卷積操作常將卷積模板通過縮放使得所有數(shù)值的和為1.進(jìn)而解決卷積后數(shù)值越界的情況發(fā)生,
void cv::filter2D(InputArray src,
OutputArray dst,
int ddepth, //輸入如下表所示的類型,默認(rèn)-1
InputArray kernel, //CV_32FC1類型的矩陣
Point anchor = Point(-1,-1),
double delta = 0,
int borderType = BORDER_DEFAULT)
需要用不同的卷積模板對(duì)不同的通道進(jìn)行卷積操作,就需要先使用split()函數(shù)將圖像多個(gè)通道分離之后單獨(dú)對(duì)每一個(gè)通道求取卷積運(yùn)算
filter2D()函數(shù)不會(huì)將卷積模板進(jìn)行旋轉(zhuǎn),如果卷積模板不對(duì)稱,需要講卷積模板旋轉(zhuǎn)180°在輸入給該函數(shù)
5.2 噪聲的種類與生成
圖像中常見的噪聲主要有4種,分別是高斯噪聲、椒鹽噪聲、泊松噪聲和乘性噪聲.
5.2.1 椒鹽噪聲
隨機(jī)數(shù)函數(shù)原型
int cvflann::rand()
double cvflann::rand_double(double high = 1.0,double low = 0)
int cvflann::rand_int(int high = RAND_MAX, int low = 0)//rand_max系統(tǒng)中最大為32767
OpenCV中沒有直接生成椒鹽噪聲的函數(shù)
椒鹽噪聲又稱作脈沖噪聲, 它會(huì)隨機(jī)改變圖像中的像素值,是由相機(jī)成像、圖像傳輸、解碼處理等過程產(chǎn)生的黑白相間的亮暗點(diǎn)噪聲
5.2.2 高斯噪聲
高斯噪聲,顧名思義是指服從高斯分布(正態(tài)分布)的一類噪聲,通常是因?yàn)椴涣颊彰骱透邷匾鸬膫鞲衅髟肼?。通常在RGB圖像中,顯現(xiàn)比較明顯。
區(qū)別于椒鹽噪聲隨機(jī)出現(xiàn)在圖像中的任意位置,高斯噪聲出現(xiàn)在圖像中的所有位置.
void cv::RNG::fill(InputOutArray mat,
int distType, //目前生成的隨機(jī)數(shù)支持均勻分布(RUNG::UNIFORM, 0)和高斯分布(RNG::NORMAL, 1).
InputArray a, //確定分布規(guī)律的參數(shù)。當(dāng)選擇均勻分布時(shí), 該參數(shù)表示均勻分布的最小下限, 當(dāng)選擇高斯分布時(shí), 該參數(shù)表示高斯分布的均值.
InputArray b, //確定分布規(guī)律的參數(shù)。當(dāng)選擇均勻分布時(shí), 該參數(shù)表示均勻分布的最大下限, 當(dāng)選擇高斯分布時(shí), 該參數(shù)表示高斯分布的標(biāo)準(zhǔn)差.
bool saturateRange = false)//預(yù)飽和標(biāo)志,僅用于均勻分布.
OpenCV 4 的RNG類,是一個(gè)非靜態(tài)成員函數(shù)
靜態(tài)函數(shù)可以直接通過 類::函數(shù) 中方式調(diào)用,不用通過對(duì)象來(lái)調(diào)用函數(shù),而非靜態(tài)函數(shù)必須通過對(duì)象來(lái)調(diào)用,這里面還涉及到實(shí)例化對(duì)象時(shí)候的內(nèi)存分配。
cv::RNG rng;
rng.fill(mat, RNG::NORMAL, 10, 20);
5.3 線性濾波
圖像濾波是指去除圖像中不重要的內(nèi)容而使關(guān)心的內(nèi)容表現(xiàn)得更加清晰的方法,例如去除圖像中的噪聲、提取某些信息等
將圖像濾波分為消除圖像噪聲的濾波和提取圖像中部分特征信息的濾波.
圖像濾波使用的濾波器允許通過的信號(hào)頻段,決定了濾波操作是去除噪聲(低通、高阻:去除圖像中的噪聲)還是提取特征信息(高通:對(duì)圖像邊緣信息提取、增強(qiáng)和圖像銳化的作用.).
在低通濾液中,模糊可以與濾波等價(jià),例如圖像高斯模糊和圖像高斯低通濾波是個(gè)概念.
卷積操作中的卷積模板在圖像濾波中稱為濾波筷扳、濾波器或者鄰域算子
5.3.1 均值濾波——blur()
求所有數(shù)據(jù)的平均值
void cv::blur(InputArray src,
OutputArray dst,
Size ksize, //卷積核尺寸
Point anchor = Point(-1,-1),
int borderType = BORDER_DEFAULT
)
5.3.2 方框?yàn)V波——boxFilter()
方框?yàn)V波可以選擇不歸一化,將所有像素值的和作為濾波結(jié)果,而不是所有像素值的平均值
boxFilter()默認(rèn)進(jìn)行歸一化,不考慮數(shù)據(jù)類型的情況下,與blur()濾波結(jié)果相同。
void cv::boxFilter(InputArray src,
OutputArray dst,
int ddepth,
Size ksize,
Poitn anchor = Point(-1,-1),
bool normalize = true, //默認(rèn)進(jìn)行歸一化
int borderType = BORDER_DEFAULT
)
sqrBoxFiler()函數(shù)實(shí)現(xiàn)對(duì)濾波器內(nèi)每個(gè)像數(shù)值的平方求和,多用來(lái)處理CV_32F類型的圖像數(shù)據(jù),歸一化后,變模糊的同時(shí)亮度也會(huì)變暗
該函數(shù)的歸一化一樣是除kernel的元素?cái)?shù)
void cv::sqrBoxFiler(InputArray src,
OutputArray dst,
int ddepth,
Size ksize,
Poitn anchor = Point(-1,-1),
bool normalize = true, //默認(rèn)進(jìn)行歸一化
int borderType = BORDER_DEFAULT
)
該函數(shù)的歸一化一樣是除kernel的元素?cái)?shù)
5.3.3 高斯濾波——GaussianBlur()
高斯平滑也用于計(jì)算機(jī)視覺算法中的預(yù)先處理階段,以增強(qiáng)圖像在不同比例大小下的圖像效果(參見尺度空間表示以及尺度空間實(shí)現(xiàn))
void cv::GaussianBlur(InputArray src, //任意的通道數(shù)目,但是數(shù)據(jù)類型必需為CV_8U、CV_16U、CV_16S、CV_32F或CV_64F .
OutputArray dst,
Size ksize,
double sigmaX,
double sigmaY = 0,
int borderType = BORDER_DEFAULT
)
建議將該函數(shù)的第三個(gè)、第四個(gè)、第五個(gè)參數(shù)都明確地給出.
高斯濾汲器的尺寸和標(biāo)準(zhǔn)偏差存在著一定的互相轉(zhuǎn)換關(guān)系.
Mat cv::getGaussianKernel(int ksize, double sigma, int ktype = CV_64F)
可以用該函數(shù)獲取一個(gè)方向的高斯濾波器,然后兩個(gè)方向的濾波器相乘得到一個(gè)k*k的二維濾波器
5.3.4 可分離濾波——sepFilter2D()
可分離性指的是先對(duì)x ( y ) 方向?yàn)V波, 再對(duì)y ( X ) 方向?yàn)V波的結(jié)果與將兩個(gè)方向的濾波器聯(lián)合后整體濾波的結(jié)果相同。兩個(gè)方向的濾波器的聯(lián)合就是將兩個(gè)方向的濾波器相乘,
void cv::sepFilter2D(InputArray src,
OutputArray dst,
int ddepth,
InputArray kernelX,
InputArray kernelY,
Point anchor = Point(-1,-1),
double delta = 0,
int borderType = BORDER_DEFAULT)
可以輸入兩個(gè)方向的濾波器,sepFilter2D不區(qū)分xy方向的濾波方向,但是filter2D需要區(qū)分
5.4 非線性濾波
常見的非線性濾波有中值濾液和雙邊濾波
5.4.1 中值濾波——medianBlur()
中值濾波不依賴于濾波器內(nèi)那些與典型值差別很大的值, 因此對(duì)斑點(diǎn)噪聲和椒鹽噪聲的處理具有較好的效果.
void cv::medianBlur(InputArray src, //可以是單通道、三逼迫和四通道(二通道和更多通道不行)
OutputArray dst,
int ksize)
5.4.2 雙邊濾波——bilateralFilter()
雙邊濾波對(duì)高頻率的波動(dòng)信號(hào)起到平滑的作用, 同時(shí)保留大幅值變化的信號(hào)波動(dòng), 進(jìn)而實(shí)現(xiàn)對(duì)保留圖像中邊緣信息的作用.
濾波器對(duì)邊緣附近的像素進(jìn)行濾波時(shí),距離邊緣較遠(yuǎn)的像素值不會(huì)對(duì)邊緣上的像索值影響太多,進(jìn)而保留邊緣的清晰性.
[外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機(jī)制,建議將圖片保存下來(lái)直接上傳(img-02ucXQ8J-1666660001265)(https://gitee.com/lyz-npu/typora-image/raw/master/img/%E5%9B%BE5-24.png)]
雙邊濾波采用了空間和灰度距離兩個(gè)高斯濾波的結(jié)合
?空間距離指當(dāng)前點(diǎn)與卷積核中心點(diǎn)的歐式距離,離中心點(diǎn)越近的點(diǎn)權(quán)重系數(shù)越大
?灰度距離指當(dāng)前點(diǎn)灰度與中心點(diǎn)灰度的差的絕對(duì)值,顏色越接近的點(diǎn)權(quán)重系數(shù)越大
void cv::bilateralFilter(InputArrray src,
OutputArray dst,
int d,
double sigmaColor, //顏色空間濾被器的標(biāo)準(zhǔn)差值.這個(gè)參數(shù)越大,表明該像素領(lǐng)域內(nèi)有越多的顏色被混合到一起, 產(chǎn)生較大的半相等顏色區(qū)域.
double sigmaSpace, //空間坐標(biāo)中濾波器的標(biāo)準(zhǔn)差值.這個(gè)參數(shù)越大,表明越遠(yuǎn)的像素會(huì)相互影響,從而使更大領(lǐng)域中有足夠相似的顏色獲取相同的顏色.
int borderType = BORDER_DEFAULT
)
5.5 圖像的邊緣檢測(cè)
5.5.1 邊緣檢測(cè)原理
得到的正數(shù)值表示像素值突然由低變高,得到的負(fù)數(shù)值表示像素值由高到低,這兩種都是圖像的邊緣,因此,為了在圖像中同時(shí)表示出這兩種邊緣信息, 需要將計(jì)算的結(jié)果求絕對(duì)值.
void cv::convertScaleAbs(InputArrray src,
OutputArray dst,
double alpha = 1, //縮放因子,默認(rèn)只求絕對(duì)值不縮放
double beta = 0//偏值
)
5.5.2 Sobel算子——Sobel()
https://blog.csdn.net/qq_32811489/article/details/90312421
Sobel算子還結(jié)合了高斯平滑濾波的思想,將濾波器尺寸由ksize x 1改進(jìn)為ksize x ksize. 提高了對(duì)平緩區(qū)域邊緣的響應(yīng),效果更好
是一種中心差分,對(duì)中間水平線和垂直線上賦予較高的權(quán)重
其中第一個(gè)式子是n等于2時(shí)展開式的高斯平滑算子,也就是二項(xiàng)式展開式的系數(shù),第二個(gè)式子表示差分。
最后得到的結(jié)果就是3階sobel邊緣檢測(cè)算子。
void cv::Sobel(InputArray src,
OutputArray dst,
int ddepth,
int dx, //x方向的差分階數(shù)
int dy, //y方向的差分階數(shù)
int ksize = 3, //sobel算子尺寸,,,任意一個(gè)方向的差分階數(shù)都需要小子算子的尺寸
double scale = 1,
double delta = 0, //偏值
int borderType = BORDRE_DEFAULT
)
5.5.3 Scharr算子——Scharr()
算子只有上面兩種
void cv::Scharr(InputArray src,
OutputArray dst,
int dx,
int dy,
double scale = 1,
double delta = 0,
int borderType = BORDER_DEFAULT
)
5.5.4 生成邊緣檢測(cè)濾波器——getDeriveKernels()
Scharr()和Sobel()函數(shù)就是通過調(diào)用getDeriveKernels()函數(shù)得到了他們使用的算子
void cv::getDeriveKernels(OutputArray kx,
OutputArray ky,
int dx,
int dy,
int ksize,
bool normalize = false,
int ktype = CV_32F
)
5.5.5 Laplacian算子——Laplacian()
Sobel算子和Scharr算子都需要提取x方向的邊緣和y方向的邊緣,然后將兩個(gè)方向的邊緣總和得到圖像的整體邊緣。
Laplacian算子具有個(gè)方向同性的特點(diǎn)
Laplacian算子對(duì)噪聲具有無(wú)法接受的敏感性
void cv::Laplacian(InputArray src,
OutputArray dst,
int ddepth,
int ksize = 1,
double scale = 1,
double delta = 0,
int borderType = BORDER_DEFAULT
)
5.5.6 Canny算子——Canny()
https://blog.csdn.net/likezhaobin/article/details/6892176
https://www.cnblogs.com/king-lps/p/8007134.html
該算法不容易受到噪聲的影響,能夠識(shí)別圖像中的弱邊緣和強(qiáng)邊緣,并結(jié)合強(qiáng)弱邊緣的位置關(guān)系, 綜合給出圖像整體的邊緣信息.
void cv::Canny(InputArray image, //必須是三通道或者單通道圖像
OutputArray edges,
double threshold1, //第一個(gè)滯后閾值
double threshold2, //第二個(gè)滯后閾值
int apertureSize = 3,//Sobel算子直徑
int L2gradient = false
)
5.5.7 LOG算子
5.5.8 斑點(diǎn)檢測(cè)
LOG斑點(diǎn)檢測(cè)https://www.cnblogs.com/ronny/p/3895883.html
SimpleBlobDetector
第6章 圖像形態(tài)學(xué)操作
6.1 像素距離與連通域
6.1.1 圖像像素距離變換——distanceTransform()
圖像形態(tài)學(xué)操作主要包括圖像腐蝕、膨脹、開運(yùn)算與閉運(yùn)算
圖像形態(tài)學(xué)在圖像處理中具有廣泛的應(yīng)用, 主要應(yīng)用于從圖像中提取對(duì)于表達(dá)和描述區(qū)域形狀有意義的圖像分囂, 以便后續(xù)的識(shí)別工作能夠抓住對(duì)象最為本質(zhì)的形狀特性,例如邊界、連通域等.
圖像處理中常用的距離有歐氏距離、街區(qū)距離和棋盤距離
該函數(shù)用于實(shí)現(xiàn)圖像的距離變換. 即統(tǒng)計(jì)圖像中所有像素距離0像素的最小距離.
void cv::distanceTransform(InputArray src,
OutputArray dst,
OutputArrya labels, //輸出一個(gè)voronoi圖
int distanceType, //距離類型
int maskSize, //距離變換掩碼矩陣尺寸,參數(shù)可以選擇的尺寸DIST_MASK_3(3*3),DIST_MASK_5(5*5)
int labelType = DIST_LABEL_CCOMP
)
Voronoi圖,又叫泰森多邊形或Dirichlet圖,它是由一組由連接兩鄰點(diǎn)直線的垂直平分線組成的連續(xù)多邊形組成。
void distanceTransform(InputArray src,
OutputArray dst,
int distanceType,
int maskSize,
int dstType = CV_32F
)
6.1.2 圖像連通域分析——connectedComponents()
圖像的連通域是指圖像中具有相同像素值并且位置相鄰的像素組成的區(qū)域.
.提取圖像中不同的連通域是圖像處理中較為常用的方法,例如在車牌識(shí)別、文字識(shí)別、目標(biāo)檢測(cè)等領(lǐng)域?qū)Ω信d趣區(qū)域的分割與識(shí)別
int cv::connectedComponents(InputArray image,
OutputArray labels,
int connectivity, //4鄰域還是8鄰域
int ltype, //輸出圖像類型
int ccltype//標(biāo)記連通域使用的算法
)
常用的圖像鄰域分析法有兩遍掃描法和種子填充法.
https://blog.csdn.net/sy95122/article/details/80757281
//該函數(shù)既可以計(jì)算連通域,同時(shí)還可以標(biāo)記處不同連通域的位置,面積信息
int cv::connectedComponentsWithStats(InputArray image,
OutputArray labels,
OutputArray stats, //不同連通域統(tǒng)計(jì)信息,如下表
OutputArray centroids, //每個(gè)連通域質(zhì)心的坐標(biāo)
int connectivity,
int ltype,
int ccltype)
//該函數(shù)既可以計(jì)算連通域,同時(shí)還可以標(biāo)記處不同連通域的位置,面積信息
int cv::connectedComponentsWithStats(InputArray image,
OutputArray labels,
OutputArray stats,
OutputArray centroids, //每個(gè)連通域質(zhì)心的坐標(biāo)
int connectivity = 8, //4表示4鄰域,8表示8鄰域
int ltype = CV_32S)
6.2 腐蝕和膨脹
腐蝕和膨脹是形態(tài)學(xué)的基本運(yùn)算,通過這些基本運(yùn)算可以去除圖像中的噪聲、分割出獨(dú)立的區(qū)域或者將兩個(gè)連通域連接在一起等.
6.2.1 圖像腐蝕——erode()
[外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機(jī)制,建議將圖片保存下來(lái)直接上傳(img-cFQHVRjl-1666660001271)(https://gitee.com/lyz-npu/typora-image/raw/master/img/%E5%9B%BE6-12.jpg)]
cv::Mat cv::getStructuringElement(int shape, //如下表
Size ksize,
Point anchor = Point(-1,-1)
)
void cv::erode(InputArray stc,
OutputArray dst,
InputArray kernel, //可以用getStructuringElement()生成
Point anchor = Point(-1,-1),
int iterations = 1,
int borderType = BORDER_DEFAULT,
const Scalar & borderValue = morphologyDefaultBorderValue()
)
白底圖像黑底圖像操作相反
6.2.2 圖像膨脹——dilate()
void cv::dilate(InputArray src,
OutputArray dst,
InputArray kernel,
Point anchor = Point(-1,-1),
int iterations = 1,
int borderType = BORDER_CONSTANT,
const Scalar & boderValue = morphologyDefaultBorderValue()
)
6.3 形態(tài)學(xué)應(yīng)用
6.3.1 開運(yùn)算——morphologyEx()
圖像開運(yùn)算可以去除圖像中的噪聲, 消除較小連通域, 保留較大連通域,同時(shí)能夠在兩個(gè)物體纖細(xì)的連接處將兩個(gè)物體分離, 并且在不明顯改變較大連通域面積的同時(shí)能夠平滑連通域的邊界.
腐蝕—>膨脹
void cv::morphologyEx(InputArray src,
OutputArray dst,
int op,//形態(tài)學(xué)操作類型的標(biāo)志
InputArray kernel,
Point anchor = Point(-1,-1),
int iterations = 1,
int borderType = BORDER_CONSTANT,
const Scalar & boderValue = morphologyDefaultBorderValue()
)
6.3.2 閉運(yùn)算
圖像閉運(yùn)算可以去除連通域內(nèi)的小型空洞, 平滑物體輪廓, 連接兩個(gè)臨近的連通域.
膨脹—>腐蝕
6.3.3 形態(tài)學(xué)梯度
形態(tài)學(xué)梯度能夠描述目標(biāo)的邊界, 根據(jù)圖像腐蝕和膨脹與原圖之間的關(guān)系計(jì)算得到, 形態(tài)學(xué)梯度可以分為基本梯度、內(nèi)部梯度和外部梯度.基本梯度是原圖像膨脹后圖像與腐蝕后圖像間的差值圖像, 內(nèi)部梯度圖像是原圖像與腐蝕后圖像間的差值圖像, 外部梯度是膨脹后圖像與原圖像間的差值圖像.
6.3.4 頂帽運(yùn)算
圖像頂帽運(yùn)算是原圖像與開運(yùn)算結(jié)果之間的差值,往往用來(lái)分離比鄰近點(diǎn)亮一些的斑塊
腐蝕—>膨脹—>減原圖像
6.3.5 黑帽運(yùn)算
黑帽運(yùn)算是原圖像與頂帽運(yùn)算結(jié)果之間的差值, 往往用來(lái)分離比鄰近點(diǎn)暗一些的斑塊.
膨脹—>腐蝕—>減原圖像
6.3.6 擊中擊不中變換
擊中擊不中變換要求原圖像中需要存在與結(jié)構(gòu)元素一模一樣的結(jié)構(gòu)
6.3.7 圖像細(xì)化——thinning()
圖像細(xì)化是將圖像的線條從多像素寬度減少到單位像索寬度的過程,有時(shí)又稱為"骨架化"或者"中軸變換氣"
細(xì)化算法主要分為迭代細(xì)化算法和非迭代細(xì)化算法
迭代細(xì)化算法:串行細(xì)化算法、并行細(xì)化算法
Zhang 細(xì)化方法被廣泛使用.
void cv::ximgproc::thinning(InputArray src,
OutputArray dst,
int thinningType = THINNING_ZHANGSUEN//還有一種THINNING_GUOHALL,,,Guo細(xì)化方法
)
第7章 目標(biāo)檢測(cè)
7.1 形狀檢測(cè)
7.1.1 直線檢測(cè)——HoughLines()
霍夫變換(Hough Transfonn ) 是圖像處理中檢測(cè)是否存在直線的重要算法
//標(biāo)準(zhǔn)霍夫變換,,多尺度霍夫變換
void cv::HoughLines(InputArray image, //必須是CV_8U的單通道二值圖像
OutputArray lines, //NX2 的vector<Vec2f>
double rho, //常用1
double theta, //常用CV_PI/180
int threshold,
double srn = 0,
double stn = 0,
double min_theta = 0,
double max_theta = CV_PI
)
使用標(biāo)準(zhǔn)霍夫變換和多尺度霍夫變換函數(shù)HoughLine()提取直線時(shí)無(wú)法準(zhǔn)確知道圖像中直線或者線段的長(zhǎng)度,只能得到圖像中是否存在符合要求的直線,以及直線的極坐標(biāo)解析式.
//漸進(jìn)概率式霍夫變換函數(shù)
void cv::HoughLinesP(InputArray image, //必須是CV_8U的單通道二值圖像
OutputArray lines, //NX4 的vector<Vec4i>
double rho, //常用1
double theta, //常用CV_PI/180
int threshold,
double minLineLength = 0, //直線的最小長(zhǎng)度,當(dāng)檢測(cè)直線的長(zhǎng)度小于該數(shù)值時(shí)將被剔除.
double maxLineGap = 0//同一直線上相鄰的兩個(gè)點(diǎn)之間的最大距離.
)
Vec4i 中前兩個(gè)元素分別是直線或者線段一個(gè)端點(diǎn)的x 坐標(biāo)和y 坐標(biāo),后兩個(gè)元素分別是直線或者線段另一個(gè)端點(diǎn)的x 坐標(biāo)和y 坐標(biāo)
//檢測(cè)點(diǎn)集中的直線
void cv::HoughLinesPointSet(InputArray _Point, //必須是cv_32FC2或CV_32SC2圖像
//vector<Point2f>或者vector<Point2i>
OutputArray _lines, //在輸入點(diǎn)集合中可能存在的直線, 每一條直線都具有3個(gè)參數(shù),分別是權(quán)重、直線距離坐標(biāo)原點(diǎn)的距離r和坐標(biāo)原點(diǎn)到直線的垂線與x軸的夾角a.,,,權(quán)重越大表示是直線的可靠性越高,
int lines_max,//檢測(cè)直線的最大數(shù)目.如果數(shù)目過大, 檢測(cè)到的直線可能存在權(quán)重較小的情況.
int threshold,
double min_rho, //檢測(cè)直線長(zhǎng)度的最小距離, 以像素為單位.
double max_rho,
double rho_step,//以像素為單位的距離分辨率. 即距離r 離散化時(shí)的單位長(zhǎng)度.
double min_theta, //檢測(cè)直線的最小角度值,以弧度為單位.
double max_theta,
double theta_step//以弧度為單位的角度分辨率,即家教θ離散化時(shí)的單位角度.
)
7.1.2 直線擬合——fitLine()
與直線檢測(cè)相比,直線擬合的最大特點(diǎn)是將所有數(shù)據(jù)只擬合除一條直線
最小二乘M-estimator 方法
void cv::fitLine(InputArray points, //輸入待擬合直線的二維或者三維點(diǎn)集.vector<>或者M(jìn)at
OutputArray line, //二維點(diǎn)集描述參數(shù)為Vec4f 類型,三維點(diǎn)集描述參數(shù)為Vec6f類型。
int distType, //M-estirnator 算法使用的距離類型標(biāo)志
double param, //某些距離類型的數(shù)值參數(shù)( C) . 如果數(shù)值為0 , 那么自動(dòng)選擇最佳值.
double reps, //坐標(biāo)原點(diǎn)與擬合直線之間的距離精度,數(shù)值0表示選擇自適應(yīng)參數(shù), 一般選擇0.01 .
double aeps//擬合直線的角度精度,數(shù)值0表示選擇自適應(yīng)參數(shù),一般選擇0.01.
)
7.1.3 圓形檢測(cè)——HoughCircles()
https://blog.csdn.net/weixin_44638957/article/details/105883829
void cv::HoughCircles(InputArray image, //數(shù)據(jù)類型必須是CV_8UC1.
OutputArray circles, //存放在vector<Vec3f>類型的變量,分別是圓心的坐標(biāo)和圓的半徑.
int method, //目前僅支持HOUGH_GRADIENT方法.
double dp, //離散化時(shí)分辨率與圖像分辨率的反比.
double minDist, //檢測(cè)結(jié)果中兩個(gè)圓心之間的最小距離.
double paraml = 100, //Canny 檢測(cè)邊緣時(shí)兩個(gè)閾值的級(jí)大值,較小闕值默認(rèn)為較大值的一半.
double param2 = 100, //檢測(cè)圓形的累加器閾值,
int minRadius = 0,
int maxRadius = 0
)
7.2 輪廓檢測(cè)
7.2.1 輪廓發(fā)現(xiàn)與繪制——findContours()、drawContours()
https://blog.csdn.net/dcrmg/article/details/51987348
為了描述不同輪廓之間的結(jié)構(gòu)關(guān)系,定義由外到內(nèi)的輪廓級(jí)別越來(lái)越低,也就是高一層級(jí)的輪廓包圍著較低層級(jí)的輪廓
void cv::findContours(InputArray image, //數(shù)據(jù)類型為CV_8U 的單通道灰皮圖像或者二值化圖像.
OutputArrayOfArrays coutours, //檢測(cè)到的輪廓,每個(gè)輪廓中存放像素的坐標(biāo).vector<vector<Point>>
OutputArray hierarchy, //輪廓結(jié)構(gòu)關(guān)系描述向量.vector<Vec4i>
int mode, //輪廓檢測(cè)模式標(biāo)志
int method, //輪廓逼近方法標(biāo)志
Point offset = Point()//每個(gè)輪廓點(diǎn)移動(dòng)的可選偏移量.這個(gè)參數(shù)主要用在從ROI圖像中找出輪廓并基于整個(gè)圖像分析輪廓的場(chǎng)景中.
)
//不輸出輪廓的結(jié)構(gòu)關(guān)系,避免內(nèi)存資源的浪費(fèi)
void cv::findContours(InputArray image,
OutputArrayOfArrays coutours,
int mode,
int method,
Point offset = Point()
)
對(duì)于連接了圖片邊框的內(nèi)容(例如下圖的手),整個(gè)手不是個(gè)封閉圖形,因此,在canny算子下找出了外輪廓,盡管使用了RETR_EXTERNEL來(lái)尋找最外層邊框,依然會(huì)找到多余的邊框。其實(shí)內(nèi)部多出來(lái)的黑色輪廓是和手外部輪廓的同級(jí)邊框
void cv::drawContours(InputOutputArray image, //繪制輪廓的目標(biāo)圖像.
InputArrayOfArray coutours, //所有將要繪制的輪廓.//vector<Vector<Point>>
int contourIdx, //輪廓的索引編號(hào)。若為負(fù)值,則繪制所有輪廓。
const Scalar & color,
int thickness,
int lineType = LINE_8, //邊界線的連接類型
InputArray hierarchy = noArray(), //可選的結(jié)構(gòu)關(guān)系信息,默認(rèn)值為noArray().
int maxLevel = INT_MAX, //表示繪制輪廓的最大等級(jí), 默認(rèn)值為INT_MAX .
Point offset = Point()//可選的輪廓偏移參數(shù),按指定的移動(dòng)距離繪制所有的輪廓。
)
7.2.2 輪廓面積——contourArea()
通過輪廓面積的大小可以進(jìn)一步分析每個(gè)輪廓隱含的信息.例如通過輪廓面積區(qū)分物體大小、識(shí)別不同的物體等
double cv::contourArea(InputArray coutour, //輪廊的像素點(diǎn). vector<Point>或者M(jìn)at.
bool oriented = false//區(qū)域面積是否具有方向的標(biāo)志. true 表示面積具有方向性. false 表示面積不具有方向性,默認(rèn)值為面積不具有方向性的false.默認(rèn)輸出絕對(duì)值
)
當(dāng)參數(shù)取值為true時(shí), 表示統(tǒng)計(jì)的面積具有方向性, 輪廓頂點(diǎn)順時(shí)針給出和逆時(shí)針給出時(shí)統(tǒng)計(jì)的面積互為相反數(shù);當(dāng)參數(shù)取值為false 時(shí), 表示統(tǒng)計(jì)的面積不具有方向性,輸出輪廓面積的絕對(duì)值.
7.2.3 輪廓長(zhǎng)度(周長(zhǎng))——arcLength()
double cv::arcLength(InputArray curve, //輪廓或者曲線的二維像素點(diǎn). vector<Point>或者M(jìn)at
bool closed//輪廓或者曲線是否閉合的標(biāo)志, true 表示閉合.
)
若第二個(gè)參數(shù)式true,則三角形三個(gè)頂點(diǎn)的周長(zhǎng)為三邊和,,,,若為false,則周長(zhǎng)為兩邊之和
7.2.4 輪廓外接多邊形——boundingRect()、minAreaRect()、approxPolyDP()
//求取輪廓最大外接矩形
Rect cv::boundingRect(InputArray array)//array 表示輸入的灰度圖像或者二維點(diǎn)集,數(shù)據(jù)類型為vector<Point>或者M(jìn)at
//輸入一個(gè)findContour的contour矩陣
//求取最小外接矩形
RotatedRect cv::minAreaRect(InputArray points)//point表示輸入的二維點(diǎn)集合.
//多邊形逼近輪廓
void cv;:approxPolyDp(InputArray curve, //輸入輪廓像素點(diǎn).vector<Point>或者M(jìn)at
OutputArray approxCurve, //多邊形逼近結(jié)果,以頂點(diǎn)坐標(biāo)的形式給出.CV_32SC2類型的Nx1的Mat類矩陣,
double epsilon, //逼近的精度,即原始曲線和逼近曲線之間的最大距離.
bool closed//逼近曲線是否為封閉曲線的標(biāo)志, true 表示曲線封閉,
)
7.2.5 點(diǎn)到輪廓距離——pointPolygonTest()
點(diǎn)到輪廓的距離,對(duì)于計(jì)算輪廓在圖像中的位置、兩個(gè)輪廓之間的距離以及確定圖像上某一點(diǎn)是否在輪廓內(nèi)部具有重要的作用.
double cv::pointPolygonTest(InputArray coutour, //輸入的輪廓. vector<Point>或者M(jìn)at
Point2f pt, //需要計(jì)算與輪廓距離的像素點(diǎn).
bool measureDist//計(jì)算的距離是否具有方向性的標(biāo)志.當(dāng)參數(shù)取值為true 時(shí),點(diǎn)在輪廓內(nèi)部時(shí),距離為正,點(diǎn)在輪廓外部時(shí),距離為負(fù);;;;當(dāng)參數(shù)取值為false時(shí),只檢測(cè)點(diǎn)是否在輪廓內(nèi).
)
7.2.6 凸包檢測(cè)——convexHull()
將二維平面上的點(diǎn)集最外層的點(diǎn)連接起來(lái)構(gòu)成的凸多邊形稱為凸包.但是逼近結(jié)果一定為凸多邊形.
void cv::convexHull(InputArray points, //輸入的二維點(diǎn)集或輪廓坐標(biāo). vector<Point>或者M(jìn)at
OutputArray hull, //輸出凸包的頂點(diǎn). vector<Point>或者vector<int>
bool clockwise = false, //當(dāng)參數(shù)取值為true時(shí),凸包順序?yàn)轫槙r(shí)針方向:
bool returnPoints = true
)
7.3 矩的計(jì)算
7.3.1 幾何矩與中心矩——moments()
https://www.zhihu.com/question/26803016
矩是描述圖像特征的算子,廣泛應(yīng)用于圖像檢索和識(shí)剔,圖像匹配,圖像重建,圖像壓縮,以及運(yùn)動(dòng)圖像序列分析等領(lǐng)域.
Moments cv::moments(InputArray array, //計(jì)算矩的區(qū)域二維像素坐標(biāo)集合或者單通道的CV_8U圖像.
bool binaryImage = false//是否將所有非零像素值視為1的標(biāo)志.
)
7.3.2 Hu矩——HuMoments()
Hu 矩具有旋轉(zhuǎn)、平移和縮放不變性,因此,在圖像具有旋轉(zhuǎn)和縮放的情況下, Hu矩具有更廣泛的應(yīng)用.
//需要先計(jì)算圖像的矩,將圖像的矩輸入到HuMonments()函數(shù)中
void cv::HuMoments(const Moments & moments, //輸入的圖像矩.
double hu[7]//輸出Hu 矩的7 個(gè)值.
)
void cv::HuMoments(const Moments & m,
OutputArray hu//輸出Hu 矩的矩陣.
)
Moments M = Moments(imgContours);
HuMoments(m, hu);
7.3.3 基于Hu矩的輪廓匹配——matchShapes()
由于Hu 矩具有旋轉(zhuǎn)、平移和縮放不變性,因此可以通過Hu 實(shí)現(xiàn)圖像輪廓的匹配.
double cv::matchShapes(InputArray contour1, //原灰度圖像或者輪廓.
InputArray contour2, //模板圖像或者輪廓.
int method, ///匹配方法的標(biāo)志
double parameter//特定于方法的參數(shù)( 現(xiàn)在不支持) .可以設(shè)置為0
)
7.4 點(diǎn)集擬合
//該函數(shù)能夠找到包含給定三維點(diǎn)集的最小區(qū)域的三角形,,,返回值為double 類型的三角形面積.
double cv::minEnclosingTriangle(InputArray points, //vector<>或者M(jìn)at類型的變量,CV_32S或CV_32F;
OutputArray triangle//三角形的3個(gè)頂點(diǎn)坐標(biāo),存放在vector<Point2f>變量中
)
//尋找二維點(diǎn)集的最小包圍圓形
void cv::minEnclosingCircle(InputArray points, //vector<>或者M(jìn)at類型的變量
Point2f & center,
float & radius
)
7.5 QR二維碼檢測(cè)
對(duì)QR 二維碼的識(shí)別儒要借助第三方工具,常用的是zbar 解碼庫(kù).OpenCV4提供了解碼函數(shù)
針對(duì)QR 二維碼識(shí)別的兩個(gè)過程. OpenCV 4 提供了多個(gè)函數(shù)用于實(shí)現(xiàn)每個(gè)過程, 這些函數(shù)分別是定位QR二維碼的detect()函數(shù)、根據(jù)定位結(jié)果解碼二維碼的decode()函數(shù),以及同時(shí)定位和解碼的detectAndDecode()函數(shù).
//定位不解碼
bool cv::QRCodeDetector::detect(InputArray img,
OutputArray points//二維碼的4 個(gè)頂點(diǎn)坐標(biāo).。。vector<Point>
)
//對(duì)定位結(jié)果進(jìn)行解碼
std::string cv::QRCodeDetector::decode(InputArray img, //含有QR 二維碼的圖像.
InputArray points, //包含QR二維碼的最小區(qū)域的四邊形的4 個(gè)頂點(diǎn)坐標(biāo).
OutputArray straight_qrcode = noArray()//經(jīng)過校正和二值化的QR二維碼.
)
//一步完成定位和解碼
std::string cv::QRCodeDetector::detectAndDecode(InputArray img,
OutputArray points = noArray(),
OutputArray straight_qrcode = noArray()
)
cv::QRCodeDetector qrcodedetector;
qrcodedetector.detect();
qrcodedetector.decode();
qrcodedetector.detectAndDecode();
第8章 圖像分析與修復(fù)
8.1 傅里葉變換
https://www.bilibili.com/video/BV1HJ411a7cp?spm_id_from=333.880
通俗講解:圖像傅里葉變換 - 麻花團(tuán)子的文章 - 知乎 https://zhuanlan.zhihu.com/p/99605178
任何信號(hào)都可以由一系列正弦信號(hào)疊加形成, 一維領(lǐng)域信號(hào)是一維正弦波的疊加,二維領(lǐng)域是二維平面波的增加.由于圖像可以看作是二維信號(hào), 因此可以對(duì)圖像進(jìn)行傅里葉變換
對(duì)圖像進(jìn)行離散傅里葉變換
離散傅里葉變換廣泛應(yīng)用在圖像的去噪、濾波等卷積領(lǐng)域
理解傅里葉分析:https://blog.csdn.net/u013921430/article/details/79683853
數(shù)字圖像傅里葉分析:https://blog.csdn.net/u013921430/article/details/79934162
8.1.1 離散傅里葉變換——dft()、idft()
圖像離散傅里葉變換之后的結(jié)果會(huì)得到既含有實(shí)數(shù)又含有虛數(shù)的圖像,在實(shí)際使用時(shí)常將結(jié)果分成實(shí)數(shù)圖像和虛數(shù)圖像,或者用復(fù)數(shù)的幅值和相位來(lái)表示變換結(jié)果, 可以分成幅值圖像和相位圖像.
圖像中像素波動(dòng)較大的區(qū)域?qū)?yīng)的頻域是高頻區(qū)域,因此高頻區(qū)域體現(xiàn)的是圖像的細(xì)節(jié)、邊緣、紋理信息,而低頻信息代表了圖像的內(nèi)容信息.
//對(duì)圖像進(jìn)行傅里葉變換
void cv::dft(InputArray src, //輸入的圖像或者數(shù)組矩陣,可以是實(shí)數(shù)也可以是復(fù)數(shù).CV_32F 或者CV_64F
OutputArray dst, //存放離散傅里葉變換結(jié)果的數(shù)組矩陣.
int flags = 0, //變換類型可選標(biāo)志
int nonzeroRows = 0//輸入、輸出結(jié)果的形式,默認(rèn)值為o .
)
//對(duì)圖像進(jìn)行離散傅里葉逆變換
idft(src, dst, flags)相當(dāng)于dft(src, dst, flags | DFT_INVERSE)
離散傅里葉變換算法傾向于對(duì)某些特定長(zhǎng)度的輸入矩陣進(jìn)行處理, 而不是對(duì)任意尺寸的矩陣進(jìn)行處理.因此,如果尺寸小于處理的最佳尺寸,那么常需要對(duì)輸入矩陣進(jìn)行尺寸變化以使得函數(shù)擁有較快的處理速度.常見的尺寸調(diào)整方式為在原矩陣的周圍增加多層。像素, 因此dft()函數(shù)第四個(gè)參數(shù)才會(huì)討論矩陣中出現(xiàn)第一個(gè)非零行.
填充多少行,列需要由下面的函數(shù)進(jìn)行計(jì)算,,計(jì)算需要多少行、列
//計(jì)算最優(yōu)尺寸
int cv::getOptimalDFTSize(int vecsize);//vecsize是圖像的rows,cols
計(jì)算好最有尺寸后,改變圖像的尺寸,在圖像周圍生成外框
void cv::copyMakeBorder(InputArray src,
OutputArray dst,
int top,
int bottom,
int left,
int right,
int borderType,
const Scalar & value = Scalar()
)
由于離散傅里葉變換得到的數(shù)值可能為雙通道的復(fù)數(shù), 在實(shí)際使用過程中更加關(guān)注復(fù)數(shù)的幅值, 因此OpenCV 4 提供了magnitude()函數(shù)用于計(jì)算由兩個(gè)矩陣組成的二維向量矩陣的幅值矩陣.
void cv::magnitude(InputArray x,
InputArray y,
OutputArray magnitude
)
因?yàn)镕(u,v)衰減、變化比較快,直接畫圖看不到什么點(diǎn),因此需要加個(gè)1,,,再log運(yùn)算,,,然后再對(duì)幅值譜進(jìn)行分析
.在該程序中, 首先計(jì)算適合圖像離散傅里葉變換的最優(yōu)尺寸,之后利用copyMakeBorder()函數(shù)擴(kuò)展圖像尺寸,然后進(jìn)行離散傅里葉變換,最后計(jì)算變換結(jié)果的幅值.為了能夠顯示變換結(jié)果中的幅值,將結(jié)果進(jìn)行歸一化處理。變換的原點(diǎn)位于4 個(gè)頂點(diǎn),因此通過圖像變換,將變換結(jié)果的原點(diǎn)調(diào)整到圖像中心.
需要注意一點(diǎn)的是,頻域圖像歸一化處理僅僅只是觀察頻譜圖時(shí)有所幫助.包括對(duì)數(shù)變換也是一樣,是為了更好的觀察頻域的能量分布的,因?yàn)閳D像的能量大多集中在低頻區(qū)域,因此變換的結(jié)果大多也就是看到低頻上一些白斑
圖像傅里葉變換后為什么要?dú)w一化? - DBinary的回答 - 知乎 https://www.zhihu.com/question/354081645/answer/890215427
#include <opencv2/opencv.hpp>
#include <iostream>
using namespace std;
using namespace cv;
int main()
{
//對(duì)矩陣進(jìn)行處理,展示正逆變換的關(guān)系
Mat a = (Mat_<float>(5, 5) << 1, 2, 3, 4, 5,
2, 3, 4, 5, 6,
3, 4, 5, 6, 7,
4, 5, 6, 7, 8,
5, 6, 7, 8, 9);
Mat b, c, d;
dft(a, b, DFT_COMPLEX_OUTPUT); //正變換
dft(b, c, DFT_INVERSE | DFT_SCALE | DFT_REAL_OUTPUT); //逆變換只輸出實(shí)數(shù)
idft(b, d, DFT_SCALE); //逆變換
//對(duì)圖像進(jìn)行處理
Mat img = imread("pic/lena.png");
if (img.empty())
{
cout << "請(qǐng)確認(rèn)圖像文件名稱是否正確" << endl;
return -1;
}
Mat gray;
cvtColor(img, gray, COLOR_BGR2GRAY);
resize(gray, gray, Size(502, 502));
imshow("原圖像", gray);
//計(jì)算合適的離散傅里葉變換尺寸
int rows = getOptimalDFTSize(gray.rows);
int cols = getOptimalDFTSize(gray.cols);
//擴(kuò)展圖像
Mat appropriate;
int T = (rows - gray.rows) / 2; //上方擴(kuò)展行數(shù)
int B = rows - gray.rows - T; //下方擴(kuò)展行數(shù)
int L = (cols - gray.cols) / 2; //左側(cè)擴(kuò)展行數(shù)
int R = cols - gray.cols - L; //右側(cè)擴(kuò)展行數(shù)
copyMakeBorder(gray, appropriate, T, B, L, R, BORDER_CONSTANT);
imshow("擴(kuò)展后的圖像", appropriate);
//構(gòu)建離散傅里葉變換輸入量
Mat flo[2], complex;
flo[0] = Mat_<float>(appropriate); //實(shí)數(shù)部分
flo[1] = Mat::zeros(appropriate.size(), CV_32F); //虛數(shù)部分
merge(flo, 2, complex); //合成一個(gè)多通道矩陣
//進(jìn)行離散傅里葉變換
Mat result;
dft(complex, result);
//將復(fù)數(shù)轉(zhuǎn)化為幅值
Mat resultC[2];
split(result, resultC); //分成實(shí)數(shù)和虛數(shù)
Mat amplitude;
magnitude(resultC[0], resultC[1], amplitude);
//進(jìn)行對(duì)數(shù)放縮公式為: M1 = log(1+M),保證所有數(shù)都大于0
amplitude = amplitude + 1;
log(amplitude, amplitude);//求自然對(duì)數(shù)
//與原圖像尺寸對(duì)應(yīng)的區(qū)域
amplitude = amplitude(Rect(T, L, gray.cols, gray.rows));//gray擴(kuò)充了矩陣尺寸,,因此構(gòu)建矩陣的時(shí)候把邊框裁掉,匹配原來(lái)的初始圖像
normalize(amplitude, amplitude, 0, 1, NORM_MINMAX); //歸一化
imshow("傅里葉變換結(jié)果幅值圖像", amplitude); //顯示結(jié)果
//重新排列傅里葉圖像中的象限,使得原點(diǎn)位于圖像中心
int centerX = amplitude.cols / 2;
int centerY = amplitude.rows / 2;
//分解成四個(gè)小區(qū)域
Mat Qlt(amplitude, Rect(0, 0, centerX, centerY));//ROI區(qū)域的左上
Mat Qrt(amplitude, Rect(centerX, 0, centerX, centerY));//ROI區(qū)域的右上
Mat Qlb(amplitude, Rect(0, centerY, centerX, centerY));//ROI區(qū)域的左下
Mat Qrb(amplitude, Rect(centerX, centerY, centerX, centerY));//ROI區(qū)域的右下
//交換象限,左上和右下進(jìn)行交換
Mat med;
Qlt.copyTo(med);
Qrb.copyTo(Qlt);
med.copyTo(Qrb);
//交換象限,左下和右上進(jìn)行交換
Qrt.copyTo(med);
Qlb.copyTo(Qrt);
med.copyTo(Qlb);
imshow("中心化后的幅值圖像", amplitude);
waitKey(0);
return 0;
}
8.1.2 傅里葉變換進(jìn)行卷積——mulSpecturms()
傅里葉變換可以將兩個(gè)矩陣的卷積轉(zhuǎn)換成兩個(gè)矩陣傅里葉變換結(jié)果的乘積,通過這種方式可以極大地提高卷積的計(jì)算速度.但是圖像傅里葉變換結(jié)果都是具有復(fù)數(shù)共輒對(duì)稱性的復(fù)數(shù)矩陣,兩個(gè)矩陣相乘需要計(jì)算對(duì)應(yīng)位置的兩個(gè)復(fù)數(shù)乘積. OpenCV 4 提供了用于計(jì)算兩個(gè)復(fù)數(shù)矩陣的乘積的mulSepctrums()函數(shù)
void cv::mulSpecturms(InputArray a,
InputArray b,
OutputArray c,
int flags, //DFT_COMPLEX_OUTPUT
bool conjB = false//是否對(duì)第二個(gè)輸入矩陣進(jìn)行共輒變換的標(biāo)志.當(dāng)參數(shù)為false時(shí),不進(jìn)行其輒變換;當(dāng)參數(shù)為true時(shí),進(jìn)行共輒變換.
)
通過離散傅里葉變換方式進(jìn)行圖像的卷積時(shí),需要將卷積核也擴(kuò)展到與圖像相同的尺寸,然后同時(shí)對(duì)兩個(gè)圖片做尺寸最優(yōu)化擴(kuò)展矩陣, 并對(duì)乘積結(jié)果進(jìn)行離散傅里葉變換的逆交換.
8.1.3 離散余弦變換
https://blog.csdn.net/akadiao/article/details/79778095
離散余弦變換經(jīng)常使用在信號(hào)處理和圖像處理領(lǐng)域中,主要用于對(duì)信號(hào)和圖像的有損數(shù)據(jù)壓縮中.離散余弦變換具有"能量集中" 的特性,信號(hào)經(jīng)過變換后能量主要集中在結(jié)果的低頻部分.
void cv::dct(InputArray src, //處理單通道數(shù)據(jù)
OutputArray dst,
int flags = 0
)
目前dct()函數(shù)只支持偶數(shù)大小的數(shù)組,
//逆變換
void cv::idct(InputArray src,
OutputArray dst,
int flags = 0
)
離散余弦變換具有很強(qiáng)的”能量集中”特性,左上方稱為低頻數(shù)據(jù),右下方稱為高頻數(shù)據(jù)。
8.2 積分圖像
用于快速計(jì)算圖像某些區(qū)域像素的平均灰度
.積分圖像是比原因像尺寸大1 的新圖像,例如,原因像尺寸為NxN , 那么積分圖像尺寸為(N + 1) x (N + 1) .
積分圖像中每個(gè)像素的像素值為原圖像中該像素點(diǎn)與坐標(biāo)原點(diǎn)組成的矩形內(nèi)所有像素值的和,
P0的像素值為原圖像中前四行和前四列相交區(qū)域內(nèi)所有像素值之和
[外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機(jī)制,建議將圖片保存下來(lái)直接上傳(img-er9De8kL-1666660001277)(https://gitee.com/lyz-npu/typora-image/raw/master/img/%E5%9B%BE8-7.png)]
積分圖像:標(biāo)準(zhǔn)求和積分圖像、平方求和積分圖像、傾斜求和積分圖像(只是累加方向旋轉(zhuǎn)了45°)
//標(biāo)準(zhǔn)求和積分
void cv::intergral(InputArray src, //NxN
OutputArray sum, //(N+I)x(N+I)
int sdepth = -1
)
//平方求和積分
void cv::intergral(InputArray src,
OutputArray sum,
OutputArray sqsum, //輸出平方求和積分圖像
int sdepth = -1,
int sqdepth = -1
)
//平方求和積分
void cv::intergral(InputArray src,
OutputArray sum,
OutputArray sqsum, //輸出平方求和積分圖像
Output Array tilted,//輸出傾斜求和積分圖像
int sdepth = -1,
int sqdepth = -1
)
8.3 圖像分割
8.3.1 漫水填充法——floodFill()
int cv::floodFill(InputOutputArray imgae, //單通道或者三通道
InputOutputArray mask, //尺寸比輸入圖像寬和高各大2 的單通道圖像,用于標(biāo)記漫水填充的區(qū)域.
Point seedPoint, //種子點(diǎn)
Scalar newVal, //歸入種子點(diǎn)區(qū)域內(nèi)像素點(diǎn)的新像素值.
Rect * rect = 0, //種子點(diǎn)漫水填充區(qū)域的最小矩形邊界, 默認(rèn)值為0 ,表示不輸出邊界
Scalar loDiff = Scalar(), //·添加進(jìn)種子點(diǎn)區(qū)域條件的下界差值
Scalar upDiff = Scalar(), //添加進(jìn)種子點(diǎn)區(qū)域條件的上界差值
int flags = 4//漫水填充法的操作標(biāo)志,,,,,鄰域種類、掩碼矩陣中被填充像素點(diǎn)的像素值、填充算法
//41 | (255<<8) | FLOODFILL_FIXED_RANGE
)
int cv::floodFill(InputOutputArray imgae,
Point seedPoint,
Scalar newVal,
Rect * rect = 0,
Scalar loDiff = Scalar(),
Scalar upDiff = Scalar(),
int flags = 4
)
8.3.2 分水嶺法——watershed()
區(qū)別在于漫水填充法是從某個(gè)像素值進(jìn)行分割, 是一種局部分割算法,而分水嶺法是從全局出發(fā),需要對(duì)全局進(jìn)行分割.
https://zhuanlan.zhihu.com/p/67741538
void cv::watershed(InputArray image, //是CU_8U數(shù)據(jù)類型的三通道彩色圖像
InputOutputArray markers//CV_32S 數(shù)據(jù)類型的單通道圖像的標(biāo)記結(jié)果
)
兩個(gè)區(qū)域的邊界是-1,其他區(qū)域的值是1-n的數(shù)字,數(shù)字不可能大于 contour的數(shù)量。
8.3.3 Grabcut法——grabCut()
Grabcut 法是重要的圖像分割算法,其使用高斯混合模型估計(jì)目標(biāo)區(qū)域的背景和前景.該算法通過法代的方法解決了能量函數(shù)最小化的問題, 使得結(jié)果具有更高的可靠性.
void cv::grabCut(InputArray img, //CV_8U
InputOutputArray mask, //CV_8U
Rect rect, //的ROI 區(qū)域, 該參數(shù)僅在mode = GC_INIT_WITH_RECT 時(shí)使用.
InputOutputArray bgdModel, //背景模型的臨時(shí)數(shù)組.
InputOutputArray fgdModel, //前景模型的臨時(shí)數(shù)組.
int iterCount, //法代次數(shù).
int mode = GC_EVAL//分割模式標(biāo)志,
)
8.3.4 Mean-Shift法——pyrMeanShiftFiltering()
https://blog.csdn.net/ttransposition/article/details/38514127
百度文庫(kù)PPT:https://wenku.baidu.com/link?url=kZt9aLkbV0D74VY9AjCs_aL4cO4eSrbU8rDdzk2pLP0ZbSieurP-xFAJAPGxZSx3bLLxv14bMC5WoSlTkjZ5QXO7UOOO5IKKMqVixxsZst_
meanshift經(jīng)常用來(lái)尋找模態(tài)點(diǎn),即密度最大的點(diǎn)
均值漂移法,是一種基于顏色空間分布的圖像分割算法,該算法的輸出是一個(gè)經(jīng)過濾色的“分色“圖像,其圖像會(huì)變得漸變,細(xì)紋紋理會(huì)變的平緩。,每個(gè)像素點(diǎn)用一個(gè)五維向量(x, y, b, g, r)表示,
void cv:pyrMeanShiftFiltering(InputArray src, //CU_8U
OutputArray dst,
double sp, //滑動(dòng)窗口的半徑.
double sr, //滑動(dòng)窗口顏色幅度.
int maxLevel = 1, //分割金字塔縮放層數(shù).
TermCriteria = TermCriteria(TermCriteria::MAX_ITER+TermCriteria::EPS,5,1))//法代算法終止條件.
cv::TermCriteria::TermCriteria(int Type,
int maxCount, //最大法代次數(shù)或者元素?cái)?shù).
double epsilon//法代算法停止時(shí)需要滿足的精度或者參數(shù)變化.
)
8.4 圖像修復(fù)
去除圖像中受"污染"的區(qū)域。圖像修復(fù)不但可以去除圖像中的劃痕,而且可以去除圖像中的水印、日期等.
void cv::inpaint(InputArray src, //當(dāng)圖像為三通道時(shí), 數(shù)據(jù)類型必須是CV_8U.
InputArray inpaintMask,
OutputArray dst,
double inpaintRadius, //算法考慮的每個(gè)像素點(diǎn)的圓形鄰域半徑.
int flags//修復(fù)圖像方法標(biāo)志,
)
離邊緣區(qū)域越遠(yuǎn)的像索估計(jì)的準(zhǔn)確度越低, 因此, 如果" 污染" 區(qū)域較大, 修復(fù)的效果就會(huì)降低.
先創(chuàng)建掩模(掩模是圖像中的污染,通過二值化等操作求出污染的區(qū)域),然后可以適當(dāng)膨脹擴(kuò)大掩模的污染區(qū)域,在inpaint()函數(shù)中輸入要去除污染的圖像和污染掩模
第9章 特征點(diǎn)檢測(cè)與匹配
9.1 角點(diǎn)檢測(cè)
9.1.1 顯示關(guān)鍵點(diǎn)——drawKeypoints()
關(guān)鍵點(diǎn)是對(duì)圖像中含有特殊信息的像素點(diǎn)的一種稱呼,主要含有像素點(diǎn)的位置、角度等信息.
drawKeypoints() 函數(shù)用于一次性繪制所有的關(guān)鍵點(diǎn)
void cv::drawKeypoints(InputArray image,
const std::vector<Keypoint> & keypoints,
InputOutputArray outImage, //繪制關(guān)鍵點(diǎn)后的圖像
const Scalar & color = Scalar::all(-1), //關(guān)鍵點(diǎn)的顏色
DrawMatchesFlags flags = DrawMatchesFlasgs::DEFAULT//繪制功能選擇標(biāo)志
)
//KeyPoint類
class KeyPoint{
float angle //關(guān)鍵點(diǎn)的角度
int class_id //關(guān)鍵點(diǎn)的分類號(hào)
int octave //特征點(diǎn)來(lái)源“金字塔”
Point2f pt //關(guān)鍵點(diǎn)坐標(biāo)
float response //最強(qiáng)關(guān)鍵點(diǎn)的響應(yīng),可用于進(jìn)一步分類和二次來(lái)樣
float size //關(guān)鍵點(diǎn)鄰域的直徑
}
//關(guān)鍵點(diǎn)類型變量的其他屬性可以默認(rèn),但是坐標(biāo)屬性必須具有數(shù)據(jù).
9.1.2 Harris角點(diǎn)檢測(cè)——cornerHarris()
Harris角點(diǎn)對(duì)于旋轉(zhuǎn)、平移具有不變性,,但是對(duì)于縮放沒有不變性
Harris角點(diǎn)響應(yīng)函數(shù)表達(dá)式是怎么得來(lái)的? - 大黑的回答 - 知乎 https://www.zhihu.com/question/37871386/answer/2311779754
角點(diǎn)檢測(cè):Harris 與 Shi-Tomasi - 程序員阿德的文章 - 知乎 https://zhuanlan.zhihu.com/p/83064609
Harris角點(diǎn)是最經(jīng)典的角點(diǎn)之一,其從像素值變化的角度對(duì)角點(diǎn)進(jìn)行定義,像素值的局部最大峰值即為Harris 角點(diǎn)
void cv::cornerHarris(InputArray src, //CV_8U 或者CV_32F
OutputArray dst, //存放Harris評(píng)價(jià)系數(shù)R的矩陣,數(shù)據(jù)類型為CV_32F的單通道圖像,
int blockSize, //鄰域大小. 常常取2
int ksize, //Sobel算子的半徑,用于得到梯度信息. 多使用3或者5
double k, //計(jì)算Harris 評(píng)價(jià)系數(shù)R 的權(quán)重系數(shù). 一般取值為0.02~0.04.
int borderType = BORDER_DEFAULT
)
該函數(shù)計(jì)算得到的結(jié)果是Harris評(píng)價(jià)系數(shù),但是由于其取值范圍較廣并且有正有負(fù), 常需要通過normalize()函數(shù)將其歸一化到指定區(qū)域內(nèi)后,再通過閾值比較判斷像素點(diǎn)是否為Harris 角點(diǎn)
9.1.3 Shi-Tomas角點(diǎn)檢測(cè)——goodFeaturesToTrack()
void cv::goodFeaturesToTrack(InputArray image,
OutputArray corners, //vector<Point2扣的向盤或者M(jìn)at 類矩陣中,那么如果存放在Mat類矩陣中, 那么生成的是數(shù)據(jù)類型為CV_32F 的單列矩陣,
int maxCorners,
double qualityLevel, //角點(diǎn)閾值與最佳角點(diǎn)之間的關(guān)系,又稱為質(zhì)量等級(jí),如果參數(shù)為0.01. 那么表示角點(diǎn)閥值是最佳角點(diǎn)的0.01倍.
double minDistance, //兩個(gè)角點(diǎn)之間的最小歐氏距離.
InputArray mask = noArray(),
int blockSize = 3, //計(jì)算梯度協(xié)方差矩陣的尺寸,,,,默認(rèn)3
bool useHarrisDetector = false, //是否使用Har由角點(diǎn)檢測(cè).
double k = 0.04//Harris角點(diǎn)檢測(cè)過程中的常值權(quán)重系數(shù).
)
9.1.4 亞像素級(jí)別角點(diǎn)檢測(cè)——cornerSubPix()
void cv::cornerSubPix(InputArray image,
InputOutputArray corners, //角點(diǎn)坐標(biāo), 既是輸入的角點(diǎn)坐標(biāo), 又是精細(xì)后的角點(diǎn)坐標(biāo).
Size winSize, //搜索窗口尺寸的一半, 必須是整數(shù).實(shí)際的搜索窗口尺寸比該參數(shù)的2倍大1.
Size zeroZone, //搜索區(qū)域中間"死區(qū)"大小的一半,即不提取像素點(diǎn)的區(qū)域,(-1,-1)表示沒"死區(qū)"
TermCriteria criteria//:終止角點(diǎn)優(yōu)化法代的條件.
)
亞像素級(jí)別角點(diǎn)檢測(cè)是先計(jì)算出Harris角點(diǎn)/Shi-Tomas角點(diǎn)后,再精細(xì)化計(jì)算亞像素角點(diǎn)
9.1.5 FAST角點(diǎn)檢測(cè)——FAST()
https://blog.csdn.net/tostq/article/details/49314017
https://aibotlab.blog.csdn.net/article/details/65662648
1.fast很快;
2.沒有sift的尺度不變性,也不具有旋轉(zhuǎn)不變性;
3.當(dāng)圖片中的噪點(diǎn)較多時(shí),它的健壯性并不好,而且算法的效果還依賴于一個(gè)閾值t
FAST(gray, fastPt, 50,true,FastFeatureDetector::TYPE_9_16);
//等價(jià)于下面
Ptr<FastFeatureDetector> fast = FastFeatureDetector::create(50, true, FastFeatureDetector::TYPE_9_16);
fast->detect(gray, fastPt);
//Ptr<類名>的用法,Ptr是一個(gè)智能指針,可以在任何地方都不使用時(shí)自動(dòng)刪除相關(guān)指針,從而幫助徹底消除內(nèi)存泄漏和懸空指針的問題。,Ptr<類名>是一個(gè)模板類,其類型為指定的類,
OpenCV筆記(Ptr) https://www.cnblogs.com/fireae/p/3684915.html
//Ptr計(jì)數(shù)指針的技術(shù)問題
shared_ptr的引用計(jì)數(shù)原理 https://blog.csdn.net/qq_29108585/article/details/78027867
深入理解智能指針之shared_ptr https://www.cnblogs.com/mrbendy/p/12701339.html
9.2 特征點(diǎn)檢測(cè)
特征點(diǎn)與角點(diǎn)在宏觀定義上相同,都是能夠表示圖像中局部特征點(diǎn)多的像素點(diǎn),但是特征點(diǎn)去有別于角點(diǎn)的是其具有能夠唯一描述像素點(diǎn)特征的描述子,例如該點(diǎn)左側(cè)像素比右側(cè)像素大,該點(diǎn)是局部最低點(diǎn)等。
通常特征點(diǎn)有關(guān)鍵點(diǎn)和描述子組成,例如SIFT特征點(diǎn)、ORB特征點(diǎn)等都需要先計(jì)算關(guān)鍵點(diǎn)坐標(biāo),之后再計(jì)算描述子
9.2.1 關(guān)鍵點(diǎn)
virtual void cv::Feature2D::detect(InputArray image,
std::vector<KeyPoint> & keypoints,
InputArray mask = noArray()
)
該函數(shù)需要被其他類繼承之后才能使用,即只有在特征點(diǎn)具體的類中才能使用,例如在ORB特征點(diǎn)的ORB類中,可以通過ORB::detect()函數(shù)計(jì)算ORB特征點(diǎn)的關(guān)鍵點(diǎn),在SIFT特征點(diǎn)的SIFT類中,可以通過SIFT::detect()函數(shù)計(jì)算SIFT特征點(diǎn)的關(guān)鍵點(diǎn)
9.2.2 描述子——detectAndCompute()
描述子是用來(lái)唯一描述關(guān)鍵點(diǎn)的一串?dāng)?shù)字,與每個(gè)人的個(gè)人信息類似,通過描述子可以區(qū)分兩個(gè)不同的關(guān)鍵點(diǎn),也可以在不同的圖像中尋找同一個(gè)關(guān)鍵點(diǎn)
//計(jì)算描述子
virtual void cv::Feature2D::compute(InputArray image,
std::vector<KeyPoint> &keypoints, //已經(jīng)在輸入圖像中計(jì)算得到的關(guān)鍵點(diǎn).
OutputArray descriptors
)
//直接計(jì)算關(guān)鍵點(diǎn)和描述子
virtual void cv::Feature2D::detectAndCompute(InputArray image,
InputArray mask, //計(jì)算關(guān)鍵點(diǎn)時(shí)的掩碼圖像.
std::vector<KeyPoint> & keypoints, //計(jì)算得到的關(guān)鍵點(diǎn).
OutputArray descriptors, //每個(gè)關(guān)鍵點(diǎn)對(duì)應(yīng)的描述子.
bool useProvidedKeypoints = false//是否使用己有關(guān)鍵點(diǎn)的標(biāo)識(shí)符。
)
9.2.3 SIFT特征點(diǎn)檢測(cè)——Ptr<SIFT>
SIFT 特征點(diǎn)之所以備受歡迎, 是因?yàn)槠湓诠庹铡⒃肼?、視角、縮放和旋轉(zhuǎn)等干擾下仍然具有良好的穩(wěn)定性.
Sift算子特征點(diǎn)提取https://blog.csdn.net/dcrmg/article/details/52577555
128維特征項(xiàng)量的來(lái)源:https://www.cnblogs.com/wangguchangqing/p/4853263.html
S1FT類變量,用于表明從Features2D類中繼承的函數(shù)是計(jì)算SIFT特征點(diǎn),而不是計(jì)算其他特征點(diǎn)、SIFT 類在xfeatures2d 頭文件和命名空間中,因此在使用時(shí)需要在程序中通過"#include <xfeatures2d.hpp>"包含頭文件,
static Ptr<SIFT> cv::xfeatures2D::SIFT::create(int nfeatures = 0, //計(jì)算SIFT特征點(diǎn)數(shù)目。
int nOctaveLayers = 3,//金字塔中每組的層數(shù).
double contrastTheshold =0.04, //過濾較差特征點(diǎn)的閾值,該參數(shù)值越大,返回的特征點(diǎn)越少.
double edgeThreshold = 10, //過濾邊緣效應(yīng)的閾值, 該參數(shù)值越大,返回的特征點(diǎn)越多.
double sigma = 1.6//"金字塔"第0 層圖像高斯濾波的系數(shù),即上圖的σ0
)
9.2.4 SURF特征點(diǎn)檢測(cè)——Ptr<SURF>
https://blog.csdn.net/zrz0258/article/details/113176528
SURF 特征點(diǎn)直接用方框?yàn)V波器去逼近高斯差分空間
SURF 特征點(diǎn)中, 不同組間圖像的尺寸都是相同的, 但不同組使用的方框?yàn)V波器的尺寸逐漸增大,同一組內(nèi)不同層間使用相同尺寸的濾波器,但是濾波器的模糊系數(shù)逐漸增大.
static Ptr<SURF> cv:xfeatures2d::SURF::create(double hessianThreshold = 100, //SURF關(guān)鍵點(diǎn)檢測(cè)的閾值.
int nOctaves = 4, //構(gòu)建"金字塔"的組數(shù).
int nOctaveLayers = 3, //"金字塔"中每組的層數(shù)
bool extended = false, //是否拓展64維描述子至128維.
int upright = false//是否計(jì)算關(guān)鍵點(diǎn)方向的標(biāo)志.
)
9.2.5 ORB特征點(diǎn)檢測(cè)——Ptr<ORB>
https://www.cnblogs.com/alexme/p/11345701.html
https://blog.csdn.net/yang843061497/article/details/38553765
ORB 特征點(diǎn)以計(jì)算速度快著稱,計(jì)算速度可以達(dá)到SURF 特征點(diǎn)的10 倍、SIFT 特征點(diǎn)的100 倍,
ORB特征點(diǎn)由FAST角點(diǎn)與BRIEF描述子組成,首先通過FAST角點(diǎn)確定圖像中與周圍像索存在明顯像素點(diǎn)作為關(guān)鍵點(diǎn), 之后計(jì)算每個(gè)關(guān)鍵點(diǎn)的BRIEF描述子, 從而唯一確定ORB特征點(diǎn).
具有旋轉(zhuǎn)不變性和尺度不變性
static Ptr<ORB> cv::ORB::create(int nfeatures = 500,
float scaleFactor = 1.2f, //" 金字塔"尺寸縮小的比例
int nlevels = 8, //金字塔"層數(shù)
int edgeThreshold = 31, //邊緣閾值.
int firstLevel = 0, //將原圖像放入"金字塔"中的等級(jí),例如放入第0 層.
int WTA_K = 2, //生成每位描述子時(shí)需要用的像索點(diǎn)數(shù)目.
ORB::ScoreType scoreType = ORB::HARRIS_SCORE, //檢測(cè)關(guān)鍵點(diǎn)時(shí)關(guān)鍵點(diǎn)評(píng)價(jià)方法.
int patchSize = 31, //生成描述子時(shí)關(guān)鍵點(diǎn)周圍鄰域的尺寸.
int fastThreshold = 20//計(jì)算FAST 角點(diǎn)時(shí)像素值差值的閾值.
)
9.3 特征點(diǎn)匹配
特征點(diǎn)匹配就是再不同的圖像中尋找統(tǒng)一物體的同一個(gè)特征點(diǎn)
9.3.1 DescriptorMatcher類介紹
//一對(duì)一的匹配
void cv::DescriptorMatcher::match(InputArray queryDescriptors,//查詢描述子集合.
InputArray trainDescriptors,//訓(xùn)練描述子集合.
std::vector<DMatch> & matches,//兩個(gè)集合描述子匹配結(jié)果.匹配結(jié)果數(shù)目可能小于描述子的數(shù)目
InputArray mask = noArray()//描述子匹配時(shí)的掩碼矩陣,用于指定匹配哪些描述子.
)
DMatch 類型是OpenCV4 中用于存放特征點(diǎn)描述子匹配關(guān)系的類型,類型中存放著兩個(gè)描述子的索引、距離等.
class cv::DMatch{
float distance //兩個(gè)描述子之間的距離
int imgIdx //訓(xùn)練描述子來(lái)自的圖像索引
int queryIdx //查詢描述子集合中的索引
int trainIdx //訓(xùn)練描述子集合中的索引
}
//一對(duì)多的描述子匹配
void cv::DescriptorMatcher::knnMatch(InputArray queryDescriptors,
INputArray trainDescriptors,
std::vector<std::vector<DMatch>> & matches,//即matches[i]中存放的是k個(gè)或者更少的與查詢描述子匹配的訓(xùn)練描述子.
int k,//每個(gè)查詢描述子在訓(xùn)練描述子集合中尋找的最優(yōu)匹配結(jié)果的數(shù)目.————————就是一個(gè)訓(xùn)練描述子集合可以尋找k個(gè)查詢描述子
InputArray mask = noArray(),
bool compactResult = false//輸出匹配結(jié)果數(shù)目是否與查詢描述子數(shù)目相同的選擇標(biāo)志。
)
//匹配所有滿足條件的描述子,即將與查詢描述子距離小于閾值的所有訓(xùn)練描述子都作為匹配點(diǎn)輸出
void cv::DescriptorMatcher::radiusMatch(InputArray queryDescriptors,
INputArray trainDescriptors,
std::vector<std::vector<DMatch>> & matches,
float maxDistance,
InputArray mask = noArray(),
bool compactResult = false
)
與特征點(diǎn)detect()函數(shù)很像,,只有創(chuàng)建了類繼承了虛類后,才能使用match函數(shù)
9.3.2 暴力匹配——BFMatcher.match()
cv::BFMatcher::BFMatcher(int normType = NORM_L2,
bool crossCheck = false
)
暴力匹配會(huì)對(duì)每個(gè)查詢描述子尋找一個(gè)最佳的描述子,但是有時(shí)這種約束條件也會(huì)造成較多錯(cuò)誤匹配,例如某個(gè)特征點(diǎn)只在查詢描述子圖像中出現(xiàn),這種情況在另一幅圖像中不會(huì)存在匹配的特征點(diǎn),但是根據(jù)暴力匹配原理,這個(gè)特征點(diǎn)也會(huì)在另一幅圖像中尋找到與之匹配的特征點(diǎn),造成錯(cuò)誤的匹配.
void cv::drawMatches(InputArray img1,
const std::vector<KeyPoint> & keypoints1,
InputArray img2,
const std::vector<KeyPoint> & keypoints2,
const std::vector<DMatch> & matches1to2,
InputOutputArray outImg,
const Scalar & matchColor = Scalar::all(-1),
const Scalar & singlePointColor = Scalar::all(-1),
const std::vector<char> & matchesMask = std::vector<char>(),
DrawMatchesFlags flags = DrawMathcesFlags::DEFAULT//繪制功能選擇標(biāo)志,
)
9.3.4 FLANN匹配——FlannBaesdMatcher.match()
雖然暴力匹配的原理簡(jiǎn)單, 但是算法的復(fù)雜度高, 當(dāng)遇到特征點(diǎn)數(shù)目較大的情況時(shí), 會(huì)嚴(yán)重影響程序運(yùn)行時(shí)間,因此OpenCV 4 提供了快速最近鄰搜索庫(kù)( Fast Library for Approximate Nearest Neighbors. FLANN) 用于實(shí)現(xiàn)特征點(diǎn)的高效匹配.
cv::FlannBasedMatcher::FlannBasedMatcher(
const Ptr<flann::IndexParams> & indexParams = makePtr<flann::KDTreeIndexParams>(),
const Ptr<flann::SearchParams> & searchParams = makePtr<flann::SearchParams>()//法代遍歷次數(shù)終止條件、一般情況下使用默認(rèn)參數(shù)即可。
)
//使用FLANN方法進(jìn)行匹配時(shí)描述子需要是CV_32F 類型,因此ORB 特征點(diǎn)的描述子變量需要進(jìn)行類型轉(zhuǎn)換后才可以實(shí)現(xiàn)特征點(diǎn)匹配.
k-d樹結(jié)構(gòu):https://blog.csdn.net/u011067360/article/details/23934361
k-means聚類:https://www.cnblogs.com/pinard/p/6164214.html
Hierarchical Clustering層次聚類:https://blog.csdn.net/zhangyonggang886/article/details/53510767
層次聚類(Hierarchical Clustering)是聚類算法的一種,通過計(jì)算不同類別數(shù)據(jù)點(diǎn)間的相似度來(lái)創(chuàng)建一棵有層次的嵌套聚類樹。在聚類樹中,不同類別的原始數(shù)據(jù)點(diǎn)是樹的最低層,樹的頂層是一個(gè)聚類的根節(jié)點(diǎn)。創(chuàng)建聚類樹有自下而上合并和自上而下分裂兩種方法,本篇文章介紹合并方法。
[外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機(jī)制,建議將圖片保存下來(lái)直接上傳(img-fcKn9Lj6-1666660001284)(https://gitee.com/lyz-npu/typora-image/raw/master/img/20161207212645385)]
9.3.5 RANSAC優(yōu)化特征匹配
為了更好地提高特征點(diǎn)匹配精度, 我們可以采用RANSAC 算法。 隨機(jī)抽樣一致算法( RANdom SAmple Consensus )
**RANSAC:**https://blog.csdn.net/robinhjwy/article/details/79174914,,,,,,非常重要的算法
使用了數(shù)據(jù)集內(nèi)隨機(jī)挑選的點(diǎn)擬合出一個(gè)模型,并計(jì)算數(shù)據(jù)集的點(diǎn)是否落在閾值內(nèi)成為內(nèi)點(diǎn),不斷迭代,迭代過程依據(jù)內(nèi)點(diǎn)數(shù)量,計(jì)算找到內(nèi)點(diǎn)最多效果最好的擬合的模型的數(shù)據(jù),,,,,,,其價(jià)值在于去除離群點(diǎn)outlier,然后再使用剩余的inlier內(nèi)點(diǎn),使用xxx擬合算法求出較為準(zhǔn)確的模型
單應(yīng)矩陣?yán)斫猓篽ttps://blog.csdn.net/lyhbkz/article/details/82254893
Mat cv::findHomography(InputArray srcPoints, //原始圖像中特征點(diǎn)的坐標(biāo). CV_32FC2或者vector<Point2f>
InputArray dstPoints, //目標(biāo)圖像中特征點(diǎn)的坐標(biāo).
int method = 0, //計(jì)算單應(yīng)矩陣方法的標(biāo)志
double ransacReprojThreshold = 3, //重投影的最大誤差.選擇RANSAC和RHO時(shí)有用,
OutputArray mask = noArray(), //掩碼矩陣,使用RANSAC 算法時(shí)表示滿足單應(yīng)矩陣的特征點(diǎn).
const int maxIters = 3000, //RANSAC 算法法代的最大次數(shù).
const double confidence = 0.995//置信區(qū)間,取值范圍為0- 1 .
)
通過該函數(shù)優(yōu)化匹配的特征點(diǎn),需要判斷輸出的掩碼矩陣中每一個(gè)元素是否為0. 如果不為0.那么表示該點(diǎn)是成功匹配的特征點(diǎn), 邊而在vector<DMatch>里尋找與之匹配的特征點(diǎn), 將匹配結(jié)果放在新的存放DMatch 類型的向量中.
//RANSAC算法實(shí)現(xiàn)
void ransac(vector<DMatch> matches, vector<KeyPoint> queryKeyPoint, vector<KeyPoint> trainKeyPoint, vector<DMatch> &matches_ransac)
{
//定義保存匹配點(diǎn)對(duì)坐標(biāo)
vector<Point2f> srcPoints(matches.size()), dstPoints(matches.size());
//保存從關(guān)鍵點(diǎn)中提取到的匹配點(diǎn)對(duì)的坐標(biāo)
for (int i = 0; i<matches.size(); i++)
{
srcPoints[i] = queryKeyPoint[matches[i].queryIdx].pt;
dstPoints[i] = trainKeyPoint[matches[i].trainIdx].pt;
}
//匹配點(diǎn)對(duì)進(jìn)行RANSAC過濾
vector<int> inliersMask(srcPoints.size());
//Mat homography;
//homography = findHomography(srcPoints, dstPoints, RANSAC, 5, inliersMask);
findHomography(srcPoints, dstPoints, RANSAC, 5, inliersMask);
//手動(dòng)的保留RANSAC過濾后的匹配點(diǎn)對(duì)
for (int i = 0; i<inliersMask.size(); i++)
if (inliersMask[i])
matches_ransac.push_back(matches[i]);
}
第10章 立體視覺
10.1 單目視覺
10.1.1 單目相機(jī)模型
測(cè)量?jī)?nèi)參系數(shù)是使用攝像頭之前首先要進(jìn)行的步驟
相機(jī)的內(nèi)參矩陣只與相機(jī)的內(nèi)部參數(shù)相關(guān), 因此稱為內(nèi)參矩陣。通過內(nèi)參矩陣可以將相機(jī)坐標(biāo)系下任意的三維坐標(biāo)映射到像素坐標(biāo)系中, 構(gòu)建空間點(diǎn)與像素點(diǎn)之間的映射關(guān)系.
//非齊次坐標(biāo)轉(zhuǎn)換成齊次坐標(biāo)
void cv::convertPointsToHomogeneous(InputArray src, //非齊次坐標(biāo),,vector<Pointnf>或者M(jìn)at
OutputArray dst//其次坐標(biāo),維數(shù)大1
)
//齊次坐標(biāo)轉(zhuǎn)換為非齊次坐標(biāo)
void cv::convertPointsFromHomogeneous(InputArray src,
OutputArray dst
)
10.1.2 標(biāo)定板角點(diǎn)提取
https://zhuanlan.zhihu.com/p/94244568
bool cv::findChessboardCorners(InputArray image,
Size patternSize, //圖像中棋盤內(nèi)角點(diǎn)行數(shù)和列數(shù).
OutputArray corners, //在vector<Point2
ing flags = CALIB_CB_ADAPTIVE_THRESH+CALIB_CB_NORMALIZE_IMAGE//檢測(cè)內(nèi)角點(diǎn)方式的標(biāo)志
)
findChessboardComers()函數(shù)檢測(cè)到的內(nèi)角點(diǎn)坐標(biāo)只是近似值,為了更精確地確定內(nèi)角點(diǎn)坐標(biāo),可以使用我們前面介紹過的計(jì)算亞像素角點(diǎn)坐標(biāo)的comerSubPix()函數(shù).此外. OpenCV4也有專用于提高標(biāo)定板內(nèi)角點(diǎn)坐標(biāo)精度的find4QuadConerSubpix()函數(shù)。
bool cv::find4QuadConerSubpix(InputArray img,
InputOutputArray corners, //待優(yōu)化的內(nèi)角點(diǎn)坐標(biāo)
Size region_size//優(yōu)化坐標(biāo)時(shí)考慮的鄰域范圍。
)
//對(duì)于圓形標(biāo)定板
bool cv::findCirclesGrid(InputArray image,
Size patternSize, //圖像中每行和每列圓形的數(shù)目.
OutputArray centers,
int flags = CALIB_CB_SYMMETRIC_GRID, //檢測(cè)圓心的操作標(biāo)志
const Ptr<FeatureDetector> & blobDetector = SimpleBlobDetector::create()//在淺色背景中尋找黑色圓形斑點(diǎn)的特征探測(cè)器.
)
//繪制角點(diǎn)位置
void cv::drawChessboardCorners(InputArray image, //需要繪制角點(diǎn)的目標(biāo)圖像,必須是CU_8U 類型的彩色圖像.
Size patternSize, //標(biāo)定板每行和每列角點(diǎn)的數(shù)目.
InputArray corners, //檢測(cè)到的角點(diǎn)坐標(biāo)數(shù)組.
bool patternWasFound//繪制角點(diǎn)樣式的標(biāo)志,用于顯示是否找到完整的標(biāo)定板.
)
10.1.3 單目相機(jī)標(biāo)定
[外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機(jī)制,建議將圖片保存下來(lái)直接上傳(img-CpCz6Uum-1666660001286)(https://gitee.com/lyz-npu/typora-image/raw/master/img/%E5%9B%BE10-6.png)]
相機(jī)標(biāo)定主要是計(jì)算機(jī)相機(jī)內(nèi)參矩陣和相機(jī)畸變的5個(gè)系數(shù)
double cv::calibrateCamera(InputArrayOfArrays objectPoints,
InputArrayOfArrays imagePoints,
Size imageSize,
InputOutputArray cameraMatrix,
InputOutputArray distCoeffs,
OutputArrayOfArrays rvecs,
OutputArrayOfArrays tvecs,
int flags = 0,
TermCriteria criteria = TermCriteria(TermCriteria::COUNT+TermCriteria::EPS, 30, DBL_EPSILON))
10.1.4 單目相機(jī)校正
在得到相機(jī)的畸變系數(shù)矩陣后,可以根據(jù)畸變模型將圖像中的畸變?nèi)サ簦衫碚撋喜缓兊膱D像,
一:initUndistortRectifyMap()——remap()
二:unidistort()
void cv::initUndistoritRectifyMap(InputArray cameraMatrix,
InputArray distCoeffs,
InputArray R,
InputArray newCameraMatrix,
Size size,
int mltype,
OutputArray map1,
OutputArray map2
)
void cv::remap(InputArray src,
OutputArray dst,
InputArray map1,
InputArray map2,
int interpolation,
int borderMode = BORDER_CONSTANT,
const Scalar & borderValue = Scalar()
)
//直接矯正
void cv::undistort(InputArray src,
OutputArray dst,
InputArray cameraMatrix,
InputArray distCoeffs,
InputArray newCameraMatrix = noArray()
)
10.1.5 單目投影
單目投影是指根據(jù)相機(jī)的成像模型計(jì)算空間中三維坐標(biāo)點(diǎn)在圖像二維平面中坐標(biāo)的過程.OpenCV4 中提供了projectPoints()函數(shù)用于計(jì)算世界坐標(biāo)系中的三維點(diǎn)投影到像素坐標(biāo)系中的二維坐標(biāo)
void cv::projectPoints(InputArray objectPoints,
InputArray rvec,
InputArray tvec,
InputArray cameraMatrix,
InputArray disCoeffs,
OutputArray imagePoints,
OutputArray jacobian = noArray(),
double aspectRatio = 0
)
10.1.6 單目位姿估計(jì)
bool cv::solvePnP(InputArray objectPoints,
InputArray imagePoints,
InputArray cameraMatrix,
InputArray disCoeffs,
OutputArray rvec,
OutputArray tvec,
bool useExtrinsicGuess = false,
int flags = SOLVEPNP_ITERATIVE
)
第11章 視頻分析
.本章中將重點(diǎn)介紹如何檢測(cè)視頻中移動(dòng)的物體,并對(duì)移動(dòng)物體進(jìn)行跟蹤, 主要的方法有差值法、均值遷移法和光流法.
11.1 插值法檢測(cè)移動(dòng)物體
//用于計(jì)算兩個(gè)圖像差值的絕對(duì)值
void cv::absdiff(InputArray src1,
InputArray src2,
OutputArray dst
)
11.2 均值遷移法目標(biāo)跟蹤
11.2.1 均值遷移法實(shí)現(xiàn)的目標(biāo)跟蹤
均值遷移法能夠?qū)崿F(xiàn)目標(biāo)跟蹤,其原理是首先計(jì)算給定區(qū)域內(nèi)的均值, 如果均值不符合最優(yōu)值條件, 那么將區(qū)域向靠近最優(yōu)條件的方向移動(dòng), 經(jīng)過不斷地法代來(lái)找到目標(biāo)區(qū)域。
均值遷移法又可以稱為爬山算法。
int cv::meanShift(InputArray probImage, //目標(biāo)區(qū)域的直方圖的反向投影
Rect & window, //初始搜索窗口和搜索結(jié)束時(shí)的窗口
TermCriteria criteria//迭代停止算法
)
//鼠標(biāo)選取目標(biāo)區(qū)域
Rect cv::selectROI(const String & windowName,
InputArray img, //選擇ROI 區(qū)域的圖像.
bool showCrosshair = true, //是否顯示選擇矩形中心的十字準(zhǔn)線的標(biāo)志.
bool fromCenter = false//ROI 矩形區(qū)域中心位置標(biāo)志.當(dāng)該參數(shù)值為true 時(shí),鼠標(biāo)當(dāng)前坐標(biāo)為ROI 矩形的中心,當(dāng)該參數(shù)值為false時(shí),鼠標(biāo)當(dāng)前坐標(biāo)為ROI 矩形區(qū)域的左上角.
)
11.2.2 自適應(yīng)均值遷移法實(shí)現(xiàn)的目標(biāo)跟蹤
自適應(yīng)均值遷移法對(duì)均值遷移法進(jìn)行了改進(jìn),使得可以根據(jù)跟蹤對(duì)象的大小自動(dòng)調(diào)整搜索窗口的大小。除此之外,改進(jìn)的均值遷移法不但能返回跟蹤目標(biāo)的位置, 而且能夠返回角度信息.
RotateRect cv::Camshift(InputArray probImage,
Rect & window,
TermCriteria criteria
)
11.3 光流法目標(biāo)跟蹤
光流是空間運(yùn)動(dòng)物體在成像圖像平面上投影的每個(gè)像素移動(dòng)的瞬時(shí)速度, 在較短的時(shí)間間隔內(nèi)可以等同于像素點(diǎn)的位移。在忽略光照變化影響的前提下,光流的產(chǎn)生主要是由于場(chǎng)景中目標(biāo)的移動(dòng)、相機(jī)的移動(dòng)或者兩者的共同運(yùn)動(dòng)。光流表示了圖像的變化,由于它包含了目標(biāo)運(yùn)動(dòng)的信息, 因此可被觀察者用來(lái)確定目標(biāo)的運(yùn)動(dòng)情況,進(jìn)而實(shí)現(xiàn)目標(biāo)跟蹤.
光流法要求像素移動(dòng)較小距離,但是有時(shí)得到的連續(xù)圖像中像素的移動(dòng)距離較大,此時(shí)需要采用圖像"金字塔" 來(lái)解決大尺度移動(dòng)的問題.
根據(jù)計(jì)算光流速度的像素點(diǎn)數(shù)目, 光流法可以分為稠密光流法(所有像素都要使用)和稀疏光流法.(只使用部分像素點(diǎn))
11.3.1 Farneback多項(xiàng)式擴(kuò)展算法
void cv::calcOpticalFlowFarnback(InputArray prev,
InputArray next,
InputOutputArray flow,
double pyr_scale,
int levels,//金字塔層數(shù)
int winsize, //均值窗口的尺寸
int iterations,
int ploy_n, //在每個(gè)像素中找到多項(xiàng)式展開的像素鄰域的大小.
double poly_sigma, //高斯標(biāo)準(zhǔn)差.
int flags//計(jì)算方法標(biāo)志. 當(dāng)該參數(shù)值為OPTFLOW_USE_INITIAL_FLOW時(shí), 表示使用輸入流作為初始流的近似值:當(dāng)該參數(shù)值為OPTFLOW_FARNEBACK_GAUSSIAN時(shí),表示使用離斯濾波器代替方框?yàn)V波器進(jìn)行光流估計(jì).
)
//計(jì)算二維向量方向和模長(zhǎng)
void cv::cartToPolar(InputArray x,
InputArray y,
OutputArray magnitude,
OutputArray angle,
bool angleInDegrees = false
)
稠密光流法常用于相機(jī)固定的視頻數(shù)據(jù)的目標(biāo)跟蹤.
11.3.2 基于LK稀疏光流法的跟蹤
https://blog.csdn.net/a_31415926/article/details/50515835
//TermCriteria終止條件
cv::TermCriteria::TermCriteria(int type, //判定迭代終止的條件類型,要么只按count算,要么只按EPS算,要么兩個(gè)條件達(dá)到一個(gè)就算結(jié)束
int maxCount, //具體的最大迭代的次數(shù)是多少
double epsilon//具體epsilon值是多少
)
/*
COUNT:按最大迭代次數(shù)算
EPS:就是epsilon,按達(dá)到某個(gè)收斂的閾值作為求解結(jié)束標(biāo)志
COUNT + EPS:要么達(dá)到了最大迭代次數(shù),要么按達(dá)到某個(gè)閾值作為收斂結(jié)束條件。
*/
void cv::calcOpticalFlowPyrLK(InputArray prevImg,
InputArray nextImg,
InputArray prePts,
InputOutputArray nextPts,
OutputArray status,
OutputArray err,
Size winSize = Size(21,21),
int maxLevel = 3,
TermCriteria criteria = TermCriteria(TermCriteria::COUNT+TermCriteria::EPS,30,0.01)
int flags = 0,
double minEigThreshold = 1e-4
)
calcOpticalFlowPyrLK()函數(shù)需要人為輸入圖像中稀疏光流點(diǎn)的坐標(biāo),通常情況下可以檢測(cè)圖像中的特征點(diǎn)或者角點(diǎn),將特征點(diǎn)或者角點(diǎn)的坐標(biāo)作為初始稀疏光流點(diǎn)的坐標(biāo)輸入文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-471201.html
最大的問題:角點(diǎn)數(shù)目越來(lái)越少,因此需要時(shí)刻統(tǒng)計(jì)跟蹤的焦點(diǎn)數(shù)目,當(dāng)角點(diǎn)數(shù)目小于一定閾值時(shí),需要再次檢測(cè)角點(diǎn),以增加角點(diǎn)數(shù)目。如果圖像中有不運(yùn)動(dòng)的物體,那么每幅圖像中都能檢測(cè)到這些物體上的角點(diǎn),從而使得角點(diǎn)數(shù)目一直高于閾值,然而這些固定的特征點(diǎn)不是我們需要的,因此需要判斷角點(diǎn)在兩幀圖像中是否移動(dòng),刪除不移動(dòng)的角點(diǎn),進(jìn)而跟蹤移動(dòng)的物體。文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-471201.html
到了這里,關(guān)于OpenCV4 快速入門 (學(xué)習(xí)筆記 全)的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!