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

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

注意通道的顺序为BGR。通常在内存足够大的情况下,图像的每一行是连续存放的,亦即在内存上图像的所有数据组成一个一维向量,这种情况下,在访问时将更快捷。可用成员函数isContinuous()来判断Mat图像在内存中是否为连续存储的。
清楚了图像在内存中的存储方式,下面对像素值操作:在只对深度为8bit字节型图像操作的前提下,导入一幅彩色图像,将其像素值变换为对255的补数。如原像素值为55,则变换为255-55=200。
使用一映射表来完成该转换:
uchar mapTable[];//mapping table:mapTable[pixel_value_before]=255-pixel_value_before;
for (int i = ; i < ; i++)
mapTable[i] = - i;
数组mapTable中装入的即是原像素值变换后的像素值。
可用如下方法对像素值进行操作:
1、指针的方式
//通过ptr和[]访问像素的值
void transformImageMethodOne(Mat& pSrcImg, const uchar* const table)
{
CV_Assert(pSrcImg.depth() != sizeof(uchar));//if the statement is false,then return a wrong message
int nchannels = pSrcImg.channels();
int nrows = pSrcImg.rows;//矩阵的行数
int ncols = pSrcImg.cols*nchannels;//矩阵的总列数=列数*nchannels
if (pSrcImg.isContinuous())//isContinuous()函数用于判断矩阵是否连续,若连续,相当于只需要遍历一个一维数组
{
cout << "only one row!" << endl;
ncols *= nrows;
nrows = ;//一维数组
}
//traverse pixel values
for (int i = ; i < nrows; i++)
{
uchar* ptr = pSrcImg.ptr<uchar>(i);//获取行地址
for (int j = ; j < ncols; j++)
ptr[j] = table[ptr[j]];//修改像素值
}
// return pSrcImg;
}
如代码所示,我们获取每一行开始处的指针,然后遍历至该行末尾。如果矩阵是连续存储的,则只需请求一次指针即可。
2、或者,也可以借助Mat的成员data。
data会从Mat中返回指向矩阵的首地址。通过遍历data来扫描整个图像。具体操作如下:
//通过ptr和[]访问像素的值(使用到了Mat的成员data)
void transformImageMethodTwo(Mat& pSrcImg, const uchar* const table)
{
CV_Assert(pSrcImg.depth() != sizeof(uchar));//if the statement is false,then return a wrong message
int nchannels = pSrcImg.channels();
int nrows = pSrcImg.rows;
int ncols = pSrcImg.cols*nchannels;
//traver pixel values
uchar* ptr = pSrcImg.data;//指向矩阵的首地址
for (int i = ; i < ncols*nrows; i++)
*ptr++ = table[*ptr];//*ptr++是先取出*p的值,然后让p++ }
3、使用迭代器
使用迭代器的方法,仅仅需要获得图像矩阵的begin和end,然后从begin迭代至end,将操作符*添加至迭代指针前,即可访问当前指向的内容。
//使用迭代器访问像素的值
void transformImageMethodThree(Mat& pSrcImg, const uchar* const table)
{
CV_Assert(pSrcImg.depth() != sizeof(uchar));//if the statement is false,then return a wrong message
const int nchannels = pSrcImg.channels();
switch (nchannels)
{
case :
{
MatIterator_<uchar> it, end;
for (it = pSrcImg.begin<uchar>(), end = pSrcImg.end<uchar>(); it != end; it++)
*it = table[*it];
break;
}
case :
{
MatIterator_<Vec3b> it, end;
for (it = pSrcImg.begin<Vec3b>(), end = pSrcImg.end<Vec3b>(); it != end; it++)
{
(*it)[] = table[(*it)[]];
(*it)[] = table[(*it)[]];
(*it)[] = table[(*it)[]];
}
break;
}
default:break;
} }
注意:这里对3通道的图像进行操作的时候,使用到了Vec3b。Vec3b作为一个对三元向量的数据结构,用在这里正好是能够表示RGB的三个分量。如果对于彩色图像,仍然使用uchar的话,则只能获得3通道中的B分量。
4、动态地址访问
这种方法在需要连续扫描所有点的应用场景时效率较低,因为它更适用于随机访问。这种方法最基本的用途是访问任意的某一行某一列:
//使用动态地址访问像素的值
void transformImageMethodFour(Mat& pSrcImg, const uchar* const table)
{
CV_Assert(pSrcImg.depth() != sizeof(uchar));//if the statement is false,then return a wrong message
const int nchannels = pSrcImg.channels();
const int nrows = pSrcImg.rows;
const int ncols = pSrcImg.cols;
switch (nchannels)
{
case :
{
for (int i = ; i < nrows;i++)
for (int j = ; j < ncols; j++)
pSrcImg.at<uchar>(i, j) = table[pSrcImg.at<uchar>(i, j)];
break;
}
case :
{
Mat_<Vec3b> _pSrcImg = pSrcImg;
for (int i = ; i < nrows;i++)
for (int j = ; j < ncols; j++)
{
_pSrcImg(i, j)[] = table[_pSrcImg(i, j)[]];
_pSrcImg(i, j)[] = table[_pSrcImg(i, j)[]];
_pSrcImg(i, j)[] = table[_pSrcImg(i, j)[]];
}
// pSrcImg = _pSrcImg;
break;
}
default:break;
}
}
测试代码:
/*
@author:CodingMengmeng
@theme:read the image pixel values by Mat
@time:2017-3-16 23:06:40
@blog:http://www.cnblogs.com/codingmengmeng/
*/
#include <cv.h>
#include <highgui.h>
using namespace std;
using namespace cv; //通过ptr和[]访问像素的值
void transformImageMethodOne(Mat& pSrcImg, const uchar* const table)
{
CV_Assert(pSrcImg.depth() != sizeof(uchar));//if the statement is false,then return a wrong message
int nchannels = pSrcImg.channels();
int nrows = pSrcImg.rows;//矩阵的行数
int ncols = pSrcImg.cols*nchannels;//矩阵的总列数=列数*nchannels
if (pSrcImg.isContinuous())//isContinuous()函数用于判断矩阵是否连续,若连续,相当于只需要遍历一个一维数组
{
cout << "only one row!" << endl;
ncols *= nrows;
nrows = ;//一维数组
}
//traverse pixel values
for (int i = ; i < nrows; i++)
{
uchar* ptr = pSrcImg.ptr<uchar>(i);//获取行地址
for (int j = ; j < ncols; j++)
ptr[j] = table[ptr[j]];//修改像素值
}
// return pSrcImg;
}
//通过ptr和[]访问像素的值(使用到了Mat的成员data)
void transformImageMethodTwo(Mat& pSrcImg, const uchar* const table)
{
CV_Assert(pSrcImg.depth() != sizeof(uchar));//if the statement is false,then return a wrong message
int nchannels = pSrcImg.channels();
int nrows = pSrcImg.rows;
int ncols = pSrcImg.cols*nchannels;
//traver pixel values
uchar* ptr = pSrcImg.data;//指向矩阵的首地址
for (int i = ; i < ncols*nrows; i++)
*ptr++ = table[*ptr];//*ptr++是先取出*p的值,然后让p++ }
//使用迭代器访问像素的值
void transformImageMethodThree(Mat& pSrcImg, const uchar* const table)
{
CV_Assert(pSrcImg.depth() != sizeof(uchar));//if the statement is false,then return a wrong message
const int nchannels = pSrcImg.channels();
switch (nchannels)
{
case :
{
MatIterator_<uchar> it, end;
for (it = pSrcImg.begin<uchar>(), end = pSrcImg.end<uchar>(); it != end; it++)
*it = table[*it];
break;
}
case :
{
MatIterator_<Vec3b> it, end;
for (it = pSrcImg.begin<Vec3b>(), end = pSrcImg.end<Vec3b>(); it != end; it++)
{
(*it)[] = table[(*it)[]];
(*it)[] = table[(*it)[]];
(*it)[] = table[(*it)[]];
}
break;
}
default:break;
} }
//使用动态地址访问像素的值
void transformImageMethodFour(Mat& pSrcImg, const uchar* const table)
{
CV_Assert(pSrcImg.depth() != sizeof(uchar));//if the statement is false,then return a wrong message
const int nchannels = pSrcImg.channels();
const int nrows = pSrcImg.rows;
const int ncols = pSrcImg.cols;
switch (nchannels)
{
case :
{
for (int i = ; i < nrows;i++)
for (int j = ; j < ncols; j++)
pSrcImg.at<uchar>(i, j) = table[pSrcImg.at<uchar>(i, j)];
break;
}
case :
{
Mat_<Vec3b> _pSrcImg = pSrcImg;
for (int i = ; i < nrows;i++)
for (int j = ; j < ncols; j++)
{
_pSrcImg(i, j)[] = table[_pSrcImg(i, j)[]];
_pSrcImg(i, j)[] = table[_pSrcImg(i, j)[]];
_pSrcImg(i, j)[] = table[_pSrcImg(i, j)[]];
}
// pSrcImg = _pSrcImg;
break;
}
default:break;
}
}
int main(void)
{
string imgName = "Route66.jpg";
Mat img = imread(imgName);
Mat imgCopy1 = img.clone();
Mat imgCopy2 = img.clone();
Mat imgCopy3 = img.clone();
Mat imgCopy4 = img.clone();
uchar mapTable[];//mapping table:mapTable[pixel_value_before]=255-pixel_value_before;
for (int i = ; i < ; i++)
mapTable[i] = - i;
imshow("SRCIMAGE", img);
transformImageMethodOne(imgCopy1, mapTable);
imshow("TRANSFORMIMAGE_USE_METHOD1", imgCopy1);
transformImageMethodTwo(imgCopy2, mapTable);
imshow("TRANSFORMIMAGE_USE_METHOD2", imgCopy2);
transformImageMethodThree(imgCopy3, mapTable);
imshow("TRANSFORMIMAGE_USE_METHOD3", imgCopy3);
transformImageMethodFour(imgCopy4, mapTable);
imshow("TRANSFORMIMAGE_USE_METHOD4", imgCopy4);
waitKey();
return ;
}
运行结果:
原图:

变换效果图:




以上。
opencv学习笔记(九)Mat 访问图像像素的值的更多相关文章
- 【opencv学习笔记七】访问图像中的像素与图像亮度对比度调整
今天我们来看一下如何访问图像的像素,以及如何改变图像的亮度与对比度. 在之前我们先来看一下图像矩阵数据的排列方式.我们以一个简单的矩阵来说明: 对单通道图像排列如下: 对于双通道图像排列如下: 那么对 ...
- (转) OpenCV学习笔记大集锦 与 图像视觉博客资源2之MIT斯坦福CMU
首页 视界智尚 算法技术 每日技术 来打我呀 注册 OpenCV学习笔记大集锦 整理了我所了解的有关OpenCV的学习笔记.原理分析.使用例程等相关的博文.排序不分先后,随机整理的 ...
- opencv学习笔记(八)IplImage* 访问图像像素的值
opencv2.1版本之前使用IplImage*数据结构来表示图像,2.1之后的版本使用图像容器Mat来存储.IplImage结构体如下所示. typedef struct _IplImage { i ...
- [OpenCV学习笔记2][Mat数据类型和操作]
[Mat数据类型和基本操作] ®.运行环境:Linux(RedHat+OpenCV3.0) 1.Mat的作用: Mat类用于表示一个多维的单通道或者多通道的稠密数组.能够用来保存实数或复数的向量.矩阵 ...
- OpenCV学习笔记(七) 图像金字塔 阈值 边界
转自: OpenCV 教程 使用 图像金字塔 进行缩放 图像金字塔是视觉运用中广泛采用的一项技术.一个图像金字塔是一系列图像的集合 - 所有图像来源于同一张原始图像 - 通过梯次向下采样获得,直到达到 ...
- OpenCV学习笔记:MAT解析
在2001年刚刚出现的时候,OpenCV基于 C 语言接口而建.为了在内存(memory)中存放图像,当时采用名为 IplImage 的C语言结构体,时至今日这仍出现在大多数的旧版教程和教学材料.但这 ...
- OpenCV学习笔记:如何扫描图像、利用查找表和计时
目的 我们将探索以下问题的答案: 如何遍历图像中的每一个像素? OpenCV的矩阵值是如何存储的? 如何测试我们所实现算法的性能? 查找表是什么?为什么要用它? 测试用例 这里我们测试的,是一种简单的 ...
- OpenCV 学习笔记(13)图像转换成视频
关键 1参数里的分辨率是图像本身的分辨率,而不是指定生成的视频分辨率.如果要修改分辨率,要么后期软件处理,要么读图的时候resize 2要正常退出,不要强制退出. 3生成的只能是avi格式. #inc ...
- OpenCV学习笔记(4)——图像上的算术运算
学习图像上的算术运算,加法,减法,位运算等 1.图像加法 使用cv2.add()将两幅图像进行加法运算,也可以用numpy运算,直接img+img1.两幅图像的大小和类型必须一致,或者第二个图像可以是 ...
随机推荐
- python操作oracle实战
import cx_Oracle conn = cx_Oracle.connect('ua_test/ua_test@192.32.98.15/oracledb') cur1 = conn.curso ...
- bracketed-paste-magic:zle:41: not enough arguments for -U
原因是zsh的插件出问题了,解法方法如下: 把 ~/.oh-my-zsh/lib/misc.zsh 文件中的第一段 if 注释掉 OK 啦 # ]]; then # for d in $fpath; ...
- CF815D Karen and Cards
CF815D Karen and Cards 固定一维c,然后(a,b)看成坐标,矩形区域求交 1.Segment tree Beats! 2.改成不合法的区域就是求并,c反向枚举,区域只增不减且完全 ...
- Android: View换切后,无法正常设置焦点或切换后TextView的虚拟键盘不弹出
边学.边测试,花了三天时间完工一个小应用. 遇到很多问题,但最终还是解决了. 我的手机是Android2.2版,所以我也在是2.2版环境下学习,开发. 1. 在同一个Activity中的不同View( ...
- Spring核心组件剖析
简介 Spring框架如今已成为服务端开发框架中的主流框架之一,是web开发者的利器.然而,真正让人着迷的,还是与其实现相关的 原理,设计模式以及许多工程化的思想.本文主要探讨Spring的三大核心组 ...
- 约会 音频mm教你追女孩
微信吧地址发给他人. 美团提前选好环境然后提前打电话订购一个位置. 微博作用是为:更多的谈资.热搜 ,最近上榜的话题说. 打车软件: 地图: 2.外表: 下澡,指甲,胡子,发型,适合服装.发型和服装搭 ...
- 利用Screen重启DevStack服务
上篇介绍了DevStack如何安装部署.集成化工具有好处,但在系统重启后,OpenStack的各个服务并不会随系统重启而启动,需要利用screen来重启,接下来就记录下如何使用screen. 准备工作 ...
- Tomcat部署实战
Tomcat部署实战 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.登录官网下载tomcat软件包(http://tomcat.apache.org/) 1>.在安装to ...
- JavaSE学习总结(十八)—— 多线程
多线程(英语:multithreading),是指从软件或者硬件上实现多个线程并发执行的技术.具有多线程能力的计算机因有硬件支持而能够在同一时间执行多于一个线程,进而提升整体处理性能.具有这种能力的系 ...
- 学习windows编程 day3 之 设置当前的背景颜色
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { HDC hdc; PAINTSTRU ...