本文不会像其他笔记那样详细讲解每一个步骤和问题,最近太忙,没有太多时间,突击学习一下,记录下遇到的一些问题,但依然会像其他笔记一样授之以渔。仅针对C++,OpenCV2,假如你没学习过官方提供详细支持的编程语言,例如C/C++/python,建议还是先学一门编程语言再来学习OpenCV吧。看到网上有教程竟然讲OpenCV时还讲起了C语言。 强烈推荐参考官方手册,官方的doc简直太棒了:http://opencv.org/documentation.html ,即能下载PDF,又能在线查看,还有详细的tutorial,以下多数问题的解决来源于查看官方的Reference Manual:http://docs.opencv.org/2.4/opencv2refman.pdf
关于环境配置 环境配置参见这里Windows下OpenCV与VS开发环境配置
一些入门常用函数 2.图像操作及用户界面的几个函数原型及作用(highgui.hpp):imread:
1 Mat imread (const string& filename, int flags=1 )
用于从文件filename读取图像并在内存中存储为Mat类型,flags用于指宝载入图像使用的色彩类型,如rgb、灰度等
imshow:
1 void imshow (const string& winname, InputArray mat)
在指定的窗口中显示图像mat,由winname指定窗口名称(即窗口标题),如果没有使用namedWindow创建窗口,则新建一个。InputArray类型是一个接口类,可以是Mat、Mat_、Mat_<T, m, n>、vector、vector<vector>、vector类型
imwrite:
1 bool imwrite (const string& filename, InputArray img, const vector<int >& params=vector<int >() )
将图像img保存于指定的文件filname中,最后一个参数于用指定保存的图片格式,如jPEG、PNG等,当不指定最后一个参数时,函数会自动根据finlname中的文件后缀来确定存储格式
createTrackbar:
1 int createTrackbar (const string& trackbarname, const string& winname, int * value, int count, TrackbarCallback onChange=0 , void * userdata=0 )
创建一个名为trackbarname的滑动条,并将其依附到指定的名为winname的窗口上。第三个参数value用于指定滑动指针的位置,需要是个整型指针变量(因为这个变量会在调节滑动条是发生变化,所以需要是个指针变量);第四个参数count就用来指定滑动条最大可以滑动到多少,例如int value = 50,count = 100,则表示程序运行时滑动指针指向50的位置,最大可以滑动的位置是100。第五个可选参数onChange是一个形如void Foo(int,void*);
的回调函数,即当滑动条的value变化时就调用该函数,该回调函数中的第一个参数是滑动条的当前值,即value,第二个参数与createTrackbar的最后一个参数userdata一样,可用于在不使用全局变量的情况下向回调函数传递数据来处理滑动条事件,暂时没有使用过。官方提供的一个使用示例在:opencv_source_code/samples/cpp/connected_components.cpp 注意:该函数创建的滑动条在窗口的顶端,水平放置,且无法改变,如果想改变它的位置,可以通过Qt的GUI来实现
getTrackbarPos:
1 int getTrackbarPos (const string& trackbarname, const string& winname)
返回滑动条的当前位置,其实也就是上面那个函数的value的当前值。
moveWindow
1 void moveWindow (const string& winname, int x, int y)
移动名为winname的窗口
destroyWindow:
1 void destroyWindow (const string& winname)
销毁给定名称的窗体,释放内存
destroyAllWindows:
1 void destroyAllWindows ()
销毁所有打开着的HighGUI窗体,并释放内存 这两个函数在小程序时可以省略,因为程序结束运行时会自动被操作系统回收。但对于大程序,应该手动去释放不需要的窗体内存,特别是需要长期运行的程序,以防止内存泄露。
waitKey:
1 int waitKey (int delay=0 )
等待按键按下,当延时delay毫秒后依然没有按下,停止等待。如果delay为0或负数时表示一直等待,直到键被按下并返回键值,如果delay时间结束时依然没有键被按下返回-1,该函数可用于函数窗口被关闭,类似C中的getchar()
如何使imshow的显示窗口能够调整大小? 对于高分辨率的图像,很容易显示太大,导致不方便查看,其实只需要在调用imshow之前使用namedWindow创建一个WINDOW_NORMAL的窗体即可,该窗体(官方称为placeholder)可用于放image和trackbar,如果已经有了一个同名的窗体,则该函数调用什么也不干。如果在调用imshow之前已经创建了一个同名window,则imshow会将显示内容置于该window中,如果没有,则imshow会默认创建一个参数为WINDOW_AUTOSIZE的窗体,结合上面的其他图像存取函数举例:
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 #include <opencv2/core/core.hpp> #include <opencv2/highgui/highgui.hpp> #include <iostream> using namespace cv;Mat img; static void resizeWin (int size, void *) { resizeWindow ("rCat" , size, size); imshow ("rCat" , img); std::cout << getTrackbarPos ("TrackBar" , "rCat" ) << std::endl; } int main (void ) { int winSize = 80 ; img = imread ("F:\\cat.jpg" , 2 | 4 ); if (img.empty ()) { std::cout << "Could not open or find the image" << std::endl; return -1 ; } namedWindow ("Cat" , WINDOW_NORMAL); imshow ("Cat" , img); namedWindow ("rCat" , WINDOW_NORMAL); createTrackbar ("TrackBar" , "rCat" , &winSize, 1024 , resizeWin); resizeWin (winSize, 0 ); imwrite ("hcat-copy.png" , img); waitKey (0 ); return 0 ; }
播放一个视频 播放视频其实就是读取视频中一帧帧的图像,并将其显示出来,同时将处理后的视频保存,直接看代码吧:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 #include <opencv2/opencv.hpp> #include <iostream> int main (void ) { cv::VideoCapture vcap ("F:\\bunny.avi" ) ; if (!vcap.isOpened ()) { std::cout << "Video or camera open failed!" << std::endl; return -1 ; } cv::VideoWriter outputVideo; int ex = static_cast <int >(vcap.get (CV_CAP_PROP_FOURCC)); cv::Size S = cv::Size ((int )vcap.get (CV_CAP_PROP_FRAME_WIDTH), (int )vcap.get (CV_CAP_PROP_FRAME_HEIGHT)); outputVideo.open ("F:\\bunny_edge.avi" , ex, vcap.get (CV_CAP_PROP_FPS), S, false ); cv::Mat edges; cv::namedWindow ("Edges" , 1 ); for (;;) { cv::Mat frame; vcap >> frame; cv::cvtColor (frame, edges, CV_BGR2GRAY); cv::GaussianBlur (edges, edges, cv::Size (7 , 7 ), 1.5 , 1.5 ); cv::Canny (edges, edges, 0 , 30 , 3 ); cv::imshow ("Edges" , edges); outputVideo << edges; if (cv::waitKey (30 ) >= 0 ) break ; } return 0 ; }
D:\opencv\sources\samples
目录下有很多好玩的实例
核心模块 库的使用,基本的像素操作方法
常用的数据类型和函数 OpenCV中的所有数据类型可以在这里查看:http://docs.opencv.org/2.4.13/modules/core/doc/core.html 1.Mat是Opencv中非常重要的数据类型,它是个很复杂的类型,即可以用于操作二维的图像方阵,又可以作为普通的矩阵类来使用,并且具有自动管理内存的功能,通过上面将视频数据写入到Mat类对象可以看出它应该是被重载了很多的操作符的。所以实际上可以通过一个VideoCapture对象对它进行>>
操作,也可以通过cout对它<<
操作。 Mat类共有24个构造函数,可见其功能的丰富程度,下面几个常用的构造方式:直接构造
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 #include <opencv2/opencv.hpp> #include <iostream> using namespace cv;int main (void ) { Mat img (300 , 400 , CV_8UC3, Scalar(122 , 33 , 104 )) ; imshow ("Picture" , img); Mat img1 (3 , 4 , CV_8UC3, Scalar(122 , 33 , 104 )) ; std::cout << img1 << std::endl; waitKey (0 ); destroyWindow ("Picture" ); return 0 ; }
Mat img1(3, 4, CV_8UC3, Scalar(122, 33, 104));
表示构造一个名为img1的二维矩阵对象,该对象由3行4列的Scalar对象构成,可以把这里的Scalar当成是一个vector,其中的三个值分别对应BGR的三个值122,33,104。这三个值构成的颜色刚好是VS2013的LOG颜色。上面第三个参数CV_8UC3是定义好的宏,用于指定RGB的存储属性,意义分别是CV_[The number of bits per item][Signed or Unsigned][Type Prefix]C[The channel number],例子中的意思就是每个像素点使用3通道的8位无符号数字表示(由后面的Scalar来填充)。所以上面的程序运行会输出一个高300像素,宽400像素VS2013的LOGO颜色的纯色框。并且会在命令行中输出3行4列的122,33,104:
1 2 3 [122, 33, 104, 122, 33, 104, 122, 33, 104, 122, 33, 104; 122, 33, 104, 122, 33, 104, 122, 33, 104, 122, 33, 104; 122, 33, 104, 122, 33, 104, 122, 33, 104, 122, 33, 104]
虽然Mat也可以构造为多维的矩阵,但由于imshow和<<
运算符只能用于2维或2维以下的Mat对象,所以这里不做解释,可以自行查看这里http://docs.opencv.org/3.0-last-rst/doc/tutorials/core/mat_the_basic_image_container/mat_the_basic_image_container.html#matthebasicimagecontainer
2.通过randu可以使用随机颜色填充Mat对象:
1 2 3 Mat img = Mat (300 , 400 , CV_8UC3); randu (img, Scalar::all (0 ), Scalar::all (255 )); imshow ("Picture" , img);
通过create函数构造 3.可以通过调用create(nrows, ncols, type)来进行构造指定size和类型的Mat对象:
1 2 3 4 Mat M (7 , 7 , CV_32FC2, Scalar(1 , 3 )) ; M.create (100 , 50 , CV8UC (15 )); Mat N; N.create (M.size (), M.type ());
创建一个多维矩阵
1 2 3 int sz[] = {100 , 100 , 100 }; Mat cube (3 , sz, CV_8U, Scalar::all(0 )) ;
通过copy函数从一个Mat对象的指定行或列构造另一个对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 M.row (3 ) = M.row (3 ) + M.row (5 )*3 ; Mat M1 = M.col (1 ); M.col (7 ).copyTo (M1); Mat img (Size(320 ,240 ),CV_8UC3) ;Mat roi (img, Rect(10 ,10 ,100 ,100 )) ;roi = Scalar (0 ,255 ,0 );
通过数组创建
1 2 3 4 5 6 7 8 9 10 11 void process_video_frame (const unsigned char * pixels, int width, int height, int step) { Mat img (height, width, CV_8UC3, pixels, step) ; GaussianBlur (img, img, Size (7 ,7 ), 1.5 , 1.5 ); } double m[3 ][3 ] = {{a, b, c}, {d, e, f}, {g, h, i}};Mat M = Mat (3 , 3 , CV_64F, m).inv ();
通过一个逗号分隔的初始化列表创建
1 2 3 4 Mat M = (Mat_ <double >(3 ,3 ) << 1 , 0 , 0 , 0 , 1 , 0 , 0 , 0 , 1 );
MATLAB风格的zeros(),ones(), eye()函数来初始化
1 2 M += Mat::eye (M.rows, M.cols, CV_64F);
3.opencv内建了多种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 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 #include "opencv2/core/core.hpp" #include <iostream> using namespace cv;static void help () { std::cout << "\n--------------------------------------------------------------------------" << std::endl << "This program shows how to create matrices(cv::Mat) in OpenCV and its serial" << " out capabilities" << std::endl << "That is, cv::Mat M(...); M.create and std::cout << M. " << std::endl << "Shows how output can be formated to OpenCV, python, numpy, csv and C styles." << std::endl << "Usage:" << std::endl << "./cvout_sample" << std::endl << "--------------------------------------------------------------------------" << std::endl << std::endl; } int main (int , char **) { help (); Mat M (2 , 2 , CV_8UC3, Scalar(0 , 0 , 255 )) ; std::cout << "M = " << std::endl << " " << M << std::endl << std::endl; M.create (4 , 4 , CV_8UC (2 )); std::cout << "M = " << std::endl << " " << M << std::endl << std::endl; int sz[3 ] = { 2 , 2 , 2 }; Mat L (3 , sz, CV_8UC(1 ), Scalar::all(0 )) ; Mat E = Mat::eye (4 , 4 , CV_64F); std::cout << "E = " << std::endl << " " << E << std::endl << std::endl; Mat O = Mat::ones (2 , 2 , CV_32F); std::cout << "O = " << std::endl << " " << O << std::endl << std::endl; Mat Z = Mat::zeros (3 , 3 , CV_8UC1); std::cout << "Z = " << std::endl << " " << Z << std::endl << std::endl; Mat C = (Mat_ <double >(3 , 3 ) << 0 , -1 , 0 , -1 , 5 , -1 , 0 , -1 , 0 ); std::cout << "C = " << std::endl << " " << C << std::endl << std::endl; Mat RowClone = C.row (1 ).clone (); std::cout << "RowClone = " << std::endl << " " << RowClone << std::endl << std::endl; Mat R = Mat (3 , 2 , CV_8UC3); randu (R, Scalar::all (0 ), Scalar::all (255 )); std::cout << "R (default) = " << std::endl << R << std::endl << std::endl; std::cout << "R (python) = " << std::endl << format(R, "python" ) << std::endl << std::endl; std::cout << "R (numpy) = " << std::endl << format(R, "numpy" ) << std::endl << std::endl; std::cout << "R (csv) = " << std::endl << format(R, "csv" ) << std::endl << std::endl; std::cout << "R (c) = " << std::endl << format(R, "c" ) << std::endl << std::endl; Point2f P (5 , 1 ) ; std::cout << "Point (2D) = " << P << std::endl << std::endl; Point3f P3f (2 , 6 , 7 ) ; std::cout << "Point (3D) = " << P3f << std::endl << std::endl; vector<float > v; v.push_back ((float )CV_PI); v.push_back (2 ); v.push_back (3.01f ); std::cout << "Vector of floats via Mat = " << Mat (v) << std::endl << std::endl; vector<Point2f> vPoints (20 ) ; for (size_t i = 0 ; i < vPoints.size (); ++i) vPoints[i] = Point2f ((float )(i * 5 ), (float )(i % 7 )); std::cout << "A vector of 2D Points = " << vPoints << std::endl << std::endl; return 0 ; }
4.Point类用于表示点,Point_<int>
、Point2i
、Point
三个一样都表示以int为基础元素类型的点,而Point_<float>
和Point2f
表示以float为基础数据类型的二维上的点:
1 2 3 4 Point p (3 , 4 ) ;Point2f pf (3.14 , 4.13 ) ;p.x = 5 ; p.y = 6 ;
5.Scalar类可用于表示颜色,它是一个含有4个元素的vector,当用于表示RGB时最后一个元素可以省略,RGB在Scalar中的顺序实际是BGR,即三个元素分量分别对应蓝、绿、红。与.NET中的颜色表示一样。上面的例子中已经使用过了
6.尺寸类Size,有2个参数分别可用于表示二维矩阵的宽、高尺寸,同样具有Size2d,表示以double为基础数据类型,感觉跟Point很像,源码定义也确实在一起。:
1 2 Size si (3 , 4 ) ;Size2d sd (3.14 , 4.13 ) ;
7.矩形类Rect,Rect类有4个成员变量用于表示一个矩形的起点坐标(x, y)和尺寸(width, height),还包括了一些很有用的成员函数,如rec.size()返回Size、area()返回面积、contains(Point)判断点是否在矩形内、inside(Rect)判断另一个矩形是否在该矩形内、tl()返回左上角点坐标、br()返回右下角点坐标。 还被重载了一些非常有用的操作符:
rect = rect + point //平移矩形
rect = rect - point
rect = rect + size //缩放矩形
rect = rect - size
rect += point, rect -= point, rect += size, rect -= size (简化版操作符)
rect = rect1 & rect2 求2个矩形的交集
rect = rect1 | rect2 返回包含两个矩形的最小矩形
rect &= rect1, rect |= rect1 //简化版操作符
rect == rect1, rect != rect1 比较两个矩形
8.颜色空间转换函数cvtColor(),前面已经用过它来转RGB到灰度图。它还可以实现HSV、HSI等色彩模式的转换,函数原型为:
1 void cvtColor (InputArray src, OutputArray dst, int code, int dstCn=0 )
第三个参数是颜色空间转换标识符、第四个参数为目标图像的通道数,0表示取源图像的通道数。
图像表示 1.opencv中的Mat图像以二维矩阵的形式存储,为了效率,有时候会将整个图像在内存中以连续的一行存储,可以通过Mat的成员函数isContinuous()为判断是否为连接存储,如果是,则所有数据在内存中将只以一行的形式存储。 内存中像素在每行中的存储与通道数相关,例如如果是3通道8位无符号,则每个像素应由3个8位无符号组成,即每行中的数字列数实际应该为图像像素列数乘以3。 Mat的一些成员变量或函数:
mat.rows 获取总行数,即图片以像素为单位的高度
mat.cols 获取总列数,即图片以像素为单位的宽度
mat.channels() 获取图像的通道数
mat.data 获取图像矩阵的第一行且为第一列的指针,如果返回为空,则表示输入的图像有问题,可以用来判断输入图像是否生效
mat.ptr(i) 获取图像第i行的uchar类型的元素指针
mat.clone() 返回对象副本
Mat类实现由两部分数据组成:矩阵头(包含矩阵尺寸、存储方法、存储地址等信息)和一个指向存储所有像素值的矩阵的指针。Mat的拷贝构造函数只是拷贝Mat对象的信息头和矩阵指针,而不复制矩阵本身,因为矩阵本身存储的数量量太大。当需要Mat副本时,应该使得cone函数获得副本。
2.缩减像素数 图像当以8位来表示一个单通道的像素点时,可以有2^8=256种色彩值,为了提高处理效率,实际上不需要那么丰富的色彩。此时可以将图像映射为较少色彩值的图像,一般的映射方法为,如果以10为一个颜色值步长的话,可以将0-9全部用0来表示,10-19用10来表示,20-29用20来表示,依次类推。这个步长越大,图像中的颜色值将越少。 可以利用除法的自动截余特性来方便的实现,例如原的来颜色值为old,新值new = (old / step ) * step
,step为颜色值的跨度步长。然后遍历所有像素,全部这样转换一遍就获得了缩减之后的图像,转换后由于色彩较少,会有点水彩画的感觉。 但为了提高效率,由于8位一共也就256个值,可以提前建个表,然后当需要转换时从这个表里面读即可,例如:
1 2 3 4 int step = 10 ;uchar table[256 ]; for (int i = 0 ; i < 256 ; ++i) table[i] = (i / step) * step;
这样table里面将是一个缩减后的表,1-9的位置都是0,10-19的位置都是1,依次。。。 当我有个数字26想知道转换后的值是多少时,直接读table[26]的值即可,这样就不用所有图像的每个像素都要进行一次计算了。
而opencv中提供了一个名为LUT的函数,可以直接根据表来实现将一个Mat对象转换为缩减后的Mat对象的功能 LUT的函数原型为:
1 void cv::LUT ( cv::InputArray src, cv::InputArray lut, cv::OutputArray dst) ;
下面分别使用普通方式和LUT方式实现图像缩减,推荐使用LUT函数,效率更高
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 #include <opencv2/opencv.hpp> using namespace cv;void scanImageAndReduce (Mat &src, uchar *tab) ; int main (void ) { Mat img = imread ("F:\\cat.jpg" ); const int divideWidth = 60 ; uchar table[256 ]; for (int i = 0 ; i < 256 ; ++i) { table[i] = (uchar)divideWidth * (i / divideWidth); } Mat img_reduce; Mat lookUpTable (1 , 256 , CV_8U) ; uchar *p = lookUpTable.ptr (); for (int i = 0 ; i < 256 ; ++i) p[i] = table[i]; LUT (img, lookUpTable, img_reduce); imshow ("Original image" , img); imshow ("Reduced image by LUT" , img_reduce); Mat img_reduce2 = img.clone (); scanImageAndReduce (img_reduce2, table); imshow ("Reduced image by manual" , img_reduce2); waitKey (0 ); return 0 ; } void scanImageAndReduce (Mat &src, uchar *tab) { int channel = src.channels (); int nRow = src.rows; int nCol = src.cols * channel; if (src.isContinuous ()) { nCol *= nRow; nRow = 1 ; } uchar *p2 = src.data; for (int i = 0 ; i < nCol * nRow; ++i) *p2++ = tab[*p2]; }
3.统计时间的两个函数,当然C++自带的也可以,举例说明:
1 2 3 4 5 6 double t = getTickCount (); double time0 = ((double )getTickCount () - t) / getTickFrequency (); std::cout << time0 * 1000 << " ms" << std::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 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 #include <opencv2/core/core.hpp> #include <opencv2/highgui/highgui.hpp> #include <opencv2/imgproc/imgproc.hpp> #include <iostream> using namespace std;using namespace cv;void Sharpen (const Mat& myImage, Mat& Result) ;int main (void ) { const char * filename = "F:\\cat.jpg" ; Mat I, J, K; I = imread (filename); if (!I.data) { cout << "Error load image" << endl; return -1 ; } namedWindow ("Input" , WINDOW_AUTOSIZE); namedWindow ("Output by Sharpen" , WINDOW_AUTOSIZE); imshow ("Input" , I); double t = (double )getTickCount (); Sharpen (I, J); t = ((double )getTickCount () - t) / getTickFrequency (); cout << "Hand written function times passed in seconds: " << t << endl; imshow ("Output by Sharpen" , J); Mat kern = (Mat_ <char >(3 , 3 ) << 0 , -1 , 0 , -1 , 5 , -1 , 0 , -1 , 0 ); t = (double )getTickCount (); filter2D (I, K, I.depth (), kern); t = ((double )getTickCount () - t) / getTickFrequency (); cout << "Built-in filter2D time passed in seconds: " << t << endl; namedWindow ("Output by fliter2D" , WINDOW_AUTOSIZE); imshow ("Output by fliter2D" , K); waitKey (0 ); return 0 ; } void Sharpen (const Mat& myImage, Mat& Result) { CV_Assert (myImage.depth () == CV_8U); const int nChannels = myImage.channels (); Result.create (myImage.size (), myImage.type ()); for (int j = 1 ; j < myImage.rows - 1 ; ++j) { const uchar* previous = myImage.ptr <uchar>(j - 1 ); const uchar* current = myImage.ptr <uchar>(j); const uchar* next = myImage.ptr <uchar>(j + 1 ); uchar* output = Result.ptr <uchar>(j); for (int i = nChannels; i < nChannels*(myImage.cols - 1 ); ++i) { *output++ = saturate_cast <uchar>(5 * current[i] - current[i - nChannels] - current[i + nChannels] - previous[i] - next[i]); } } Result.row (0 ).setTo (Scalar (0 )); Result.row (Result.rows - 1 ).setTo (Scalar (0 )); Result.col (0 ).setTo (Scalar (0 )); Result.col (Result.cols - 1 ).setTo (Scalar (0 )); }
通过上面的运行结果,明显内建函数速度要快很多
其中int Mat::depth() cont
返回对象的位深度,例如16-bit signed的图像返回的就是CV_16S。所有位深度的对应信息如下:
CV_8U - 8-bit unsigned integers ( 0..255 )
CV_8S - 8-bit signed integers ( -128..127 )
CV_16U - 16-bit unsigned integers ( 0..65535 )
CV_16S - 16-bit signed integers ( -32768..32767 )
CV_32S - 32-bit signed integers ( -2147483648..2147483647 )
CV_32F - 32-bit floating-point numbers ( -FLT_MAX..FLT_MAX, INF, NAN )
CV_64F - 64-bit floating-point numbers ( -DBL_MAX..DBL_MAX, INF, NAN )
4.读写感兴趣区域ROI ROI也即是Region of interest,即针对一副图像中的感兴趣区域进行操作。 由于Mat对象的拷贝构造函数实际上拷贝的是指针,所以可以很方便地从源图像获取感兴趣获取的矩阵到新的Mat对象,而该新的Mat对象实际上关联的是源图像的像素矩阵。有2种方法获取ROI区域,其实就是Mat的两个构造函数: 原型:
1 2 Mat::Mat (const Mat& m, const Range& rowRange, const Range& colRange=Range::all () ) Mat::Mat (const Mat& m, const Rect& roi)
举例:
1 2 3 4 5 Mat srcImg; Mat imgROI = srcImg (Rect (100 , 80 , ncols, nrows)); Mat imgROI2 = srcImg (Range (100 , 100 + nrows), Range (80 , 80 + ncols));
使用举例,通过copyTo()函数将一个图像拷贝到另一个图像,copyTo()有一个重载可以附加一个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 28 29 30 #include <opencv2/core/core.hpp> #include <opencv2/highgui/highgui.hpp> #include <iostream> using namespace cv;int main (void ) { const char * srcFileName = "F:\\cat.jpg" ; const char * logoFileName = "F:\\mini.jpg" ; Mat srcImg = imread (srcFileName); Mat logoImg = imread (logoFileName); if (!srcImg.data || !logoImg.data) { std::cout << "File load error \n" ; return -1 ; } Mat roiImg = srcImg (Rect (srcImg.rows / 2 , srcImg.cols / 2 , logoImg.cols, logoImg.rows)); logoImg.copyTo (roiImg, logoImg); imshow ("ROI mask picture" , srcImg); waitKey (0 ); return 0 ; }
5.addWeighted线性混合两张图像 官方示例:http://docs.opencv.org/3.0-last-rst/doc/tutorials/core/adding_images/adding_images.html split()和merge()函数可以实现通道的与合并
6.调整图像对比度与亮度 可以用如下方法初始化一个新Mat对象的所有像素内存为0:
1 Mat new_image = Mat::zeros ( image.size (), image.type () );
官方示例:http://docs.opencv.org/3.0-last-rst/doc/tutorials/core/basic_linear_transform/basic_linear_transform.html 注意其中访问每个像素用的语法:new_image.at<Vec3b>(y, x)[c]
,其中y是行,x是列,Vec3b就是一个含有3个uchar的vector:typedef Vec<uchar, 3> Vec3b
,c表示R、G或B,取值0、1或2。 opencv内建了saturate_cast<type>(value)
将value强制转换为有效的type类型的值
7.离散傅里叶变换DFT DFT即Discrete Fourier Transform,该变换在图像处理中的应用可以做到图像增强与图像去噪、图像分割中的边缘检测、图像特征提取以及图像压缩等。 OpenCV中提供了dft()函数,可以对一维或二维浮点数数组进行正向或反向离散傅里叶变换,可以用于计算两个二维实矩阵的卷积。http://docs.opencv.org/3.0-last-rst/doc/tutorials/core/discrete_fourier_transform/discrete_fourier_transform.html
8.OpenCV内建对xml和yaml的解析 还是官方文档:http://docs.opencv.org/3.0-last-rst/doc/tutorials/core/file_input_output_with_xml_yml/file_input_output_with_xml_yml.html http://docs.opencv.org/2.4.13/modules/core/doc/xml_yaml_persistence.html
9.内建多种图形绘制 http://docs.opencv.org/3.0-last-rst/doc/tutorials/core/basic_geometric_drawing/basic_geometric_drawing.html 直线函数line 函数原型如下:
1 void line (Mat& img, Point pt1, Point pt2, const Scalar& color, int thickness=1 , int lineType=8 , int shift=0 )
thickness就是线宽,lineType就是线的结构,8表示当斜线时两个像素角相连就认为其已经相连了,如果是4则是两个像素有一整条边相连时才算相连。 画箭头可以用arrowedLine
圆circle 函数原型:
1 void circle (Mat& img, Point center, int radius, const Scalar& color, int thickness=1 , int lineType=8 , int shift=0 )
椭圆函数elipse 函数原型:
1 2 void ellipse (Mat& img, Point center, Size axes, double angle, double startAngle, double endAngle, const Scalar& color, int thickness=1 , int lineType=8 , int shift=0 ) void ellipse (Mat& img, const RotatedRect& box, const Scalar& color, int thickness=1 , int line- Type=8 )
矩形函数rectangle 函数原型:
1 2 void rectangle (Mat& img, Point pt1, Point pt2, const Scalar& color, int thickness=1 , int lineType=8 , int shift=0 ) void rectangle (Mat& img, Rect rec, const Scalar& color, int thickness=1 , int lineType=8 , int shift=0 )
画填充的多边形用fillPoly
举例:
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 #include <opencv2/core.hpp> #include <opencv2/highgui.hpp> using namespace cv;const int backWidth = 400 ;int main (void ) { Mat backImg = Mat::zeros (backWidth, backWidth, CV_8UC3); int thickness = 2 ; int lineType = 8 ; int radius = 100 ; Point start = Point (backWidth / 2 - radius, backWidth / 2 - radius); Point end = Point (backWidth / 2 + radius, backWidth / 2 + radius); rectangle (backImg, start, end, Scalar (255 , 255 , 255 ), thickness, lineType); circle (backImg, Point (backWidth / 2 , backWidth / 2 ), radius, Scalar (255 , 255 , 255 ), thickness); line (backImg, start + Point (0 , radius), end - Point (0 , radius), Scalar (255 , 255 , 255 ), thickness); line (backImg, start + Point (radius, 0 ), end - Point (radius, 0 ), Scalar (255 , 255 , 255 ), thickness); imshow ("Draw" , backImg); waitKey (0 ); return 0 ; }
RNG可以实现产生随机数,putText将窗口中显示文字 http://docs.opencv.org/3.0-last-rst/doc/tutorials/core/random_generator_and_text/random_generator_and_text.html RNG就是个随机数生成器类,与C++11中的随机数生成器类类似,下面给出这个比较常用的RNG::uniform函数的原型:
1 2 3 int RNG::uniform (int a, int b) float RNG::uniform (float a, float b) double RNG::uniform (double a, double b)
putText显示文字 原型:
1 void putText (Mat& img, const string& text, Point org, int fontFace, double fontScale, Scalar color, int thickness=1 , int lineType=8 , bool bottomLeftOrigin=false )
getTextSize可以返回字符串的大小:
1 Size getTextSize (const string& text, int fontFace, double fontScale, int thickness, int * baseLine)
举例:
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 #include <opencv2/core.hpp> #include <opencv2/highgui.hpp> using namespace cv;const int backWidth = 400 ;static Scalar randomColor (RNG& rng) { int icolor = (unsigned )rng; return Scalar (icolor & 255 , (icolor >> 8 ) & 255 , (icolor >> 16 ) & 255 ); } int main (void ) { Mat backImg = Mat::zeros (backWidth, backWidth, CV_8UC3); RNG rng (0xFFFFFFFF ) ; int thickness = 2 ; int lineType = 8 ; int radius = 100 ; Point start = Point (backWidth / 2 - radius, backWidth / 2 - radius); Point end = Point (backWidth / 2 + radius, backWidth / 2 + radius); rectangle (backImg, start, end, randomColor (rng), thickness, lineType); circle (backImg, Point (backWidth / 2 , backWidth / 2 ), radius, randomColor (rng), thickness); line (backImg, start + Point (0 , radius), end - Point (0 , radius), randomColor (rng), thickness); line (backImg, start + Point (radius, 0 ), end - Point (radius, 0 ), randomColor (rng), thickness); Point org; org.x = rng.uniform (10 , backWidth / 2 ); org.y = rng.uniform (backWidth / 2 , backWidth - 10 ); putText (backImg, "Maxwi.com" , org, rng.uniform (0 , 8 ), rng.uniform (0 , 100 ) * 0.05 + 0.1 , randomColor (rng), rng.uniform (1 , 10 ), lineType); imshow ("Draw" , backImg); waitKey (0 ); return 0 ; }
imgproc组件 手动图像处理功能。包括图像的基本处理、变换、轮廓提取、图像分割与修复、直方图与匹配http://docs.opencv.org/3.0-last-rst/doc/tutorials/imgproc/table_of_content_imgproc/table_of_content_imgproc.html 平滑处理(smoothing) 可以用来对图像降噪,当然也可以模糊图像,是一批带有blur的函数,常用的有以下几个:
方框滤波:boxFilter()
均值滤波(领域平均滤波):blur(),其实是基本的方框滤波
高斯滤波:GaussianBlur()
中值滤波:medianBlur()
双边滤波:bilateraFilter()
举例:
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 #include "opencv2/imgproc/imgproc.hpp" #include "opencv2/highgui/highgui.hpp" #include "opencv2/features2d/features2d.hpp" using namespace std;using namespace cv;int DELAY_BLUR = 500 ;int MAX_KERNEL_LENGTH = 31 ;Mat src; int display_dst (const char * winname, const Mat& img, int delay, int i) ;int main (void ) { Mat dst_blur, dst_ga, dst_me, dst_bi; src = imread ("F:\\lena.jpg" , 1 ); imshow ("Original" , src); dst_blur = src.clone (); dst_ga = src.clone (); dst_me = src.clone (); dst_bi = src.clone (); for (int i = 1 ; i < MAX_KERNEL_LENGTH; i = i + 2 ) { blur (src, dst_blur, Size (i, i), Point (-1 , -1 )); if (display_dst ("blur" , dst_blur, DELAY_BLUR, i) != 0 ) { return 0 ; } } for (int i = 1 ; i < MAX_KERNEL_LENGTH; i = i + 2 ) { GaussianBlur (src, dst_ga, Size (i, i), 0 , 0 ); if (display_dst ("GaussianBlur" , dst_ga, DELAY_BLUR, i) != 0 ) { return 0 ; } } for (int i = 1 ; i < MAX_KERNEL_LENGTH; i = i + 2 ) { medianBlur (src, dst_me, i); if (display_dst ("medianBlur" , dst_me, DELAY_BLUR, i) != 0 ) { return 0 ; } } for (int i = 1 ; i < MAX_KERNEL_LENGTH; i = i + 2 ) { bilateralFilter (src, dst_bi, i, i * 2 , i / 2 ); if (display_dst ("bilateralFilter" , dst_bi, DELAY_BLUR, i) != 0 ) { return 0 ; } } waitKey (0 ); return 0 ; } int display_dst (const char * winname, const Mat& img, int delay, int i) { imshow (winname, img); if (i < MAX_KERNEL_LENGTH) waitKey (delay); else waitKey (0 ); return 0 ; }
highgui组件 读写图像及视频文件、如何使用内建的用户界面库http://docs.opencv.org/3.0-last-rst/doc/tutorials/highgui/table_of_content_highgui/table_of_content_highgui.html
calib3d组件 相机校正及三维信息重建http://docs.opencv.org/3.0-last-rst/doc/tutorials/calib3d/table_of_content_calib3d/table_of_content_calib3d.html
feature2d组件 主要用于触点检测,边缘检测等http://docs.opencv.org/3.0-last-rst/doc/tutorials/features2d/table_of_content_features2d/table_of_content_features2d.html
video组件 主要用于对视频流的处理算法,可以进行运动提取、目标跟踪等http://docs.opencv.org/3.0-last-rst/doc/tutorials/video/table_of_content_video/table_of_content_video.html
objdetect组件 进行目标检测http://docs.opencv.org/3.0-last-rst/doc/tutorials/objdetect/table_of_content_objdetect/table_of_content_objdetect.html
ml组件:Machine Learning opencv在机器学习中的使用http://docs.opencv.org/3.0-last-rst/doc/tutorials/ml/table_of_content_ml/table_of_content_ml.html
photo组件 使用opencv进行高级图像处理http://docs.opencv.org/3.0-last-rst/doc/tutorials/photo/table_of_content_photo/table_of_content_photo.html
gpu模块 使用GPU加速计算机视觉http://docs.opencv.org/3.0-last-rst/doc/tutorials/gpu/table_of_content_gpu/table_of_content_gpu.html
opencv在iOS中的应用 http://docs.opencv.org/3.0-last-rst/doc/tutorials/ios/table_of_content_ios/table_of_content_ios.html
OpenCV Viz模块 3D虚拟空间模块http://docs.opencv.org/3.0-last-rst/doc/tutorials/viz/table_of_content_viz/table_of_content_viz.html