opencv-4-成像系统与Mat图像颜色空间

opencvc++qtmat

目标

  1. 知道 opencv 处理图像数据的格式
  2. 介绍 mat 基础内容
  3. 知道 BGR 颜色 显示
  4. 颜色转换 BGR 到 灰度图像

开始

Opencv 主要是图像处理, 在进行图像处理的过程中有一个必须要解决的问题: 图像的内存存储, 最简单的方式就是使用二维数组来存储了. opencv 在1.0时代使用的是二维数组, 然后使用 IplImage 的指针指向数据起始的地址, 指针很强大, 但是对于新手不是很友好, 容易出现各种奇怪的问题..

opencv 从2.0 时代就开始使用 Mat 格式 来存储图像数据, 相当于从 C 版本的接口 升级到了 C++ 的接口, 到了后面就逐渐开始移除 C 接口了, 开始了 C++ 的接口, 不再需要自己去维护内存, 让它来做, 让我们不再去考虑太过底层的实现, 专注与我们自己的 算法实现上来..

VS2017 下面有一个插件 Image Watch, 可以在调试过程中查看 内存中的 opencv Mat 数据.

我们在VS 的调试过程中, 点击 视图--其他窗口--ImageWatch 会弹出窗口 我们能够看到图像的预览与实际, 点击可以使用滚轮进行放大, 比如


lena预览与眼睛部分放大

我们将其放大到一定程度就发现图像数据是一个一个的"格子", 也就是我们称为的像素,

对于我们原始的 lena 图像 就是一个 尺寸的图像


原始lena 图像

px 是 图像像素的单位, 可以理解为 512*512 个小格子

图像数据不是矢量数据, 不能够无限放大, 能够满足我们的日常实现即可

Mat 图像数据格式

一般来说, Mat 是一种对象, 有着相应的成员, 或者可以理解为属性, 可以理解为, 我们使用二维数组存储图像数据, 可以通过一些方法直接获取到这些二维数组的参数信息以及其他信息, 便于后续实现


lena_img 属性

比如 Mat类 常用的有以下几种[1]

  • type: CV_8UC3 图像数据类型和通道数目
  • depth: 图像的精度, 用于表示每一个像素每一个通道的精度值 8U 表示 无符号 精度, 最高256个灰度等级
  • channels: 通道数目 表示图像每个像素的数目
  • cols: 图像数据宽度 横向
  • rows: 图像数据高度 纵向
  • size(): 返回(cols, rows) 数据
  • data: 指向二维数组的首地址, 指向图像数据最开始的地方
  • step: = stride 表示图像数据每一行的字节大小
  • ...

不再意义列举, 常用的就这么多

成像系统

数字成像系统概述 这篇文章中, 详细介绍了数字的成像系统[2], 我们以这幅图为例


成像系统

成像系统 满足高斯公式 ,

传感器成像

我们只看成像传感器部分, 传感器实际上是分为了每一个很小的格子, 每一个格子都会受到光照, 根据光照强度不同, 产生不同的电压信号, 通过放大电路被我们采集便能够得到整幅图像的电压, 转换以下辨识我们得到的图像数据了,


图像传感器

但是这里最重要的问题是, 光电传感器只能感受到光的强度, 不能感受到光的颜色, 所以 传感器上会附加 透镜, 每一个传感器只会感受到一种颜色光的强度, 组合以下便能够得到 颜色了,


传感器滤镜

颜色混合模型

这里又有一个概念, 颜色混合模型,

其实每个概念都是很深的东西, 但是如果不是专业涉及的话, 我们暂时不做太深入的了解 ,有兴趣的可以看CMYK 和 RGB 这两种色彩模式本质与区别在哪? 和这篇文章从“减色法”的本质,到广色域输出的可能性

主要有两种颜色模型, 加色和减色, 可以去参考

参考RGB vs CMYK: What’s the difference?这篇文章,

减色模型 一般情况下可以了理解为 CMYK 模型, 一般用于印刷,

加法模型 一般是指 RGB 颜色加法混合模型 可以用于颜色 的表示, 符合人眼的颜色感觉


加法模型

跑的有点远了, 收回来

我们使用三种颜色等量叠加便能够得到白色, 对应的, 使用不同比例的颜色叠加便能够得到不同的颜色,


颜色叠加

对应的, 我们在lena 显示的时候, 放大之后得到的数字就是我们图像的演示的 "RGB" 值,

颜色深度

这里还要再做一点介绍, 颜色深度,

数字图像, 自然所有的东西都是数字了, 考虑到实际计算机存储一个字节的数据为 8个bit, 1Byte = 8 bit , 每一个bit 可以表示成0或者1 则, 我们可以将每一个颜色分为 种颜色进行表示, 这样即能够有效的利用数据, 也能方便的进行运算.

对于灰度图像, 一共有 256个层级 使用进行表示, 对于彩色图像, 我们每一个颜色都能表示成256个颜色, 则可以得到 种颜色, 我们可以使用 $ RGB(255,255,255)$ 来表示白色, 或者类似与 html 中使用 16进制来表示颜色一样 使用#ffffff 来分别表示 RGB的 颜色值. 用于表示白色

opencv 颜色空间

opencv 的大坑

由于历史遗留问题, opencv 3通道彩色图像的数据格式 为 BGR的顺序, 而不是常用的RGB顺序, 在我们进行实际上的颜色选择方面一定要注意这一点, 至于为什么, 只能说为了兼容某些硬件设备, 统一一个顺序, 定下来的一个标准, 后面的又为了兼容以前的版本.. 具体的可以参考Why does OpenCV use BGR color format ?

Why did they choose BGR color space in OpenCV ?

The reason the early developers at OpenCV chose BGR color format is that back then BGR color format was popular among camera manufacturers and software providers. E.g. in Windows, when specifying color value using COLORREF they use the BGR format 0x00bbggrr.

实际上我们注意就好, opencv 的颜色顺序为 BGR

一般来说, 我们在 opencv 中表示各种颜色

  • 蓝色 BGR(255,0,0);
  • 红色 BGR(0,0,255);
  • 绿色 BGR(0,255,0);
  • 青色 BGR(255,255,0);
  • 紫色 BGR(255,0,255);
  • 黄色 BGR(0,255,255);
  • 白色 BGR(255,255,255);
  • 黑色 BGR(0,0,0);
  • 中等灰色 BGR(128,128,128 );

彩色图像灰度化

我感觉这篇文章图像处理:图像灰度化 做了很多介绍, 其实 颜色对于图像处理来说意义不大, 更多的是给人的一种颜色的感觉, 所以很多时候进行图像处理的过程都是进行灰度化处理了,

由我们的成像系统可以得知, 每一个像素的位置存在三种颜色的值, 灰度化的过程就是将三个值变成一个值, 就是我们所谓的灰度化, 将原本三通道的像素值转换为1通道的像素值,

一般来说, 我们就考虑颜色灰度的平均得到灰度值即可,

这就是平均值的方式,

但是呢, 人眼对于绿色比较敏感 进而提出了给出不同权重的灰度化过程, 属于一个心理学公式

这个权重值是一个目前使用最多的比例, 但是也有其他人提出的权重参数, 详细可以见[Grayscale wiki 页面](https://en.wikipedia.org/wiki/Grayscale

进而我比较喜欢是这篇文章从RGB色转为灰度色算法, 给出了一个比较有效的 整数移位算法, 减少整数计算带来的精度损失以及浮点运算的低效率.

参照他给出的数据, 得到一个有趣的公式, 可以去详细看下,

编码实现

编辑代码

我们这里为了对比 不同的灰度化做了一个展示, 其实很简单, 这里用到的 图像遍历的方式, 对于每一个像素点进行处理 三种灰度化的方式, 存入到三副图像中, 然后一起显示出来用于对比,

  1. #include "mainwindow.h" 



  2. #include <QApplication> 




  3. // 引入 opencv 函数头文件 


  4. #include <opencv2/opencv.hpp> 



  5. int main(int argc, char *argv[]) 





  6. QApplication a(argc, argv); 


  7. MainWindow w; 


  8. w.show(); 




  9. // 设置 要显示的图像路径 


  10. std::string test_pic = "./TestImages/lena.png"; 



  11. // 读取图像 


  12. cv::Mat lena_rgb = cv::imread(test_pic); 



  13. // 生命三个灰色的图像 


  14. cv::Mat lena_gray_avg = cv::Mat::zeros(lena_rgb.size(), CV_8UC1); 


  15. cv::Mat lena_gray_weighted = cv::Mat::zeros(lena_rgb.size(), CV_8UC1); 


  16. cv::Mat lena_gray_shift = cv::Mat::zeros(lena_rgb.size(), CV_8UC1); 



  17. // 遍历每一个像素进行灰度化 


  18. for (int i = 0; i < lena_rgb.rows; i++) 





  19. for (int j = 0; j < lena_rgb.cols; j++) 





  20. cv::Vec3b tmp_px = lena_rgb.at<cv::Vec3b>(i, j); 


  21. lena_gray_avg.at<uchar>(i, j) = (uchar)((tmp_px[0] + tmp_px[1] + tmp_px[2]) / 3); 


  22. lena_gray_weighted.at<uchar>(i, j) = (uchar)((0.299f * tmp_px[0] + 0.587f * tmp_px[1] + 0.114f * tmp_px[2])); 


  23. lena_gray_shift.at<uchar>(i, j) = (uchar)((38 * tmp_px[0] + 75 * tmp_px[1] + 15 * tmp_px[2]) >> 7); 









  24. // 显示图像 


  25. cv::imshow("lena_rgb", lena_rgb); 


  26. cv::imshow("lena_gray_avg", lena_gray_avg); 


  27. cv::imshow("lena_gray_weighted", lena_gray_weighted); 


  28. cv::imshow("lena_gray_shift", lena_gray_shift); 



  29. cv::waitKey(0); 




  30. return a.exec(); 





运行效果

最后运行出来的结果就是下图了, 很简单, 但是 很有效,


运行效果展示

对应最开始的图, 我们选取lena 眼镜部分, 查看 lena_gray_shift 的眼镜细节, 得到图


灰度图 眼镜部分细节放大

其他

我们稍微介绍了成像的系统, 然后介绍了opencv 的 成像方法, 以及具体的颜色表示, 在后面我们就要进行 稍微深入的像素操作与图形操作了.

参考



  1. 《Opencv学习(三)简记Mat中的数据类型_人工智能_hjxu2016的博客-CSDN博客》. 见于 2020年4月21日. https://blog.csdn.net/hjxu2016/article/details/81116040.

  2. 《数字成像系统概述|Camera》. 见于 2020年4月21日. http://camera.geek-docs.com/camera-isp/digital-camera-system-intro.html.

opencv-4-成像系统与Mat图像颜色空间的更多相关文章

  1. 快速遍历OpenCV Mat图像数据的多种方法和性能分析 | opencv mat for loop

    本文首发于个人博客https://kezunlin.me/post/61d55ab4/,欢迎阅读! opencv mat for loop Series Part 1: compile opencv ...

  2. 【视频开发】OpenCV中Mat,图像二维指针和CxImage类的转换

    在做图像处理中,常用的函数接口有OpenCV中的Mat图像类,有时候需要直接用二维指针开辟内存直接存储图像数据,有时候需要用到CxImage类存储图像.本文主要是总结下这三类存储方式之间的图像数据的转 ...

  3. Python 图像处理 OpenCV (4):图像算数运算以及修改颜色空间

    前文传送门: 「Python 图像处理 OpenCV (1):入门」 「Python 图像处理 OpenCV (2):像素处理与 Numpy 操作以及 Matplotlib 显示图像」 「Python ...

  4. 【OpenCV入门教程之三】 图像的载入,显示和输出 一站式完全解析(转)

    本系列文章由@浅墨_毛星云 出品,转载请注明出处. 文章链接:http://blog.csdn.net/poem_qianmo/article/details/20537737 作者:毛星云(浅墨)  ...

  5. (原)使用opencv的warpAffine函数对图像进行旋转

    转载请注明出处: http://www.cnblogs.com/darkknightzh/p/5070576.html 参考网址: http://stackoverflow.com/questions ...

  6. OpenCV成长之路:图像直方图的应用

    OpenCV成长之路:图像直方图的应用 2014-04-11 13:57:03 标签:opencv 图像 直方图 原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 .作者信息和本声明.否 ...

  7. OpenCV成长之路:图像滤波

    http://ronny.blog.51cto.com/8801997/1394138 OpenCV成长之路:图像滤波 2014-04-11 14:28:44 标签:opencv 边缘检测 sobel ...

  8. [OpenCV学习笔记2][Mat数据类型和操作]

    [Mat数据类型和基本操作] ®.运行环境:Linux(RedHat+OpenCV3.0) 1.Mat的作用: Mat类用于表示一个多维的单通道或者多通道的稠密数组.能够用来保存实数或复数的向量.矩阵 ...

  9. OpenCV 鼠标手动绘制掩码图像

    OpenCV 鼠标手动绘制掩码图像 完整的代码: #include <opencv2/core/core.hpp> #include <opencv2/highgui/highgui ...

随机推荐

  1. App 性能测试分享

    在本文内,主要以Android性能测试为主进行分析 一.性能测试包含 1.启动时间测试   测试场景包括 - - - 首次安装启动时间.冷启动.热启动测试 2.页面响应时间:   用户从点击一个控件, ...

  2. 用命令在本地创建github仓库

    问题 每次创建github仓库,都要到github官网,有点麻烦,想在本地直接创建github仓库,写好项目后直接push. 操作系统:linux 步骤 1, 首先在github申请一个私人api t ...

  3. php--phpstorm使用正则匹配批量替换

    1.首先勾选正则规则 如图勾选右侧的Match Case和Regex 2.编写正则规则:无须添加//左右分解符,直接写正则表达式,注意应该转义的部分,需要原封不动替换的部分加上括号 3.编写替换规则: ...

  4. 「一闻秒懂」你了解goroutine和channel吗?

    开源库「go home」聚焦Go语言技术栈与面试题,以协助Gopher登上更大的舞台,欢迎go home~ 背景介绍 大家都知道进程是操作系统资源分配的基本单位,有独立的内存空间,线程可以共享同一个进 ...

  5. 四、华为VRP平台介绍和常用配置

    一.华为VRP平台 华为现用的平台是VRP(Versatile Routing Platform)是华为公司数据通信产品的通用操作系统平台. 包含华为产品中的路由.交换.安全.无线等等 二.华为设备管 ...

  6. Activity A 跳转到Activity B 生命周期

    又被生命周期折磨了一段时间,这次是被onPause 和 onStop 折磨了,一直认为Activity A 跳转到到 Activity B的生命周期是onPause(A),onStop(A),onCr ...

  7. .net批量更新(插入、修改、删除)数据库

    思路: 1. 设置DataTable中每行的状态标识,即调用DataRow的方法setAdded().setModified().Delete() 2. 使用DataAdapter的Update(Da ...

  8. 第八节:time和random模块

    定义: 模块是一组Python代码的集合,可以使用其他模块,也可以被其他模块使用. 重点: 1.模块的名字不要和自带的模块名字相同,不然会优先调用自己的那个模块,因为查找模块的时候是按照sys.pat ...

  9. 用Python绘制全球疫情变化地图

    目前全球疫情仍然比较严重,为了能清晰地看到疫情爆发以来至现在全球疫情的变化趋势,我绘制了一张疫情变化地图,完整代码共 230 行,需要的朋友在公众号回复关键字 疫情地图 即可. 废话不多说,先上图 下 ...

  10. Eclipse版本控制

    各版本的区别: 1.Eclipse IDE for Java Developers 是Eclipse的platform加上JDT插件,用来java开发的 2.Eclipse IDE for Java  ...