C# OpenCvSharp 实现Reinhard颜色迁移算法

C# OpenCvSharp 实现Reinhard颜色迁移算法

目录

效果

项目

代码

下载


效果

项目

Reinhard颜色迁移算法的步骤:
1、将参考图片和目标图片转换到LAB空间下
2、得到参考图片和目标图片的均值和标准差
3、对目标图片的每一个像素值,减去目标图像均值然后乘上参考图片和目标图片标准差的比值,再加上参考图像均值
4、将目标图片转换到RGB空间

代码

using OpenCvSharp;
using System;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Imaging;
using System.Windows.Forms;

namespace OpenCvSharp_Demo
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        string fileFilter = "选择图片|*.bmp;*.jpg;*.jpeg;*.tiff;*.tiff;*.png";
        string sc_image_path;
        string dc_image_path;

        Stopwatch stopwatch = new Stopwatch();

        private void Form1_Load(object sender, EventArgs e)
        {
            sc_image_path = "1.jpg";
            dc_image_path = "2.jpg";

            pictureBox1.Image = new Bitmap(sc_image_path);
            pictureBox3.Image = new Bitmap(dc_image_path);
        }

        private void button1_Click(object sender, EventArgs e)
        {
            OpenFileDialog ofd = new OpenFileDialog();
            ofd.Filter = fileFilter;
            if (ofd.ShowDialog() != DialogResult.OK) return;

            pictureBox1.Image = null;
            pictureBox2.Image = null;
            textBox1.Text = "";

            sc_image_path = ofd.FileName;
            pictureBox1.Image = new Bitmap(sc_image_path);
        }

        private void button4_Click(object sender, EventArgs e)
        {
            OpenFileDialog ofd = new OpenFileDialog();
            ofd.Filter = fileFilter;
            if (ofd.ShowDialog() != DialogResult.OK) return;

            pictureBox3.Image = null;
            pictureBox2.Image = null;
            textBox1.Text = "";

            dc_image_path = ofd.FileName;
            pictureBox3.Image = new Bitmap(dc_image_path);
        }

        private void button2_Click(object sender, EventArgs e)
        {

            if (sc_image_path == "")
            {
                return;
            }

            if (dc_image_path == "")
            {
                return;
            }

            stopwatch.Restart();

            Mat sc = Cv2.ImRead(sc_image_path, ImreadModes.Color);
            Cv2.CvtColor(sc, sc, ColorConversionCodes.BGR2Lab);
            Mat sc_m = new Mat();
            Mat sc_sd = new Mat();
            Cv2.MeanStdDev(sc, sc_m, sc_sd);


            Mat dc = Cv2.ImRead(dc_image_path, ImreadModes.Color);
            Cv2.CvtColor(dc, dc, ColorConversionCodes.BGR2Lab);
            Mat dc_m = new Mat();
            Mat dc_sd = new Mat();
            Cv2.MeanStdDev(dc, dc_m, dc_sd);

            var mat3 = new Mat<Vec3b>(sc);
            var indexer = mat3.GetIndexer();

            for (int y = 0; y < sc.Height; y++)
            {
                for (int x = 0; x < sc.Width; x++)
                {
                    Vec3b color = indexer[y, x];

                    byte temp = color.Item0;
                    double dtemp = (temp - sc_m.At<double>(0, 0)) * (dc_sd.At<double>(0, 0) / sc_sd.At<double>(0, 0)) + dc_m.At<double>(0, 0);
                    dtemp = Math.Round(dtemp);
                    if (dtemp < 0)
                    {
                        dtemp = 0;
                    }
                    else if (dtemp > 255)
                    {
                        dtemp = 255;
                    }

                    byte temp1 = color.Item1;
                    double dtemp1 = ((byte)((temp1 - sc_m.At<double>(0, 1)) * (dc_sd.At<double>(0, 1) / sc_sd.At<double>(0, 1)) + dc_m.At<double>(0, 1)));
                    dtemp1 = Math.Round(dtemp1);
                    if (dtemp1 < 0)
                    {
                        dtemp1 = 0;
                    }
                    else if (dtemp1 > 255)
                    {
                        dtemp1 = 255;
                    }

                    byte temp2 = color.Item2;
                    double dtemp2 = ((byte)((temp2 - sc_m.At<double>(0, 2)) * (dc_sd.At<double>(0, 2) / sc_sd.At<double>(0, 2)) + dc_m.At<double>(0, 2)));
                    dtemp2 = Math.Round(dtemp2);
                    if (dtemp2 < 0)
                    {
                        dtemp2 = 0;
                    }
                    else if (dtemp2 > 255)
                    {
                        dtemp2 = 255;
                    }

                    color.Item0 = (byte)dtemp;
                    color.Item1 = (byte)dtemp1;
                    color.Item2 = (byte)dtemp2;

                    indexer[y, x] = color;
                }
            }

            Cv2.CvtColor(sc, sc, ColorConversionCodes.Lab2BGR);

            double costTime = stopwatch.Elapsed.TotalMilliseconds;

            textBox1.Text = $"耗时:{costTime:F2}ms";
            pictureBox2.Image = new Bitmap(sc.ToMemoryStream());

        }

        private void button3_Click(object sender, EventArgs e)
        {
            if (pictureBox2.Image == null)
            {
                return;
            }
            Bitmap output = new Bitmap(pictureBox2.Image);
            var sdf = new SaveFileDialog();
            sdf.Title = "保存";
            sdf.Filter = "Images (*.jpg)|*.jpg|Images (*.png)|*.png|Images (*.bmp)|*.bmp";
            if (sdf.ShowDialog() == DialogResult.OK)
            {
                switch (sdf.FilterIndex)
                {
                    case 1:
                        {
                            output.Save(sdf.FileName, ImageFormat.Jpeg);
                            break;
                        }
                    case 2:
                        {
                            output.Save(sdf.FileName, ImageFormat.Png);
                            break;
                        }
                    case 3:
                        {
                            output.Save(sdf.FileName, ImageFormat.Bmp);
                            break;
                        }
                }
                MessageBox.Show("保存成功,位置:" + sdf.FileName);
            }
        }

    }
}
/*
Reinhard颜色迁移算法的过程
1、将参考图片和目标图片转换到LAB空间下
2、得到参考图片和目标图片的均值和标准差
3、对目标图片的每一个像素值,减去目标图像均值然后乘上参考图片和目标图片标准差的比值,再加上参考图像均值
4、将目标图片转换到RGB空间
 */

using OpenCvSharp;
using System;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Imaging;
using System.Windows.Forms;

namespace OpenCvSharp_Demo
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        string fileFilter = "选择图片|*.bmp;*.jpg;*.jpeg;*.tiff;*.tiff;*.png";
        string sc_image_path;
        string dc_image_path;

        Stopwatch stopwatch = new Stopwatch();

        private void Form1_Load(object sender, EventArgs e)
        {
            sc_image_path = "1.jpg";
            dc_image_path = "2.jpg";

            pictureBox1.Image = new Bitmap(sc_image_path);
            pictureBox3.Image = new Bitmap(dc_image_path);
        }

        private void button1_Click(object sender, EventArgs e)
        {
            OpenFileDialog ofd = new OpenFileDialog();
            ofd.Filter = fileFilter;
            if (ofd.ShowDialog() != DialogResult.OK) return;

            pictureBox1.Image = null;
            pictureBox2.Image = null;
            textBox1.Text = "";

            sc_image_path = ofd.FileName;
            pictureBox1.Image = new Bitmap(sc_image_path);
        }

        private void button4_Click(object sender, EventArgs e)
        {
            OpenFileDialog ofd = new OpenFileDialog();
            ofd.Filter = fileFilter;
            if (ofd.ShowDialog() != DialogResult.OK) return;

            pictureBox3.Image = null;
            pictureBox2.Image = null;
            textBox1.Text = "";

            dc_image_path = ofd.FileName;
            pictureBox3.Image = new Bitmap(dc_image_path);
        }

        private void button2_Click(object sender, EventArgs e)
        {

            if (sc_image_path == "")
            {
                return;
            }

            if (dc_image_path == "")
            {
                return;
            }

            stopwatch.Restart();

            Mat sc = Cv2.ImRead(sc_image_path, ImreadModes.Color);
            Cv2.CvtColor(sc, sc, ColorConversionCodes.BGR2Lab);
            Mat sc_m = new Mat();
            Mat sc_sd = new Mat();
            Cv2.MeanStdDev(sc, sc_m, sc_sd);


            Mat dc = Cv2.ImRead(dc_image_path, ImreadModes.Color);
            Cv2.CvtColor(dc, dc, ColorConversionCodes.BGR2Lab);
            Mat dc_m = new Mat();
            Mat dc_sd = new Mat();
            Cv2.MeanStdDev(dc, dc_m, dc_sd);

            var mat3 = new Mat<Vec3b>(sc);
            var indexer = mat3.GetIndexer();

            for (int y = 0; y < sc.Height; y++)
            {
                for (int x = 0; x < sc.Width; x++)
                {
                    Vec3b color = indexer[y, x];

                    byte temp = color.Item0;
                    double dtemp = (temp - sc_m.At<double>(0, 0)) * (dc_sd.At<double>(0, 0) / sc_sd.At<double>(0, 0)) + dc_m.At<double>(0, 0);
                    dtemp = Math.Round(dtemp);
                    if (dtemp < 0)
                    {
                        dtemp = 0;
                    }
                    else if (dtemp > 255)
                    {
                        dtemp = 255;
                    }

                    byte temp1 = color.Item1;
                    double dtemp1 = ((byte)((temp1 - sc_m.At<double>(0, 1)) * (dc_sd.At<double>(0, 1) / sc_sd.At<double>(0, 1)) + dc_m.At<double>(0, 1)));
                    dtemp1 = Math.Round(dtemp1);
                    if (dtemp1 < 0)
                    {
                        dtemp1 = 0;
                    }
                    else if (dtemp1 > 255)
                    {
                        dtemp1 = 255;
                    }

                    byte temp2 = color.Item2;
                    double dtemp2 = ((byte)((temp2 - sc_m.At<double>(0, 2)) * (dc_sd.At<double>(0, 2) / sc_sd.At<double>(0, 2)) + dc_m.At<double>(0, 2)));
                    dtemp2 = Math.Round(dtemp2);
                    if (dtemp2 < 0)
                    {
                        dtemp2 = 0;
                    }
                    else if (dtemp2 > 255)
                    {
                        dtemp2 = 255;
                    }

                    color.Item0 = (byte)dtemp;
                    color.Item1 = (byte)dtemp1;
                    color.Item2 = (byte)dtemp2;

                    indexer[y, x] = color;
                }
            }

            Cv2.CvtColor(sc, sc, ColorConversionCodes.Lab2BGR);

            double costTime = stopwatch.Elapsed.TotalMilliseconds;

            textBox1.Text = $"耗时:{costTime:F2}ms";
            pictureBox2.Image = new Bitmap(sc.ToMemoryStream());

        }

        private void button3_Click(object sender, EventArgs e)
        {
            if (pictureBox2.Image == null)
            {
                return;
            }
            Bitmap output = new Bitmap(pictureBox2.Image);
            var sdf = new SaveFileDialog();
            sdf.Title = "保存";
            sdf.Filter = "Images (*.jpg)|*.jpg|Images (*.png)|*.png|Images (*.bmp)|*.bmp";
            if (sdf.ShowDialog() == DialogResult.OK)
            {
                switch (sdf.FilterIndex)
                {
                    case 1:
                        {
                            output.Save(sdf.FileName, ImageFormat.Jpeg);
                            break;
                        }
                    case 2:
                        {
                            output.Save(sdf.FileName, ImageFormat.Png);
                            break;
                        }
                    case 3:
                        {
                            output.Save(sdf.FileName, ImageFormat.Bmp);
                            break;
                        }
                }
                MessageBox.Show("保存成功,位置:" + sdf.FileName);
            }
        }

    }
}
/*
Reinhard颜色迁移算法的过程
1、将参考图片和目标图片转换到LAB空间下
2、得到参考图片和目标图片的均值和标准差
3、对目标图片的每一个像素值,减去目标图像均值然后乘上参考图片和目标图片标准差的比值,再加上参考图像均值
4、将目标图片转换到RGB空间
 */

下载

源码下载

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【数据结构(邓俊辉)学习笔记】二叉搜索树04——AVL树

文章目录 1.重平衡1.1 AVL BBST1.2 平衡因子1.3 适度平衡1.4 接口1.5 失衡 复衡 2. 插入2.1 单旋2.2 双旋2.3 实现 3. 删除3.1 单旋3.2 双旋3.3 实现 4. &#xff08;3 4&#xff09;-重构4.1 "34"重构4.2 "34"实现4.3 rotateAt4.4 综合评价 1.重平衡 1…

自动雪深传感器的类型

TH-XL2随着科技的飞速发展&#xff0c;气象监测技术也在不断进步。在降雪天气频发的冬季&#xff0c;雪深数据对于保障道路交通、农业生产和电力供应等具有至关重要的作用。自动雪深传感器作为气象监测的重要工具&#xff0c;其类型多样、功能各异&#xff0c;为气象数据的准确…

让GNSSRTK不再难【第17讲 RTK定位技术原理-站间单差浮点解--第1部分】

第17讲 RTK定位技术原理-站间单差浮点解 RTK技术其实就是在RTD技术的基础上增加载波观测值的使用。由于伪距的误差在分米量级&#xff0c;即使我们通过站间单差消除掉绝大部分的伪距误差&#xff0c;但受限于伪距的精度&#xff0c;我们也只能达到分米量级的定位水平。 但载波…

.NET 矩阵6月红队工具和资源集合

01外网入口打点 1.1 Sharp4WbemScripting 1.2 ASP4Eval 1.3 Sharp4Web.config 1.4 Sharp4AddScript 02安全防御绕过 2.1 Sharp4DefenderStop 03搭建代理隧道 3.1 Sharp4suo5 04混淆加密防护 4.1 Obfuscar混淆器 4.2 Sharp4BatchGuard 05安全技术文档 5.1 .NET 通过Junction Fol…

对原生textarea加上:当前输入字数/最大输入字数

源码: <!DOCTYPE html> <html lang"en"> <head> <meta charset"UTF-8"> <meta name"viewport" content"widthdevice-width, initial-scale1.0"> <title>Textarea Character Counter with Dragga…

OpenCV 张正友标定法(二)

文章目录 一、简介二、实现代码三、实现效果参考资料一、简介 在之前的博客OpenCV 张氏标定法中,我们没有考虑镜头畸变等因素,因此计算出的内参与外参均是理想情况下的数值,而如果我们考虑到镜头的畸变: 我们就需要考虑使用最小二乘法最小化像素坐标的重投影误差(上述所求…

Python中的数据结构

一.堆 堆的建立可以通过导入heapq库来实现 在Python中建立的是最小堆 即heap[k]<heap[2*k1]and heap[k]<heap[2*k2] 下面是一些 堆使用的方法 heapq.heappush([],加入的元素) heapq.heappop(heap)弹出最小的元素 heapq.nlargest(3,heap)返回最大的三个元素 hea…

无人机赋能自然资源调查

确权 业务挑战 由于测绘人员难以到达现场&#xff0c;确权区域大&#xff0c;传统人工测绘覆盖 不全面&#xff0c;信息不完整 传统测绘成果单一&#xff0c;现场核实难度高&#xff0c;确权采集信息不对称 无人机优势 数据采集效率是人工的10倍以上&#xff0c;可自动将…

【数学建模】 进化计算与群体智能

文章目录 进化计算与群体智能1. 遗传算法理论与实现1.1 遗传算法介绍1.2 遗传算法详细示例流程1) 初始种群2) 适应度评估3) 选择&#xff08;轮盘赌法&#xff09;4) 交叉5) 变异6) 迭代 1.3 遗传算法的实现1.4 scikit-opt 库实现遗传算法1.4.1 求解函数极值代码实现代码说明运…

智能制造 v3.13.14 发布,ERP、MES 更新

智能制造一体化管理系统 [SpringBoot2 - 快速开发平台]&#xff0c;适用于制造业、建筑业、汽车行业、互联网、教育、政府机关等机构的管理。包含文件在线操作、工作日志、多班次考勤、CRM、ERP 进销存、项目管理、EHR、拖拽式生成问卷、日程、笔记、工作计划、行政办公、薪资模…

C++20中的三向比较运算符(three-way comparison operator)

在C20中&#xff0c;引入了一个新的特性&#xff0c;即"三向比较运算符(three-way comparison operator)"&#xff0c;由于其外观&#xff0c;也被称为"宇宙飞船运算符(spaceship operator)"&#xff0c;其符号为<>。目的是简化比较对象的过程。这个…

八爪鱼现金流-032,给用户发邮件提示功能

每个月的 5 号、15 号、25 号的 17:30 工资日&#xff0c;给用户发送邮件&#xff0c;提示记账月报。 您也来记账一笔吧。 然后首页能看到趋势图。 八爪鱼现金流 八爪鱼

使用Petalinux设计linux系统

文章目录 1.通过 Vivado 创建硬件平台&#xff0c;得到 hdf 硬件描述文件2.设置 Petalinux 环境变量3.创建 Petalinux 工程4.配置Petalinux 工程5.配置Linux内核6.配置Linux根文件系统7.配置设备树文件8.编译 Petalinux 工程9.制作BOOT.BIN启动文件10.制作SD启动卡 1.通过 Viva…

智能旅行规划的未来:大模型与形式化验证的融合

我们在做旅行规划时面对众多的目的地选择、复杂的交通连接、预算限制以及个人偏好等多重因素&#xff0c;即使是最有经验的旅行者也可能会陷入选择困境。传统的旅行规划方法往往依赖于人工操作&#xff0c;这不仅耗时耗力&#xff0c;而且难以保证计划的最优性和可执行性。 本…

C++学习笔记---POCO库

在Windows系统中安装POCO 1&#xff09;安装OpenSSL POCO编译安装依赖OpenSSL&#xff0c;如果未安装OpenSSL则应该先安装OpenSSL。 假设将OpenSSL安装在C:\OpenSSL-Win64&#xff0c;将C:\OpenSSL-Win64、C:\OpenSSL-Win64\lib添加到PATH环境变量中2&#xff09;安装POCO 将p…

Java代码生成器(开源版本)

一、在线地址 Java在线代码生成器&#xff1a;在线访问 二、页面截图 三、核心功能 支持Mybatis、MybatisPlus、Jpa代码生成使用 antlr4 解析SQL语句&#xff0c;保证了SQL解析的成功率支持自定义包名、作者名信息支持自定义方法名、接口地址支持自定义选择是否生成某个方法…

前端面试题(基础篇十四)

一、DOMContentLoaded 事件和 Load 事件的区别&#xff1f; 当初始的 HTML 文档被完全加载和解析完成之后&#xff0c;DOMContentLoaded 事件被触发&#xff0c;而无需等待样式表、图像和子框架的加载完成。 Load 事件是当所有资源加载完成后触发的。 二、简述一下你对 HTML 语…

解析桥式整流电路

下面这个桥式整流电路出场率很高&#xff0c;看着一定眼熟。 事实证明&#xff0c;强行灌输的东西总是难以下咽。记得读书那会&#xff0c;第一次看到这个电路时被吓到了&#xff0c;以至于直到这门课结束了也没搞清楚。 本文就来分析一下此电路中电流的走向&#xff0c;进而理…

【初阶数据结构】深入解析队列:探索底层逻辑

&#x1f525;引言 本篇将深入解析队列:探索底层逻辑&#xff0c;理解底层是如何实现并了解该接口实现的优缺点&#xff0c;以便于我们在编写程序灵活地使用该数据结构。 &#x1f308;个人主页&#xff1a;是店小二呀 &#x1f308;C语言笔记专栏&#xff1a;C语言笔记 &#…