访问像素:at()方法

简介


图像本质上就是一个由数值组成的矩阵。矩阵中的每个元素表示一个像素。对于灰度图像(黑白图像),像素是 8 8 8 位无符号数, 0 0 0 表示黑色, 255 255 255表示白色。对于彩色图像,需要用三原色数据来重现不同的可见色,这是因为我们人类的视觉系统是三原色的,视网膜上有三种类型的视锥细胞,它们将颜色信息传递给大脑。这意味着对于彩色图像,每个像素都要对应三个数值。在摄影和数字成像技术中,常用的主颜色通道是红色、绿色和蓝色,因此每3个8位数值组成矩阵的一个元素。
注意, 8 8 8 位通道通常是够用的,但有些特殊的应用程序需要用 16 16 16 位通道(例如医学图像)。


访问像素值


要访问矩阵中的每个独立元素,你只需要指定它的行号和列号。返回的对应元素可以是单个数值,也可以是多通道图像的数值向量。
cv::Mat 类包含有多种方法,可用来访问图像的各种属性:

  • 利用公共成员变量 colsrows 可得到图像的列数和行数;
  • 利用 cv::Matat(int y,int x) 方法可以访问元素;at 方法被实现成一个模板方法,在调用时必须指定图像元素的类型;
image.at<uchar>(j, i) = 255;

注意,用户必须保证指定的类型与矩阵内的类型是一致的, at 方法不会进行任何类型转换。
彩色图像的每个像素对应三个部分:红色、绿色和蓝色通道。因此包含彩色图像的cv::Mat类会返回一个向量,向量中包含三个8位的数值。 OpenCV为这样的短向量定义了一种类型,即cv::Vec3b。这个向量包含三个无符号字符( unsigned character)类型的数据。因此,访问彩色像素中元素的方法如下:

image.at<cv::Vec3b>(j, i)[channel] = value;

channel 索引用来指明三个颜色通道中的一个。 OpenCV存储通道数据的次序是蓝色、绿色和红色(因此蓝色是通道0)。

为了说明如何直接访问像素值,我们在图像中加入椒盐噪声( salt-and-pepper noise)。椒盐噪声是一个专门的噪声类型,它随机选择一些像素,把它们的颜色替换成白色或黑色。在现实生活中,如果通信时出错,部分像素的值在传输时丢失,就会发生这种噪声。这里我们只是随机地选择一些像素,把它们设置为白色。

样例:

#include <opencv2/opencv.hpp>

/*对图像img添加椒盐噪声,n表示椒盐像素数量*/
void salt(cv::Mat img, int n) 
{
     int col, row;
     for (int k = 0; k < n; k++) {
          // rand 随机生成器
         int rand = std::rand();
          col = rand % img.cols;
          row = rand % img.rows;

          if (CV_8UC1 == img.type()) // 灰度图像
               img.at<uchar>(row, col) = 255;
          else if (CV_8UC3 == img.type()) { // 彩色图像 
               img.at<cv::Vec3b>(row, col)[0] = 255;
               img.at<cv::Vec3b>(row, col)[1] = 255;
               img.at<cv::Vec3b>(row, col)[2] = 255;
          }
     }
}

int main()
{
     /*加载图像*/
     cv::Mat srcImg = cv::imread("bird.bmp", 1);

     const int64 start = cv::getTickCount();
     salt(srcImg, 3000); // 访问像素
     double duration = (cv::getTickCount() - start) / cv::getTickFrequency();
     std::cout << __FILE__ << ": " << __LINE__ << ": duration: " << duration << std::endl;

     /*显示图像*/
     cv::imshow("srcImg", srcImg);
     cv::waitKey();

     return 0;
}

现实结果如下:

在这里插入图片描述

最后一点需要说明的是:这些修改图像的函数在使用图像作为参数时,都采用了值传递的方式。之所以这样做,是因为它们在复制图像时仍共享了同一块图像数据。因此在需要修改图像内容时,图像参数没必要采用引用传递的方式。


衡量算法性能


为了衡量函数或代码短的运行时间,OpenCv 有两个非常实用的函数:

  • cv::getTickCount ,该函数返回从最近一次电脑开机到当前的时间周期数;
  • cv::getTickFrequency(),返回每秒的时钟周期数。
    为了获得某个函数的运行时间,我们可以使用如下的程序模板:
const int64 start = cv::getTickCount();
salt(srcImg, 3000); // 调用函数
/*经过的时间段(单位:秒)*/
double duration = (cv::getTickCount() - start) / cv::getTickFrequency();
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值