操作像素
1、访问像素值
1、at方法
image.at<uchar>(j,i)= 255;//灰度
image.at<cv::Vec3b>(j,i)[channel]= value;//彩色像素
随机数生成器
// C++11 的随机数生成器
std::default_random_engine generator;
std::uniform_int_distribution<int>
randomRow(0, image.rows - 1);
std::uniform_int_distribution<int>
randomCol(0, image.cols - 1);
随机数由生成器和分布器结合产生
生成器generator:能够产生离散的等可能分布数值
分布器distributions: 能够把generator产生的均匀分布值映射到其他常见分布,如均匀分布uniform,正态分布normal,二项分布binomial,泊松分布poisson
种子:相当于外部传给随机数生成器的诱因,如果每次传入的种子一样,则每次生成的随机数还是一样
cv::Mat_模板类
因为每次调用都必须在模板参数中指明返回类型,所以 cv::Mat 类的 at 方法有时会显得冗长。如果已经知道矩阵的类型,就可以使用 cv::Mat_类(cv::Mat 类的模板子类)。cv::Mat_类定义了一些新的方法,但没有定义新的数据属性,因此这两个类的指针或引用可以直接互相转换。
新方法中有一个 operator(),可用来直接访问矩阵的元素。因此可以这样写代码(其中 image是一个对应 uchar 矩阵的 cv::Mat 变量):
// 用 Mat 模板操作图像
cv::Mat_<uchar> img(image);
img(50,100)= 0; // 访问第 50 行、第 100 列处那个值
CV_Assert函数
// 限制图像为灰度图像
CV_Assert(image.type() == CV_8UC1);
2、.ptr和[]操作符
为了简化指针运算的计算过程,Mat最直接的访问方法是通过.ptr<>函数得到一行的指针,并用[]操作符访问某一列的像素值。
//返回第 j 行的地址:
uchar* data= image.ptr<uchar>(j);
用 cols和 rows 属性可得到图像的宽度和高度。与之类似,用 step 数据属性可得到单位是字节的有效宽度。即使图像的类型不是 uchar,step 仍然能提供行的字节数。我们可以通过 elemSize方法(例如一个三通道短整型的矩阵 CV_16SC3,elemSize 会返回 6)获得像素的大小,通过 nchannels 方法(灰度图像为 1,彩色图像为 3)获得图像中通道的数量,最后用 total方法返回矩阵中的像素(即矩阵的条目)总数。
用下面的代码可获得每一行中像素值的个数:
int nc= image.cols * image.channels();
示例代码:
//减色函数,利用整数除法的特性,即取不超过又最接近结果的整数
//处理过程很简单,只要创建一个二重循环遍历所有像素值,代码如下所示:
void colorReduce(cv::Mat image, int div=64) {
int nl= image.rows; // 行数
// 每行的元素数量
int nc= image.cols * image.channels();
for (int j=0; j<nl; j++) {
// 取得行 j 的地址
uchar* data= image.ptr<uchar>(j);
for (int i=0; i<nc; i++) {
// 处理每个像素 ---------------------
data[i]= data[i]/div*div + div/2;
// 像素处理结束 ----------------
} // 一行结束
}
}
我们也可以在处理语句中采用另一种等价的做法,即利用指针运算从一列移到下一列。
*data++= *data/div*div + div2;
减色计算也可以使用取模运算符,它可以直接得到 div 的倍数,代码如下所示:
data[i]= data[i] – data[i]%div + div/2;
另外还可以使用位运算符。如果把减色因子限定为 2 的指数,即 div=pow(2,n),那么把像素值的前 n 位掩码后就能得到最接近的 div 的倍数。可以用简单的位移操作获得掩码,代码如下所示:
// 用来截取像素值的掩码
uchar mask= 0xFF<<n; // 如 div=16,则 mask= 0xF0
//可用下面的代码实现减色运算:
*data &= mask; // 掩码
*data++ += div>>1; // 加上 div/2
// 这里的+也可以改用“按位或”运算符
//*data++ |= div>>1;
一般来说,使用位运算的代码运行效率很高,因此在效率为重时,位运算是不二之选。
检查
为确保完整性,测试时还需要检查矩阵是否只有一行;如果是,这个矩阵就是连续的。但是不管哪种情况,都可以用 isContinuous 方法检查矩阵的连续性。
if (image.isContinuous()) {
// 没有填充的像素
nc= nc*nl;
nl= 1; // 它现在成了一个一维数组
}
//或者使用reshape
if (image.isContinuous())
{
// 没有填充像素
image.reshape(1, // 新的通道数
1); // 新的行数
}
一些需要注意的东西
1、
uchar div2 = div >> 1; // div2 = div/2