Smart Light Random Memory Sprays Retinex 传统图像增强 SLRMSR

news2024/11/15 12:40:39

文章目录

  • 前言
  • 1、Smart Light Random Memory Sprays Retinex概况
  • 2、Smart Light Random Memory Sprays Retinex的实现
    • 2.1、SLRMSR算法的伪代码
    • 2.2、初始化记忆喷雾(CreateInitialMemorySpray)
    • 2.3、更新记忆喷雾 (UpdateMemorySpray)
    • 2.4、计算颜色校正因子(ComputeColorCorrectionFactor)
    • 2.5、应用强度重映射(ApplyIntensityRemapping)
    • 2.6、应用引导滤波 (ApplyGuidedImageFiltering)
  • 3、Smart Light Random Memory Sprays Retinex效果


前言

  Smart Light Random Memory Sprays Retinex,即“智能光随机记忆喷雾Retinex”,简称SLRMSR。作为一种新的基于Retinex理论的图像增强算法,旨在解决图像亮度调整和颜色校正的问题。


1、Smart Light Random Memory Sprays Retinex概况

论文名称:
Smart light random memory sprays Retinex: a fast Retinex implementation for high-quality brightness adjustment and color correction
作者:
Nikola Banić,Sven Lončarić

  “智能光随机记忆喷雾Retinex”(Smart Light Random Memory Sprays Retinex,简称SLRMSR),该算法提出了记忆喷雾的概念,以减少每个像素操作的数量,从而实现了快速的Retinex基础的局部图像增强。同时提出了一种有效的图像强度重映射的方法,进一步显著地提高了图像的质量。最后通过使用引导滤波作为替代的光照处理方法,减少了原有LRSR算法的光晕效应。作为一种新的基于Retinex理论的图像增强算法,在解决图像亮度调整和颜色校正的问题上,具有显著优势。

2、Smart Light Random Memory Sprays Retinex的实现

2.1、SLRMSR算法的伪代码

在这里插入图片描述

2.2、初始化记忆喷雾(CreateInitialMemorySpray)

  图像第一个像素创建一个包含随机选择的邻近像素强度的记忆喷雾。
① 定义喷雾大小:
  确定记忆喷雾参数的大小n,作为预设值,决定了喷雾中包含像素的数量。
② 选择邻近像素:
  对于图像中每个像素,在其邻域定义一个包含n个随机选择的邻近像素区域。
③ 构建笛卡尔树:
  对于选定的每个邻近像素,收集其RGB强度值,进行笛卡尔树的构造,其每个节点包含子树的最大值。
④ 存储喷雾内容:
  构建好的笛卡尔树被作为存储记忆喷雾的数据结构,用于后续的像素处理。

2.3、更新记忆喷雾 (UpdateMemorySpray)

① 选择新像素:
  当处理新的像素时,算法会从当前像素的局部邻域中选择一个新的邻近像素。
② 更新笛卡尔树:
  新选定的像素强度值被添加到笛卡尔树中,同时,最旧的像素强度值被从树中移除。这种“先进先出”的更新机制确保了记忆喷雾始终反应最新的局部信息。
③ 计算最大值:
  通过笛卡尔树的根节点,可以快速获取当前记忆喷雾中所有像素强度的最大值。

//基于随机内存喷雾Retinex(Random Memory Sprays Retinex)算法对输入的彩色图像进行白平衡处理
void RandomMemorySpraysRetinexPerformWhiteBalance(Mat source, Mat& destination, int N, int n, double upperBound, int rowsStep, int colsStep, double rFactor) {

	int rows = source.rows;
	int cols = source.cols;

	int R = rFactor * sqrt((double)(rows * rows + cols * cols)) + 0.5;

	Mat normalized;
	source.convertTo(normalized, CV_64FC3);

	int outputRows = rows / rowsStep;
	int outputCols = cols / colsStep;
	destination = Mat(outputRows, outputCols, CV_64FC3);

	Vec3d* input = (Vec3d*)normalized.data;
	Vec3d* inputPoint = input;
	Vec3d* output = (Vec3d*)destination.data;
	Vec3d* outputPoint = output;

	RNG random;

	CartesianTree<double>** qhs;

	qhs = new CartesianTree<double>*[N];
	for (int i = 0; i < N; ++i) {
		qhs[i] = new CartesianTree<double>[3];
	}

	for (int outputRow = 0; outputRow < outputRows; ++outputRow) {
		for (int outputCol = 0; outputCol < outputCols; ++outputCol) {

			int row = outputRow * rowsStep;
			int col = outputCol * colsStep;

			inputPoint = input + row * cols + col;
			outputPoint = output + outputRow * outputCols + outputCol;

			Vec3d& currentPoint = *inputPoint;
			Vec3d& finalPoint = *outputPoint;
			finalPoint = Vec3d(0, 0, 0);

			for (int i = 0; i < N; ++i) {

				Vec3d max = Vec3d(0, 0, 0);

				while (qhs[i][0].Size() < n) {
					double angle = 2 * CV_PI * random.uniform(0.0, 1.0);
					double r = R * random.uniform(0.0, 1.0);

					int newRow = row + r * sin(angle);
					int newCol = col + r * cos(angle);

					if (newRow >= 0 && newRow < rows && newCol >= 0 && newCol < cols) {
						Vec3d& newPoint = input[newRow * cols + newCol];
						for (int k = 0; k < 3; ++k) {
							qhs[i][k].Push(newPoint[k]);
						}
					}
				}

				for (int k = 0; k < 3; ++k) {
					if (max[k] < qhs[i][k].Max()) {
						max[k] = qhs[i][k].Max();
					}

					qhs[i][k].Pop();

				}

				for (int k = 0; k < 3; ++k) {
					if (max[k] == 0) {
						max[k] = 1;
					}
					finalPoint[k] += currentPoint[k] / max[k];
				}

			}

			finalPoint /= N;

			for (int i = 0; i < 3; ++i) {
				if (finalPoint[i] > 1) {
					finalPoint[i] = 1;
				}
			}

		}
	}

	for (int i = 0; i < N; ++i) {
		delete[] qhs[i];
	}
	delete[] qhs;

	double scaleFactor = upperBound;

	if (rowsStep > 1 || colsStep > 1) {
		resize(destination, destination, source.size());
	}

	destination = destination * scaleFactor - 1;

	destination.convertTo(destination, source.type());

}

2.4、计算颜色校正因子(ComputeColorCorrectionFactor)

  颜色校正因子是基于当前像素亮度值和记忆喷雾的最大强度计算得出。其主要用于调整像素的颜色,以便在不同的光照条件下保持颜色的一致性。

//光照校正算法的实现
void ApplyIllumination(Mat source, Mat illumination, Mat& destination, double upperBound) {
	vector<Mat> destinationChannels;
	split(source, destinationChannels);
	vector<Mat> illuminationChannels;
	split(illumination, illuminationChannels);
	for (int i = 0; i < destinationChannels.size(); ++i) {
		destinationChannels[i].convertTo(destinationChannels[i], CV_64FC1);
		divide(destinationChannels[i], illuminationChannels[i], destinationChannels[i]);
	}

	merge(destinationChannels, destination);

	double* check = (double*)destination.data;
	for (int i = 0; i < destination.rows * destination.cols * 3; ++i) {
		if (check[i] >= upperBound) {
			check[i] = upperBound - 1;
		}
	}

	destination.convertTo(destination, source.type());

}

2.5、应用强度重映射(ApplyIntensityRemapping)

  强度重映射主要用于改善图像亮度调整和颜色校正的方法,特别是在处理高光细节处,以防止过度增强造成细节的丢失。即,在增强图像暗区域的同时,保持亮区域的高光细节,从而获得更为自然的视觉效果。
① 计算初始及LRSR的亮度:
  对于每个像素,首先计算其初始亮度Yi,及LRSR算法处理后的亮度Yo。
② 确定重映射的参数:
  引入重映射参数,用于调整亮度的增加强度。
③ 计算调整后的亮度:
  调整后的亮度是初始亮度Yi和处理后亮度Yo的凸组会。即:Yr = λi * Yi + (1 - λi) * Yo
  其中,λi 是一个介于0和1之间的权重因子,主要基于初始亮度Yi和一个调整参数a来计算。
④ 计算权重因子:
  权重因子λi,即:λi = (Yi / D) / (Yi / D + a)。
  其中,D是颜色通道的最大值(对于8bit颜色通道,D通常是255)。λi的值越大,表示保留的初始亮度越多,处理后亮度的影响越小。
⑤ 过滤处理:
  为了避免亮度增加的剧烈变化,对λi施以一个滤波器(例如使用5*5的均值核)进行平滑处理,得到平滑后的权重因子λ’i。
⑥ 计算最终亮度:
  最终的亮度值,即:OpIci = λ’i * Yo + (1 - λ’i) * Ici。
  其中OpIci是最终的输出亮度,Ici是原始图像中的像素亮度通道强度。

//基于随机内存喷雾Retinex算法对输入的彩色图像进行局部光照估计
void LightRandomMemorySpraysRetinexEstimateLocalIlumination(Mat source, Mat& destination, int N, int n, int inputKernelSize, int illuminantKernelSize, bool normalizeIlluminant = false, int rowsStep = 1, int colsStep = 1, double upperBound = 256.0, double rFactor = 1.0) {

	Mat retinex;
	RandomMemorySpraysRetinexPerformWhiteBalance(source, retinex, N, n, upperBound, rowsStep, colsStep, rFactor);

	Mat inputSource;
	Mat inputRetinex;

	source.convertTo(inputSource, CV_64FC3);
	retinex.convertTo(inputRetinex, CV_64FC3);

	Mat guidance;
	inputSource.copyTo(guidance);

	if (normalizeIlluminant) {
		Mat illuminant;
		divide(inputSource, inputRetinex, illuminant);
		vector<Mat> illuminantChannels;

		split(illuminant, illuminantChannels);
		Mat illuminantAverage = (illuminantChannels[0] + illuminantChannels[1] + illuminantChannels[2]) / 3;
		for (int i = 0; i < 3; ++i) {
			divide(illuminantChannels[i], illuminantAverage, illuminantChannels[i]);
		}
		merge(illuminantChannels, illuminant);

		inputSource = inputRetinex.mul(illuminant);
	}

	if (inputKernelSize > 1) {
		Mat averaging = Mat::ones(inputKernelSize, inputKernelSize, CV_64FC1) / (double)(inputKernelSize * inputKernelSize);
		boxFilter(inputSource, inputSource, -1, Size(inputKernelSize, inputKernelSize));
		boxFilter(inputRetinex, inputRetinex, -1, Size(inputKernelSize, inputKernelSize));
	}

	Mat illuminant;
	divide(inputSource, inputRetinex, illuminant);
	vector<Mat> illuminantChannels;

	if (illuminantKernelSize > 1) {
		Mat averaging = Mat::ones(illuminantKernelSize, illuminantKernelSize, CV_64FC1) / (double)(illuminantKernelSize * illuminantKernelSize);
		boxFilter(illuminant, illuminant, -1, Size(illuminantKernelSize, illuminantKernelSize));
	}

	illuminant.copyTo(destination);

}

2.6、应用引导滤波 (ApplyGuidedImageFiltering)

  使用引导滤波来处理重映射后的像素强度,以减少光晕效果并保留图像边缘。
其中,对图像进一步灰度化作为引导图像,进而采用引导滤波进行处理,最后输出目标图像。

//图像引导滤波
//对输入的图像img进行引导滤波,输出结果保存在result中。
void GuidedImageFilterC1(Mat img, Mat guidance, Mat& result, int r, double epsilon) {

	Mat p;
	img.convertTo(p, CV_64F);

	Mat I;
	guidance.convertTo(I, CV_64F);

	Mat meanI;
	boxFilter(I, meanI, -1, Size(r, r));

	Mat meanP;
	boxFilter(p, meanP, -1, Size(r, r));

	Mat corrI;
	boxFilter(I.mul(I), corrI, -1, Size(r, r));

	Mat corrIp;
	boxFilter(I.mul(p), corrIp, -1, Size(r, r));

	Mat varI = corrI - meanI.mul(meanI);
	Mat covIp = corrIp - meanI.mul(meanP);

	Mat a;
	divide(covIp, varI + epsilon, a);
	Mat b = meanP - a.mul(meanI);

	Mat meanA;
	boxFilter(a, meanA, -1, Size(r, r));

	Mat meanB;
	boxFilter(b, meanB, -1, Size(r, r));

	Mat q = meanA.mul(I) + meanB;

	q.convertTo(result, img.type());
}


//基于引导图像的彩色图像滤波方法
//基于引导图像guidance对输入的彩色图像img进行引导滤波,输出结果保存在result中。
void GuidedImageFilterC3(Mat img, Mat guidance, Mat& result, int r, double epsilon) {

	vector<Mat> imgChannels;
	vector<Mat> guidanceChannels;

	split(img, imgChannels);
	split(guidance, guidanceChannels);

	vector<Mat> resultChannels;

	for (int i = 0; i < imgChannels.size(); ++i) {
		Mat channelResult;
		GuidedImageFilterC1(imgChannels[i], guidanceChannels[i], channelResult, r, epsilon);
		resultChannels.push_back(channelResult);
	}

	merge(resultChannels, result);

}

//基于引导图像和随机内存喷雾Retinex算法的彩色图像局部光照估计方法
void GuidedLightRandomMemorySpraysRetinexEstimateLocalIlumination(Mat source, Mat& destination, int N, int n, int inputKernelSize, int illuminantKernelSize, bool normalizeIlluminant = false, int rowsStep = 1, int colsStep = 1, double upperBound = 256.0, double rFactor = 1.0) {

	Mat retinex;
	//白平衡化处理
	RandomMemorySpraysRetinexPerformWhiteBalance(source, retinex, N, n, upperBound, rowsStep, colsStep, rFactor);

	Mat inputSource;
	Mat inputRetinex;

	source.convertTo(inputSource, CV_64FC3);
	retinex.convertTo(inputRetinex, CV_64FC3);

	Mat guidance;
	inputSource.copyTo(guidance);

	//归一化处理
	if (normalizeIlluminant) {
		Mat illuminant;
		divide(inputSource, inputRetinex, illuminant);
		vector<Mat> illuminantChannels;

		split(illuminant, illuminantChannels);
		Mat illuminantAverage = (illuminantChannels[0] + illuminantChannels[1] + illuminantChannels[2]) / 3;
		for (int i = 0; i < 3; ++i) {
			divide(illuminantChannels[i], illuminantAverage, illuminantChannels[i]);
		}
		merge(illuminantChannels, illuminant);

		inputSource = inputRetinex.mul(illuminant);
	}

	double value = 40;
	double epsilon = value * value;

	if (inputKernelSize > 1) {
		//平滑图像
		GuidedImageFilterC3(inputSource, guidance, inputSource, inputKernelSize, epsilon);
		GuidedImageFilterC3(inputRetinex, guidance, inputRetinex, inputKernelSize, epsilon);
	}

	Mat illuminant;
	divide(inputSource, inputRetinex, illuminant);
	vector<Mat> illuminantChannels;

	if (illuminantKernelSize > 1) {
		GuidedImageFilterC3(illuminant, guidance, illuminant, illuminantKernelSize, epsilon);
	}

	illuminant.copyTo(destination);

}

3、Smart Light Random Memory Sprays Retinex效果

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1527526.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

到底什么时候该使用MongoDB

NoSQL是什么 NoSQL &#xff1a; Not Only SQL , 本质也是一种数据库的技术&#xff0c;相对于传统数据库技术&#xff0c;它不会遵循一些约束&#xff0c;比如 &#xff1a; sql 标准、 ACID 属性&#xff0c;表结构等。 NoSQL分类 类型应用场景典型产品Key-value存储缓存&…

【Excel自动化办公】使用openpyxl对Excel进行读写操作

目录 一、环境安装 1.1 创建python项目 1.2 安装openpyxl依赖 二、Excel数据读取操作 三、Excel数据写入操作 3.1 创建空白工作簿 3.2 写数据 四、设置单元格样式 4.1 字体样式 4.2 设置单元格背景填充色 4.3 设置单元格边框样式 4.4 单元格对齐方式 4.5 数据筛选…

体系化全面认识 Nginx !

高并发、高性能&#xff1b;模块化架构使得它的扩展性非常好&#xff1b;异步非阻塞的事件驱动模型这点和 Node.js 相似&#xff1b;相对于其它服务器来说它可以连续几个月甚至更长而不需要重启服务器使得它具有高可靠性&#xff1b;热部署、平滑升级&#xff1b;完全开源&…

代码随想录算法训练营第46天 | 完全背包,139.单词拆分

动态规划章节理论基础&#xff1a; https://programmercarl.com/%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92%E7%90%86%E8%AE%BA%E5%9F%BA%E7%A1%80.html 完全背包理论基础&#xff1a; https://programmercarl.com/%E8%83%8C%E5%8C%85%E9%97%AE%E9%A2%98%E7%90%86%E8%AE%BA%E5%9…

数字化社会的新纪元:揭秘 Web3 的社交网络

随着区块链技术的不断发展和普及&#xff0c;Web3作为其重要组成部分&#xff0c;正逐渐改变着社交网络的面貌。Web3的出现不仅为社交网络带来了新的技术和理念&#xff0c;还为用户提供了更加开放、透明和安全的社交体验。本文将深入探讨Web3的社交网络&#xff0c;揭示其在数…

计算机网络:性能指标

计算机网络&#xff1a;性能指标 速率带宽吞吐量时延时延带宽积往返时间利用率丢包率 本博客介绍计算机网络的性能指标&#xff0c;我们可以从不同的方面来度量计算机网络的性能。常用的计算机网络性能指标有以下 8 个&#xff0c;他们是&#xff1a;速率、带宽、吞吐量、时延、…

47.全排列II

// 定义一个Solution类&#xff0c;用于解决给定不重复整数数组的全排列问题 class Solution {// 初始化结果集&#xff0c;用于存放所有不重复的全排列组合List<List<Integer>> result new ArrayList<>();// 初始化路径变量&#xff0c;用于暂存当前递归生…

ESP32实现(MQTT Client)连接物联网平台(EMQX)

目录 概述 1 配置EMQX服务器 1.1 搭建EMQX服务器 1.2 配置服务器参数 2 ESP32实现MQTT Client 2.1 创建MQTT Client项目 2.2 实现MQTT Client 2.3 ESP32连接EMQX 3 ESP32Client实现广播和订阅消息 3.1 广播消息 3.1.1 编写广播消息函数 3.1.2 下载和验证 3.1.3 订阅…

Windows11安装Msql8.0版本详细安装步骤!

文章目录 前言一、下载Mysql二、安装Mysql三、登录验证三、环境变量配置总结 前言 每次搭建新环境的时候&#xff0c;都需要网上搜寻安装的步骤教程&#xff01;为了以后方便查阅&#xff01;那么本次就记录一下Windows11安装Msql8.0的详细步骤&#xff01;也希望能帮助到有需…

蓝桥杯物联网竞赛_STM32L071_12_按键中断与串口中断

按键中断&#xff1a; 将按键配置成GPIO_EXTI中断即外部中断 模式有三种上升沿&#xff0c;下降沿&#xff0c;上升沿和下降沿都会中断 external -> 外部的 interrupt -> 打断 trigger -> 触发 detection -> 探测 NVIC中将中断线ENABLE 找接口函数 在接口函数中写…

Apache Doris 2.1 核心特性 Variant 数据类型技术深度解析

在最新发布的 Apache Doris 2.1 新版本中&#xff0c;我们引入了全新的数据类型 Variant&#xff0c;对半结构化数据分析能力进行了全面增强。无需提前在表结构中定义具体的列&#xff0c;彻底改变了 Doris 过去基于 String、JSONB 等行存类型的存储和查询方式。为了让大家快速…

redis-黑马点评-商户查询缓存

缓存&#xff1a;cache public Result queryById(Long id) {//根据id在redis中查询数据String s redisTemplate.opsForValue().get(CACHE_SHOP_KEY id);//判断是否存在if (!StrUtil.isBlank(s)) {//将字符串转为bean//存在&#xff0c;直接返回Shop shop JSONUtil.toBean(s, …

Linux课程四课---Linux第一个小程序(进度条)

作者前言 &#x1f382; ✨✨✨✨✨✨&#x1f367;&#x1f367;&#x1f367;&#x1f367;&#x1f367;&#x1f367;&#x1f367;&#x1f382; ​&#x1f382; 作者介绍&#xff1a; &#x1f382;&#x1f382; &#x1f382; &#x1f389;&#x1f389;&#x1f389…

Windows影子账户

Windows影子账户 先检查administrator账户有没有被禁用&#xff0c;如果administrator账户被禁用 所属组内的账户都会被禁用&#xff0c;导致影子账户无法登录 创建隐藏用户 加入管理员组 隐藏用户创建方法&#xff1a; net user 用户名$ 密码 /add 输入 regedit打开注册表…

微信小程序简单实现手势左右滑动和点击滑动步骤条功能

使用微信小程序实现左右滑动功能&#xff0c;自定义顶部图案&#xff0c;点击文字滑动和手势触屏滑动&#xff0c;功能简单&#xff0c;具体实现代码如下所示&#xff1a; 1、wxss代码&#xff1a; /* 步骤条 */ .tab-box {display: flex;flex-direction: row;position: fix…

springboot整合springsecurity,从数据库中认证

概述&#xff1a;springsecurity这个东西太容易忘了&#xff0c;这里写点东西&#xff0c;避免忘掉 目录 第一步&#xff1a;引入依赖 第二步&#xff1a;创建user表 第三步&#xff1a;创建一个用户实体类&#xff08;User&#xff09;和一个用于访问用户数据的Repository…

Midjourney 和 Dall-E 的优劣势比较

Midjourney 和 Dall-E 的优劣势比较 Midjourney 和 Dall-E 都是强大的 AI 绘画工具&#xff0c;可以根据文本描述生成图像。 它们都使用深度学习模型来理解文本并将其转换为图像。 但是&#xff0c;它们在功能、可用性和成本方面存在一些差异。 Midjourney 优势: 可以生成更…

yocto编译测试

源码下载 git clone -b gatesgarth git://git.yoctoproject.org/poky lkmaolkmao-virtual-machine:~/yocto$ git clone -b gatesgarth git://git.yoctoproject.org/poky Cloning into poky... remote: Enumerating objects: 640690, done. remote: Counting objects: 100% (13…

【漏洞复现】CVE-2004-2761:使用弱哈希算法签名的 SSL 证书(SSL Certificate Signed Using Weak Hashing Algorithm)

概要&#xff1a;本次复现是针对编号为CVE-2004-2761的漏洞&#xff0c;由于条件有限&#xff0c;本次复现通过创建自签名证书进行操作。 问题描述&#xff1a;证书链中的 SSL 证书使用弱哈希算法进行签名。 1 环境搭建 本次复现环境在Linux平台下使用Nginx进行环境的搭建&…

ModbusTCP转Profinet网关高低字节交换切换

背景&#xff1a;在现场设备与设备通迅之间通常涉及到从一种字节序&#xff08;大端或小端&#xff09;转换到另一种字节序。大端字节序是指高位字节存储在高地址处&#xff0c;而小端字节序是指低位字节存储在低地址处。在不动原有程序而又不想或不能添加程序下可选用ModbusTC…