OpenCV暗通道去雾算法在内窥镜视频流中的应用

OpenCV暗通道去雾算法在内窥镜视频流中的应用

本文算法基于大气去雾模型,以及何凯明博士的暗通道先验的理论思想,细化到内窥镜图像处理领域,做了一些速度上的提升以及优化。针对内窥镜视频必有反光,且手术腔内光强不会发生剧烈变化的特点,因此讲光强估计值A直接定义为255,减少了每帧图像在估算光强值时的时间。其他领域可能不适用该算法。

#include<opencv2\opencv.hpp>
#include<iostream>

using namespace cv;
using namespace std;
//block模板大小与omega作用程度这两个参数可调整
int block = 3;//每个像素点的周围block*block的矩形,block越大,速度越快,失真越明显
double omega = 0.7;//除雾的程度,取值范围为[0,1],值越大,处理后图像颜色越深
Mat ori, out;//原始Mat对象ori,处理后Mat对象out

//定义三通道最小值函数min_3
double min_3(double g, double b, double r)
{
	double min;
	if (g <= b && g <= r)min = g;
	if (b <= g && b <= r)min = b;
	if (r <= g && r <= b)min = r;
	return min;
}

//定义矩阵内的最小值函数
double mat_min(Mat matrix)
{
	double min_in_matrix = 255.0;
	for (int i = 0; i < matrix.cols; i++)
	{
		for (int j = 0; j < matrix.rows; j++)
		{
			if (min_in_matrix > matrix.at<uchar>(i, j))
				min_in_matrix = matrix.at<uchar>(i, j);
		}
	}
	return min_in_matrix;
}

//定义去雾函数如下
Mat defogging(Mat image_in, int block, double omega)
{
	//创建一个Mat类型的vector  out_temp,其中维数是输入图像image_in的通道数
	vector<Mat> out_temp(3);
	split(image_in, out_temp);//将原图ori的三通道分割并存储到out_temp内

	//创建一个Mat类型的vector  out_ROI
	vector<Mat>out_ROI(3);
	out_ROI[0] = Mat(block, block, CV_8UC1);//绿色8位单通道
	out_ROI[1] = Mat(block, block, CV_8UC1);//蓝色8位单通道
	out_ROI[2] = Mat(block, block, CV_8UC1);//红色8位单通道

	//创建暗通道ROI对象,暗通道对象,输出图像的Mat对象。
	Mat dark_ROI, dark_channel, out;
	dark_ROI = Mat(block, block, CV_8UC1);//暗通道ROI的8位单通道
	dark_channel = Mat(image_in.rows, image_in.cols, CV_8UC1);//给暗通道分配大小
	out = Mat(image_in.rows, image_in.cols, CV_8UC3);//去雾后的图像,三通道合并成out的Mat对象

	//创建每次循环的处理区域work_ROI,并初始化坐标极其宽度高度
	Rect work_ROI;
	work_ROI.x = 0;//矩形左上角横坐标
	work_ROI.y = 0;//矩形左上角纵坐标
	work_ROI.width = block;//矩形宽为block
	work_ROI.height = block;//矩形高为block
	//原图像ROI位置以及大小Rect类型储存

	//按照论文方法的大气光强估计算法计算手术腔内光强估计值。
	//在原始有雾图像中寻找对应的具有最高亮度的点的值,作为A值。
	//并且由于是固定光源,腔内光强不会发生剧烈变化,因此无需每一帧的计算一次A。
	//因为必定会有镜面反射点,于是直接令A_0,A_1,A_2都为255。
	//简化排序每帧对于求A的一个大排序时间,优化在该领域算法复杂度。
	//定义完毕手术腔内光强估计值
	std::vector<double>A(3);
	A = { 255.0,255.0,255.0 };//公式中每个通道的光强估计值A

	//下面求暗通道dark_channel。
	double min0 = 0, min1 = 0, min2 = 0, min = 0;//每个通道的最小值,以及整体最小值。
	for (int i = 0; i < image_in.cols / block; i++)//外层循环循环原图列数/block 次
	{
		for (int j = 0; j < image_in.rows / block; j++)	//内层循环原图行数/block 次
		{
			//分别计算三个通道内ROI的最小值
			out_ROI[0] = out_temp[0](work_ROI);//在矩阵work_ROI内的绿色分量
			min0 = mat_min(out_ROI[0]);//求出第一个像素点在out_ROI_0绿色分量的最小值

			out_ROI[1] = out_temp[1](work_ROI);//蓝色分量在矩阵work_ROI内的小值
			min1 = mat_min(out_ROI[1]);//求出第一个像素点蓝色分量的最小值

			out_ROI[2] = out_temp[2](work_ROI);//红色分量在矩阵work_ROI内的小值
			min2 = mat_min(out_ROI[2]);//求出第一个像素点红色分量的最小值

			//求三个通道内最小值的最小值 即求min1 min2 min3中的最小值赋值给min
			min = min_3(min0, min1, min2);//min为这个ROI中暗原色

			dark_ROI = dark_channel(work_ROI);//拷贝work_ROI给dark_channel后再赋给dark_ROI
			Mat temp = Mat(block, block, CV_8UC1, Scalar(min));//将最小值赋给temp
			temp.copyTo(dark_ROI);//按原色拷贝给dark_ROI
			//rect_ROI坐标转换到下一个矩形区块。
			work_ROI.x = block * i;//工作区域向后移动block个像素
			work_ROI.y = block * j;//工作区域向后移动block个像素

		}
	}


	//下面求处理过后的图像
	double tx;//透射率t(x)
	uchar ori_0, ori_1, ori_2;//输出图像每个通道像素值
	for (int i = 0; i < image_in.rows; i++)
	{
		for (int j = 0; j < image_in.cols; j++)
		{
			tx = 1 - omega * dark_channel.at<uchar>(i, j)/255.0;
			//根据公式透射率=1-omega*min(dark_channel)/255,omega的值不要太大,否则颜色过暗视觉效果不好,得到每个点的透射率
			if (tx < 0.1)tx = 0.1;
			//防止精度不够之后会出现除0的错误

			//根据雾图模型 I(x)=J(x)t(x)+A(1-t(x))可以得到:J(x)=(I(x)-A)/t(x)+A 根据上式计算每个通道
			ori_0 = (image_in.at<Vec3b>(i, j)[0] - A[0]) / tx + A[0];
			//绿色通道每个像素点=(输入图像绿色通道值-光强估计值)/透射率+光强估计值
			ori_1 = (image_in.at<Vec3b>(i, j)[1] - A[1]) / tx + A[1];
			//蓝色通道每个像素点=(输入图像蓝色通道值-光强估计值)/透射率+光强估计值
			ori_2 = (image_in.at<Vec3b>(i, j)[2] - A[2]) / tx + A[2];
			//红色通道每个像素点=(输入图像红色通道值-光强估计值)/透射率+光强估计值

			out.at<Vec3b>(i, j)[0] = ori_0;
			//输出图像绿色通道值写入
			out.at<Vec3b>(i, j)[1] = ori_1;
			//输出图像蓝色通道值写入
			out.at<Vec3b>(i, j)[2] = ori_2;
			//输出图像红色通道值写入
		}
	}
	return out;
}

int main()
{	
	cout << "请输入视频流路径: << endl;
	string PATH;
	cin >> PATH;
	VideoCapture capture;
	capture.open(PATH);
	while (true)
	{	
		Mat ori;
		capture >> ori;
		imshow("原图像ori", ori);
		waitKey(20);
		//算法实现
		out = defogging(ori, block, omega);
		imshow("处理后图像out", out);
		//算法结束
		waitKey(20);
	}
	return 0;
}

实现效果图如下:
处理前
处理后
可以看出效果差别。参数选取了block=3以及omega=0.7

总结:该算法思路,循环执行3*3的每一小块图片,将其在块中每个通道的最小值赋值给暗通道,在经过论文公式计算即可还输出去雾后图像,这也间接实现了对比度增强。

  • 1
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
OpenCV暗通道去雾的步骤如下: 1. 读取待去雾的图像I(x)和全球大气光成分A。 2. 计算暗通道图像Jdark(x),其Jdark(x) = min{min{I(x)}|x∈Ω},Ω为图像块的集合,一般取3*3或者15*15的块。 3. 估计全球大气光成分A,其A = max{J(x)|x∈Ω},Ω为图像块的集合,一般取3*3或者15*15的块。 4. 估计折射率t,其t = 1 - w * min{Jdark(x)/A|x∈Ω},w为权重系数,一般取0.95。 5. 对每个颜色通道进行去雾处理,其J(x) = (I(x) - A) / t + A。 6. 输出去雾后的图像J(x)。 下面是OpenCV暗通道去雾的Python代码示例: ```python import cv2 import numpy as np def dark_channel(image, block_size): # 计算暗通道图像 b, g, r = cv2.split(image) min_channel = cv2.min(cv2.min(r, g), b) kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (block_size, block_size)) dark_channel = cv2.erode(min_channel, kernel) return dark_channel def estimate_atmosphere(image, dark_channel): # 估计全球大气光成分 h, w = image.shape[:2] flat_image = image.reshape(h * w, 3) flat_dark = dark_channel.reshape(h * w) indices = np.argsort(flat_dark)[::-1][:h * w // 100] atmosphere = np.max(flat_image[indices], axis=0) return atmosphere def estimate_transmission(dark_channel, atmosphere, w): # 估计折射率 transmission = 1 - w * (dark_channel / atmosphere) return transmission def dehaze(image, block_size=15, w=0.95): # OpenCV暗通道去雾 dark = dark_channel(image, block_size) atmosphere = estimate_atmosphere(image, dark) transmission = estimate_transmission(dark, atmosphere, w) transmission = cv2.max(transmission, 0.1) result = np.empty_like(image) for i in range(3): result[:, :, i] = (image[:, :, i] - atmosphere[i]) / transmission + atmosphere[i] result = cv2.normalize(result, None, 0, 255, cv2.NORM_MINMAX) return result.astype(np.uint8) ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值