ReinHard颜色迁移

2023-10-27

看到颜色迁移,觉得还蛮有意思的,遂简单看了一下,代码实现好像有错误,但是不知道错误出在哪里。

算法参考:

https://blog.csdn.net/sin_geek/article/details/22443537

https://blog.csdn.net/sin_geek/article/details/22325229

代码实现:希望有高手指出我的错误

理论效果:

// Reinhard.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

#include "pch.h"
#include <iostream>
#include <opencv2\opencv.hpp>

using namespace cv;
using namespace std;

bool TranRein(uchar* lpDIBBits, int lmageWidth, int lmageHeight, uchar* lpDIBBits2, int lmageWidth2, int lmageHeight2, uchar* lpDIBBits3);
void AverageVariance(double* lpLab, int Width, int Height, double& al, double& aa, double& ab, double& vl, double& va, double& vb);
void RgbToLab(uchar R, uchar G, uchar B, double& l, double& a, double& b);
void LabToRgb(double l, double a, double b, uchar& R, uchar& G, uchar& B);
void mat2Sample(Mat& src, uchar* sample, int nWidth, int nHeight);
void sample2Mat(Mat&src, int nWidth, int nHeight, uchar* sample);

int main()
{
	Mat src = imread("3.jpg");
	int ImageHeight = src.rows;
	int ImageWidth = src.cols;
	uchar* lpDIBBits = new uchar[3 * ImageHeight * ImageWidth];
	mat2Sample(src, lpDIBBits, ImageWidth, ImageHeight);
	imshow("src", src);

	Mat dst = imread("1.jpg");
	int ImageHeight2 = dst.rows;
	int ImageWidth2 = dst.cols;
	uchar* lpDIBBits2 = new uchar[3 * ImageHeight2 * ImageWidth2];
	mat2Sample(dst, lpDIBBits2, ImageWidth2, ImageHeight2);
	imshow("dst", dst);


	uchar* lpDIBBits3 = new uchar[3 * ImageHeight2 * ImageWidth2];

	TranRein(lpDIBBits, ImageWidth, ImageHeight, lpDIBBits2, ImageWidth2, ImageHeight2, lpDIBBits3);

	Mat result(ImageHeight2, ImageWidth2, CV_8UC3);
	sample2Mat(result, ImageWidth2, ImageHeight2, lpDIBBits3);

	imshow("result", result);
	waitKey(0);

}



bool TranRein(uchar* lpDIBBits, int lmageWidth, int lmageHeight, uchar* lpDIBBits2, int lmageWidth2, int lmageHeight2, uchar* lpDIBBits3)
{
	//控制循环
	int i;
	int j;
	int nindex;
	//a为均值,v为方差,分别为源图像和目标图像的lab通道的均值和方差
	double al, aa, ab, vl, va, vb, al2, aa2, ab2, vl2, va2, vb2;
	//lab空间的矩阵  1目标图像,2源图像,3结果图像
	double* lpImageLab = new  double[lmageWidth*lmageHeight * 3];
	double* lpImageLab2 = new  double[lmageWidth2*lmageHeight2 * 3];
	double* lpImageLab3 = new  double[lmageWidth*lmageHeight * 3];

	//目标图像转换为lab,并求lab的均值及标准差
	for (j = 0; j < lmageHeight; j++)
	{
		for (i = 0; i < lmageWidth; i++)
		{
			nindex = ((lmageHeight - j - 1)*lmageWidth + i);
			RgbToLab(lpDIBBits[nindex * 3 + 2], lpDIBBits[nindex * 3 + 1], lpDIBBits[nindex * 3 + 0],
				lpImageLab[nindex * 3 + 0], lpImageLab[nindex * 3 + 1], lpImageLab[nindex * 3 + 2]);
		}
	}
	AverageVariance(lpImageLab, lmageWidth, lmageHeight, al, aa, ab, vl, va, vb);

	//源图像转换为lab,并求lab的均值及标准差
	for (j = 0; j < lmageHeight2; j++)
	{
		for (i = 0; i < lmageWidth2; i++)
		{
			nindex = ((lmageHeight2 - j - 1)*lmageWidth2 + i);
			RgbToLab(lpDIBBits2[nindex * 3 + 2], lpDIBBits2[nindex * 3 + 1], lpDIBBits2[nindex * 3 + 0],
				lpImageLab2[nindex * 3 + 0], lpImageLab2[nindex * 3 + 1], lpImageLab2[nindex * 3 + 2]);
		}
	}
	AverageVariance(lpImageLab2, lmageWidth2, lmageHeight2, al2, aa2, ab2, vl2, va2, vb2);

	//求结果图像的lab
	for (i = 0; i < lmageWidth*lmageHeight; i++)
	{
		lpImageLab3[i * 3 + 0] = (lpImageLab[i * 3 + 0] - al) * vl2 / vl + al2;
		lpImageLab3[i * 3 + 1] = (lpImageLab[i * 3 + 1] - aa) * va2 / va + aa2;
		lpImageLab3[i * 3 + 2] = (lpImageLab[i * 3 + 2] - ab) * vb2 / vb + ab2;
	}

	//将结果图像的lab转换为RGB
	for (j = 0; j < lmageHeight; j++)
	{
		for (i = 0; i < lmageWidth; i++)
		{
			nindex = ((lmageHeight - j - 1)*lmageWidth + i);

			LabToRgb(lpImageLab3[nindex * 3 + 0], lpImageLab3[nindex * 3 + 1], lpImageLab3[nindex * 3 + 2],
				lpDIBBits3[nindex * 3 + 2], lpDIBBits3[nindex * 3 + 1], lpDIBBits3[nindex * 3 + 0]);
		}
	}
	return true;
}


void AverageVariance(double* lpLab, int Width, int Height, double& al, double& aa, double& ab, double& vl, double& va, double& vb)
{
	double suml = 0;
	double suma = 0;
	double sumb = 0;
	double lsuml = 0;
	double lsuma = 0;
	double lsumb = 0;

	//分行求平均,避免和过大而溢出
	for (int j = 0; j < Height; j++)
	{
		for (int i = 0; i < Width; i++)
		{
			lsuml += lpLab[(j*Width + i) * 3];
			lsuma += lpLab[(j*Width + i) * 3 + 1];
			lsumb += lpLab[(j*Width + i) * 3 + 2];
		}
		suml += lsuml / Width;
		suma += lsuma / Width;
		sumb += lsumb / Width;
		lsuml = lsuma = lsumb = 0;
	}
	al = suml / Height;
	aa = suma / Height;
	ab = sumb / Height;

	suml = suma = sumb = 0;
	for (int i = 0; i < Width*Height; i++)
	{
		suml += pow(lpLab[i * 3] - al, 2);
		suma += pow(lpLab[i * 3 + 1] - aa, 2);
		sumb += pow(lpLab[i * 3 + 2] - ab, 2);
	}
	vl = sqrt(suml);
	va = sqrt(suma);
	vb = sqrt(sumb);
}


void RgbToLab(uchar R, uchar G, uchar B, double& l, double& a, double& b)
{
	double L = 0.3811*R + 0.5783*G + 0.0402*B;
	double M = 0.1967*R + 0.7244*G + 0.0782*B;
	double S = 0.0241*R + 0.1288*G + 0.8444*B;

	//若RGB值均为0,则LMS为0,防止数学错误log0
	if (L != 0) L = log(L) / log(10);
	if (M != 0) M = log(M) / log(10);
	if (S != 0) S = log(S) / log(10);

	l = (L + M + S) / sqrt(3);
	a = (L + M - 2 * S) / sqrt(6);
	b = (L - M) / sqrt(2);

}

void LabToRgb(double l, double a, double b, uchar& R, uchar& G, uchar& B)
{
	l /= sqrt(3);
	a /= sqrt(6);
	b /= sqrt(2);
	double L = l + a + b;
	double M = l + a - b;
	double S = l - 2 * a;

	L = pow(10, L);
	M = pow(10, M);
	S = pow(10, S);

	double dR = 4.4679*L - 3.5873*M + 0.1193*S;
	double dG = -1.2186*L + 2.3809*M - 0.1624*S;
	double dB = 0.0497*L - 0.2439*M + 1.2045*S;

	//防止溢出,若求得RGB值大于255则置为255,若小于0则置为0
	if (dR > 255) R = 255;
	else if (dR < 0) R = 0;
	else R = uchar(dR);

	if (dG > 255) G = 255;
	else if (dG < 0) G = 0;
	else G = uchar(dG);

	if (dB > 255) B = 255;
	else if (dB < 0) B = 0;
	else B = uchar(dB);
}


void mat2Sample(Mat& src, uchar* sample, int nWidth, int nHeight)
{
	//uchar* temp;
	for (int i = 0; i < nHeight; ++i)
	{
		//temp = src.ptr<uchar>(i);
		for (int j = 0; j < nWidth; ++j)
		{
			/*sample[i*nWidth + j * 3] = temp[j * 3];
			sample[i*nWidth + j * 3 + 1] = temp[j * 3 + 1];
			sample[i*nWidth + j * 3 + 2] = temp[j * 3 + 2];*/
			sample[i*nWidth + j * 3] = src.at<Vec3b>(i, j)[0];
			sample[i*nWidth + j * 3 + 1] = src.at<Vec3b>(i, j)[1];
			sample[i*nWidth + j * 3 + 2] = src.at<Vec3b>(i, j)[2];
		}
	}
}

void sample2Mat(Mat&src, int nWidth, int nHeight, uchar* sample)
{
	//uchar* temp;
	for (int i = 0; i < nHeight; ++i)
	{
		//temp = src.ptr<uchar>(i);
		for (int j = 0; j < nWidth; ++j)
		{
			src.at<Vec3b>(i, j)[0] = sample[i*nWidth + j * 3];
			src.at<Vec3b>(i, j)[1] = sample[i*nWidth + j * 3 + 1];
			src.at<Vec3b>(i, j)[2] = sample[i*nWidth + j * 3 + 2];

			/*temp[j * 3] = sample[i*nWidth + j * 3];
			temp[j * 3 + 1] = sample[i*nWidth + j * 3 + 1];
			temp[j * 3 + 2] = sample[i*nWidth + j * 3 + 2];*/
		}
	}

}

 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

ReinHard颜色迁移 的相关文章

随机推荐

  • 如何解决vcruntime140.dll找不到的问题?两种方法教你解决

    当你在运行某些应用程序或游戏时 可能会遇到一个错误提示 即 找不到vcruntime140 dll 文件 这是因为你的电脑中缺少了这个动态链接库文件 这个问题可能会导致你无法正常使用某些应用程序 在本文中 我们将介绍两种方法来解决 目录 一
  • 树莓派安装Ubuntu22.04后使用X86_Linux交叉编译Qt5+opencv4

    文章目录 准备工作 环境搭建 准备编译 未完待续 准备工作 树莓派安装Ubuntu 直接从官网下载对应的镜像烧写工具下载地址 工具里面准备好了对应的镜像地址 直接烧写入SD卡就行了 进入系统 ubuntu server22 04默认密码应该
  • k8s 使用GlusterFS做持久化存储

    一 创建GlusterFS 首先找几台主机做GlusterFS存储 这里用了3台主机 10 244 0 10 10 244 0 11 10 244 0 12 安装GlusterFS 安装过程如下 安装 gluster 源 yum insta
  • 【数据结构】图解八大排序(上)

    文章目录 一 排序简介 二 直接插入排序 三 希尔排序 四 直接选择排序 五 堆排序 六 冒泡排序 七 冒泡排序与直接插入排序效率对比 一 排序简介 生活中 我们经常能看到排序的应用 例如 我们在网购商品的时候 经常按销量从高到低排序 常见
  • C语言经典100例题(38)--求一个3 * 3矩阵对角线元素之和

    目录 题目 问题分析 代码 测试结果 题目 求一个3 3矩阵对角线元素之和 问题分析 利用双重for循环控制输入二维数组 再将 a i i 累加后输出 代码 include
  • SpringBoot2.x使用缓存注解操作Redis

    为了进一步简化 Redis 的使用 Spring还提供了缓存注解 使用这些注解可以有效简化编程过程 缓存管理器和缓存的启用 Spring 在使用缓存注解前 需要配置缓存管理器 缓存管理器将提供一些重要的信息 如缓存类型 超时时间等 Spri
  • 低代码开发工具到底是给“谁”用的?

    不同的工具 受众也不一样 你不要认为 低代码开发工具 只有一种 实际上它分 3 种 第一种 企业级低代码开发平台 这种通常是给专业开发人员使用的 但也没有限制得很死 只要你懂编程逻辑 能写sql语句 就基本会用 就连专业的产品经理也可以用来
  • Vue实现多文件上传功能(前端 + 后端代码)

    开发项目的时候 用到文件上传的功能很常见 包括单文件上传和多文件上传 上传各种类型的文件 在vue里面要实现多文件上传功能 还是很方便的 本文就一起来学习一下 如何把多文件上传功能封装成一个组件 后面需要使用的时候 直接两三行代码就能搞定
  • 已解决(Python爬虫requests报错)requests.exceptions.ProxyError: HTTPSConnectionPool

    成功解决 Python爬虫requests报错 requests exceptions ProxyError HTTPSConnectionPool 文章目录 报错信息 报错翻译 报错原因 解决方法 千人全栈VIP答疑群联系博主帮忙解决报错
  • Unix域编程流程简单梳理

    文章目录 Unix域编程作用 Unix域编程流程 Unix域编程的地址格式 Unix编程注意事项 Unix编程简单示例 客户端实例 服务端实例 Unix域编程作用 Unix域编程用于同一台主机内部的进程之间的客户端 服务端通信 使用和网络s
  • 什么是LTS、Alpha、Beta、Dev、Release、Patch版本,软件的开发周期有多少种命名

    根据Wikipedia 2023 Software release life cycle显示 软件的开发周期版本命名有以下几种 Pre alpha Dev Alpha Beta Perpetual beta Open and closed
  • Boyer-Moore 投票算法(摩尔投票法)

    摩尔投票法简单来说就是在不影响或者增大众数在整个数组中的地位的情况下去消除无关数字带来的影响 只需遍历一遍数组即可找到众数 算法流程 先随机假设一个数x为候选数 可以假设数组的第一个数 并尝试维护一个count计数器 开始设置为0 设置了众
  • GPT系列训练与部署——Colossal-AI环境配置与测试验证

    Colossal AI框架主要特色在于对模型进行并行训练与推理 多GPU 从而提升模型训练效率 可快速实现分布式训练与推理 目前 该框架已集成很多计算机视觉 CV 和自然语言处理 NLP 方向的算法模型 特别是包括GPT和Stable Di
  • Hbuilder We're sorry

    当Webview窗口加载错误地址 如本地页面不存在 或者访问网络资源失败 如无法访问网络 时会自动显示默认错误页面 可以通过以下方法自定义Webview的404等错误页面 设置应用全局默认错误页面 5 App和wap2app 在应用的man
  • OSPF从初学到放弃 2.1

    本来要开开心心写博客的 结果刚刚看见之间知乎上的两个评论被要求修改 两篇评论都是关于同一个问题的 我都有理由怀疑是资本的力量太强大了 说好的舆论自由呢 一群骗子 好气好气好气 OSPF协议及作业 前言 一 OSPF是什么 仔细说一下 二 聊
  • 一小时入门Python爬虫,连我都会了!Python爬取租房数据实例

    一 什么叫爬虫 爬虫 又名 网络爬虫 就是能够自动访问互联网并将网站内容下载下来的程序 它也是搜索引擎的基础 像百度和GOOGLE都是凭借强大的网络爬虫 来检索海量的互联网信息的然后存储到云端 为网友提供优质的搜索服务的 二 爬虫有什么用
  • 【算法系列篇】二分查找——这还是你所知道的二分查找算法吗?

    文章目录 前言 什么是二分查找算法 1 二分查找 1 1 题目要求 1 2 做题思路 1 3 Java代码实现 2 在排序数组中查找元素的第一个和最后一个位置 2 1 题目要求 2 2 做题思路 2 3 Java代码实现 3 搜索插入位置
  • USB MSC类存储设备及FatfsR0.14移植

    USB MSC类存储设备及FatfsR0 14移植 效果演示 配置说明 USB MSC类配置 FatFs移植 FatFs的配置 驱动接口 FatFs测试 USB MSC驱动接口完善 效果演示 这里演示了 FatFs挂载SPI Flash设备
  • java中的锁(基础篇)

    乐观锁和悲观锁 悲观锁 适合写操作多的场景 先加锁可以保证写操作时的数据正确 乐观锁 实际开发 Synchronized关键字 悲观锁 加了后同一时间有且只有一个线程可以进入锁内 1 修饰实例方法 作用于当前实例对象加锁 进入同步代码前要获
  • ReinHard颜色迁移

    看到颜色迁移 觉得还蛮有意思的 遂简单看了一下 代码实现好像有错误 但是不知道错误出在哪里 算法参考 https blog csdn net sin geek article details 22443537 https blog csdn