从根本上说,一张图像时一个由数值组成的矩阵,这也是Opencv2用cv::Mat这个数据结构来表示图像的原因。矩阵的每个元素代表一个像素,对于灰度图像,像素有8为无符号数来表示,其中0代表黑色,255代表白色;对于彩色图象,每个像素需要三个这样的8位无符号数来表示三个颜色通道(红蓝绿)。此时矩阵的元素是一个三元数。
为了存取矩阵元素,你需要在代码中指定元素所在的行和列。程序会返回相应的元素。如果图像是单通道的,返回值是单个数值;如果图像时多通道的,返回值怎是一组向量(Vector)。
我们通过一个简单的函数俩演示直接存取像素值,该函数会在图像中加入盐噪点(随机的部分像素设置为白色或黑色),即模拟像素值丢失的情况。
一、代码
#include
<iostream>
#include
<opencv2\core\core.hpp>
#include
<opencv2\highgui\highgui.hpp>
using
namespace
std;
using
namespace
cv;
void
salt(
Mat
&
image
,
int
n
)
{
for
(
int
k = 0; k <
n
; k++)
{
//图像的行号为j,列号为i
int
i = rand()%
image
.cols;
int
j = rand() %
image
.rows;
//如果通道数是1,即为灰度图;如果通道数为3,即为彩色图
if
(
image
.channels() == 1) {
image
.at<
uchar
>(j, i) = 255;
}
else
if
(
image
.channels() == 3) {
image
.at<
Vec3b
>(j, i)
[
0
]
= 255;
image
.at<
Vec3b
>(j, i)
[
1
]
= 255;
image
.at<
Vec3b
>(j, i)
[
2
]
= 255;
}
}
}
int
main(
int
argc
,
char
**
argv
)
{
Mat
srcImage = imread(
"1.jpg"
);
if
(!srcImage.data)
{
cout
<<
"“1.jpg”读取失败!"
<<
endl;
return
0;
}
salt(srcImage, 3000);
namedWindow(
"Image"
);
imshow(
"Image"
, srcImage);
waitKey(0);
return
0;
}
二、分析
类cv::Mat有若干成员函数可以获取图像属性。公有成员变量 cols 和 rows 给出了图像的宽和高。成员函数 at( int y, int x) 可以用来存放图像元素。但必须在编译期间知道图像的数据类型,因为 cv::Mat 可以存放任意的数据类型的元素。这也是这个函数用模板函数来实现的原因。这也意味着,当调用该函数时,你需要使用以下方式指定数据类型:
image.at<uchar>(j,i)=255;
指定的数据类型一定和矩阵中的数据类型相符合。at方法本身不会进行任何数据类型转换。
对于彩色图像,每个像素有三个通道,一个包含彩色图像的cv::Mat 会返回三个8位数组成的向量。opencv将此向量定义为cv::Vec3b,即由三个unsigned char组成的向量,所以存取彩色图像像素的代码写成以下形式:
image.at<cv::Vec3b>(j, i)[channel]= value ;
三、扩展
cv::Mat 成员函数有时候比较麻烦,因为返回值的类型必须通过在调用时通过模板参数指定。因此,opencv提供了类cv::Mat_ ,它是cv::Mat的一个字类,此类的指针或引用可以直接进行相互类型转换。该类重载了操作符():
cv::Mat_ <uchar> im2=image; //im2指向image
im2(50,100)=0 ; //存取第50行,100列
四、测试结果