OpenCV Tutorials — OpenCV 2.4.13.7 documentation
Welcome to opencv documentation! — OpenCV 2.3.2 documentation
OpenCV: Examples
https://github.com/sileixinhua/OpenCV_C-_tutorials
include 1 2 3 4 5 6 7 8 9 #include <iostream> #include <opencv2/core.hpp> #include <opencv2/highgui.hpp> #include <opencv2/opencv.hpp> #include <opencv2/imgproc/types_c.h> #include <string> #include <vector> using namespace std;using namespace cv;
mat矩阵 创建mat矩阵 1 2 3 4 5 cv::Mat mats (500 , 400 , CV_8U,100 ) ;Matx<double , 5 , 3 > matrix; matrix << 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 , 11 , 12 , 13 , 14 , 15 ;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 void creatMat () { int rows = 5 ; int cols = 5 ; Mat eyeMat = Mat::eye (rows, cols, CV_8UC1); Size zeroSize = Size_<int >(5 , 5 ); Mat zeroMat = Mat::zeros (zeroSize, CV_8UC1); Mat onesMat = Mat::ones (rows, cols, CV_8UC1); cout << eyeMat << "\n----\n" << zeroMat << "\n----\n" << onesMat << endl; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 void createMat2 () { int rows = 5 ; int cols = 5 ; Mat mat1 (rows, cols, CV_8UC1) ; Mat mat2 (cv::Size(rows, cols), CV_8UC1) ; Mat mat3 (rows, cols, CV_8UC3, Scalar(2 , 3 , 1 )) ; Mat mat4 (std::vector<int >(rows, cols), CV_8UC3) ; Mat mat5 (mat3) ; Mat imageROI (mat5,Rect(10 ,10 ,500 ,400 )) } void define_create () { int rows = 5 ; int cols = 5 ; Mat mat; mat.create (rows, cols, CV_8UC3); Mat mat1; mat1.create (Size (rows, cols), CV_8UC1); }
mat3输出结果:
[ 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1; 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1; 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1; 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1; 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1]
矩阵mat赋值 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 void MatrixInit () { int rows = 5 ; int cols = 5 ; Mat mat8 = (Mat_<uchar>(rows, cols) << 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 ); cout << "采用Mat_类构造矩阵\n" << mat8 << endl; Mat mat9 (rows, cols, CV_8UC3, Scalar(255 )) ; cout << "构造初始化\n" << mat9 << endl; Mat mat1 (rows, cols, CV_8UC1) ; for (int i = 0 ; i < mat1.rows; i++) { for (int j = 0 ; j < mat1.cols; j++) { mat1.at<uchar>(i, j) = 12 ; } } cout << "单通道矩阵\n" << mat1 << endl; Mat mat6 (rows, cols, CV_8UC3) ; for (int i = 0 ; i < mat6.rows; i++) { for (int j = 0 ; j < mat6.cols; j++) { mat6.at<cv::Vec3b>(i, j) = cv::Vec3b (1 , 3 , 2 ); } } for (int i = 0 ; i < mat1.rows; i++) { uchar *ptr = mat1.ptr<uchar>(i); for (int j = 0 ; j < mat1.cols; j++) { ptr[j] = 132 ; } } }
拷贝 1.浅拷贝 B = A
B(A)
这类拷贝方法仅创建了新的矩阵头,共用同一个内存空间,在修改新对象的时候,旧对象也会改变。
2.深拷贝 B = A.clone()
A.copyTo(B)
这类拷贝方法为新的矩阵申请了新的内存空间,在修改新对象的时候,旧对象不会改变。
在此辨析setTo与copyTo 利用setTo()赋值
切记:setTo只能给矩阵赋一个标量值,即第一个参数必须是数值,不能是图像
用法:
1 2 3 Mat mat1; mat1.create (rows,cols,CV_8UC1); mat1.setTo (5 );
setTo可以利用mask蒙版
1 2 3 Mat mmat; mmat.create (rows,cols,CV_8UC1); mmat.setTo (0 , mmat < 85 );
第二个参数相当于:
copyTo
Mat属性
data uchar型的指针。Mat类分为了两个部分:矩阵头和指向矩阵数据部分的指针,data就是指向矩阵数据的指针。
dims 矩阵的维度,例如5*6矩阵是二维矩阵,则dims=2,三维矩阵dims=3.
rows 矩阵的行数
cols 矩阵的列数
size 矩阵的大小,size(cols,rows),如果矩阵的维数大于2,则是size(-1,-1)
channels 矩阵元素拥有的通道数,例如常见的彩色图像,每一个像素由RGB三部分组成,则channels = 3
下面的几个属性是和Mat中元素的数据类型相关的。
type
表示了矩阵中元素的类型以及矩阵的通道个数,它是一系列的预定义的常量,其命名规则为CV_(位数)+(数据类型)+(通道数)。具体的有以下值:
| CV_8UC1 | CV_8UC2 | CV_8UC3 | CV_8UC4 | | ———— | ———— | ———— | ———— | | CV_8SC1 | CV_8SC2 | CV_8SC3 | CV_8SC4 | | CV_16UC1 | CV_16UC2 | CV_16UC3 | CV_16UC4 | | CV_16SC1 | CV_16SC2 | CV_16SC3 | CV_16SC4 | | CV_32SC1 | CV_32SC2 | CV_32SC3 | CV_32SC4 | | CV_32FC1 | CV_32FC2 | CV_32FC3 | CV_32FC4 | | CV_64FC1 | CV_64FC2 | CV_64FC3 | CV_64FC4 |
这里U(unsigned integer)表示的是无符号整数,S(signed integer)是有符号整数,F(float)是浮点数。
例如:CV_16UC2,表示的是元素类型是一个16位的无符号整数,通道为2.
C1,C2,C3,C4则表示通道是1,2,3,4
type一般是在创建Mat对象时设定,如果要取得Mat的元素类型,则无需使用type,使用下面的depth
depth 矩阵中元素的一个通道的数据类型,这个值和type是相关的。例如 type为 CV_16SC2,一个2通道的16位的有符号整数。那么,depth则是CV_16S。depth也是一系列的预定义值, 将type的预定义值去掉通道信息就是depth值: CV_8U CV_8S CV_16U CV_16S CV_32S CV_32F CV_64F
图像基本计算 利用函数进行加减乘除等 对于add
函数,只能两个大小、通道数相同的两个矩阵相加
可以添加mask矩阵,mask矩阵中,数值为0的地方,最终输出矩阵的对应元素值为0;数值不为0的地方,最终输出矩阵的对应元素值为两个矩阵对应元素的相加值。
例如下例中,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 int Add () { cv::Mat src1, src2, dst; int rows = 10 ; int cols = 10 ; src1.create (rows, cols, CV_8UC1); src1.setTo (20 ); src2.create (rows, cols, CV_8UC1); src2.setTo (100 ); Mat mask (rows, cols, CV_8UC1, Scalar(0 )) ; for (int i = 0 ; i < mask.rows/2 ; i++) { for (int j = 0 ; j < mask.cols/2 ; j++) { mask.at<uchar>(i, j) = 1 ; } } add (src1, src2, dst, mask); cout << dst << endl; return 0 ; }
其他的还有:
1 2 3 4 5 6 7 8 9 10 11 void add (InputArray src1, InputArray src2, OutputArray dst,InputArray mask=noArray(), int dtype=-1 ) ;void subtract (InputArray src1, InputArray src2, OutputArray dst,InputArray mask=noArray(), int dtype=-1 ) ;void multiply (InputArray src1, InputArray src2,OutputArray dst, double scale=1 , int dtype=-1 ) ;void divide (InputArray src1, InputArray src2, OutputArray dst,double scale=1 , int dtype=-1 ) ;void divide (double scale, InputArray src2,OutputArray dst, int dtype=-1 ) ;void scaleAdd (InputArray src1, double alpha, InputArray src2, OutputArray dst) ;void addWeighted (InputArray src1, double alpha, InputArray src2,double beta, double gamma, OutputArray dst, int dtype=-1 ) ;void sqrt (InputArray src, OutputArray dst) ;void pow (InputArray src, double power, OutputArray dst) ;void exp (InputArray src, OutputArray dst) ;void log (InputArray src, OutputArray dst) ;
重载运算符 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 int learn_operator () { cv::Mat src1, src2, dst,multiply,add,subtract,divide, divide2, multiply2, add2, subtract2; int rows = 10 ; int cols = 10 ; src1.create (rows, cols, CV_8UC1); src1.setTo (2 ); src2.create (rows, cols, CV_8UC1); src2.setTo (100 ); Mat mask (rows, cols, CV_8UC1, Scalar(0 )) ; for (int i = 0 ; i < mask.rows / 2 ; i++) { for (int j = 0 ; j < mask.cols / 2 ; j++) { mask.at<uchar>(i, j) = 1 ; } } divide = src2 / src1; divide2 = src2 / 10 ; multiply = src1 * 5 ; add = src1 + src2; add2 = src1 + 37 ; subtract = src2 - src1; subtract2 = src2 - 43 ; return 0 ; }
我们可以看出在opencv中可以运用+-/*,得出一个矩阵。
1 2 3 4 5 Mat rand_mat (rows, cols, CV_8UC1) ;randn (rand_mat, 100 , 10 );cout << rand_mat << endl; cout << "--------result------------" << endl; cout << (rand_mat < 100 ) << endl;
[100, 102, 93, 96, 112; 97, 94, 103, 105, 89; 108, 100, 113, 80, 81; 95, 93, 119, 103, 86; 115, 92, 75, 85, 86] ————result—————— [ 0, 0, 255, 255, 0; 255, 255, 0, 0, 255; 0, 0, 0, 255, 255; 255, 255, 0, 0, 255; 0, 255, 255, 255, 255]
由此可以看出(rand_mat < 100)
的作用可以生成蒙版,这一启示可以帮助我们在setTo
等函数中构建mask
在图像上绘制 绘制矩形
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 int DrawRect () { const char * filename = "D:\\myTemp\\cv\\image\\test.jpg" ; cv::Mat mat = cv::imread (filename); if (mat.empty ()) { throw ("Faild open file." ); } cv::Point p0 = cv::Point (mat.cols / 8 , mat.rows / 8 ); cv::Point p1 = cv::Point (mat.cols * 7 / 8 , mat.rows * 7 / 8 ); rectangle (mat, p0, p1, cv::Scalar (0 , 255 , 0 ), 5 , 8 ); cv::Point p2 = cv::Point (mat.cols * 2 / 8 , mat.rows * 2 / 8 ); cv::Point p3 = cv::Point (mat.cols * 6 / 8 , mat.rows * 6 / 8 ); rectangle (mat, p2, p3, cv::Scalar (0 , 255 , 255 ), 2 , 4 ); cv::imshow ("mat" , mat); cv::waitKey (); return 0 ; }
绘制圆
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 int Circles () { cv::Mat img0 (400 , 400 , CV_8UC3, cv::Scalar(150 , 150 , 150 )) ; circle (img0, cv::Point (200 , 200 ), 50 , cv::Scalar (255 , 0 , 0 )); cv::imwrite ("CirclesImg0.jpg" , img0); cv::Mat img1 (400 , 400 , CV_8UC3, cv::Scalar(150 , 150 , 150 )) ; circle (img1, cv::Point (200 , 200 ), 100 , cv::Scalar (0 , 255 , 0 ), 3 ); cv::imwrite ("CirclesImg1.jpg" , img1); cv::Mat img2 (400 , 400 , CV_8UC3, cv::Scalar(150 , 150 , 150 )) ; circle (img2, cv::Point (200 , 200 ), 150 , cv::Scalar (0 , 0 , 255 ), -1 ); cv::imwrite ("CirclesImg2.jpg" , img2); cv::waitKey (); return 0 ; }
绘制直线
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 int Lines () { const char * filename = "D:\\myTemp\\cv\\image\\test.jpg" ; cv::Mat mat = cv::imread (filename); if (mat.empty ()) { throw ("Faild open file." ); } int x0 = mat.cols / 4 ; int x1 = mat.cols * 3 / 4 ; int y0 = mat.rows / 4 ; int y1 = mat.rows * 3 / 4 ; cv::Point p0 = cv::Point (x0, y0); cv::Point p1 = cv::Point (x1, y1); cv::line (mat, p0, p1, cv::Scalar (0 , 0 , 255 ), 3 , 4 ); p0.y = y1; p1.y = y0; cv::line (mat, p0, p1, cv::Scalar (255 , 0 , 0 ), 3 , 4 ); cv::imshow ("mat" , mat); cv::waitKey (); return 0 ; }
添加文字
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 int DrawText () { const char * filename = "D:\\myTemp\\cv\\image\\test.jpg" ; cv::Mat mat = cv::imread (filename); if (mat.empty ()) { throw ("Faild open file." ); } cv::Point p = cv::Point (50 , mat.rows / 2 - 50 ); cv::putText (mat, "Hello OpenCV" , p, cv::FONT_HERSHEY_TRIPLEX, 1.5 , cv::Scalar (255 , 200 , 200 ), 2 ); cv::imshow ("mat" , mat); cv::waitKey (); return 0 ; }
简单图像处理 矩阵遍历 取出R G B 各个通道
1 2 3 vector<Mat> channel_mats; split (mmat, channel_mats);imshow ("mat" , channel_mats[0 ]);
快速翻转 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 int Flip () { const char * filename = "D:\\myTemp\\cv\\image\\test.jpg" ; cv::Mat src = cv::imread (filename, IMREAD_GRAYSCALE); int flipCode = -1 ; if (src.empty ()) { throw ("Faild open file." ); } cv::Mat dst; cv::flip (src, dst, flipCode); cv::imshow ("src" , src); cv::imshow ("dst" , dst); cv::waitKey (); return 0 ; }
resize 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 int Resize () { const char * filename = "D:\\myTemp\\cv\\image\\test.jpg" ; cv::Mat src, dst; float scaleW = 0.8 ; float scaleH = scaleW; src = cv::imread (filename); if (src.empty ()) { throw ("Faild open file." ); } int width = static_cast <float >(src.cols*scaleW); int height = static_cast <float >(src.rows*scaleH); resize (src, dst, cv::Size (width, height)); cv::imshow ("src" , src); cv::imshow ("dst" , dst); cv::waitKey (); return 0 ; }
仿射变换 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 int Rotate () { cv::Mat src, dst; float angle = 90 ; const char * filename = "D:\\myTemp\\cv\\image\\test.jpg" ; src = cv::imread (filename); if (src.empty ()) { throw ("Faild open file." ); } cv::Point2f center = cv::Point2f (static_cast <float >(src.cols / 2 ), static_cast <float >(src.rows / 2 )); cv::Mat affineTrans = getRotationMatrix2D (center, angle, 0.5 ); cv::warpAffine (src, dst, affineTrans, src.size (), cv::INTER_CUBIC, cv::BORDER_REPLICATE); cv::imshow ("src" , src); cv::imshow ("dst" , dst); cv::waitKey (); }
INTER_NEAREST
最临近插值算法
INTER_LINEAR
线性插值算法
INTER_CUBIC
双立方插值算法
INTER_AREA
区域插值算法(使用像素区域关系的重采样,时图像抽取的首选方法,但是当图像被放大,它类似于INTER_NEAREST方法)
INTER_LANCZOS4
Lanczos插值(超过8x8邻域的插值算法)
INTER_MAX
用于插值的掩模板
WARP_FILL_OUTLIERS
标志位,用于填充目标图像的像素值,如果其中的一些值对应于原图像中的异常值,那么这些值将被设置为0
WARP_INVERSE_MAP
标志位,反变换
borderMode: 边界像素模式,有默认值BORDER_CONSTANT . borderValue: 边界取值,有默认值Scalar()即0
这段代码效果:图片缓缓旋转一周
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 int RotateCotinue () { cv::Mat src, dst; const char * filename = "D:\\myTemp\\cv\\image\\test.jpg" ; cv::imread (filename).copyTo (src); if (src.empty ()) { throw ("Faild open file." ); } cv::Point2f center = cv::Point2f (static_cast <float >(src.cols / 2 ), static_cast <float >(src.rows / 2 )); cv::imshow ("src" , src); cv::namedWindow ("dst" , cv::WINDOW_AUTOSIZE); for (float angle = 0.0 ; angle < 360.0 ; angle++) { cv::Mat affineTrans = getRotationMatrix2D (center, angle, 1.0 ); cv::warpAffine (src, dst, affineTrans, src.size (), cv::INTER_CUBIC); cv::imshow ("dst" , dst); if (cv::waitKey (1 ) >= 0 ) break ; } return 0 ; }
视角设置 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 int Perspective () { cv::Mat src, dst; cv::Point2f dstPoint[4 ]; int xMergin, yMergin; int pattern = 2 ; const char * filename = "D:\\myTemp\\cv\\image\\test.jpg" ; cv::imread (filename).copyTo (src); if (src.empty ()) { throw ("Faild open file." ); } int x0 = src.cols / 4 ; int x1 = (src.cols / 4 ) / 3 ; int y0 = src.rows / 4 ; int y1 = (src.rows / 4 ) / 3 ; cv::Point2f srcPoint[4 ] = { cv::Point (x0,y0), cv::Point (x0,y1), cv::Point (x1,y1), cv::Point (x1,y0), }; switch (pattern) { case 0 : xMergin = src.cols / 10 ; yMergin = src.rows / 10 ; dstPoint[0 ] = cv::Point (x0 + xMergin, y0 + yMergin); dstPoint[1 ] = srcPoint[1 ]; dstPoint[2 ] = srcPoint[2 ]; dstPoint[3 ] = cv::Point (x1 - xMergin, y0 + yMergin); break ; case 1 : xMergin = src.cols / 8 ; yMergin = src.rows / 8 ; dstPoint[0 ] = srcPoint[0 ]; dstPoint[1 ] = srcPoint[1 ]; dstPoint[2 ] = cv::Point (x1 - xMergin, y1 - yMergin); dstPoint[3 ] = cv::Point (x1 - xMergin, y0 + yMergin); break ; case 2 : xMergin = src.cols / 6 ; yMergin = src.rows / 6 ; dstPoint[0 ] = cv::Point (x0 + xMergin, y0 + yMergin); dstPoint[1 ] = srcPoint[1 ]; dstPoint[2 ] = cv::Point (x1 - xMergin, y1 - yMergin); dstPoint[3 ] = srcPoint[3 ]; break ; } cv::Mat perspectiveMmat = cv::getPerspectiveTransform (srcPoint, dstPoint); cv::warpPerspective (src, dst, perspectiveMmat, src.size (), cv::INTER_CUBIC); cv::imshow ("src" , src); cv::imshow ("dst" , dst); cv::waitKey (); }
颜色通道变化 1 2 cv::cvtColor (src, dst, cv::COLOR_RGB2GRAY); cvtColor (image,imagehsi, CV_BGR2HSV);
均衡化 1 2 3 cv::imread (filename).copyTo (src); cvtColor (src, src, COLOR_RGB2GRAY); equalizeHist (src, dst);
图像二值化 threshold
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 int Threshold () { cv::Mat src, dst; double thresh = 60.0 , maxval = 180.0 ; int type = cv::THRESH_BINARY; const char * filename = "D:\\myTemp\\cv\\image\\test.jpg" ; cv::imread (filename).copyTo (src); if (src.empty ()) { throw ("Faild open file." ); } cv::equalizeHist (src,dst); thresh = 80.0 ; maxval = 210.0 ; int number = 0 ; switch (number) { case 0 :type = cv::THRESH_BINARY; break ; case 1 :type = cv::THRESH_BINARY_INV; break ; case 2 :type = cv::THRESH_TRUNC; break ; case 3 :type = cv::THRESH_TOZERO; break ; case 4 :type = cv::THRESH_TOZERO_INV; break ; } cv::threshold (src,dst,thresh,maxval,type); }
图像滤波 【OpenCV入门教程之八】线性邻域滤波专场:方框滤波、均值滤波与高斯滤波_【浅墨的游戏编程Blog】毛星云(浅墨)的专栏-CSDN博客_均值滤波与方框滤波
方框滤波——boxblur函数
均值滤波(邻域平均滤波)——blur函数
高斯滤波——GaussianBlur函数
中值滤波——medianBlur函数
双边滤波——bilateralFilter函数
均值滤波 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 int Blur () { cv::Mat src, dst; const char * filename = "D:\\myTemp\\cv\\image\\test.jpg" ; cv::imread (filename).copyTo (src); if (src.empty ()) { throw ("Faild open file." ); } int ksize = 3 ; blur (src, dst, cv::Size (ksize, ksize)); cv::imshow ("src" , src); cv::imshow ("dst" , dst); cv::waitKey (); return 0 ; }
高斯滤波 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 int GaussianBlur () { cv::Mat src, dst; const char * filename = "D:\\myTemp\\cv\\image\\test.jpg" ; cv::imread (filename).copyTo (src); if (src.empty ()) { throw ("Faild open file." ); } int ksize1 = 11 ; int ksize2 = 11 ; double sigma1 = 10.0 ; double sigma2 = 20.0 ; cv::GaussianBlur (src, dst, cv::Size (ksize1, ksize2), sigma1, sigma2); cv::imshow ("src" , src); cv::imshow ("dst" , dst); cv::waitKey (); return 0 ; }
卷积算子 以下laplacian、sobel、canny算子常用于边缘检测
Laplacian
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 int Laplacian () { cv::Mat src, dst; const char * filename = "D:\\myTemp\\cv\\image\\test.jpg" ; cv::imread (filename).copyTo (src); if (src.empty ()) { throw ("Faild open file." ); } Laplacian (src, dst, 0 ); cv::imshow ("src" , src); cv::imshow ("dst" , dst); return 0 ; }
sobel算子
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 int Sobel () { cv::Mat src, dst; const char * filename = "D:\\myTemp\\cv\\image\\test.jpg" ; cv::imread (filename).copyTo (src); if (src.empty ()) { throw ("Faild open file." ); } Sobel (src, dst, -1 , 0 , 1 ); cv::imshow ("src" , src); cv::imshow ("sobel" , dst); return 0 ; }
canny算子
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 int Canny () { cv::Mat src, dst; const char * filename = "D:\\myTemp\\cv\\image\\test.jpg" ; cv::imread (filename).copyTo (src); if (src.empty ()) { throw ("Faild open file." ); } double threshold1 = 40.0 ; double threshold2 = 200.0 ; Canny (src, dst, threshold1, threshold2); cv::imshow ("src" , src); cv::imshow ("canny" , dst); return 0 ; }
特征提取 角点检测 goodFeaturesToTrack函数
可以计算Harris角点和shi-tomasi角点,但默认情况下计算的是shi-tomasi角点
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 int DetectConers () { cv::Mat src, gray, dst; const int maxCorners = 50 , blockSize = 3 ; const double qualityLevel = 0.01 , minDistance = 20.0 , k = 0.04 ; const bool useHarrisDetector = false ; std::vector< cv::Point2f > corners; const char * filename = "D:\\myTemp\\cv\\image\\demo.jpg" ; cv::imread (filename).copyTo (src); if (src.empty ()) { throw ("Faild open file." ); } dst = src.clone (); cvtColor (src, gray, cv::COLOR_RGB2GRAY); goodFeaturesToTrack (gray, corners, maxCorners, qualityLevel, minDistance, cv::Mat (), blockSize, useHarrisDetector, k); for (size_t i = 0 ; i < corners.size (); i++) { circle (dst, corners[i], 8 , cv::Scalar (255 , 255 , 0 ), 2 ); } cv::imshow ("src" , src); cv::imshow ("dst" , dst); return 0 ; }
==可以学习Harris和shi-tomasi角点检测方法==
Harris角点检测原理详解_lwzkiller的专栏-CSDN博客_harris角点检测
图像形态学处理 形态学,即数学形态学(mathematical Morphology),是图像处理中应用最为广泛的技术之一,主要用于从图像中提取对表达和描绘区域形状有意义的图像分量,使后续的识别工作能够抓住目标对象最为本质〈最具区分能力-most discriminative)的形状特征,如边界和连通区域等。同时像细化、像素化和修剪毛刺等技术也常应用于图像的预处理和后处理中,成为图像增强技术的有力补充。
二值图像 的基本形态学运算, 包括腐蚀、膨胀、开和闭。
二值形态学的经典应用, 包括击中击不中变换、边界提取和跟踪、区域填充、提取连通分量、细化和像素化, 以及凸壳
灰度图像的形态学运算, 包括灰度腐蚀、灰度膨胀、灰度开和灰度闭
所有形态学运算都是针对图像中的前景物体进行的, 因而首先对图像前景和背景的认定给出必要的说明.
大多数图像,一般相对于背景而言物体的颜色(灰度)更深, 二值化之后物体会成为黑色, 而背景则成为白色, 因此我们通常是习惯于将物体用黑色(灰度值0)表示, 而背景用白色(灰度值255)表示。 如果有例外,可以先反色处理。
参考了:形态学图像处理_Ricardo的博客-CSDN博客_形态学处理
图像处理中常见的形态学方法 - 知乎 (zhihu.com)
腐蚀与膨胀 数字图像处理—-通俗理解腐蚀与膨胀_alw_123的博客-CSDN博客_图像腐蚀和膨胀的作用
形象来看:膨胀更白,腐蚀更黑
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 int Image_threshold (const Mat inputMat, Mat &outsingleMat) { Mat dst; cvtColor (inputMat, dst, COLOR_RGB2GRAY); imshow ("cvtcolor" ,dst); threshold (dst, outsingleMat,80 ,255 , cv::THRESH_BINARY); return 0 ; } int Dilate (Mat &singleMat,Mat &outputArray) { dilate (singleMat, outputArray, getStructuringElement (MORPH_RECT, Size (3 ,3 ))); return 0 ; } int Erode (Mat &singleMat, Mat &outputArray) { erode (singleMat, outputArray, getStructuringElement (MORPH_RECT, Size (3 , 3 ))); return 0 ; } int main () { Mat mmat, inputMat, outsingleMat; const char * filename = "D:\\myTemp\\cv\\image\\DSCF1062.jpg" ; mmat = imread (filename,IMREAD_COLOR); Image_threshold (mmat, outsingleMat); Erode (outsingleMat, outsingleMat); imwrite ("erode.jpg" , outsingleMat); Dilate (outsingleMat, outsingleMat); imwrite ("dilate.jpg" , outsingleMat); Mat dst (mmat.rows, mmat.cols,CV_8UC3) ; mmat.copyTo (dst, outsingleMat); imwrite ("erode_dilateS.jpg" , dst); waitKey (); }
视频处理 摄像头数据获取 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 int DispBasic () { cv::VideoCapture capture (0 ) ; int width = static_cast <int >(capture.get (CAP_PROP_FRAME_WIDTH)); int height = static_cast <int >(capture.get (CAP_PROP_FRAME_WIDTH)); std::cout << "frame size = " << width << " * " << height << std::endl; const char * wName = "camera" ; cv::Mat src; cv::namedWindow (wName, WINDOW_AUTOSIZE); while (true ) { capture >> src; cv::imshow (wName, src); if (cv::waitKey (1 ) >= 0 ) { break ; } } return 0 ; }
窗口与鼠标 namedWindow
1 namedWindow ("myWindow" , WINDOW_NORMAL);
参数2:窗口的标识,一般默认为WINDOW_AUTOSIZE 。
WINDOW_AUTOSIZE 窗口大小自动适应图片大小,并且不可手动更改。(上面图1就是使用的它)
WINDOW_NORMAL 用户可以改变这个窗口大小(上面图2就是使用的它)
WINDOW_OPENGL 窗口创建的时候会支持OpenGL
resizeWindow
1 2 3 4 mmat = imread (filename, IMREAD_COLOR); namedWindow ("myWindow" , WINDOW_NORMAL);resizeWindow ("myWindow" , Size (mmat.cols,mmat.rows));imshow ("myWindow" , mmat);
鼠标操作 首先我们可以通过以下例子,掌握鼠标回调函数中 event
和 flags
不同取值的作用,以及setMouseCallback
用法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 #include <opencv2/opencv.hpp> #include <iostream> bool down = false ;int i = 0 ;void onMouse (int event, int x, int y, int flags, void * param) { cv::Mat* im = reinterpret_cast <cv::Mat*>(param); if (event== cv::EVENT_LBUTTONDOWN) { std::cerr << "你按下了鼠标左键x=" << x << ", y=" << y << std::endl << std::endl; down = true ; } if (event == cv::EVENT_LBUTTONUP) { std::cerr << "你释放了鼠标左键x=" << x << ", y=" << y << std::endl << std::endl; down = false ; } if (event == cv::EVENT_MOUSEMOVE && down==true ) { std::cerr << "你按下了鼠标左键并移动 x=" << x << ", y=" << y << std::endl << std::endl; } if (flags == cv::EVENT_FLAG_LBUTTON) { std::cerr << "你拖拽了鼠标左键 x=" << x << ", y=" << y << std::endl << std::endl; } if (flags == 8 ) { std::cerr << "按住CTRL拖拽 i=" << i++ << std::endl << std::endl; } } int main (int argc, char ** argv) { cv::Mat image = cv::imread ("D:/bb/tu/1.jpg" ); if (image.empty ()) { std::cout << "图像读取失败..." << std::endl; return 0 ; } cv::namedWindow ("Original Image" ); cv::imshow ("Original Image" , image); cv::setMouseCallback ("Original Image" , onMouse, reinterpret_cast <void *>(&image)); cv::waitKey (0 ); return 0 ; }
示例:交互绘制ROI 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 bool leftButtonPushed = false ;cv::Point startPoint,endPoint; Mat image,image_copy,imagePartial,imageROI; void createROI (int event, int x, int y, int flag, void * param) { if (event == cv::EVENT_LBUTTONDOWN && leftButtonPushed == false ) { leftButtonPushed = true ; startPoint.x = x; startPoint.y = y; } if (leftButtonPushed) { if (flag == EVENT_FLAG_LBUTTON) { rectangle (image, Rect (startPoint, Point (x, y)), Scalar (0 , 255 , 255 ,80 ),-1 ); } } if (event == cv::EVENT_LBUTTONUP) { endPoint = Point (x, y); leftButtonPushed = false ; } imshow ("win" , image); } int main () { image = cv::imread ("D:\\myTemp\\cv\\image\\lena.jpg" ); image_copy = image.clone (); if (image.empty ()) { std::cout << "图像读取失败..." << std::endl; return 0 ; } namedWindow ("win" ); imshow ("win" , image); setMouseCallback ("win" , createROI, 0 ); while (1 ) { imshow ("win" , image); int c = waitKey (0 ); if ((char )c == 'q' ) { destroyAllWindows (); break ; } } imageROI.create (image.rows, image.cols, CV_8UC1); imageROI.setTo (0 ); for (int i = startPoint.y; i < endPoint.y; i++) { for (int j = startPoint.x; j < endPoint.x; j++) { imageROI.at<uchar>(i,j) = 255 ; } } imshow ("roi" , imageROI); Mat out; image_copy.copyTo (out, imageROI); imshow ("new" , out); waitKey (); }
与其他库数据交互 Eigen 头文件必须把Eigen写在前面
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 #include <Eigen/Dense> #include <iostream> #include <opencv2/core/eigen.hpp> #include <opencv2/opencv.hpp> using namespace std;using namespace cv;using namespace Eigen;void eigen_opencv () { Eigen::MatrixXd m (2 , 5 ) ,d ; m << 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 ; Mat mat; eigen2cv (m, mat); mat = mat + 5 ; cv2eigen (mat, d); }