- 选取图像区域
- Mat_类
- Mat类的内存管理
- 图像标注
选取图像局部区域
- Mat类提供了多种方法选取图像局部区域
- 需要注意的是,这些方法并不进行内存的复制操作,新对象只是与原始对象共享 - 相同的数据区域,而不新申请内存,这也是这些方法执行速度比较快的原因 选取单行或单列
Mat::row(int y) const
Mat::col(int x) const
参数 y 和 x 分别是行索引和列索引。
Mat img = imread(".../opencv/sources/samples/data/HappyFish.jpg");
// 259 -194
cout << img.cols<<" - " << img.rows << endl;
imshow("mat", img);
//获取图像矩阵的第100行数据
Mat line = img.row(100);
cout << line << endl;
- 选取多行或多列
通过使用 Range 类,可以选取矩阵的多行或多列。
Range 类是 OpenCV 的一个基本数据类型,其定义如下:
1.它有两个重要变量 star 和 end,表示的范围为从 start 到 end,包含 start 但不包含 end。
2.Range类还有一个静态方法 all(),可直接选取所有行或者所有列,作用类似 Matlab 中的“:”。
//一次获取图像矩阵的多行和多列
//获取全部行,获取列100到149数据
Mat range = img(Range::all(), Range(100, 150));
//cout << range << endl;
imshow("range", range);
原图
截取部分图像
- 选取感兴趣区域
有两种方法可以从图像中选取感兴趣区域(ROI,Region of Interest)
1. 使用构造函数,如以下代码所示:
//3.选取感兴趣区域
//通过在原图中设置截取区域
//3.1.构造函数中设置
Mat roi1(img, Rect(80, 60, 160, 120));
imshow("roi1", roi1);
Mat roi2(img, Range(80,160),Range(60,120));
imshow("roi2", roi2);
2. 使用括号运算符,如以下代码所示:
//上面的代码也可以这样写法
Mat roi3 = img(Rect(80, 60, 160, 120));
Mat roi4 = img(Range(80, 160), Range(60, 120));
原图与感兴趣区域
- 选取对角线
//4.选取对角线 diag()函数
Mat M = (Mat_<int>(4, 4) <<
1, 2, 3, 0,
4, 5, 6, 0,
7, 8, 9, 0,
10, 11, 12, 13);
//取主对角线
Mat d0 = M.diag(0);
cout << "d0" << endl<<" " << d0 << endl;
//取主对角线上方第2条对角线
Mat d2 = M.diag(2);
cout << "d2" << endl << " " << d2 << endl;
//取主对角线下方第1条对角线
Mat d1 = M.diag(-1);
cout << "d1" << endl << " " << d1 << endl;
打印结果:
d0
[1;
5;
9;
13]
d2
[3;
0]
d1
[4;
8;
12]
- 输出 重载符: "<<"
Mat_类
- Mat_类是对Mat类的一个轻量级包装,它是一个模板类,使得访问元素时可以不需要指定元素类型,使得代码简洁,又减少了出错的可能性
- 将之间图像元素遍历的代码使用Mat_类进行改造如下
//Mat_ 类使用
//指针方式遍历像素
Mat grayImg(480, 640, CV_8UC1);
Mat_<uchar> grayImg_ = grayImg;
for (int i = 0; i < grayImg.rows; i++)
{
//获取指针对象,不用设置泛型
uchar* p = grayImg_.ptr(i);
//比较
uchar* p2 = grayImg.ptr<uchar>(i);
for (int j = 0; j < grayImg.cols; j++)
{
grayImg_(i, j) = (i + j) % 255;
//grayImg(i, j) = 100; 不能使用
}
}
imshow("grayImg_", grayImg_);
Mat类的内存管理
Mat类由两个数据部分组成
- 矩阵头:矩阵头的尺寸时常数值
- 存储所有像素值的内存指针uchar* data
复制矩阵数据往往要花费较多时间,尤其是大矩阵。为了解决矩阵数据的传递,OpenCV使用了引用计数机制。
- 思路是让每个Mat对象有自己的矩阵头信息,但多个Mat对象可以共享同一个矩阵数据,即让多个矩阵指针data指向同一个地址
- 所以当一个Mat对象中的data改变了,其他的Mat对象的数据内容也跟着改变
标注图像
- 在图像上进行标记或标注,方便显示算法的结果
- 常见场景为:识别原始图像中的人脸或物体,在图像上用矩形框框出物体显示物体位置,并表示出物体的类别及其他信息
OpenCV 中常用的绘制函数有:
1.绘制直线
void cv::line(InputOutputArray img, Point pt1, Point pt2, const Scalar& color, int thickness=1,
int lineType=LINE_8, int shift=0)
img 为需要绘制标记的图像;pt1 和 pt2 分别为直线起点和终点的坐标;
color 为直线的颜色;thickness 为直线的宽度;lineType 为直线的类型
2.绘制圆
void cv::circle(InputOutputArray img, Point center, int radius, const Scalar& color, int
thickness=1, int lineType=LINE_8, int shift=0)
img 为需要绘制标记的图像;center 为圆心坐标;radius 为圆半径;color 为圆的颜色;
thickness 为圆的线条宽度;lineType 为圆的线条类型
3.绘制矩形
void cv::rectangle(InputOutputArray img, Point pt1, Point pt2, const Scalar& color, int
thickness=1, int lineType=LINE_8, int shift=0)
img 为需要绘制标记的图像;pt1 为矩形左上角坐标, pt2 为矩形右下角坐标;color 为
矩形边颜色;thickness 为矩形边的宽度;lineType 为矩形边的类型;shift 为坐标小数位数。
void cv::rectangle(InputOutputArray img, Rect rect, const Scalar& color, int thickness=1, int
lineType=LINE_8, int shift=0)
img 为需要绘制标记的图像;rect 为所要绘制的矩形;color 为矩形边颜色;thickness
为矩形边的宽度;lineType 为矩形边的类型
4.绘制文字
void cv::putText(InputOutputArray img, const String& text, Point org, int fontFace, double
fontScale, Scalar color, int thickness=1, int lineType=LINE_8, bool bottomLeftOrigin=false)
img 为需要绘制标记的图像;text 为要显示的文字;org 为文字左下角的坐标;fontFace
为字体;fontScale 为相对于字体基础大小的缩放比例;color 为文字的颜色;thickness 为文
字线条的宽度;lineType 为文字线条的类型;bottomLeftOrigin 为 true 时,图像坐标的原点
在左下角,bottomLeftOrigin 为 false 时,图像坐标的原点在左上角。
例子
Mat img = imread(".../opencv/sources/samples/data/lena.jpg");
//图像标记
//1.绘制绿色直线
line(img, Point(300, 300), Point(450, 100), Scalar(0, 255, 0), 3);
//绘制黄色园
circle(img, Point(200, 200), 100, Scalar(0, 255, 255), 3);
//绘制红色矩形
rectangle(img, Point(20, 20), Point(50, 100), Scalar(0, 0, 255), 3);
//绘制蓝色矩形
rectangle(img, Rect(30,30,40,100), Scalar(255, 0, 0), 3);
//绘制白色文字
putText(img, "Beautiful girl", Point(50, 50), FONT_HERSHEY_COMPLEX,
1.0, Scalar(255, 255, 255), 3);
imshow("img", img);