【opencv学习笔记七】访问图像中的像素与图像亮度对比度调整
今天我们来看一下如何访问图像的像素,以及如何改变图像的亮度与对比度。
在之前我们先来看一下图像矩阵数据的排列方式。我们以一个简单的矩阵来说明:
对单通道图像排列如下:
对于双通道图像排列如下:
那么对于三通道的RGB图像则为:
知道了排列方式之后我们来讨论一下访问图像像素常用的三种方式:
1.使用指针访问;
2.使用迭代器访问;
3.使用动态地址访问;
为了比较一下三种方式的效率,我们介绍两个函数来统计一下每种方式所需的时间。
int64 getTickCount()函数:返回CPU自某个时间(如开启电脑)以来走过的时钟周期数。
double getTickFrequency()函数:返回每秒钟CPU走过的时钟周期数。
然后我们来看第一种方式。
1.使用指针访问图像像素:我们将输入图像img_src的每一个像素值加上50后赋值给输出图像img_dst。
- int main()
- {
- int c;
- Mat img_src = imread("1.jpg");
- Mat img_dst;
- namedWindow("原图");
- namedWindow("处理图");
- int channels = img_src.channels();//获取图像通道数
- img_dst = img_src.clone();
- double time1 = static_cast<double>(getTickCount());//获取开始处理前时间
- for (int i = ; i < img_src.rows; i++)//访问图像行数据
- {
- uchar* p_data1 = img_src.ptr(i);//获取图像行首地址
- uchar* p_data2 = img_dst.ptr(i);//获取图像行首地址
- for (int k = ; k < img_src.cols*channels; k++)//获取图像列(含通道)
- {
- p_data2[k] = saturate_cast<uchar>(p_data1[k] + );//图像处理
- //*p_data2++ = saturate_cast<uchar>((*p_data1++) + 100);//与上一行图像处理的等效方式
- //*(p_data2 + k) = saturate_cast<uchar>(*(p_data1 + k) + 50);//同上
- }
- }
- double time2 = static_cast<double>(getTickCount());//获取结束处理时间
- time1 = (time2 - time1) / getTickFrequency();//计算处理所用时间
- cout << "指针访问像素时间(S):" << time1 << endl;
- while ()
- {
- imshow("原图", img_src);//显示图像
- imshow("处理图", img_dst);//显示图像
- c = waitKey();
- if (c == || char(c) == 'q' || char(c) == 'Q')//按下Q键或者ESC键退出程序
- break;
- }
- return ;
- }
2.使用迭代器方式:
- int main()
- {
- int c;
- Mat img_src = imread("1.jpg");
- Mat img_dst;
- namedWindow("原图");
- namedWindow("处理图");
- int channels = img_src.channels();//获取图像通道数
- img_dst = img_src.clone();
- double time1 = static_cast<double>(getTickCount());//获取开始处理前时间
- Mat_<Vec3b>::iterator it = img_src.begin<Vec3b>();//获取原图开始地址
- Mat_<Vec3b>::iterator itend = img_src.end<Vec3b>();//获取原图结束地址
- Mat_<Vec3b>::iterator it2 = img_dst.begin<Vec3b>();//获取输出图开始地址
- for (; it != itend; ++it, ++it2)
- {
- for (int i = ; i < ; i++)
- {
- (*it2)[i] = saturate_cast<uchar>((*it)[i] + );//图像处理
- }
- }
- double time2 = static_cast<double>(getTickCount());//获取结束处理时间
- time1 = (time2 - time1) / getTickFrequency();//计算处理所用时间
- cout << "指针访问像素时间(S):" << time1 << endl;
- while ()
- {
- imshow("原图", img_src);//显示图像
- imshow("处理图", img_dst);//显示图像
- c = waitKey();
- if (c == || char(c) == 'q' || char(c) == 'Q')//按下Q键或者ESC键退出程序
- break;
- }
- return ;
- }
3.动态地址方式:
- int main()
- {
- int c;
- Mat img_src = imread("1.jpg");
- Mat img_dst;
- namedWindow("原图");
- namedWindow("处理图");
- int channels = img_src.channels();//获取图像通道数
- img_dst = img_src.clone();
- double time1 = static_cast<double>(getTickCount());//获取开始处理前时间
- for (int i = ; i < img_src.rows; i++)
- {
- for (int k = ; k < img_src.cols; k++)
- {
- for (int j = ; j < channels; j++)
- {
- img_dst.at<Vec3b>(i, k)[j] = saturate_cast<uchar>(img_src.at<Vec3b>(i, k)[j] + );
- }
- }
- }
- double time2 = static_cast<double>(getTickCount());//获取结束处理时间
- time1 = (time2 - time1) / getTickFrequency();//计算处理所用时间
- cout << "指针访问像素时间(S):" << time1 << endl;
- while ()
- {
- imshow("原图", img_src);//显示图像
- imshow("处理图", img_dst);//显示图像
- c = waitKey();
- if (c == || char(c) == 'q' || char(c) == 'Q')//按下Q键或者ESC键退出程序
- break;
- }
- return ;
- }
我们来看一下处理的结果吧:
实例
下面我们来看一个完整调用三种方式的例子,我们定义三个函数Mat image_bright1(Mat src);Mat image_bright2(Mat src);Mat image_bright3(Mat src);分别用来用三种方式处理图片。
- //************头文件包含*************
- #include "stdafx.h"
- #include<iostream>
- #include<opencv.hpp>//包含opencv的头文件
- //***********************************
- //************命名空间***************
- using namespace cv;//使用opencv命名空间
- using namespace std;
- //***********************************
- //************全局变量***************
- //***********************************
- //************全局函数***************
- Mat image_bright1(Mat src);//使用指针访问像素
- Mat image_bright2(Mat src);//使用迭代器访问像素
- Mat image_bright3(Mat src);//使用动态地址访问像素
- //***********************************
- //************主函数*****************
- int main()
- {
- int c;
- Mat img_src = imread("1.jpg");
- Mat img_dst1, img_dst2, img_dst3;
- namedWindow("原图",);
- namedWindow("指针访问像素",);
- namedWindow("迭代器访问像素",);
- namedWindow("动态地址访问像素",);
- double time1 = static_cast<double>(getTickCount());
- img_dst1 = image_bright1(img_src);//使用指针访问像素
- double time2 = static_cast<double>(getTickCount());
- img_dst2 = image_bright2(img_src);//使用迭代器访问像素
- double time3 = static_cast<double>(getTickCount());
- img_dst3 = image_bright3(img_src);//使用动态地址访问像素
- double time4 = static_cast<double>(getTickCount());
- time1 = (time2 - time1) / getTickFrequency();
- time2 = (time3 - time2) / getTickFrequency();
- time3 = (time4 - time3) / getTickFrequency();
- cout << "指针访问像素时间(S):"<<time1<<endl;
- cout << "迭代器访问像素时间(S):" << time2 << endl;
- cout << "动态地址访问像素时间(S):" << time3 << endl;
- while ()
- {
- imshow("原图", img_src);//显示图像
- imshow("指针访问像素", img_dst1);//显示图像
- imshow("迭代器访问像素", img_dst2);//显示图像
- imshow("动态地址访问像素", img_dst3);//显示图像
- c = waitKey();
- if (c == || char(c) == 'q' || char(c) == 'Q')//按下Q键或者ESC键退出程序
- break;
- }
- return ;
- }
- //使用指针访问像素
- Mat image_bright1(Mat src)
- {
- Mat dst;
- int channels = src.channels();
- dst = src.clone();
- for (int i = ; i < src.rows; i++)
- {
- uchar* p_data1 = src.ptr(i);
- uchar* p_data2 = dst.ptr(i);
- for (int k = ; k < src.cols*channels; k++)
- {
- //*p_data2++ = saturate_cast<uchar>((*p_data1++) + 100);
- //*(p_data2 + k) = saturate_cast<uchar>(*(p_data1 + k) + 50);
- p_data2[k] = saturate_cast<uchar>(p_data1[k] + );//输出图像像素=原图像像素+50
- }
- }
- return dst;
- }
- //使用迭代器访问像素
- Mat image_bright2(Mat src)
- {
- Mat dst;
- dst = src.clone();
- Mat_<Vec3b>::iterator it = src.begin<Vec3b>();
- Mat_<Vec3b>::iterator itend = src.end<Vec3b>();
- Mat_<Vec3b>::iterator it2 = dst.begin<Vec3b>();
- for (; it != itend; ++it, ++it2)
- {
- for (int i = ; i < ; i++)
- {
- (*it2)[i] = saturate_cast<uchar>(*(*it)[i]);//输出图像像素=2*原图像像素
- }
- }
- return dst;
- }
- //使用动态地址访问像素
- Mat image_bright3(Mat src)
- {
- Mat dst;
- int channels = src.channels();
- dst = src.clone();
- for (int i = ; i < src.rows; i++)
- {
- for (int k = ; k < src.cols; k++)
- {
- for (int j = ; j < channels; j++)
- {
- dst.at<Vec3b>(i, k)[j] = saturate_cast<uchar>(*src.at<Vec3b>(i, k)[j] + );//输出图像像素=2*原图像像素+50
- }
- }
- }
- return dst;
- }
结果:
从时间上我们可以看出来,使用指针的速度是最快的。
有些童鞋应该已经看出来了,在三种方法中我们将图像像素的处理方法变了一下,得出的图像也不一样了。在三种方法中我们处理像素的计算方式分别为:
- 输出图像像素=原图像像素+50;
- 输出图像像素=2*原图像像素;
- 输出图像像素=2*原图像像素+50;
其实这就是处理亮度与对比度的方法,从图像上也能看出来。
总结一下:g(x)=k*f(x)+b;其中g(x)为输出图像,f(x)为输入图像;
- 调节k的值则可以改变图像的对比度;
- 调节b的值则可以改变图像的亮度;
下载
功能很简单,代码很少,建议自己写一下或者在博文中复制一下,当然实在是懒的不要不要的土豪可以去下面的连接直接下载。
【opencv学习笔记七】访问图像中的像素与图像亮度对比度调整
【opencv学习笔记七】访问图像中的像素与图像亮度对比度调整的更多相关文章
- 【opencv学习笔记六】图像的ROI区域选择与复制
图像的数据量还是比较大的,对整张图片进行处理会影响我们的处理效率,因此常常只对图像中我们需要的部分进行处理,也就是感兴趣区域ROI.今天我们来看一下如何设置图像的感兴趣区域ROI.以及对ROI区域图像 ...
- 访问图像中的像素[OpenCV 笔记16]
再更一发好久没更过的OpenCV,不过其实写到这个部分对计算机视觉算法有所了解的应该可以做到用什么查什么了,所以后面可能会更的慢一点吧,既然开了新坑,还是机器学习更有研究价值吧... 图像在内存中的存 ...
- opencv 3 core组件进阶(1 访问图像中的像素)
访问图像像素的三类方法 ·方法一 指针访问:C操作符[ ]; ·方法二 迭代器iterator; ·方法三 动态地址计算. #include <opencv2/core/core.hpp> ...
- Java泛型学习笔记 - (七)浅析泛型中通配符的使用
一.基本概念:在学习Java泛型的过程中, 通配符是较难理解的一部分. 主要有以下三类:1. 无边界的通配符(Unbounded Wildcards), 就是<?>, 比如List< ...
- opencv学习笔记(七)---图像金字塔
图像金字塔指的是同一图像不同分辨率的子图的集合,有向下取样金字塔,向上取样金字塔,拉普拉斯金字塔....它是图像多尺度表达的一种,最主要的是用于图像的分割 向下取样金字塔指高分辨率图像向低分辨率图像的 ...
- 【OpenCV学习笔记之一】图像加载,修改及保存
加载图像(用cv::imread)imread功能是加载图像文件成为一个Mat对象 其中第一个参数表示图像文件名称第二个参数 表示加载的图像是什么类型 支持常见的三个参数值IMREAD_UNCHANG ...
- Vue学习笔记七:Vue中的样式
目录 两种样式 class样式 内联样式 两种样式 Vue中使用样式方式有两种,一种是class样式,一种是内联样式也就是style class样式 class样式使用的方式有5种,HTML如下 &l ...
- MYSQL初级学习笔记七:MySQL中使用正则表达式!(视频序号:初级_44)
知识点九:MySQL中使用正则表达式(44) (1):REGEXP‘匹配方式’: (2):常用匹配方式: 模式字符 ^ 匹配字符开始的部分 $ 匹配字符串结尾的部分 . 代表字符串中的任意一个字符,包 ...
- 【OpenCV】访问图像中每个像素的值
http://blog.csdn.net/xiaowei_cqu/article/details/7557063
随机推荐
- CString 成员函数用法大全(转)
CString( );例:CString csStr; CString( const CString& stringSrc );例:CString csStr("ABCDEF中文12 ...
- Python中文编码过程中遇到的一些问题
首先,要明确encode()和decode()的差别 encode()的作用是将Unicode编码的字符串转换为其它编码格式. 比如:st1.encode("utf-8") 这句 ...
- EasyPlayerPro Windows播放器进行本地对讲喊话音频采集功能实现
需求 在安防行业应用中,除了在本地看到摄像机的视频和进行音频监听外,还有一个重要的功能,那就是对讲. EasyPlayerPro-win为了减轻二次开发者的工作量,将本地音频采集也进行了集成: 功能特 ...
- 使用ab 进行并发压力测试
使用ab 进行并发压力测试 - 参与商 - 博客园 https://www.cnblogs.com/shenshangzz/p/8340640.html 使用ab 进行并发压力测试 ab全称为:a ...
- 【题解】CF891CEnvy
[题解] CF891C Envy 很好玩的一道题.尽管不难,但是调了很久QAQ 考虑克鲁斯卡尔最小生成树的算法,可以发现这些最小树生成的性质: 当生成树所有边的权值都\(\le\)某个$ w$的时刻, ...
- 流畅python学习笔记:第十四章:迭代器和生成器
迭代器和生成器是python中的重要特性,本章作者花了很大的篇幅来介绍迭代器和生成器的用法. 首先来看一个单词序列的例子: import re re_word=re.compile(r'\w+') c ...
- springMvc 4.0 jackson包改变
使用之前的json包出包java.lang.NoClassDefFoundError: com/fasterxml/jackson/core/JsonProcessingException错误. sp ...
- 扫盲-wpf依赖属性
一.什么是依赖属性 依赖属性就是一种自己可以没有值,并且可以通过绑定从其他数据源获取值.依赖属性可支持WPF中的样式设置.数据绑定.继承.动画及默认值. 将所有的属性都设置为依赖属性并不总是正确的解决 ...
- (转).NET基础拾遗(5)多线程开发基础
https://www.cnblogs.com/edisonchou/p/4848131.html
- Mongo 分组后排序取时间最大的一整条数据对象
db.getCollection('product_protocol_new').aggregate([ {$sort:{"end_date":-1}}, {$group:{ _i ...