opencv学习笔记(九)Mat 访问图像像素的值

对图像的像素进行访问,可以实现空间增强,反色,大部分图像特效系列都是基于像素操作的。图像容器Mat是一个矩阵的形式,一般情况下是二维的。单通道灰度图一般存放的是<uchar>类型,其数据存放格式如下:

多通道的图像中,每列并列存放通道数量的子列,如RGB三通道彩色图:

注意通道的顺序为BGR。通常在内存足够大的情况下,图像的每一行是连续存放的,亦即在内存上图像的所有数据组成一个一维向量,这种情况下,在访问时将更快捷。可用成员函数isContinuous()来判断Mat图像在内存中是否为连续存储的。

清楚了图像在内存中的存储方式,下面对像素值操作:在只对深度为8bit字节型图像操作的前提下,导入一幅彩色图像,将其像素值变换为对255的补数。如原像素值为55,则变换为255-55=200。

使用一映射表来完成该转换:

1     uchar mapTable[256];//mapping table:mapTable[pixel_value_before]=255-pixel_value_before;
2     for (int i = 0; i < 256; i++)
3         mapTable[i] = 255 - i;

数组mapTable中装入的即是原像素值变换后的像素值。

可用如下方法对像素值进行操作:

1、指针的方式

 1 //通过ptr和[]访问像素的值
 2 void transformImageMethodOne(Mat& pSrcImg, const uchar* const table)
 3 {
 4     CV_Assert(pSrcImg.depth() != sizeof(uchar));//if the statement is false,then return a wrong message
 5     int nchannels = pSrcImg.channels();
 6     int nrows = pSrcImg.rows;//矩阵的行数
 7     int ncols = pSrcImg.cols*nchannels;//矩阵的总列数=列数*nchannels
 8     if (pSrcImg.isContinuous())//isContinuous()函数用于判断矩阵是否连续,若连续,相当于只需要遍历一个一维数组
 9     {
10         cout << "only one row!" << endl;
11         ncols *= nrows;
12         nrows = 1;//一维数组
13     }
14     //traverse pixel values
15     for (int i = 0; i < nrows; i++)
16     {
17         uchar* ptr = pSrcImg.ptr<uchar>(i);//获取行地址
18         for (int j = 0; j < ncols; j++)
19             ptr[j] = table[ptr[j]];//修改像素值
20     }
21 //    return pSrcImg;
22 }

如代码所示,我们获取每一行开始处的指针,然后遍历至该行末尾。如果矩阵是连续存储的,则只需请求一次指针即可。

2、或者,也可以借助Mat的成员data。

data会从Mat中返回指向矩阵的首地址。通过遍历data来扫描整个图像。具体操作如下:

 1 //通过ptr和[]访问像素的值(使用到了Mat的成员data)
 2 void transformImageMethodTwo(Mat& pSrcImg, const uchar* const table)
 3 {
 4     CV_Assert(pSrcImg.depth() != sizeof(uchar));//if the statement is false,then return a wrong message
 5     int nchannels = pSrcImg.channels();
 6     int nrows = pSrcImg.rows;
 7     int ncols = pSrcImg.cols*nchannels;
 8     //traver pixel values
 9     uchar* ptr = pSrcImg.data;//指向矩阵的首地址
10     for (int i = 0; i < ncols*nrows; i++)
11         *ptr++ = table[*ptr];//*ptr++是先取出*p的值,然后让p++
12 
13 }

3、使用迭代器

使用迭代器的方法,仅仅需要获得图像矩阵的begin和end,然后从begin迭代至end,将操作符*添加至迭代指针前,即可访问当前指向的内容。

 1 //使用迭代器访问像素的值
 2 void transformImageMethodThree(Mat& pSrcImg, const uchar* const table)
 3 {
 4     CV_Assert(pSrcImg.depth() != sizeof(uchar));//if the statement is false,then return a wrong message
 5     const int nchannels = pSrcImg.channels();
 6     switch (nchannels)
 7     {
 8     case 1:
 9     {
10         MatIterator_<uchar> it, end;
11         for (it = pSrcImg.begin<uchar>(), end = pSrcImg.end<uchar>(); it != end; it++)
12             *it = table[*it];
13         break;
14     }
15     case 3:
16     {
17               MatIterator_<Vec3b> it, end;
18               for (it = pSrcImg.begin<Vec3b>(), end = pSrcImg.end<Vec3b>(); it != end; it++)
19               {
20                   (*it)[0] = table[(*it)[0]];
21                   (*it)[1] = table[(*it)[1]];
22                   (*it)[2] = table[(*it)[2]];
23               }
24               break;
25     }
26     default:break;
27     }
28 
29 }

注意:这里对3通道的图像进行操作的时候,使用到了Vec3b。Vec3b作为一个对三元向量的数据结构,用在这里正好是能够表示RGB的三个分量。如果对于彩色图像,仍然使用uchar的话,则只能获得3通道中的B分量

4、动态地址访问

这种方法在需要连续扫描所有点的应用场景时效率较低,因为它更适用于随机访问。这种方法最基本的用途是访问任意的某一行某一列:

 1 //使用动态地址访问像素的值
 2 void transformImageMethodFour(Mat& pSrcImg, const uchar* const table)
 3 {
 4     CV_Assert(pSrcImg.depth() != sizeof(uchar));//if the statement is false,then return a wrong message
 5     const int nchannels = pSrcImg.channels();
 6     const int nrows = pSrcImg.rows;
 7     const int ncols = pSrcImg.cols;
 8     switch (nchannels)
 9     {
10     case 1:
11     {
12               for (int i = 0; i < nrows;i++)
13               for (int j = 0; j < ncols; j++)
14                   pSrcImg.at<uchar>(i, j) = table[pSrcImg.at<uchar>(i, j)];
15               break;
16     }
17     case 3:
18     {
19               Mat_<Vec3b> _pSrcImg = pSrcImg;
20               for (int i = 0; i < nrows;i++)
21                 for (int j = 0; j < ncols; j++)
22                 {
23                     _pSrcImg(i, j)[0] = table[_pSrcImg(i, j)[0]];
24                     _pSrcImg(i, j)[1] = table[_pSrcImg(i, j)[1]];
25                     _pSrcImg(i, j)[2] = table[_pSrcImg(i, j)[2]];
26                 }
27 //                pSrcImg = _pSrcImg;
28                 break;
29     }
30     default:break;
31     }
32 }

测试代码:

  1 /*
  2 @author:CodingMengmeng
  3 @theme:read the image pixel values by Mat
  4 @time:2017-3-16 23:06:40
  5 @blog:http://www.cnblogs.com/codingmengmeng/
  6 */
  7 #include <cv.h>  
  8 #include <highgui.h>  
  9 using namespace std;
 10 using namespace cv;
 11 
 12 //通过ptr和[]访问像素的值
 13 void transformImageMethodOne(Mat& pSrcImg, const uchar* const table)
 14 {
 15     CV_Assert(pSrcImg.depth() != sizeof(uchar));//if the statement is false,then return a wrong message
 16     int nchannels = pSrcImg.channels();
 17     int nrows = pSrcImg.rows;//矩阵的行数
 18     int ncols = pSrcImg.cols*nchannels;//矩阵的总列数=列数*nchannels
 19     if (pSrcImg.isContinuous())//isContinuous()函数用于判断矩阵是否连续,若连续,相当于只需要遍历一个一维数组
 20     {
 21         cout << "only one row!" << endl;
 22         ncols *= nrows;
 23         nrows = 1;//一维数组
 24     }
 25     //traverse pixel values
 26     for (int i = 0; i < nrows; i++)
 27     {
 28         uchar* ptr = pSrcImg.ptr<uchar>(i);//获取行地址
 29         for (int j = 0; j < ncols; j++)
 30             ptr[j] = table[ptr[j]];//修改像素值
 31     }
 32 //    return pSrcImg;
 33 }
 34 //通过ptr和[]访问像素的值(使用到了Mat的成员data)
 35 void transformImageMethodTwo(Mat& pSrcImg, const uchar* const table)
 36 {
 37     CV_Assert(pSrcImg.depth() != sizeof(uchar));//if the statement is false,then return a wrong message
 38     int nchannels = pSrcImg.channels();
 39     int nrows = pSrcImg.rows;
 40     int ncols = pSrcImg.cols*nchannels;
 41     //traver pixel values
 42     uchar* ptr = pSrcImg.data;//指向矩阵的首地址
 43     for (int i = 0; i < ncols*nrows; i++)
 44         *ptr++ = table[*ptr];//*ptr++是先取出*p的值,然后让p++
 45 
 46 }
 47 //使用迭代器访问像素的值
 48 void transformImageMethodThree(Mat& pSrcImg, const uchar* const table)
 49 {
 50     CV_Assert(pSrcImg.depth() != sizeof(uchar));//if the statement is false,then return a wrong message
 51     const int nchannels = pSrcImg.channels();
 52     switch (nchannels)
 53     {
 54     case 1:
 55     {
 56         MatIterator_<uchar> it, end;
 57         for (it = pSrcImg.begin<uchar>(), end = pSrcImg.end<uchar>(); it != end; it++)
 58             *it = table[*it];
 59         break;
 60     }
 61     case 3:
 62     {
 63               MatIterator_<Vec3b> it, end;
 64               for (it = pSrcImg.begin<Vec3b>(), end = pSrcImg.end<Vec3b>(); it != end; it++)
 65               {
 66                   (*it)[0] = table[(*it)[0]];
 67                   (*it)[1] = table[(*it)[1]];
 68                   (*it)[2] = table[(*it)[2]];
 69               }
 70               break;
 71     }
 72     default:break;
 73     }
 74 
 75 }
 76 //使用动态地址访问像素的值
 77 void transformImageMethodFour(Mat& pSrcImg, const uchar* const table)
 78 {
 79     CV_Assert(pSrcImg.depth() != sizeof(uchar));//if the statement is false,then return a wrong message
 80     const int nchannels = pSrcImg.channels();
 81     const int nrows = pSrcImg.rows;
 82     const int ncols = pSrcImg.cols;
 83     switch (nchannels)
 84     {
 85     case 1:
 86     {
 87               for (int i = 0; i < nrows;i++)
 88               for (int j = 0; j < ncols; j++)
 89                   pSrcImg.at<uchar>(i, j) = table[pSrcImg.at<uchar>(i, j)];
 90               break;
 91     }
 92     case 3:
 93     {
 94               Mat_<Vec3b> _pSrcImg = pSrcImg;
 95               for (int i = 0; i < nrows;i++)
 96                 for (int j = 0; j < ncols; j++)
 97                 {
 98                     _pSrcImg(i, j)[0] = table[_pSrcImg(i, j)[0]];
 99                     _pSrcImg(i, j)[1] = table[_pSrcImg(i, j)[1]];
100                     _pSrcImg(i, j)[2] = table[_pSrcImg(i, j)[2]];
101                 }
102 //                pSrcImg = _pSrcImg;
103                 break;
104     }
105     default:break;
106     }
107 }
108 int main(void)
109 {
110     string imgName = "Route66.jpg";
111     Mat img = imread(imgName);
112     Mat imgCopy1 = img.clone();
113     Mat imgCopy2 = img.clone();
114     Mat imgCopy3 = img.clone();
115     Mat imgCopy4 = img.clone();
116     uchar mapTable[256];//mapping table:mapTable[pixel_value_before]=255-pixel_value_before;
117     for (int i = 0; i < 256; i++)
118         mapTable[i] = 255 - i;
119     imshow("SRCIMAGE", img);
120     transformImageMethodOne(imgCopy1, mapTable);
121     imshow("TRANSFORMIMAGE_USE_METHOD1", imgCopy1);
122     transformImageMethodTwo(imgCopy2, mapTable);
123     imshow("TRANSFORMIMAGE_USE_METHOD2", imgCopy2);
124     transformImageMethodThree(imgCopy3, mapTable);
125     imshow("TRANSFORMIMAGE_USE_METHOD3", imgCopy3);
126     transformImageMethodFour(imgCopy4, mapTable);
127     imshow("TRANSFORMIMAGE_USE_METHOD4", imgCopy4);
128     waitKey(0);
129     return 0;
130 }

运行结果:

原图:

变换效果图:

以上。

原文地址:https://www.cnblogs.com/codingmengmeng/p/6560677.html