opencv 入門學(xué)習(xí)筆記(C++)
4.1.2 Mat 結(jié)構(gòu)的使用
? 關(guān)于Mat類,我們首先要知道的是:
(1)不必再手動(dòng)為其開辟空間。
(2)不必再在不需要時(shí)立即將空間釋放
總結(jié):
- OpenCV 函數(shù)中輸出圖像的內(nèi)存分配是自動(dòng)完成的
- 使用opencv的c++結(jié)構(gòu)時(shí)不需要考慮內(nèi)存釋放的問日
- 賦值運(yùn)算符和拷貝構(gòu)造函數(shù)
Mat B(A)
只復(fù)制信息頭 - 使用函數(shù)
clone()
或者copyTo()
來復(fù)制一副圖像的矩陣
4.1.3 像素值的存儲(chǔ)方法
- RGB/RGBA
- YCrCb在JPEG中廣泛使用
- HSV和HLS 把顏色分解成色調(diào)、飽和度和亮度。
4.1.4 顯式創(chuàng)建Mat對(duì)象的七種方式
-
【方法一】使用Mat()構(gòu)造函數(shù)
Mat M(2,2,CV_8UC3,Scalar(0,0,255)); //CV_[位數(shù)][帶符號(hào)與否][類型前綴]C[通道數(shù)] //預(yù)先定義的通道數(shù)可以多達(dá)4個(gè)
-
【方法二】在C/C++中通過構(gòu)造函數(shù)進(jìn)行初始化
int sz[3]={2,2,2}; Mat L(3,sz,cv_8UC,Scalar::all(0)); //上面的例子演示了如何創(chuàng)建一個(gè)超過兩維的矩陣:指定維數(shù),然后傳遞給一個(gè)指向數(shù)組的指針,這個(gè)數(shù)組包含每個(gè)維度的尺寸;后續(xù)兩個(gè)參數(shù)與方法一中的相同
-
【方法三】為已存在的IplImage指針創(chuàng)建信息頭
IplImage* img = cvLoadImage("1.jpg",1); Mat mtx(img);//轉(zhuǎn)換 IplImage*->Mat
-
【方法四】利用Create()函數(shù)
M.create(4,4,CV_8UC(2)); //需要注意的是,此方法不能為矩陣設(shè)初值,只是在該百年尺寸時(shí)重新為矩陣數(shù)據(jù)開辟內(nèi)存而已。
-
【方法六】對(duì)小矩陣使用逗號(hào)分隔式初始化函數(shù)
Mat C = (Mat_<double>(3,3)<<0,-1,0,-1,5,-1,0,-1,0); cout<<"C = "<<endl<<" "<C<<endl<<endl;
-
【方法七】為已存在的對(duì)象創(chuàng)建新信息頭
Mat RowClone = C.row(1).clone(); cout<<"RowClone = "<<endl<<" "<<RowClone<<endl<<endl; //方法七為使用成員函數(shù)clone()或者copyTo()為一個(gè)已存在的Mat對(duì)象創(chuàng)建一個(gè)新的信息頭
cvtColor
- cvtColor的功能是把圖像從一個(gè)色彩空間轉(zhuǎn)換到另外一個(gè)色彩空間,有三個(gè)參數(shù),第一個(gè)參數(shù)表示源圖像,第二個(gè)參數(shù)表示色彩空間轉(zhuǎn)換之后的圖像,第三個(gè)參數(shù)表示源和目標(biāo)色彩空間如:COLOR_BGR2HLS、COLOR_BGR2GRAY等
- cvtColor(image,gray_image,COLOR_BGR2GRAY);
imwrite
- 保存文件到指定的目錄途徑
- 只有8位,16位的PNG/JPG/Tiff文件格式而且是單通道或者三通道的BGR圖像 才可以通過這種方式保存
- 保存PNG格式的時(shí)候可以保存透明通道的圖片(RGBA)
- 可以指定壓縮參數(shù)
矩陣的掩碼操作
-
獲取圖像像素指針
-
掩碼操作解釋
-
代碼演示
像素處理范圍 saturate_cast<uchar>
這個(gè)函數(shù)的功能是確保RGB值得范圍在0~255之間
掩碼操作實(shí)現(xiàn)圖像對(duì)比度的調(diào)整 I ( i , j ) = 5 ? I ( i , j ) ? [ I ( i ? 1 , j ) + I ( i + 1 , j ) + I ( i , j ? 1 ) , I ( i , j + 1 ) ] I(i,j)=5*I(i,j)-[I(i-1,j)+I(i+1,j)+I(i,j-1),I(i,j+1)] I(i,j)=5?I(i,j)?[I(i?1,j)+I(i+1,j)+I(i,j?1),I(i,j+1)]
#include<iostream>
#include<opencv2/opencv.hpp>
using namespace cv;
int main(int argc,char** argv)
{
Mat src = imread("test.jpg");
Mat dst;
if (!src.data)//如果圖像為空,返回 error
{
std::cout << "can't load this img" <<std::endl;
return -1;
}
namedWindow("input image", WINDOW_AUTOSIZE);
imshow("input image", src);
//RGB圖像按列存儲(chǔ),注意存儲(chǔ)順序是BGR
int cols = (src.cols - 1) * src.channels();
//cols 應(yīng)該用width更為妥當(dāng),-1是因?yàn)橐粘鲆涣邢袼匾苑罒o定義
int offsetx = src.channels();
//偏移量是通道數(shù)
int rows = src.rows;
//RGB按照列存儲(chǔ),與行數(shù)無關(guān)
dst = Mat::zeros(src.size(), src.type());
//重新創(chuàng)建一個(gè)mat矩陣用于存放輸出的圖像
//src.copyTo(dst);也可
for (int row = 1; row < rows - 1; row++) {
const uchar* previous = src.ptr<uchar>(row - 1);
const uchar* current = src.ptr<uchar>(row);
const uchar* next = src.ptr<uchar>(row +1);
//創(chuàng)建核的數(shù)組
uchar* output = dst.ptr<uchar>(row);
for (int col = offsetx; col < cols; col++) {
output[col] = saturate_cast<uchar>(5 * current[col] - (current[col - offsetx] + current[col + offsetx] + previous[col] + next[col]));
}
}
namedWindow("constrast image", WINDOW_AUTOSIZE);
imshow("constrast image", dst);
waitKey(0);
return 0;
}
函數(shù)filter 2D實(shí)現(xiàn)掩碼操作
可直接用opencv的API做掩碼操作來提高對(duì)比度
filter2D(src,dst,src.depth(),kernel):其中src與dst是Mat類型變量,src.depth表示位圖深度,有32、24、8等,直接寫-1表示與輸入圖深度一致。(filter濾波器)
定義掩碼: Mat lernel = (Mat_<char>(3,3)<<0,-1,0,-1,5,-1,0,-1,0);
#include <opencv2/opencv.hpp>
#include<iostream>
#include<math.h>
#include<stdlib.h>
using namespace cv;
int main(int argc, char** argv) {
Mat src = imread("C:/Users/admin/Desktop/lenna.png");//讀入圖片
Mat dst;//提高對(duì)比度之后的圖片矩陣
if (src.empty()){ // 特判
printf("cannot see\n");
return -1;
}
namedWindow("opencv setup1", CV_WINDOW_AUTOSIZE);
imshow("opencv setup1", src);
Mat kernel = (Mat_<char>(3, 3) << 0, -1, 0, -1, 5, -1, 0, -1, 0);//定義掩碼規(guī)則
filter2D(src, dst, src.depth()/*-1*/, kernel);
//輸出目標(biāo)圖像
namedWindow("opencv setup2", CV_WINDOW_AUTOSIZE);
imshow("opencv setup2", dst);
waitKey(0);
system("pause"); //以便在退出程序前調(diào)用系統(tǒng)的暫停命令暫停命令行
//爽得很??!
}
Mat
mat對(duì)象的使用:
- 部分復(fù)制:一般情況下只會(huì)復(fù)制mat對(duì)象的頭和指針部分,不會(huì)復(fù)制數(shù)據(jù)部分
- 完全復(fù)制:使用如下兩個(gè)API:
Mat F=A.clone();
//或者
Mat G;
A.copyTo(G);
函數(shù)用法:
#include<iostream>
#include<opencv2/opencv.hpp>
using namespace cv;
using namespace std;
int main(int argc,char** argv)
{
Mat src = imread("test.jpg");
if (!src.data)
{
std::cout << "can't load this img" <<std::endl;
return -1;
}
//namedWindow("input image", WINDOW_AUTOSIZE);
//imshow("input image", src);
/*Mat dst;
dst = Mat(src.size(), src.type());
dst = Scalar(255, 0, 255);
namedWindow("out put", WINDOW_AUTOSIZE);
imshow("out put", dst);*/
/*Mat dst = src.clone();
namedWindow("out put", WINDOW_AUTOSIZE);
imshow("out put", dst);*/
//Mat dst;
//cvtColor(src, dst, COLOR_BGR2GRAY);
//namedWindow("out put", WINDOW_AUTOSIZE);
//imshow("out put", dst);
//cout << src.channels() << endl;//通道數(shù)目
//cout << dst.channels() << endl;
//int cols = dst.cols;//全部的列
//int rows = dst.rows;//全部的行
//const uchar* firstRow = dst.ptr<uchar>(0);
//printf("%d", *firstRow);
Mat M(3, 3, CV_8UC3, Scalar(0, 0, 255));
Mat m1;
m1.create(src.size(),src.type());
m1=Scalar(0,0,255);
Mat m2 = Mat::zeros(src.size(),src.type());
Mat m2 = Mat::zeros(2,2,CV_8UC1);
Mat m2 = Mat::eye(2,2,CV_8UC1);
cout << M << endl;
waitKey(0);
return 0;
}
圖片操作
讀寫圖像
-
imread
可以指定加載為灰度圖像或者RGB圖像 -
imwrite
保存圖像文件,類型由擴(kuò)展名決定
讀寫像素(pixel)
- 讀取一個(gè)GRAY像素點(diǎn)的像素值(CV_8UC1)
Scalar intensity = img.at<uchar>(y,x);
或者
Scalar intensity = img.at<uchar>(Point(y,x));
- 讀一個(gè)RGB像素點(diǎn)的像素值
Vec3f intensity = img.at<Vec3f>(y,x);
float blue = intensity.val[0];
float green = intensity.val[1];
float red = intensity.val[2];
-
獲取像素的練習(xí)
#include <opencv2/core/utils/logger.hpp> #include<opencv2/opencv.hpp> #include<iostream> using namespace cv; using namespace std; int main(int argc, char** argv) { cv::utils::logging::setLogLevel(utils::logging::LOG_LEVEL_SILENT);//不再輸出日志 Mat src = imread("test.jpg"); if (!src.data) { cout << "could not load this image..." << endl; } char output_origin[] = "origin demo"; char output_gray[] = "gray demo"; namedWindow(output_origin, WINDOW_AUTOSIZE); imshow(output_origin, src); Mat dst; cvtColor(src, dst, COLOR_BGR2GRAY); namedWindow(output_gray, WINDOW_AUTOSIZE); imshow(output_gray, dst); dst.create(src.size(), src.type()); for (int row = 0; row < dst.rows; row++) for (int col = 0; col <dst.cols; col++) { //int gray = dst.at<uchar>(row,col); //dst.at<uchar>(row,col) = 255 - gray; int b = src.at<Vec3b>(row, col)[0]; int g = src.at<Vec3b>(row, col)[1]; int r = src.at<Vec3b>(row, col)[2]; dst.at<Vec3b>(row, col)[0]=255-b; dst.at<Vec3b>(row, col)[1] = 255 -g; dst.at<Vec3b>(row, col)[2] = 255 - r; //另一種轉(zhuǎn)換成灰度圖像的方法: //dst.at<uchar>(row,col)=max(r,max(b,g)); //dst.at<uchar>(row,col)=min(r,min(b,g)); } namedWindow("anti color demo", WINDOW_AUTOSIZE); imshow("anti color demo", dst); waitKey(0); } //已經(jīng)有封裝好的API干嘛要練習(xí)這個(gè) bitwise_not(src,dst);
Vec3b與Vec3f
-
Vec3b對(duì)應(yīng)的三通道的順序是blue、green、red的uchar類型的數(shù)據(jù)
-
Vec3f對(duì)應(yīng)三通道的float類型數(shù)據(jù)
-
把CV_8UC1轉(zhuǎn)換到CV32F1實(shí)現(xiàn)如下:
src.convertTo(dst,CV_32F);
圖像混合
理論-線性混合操作
g ( x ) = ( 1 ? α ) f 0 ( x ) + α f 1 ( x ) 其中 α 的取值范圍為 0 到 1 之間 g(x)=(1-\alpha)f_0(x)+\alpha f_1(x)\\其中\(zhòng)alpha的取值范圍為0到1之間 g(x)=(1?α)f0?(x)+αf1?(x)其中α的取值范圍為0到1之間
相關(guān)的API(addWeighted):
addWeight(src1,alpha,src2,beta,gamma(校驗(yàn)值),dst);
d s t ( I ) = s a t u r a t e _ c a s t ( s r c 1 ( I ) ? a l p h a + s r c 2 ( I ) ? b e t a + g a m m a ) dst(I)=saturate\_cast(src1(I)*alpha+src2(I)*beta+gamma) dst(I)=saturate_cast(src1(I)?alpha+src2(I)?beta+gamma)
改變圖像的亮度和對(duì)比度
理論:
-
圖像變換可以看作如下:
- 像素變換——點(diǎn)操作
- 鄰域操作——區(qū)域
調(diào)整圖像亮度屬于像素變換-點(diǎn)操作 g ( i , j ) = α f ( i , j ) + β g(i,j)=\alpha f(i,j)+\beta g(i,j)=αf(i,j)+β其中 α \alpha α>0, β \beta β是增益變量
#include<opencv2/opencv.hpp>
#include<iostream>
using namespace cv;
using namespace std;
int main(int argc, char** argv)
{
Mat src = imread("test.jpg");
char input_win[] = "src image";
if (!src.data) {
cout << "could not load the image..." << endl;
}
namedWindow(input_win, WINDOW_AUTOSIZE);
imshow(input_win, src);
Mat dst;
int height = src.rows;
int width = src.cols;
dst = Mat::zeros(src.size(), src.type());
double alpha = 1.5;
double beta = 10;
src.convertTo(src, CV_32F);
for (int row = 0; row < height; row++)
for (int col = 0; col < width; col++) {
if (src.channels() 3) {
float b = src.at<Vec3f>(row, col)[0];
float g = src.at<Vec3f>(row, col)[1];
float r = src.at<Vec3f>(row, col)[2];
dst.at<Vec3b>(row, col)[0] = saturate_cast<uchar>(b * alpha + beta);
dst.at<Vec3b>(row, col)[1] = saturate_cast<uchar>(g * alpha + beta);
dst.at<Vec3b>(row, col)[2] = saturate_cast<uchar>(r * alpha + beta);
}
else if (src.channels() 1) {
int v = src.at<uchar>(row, col);
dst.at<uchar>(row, col) = saturate_cast<uchar>(v * alpha + beta);
}
//else
}
char output_title[] = "contrast and brightness change demo";
namedWindow(output_title, WINDOW_AUTOSIZE);
imshow(output_title, dst);
waitKey(0);
}
繪制圖像和文字
-
使用Point與Scalar
-
? Point表示2D平面上一個(gè)點(diǎn)(x,y)
Point p; p.x=10;p.y=8;//p=Point(10,8);
-
Scalar表示四個(gè)元素的向量
Scalar(a,b,c);//a=bule,b=green,c=red表示RGB三個(gè)通道
-
-
在圖片中插入線、矩形、橢圓、圓、多邊形。
#include<opencv2/opencv.hpp> #include<iostream> using namespace cv; using namespace std; Mat src; void lines(); void myRectangle(); void myEllipse(); void myCircle(); void myPolygon(); int main(int argc, char** argv) { src = imread("test.jpg"); if (!src.data) { cout << "could not liad this image.." << endl; return -1; } lines(); myRectangle(); myEllipse(); myCircle(); myPolygon(); namedWindow("line_demo", WINDOW_AUTOSIZE); imshow("line_demo", src); waitKey(0); } void lines() {//線 Point p1 = Point(20, 30); Point p2 = Point(200, 300); Scalar color =Scalar(0, 0, 255); line(src, p1, p2, color, 5, LINE_AA); } void myRectangle() {//矩形 Rect rect = Rect(200, 100, 300, 300); Scalar color = Scalar(0, 0, 255); rectangle(src, rect, color,1, LINE_8); } void myEllipse() {//橢圓 Scalar color = Scalar(175, 145, 76); ellipse(src, Point(src.cols/ 2, src.rows / 2), Size(src.rows / 4, src.cols / 8), 0, 0, 360, color, 1, LINE_AA); } void myCircle() {//圓 Scalar color = Scalar(175, 146, 76); circle(src, Point(src.cols / 2, src.rows / 2), src.cols / 4, color, 1, LINE_AA); } void myPolygon() {//多邊形 Scalar color = Scalar(0, 0, 255); Point pts [1][6]; pts[0][0] = Point(100, 100); pts[0][1] = Point(100, 200); pts[0][2] = Point(200, 200); pts[0][3] = Point(200, 100); pts[0][4] = Point(150, 50); pts[0][5] = Point(100, 100); const Point* ppts[] = { pts[0] }; int npt[] = { 6 }; fillPoly(src, ppts, npt, 1, color, 8); }
-
在圖像中插入文字。
putText();
#include<opencv2/opencv.hpp>
#include<iostream>
using namespace cv;
using namespace std;
Mat src;
int main(int argc, char** argv)
{
src = imread("test.jpg");
if (!src.data) {
cout << "could not liad this image.." << endl;
return -1;
}
putText(src, "hello opencv", Point(src.cols / 2-60, src.rows / 2), FONT_HERSHEY_COMPLEX, 0.8, Scalar(150, 12, 255), 1, 8);
namedWindow("line_demo", WINDOW_AUTOSIZE);
imshow("line_demo", src);
waitKey(0);
}
//隨機(jī)生成顏色
-
隨機(jī)生成線條
#include<opencv2/opencv.hpp> #include<iostream> using namespace cv; using namespace std; Mat src; void RandowLineDemo(); int main(int argc, char** argv) { src = imread("test.jpg"); if (!src.data) { cout << "could not liad this image.." << endl; return -1; } //putText(src, "hello opencv", Point(src.cols / 2-60, src.rows / 2), FONT_HERSHEY_COMPLEX, 0.8, Scalar(150, 12, 255), 1, 8); RandowLineDemo(); //namedWindow("line_demo", WINDOW_AUTOSIZE); //imshow("line_demo", src); waitKey(0); } void RandowLineDemo() { RNG rng(12346);//RNG 變量(種子數(shù)) Mat bg = Mat::zeros(src.size(), src.type()); for (int i = 0; i < 10000; i++) { line(bg, Point(rng.uniform(0, src.cols), rng.uniform(0, src.rows)), Point(rng.uniform(0, src.cols), rng.uniform(0, src.rows)), Scalar(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255)), 1, LINE_AA); //rng.uniform(范圍); if (waitKey(50) > 0) { break; } imshow("RandowLineDemo", bg); } }
模糊圖像
模糊原理
- Smooth/Blur是圖像處理中最簡單和常用的操作之一
- 使用該操作的原因之一就是為了給圖像預(yù)處理的時(shí)候減低噪聲
- 使用Smooth/Blur操作背后是數(shù)學(xué)的卷積計(jì)算 g ( i , j ) = ∑ k j f ( i + k , j + l ) h ( k , l ) g(i,j)=\sum\limits_{kj}f(i+k,j+l)h(k,l) g(i,j)=kj∑?f(i+k,j+l)h(k,l)
- 通常這些卷積算子計(jì)算都是線性操作,所以又叫線性濾波
- 歸一化盒子濾波(均值濾波)和高斯濾波(權(quán)重不一樣,會(huì)保留一些原有像素特質(zhì))
- 相關(guān)API
- 均值模糊:
blur(Mat src,Mat dst,Size(xradius,yradius),Point(-1,-1));
- 高斯模糊:
GaussianBlur(Mat src,Mat dst,Size(11,11),sigmax,sigmay);
其中Size(x,y)必須是正數(shù)而且是奇數(shù)。
- 均值模糊:
均值模糊
#include<opencv2/opencv.hpp>
#include<iostream>
using namespace std;
using namespace cv;
int main() {
Mat src, dst;
src = imread("test.jpg");
if (src.empty()) {
cout << "could not load this image..." << endl;
return -1;
}
namedWindow("originalDemo", WINDOW_AUTOSIZE);
imshow("originalDemo", src);
blur(src, dst, Size(3, 3), Point(-1, -1));
namedWindow("blur3Demo", WINDOW_AUTOSIZE);
imshow("blur3Demo", dst);
blur(src, dst, Size(5, 5), Point(-1, -1));
namedWindow("blur5Demo", WINDOW_AUTOSIZE);
imshow("blur5Demo", dst);
waitKey(0);
}
高斯模糊
#include<opencv2/opencv.hpp>
#include<iostream>
using namespace std;
using namespace cv;
int main() {
Mat src, dst;
src = imread("test.jpg");
if (src.empty()) {
cout << "could not load this image..." << endl;
return -1;
}
namedWindow("originalDemo", WINDOW_AUTOSIZE);
imshow("originalDemo", src);
GaussianBlur(src, dst, Size(11, 11), 5, 5);
namedWindow("GaussianBlurDemo", WINDOW_AUTOSIZE);
imshow("GaussianBlurDemo", dst);
//blur(src, dst, Size(5, 5), Point(-1, -1));
//namedWindow("blur5Demo", WINDOW_AUTOSIZE);
//imshow("blur5Demo", dst);
waitKey(0);
}
- 中值濾波
- 統(tǒng)計(jì)排序?yàn)V波器
- 中值對(duì)椒鹽噪聲有很好的抑制作用
- 高斯雙邊濾波
- 均值模糊無法克服邊緣像素信息丟失缺陷。原因是均值濾波是基于平均權(quán)重
- 高斯模糊部分克服了該缺陷,但是無法完全避免,因?yàn)闆]有考慮像素值的不同
- 高斯雙邊模糊-是邊緣保留的濾波方法,避免看邊緣信息丟失,保留了圖像輪廓不變,所以雙邊模糊需要輸入兩個(gè),一個(gè)空域kernel(空間),一個(gè)值域kernel(值)
- 相關(guān)的API
- 中值模糊
medianBlur(Mat src,Mat dest,ksize)
- 雙邊模糊
bilateralFilter(src,dest,d=15,150,3);
- 15-計(jì)算的半徑,半徑之內(nèi)的像素都會(huì)被納入計(jì)算,如果提供-1則會(huì)根據(jù)sigma space參數(shù)取值
- 150-sigma color 決定多少差值之內(nèi)的像素會(huì)被計(jì)算
- 3-sigma space如果d的值大于0則聲明無效,否則根據(jù)它來計(jì)算d值
- 中值模糊的ksize大小必須大于一而且必須是奇數(shù)
- 中值模糊
#include<opencv2/opencv.hpp>
#include<iostream>
using namespace std;
using namespace cv;
int main() {
Mat src, dst;
src = imread("cat1.jpg");
if (src.empty()) {
cout << "could not load this image..." << endl;
return -1;
}
namedWindow("originalDemo", WINDOW_AUTOSIZE);
imshow("originalDemo", src);
//GaussianBlur(src, dst, Size(11, 11), 5, 5);
//medianBlur(src, dst, 3);
bilateralFilter(src, dst, 15, 150, 3);
namedWindow("BilateralFilterDemo", WINDOW_AUTOSIZE);
imshow("BilateralFilterDemo", dst);
waitKey(0);
}
膨脹與腐蝕
形態(tài)學(xué)操作-膨脹
- 圖像形態(tài)學(xué)操作-基于形狀的一系列圖像處理操作的合集,主要是基于集合論基礎(chǔ)上的形態(tài)學(xué)數(shù)學(xué)
- 形態(tài)學(xué)有四個(gè)基本操作:膨脹、腐蝕、開、閉
- 跟卷積操作類似,假設(shè)有圖像A和結(jié)構(gòu)元素B,結(jié)構(gòu)元素B在A上面移動(dòng),其中B定義其中心為錨點(diǎn)計(jì)算B覆蓋下A的最大像素值(腐蝕是最小像素值)用來替換錨點(diǎn)的像素,其中B作為結(jié)構(gòu)體可以是任意形狀
相關(guān)API
-
getStructuringElement(int shape,Size ksize,Point anchor)
- 形狀(MORPH_RECT/MORPH_CROSS/MORPH_ELLIPSE)
- 大小(奇數(shù))
- 錨點(diǎn) 默認(rèn)是Point(-1,-1)意思就是中心像素
dilate(src,dst,kernel)
erode(src,dst,kernel)
動(dòng)態(tài)調(diào)整結(jié)構(gòu)元素大小
-
createTrackbar(constString&trackbarname,winname,int*value,int count,Trackbarcallback func,void* userdata=0)
- 其中最主要的是callback函數(shù)功能。如果設(shè)置為NULL就是說只有值update,但是不會(huì)調(diào)用callback的函數(shù)
#include<opencv2/opencv.hpp>
#include<iostream>
#include<opencv2/imgproc/imgproc.hpp>
using namespace std;
using namespace cv;
void CallBack_Demo(int, void*);//回調(diào)函數(shù)
int element_size = 3;
int Max_size = 21;
Mat src, dst;
int main()
{
src = imread("cat1.jpg");
if (!src.data)
{
cout << "could not load this image.." << endl;
return -1;
}
namedWindow("input image", WINDOW_AUTOSIZE);
imshow("input image", src);
namedWindow("output image", WINDOW_AUTOSIZE);
createTrackbar("Element Size:", "output image", &element_size, Max_size, CallBack_Demo);
CallBack_Demo(0, 0);
waitKey(0);
return 0;
}
void CallBack_Demo(int, void*)
{
int s = element_size * 2 + 1;
Mat structureElement = getStructuringElement(MORPH_RECT, Size(s, s), Point(-1, -1));
//dilate(src, dst, structureElement, Point(-1, -1), 1);//膨脹
erode(src, dst, structureElement, Point(-1, -1), 1);//腐蝕
imshow("output image", dst);
return;
}
//深入理解createTrackBar函數(shù)
#include<opencv2/opencv.hpp>
#include<iostream>
#include<opencv2/imgproc/imgproc.hpp>
using namespace std;
using namespace cv;
//void CallBack_Demo(int, void*);
int element_size = 3;
int Max_size = 21;
Mat src, dst;
void text(int, void*) {
cout << element_size << endl;
}
int main()
{
src = imread("cat1.jpg");
if (!src.data)
{
cout << "could not load this image.." << endl;
return -1;
}
namedWindow("測(cè)試窗口", WINDOW_AUTOSIZE);
createTrackbar("數(shù)字", "測(cè)試窗口", &element_size, Max_size, text);
text(0, 0);
waitKey(0);
return 0;
}
形態(tài)學(xué)操作
- 相關(guān)API
morphologyEx(src,dest,CV_MOP_BLACKHAT,kernel);
- Mat src-輸入圖像
- Mat dest-輸出結(jié)果
- int OPT-CV_MOP_OPEN/CV_MOP_CLOSE/CV_MOP_GRADIENT/CV_MOP_TOPHAT/CV_MOP_BLACKHAT形態(tài)學(xué)操作類型
- Mat kernel -結(jié)構(gòu)元素
- int Iteration-迭代次數(shù),默認(rèn)是1
- 開操作-open
- 先腐蝕后膨脹
- 可以去掉較小的對(duì)象
- 閉操作-close
- 先膨脹后腐蝕
- 可以填充小的洞(fill hole)
- 形態(tài)學(xué)梯度-MOPGRADIENT
- 膨脹減去腐蝕
- 又稱為基本梯度(其他還包括-內(nèi)部梯度、方向梯度)
- 頂帽-TOPHAT
- 頂帽是原圖像與開操作之間的差值圖像
- 計(jì)算結(jié)果是把較小的噪聲對(duì)象呈現(xiàn)出來
- 黑帽
- 黑帽是閉操作圖像與原圖像的差值圖像
針對(duì)二值圖像進(jìn)行處理
形態(tài)學(xué)操作應(yīng)用-提取水平與垂直線
膨脹:輸出的像素值是結(jié)構(gòu)元素覆蓋下輸入圖像的最大像素值
腐蝕:輸出的像素值是結(jié)構(gòu)元素覆蓋下輸入圖像的最小像素值
結(jié)構(gòu)元素
- 上述膨脹與腐蝕過程可以使用任意的結(jié)構(gòu)元素
- 常見的形狀:直線、圓、磁盤形狀等各種自定義形狀。
提取步驟
- 輸入圖像imread
- 轉(zhuǎn)換為灰度圖像-cvtColor
- 轉(zhuǎn)換為二值圖像-adaptiveThreshold
- API說明-adaptiveThreshold
- Mat src-輸入的圖像
- Mat dest-二值圖像
- double maxValue-二值圖像最大值
- int adaptiveMethod-自適應(yīng)方法,只能其中之一-ADAPTIVE_THRESH_MEAN_C,ADAPTIVE_THRESH_GAUSSIAN_C
- int thresholdType-閾值類型(binary)
- int blockSize-塊大?。?5)
- double C-常量C,可以是正數(shù),0,負(fù)數(shù)(-2)
- 定義結(jié)構(gòu)元素
- 開操作(腐蝕+膨脹)提取水平與垂直線在結(jié)構(gòu)元素上做文章,把結(jié)構(gòu)元素定義為水平線或者垂直線
bitwise_not(src,dst):dst=255-src;
代碼樣例
#include<iostream>
#include<opencv2/opencv.hpp>
using namespace cv;
using namespace std;
int main(int argc, char** argv) {
Mat src,temp, dst;
src = imread("0-1.jpg");
if (src.empty()) {
cout << "could not load this img..." << endl;
return -1;
}
imshow("src", src);
cvtColor(src, temp, COLOR_BGR2GRAY);
adaptiveThreshold(temp, temp, 255, ADAPTIVE_THRESH_MEAN_C, THRESH_BINARY, 15, -2);
Mat x_kernel = getStructuringElement(MORPH_RECT, Size(src.cols / 16, 1), Point(-1, -1));
Mat y_kernel = getStructuringElement(MORPH_RECT, Size(1,src.rows/16), Point(-1, -1));
morphologyEx(temp, dst, MORPH_OPEN, x_kernel);
bitwise_not(dst, dst);
blur(dst,dst,Size(3,3),Point(-1,-1));
namedWindow("x_line", WINDOW_AUTOSIZE);
imshow("x_line", dst);
waitKey(0);
return 0;
}
- 去除干擾項(xiàng) --轉(zhuǎn)換為二值圖像–定義核矩形–形態(tài)學(xué)操作–開
#include<iostream>
#include<opencv2/opencv.hpp>
using namespace cv;
using namespace std;
int main(int argc, char** argv) {
Mat src,temp, dst;
src = imread("ganrao.png");
if (src.empty()) {
cout << "could not load this img..." << endl;
return -1;
}
imshow("src", src);
cvtColor(src, temp, COLOR_BGR2GRAY);
adaptiveThreshold(temp, temp, 255, ADAPTIVE_THRESH_MEAN_C, THRESH_BINARY, 15, -2);//自適應(yīng)閾值,可以轉(zhuǎn)換為二值圖像
Mat kernel = getStructuringElement(MORPH_RECT, Size(3, 3), Point(-1, -1));//核,getstructingelement獲取結(jié)構(gòu)元素
morphologyEx(temp, dst, MORPH_OPEN, kernel);
//形態(tài)學(xué)操作--開--morph——open
bitwise_not(dst, dst);
imshow("干擾去除", dst);
waitKey(0);
return 0;
}
圖像上采樣與降采樣
-
圖像金字塔概念
- 采樣越多,分辨率越高,金字塔變換特征是不會(huì)改變的
- 我們?cè)趫D像畜欄里中常常會(huì)調(diào)整圖像的大小,最常見的就是放大(zoom in)和縮小(zoom out),盡管幾何變換也可以實(shí)現(xiàn)圖像放大和縮小,但是這里我們介紹圖像金字塔
- 一個(gè)圖像金字塔是一系列圖像組成,最底下一張是圖像尺寸最大,最上方的圖像尺寸最小,從孔金賞從上向下看就像一個(gè)古代的金字塔。
- 高斯金字塔-用來對(duì)圖像進(jìn)行降采樣
- 從底向上,逐層降采樣得到
- 降采樣之后圖像的大小是M/2*N/2,即得到降采樣之后上一層的圖片
- 高斯金字塔的生成過程分為兩步:
- 對(duì)當(dāng)前層進(jìn)行高斯模糊
- 刪除當(dāng)前層的偶數(shù)行與列
- 拉普拉斯金字塔-用來重建一張圖片根據(jù)它的上層降采樣圖片
- 高斯不同(Difference of Gaussian-DOG)
- 定義:就是把一張圖像在不同的參屬下做高斯模糊之后的結(jié)果相減,得到的輸出圖像。稱為高斯不同
- 高斯不同是圖像的內(nèi)在特征,在灰度圖像增強(qiáng)、角點(diǎn)檢測(cè)中經(jīng)常用到
-
采樣API
- 上采樣(pyrUp)-zoom in 放大
- 降采樣(pyrDown)-zoom out 縮小
如何進(jìn)行上采樣和降采樣從而得到高斯金字塔的圖像,在一個(gè)是如何對(duì)每一層進(jìn)行處理得到它的DOG,歸一化,彩色圖像通道也可做DOG
#include<iostream> #include<opencv2/opencv.hpp> using namespace cv; using namespace std; int main(int argc, char** argv) { Mat src,temp, g1,g2,dogImg; src = imread("test.jpg"); if (src.empty()) { cout << "could not load this img..." << endl; return -1; } imshow("src", src); //DOG cvtColor(src, temp, COLOR_BGR2GRAY);//轉(zhuǎn)換為灰度圖像 GaussianBlur(temp, g1, Size(3, 3), 0, 0);//兩次高斯模糊 GaussianBlur(g1, g2, Size(3, 3), 0,0); subtract(g1, g2, dogImg, Mat());//減去 //歸一化顯示 normalize(dogImg, dogImg, 255, 0, NORM_MINMAX);//用此函數(shù)也可化為0-1圖像 imshow("DOG Image", dogImg); waitKey(0); return 0; }
基本閾值操作(threshold)
- 閾值是什么,簡單來說是把圖像分割的標(biāo)尺
閾值類型
-
閾值二值化(threshold binary)
-
閾值反二值化(threshold binary Inverted)
-
閾值截?cái)?/strong>(threshold trunc)
-
閾值取零(threshold to zero)
-
閾值反取零(threshold to zero Inverted)
- OTSU與TRANGLE
首先轉(zhuǎn)化為灰度圖像
閾值操作
threshold(src,dst,threshold_value,threshold_max,THRESH_TYPE);
#include<iostream>
#include<opencv2/opencv.hpp>
#include<math.h>
using namespace cv;
using namespace std;
int threshold_value = 127;//閾值
int threshold_max = 255;//最大閾值
Mat src, dst;
void threshold_Demo(int, void*);//聲明回調(diào)函數(shù)
int main(int argc, char** argv) {
src = imread("test.jpg");
if (src.empty()) {
cout << "could not load this img..." << endl;
return -1;
}
namedWindow("input image", WINDOW_AUTOSIZE);
namedWindow("output image", WINDOW_AUTOSIZE);
imshow("input image", src);
createTrackbar("threshold", "output image", &threshold_value, threshold_max, threshold_Demo);//創(chuàng)建滑動(dòng)條
threshold_Demo(0, 0);
waitKey(0);
return 0;
}
void threshold_Demo(int, void*) {
cvtColor(src, dst, COLOR_BGR2GRAY);//轉(zhuǎn)換為灰度圖
threshold(dst, dst, threshold_value, threshold_max, THRESH_BINARY);//轉(zhuǎn)換為二值圖像
imshow("output image", dst);
}
輸入輸出XML和YAML文件
- XML,是“可擴(kuò)展表示為語言”,任何滿足XML命名規(guī)則的名稱都可以標(biāo)記,這就像不同的應(yīng)用程序打開了大門
- YAML是以數(shù)據(jù)為中心,而不是以置標(biāo)語言為重點(diǎn)。YAML是一個(gè)可讀性高,用來表達(dá)資料序列的格式。哦內(nèi)閣制,YAML試圖用一種比XML更敏捷的方式,來完成XML所完成的任務(wù)
FileStorage類操作文件的使用引導(dǎo)
XML是使用非常廣泛的文件格式,可以利用XML或者YAML格式的文件存儲(chǔ)和還原各式各樣的數(shù)據(jù)結(jié)構(gòu)。當(dāng)然,他們還可以存儲(chǔ)和載入任意復(fù)雜的數(shù)據(jù)結(jié)構(gòu),其中就包括了OpenCV相關(guān)周邊的數(shù)據(jù)結(jié)構(gòu),以及各種原始數(shù)據(jù)類型,如整數(shù)和浮點(diǎn)數(shù)字和文本字符串。
過程:
- 實(shí)例化一個(gè)
FileStorage
類的對(duì)象,用默認(rèn)帶參數(shù)的構(gòu)造函數(shù)完成初始化,或者用FileStorage::open()
成員函數(shù)輔助初始化。 - 使用流操作符<<進(jìn)行文件寫入操作,或者>>進(jìn)行文件讀取操作,類似C++中的文件輸入輸出流。
- 使用
FileStorage::release()
函數(shù)析構(gòu)掉FileStorage
類對(duì)象,同時(shí)關(guān)閉文件。
示例程序:XML和YAML文件的寫入
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<opencv2/opencv.hpp>
#include<math.h>
#include<time.h>
using namespace cv;
using namespace std;
int main() {
//初始化
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();
return 0;
}
模板匹配(template match)
-
模板也是一個(gè)小的圖像,用小圖像匹配的過程叫做模板匹配
-
從左到右,從上到下計(jì)算匹配度
-
計(jì)算歸一化平方不同TM_SQDIFF_NORMED -1
- R ( x , y ) = ∑ x ′ y ′ ( T ( x ′ , y ′ ) ? I ( x + x ′ , y + y ′ ) ) 2 R(x,y)=\sum\limits_{x'y'}(T(x',y')-I(x+x',y+y'))^2 R(x,y)=x′y′∑?(T(x′,y′)?I(x+x′,y+y′))2
-
計(jì)算歸一化相關(guān)性TM_CCORR_NORMED ----3
-
計(jì)算歸一化相關(guān)系數(shù)TM_CCOEFF_NORMED -----5
-
相關(guān)API介紹
- matchTemplate(src,templ,result,method)
- src-源圖像,必須是8-bit或者32-bit浮點(diǎn)數(shù)圖像
- templ-模板圖像,類型與輸入圖像一致
- result-輸出結(jié)果,必須是單通道32位浮點(diǎn)數(shù),假設(shè)源圖像W*H,模板圖像w*h,則結(jié)果必須為W-w+1,H-h+1的大小
- int method 使用的匹配方法
#include<opencv2/opencv.hpp>
#include<iostream>
#include<math.h>
using namespace cv;
using namespace std;
int trackbar_value = TM_CCOEFF_NORMED;//閾值,
int max_track = 5;//最大閾值/類型數(shù)目
Mat src, temp, dst;
void templateMatch_demo(int, void*);
int main(int argc, char** argv) {
src = imread("src.jpg");
temp = imread("template.jpg");
if (!src.data || !temp.data) {
cout << "could not load this image..." << endl;
return -1;
}
//namedWindow("input img", WINDOW_AUTOSIZE);
namedWindow("output img", WINDOW_AUTOSIZE);
createTrackbar("templatemacth", "output img", &trackbar_value, max_track, templateMatch_demo);//創(chuàng)建滑動(dòng)條
templateMatch_demo(0, 0);
waitKey(0);
}
void templateMatch_demo(int ,void*) {
int height = src.rows - temp.rows + 1;//格式要求:大-小+1
int width = src.cols - temp.cols + 1;
Mat result(width, height, CV_32FC1);//要求輸出32位單通道用于模板匹配
matchTemplate(src, temp, result, trackbar_value,Mat());//模板匹配
normalize(result, result, 0, 1, NORM_MINMAX,-1,Mat());//歸一化0-1圖像
Point minLoc, maxLoc,temLoc;//定位
src.copyTo(dst);//把源圖像給dst
double min, max;
minMaxLoc(result, &min, &max, &minLoc, &maxLoc,Mat());
if (trackbar_value TM_SQDIFF || trackbar_value TM_SQDIFF_NORMED) {
temLoc = minLoc;
}
else temLoc = maxLoc;
rectangle(dst, Rect(temLoc.x, temLoc.y, temp.cols, temp.rows), Scalar(0, 0, 255),2,8);//在dst上畫出框
rectangle(result, Rect(temLoc.x, temLoc.y, temp.cols, temp.rows), Scalar(0, 0, 255),2,8);
imshow("output img", result);
imshow("match", dst);
}
輪廓發(fā)現(xiàn)(find contour in your image)
- 輪廓發(fā)現(xiàn)是基于圖像邊緣提取的基礎(chǔ)尋找對(duì)象輪廓的方法。所以邊緣提取的閾值選定會(huì)影響最終輪廓發(fā)現(xiàn)效果
-
API介紹
-
findContours發(fā)現(xiàn)輪廓
- img 輸入圖像,非0的像素被看成1,0的像素保持不變,8-bit
- contours 全部發(fā)現(xiàn)的輪廓對(duì)象
- hierachy 圖像的拓?fù)浣Y(jié)構(gòu),可選,該輪廓發(fā)現(xiàn)算法正是基于圖像拓?fù)浣Y(jié)構(gòu)實(shí)現(xiàn)
- mode 輪廓返回的模式
- method 發(fā)現(xiàn)方法
- Point offset-Point() 輪廓像素的位移,默認(rèn)(0,0)沒有位移
-
drawContours繪制輪廓
- img 輸出圖像
- contours 全部發(fā)現(xiàn)的輪廓對(duì)象
- contourldx 輪廓索引號(hào)
- color 繪制時(shí)候顏色
- thickness 繪制線寬
- lineType 線的類型LINE_8
- hierarchy 拓?fù)浣Y(jié)構(gòu)圖
- maxlevel最大層數(shù),0表示只繪制當(dāng)前的,1表示繪制當(dāng)前及其內(nèi)嵌的輪廓
-
findContours發(fā)現(xiàn)輪廓
- 過程
- 輸入圖像轉(zhuǎn)為灰度圖像cvtColor
- 使用Canny進(jìn)行邊緣提取,得到二值圖像
- 使用findContours尋找輪廓
- 使用drawContours繪制輪廓
#include<iostream>
#include<opencv2/opencv.hpp>
using namespace cv;
using namespace std;
int threshold_value = 100;
int threshold_max = 255;
Mat src, dst;
void Demo_Contours(int, void*);
RNG rng;
int main() {
src = imread("cat3.jpg");
if (src.empty()) {
cout << "could not load this image..." << endl;
return -1;
}
Demo_Contours(0, 0);//contours輪廓
waitKey(0);
return 0;
}
void Demo_Contours(int, void*) {
vector<vector<Point>> contours;
vector<Vec4i> hierarchy;
Canny(src, dst, threshold_value, threshold_value * 2, 3, false);
//Canny(輸入,輸出,低閾值,高閾值(低閾值的2·3倍),3,false);
findContours(dst, contours, hierarchy, RETR_TREE, CHAIN_APPROX_SIMPLE, Point(0, 0));//尋找輪廓
Mat drawImg = Mat::zeros(dst.size(), CV_8UC3);//8bit3通道的彩色圖像
for (size_t i = 0; i < contours.size(); i++) {
Scalar color = Scalar(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255));//RNG rng生成一個(gè)隨機(jī)數(shù)。
drawContours(drawImg, contours, i, color, 2, LINE_8, hierarchy, 0, Point(0, 0));//畫出輪廓(顏色隨機(jī)),
}
imshow("output", drawImg);
}
凸包-Convex Hull
- 在一個(gè)多邊形邊緣或者內(nèi)部任意兩個(gè)點(diǎn)的連線都包含在多邊形邊界或者內(nèi)部—包含集合S中所有點(diǎn)的最小凸多邊形稱為凸包
Graham掃描算法
- 首先選擇Y方向最低的點(diǎn)作為起始點(diǎn)P0
- 從P0開始極坐標(biāo)掃描,依次添加p1…pn(排序順序是根據(jù)極坐標(biāo)的角度大小,逆時(shí)針方向)
- 對(duì)每個(gè)點(diǎn)pi來說,如果添加pi點(diǎn)到凸包中導(dǎo)致一個(gè)左轉(zhuǎn)向(逆時(shí)針方向)則添加該點(diǎn)到凸包,反之則從凸包中刪除該點(diǎn)。
API說明 convexHull
- convexHull(
- pointd,//輸入候選點(diǎn),來自findContours
- hull//凸包
- bool clockwise//順時(shí)針方向
- returnPoints)//true表示返回點(diǎn)荷屬,如果第二個(gè)參數(shù)是vector則自動(dòng)忽略
凸包代碼演示(凸包只是一個(gè)比較特殊的輪廓而已)
- 首先把圖像從RGB轉(zhuǎn)為灰度
- 然后再轉(zhuǎn)為二值圖像
- 再通過發(fā)現(xiàn)輪廓得到候選點(diǎn)
- 凸包API調(diào)用
- 繪制顯示
#include<iostream>
#include<opencv2/opencv.hpp>
using namespace cv;
using namespace std;
int threshold_value = 100;
int threshold_max = 255;
Mat src,src_gray;
void Thershold_Callback(int, void*);
RNG rng(12345);
const char* OUTPUT = "output";
const char* TRACKBAR = "trackbar";
int main() {
src = imread("cat1.jpg");
if (src.empty()) {
cout << "could not load this image..." << endl;
return -1;
}
cvtColor(src, src_gray, COLOR_BGR2GRAY);//先轉(zhuǎn)化為灰度圖像
blur(src_gray, src_gray, Size(3, 3), Point(-1, -1));//然后進(jìn)行模糊,降低噪聲,以用來更好的二值化
namedWindow(OUTPUT, WINDOW_AUTOSIZE);
createTrackbar(TRACKBAR, OUTPUT, &threshold_value, threshold_max, Thershold_Callback);
Thershold_Callback(0, 0);
waitKey(0);
return 0;
}
void Thershold_Callback(int, void*) {
Mat threshold_output;
vector<vector<Point>> contours;
vector<Vec4i> hierarchy;
threshold(src_gray, threshold_output, threshold_value, threshold_max, THRESH_BINARY);//轉(zhuǎn)化為二值圖像
findContours(threshold_output, contours, hierarchy, RETR_TREE, CHAIN_APPROX_SIMPLE, Point(0, 0));//通過發(fā)現(xiàn)輪廓的到候選點(diǎn)
vector<vector<Point>> hull(contours.size());
for (size_t i = 0; i < contours.size(); i++) {
convexHull(Mat(contours[i]), hull[i], false);
}
Mat dst = Mat::zeros(threshold_output.size(), CV_8UC3);
for (size_t i = 0; i < contours.size(); i++) {
Scalar color = Scalar(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255));
drawContours(dst, hull, i, color, 1, LINE_8, hierarchy, 0, Point(0, 0));
drawContours(dst, contours, i, color, 1, LINE_8, hierarchy, 0, Point(0, 0));
}
imshow(OUTPUT, dst);
}
輪廓周圍繪制矩形框和圓形框
從彩色圖像轉(zhuǎn)化為灰度圖像,然后進(jìn)行模糊,然后進(jìn)行二值化處理,或者使用Canny邊緣檢測(cè)
- API介紹
-
approxPolyDP(InputArray curve,OutputArray approxCurve,double epsilon,bool closed)
基于RDP算法實(shí)現(xiàn),目的是減少多邊形輪廓點(diǎn)數(shù) - 輪廓周圍矩形繪制-API
-
boundingRect(InputArray points)
得到輪廓周圍最小矩形左上角點(diǎn)坐標(biāo)和右下角點(diǎn)坐標(biāo),繪制一個(gè)矩形 -
minAreaRect(InputArray points)
得到一個(gè)旋轉(zhuǎn)的矩形,返回旋轉(zhuǎn)矩形
-
- 輪廓周圍繪制圓和橢圓-API
-
minEnclosingCircle(InputArray points)
//得到最小區(qū)域圓形- Point2f& center //圓心位置
- float& radius//圓的半徑
-
fitEllipse(InputArray points)
得到最小橢圓
-
-
- 步驟
- 首先將圖像變?yōu)槎祱D像(先變?yōu)榛叶葓D像,然后進(jìn)行模糊,進(jìn)而用閾值函數(shù)變?yōu)槎祱D像,或者用canny邊緣檢測(cè)變?yōu)槎祱D像)
- 發(fā)現(xiàn)輪廓,找到圖像輪廓(
findContours
) - 通過相關(guān)API再輪廓點(diǎn)上找到最小包含矩形和圓,旋轉(zhuǎn)矩形與橢圓。
- 繪制他們
//壞代碼
#include<iostream>
#include<opencv2/opencv.hpp>
using namespace cv;
using namespace std;
int threshold_value = 170;
int threshold_max = 255;
Mat src, src_gray, dst;
void Contours_Callback(int, void*);
RNG rng(12345);
const char* OUTPUT = "rectangle-Demo";
const char* TRACKBAR = "trackbar";
int main() {
src = imread("cat1.jpg");
if (src.empty()) {
cout << "could not load this image..." << endl;
return -1;
}
cvtColor(src, src_gray, COLOR_BGR2GRAY);//先轉(zhuǎn)化為灰度圖像
blur(src_gray, src_gray, Size(3, 3), Point(-1, -1));//然后進(jìn)行模糊,降低噪聲,以用來更好的二值化
namedWindow(OUTPUT, WINDOW_AUTOSIZE);
createTrackbar(TRACKBAR, OUTPUT, &threshold_value, threshold_max, Contours_Callback);
Contours_Callback(0, 0);
waitKey(0);
return 0;
}
void Contours_Callback(int, void*) {
Mat binary_output;//把灰度圖像變?yōu)槎祱D像
vector<vector<Point>>contours;
vector<Vec4i> hierachy;
threshold(src_gray, binary_output, threshold_value, threshold_max, THRESH_BINARY);
findContours(binary_output, contours, hierachy, RETR_TREE, CHAIN_APPROX_SIMPLE, Point(-1, -1));
vector<vector<Point>>contours_ploy(contours.size());
vector<Rect>ploy_rects(contours.size());
vector<Point2f> ccs(contours.size());
vector<float> radius(contours.size());
vector<RotatedRect> minRect(contours.size());
vector<RotatedRect> myellipse(contours.size());
for (size_t i = 0; i < contours.size(); i++) {
approxPolyDP(Mat(contours[i]), contours_ploy[i], 3, true);//初始化
/*ploy_rects[i] = boundingRect(contours_ploy[i]);
minEnclosingCircle(contours_ploy[i], ccs[i], radius[i]);*/
if (contours_ploy.size() > 5) {
myellipse[i] = fitEllipse(contours_ploy[i]);
minRect[i] = minAreaRect(contours_ploy[i]);
}
}
src.copyTo(dst);
Point2f pts[4];
for (size_t i = 0; i < contours.size(); i++) {
Scalar color = Scalar(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255));
if (contours_ploy.size() > 5) {
//rectangle(dst, ploy_rects[i], color, 2, LINE_8);
//circle(dst, ccs[i], radius[i], color, 2, LINE_8);
ellipse(dst, myellipse[i], color, 1, LINE_8);
minRect[i].points(pts);
for (int r = 0; r < 4; r++) {
line(dst, pts[r], pts[(r + 1) % 4], color, 1, LINE_8);
}
}
}
imshow(OUTPUT, dst);
}
圖像矩(Image Moments)
持續(xù)更新…文章來源:http://www.zghlxwxcb.cn/news/detail-774560.html
自用整理,僅為了記錄,如需轉(zhuǎn)載請(qǐng)標(biāo)明出處。文章來源地址http://www.zghlxwxcb.cn/news/detail-774560.html
到了這里,關(guān)于opencv 入門學(xué)習(xí)筆記(C++)的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!