OpenCV入门教程(2)-Mat类之像素的读写
2017-01-05 23:48
375 查看
一、矩阵元素的基本表达
对于单通道图像,其元素类型一般为 8U(即 8 位无符号整数),当然也可以是 16S、32F等;这些类型可以直接用 uchar、short、float 等 C/C++语言中的基本数据类型表达。
如果多通道图像,如 RGB 彩色图像,需要用三个通道来表示。在这种情况下,如果依然将图像视作一个二维矩阵,那么矩阵的元素不再是基本的数据类型。
OpenCV 中有模板类 Vec,可以表示一个向量。OpenCV 中使用 Vec 类预定义了一些小向量,可以将之用于矩阵元素的表达。
例如 8U 类型的 RGB 彩色图像可以使用 Vec3b,3 通道 float 类型的矩阵可以使用 Vec3f。
对于 Vec 对象,可以使用[]符号如操作数组般读写其元素,如:
二、像素值的读写
很多时候,我们需要读取某个像素值,或者设置某个像素值;在更多的时候,我们需要对整个图像里的所有像素进行遍历。OpenCV 提供了多种方法来实现图像的遍历。
2.1、at()函数
函数 at()来实现读取矩阵中的某个像素,或者对某个像素进行赋值操作。下面两行代码演示了 at()函数的使用方法。
如果要对图像进行遍历,可以参考下面的例程。这个例程创建了两个图像,分别是单通道的 grayim 以及 3 个通道的 colorim,然后对两个图像的所有像素值进行赋值,最后现实结果。
需要注意的是,如果要遍历图像,并不推荐使用 at()函数。使用这个函数的优点是代码的可读性高,但是效率并不是很高。
显示结果如下图所示:
2.2 使用迭代器
如果你熟悉 C++的 STL 库,那一定了解迭代器(iterator)的使用。迭代器可以方便地遍历所有元素。Mat 也增加了迭代器的支持,以便于矩阵元素的遍历。
下面的例程功能跟上一节的例程类似,但是由于使用了迭代器,而不是使用行数和列数来遍历,所以这儿没有了 i 和 j 变量,图像的像素值设置为一个随机数。
例程的输出结果如图,所示
2.3 通过数据指针
使用 IplImage 结构的时候,我们会经常使用数据指针来直接操作像素。通过指针操作来访问像素是非常高效的,但是你务必十分地小心。C/C++中的指针操作是不进行类型以及越界检查的,如果指针访问出错,程序运行时有时候可能看上去一切正常,有时候却突然弹出“段错误”(segment fault)。
当程序规模较大,且逻辑复杂时,查找指针错误十分困难。对于不熟悉指针的编程者来说,指针就如同噩梦。如果你对指针使用没有自信,则不建议直接通过指针操作来访问像素。虽然 at()函数和迭代器也不能保证对像素访问进行充分的检查,但是总是比指针操作要可靠一些。
如果你非常注重程序的运行速度,那么遍历像素时,建议使用指针。下面的例程演示如何使用指针来遍历图像中的所有像素。此例程实现的操作跟第 2.1节中的例程完全相同。例程代码如下:
运行结果遇上面的一致。
对于单通道图像,其元素类型一般为 8U(即 8 位无符号整数),当然也可以是 16S、32F等;这些类型可以直接用 uchar、short、float 等 C/C++语言中的基本数据类型表达。
如果多通道图像,如 RGB 彩色图像,需要用三个通道来表示。在这种情况下,如果依然将图像视作一个二维矩阵,那么矩阵的元素不再是基本的数据类型。
OpenCV 中有模板类 Vec,可以表示一个向量。OpenCV 中使用 Vec 类预定义了一些小向量,可以将之用于矩阵元素的表达。
typedef Vec<uchar, 2> Vec2b; typedef Vec<uchar, 3> Vec3b; typedef Vec<uchar, 4> Vec4b; typedef Vec<short, 2> Vec2s; typedef Vec<short, 3> Vec3s; typedef Vec<short, 4> Vec4s; typedef Vec<int, 2> Vec2i; typedef Vec<int, 3> Vec3i; typedef Vec<int, 4> Vec4i; typedef Vec<float, 2> Vec2f; typedef Vec<float, 3> Vec3f; typedef Vec<float, 4> Vec4f; typedef Vec<float, 6> Vec6f; typedef Vec<double, 2> Vec2d; typedef Vec<double, 3> Vec3d; typedef Vec<double, 4> Vec4d; typedef Vec<double, 6> Vec6d;
例如 8U 类型的 RGB 彩色图像可以使用 Vec3b,3 通道 float 类型的矩阵可以使用 Vec3f。
对于 Vec 对象,可以使用[]符号如操作数组般读写其元素,如:
Vec3b color; //用 color 变量描述一种 RGB 颜色 color[0]=255; //B 分量 color[1]=0; //G 分量 color[2]=0; //R 分量
二、像素值的读写
很多时候,我们需要读取某个像素值,或者设置某个像素值;在更多的时候,我们需要对整个图像里的所有像素进行遍历。OpenCV 提供了多种方法来实现图像的遍历。
2.1、at()函数
函数 at()来实现读取矩阵中的某个像素,或者对某个像素进行赋值操作。下面两行代码演示了 at()函数的使用方法。
// 读出第 i 行第 j 列像素值 uchar value = grayim.at<uchar>(i,j); //将第 i 行第 j 列像素值设置为 128 grayim.at<uchar>(i,j)=128;
如果要对图像进行遍历,可以参考下面的例程。这个例程创建了两个图像,分别是单通道的 grayim 以及 3 个通道的 colorim,然后对两个图像的所有像素值进行赋值,最后现实结果。
#include <QCoreApplication> #include <iostream> #include "opencv2/opencv.hpp" using namespace std; using namespace cv; int main(void) { Mat grayim(400, 600, CV_8UC1); Mat colorim(400, 600, CV_8UC3); //遍历所有像素,并设置像素值 for( int i = 0; i < grayim.rows; ++i) for( int j = 0; j < grayim.cols; ++j ) grayim.at<uchar>(i,j) = (i+j)%255; //遍历所有像素,并设置像素值 for( int i = 0; i < colorim.rows; ++i) for( int j = 0; j < colorim.cols; ++j ) { Vec3b pixel; pixel[0] = i%255; //Blue // B pixel[1] = j%255; //Green // G pixel[2] = 0; // R //Red colorim.at<Vec3b>(i,j) = pixel; } //显示结果 imshow("grayim", grayim); imshow("colorim", colorim); waitKey(0); return 0; }
需要注意的是,如果要遍历图像,并不推荐使用 at()函数。使用这个函数的优点是代码的可读性高,但是效率并不是很高。
显示结果如下图所示:
2.2 使用迭代器
如果你熟悉 C++的 STL 库,那一定了解迭代器(iterator)的使用。迭代器可以方便地遍历所有元素。Mat 也增加了迭代器的支持,以便于矩阵元素的遍历。
下面的例程功能跟上一节的例程类似,但是由于使用了迭代器,而不是使用行数和列数来遍历,所以这儿没有了 i 和 j 变量,图像的像素值设置为一个随机数。
#include <QCoreApplication> #include <iostream> #include "opencv2/opencv.hpp" using namespace std; using namespace cv; int main(void) { Mat grayim(600, 800, CV_8UC1); Mat colorim(600, 800, CV_8UC3); //遍历所有像素,并设置像素值 MatIterator_<uchar> grayit, grayend; for(grayit = grayim.begin<uchar>(), grayend = grayim.end<uchar>(); grayit != grayend; ++grayit) *grayit = rand()%255; //遍历所有像素,并设置像素值 MatIterator_<Vec3b> colorit, colorend; for(colorit = colorim.begin<Vec3b>(),colorend = colorim.end<Vec3b>(); colorit != colorend; ++colorit) { (*colorit)[0] = rand()%255; //Blue (*colorit)[1] = rand()%255; //Green (*colorit)[2] = rand()%255; //Red } //显示结果 imshow("grayim", grayim); imshow("colorim", colorim); waitKey(0); return 0; }
例程的输出结果如图,所示
2.3 通过数据指针
使用 IplImage 结构的时候,我们会经常使用数据指针来直接操作像素。通过指针操作来访问像素是非常高效的,但是你务必十分地小心。C/C++中的指针操作是不进行类型以及越界检查的,如果指针访问出错,程序运行时有时候可能看上去一切正常,有时候却突然弹出“段错误”(segment fault)。
当程序规模较大,且逻辑复杂时,查找指针错误十分困难。对于不熟悉指针的编程者来说,指针就如同噩梦。如果你对指针使用没有自信,则不建议直接通过指针操作来访问像素。虽然 at()函数和迭代器也不能保证对像素访问进行充分的检查,但是总是比指针操作要可靠一些。
如果你非常注重程序的运行速度,那么遍历像素时,建议使用指针。下面的例程演示如何使用指针来遍历图像中的所有像素。此例程实现的操作跟第 2.1节中的例程完全相同。例程代码如下:
#include <QCoreApplication> #include <iostream> #include "opencv2/opencv.hpp" using namespace std; using namespace cv; int main(int argc, char* argv[]) { Mat grayim(300, 400, CV_8UC1); Mat colorim(300, 400, CV_8UC3); //遍历所有像素,并设置像素值 for( int i = 0; i < grayim.rows; ++i) { //获取第 i 行首像素指针 uchar *p = grayim.ptr<uchar>(i); //对第 i 行的每个像素(byte)操作 for( int j = 0; j < grayim.cols; ++j ) p[j] = (i+j)%255; } //遍历所有像素,并设置像素值 for( int i = 0; i < colorim.rows; ++i) { //获取第 i 行首像素指针 Vec3b * p = colorim.ptr<Vec3b>(i); for( int j = 0; j < colorim.cols; ++j ) { p[j][0] = i%255; //Blue p[j][1] = j%255; //Green p[j][2] = 0; //Red } } //显示结果 imshow("grayim", grayim); imshow("colorim", colorim); waitKey(0); return 0; }
运行结果遇上面的一致。
相关文章推荐
- OpenCV入门教程(4)-Mat类之表达式
- OpenCV入门教程(6)-Mat类之内存管理和输出
- OpenCV入门教程(3)-Mat类之选取图像局部区域
- OpenCV入门教程(5)-Mat类之Mat_类
- OpenCV从入门到放弃:摸鱼笔记(二)Mat类常用变量和像素访问
- 【OpenCV入门教程之一】 OpenCV 2.4.8 +VS2010的开发环境配置
- 【OpenCV入门教程之二】 一览众山小:OpenCV 2.4.8组件结构全解析
- OpenCV教程(42) xml/yaml文件的读写
- jQuery 入门教程(17): 读写HTML元素的css 属性
- Opencv入门教程之使用Opencv进行模式识别与分类
- 【OpenCV入门教程之四】 ROI区域图像叠加&初级图像混合 全剖析
- 【OpenCV入门教程之一】 OpenCV 2.4.8 +VS2010的开发环境配置
- jQuery 入门教程(17): 读写HTML元素的css 属性
- 【OpenCV入门教程之六】 创建Trackbar & 图像对比度、亮度值调整
- 【OpenCV入门教程之一】 OpenCV 2.4.8 +VS2010的开发环境配置
- 【OpenCV入门教程之二】 一览众山小:OpenCV 2.4.8组件结构全解析
- 【OpenCV入门教程之三】 图像的载入,显示和输出 一站式完全解析
- directshow的下载、VC2008中的配置、入门教程以及如何在opencv中使用
- STM32开发板入门教程(十三) - SPI模式读写SD卡
- 【OpenCV入门教程之二】 一览众山小:OpenCV 2.4.8 or OpenCV 2.4.9组件结构全解析