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空间
*/
下载
源码下载