博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
opencv学习笔记(九)Mat 访问图像像素的值
阅读量:5819 次
发布时间:2019-06-18

本文共 8355 字,大约阅读时间需要 27 分钟。

对图像的像素进行访问,可以实现空间增强,反色,大部分图像特效系列都是基于像素操作的。图像容器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 values15     for (int i = 0; i < nrows; i++)16     {17         uchar* ptr = pSrcImg.ptr
(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_
it, end;11 for (it = pSrcImg.begin
(), end = pSrcImg.end
(); it != end; it++)12 *it = table[*it];13 break;14 }15 case 3:16 {17 MatIterator_
it, end;18 for (it = pSrcImg.begin
(), end = pSrcImg.end
(); 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
(i, j) = table[pSrcImg.at
(i, j)];15 break;16 }17 case 3:18 {19 Mat_
_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 
8 #include
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
(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_
it, end; 57 for (it = pSrcImg.begin
(), end = pSrcImg.end
(); it != end; it++) 58 *it = table[*it]; 59 break; 60 } 61 case 3: 62 { 63 MatIterator_
it, end; 64 for (it = pSrcImg.begin
(), end = pSrcImg.end
(); 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
(i, j) = table[pSrcImg.at
(i, j)]; 90 break; 91 } 92 case 3: 93 { 94 Mat_
_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 }

 

运行结果:

原图:

 

变换效果图:

 

以上。

你可能感兴趣的文章
802.11 学习笔记
查看>>
Leetcode-Database-176-Second Highest Salary-Easy(转)
查看>>
构建Docker Compose服务堆栈
查看>>
最小角回归 LARS算法包的用法以及模型参数的选择(R语言 )
查看>>
CentOS7下zip解压和unzip压缩文件
查看>>
Hadoop生态圈-Kafka常用命令总结
查看>>
如何基于Redis Replication设计并实现Redis-replicator?
查看>>
Linux 环境下 PHP 扩展的编译与安装 以 mysqli 为例
查看>>
laravel中 url() route() URL::asset()
查看>>
浮点数内存如何存储的
查看>>
贪吃蛇
查看>>
EventSystem
查看>>
用WINSOCK API实现同步非阻塞方式的网络通讯
查看>>
vue
查看>>
玩一玩博客,嘿嘿
查看>>
P1352 没有上司的舞会
查看>>
ios11文件夹
查看>>
【HLOJ 559】好朋友的题
查看>>
DataSet用法3操作数据
查看>>
Electric Fence(皮克定理)
查看>>