Mat 是一个类,由两个数据部分组成:矩阵头(包含矩阵尺寸、存储方法,存储地址等信息)和一个指向所有像素值的矩阵(根据所选存储方法的不同,矩阵可以是不同的维数)的指针。矩阵头的尺寸是常数值,但矩阵本身的尺寸会依图像的不同而不同,通常比矩阵头的尺寸大数个数量级。因此在程序中传递图像并创建副本时,大的开销是由矩阵造成的,而不是信息头。
为了解决开销问题,OpenCV 使用了引用计数机制。其思路是让每个 Mat 对象都拥有自己的信息头,但共享同一个矩阵。这通过让矩阵指针指向同一个地址实现。而拷贝构造函数则只复制信息头和矩阵指针,而不复制矩阵。
Mat A,C; //仅创建信息头部分
A = imread("1.jpg, CV_LOAD_IMAGE_COLOR); //为矩阵开辟内存
Mat B(A); //使用拷贝构造函数
C = A; //赋值运算
我们可以创建只引用部分数据的信息头。比如想要创建一个感兴趣区域(ROI,region of interest),只需要创建包含边界信息的信息头:
Mat D (A ,Rect(10, 10, 100)); //使用矩形界定
Mat E = A(Range:all(), Range(1,3)); //用行和列界定
对于矩阵的清理:通过引用计数机制来实现,我们无论什么时候复制一个 Mat 对象的信息头,都会增加矩阵的引用次数,反之,当一个头被释放后,这个计数被减一,当计数为零时,矩阵就会被清理。但某些时候想复制矩阵本身时(不是信息头和矩阵指针)。这时可以使用函数 clone() 或者 copyTo()。
Mat F = A.clone();
Mat G;
A.copyTo(G);
现在改变 F 或者 G就不会影响 Mat 信息头所指向的矩阵。
可总结为以下四个要点:
● OpenCV 函数中输出图像的内存分配是自动完成的(如果不特别指定的话)。
● 使用 OpenCV 的 C++ 接口时不需要考虑内存释放的问题。
● 赋值运算符和拷贝构造函数(构造函数)只复制信息头。
● 使用函数 clone() 或者 copyTo() 来复制一幅图像的矩阵。
存储像素值需要指定颜色空间和数据类型。颜色空间是指对于一个给定的颜色,如何组合颜色元素以对其编码。
GRB 颜色空间是最常用的颜色空间,它的基色是红色、绿色和蓝色,有时为了表示透明颜色也会加入第四个元素 alpha(A)。
颜色系统有很多,它们各有优势:
● GRB 是最常见的,这是因为人眼采用相似的工作机制,它也被显示设备所采用。
● HSV 和 HLS 把颜色分解成色调、饱和度和亮度/明度。这是描述颜色更自然的方式,比如可以通过抛弃最后一个元素,使算法对输入图像的光照条件不敏感。
● YCrCb 在 JPEG 图像格式中广泛使用。
● CIE Lab* 是一种在感知上均匀的颜色空间,它适合用来度量两个颜色之间的距离。
每个组成元素都有自己的定义域,而定义域取决去其数据类型,如何存储一个元素决定了我们在其定义域上能控制的精度。最小的数据类型是 char ,占一个字节或者 8 位,可以是有符号型(0 ~ 255 之间)或是无符号型(-127 ~ +127)之间。尽管使用三个 char 类型元素已经可以表示 1600 万种颜色可能(使用 GBR 颜色空间),但若使用 float (4 字节,32 位)或 double 型(8 字节,64 位)则能给出更加精细的颜色分辨能力。
Mat 的 " << " 运算符也能将图片写入文件中,但该运算符只对二维矩阵有效。
方法一:使用 Mat 的构造函数:
Mat M(2, 2, CV_8UC3, Scalar(0, 0, 255));
cout << "M = " << endl << " " << M << endl << endl;
运行结果:
对于二维多通道图像,需要定义其尺寸,即行数和列数。然后,需要指定存储元素的数据类型以及每个矩阵的点通道数。为此,依以下规则有多种定义:
CV_[位数][带符号与否][类型前缀]C[通道数]
比如 CV_8UC3 表示使用 8 位的 unsigned char 型,每个像素由三个元素组成三通道。而预先定义的 通道数可以多达四个。另外,Scalar 是个 short 型的向量,能使用指定的定制化值来初始化矩阵,它还可以用于表示颜色。当然,若需要更多通道,可以使用大写的宏并把通道数放在小括号中,如方法二。
//1.无参构造函数
Mat::Mat()
//2.创建行为 rows,列为 cols,类型为 type 的图像
Mat::Mat(int rows,int cols,int type)
//3.创建大小为 size,类型为 type 的图像
Mat::Mat(Size size,int type)
//4.创建行数为 rows,列数为 cols,类型为 type 的图像,并将所有元素初始化值为 s
Mat::Mat(int rows,int cols,int type,const Scalar& s)
//5.创建大小为 size,类型为 type 的图像,并将所有元素初始化为值 s
Mat::Mat(Size size,int type,const Scalar& s)
//6.将 m 赋值给新创建的对象,此处不会对图像数据进行复制,m 和新的图像共用图像数据
Mat::Mat(const Mat& m)
/*7.创建行数为 rows,列数为 cols,类型为 type 的图像,此构造函数不创建图像数据所需内存,而是直接使
用 data 所指内存,图像的步长由 step 指定8*/
Mat::Mat(int rows,int cols,int type,void* data,size_t step = AUTO_STEP)
//8.创建大小为 size,类型为 type 的图像,此构造函数不创建图像数据所需内存,而是直接使
//用 data 所指内存,图像的步长由 step 指定
Mat::Mat(int rows,int cols,int type,void* data,size_t step = AUTO_STEP)
/*9.创建的新图像为 m 的一部分,具体范围由 rowRange 和 colRange 指定,此构造函数也不进行图像数据
的复制操作,新图像与 m 共用图像数据。*/
Mat::Mat(const Mat& m,const Range& rowRange,const Range& colRange)
/*10.创建的图像为 m 的一部分,具体的范围 roi 指定,此函数也不进行图像的复制,新图像与原图像共享
图像数据 */
Mat::Mat(const Mat& m,const Rect& roi)
方法二:在 C\C++ 中通过构造函数进行初始化
Mat sz[3] = {
2,2,2};
Mat L(3, sz, CV_8UC, Scalar::all(0));
上面的例子演示了如何创建一个超过两维的矩阵:指定维数,然后传递一个指向一个数组的指针,这个数组包含两个维度的尺寸,后续两个参数与方法一中的相同。
方法三:为已存在的 IplIamge 指针创建信息头
IplImage 的头文件为: #include
IplImage* img = cvLoadImage("1,jpg",1);
Mat mtx(img); //转换 IplImage* -> Mat;
方法四:利用 Create() 函数
M.create(4, 4, CV_8UC(2));
cout << "M = " << endl << " " << M << endl << endl;
运行结果:
需要注意的是,此创建方法不能为矩阵设初值,只是在改变尺寸重新为矩阵开辟内存而已。
方法五:采用 Matlab 式的初始化方法
Matlab 形式的初始化方法有:zeros() , ones() , eyes() 。使用以下方式指定尺寸和数据类型:
Mat E = Mat::eye(4, 4, CV_64F);
cout << "E = " << endl << " " << E << endl << endl;
Mat O = Mat::ones(2, 2, CV_32F);
cout << "O = " << endl << " " << O << endl << endl;
Mat Z = Mat::zeros(3, 3, CV_8UC1);
cout << "Z = " << endl << " " << Z << endl << endl;
Mat C = (Mat_<double>(3,3) << 0, -1, 0, -1, 5, -1, 0, -1, 0);
cout << "C = " << endl << " " << C << endl << endl;
方法七:为已存在的对象创建新信息头
使用成员函数 copyTo() 或 clone() 为一个已存在的 Mat 对象创建一个新的信息头。
Mat RowClone = C.row(1).clone();
cout << "RowClone = " << endl << " " << RowClone << endl << endl;
先定义一个矩阵以供下列方法使用,可通过 randu() 函数产生的随机值来填充矩阵,需要给一个上限和下限来确保随机值在期望的范围内。
Mat r = Mat(10, 3, CV_8UC3);
randu(r, Scalar::all(0), Scalar::all(255));
风格一:OpenCV 默认风格
cout << "r (OpenCV 默认风格) = " << endl << r << ";" << endl << endl;
风格二:Python 风格
cout << "r (Python风格) = " << endl << format(r, Formatter::FMT_PYTHON) << ";" << endl << endl;
风格三:逗号风格(CSV)
cout << "r (逗号分隔风格) = " << endl << format(r, Formatter::FMT_CSV) << ";" << endl << endl;
风格三:Numpy风格
cout << "r (Numpy风格) = " << endl << format(r, Formatter::FMT_NUMPY) << ";" << endl << endl;
风格三:C语言风格
cout << "r (C语言风格) = " << endl << format(r, Formatter::FMT_C) << ";" << endl << endl;
1)定义和输出二维点
Point2f p(6, 2);
cout << "二维点 p" << p << ";\n" << endl;
2)定义和输出三维点
Point3f p3f(8, 2, 0);
cout << "三维点 p3f" << p3f << ";\n" << endl;
3)定义和输出基于 Mat 类的 std::vector
vector<float> v;
v.push_back(3);
v.push_back(4);
v.push_back(5);
cout << "基于 Mat 的 vector : shortvec = " << Mat(v) << ";\n" << endl;
4)定义和输出二维点
定义和输出存放着点的 vector 容器,以存放二维点 Point2f 为例:
vector<Point2f> points(20);
for (size_t i = 0; i < points.size(); ++i)
{
points[i] = Point2f((float)(i * 5), (float)(i % 7));
}
cout << "二维点向量 points = " << points << ";" ;
矩阵的对角线元素可以使用Mat类的diag()函数获取,该函数的定义如下:
Mat Mat::diag(int d) const
参数d=0时,表示取主对角线;当参数d>0是,表示取主对角线下方的次对角线,如d=1时,表示取主对角线下方,且紧贴主多角线的元素;当参数d<0时,表示取主对角线上方的次对角线。
如同row()和col()函数,diag()函数也不进行内存复制操作,其复杂度也是O(1)。
如果矩阵 A 与 B 大小相同,则可以使用表达式:
C = A + B + 1
其执行结果是 A 的矩阵元素与 B 的矩阵元素对应相加然后再加 1 ,并将生产的矩阵赋值给 C 变量。
下面给出Mat表达式所支持的运算。下面的列表中使用A和B表示Mat类型的对象,使用s表示Scalar对象,alpha表示double值。
加法,减法,取负:A+B, A-B, A+s, A-s, s+A, s-A,-A
缩放取值范围:A*alpha
矩阵对应元素的乘法和除法:A.mul(B), A/B, alpha/A
矩阵乘法:A*B(注意此处是矩阵乘法,而不是矩阵对应元素相乘)
矩阵转置:A.t()
矩阵求逆和求伪逆:A.inv()
矩阵比较运算:A cmpop B, A cmpop alpha, alpha cmpop A。
此处cmpop 可以是>,>=,==,!=,<=,<。如果条件成立,则结果矩阵(8U类型矩阵)的对应元素被置为255;
否则置 0。
矩阵位逻辑运算:A logicop B, A logicop s, s logicop A,~A,此处logicop可以是 &、| 和 ^ 。
矩阵对应元素的最大值和最小值:min(A, B), min(A, alpha), max(A, B),max(A, alpha)。
矩阵中元素的绝对值:abs(A)
叉积和点积:A.cross(B), A.dot(B)
下面展示了 Mat 表达式的使用方法:
#include
#include
#include
using namespace std;
using namespace cv;
int main(int argc, char* argv[]) {
system("color 2F");
//eye 为构建单位矩阵
Mat A = Mat::eye(4, 4, CV_32SC1);
cout << "A = " << endl << A << endl << endl;
Mat B = A * 3 + 1;
cout << "B = " << endl << B << endl << endl;
//diag(0) 为从第 1 列开始取对角线元素,col(1) 为取 B 的第 2 行元素
//diag(1) 为从第 2 列开始取 (rows - 1) x (cols - 1) 对角线元素
Mat C = B.diag(0) + B.col(1);
cout << "C = " << endl << C << endl << endl;
cout << "C .* diag(B) = " << C.dot(B.diag(0)) << endl;
//waitKey(0);
system("pause");
return 0;
}
运行结果:
5x4 + 8x4 + 5x4 + 5x4 = 92。
假如你有一个以前写的函数,函数的定义为:
void mycvOldFunc(IplImage *p, …);
函数的参数需要Ipllmage类型的指针。Mat转为Ipllmage,可以用简单的等号赋值操作来进行类型转换,这样实现:
Mat img(Size(320, 240), CV 8UC3);
IplImage iplimg = img; //转为IplImage结构
mycvOldFunc(&iplimq, …);//对Iplimq取地址
如果要转为 CvMat 类型,操作类似:
CvMat cvimg = img; //转为 CvMat 结构
示例:
char path[100] = "E:\\OpenCV\\1.jpg";
Mat ig = imread(path);
IplImage img1 = ig;
IplImage* img2 = &img1;
cvShowImage("img1", img2);
需要特别注意的是,类型转换后,IplImage 和 CvMat 与 Mat 共用同一矩阵数据,Ipllmage 和 CvMat 没有引用计数功能,如果上例中的 img 中数据被释放,iplimg 和 cvimg 也就失去了数据。因此要牢记不可将 Mat 对象提前释放。
Mat 类有两个构造函数,可以实现 Ipllmage 和 CvMat 到 Mat 的转换。这两个函数都有一个参数 copyData。如果 copyData 的值是 false,那么 Mat 将与 Ipllmage 或 CvMat 共用同一矩阵数据;如果值是 true, Mat 会新申请内存,后将 Ipllmage 或 CvMat 的数据复制到 Mat 的数据区。
如果共用数据,Mat 也将不会使用引用计数来管理内存,需要开发者自己来管理。建议做此转换是将参数置为 true,这样内存管理变得简单。
Mat::Mat(const CvMat* m, bool copyData = false)
Mat::Mat(const IplImage* img, bool copyData = false)
例如:
IplImage* iplimg = cvLoadImage("1.jpg",1);
Mat im(iplimg, true);
Point 类数据结构表示了二维坐标系下的点,即由其图像坐标 x 和 y 指定的 2D 点。用法如下:
Point point;
point.x = 10;
point.y = 8;
//或者
Point point = Point(10, 8);
//另外,在 OpneCV 下有如下定义:
typedef Point_<int> Point2i;
typedef Point2i Point;
typedef Point_<float> Point2f;
所以,Point_< int > 、Point2i、Point 互相等价,Point_< float > 、Point2f 互相等价。
Scalar() 表示有四个元素的数组,在 OpenCV 中被大量用于传递像素值,如果用不到第四个参数,则不必写出来。
如果给出以下颜色参数表达式:
Scalar(a, b, c);
那么定义的 RGB 颜色值:红色分量为 c ,绿色分量为 b,蓝色分量为 a 。
Scalar 类的源头为 Scalar_ 类,而 Scalar_ 类是 Vec4x 的一个变种,我们常用的 Scalar 其实就是 Scalar< double >。这就解释了为什么很多函数的参数输入可以是 Mat ,也可以是 Scalar。
Size 类的相关源码:
typedef Size_<int> Size2i;
typedef Size2i Size;
其中,Size_ 是个模板类,Size_< int > 、Size2i、Size 这三个类型名等价。
对于 Size_ 模板类的定义中,我们使用最频繁的是下面这个构造函数:
Size_(_Tp _width, _Tp _heigth);
//模板类的高度和宽度
_Tp width, height;
//于是可用 XXX.width 与 XXX.heigth 分别表示其宽度和高度。
Rect 类的成员变量有 x、y、width、heigth,分别为左上角点的坐标和矩形的宽和高。常用的成员函数有:Size() 返回值为 Size,area() 返回矩形的面积,contains(Point) 判断点是否在矩形内,inside(Rect) 判断矩形是否在矩形内,tl() 返回左上角点坐标,br() 返回右下角点坐标。若是想求两个矩形的交集和并集,可用如下格式:
Rect rect = rect1 & rect2;
Rect rect = rect1 | rect2;
若想让矩形进行平移操作和缩放操作,可以这样写:
Rect rectShift = rect + point;
Rect rectScale = rect + size;
cvtColor() 函数是 OpenCV 中的颜色空间装换函数,可以实现 RGB 颜色空间想 HSV、HIS 等颜色空间转换,也可以转换为灰度图像。原型如下:
void cvtColor(InputArray src, OutputArray dst, int code, int dstCn = 0)
第一个参数为输入图像,第二个参数我输出图像,第三个参数为颜色空间转换的标识符(见下表),第四个参数为目标图像的通道数,若该参数是 0,则表示目标图像取源图像的通道数。
下面是一个调用示例:
cvtColor(srcImage, dstImage, COLOR_GRAY2BGR); //原始图转化为灰度图
需要注意的是:OpenCV 中默认的图片通道存储顺序是 BGR ,即蓝绿红,而不是 RGB。
下面给出颜色空间转换的简单代码:
#include
using namespace std;
using namespace cv;
int main(int argc, char** argv)
{
//1.载入图片
Mat srcImage = imread("8.jpg"),dstImage;
//2.转换颜色空间
cvtColor(srcImage, dstImage, COLOR_BGR2Lab);
//3.显示原图和效果图
imshow("原图", srcImage);
imshow("效果图", dstImage);
waitKey();
//system("pause");
return 0;
}
● Matx 是个轻量级的 Mat ,必须在使用前规定好大小,比如一个 2 * 3 的 float 型的 Matx,可以声明为 Matx23f。
● Vec 是 Matx 的一个派生类,是一个一维的 Matx ,跟 vector 很相似。
● Range 类其实就是为了使 OpenCV 的使用更像 MATLAB 而产生的。
● OpenCV 中防止内存泄漏的函数有 alignPtr、alignSize、allocate、deallocate、fastMalloc、fastFree 等。
●
● 显示文字相关的函数有 getTextSize、cvInitFont、putText。
● 作图相关的函数有 circle、clipLine、ellipse、ellipse2Poly、line、rectangle、polylines、类 LineIterator。
● 填充相关的函数有 fillConvexPoly、fillPoly。
● OpenCV 中 RNG() 函数的作用为初始化随机数状态的生成器。
便于代码的编写,先在开头加上宏定义:
#define WINDOW_WIDTH 600 //定义窗口的大小
描述:自定义的绘制函数,实现了绘制不同角度、相同尺寸的椭圆
void DrawEclipse(Mat img, double angle)
{
int thickness = 2;
int lineType = 8;
ellipse(img,
Point(WINDOW_WIDTH / 2, WINDOW_WIDTH / 2), //中心点
Size(WINDOW_WIDTH / 4, WINDOW_WIDTH / 16), //大小位于该尺寸的矩形内
angle, //旋转角度
0, //拓展的弧度从 0 到 360
360,
Scalar(255,129,0), //图形颜色 ,该配置代表蓝色
thickness, //线宽为 2
lineType); //线型为 8 (8 联通线型)
}
此函数的解析如下:
函数中调用了 OpenCV 的 ellipse 函数,将椭圆画到图像 img 上,椭圆中心点为 (WINDOW_WIDTH / 2, WINDOW_WIDTH / 2),并且大小位于矩形 (WINDOW_WIDTH / 4, WINDOW_WIDTH / 16) 内。椭圆旋转角度为 angle,拓展的弧度从 0 到 360 度。图形颜色为 Scalar(255,129,0) 代表的蓝色,线宽 thickness 为 2,线型 lineType 为 8(8 联通线型)。
描述:自定义的绘画函数,实现了实心圆的绘制
void DrawFilledCircle(Mat img, Point center)
{
int thickness = -1;
int lineType = 8;
circle(
img, //目标图像
center, //圆心点
WINDOW_WIDTH / 32, //半径
Scalar(0,0,255), //颜色 按 GBR 为红色
thickness, //线粗
lineType);
}
函数调用了 OpenCV 中的 cirlce 函数,将圆画到图像 img 上,圆心由点 center 定义,圆的半径为 WINDOW_WIDTH / 32 , 圆的颜色为 Scalar(0,0,255),按 BGR 格式为红色,线粗定义为 thickness = -1 ,因此绘制的圆是实心的。
描述:实现了凹多变形的绘制
void DrawPolygon(Mat img)
{
int lineType = 8;
//创建一些点
Point rookPoints[1][20];
rookPoints[0][0] = Point(WINDOW_WIDTH / 4, 7 * WINDOW_WIDTH / 8);
rookPoints[0][1] = Point(3 * WINDOW_WIDTH / 4, 7 * WINDOW_WIDTH / 8);
rookPoints[0][2] = Point(3 * WINDOW_WIDTH / 4, 13 * WINDOW_WIDTH / 16);
rookPoints[0][3] = Point(11 * WINDOW_WIDTH / 16, 13 * WINDOW_WIDTH / 16);
rookPoints[0][4] = Point(19 * WINDOW_WIDTH / 32, 3 * WINDOW_WIDTH / 8);
rookPoints[0][5] = Point(3 * WINDOW_WIDTH / 4, 3 * WINDOW_WIDTH / 8);
rookPoints[0][6] = Point(3 * WINDOW_WIDTH / 4, WINDOW_WIDTH / 8);
rookPoints[0][7] = Point(26 * WINDOW_WIDTH / 40, WINDOW_WIDTH / 8);
rookPoints[0][8] = Point(26 * WINDOW_WIDTH / 40, WINDOW_WIDTH / 4);
rookPoints[0][9] = Point(22 * WINDOW_WIDTH / 40, WINDOW_WIDTH / 4);
rookPoints[0][10] = Point(22 * WINDOW_WIDTH / 40, WINDOW_WIDTH / 8);
rookPoints[0][11] = Point(18 * WINDOW_WIDTH / 40, WINDOW_WIDTH / 8);
rookPoints[0][12] = Point(18 * WINDOW_WIDTH / 40, WINDOW_WIDTH / 4);
rookPoints[0][13] = Point(14 * WINDOW_WIDTH / 40, WINDOW_WIDTH / 4);
rookPoints[0][14] = Point(14 * WINDOW_WIDTH / 40, WINDOW_WIDTH / 8);
rookPoints[0][15] = Point(WINDOW_WIDTH / 4, WINDOW_WIDTH / 8);
rookPoints[0][16] = Point(WINDOW_WIDTH / 4, 3 * WINDOW_WIDTH / 8);
rookPoints[0][17] = Point(13 * WINDOW_WIDTH / 32, 3 * WINDOW_WIDTH / 8);
rookPoints[0][18] = Point(5 * WINDOW_WIDTH / 16, 13 * WINDOW_WIDTH / 16);
rookPoints[0][19] = Point(WINDOW_WIDTH / 4, 13 * WINDOW_WIDTH / 16);
const Point* ppt[1] = {
rookPoints[0] };
int npt[] = {
20 };
fillPoly(
img, //目标图像
ppt, //多边形顶点集
npt, //多边形顶点数目
1, //要绘制的多边形数量
Scalar(255, 255, 255), //多边形颜色--白色
lineType);
}
该函数调用了 OpenCV 中的 fillPoly 函数,用于将多边形画到图 img 上,其中多边形的顶点集为 ppt ,要绘制的多边形的顶点数目为 npt,要绘制的多边形数量为 1,多边形的颜色设置为白色 Scalar(255, 255, 255)。
描述:实现了线的绘制
void DrawLine(Mat img, Point start, Point end) //绘制直线
{
int thickness = 2;
int lineType = 8;
line(
img, //目标图像
start, //直线起点
end, //直线终点
Scalar(0, 0, 0), //黑色
thickness, //线的粗细
lineType
);
}
该函数调用了 OpenCV 中的 line 函数,用于在图像 img 上画一条从点 start 到 end 的直线段,线的颜色为 Scalar(0, 0, 0) 代表的黑色,先的粗细为 thickness 为 2,且此线为 8 联通(lineType = 8)。
main 函数的写法非常简单,先创建空白的 Mat 图像,然后调用绘制化学中的原子示例图,接着绘制组合图,最后显示绘制出的图像。
int main(int argc, char** argv)
{
//创建空白的 Mat 图像
Mat atomImage = Mat::zeros(WINDOW_WIDTH, WINDOW_WIDTH, CV_8UC3);
Mat rookImage = Mat::zeros(WINDOW_WIDTH, WINDOW_WIDTH, CV_8UC3);
//-------------1.绘制化学中的原子示例图------------------------
//1.1先绘制出椭圆
DrawEclipse(atomImage, 90);
DrawEclipse(atomImage, 0);
DrawEclipse(atomImage, 45);
DrawEclipse(atomImage, -45);
//1.2再绘制圆心
DrawFilledCircle(atomImage, Point(WINDOW_WIDTH / 2, WINDOW_WIDTH / 2));
//-------------2.绘制组合图----------------------------
//2.1先绘制出椭圆
DrawPolygon(rookImage);
//2.2绘制矩形
rectangle(
rookImage,
Point(0, 7 * WINDOW_WIDTH / 8), //矩形的一个顶点
Point(WINDOW_WIDTH, WINDOW_WIDTH), //对角线上的另一个顶点
Scalar(0, 255, 255),
-1,
8
);
//2.3绘制一些线段
DrawLine(rookImage, Point(0, 15 * WINDOW_WIDTH / 16),
Point(WINDOW_WIDTH, 15 * WINDOW_WIDTH / 16));
DrawLine(rookImage, Point(WINDOW_WIDTH / 4, 7 * WINDOW_WIDTH / 8),
Point(WINDOW_WIDTH / 4, WINDOW_WIDTH));
DrawLine(rookImage, Point(WINDOW_WIDTH / 2, 7 * WINDOW_WIDTH / 8),
Point(WINDOW_WIDTH / 2, WINDOW_WIDTH));
DrawLine(rookImage, Point(3 * WINDOW_WIDTH / 4, 7 * WINDOW_WIDTH / 8),
Point(3 * WINDOW_WIDTH / 4, WINDOW_WIDTH));
//-------------------显示绘制出的图像---------------------------
imshow(WINDOW_NAME1, atomImage);
moveWindow(WINDOW_NAME1, 0, 200);
imshow(WINDOW_NAME2, rookImage);
moveWindow(WINDOW_NAME2, WINDOW_WIDTH, 200);
waitKey(0);
//system("pause");
return 0;
}