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.两幅图像的大小和类型必须一致,或者第二个图像可以是 ...
随机推荐
- 洛谷 P2463 [SDOI2008]Sandy的卡片 解题报告
P2463 [SDOI2008]Sandy的卡片 题意 给\(n(\le 1000)\)串,定义两个串相等为"长度相同,且一个串每个数加某个数与另一个串完全相同",求所有串的最长公 ...
- 洛谷 P2812 校园网络【[USACO]Network of Schools加强版】 解题报告
P2812 校园网络[[USACO]Network of Schools加强版] 题目背景 浙江省的几所OI强校的神犇发明了一种人工智能,可以AC任何题目,所以他们决定建立一个网络来共享这个软件.但是 ...
- Libre 6011 「网络流 24 题」运输问题 (网络流,最小费用最大流)
Libre 6011 「网络流 24 题」运输问题 (网络流,最小费用最大流) Description W 公司有m个仓库和n个零售商店.第i个仓库有\(a_i\)个单位的货物:第j个零售商店需要\( ...
- 面向对象【day07】:类的继承(七)
本节内容 1.概述 2.类的继承 3.总结 4.练习 一.概述 之前我们说到了类的公有属性和类的私有属性,其实就是类的封装,下面我们来讲讲继承,是面向对象的第二大特性. 面向对象编程 (OOP) 语言 ...
- ivew实现table的编辑保存追加删除
ivew实现table的编辑 例子1 例子2
- Java编程思想 学习笔记1
一.对象导论 1.抽象过程 Alan Kay曾经总结了第一个成功的面向对象语言.同时也是Java所基于的语言之一的Smalltalk的五个基本特性,这些特性表现了纯粹的面向对象程序设计方式 1)万物皆 ...
- ThinkPHP 3.2 DEMO案例系列【phpmailer批量发送邮件】
但是邮件和短信相比在一些场景依然有着重要的意义和优势: 1:零成本:发邮件没有费用: 2:内容丰富且量大:邮件可以长篇大论:图文并茂: 3:增加访问量:用户很容易通过邮件中的链接访问网站: 好了:下面 ...
- db.properties文件的配置格式
#加载驱动 mysql.driver=com.mysql.jdbc.Driver #加载数据库 mysql.url=jdbc:mysql://localhost:3306/floor_shop #用户 ...
- 《Two Dozen Short Lessons in Haskell》所有习题的索引
<Two Dozen Short Lessons in Haskell>(Copyright © 1995, 1996, 1997 by Rex Page,有人翻译为Haskell二十四学 ...
- js对当前时间的相关操作
链接:https://www.cnblogs.com/visi_zhangyang/p/3490122.html js中获得当前时间是年份和月份,形如:201208 //获取完整的日期 v ...