本文的主要思想参考了以下文章,学习过程中对各个api进行了了解和分析。
原文

CV_Assert

检查运行情况,如果出现错误,则显示错误信息。
在括号内填入想要检测的信息然后错误则抛出一个异常,否则就返回true
在这里就相当于我们做的一个image.empty()检测。

src.convertTo(dst, type, scale, shift)

注意也不是所有格式的Mat型数据都能被使用保存为图片,目前OpenCV主要只支持单通道和3通道的图像,并且此时要求其深度为8bit和16bit无符号(即CV_16U),所以其他一些数据类型是不支持的,比如说float型等。
convertTo()函数负责转换数据类型不同的Mat,即可以将类似float型的Mat转换到imwrite()函数能够接受的类型。
而cvtColor()函数是负责转换不同通道的Mat,因为该函数的第4个参数就可以设置目的Mat数据的通道数(只是我们一般没有用到它,一般情况下这个函数是用来进行色彩空间转换的)。
缩放并转换到另外一种数据类型:
dst:目的矩阵;
type:需要的输出矩阵类型,或者更明确的,是输出矩阵的深度,如果是负值(常用-1)则输出矩阵和输入矩阵类型相同;
scale:比例因子;
shift:将输入数组元素按比例缩放后添加的值;
dst(i)=src(i)xscale+(shift,shift,…)
在去雾操作的第一步里面就需要做一个转化数据类型的操作

image.convertTo(fImage, CV_32FC3, 1.0 / 255.0);

那么在这里我们需要知道一个知识点:
就是在浮点数的图像数据类型中,其灰度值的范围都是在0-1之间的,而原图像是BGR的,所有像素点的灰度值都在0-255之间,所以如果要保持原图像不变,要乘于一个比例因子:(float)(1/255)

copyMakeBorder

函数原型:

void copyMakeBorder( const Mat& src, Mat& dst,
int top, int bottom, int left, int right,
int borderType, const Scalar& value=Scalar() );

功能

扩充src的边缘,将图像变大,然后以各种外插方式自动填充图像边界,这个函数实际上调用了函数cv::borderInterpolate,这个函数最重要的功能就是为了处理边界,比如均值滤波或者中值滤波中,使用copyMakeBorder将原图稍微放大,然后我们就可以处理边界的情况了

参数解释

src,dst:原图与目标图像

top,bottom,left,right分别表示在原图四周扩充边缘的大小

borderType:扩充边缘的类型,就是外插的类型,OpenCV中给出以下几种方式

  • BORDER_REPLICATE
  • BORDER_REFLECT
  • BORDER_REFLECT_101
  • BORDER_WRAP
  • BORDER_CONSTANT

实际中,还有其他的宏定义

//! various border interpolation methods
enum { BORDER_REPLICATE=IPL_BORDER_REPLICATE, BORDER_CONSTANT=IPL_BORDER_CONSTANT,
BORDER_REFLECT=IPL_BORDER_REFLECT, BORDER_WRAP=IPL_BORDER_WRAP,
BORDER_REFLECT_101=IPL_BORDER_REFLECT_101, BORDER_REFLECT101=BORDER_REFLECT_101,
BORDER_TRANSPARENT=IPL_BORDER_TRANSPARENT,
BORDER_DEFAULT=BORDER_REFLECT_101, BORDER_ISOLATED=16 };

总而言之,在进行图像卷积操作的时候为了处理图像边缘等像素的问题都是需要通过设定重复边缘来达到我们想要的效果的

reshape

reshape有两个参数:Mat::reshape(int cn, int rows=0)

其中,参数:cn为新的通道数,如果cn = 0,表示通道数不会改变。

参数rows为新的行数,如果rows = 0,表示行数不会改变。

注意:新的行列必须与原来的行列相等。就是说,如果原来是5行3列,新的行和列可以是1行15列,3行5列,5行3列,15行1列。仅此几种,否则会报错。

Mat_

在读取矩阵元素时,以及获取矩阵某行的地址时,需要指定数据类型。这样首先需要不停地写“”,让人感觉很繁琐,在繁琐和烦躁中容易犯错,如下面代码中的错误,用 at()获取矩阵元素时错误的使用了 double 类型。这种错误不是语法错误,因此在编译时编译器不会提醒。在程序运行时, at()函数获取到的不是期望的(i,j)位置处的元素,数据已经越界,但是运行时也未必会报错。这样的错误使得你的程序忽而看上去正常,忽而弹出“段错误”,特别是在代码规模很大时,难以查错。

      如果使用 Mat_类,那么就可以在变量声明时确定元素的类型, 访问元素时不再需要指定元素类型,即使得代码简洁,又减少了出错的可能性。

sortIdx

在 MATLAB 里,返回排序后的矩阵以及对应原矩阵的索引是在 sort 一个函数搞定,但在 OpenCV 中,其功能分别被分配到了 cv::sort 和 cv::sortIdx 里,cv::sort 负责返回排序后的矩阵,cv::sortIdx 负责返回对应原矩阵的索引。
在 OpenCV 中,我们用类似于 CV_SORT_EVERY_ROW + CV_SORT_ASCENDING 这样的方式来一并指定对列还是对行以及升序还是降序,其指示值定义如下,所以可以组合出 4 种不同的方式:

#define CV_SORT_EVERY_ROW    0
#define CV_SORT_EVERY_COLUMN 1
#define CV_SORT_ASCENDING    0
#define CV_SORT_DESCENDING   16//CV_SORT_EVERY_ROW + CV_SORT_ASCENDING:对矩阵的每行按照升序排序;
//CV_SORT_EVERY_ROW + CV_SORT_DESCENDING:对矩阵的每行按照降序排序;
//CV_SORT_EVERY_COLUMN + CV_SORT_ASCENDING:对矩阵的每列按照升序排序;
//CV_SORT_EVERY_COLUMN + CV_SORT_DESCENDING:对矩阵的每列按照降序排序;
————————————————
版权声明:本文为CSDN博主「JoannaJuanCV」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/zfjBIT/article/details/88397910

其他的就都没有新知识点了,靠着论文去编码就行

通过测试代码

#include <iostream>
#include <opencv2/opencv.hpp>
#include <vector>
#include <cstring>
#include<opencv2\core\core.hpp>
#include<opencv2\highgui\highgui.hpp>
#include<opencv2\imgproc\imgproc.hpp>#define CV_SORT_EVERY_ROW    0
#define CV_SORT_EVERY_COLUMN 1
#define CV_SORT_ASCENDING    0
#define CV_SORT_DESCENDING   16//CV_SORT_EVERY_ROW + CV_SORT_ASCENDING:对矩阵的每行按照升序排序;
//CV_SORT_EVERY_ROW + CV_SORT_DESCENDING:对矩阵的每行按照降序排序;
//CV_SORT_EVERY_COLUMN + CV_SORT_ASCENDING:对矩阵的每列按照升序排序;
//CV_SORT_EVERY_COLUMN + CV_SORT_DESCENDING:对矩阵的每列按照降序排序;using namespace std;
using namespace cv;int main()
{Mat image = imread("E:/data/h1.jpg");if (image.empty()){cout << "error!";return 0;}cout << "complete" << endl;imshow("src", image);//imshow("src", src);//一、求暗通道//1.首先对图像进行归一化Mat fImage;image.convertTo(fImage, CV_32FC3, 1.0 / 255.0);//就是在浮点数的图像数据类型中,其灰度值的范围都是在0-1之间的,而原图像是BGR的,所有像素点的灰度值都在0-255之间,所以如果要保持原图像不变,要乘于一个比例因子:(float)(1/255)//2.设定运算窗口int hPatch = 15;int vPatch = 15;//3.给归一化的图片设定边界Mat fImageBorder;//此边界是为了便于我们处理边缘数据,在后续的卷积操作中,由于部分卷积算子的尺寸特殊性,有些边缘像素没办法处理到,所以就要用此api进行边缘的复制copyMakeBorder(fImage, fImageBorder, vPatch / 2, vPatch / 2, hPatch / 2, hPatch / 2, BORDER_REPLICATE);//4.分离通道std::vector<Mat> fImageBorderVector(3);split(fImageBorder,fImageBorderVector);//把三个通道push到vector中去//5.创建并且计算暗通道Mat darkChannel(image.rows,image.cols, CV_32FC1);//单通道图像double minTemp, minPixel;//计算暗通道for (unsigned int r = 0; r < darkChannel.rows; r++){for (unsigned int c = 0; c < darkChannel.cols; c++){minPixel = 1.0;for (std::vector<Mat>::iterator it = fImageBorderVector.begin(); it != fImageBorderVector.end(); it++){Mat roi(*it, Rect(c,r,hPatch,vPatch));minMaxLoc(roi,&minTemp);//相当于一个打擂算法,在这个窗口里面找最小值minPixel = std::min(minPixel, minTemp);}//打擂结束,设定最小值,把暗通道算出来,并且赋值darkChannel.at<float>(r, c) = float(minPixel);}}/*这一段代码是用来测试我们的暗通道是否提取成功imshow("dst", darkChannel);Mat darkChannel8U;darkChannel.convertTo(darkChannel8U, CV_8UC1, 255, 0);imwrite("E:/outputdata/darkchannel_h1.jpg", darkChannel8U);success!*///二、通过暗通道来实现A的过程,求的是大气的那个值//1.计算出darkChannel中, 前top个亮的值, 论文中取值为0.1 %float top = 0.001;float numberTop = top * darkChannel.rows * darkChannel.cols;Mat darkChannelVectorOneRow = darkChannel.reshape(1, 1);//单通道,一行Mat_<int> darkChannelVectorIndex;//Mat_类sortIdx(darkChannelVectorOneRow, darkChannelVectorIndex, CV_SORT_EVERY_ROW + CV_SORT_DESCENDING);//获取数组的索引//这个函数是用来先对原来的进行一次sort,然后返回这个sort的数组的中每一个元素所对应元素数组的index,再形成一个arrary赋值到第二个参数中去。//2.制作掩码,设定一个二值maskMat mask(darkChannelVectorIndex.rows, darkChannelVectorIndex.cols, CV_8UC1);//注意mask的类型必须是CV_8UC1//然后要做的是,找出原图像中亮度在前百分之1的点,把对应的点设置为1,其他的都设置成0//然后由于每行是已经通过sortIdx序列化之后的结果for (unsigned int r = 0; r < darkChannelVectorIndex.rows; r++){for (unsigned int c = 0; c < darkChannelVectorIndex.cols; c++){if (darkChannelVectorIndex.at<int>(r, c) <= numberTop)mask.at<uchar>(r, c) = 1;elsemask.at<uchar>(r, c) = 0;}}Mat darkChannelIndex = mask.reshape(1, darkChannel.rows);//单通道vector<double> A(3);//分别存取A_b,A_g,A_rvector<double>::iterator itA = A.begin();vector<Mat>::iterator it = fImageBorderVector.begin();//2.2在求第三步的t(x)时,会用到以下的矩阵,这里可以提前求出vector<Mat> fImageBorderVectorA(3);vector<Mat>::iterator itAA = fImageBorderVectorA.begin();for (it= fImageBorderVector.begin() ; it != fImageBorderVector.end() && itA != A.end() && itAA != fImageBorderVectorA.end(); it++, itA++, itAA++){Mat roi(*it, Rect(hPatch / 2, vPatch / 2, darkChannel.cols, darkChannel.rows));minMaxLoc(roi, 0, &(*itA), 0, 0, darkChannelIndex);//(*itAA) = (*it) / (*itA); //[注意:这个地方有除号,但是没有判断是否等于0]}/*第三步:求t(x)*/Mat darkChannelA(darkChannel.rows, darkChannel.cols, CV_32FC1);float omega = 0.95;//0<w<=1,论文中取值为0.95//代码和求darkChannel的时候,代码差不多for (unsigned int r = 0; r < darkChannel.rows; r++){for (unsigned int c = 0; c < darkChannel.cols; c++){minPixel = 1.0;for (itAA = fImageBorderVectorA.begin(); itAA != fImageBorderVectorA.end(); itAA++){Mat roi(*itAA, Rect(c, r, hPatch, vPatch));minMaxLoc(roi, &minTemp);minPixel = min(minPixel, minTemp);}darkChannelA.at<float>(r, c) = float(minPixel);}}Mat tx = 1.0 - omega * darkChannelA;/*第四步:我们可以求J(x)*/float t0 = 0.1;//论文中取t0 = 0.1Mat jx(image.rows, image.cols, CV_32FC3);for (size_t r = 0; r < jx.rows; r++){for (size_t c = 0; c < jx.cols; c++){jx.at<Vec3f>(r, c) = Vec3f((fImage.at<Vec3f>(r, c)[0] - A[0]) / max(tx.at<float>(r, c), t0) + A[0], (fImage.at<Vec3f>(r, c)[1] - A[1]) / max(tx.at<float>(r, c), t0) + A[1], (fImage.at<Vec3f>(r, c)[2] - A[2]) / max(tx.at<float>(r, c), t0) + A[2]);}}namedWindow("jx", 1);imshow("jx", jx);Mat jx8u;jx.convertTo(jx8u, CV_8UC3, 255, 0);imwrite("E:/data-output/h1_res.jpg", jx8u);waitKey(0);
}

Opencv4(C++)实战案例1:将朦胧的图像变清晰(去雾)相关推荐

  1. 2021年大数据Spark(四十一):SparkStreaming实战案例六 自定义输出 foreachRDD

    目录 SparkStreaming实战案例六 自定义输出-foreachRDD 需求 注意: 代码实现 SparkStreaming实战案例六 自定义输出-foreachRDD 需求 对上述案例的结果 ...

  2. 2021年大数据Spark(三十九):SparkStreaming实战案例四 窗口函数

    目录 SparkStreaming实战案例四 窗口函数 需求 代码实现 SparkStreaming实战案例四 窗口函数 需求 使用窗口计算: 每隔5s(滑动间隔)计算最近10s(窗口长度)的数据! ...

  3. 2021年大数据Spark(三十七):SparkStreaming实战案例二 UpdateStateByKey

    目录 SparkStreaming实战案例二 UpdateStateByKey 需求 1.updateStateByKey 2.mapWithState 代码实现 SparkStreaming实战案例 ...

  4. 2021年大数据Spark(三十六):SparkStreaming实战案例一 WordCount

    目录 SparkStreaming实战案例一 WordCount 需求 准备工作 代码实现 第一种方式:构建SparkConf对象 第二种方式:构建SparkContext对象 完整代码如下所示: 应 ...

  5. 【分布式事务】tcc-transaction分布式TCC型事务框架搭建与实战案例(基于Dubbo/Dubbox)...

    一.背景 有一定分布式开发经验的朋友都知道,产品/项目/系统最初为了能够快速迭代上线,往往不太注重产品/项目/系统的高可靠性.高性能与高扩展性,采用单体应用和单实例数据库的架构方式快速迭代开发:当产品 ...

  6. 7个实战案例、24个学习视频、12G干货资料...今天带你免费入门Python数据分析!...

    相信许多做数据的都有这样的经历: 你花了大半天整合了一张数据表,却因为其他部门的错误,导致表格结构全错了!于是你又要吭哧吭哧重新来过... 每次数据都重复洗一遍,还这么慢,要是有一劳永逸的方法就好了. ...

  7. R语言诊断试验数据处理与ROC分析实战案例2

    R语言诊断试验数据处理与ROC分析实战案例2 目录 R语言诊断试验数据处理与ROC分析实战案例2 #ROC指标 #样例数据

  8. R语言诊断试验数据处理与ROC分析实战案例1

    R语言诊断试验数据处理与ROC分析实战案例1 目录 R语言诊断试验数据处理与ROC分析实战案例1 #ROC指标 #样例数据

  9. R语言Kaplan-Meier绘制生存分析、Log-rank假设检验、Cox回归曲线实战案例:恶性黑色素瘤的术后数据生存分析

    R语言Kaplan-Meier绘制生存分析.Log-rank假设检验.Cox回归曲线实战案例:恶性黑色素瘤的术后数据生存分析 目录

最新文章

  1. java截取图片-设置方位+设置大小
  2. MongoDB数据库--扩展Base64,算法
  3. 01 ORA系列:ORA-00904 标识符无效 invalid identifier
  4. SparkSQL-从0到1认识Catalyst
  5. 不知道工作组名称怎样加入_剩米饭不知道怎样做?试试泡菜炒饭,再也不用担心米饭做多了...
  6. MySQL必会企业面试题
  7. [FPGA][DE0] Qsys 加入 FLASH 記憶體 方法及步驟
  8. Eclipse中如何安装Spring Tool Suite(STS)
  9. JAVA 中一个非常轻量级只有 200k 左右的 RESTful 路由框架
  10. mtd设备操作、jffs2
  11. [转]CxImage使用指南
  12. 常用中文字体 Unicode 编码
  13. php日志在哪,php日志在哪
  14. python_使用需要的气象台站提取气象数据
  15. java使用aspose打印pdf、word文件
  16. 布线时排阻(IC)管脚交换步骤
  17. 【Java获取国家法定节假日三种工具类其三】
  18. 二维快速傅里叶逆变换
  19. 24小时制和12小时制am Matlab,十二钟头制与二十四小时制详解
  20. 钱文品 | 《Redis在海量数据和高并发下的优化实践》主题分享

热门文章

  1. Chaos Emulator核心功能开发历程
  2. 杭州烟花大会发生爆炸事故近百人受伤-烟花大会-杭州西湖-烟花燃放
  3. wpf 音频波形图_wpf波形图 - IT屋-程序员软件开发技术分享社区
  4. 中西医结合骨伤科学练习考试答案
  5. c语言编写f16仿真程序,想学习一下 用C语言开发PIC的F16和F18系列单片机, 结果让IDE开发环境给搞悲剧,巨难用,...
  6. Lenovo E570 Windows 10 换Windows7
  7. 如何去除电脑桌面软件的图标箭头符号,以及“快捷方式字样”?
  8. 戴尔服务器找不到网卡驱动终极解决办法
  9. 贝叶斯决策理论(一):两种基本决策规则
  10. #创新应用#善听听书:进入听书的时代!