opencv学习笔记——数据结构与基本绘图

来源:互联网 发布:淘宝化妆品推广文章 编辑:程序博客网 时间:2024/06/04 17:46

1、基础图像容器Mat

1.1 Mat结构

  早期OpenCV的函数库是基于C接口够贱的,使用名为IplImage的结构体存储图像。缺点是退出之前必须release,否则会造成内存泄漏。

  OpenCV2.0引入了新的C++接口,使用Mat类数据结构,好处是不必手动为其开辟空间,不必在不需要是立即将空间释放。

  Mat类由两个数据部分组成:矩阵头(矩阵尺寸、存储方法、存储地址等)和一个指向存储所有像素值的矩阵(可以是不同的维数)的指针。每个Mat对象有自己的信息头,但通过指针可以共享同一个矩阵。

//所有Mat对象最终都指向同一个数据矩阵,但信息头不同Mat A, C;               //仅创建信息头部分A = imread("1.jpg", CV_LOAD_IMAGE_COLOR);       //为矩阵开辟空间Mat B(A);               //使用拷贝构造函数C = A;                  //赋值运算符
//只引用部分数据的信息头,创建感兴趣区域(ROI)Mat D(A, Rect(10, 10, 100, 100));       //使用矩形界定Mat E = A(Range:all(), Range(1, 3));    //用行和列来界定

  复制/释放一个Mat对象的信息头,会增加/减少矩阵的引用次数,当计数值为零时,矩阵被清理。想要复制矩阵本身而不只是矩阵指针,使用函数clone()和copyTo()。

Mat F = A.clone();Mat G;A.copyTo(G);

1.2 像素值的存储方法

  存储像素值需要制定颜色空间和数据类型。颜色空间是指针对一个给定的颜色,如何组合颜色元素以对其编码。例如RGB颜色空间的基色是红、绿、蓝,以及表示透明颜色的alpha(A)。

  几种颜色空间:

  • RGB最常用,采用人眼相似的工作机制。
  • HSV和HLS把颜色分解成色调、饱和度和亮度/明度,是描述颜色更自然的方法。
  • YCrCb在JPEG图像格式中广泛使用。
  • CIE L*a*b*是一种在感知上均匀的颜色空间,适合用来度量两个颜色之间的距离。

  每个组成元素都有自己的定义域,定义域取决于其数据类型,数据类型决定了在其定义域上能够控制的精度。

1.3 显式创建Mat对象

  使用Mat()构造函数

Mat M(2, 2, CV_8UC3, Scalar(0, 0, 255));cout << "M = " << endl << " " << M << endl << endl;

  首先定义其尺寸,即行数和列数。然后指定存储元素的数据类型以及每个矩阵点的通道数,定义规则:CV_[位数][带符号与否][类型前缀]C[通道数]。Scalar是short型的向量,使用指定的定制化值来初始化矩阵,可以用于表示颜色。

  在C\C++中通过构造函数进行初始化

int sz[3] = {2, 2, 2};Mat L(3, sz, CV_8UC, Scalar:all(0));

  创建超过二维的矩阵:指定维数,传递一个指向数组的指针,数组包含每个维度的尺寸。

  使用IplImage指针创建信息头

IplImage* img = cvLoadImage("1.jpg", 1);Mat mtx(img);                               //转换IplImage*为Mat

  使用Create()函数

M.Create(4, 4, CV_8UC(2));

  此方法不能为矩阵设初值,改变尺寸是重新为矩阵开辟内存。

  采用Matlab式初始化方式

Mat E = Mat::eye(4, 4, CV_64F);Mat O = Mat::ones(2, 2, CV_32F);Mat Z = Mat::zeros(3, 3, CV_8UC1);

  采用Matlab形式初始化方式:zeros()、ones()、eye()。

  小矩阵使用逗号分隔式初始化函数

Mat C = (Mat_<double>(3, 3) << 0, -1, 0, -1, 5, -1, 0, -1, 0);

  为已存在的对象创建新信息头

Mat RowClone = C.row(1).clone();

  使用函数clone()或copyTo()为已存在的Mat对象创建一个新的信息头。

1.4 格式化输出方法

Mat r = Mat(10, 3, CV_8UC3);randu(r, Scalar::all(0), Scalar::all(255));

  给定一个上限和下限,使用randu()函数产生的随机值来填充矩阵。

  OpenCV默认风格

cout << "r (OpenCV默认风格) = " << r << ";" << endl << endl; 

  Python风格

//OpenCV2版cout << "r (Python风格) = " << format(r, "python") << ";" << endl << endl;//OpenCV3版cout << "r (Python风格) = " << format(r, Formatter::FMT_PYTHON) << ";" << endl << endl;

  逗号分隔风格

//OpenCV2版cout << "r(逗号分隔风格) = " << format(r, "cvs") << ";" << endl << endl;//OpenCV3版cout << "r(逗号分隔风格) = " << format(r, Formatter::FMT_CVS) << ";" << endl << endl;

  Numpy风格

//OpenCV2版cout << "r(Numpy风格) = " << format(r, "numpy") << ";" << endl << endl;//OpenCV3版cout << "r(Numpy风格) = " << format(r, Formatter::FMT_NUMPY) << ";" << endl << endl;

  C语言风格

//OpenCV2版cout << "r(C语言风格) = " << format(r, "C") << ";" << endl << endl;//OpenCV3版cout << "r(C语言风格) = " << format(r, Formatter::FMT_C) << ";" << endl << endl;

1.5 输出其他常用数据结构

  定义二维点

Point2f p(6, 2);

  定义三维点

Point3f p3f(8, 2, 0);

  定义基于Mat的std::vector

vector<float> v;v.push_back(3);v.push_back(5);v.push_back(7);

  定义std::vector点

vector<Point2f> points(20);for (size_t i; i < points.size(); ++i)    points[i] = Point2f((flaot)i * 5, float(i % 7));

2、示例程序

2.1 常用数据结构和函数

  Point类:点的表示

//定义方式Point point;point.x = 10;point.y = 8;Point point = Point(10, 8);//OpenCV定义typedef Point_<int> Point2i;typedef Point2i Point;typedef Point_<float> Point2f;

  Scalar类:颜色的表示
  Scalar()表示4个元素的数组,被大量用于传递像素值。

Scalar(a, b, c);

  定义RGB值,a表示蓝色,b表示绿色,c表示红色,只使用单个参数。
  Scalar类基于Scalar_类,Scalar_类基于Vec4x类。很多函数的参数可以是Mat,也可以是Scalar。

  Size类:尺寸的表示

//Size类的定义typedef Size_<int> Size2i;typedef Size2i Size;Size(5, 5);

  构造出的Size的宽度和高度都为5,可以用XXX.height和XXX.width表示。

  Rect类:矩形的表示
  Rect类的成员变量有x、y、width、height,表示左上角点的坐标和矩形的宽和高。常用的成员函数:
    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()函数:颜色空间转换

//函数原型void cvtColor(InputArray src, OutputArray dst, int code, int dstCn = 0)

  第一个参数为输入图像,第二个参数为输出图像,第三个参数为颜色空间转换的标识符,第四个参数为目标图像的通道数,0表示取源图像的通道数。

2.2 其他知识点

  • Matx是轻量级的Mat,必须在使用前规定好大小,如2*3的float型Matx,声明为Matx23f;
  • Vec是Matx的派生类,是一维的Matx;
  • Range类类似MATLAB使用;
  • 防止内存溢出函数:alignPtr、alignSize、allocate、deallocate、fastMalloc、fastFree;
  • math.h中函数fastAtan2计算向量角度、cubeRoot计算立方根、cvCeil向上取整、cvFloor向下取整、cvRound四舍五入、cvIsInf判断自变量是否无穷大、cvIsNaN判断自变量是否不是一个数。
  • 显示文字的函数:getTextSize、cvInitFont、putText;
  • 作图函数:circle、clipLine、ellipse、ellipse2Poly、line、rectangle、polylines、类LineIterator;
  • 填充函数:fillConvexPoly、fillPoly;
  • RNG()函数为初始化随机数状态的生成器。

2.3 示例程序

#include <opencv2/core/core.hpp>#include <opencv2/highgui/highgui.hpp>#include <opencv2/imgproc/imgproc.hpp>using namespace cv;/* 定义辅助宏 */#define WINDOW_NAME1    "【绘制图1】"                //为窗口标题定义的宏#define WINDOW_NAME2    "【绘制图2】"                //为窗口标题定义的宏#define WINDOW_WIDTH    600                         //定义窗口大小的宏/* 绘制不同角度相同尺寸的椭圆 */void DrawEllipse(Mat img, double angle){    int thickness = 2;    int lineType = 8;    //将椭圆画到img上,中心为点Point,位于矩形Size内,椭圆角度为angle,扩展弧度0到360度    //图形颜色为Scalar,线宽为2,线型为8    ellipse(img, Point(WINDOW_WIDTH / 2, WINDOW_WIDTH / 2), Size(WINDOW_WIDTH / 4, WINDOW_WIDTH / 16),         angle, 0, 360, Scalar(255, 129, 0), thickness, lineType);}/* 绘制实心圆 */void DrawFilledCircle(Mat img, Point center){    int thickness = -1;    int lineType = 8;    //将圆画到img上,圆心为点center,半径为WINDOW_WIDTH/32,颜色为Scalar,线粗-1,线型为8    circle(img, center, WINDOW_WIDTH / 32, Scalar(0, 0, 255), thickness, lineType);}/* 绘制凹多边形 */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 };    //多边形的顶点集为ppt,顶点数为npt,绘制数量为1,颜色为Scalar,线型为8    fillPoly(img, ppt, npt, 1, Scalar(255, 255, 255), lineType);}/* 绘制线 */void DrawLine(Mat img, Point start, Point end){    int thickness = 2;    int lineType = 8;    //画一条从start到end的线    line(img, start, end, Scalar(0, 0, 0), thickness, lineType);}int main(void){    Mat atomImage = Mat::zeros(WINDOW_WIDTH, WINDOW_WIDTH, CV_8UC3);    Mat rookImage = Mat::zeros(WINDOW_WIDTH, WINDOW_WIDTH, CV_8UC3);    //绘制出椭圆    DrawEllipse(atomImage, 90);    DrawEllipse(atomImage, 0);    DrawEllipse(atomImage, 45);    DrawEllipse(atomImage, -45);    //绘制圆心    DrawFilledCircle(atomImage, Point(WINDOW_WIDTH / 2, WINDOW_WIDTH / 2));    //绘制多边形    DrawPolygon(rookImage);    //绘制矩形    rectangle(rookImage, Point(0, 7 * WINDOW_WIDTH / 8), Point(WINDOW_WIDTH, WINDOW_WIDTH), Scalar(0, 255, 255), -1, 8);    //绘制线段    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);    return 0;}