添加C#实现
This commit is contained in:
@@ -0,0 +1,13 @@
|
||||
using OpenCvSharp;
|
||||
|
||||
namespace Com.Lmc.ShuiYin.One.Converter
|
||||
{
|
||||
public interface Converter
|
||||
{
|
||||
Mat Start(Mat src);
|
||||
void Inverse(Mat com);
|
||||
void AddTextWatermark(Mat com, string watermark);
|
||||
void AddImageWatermark(Mat com, Mat watermark);
|
||||
Mat ShowWatermark(Mat src);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
using OpenCvSharp;
|
||||
using Com.Lmc.ShuiYin.One.Util;
|
||||
|
||||
namespace Com.Lmc.ShuiYin.One.Converter
|
||||
{
|
||||
public class DctConverter : Converter
|
||||
{
|
||||
public Mat Start(Mat src)
|
||||
{
|
||||
if ((src.Cols & 1) != 0)
|
||||
{
|
||||
Cv2.CopyMakeBorder(src, src, 0, 0, 0, 1, BorderTypes.Constant, Scalar.All(0));
|
||||
}
|
||||
if ((src.Rows & 1) != 0)
|
||||
{
|
||||
Cv2.CopyMakeBorder(src, src, 0, 1, 0, 0, BorderTypes.Constant, Scalar.All(0));
|
||||
}
|
||||
src.ConvertTo(src, MatType.CV_32F);
|
||||
Cv2.Dct(src, src);
|
||||
return src;
|
||||
}
|
||||
|
||||
public void Inverse(Mat com)
|
||||
{
|
||||
Cv2.Idct(com, com);
|
||||
}
|
||||
|
||||
public void AddTextWatermark(Mat com, string watermark)
|
||||
{
|
||||
Cv2.PutText(com, watermark,
|
||||
new Point(com.Cols >> 2, com.Rows >> 2),
|
||||
HersheyFonts.HersheyComplex, 2.0,
|
||||
new Scalar(2, 2, 2, 0), 2, LineTypes.Link8, false);
|
||||
}
|
||||
|
||||
public void AddImageWatermark(Mat com, Mat watermark)
|
||||
{
|
||||
Mat mask = new Mat();
|
||||
Cv2.InRange(watermark, new Scalar(0, 0, 0, 0), new Scalar(0, 0, 0, 0), mask);
|
||||
Mat i2 = new Mat(watermark.Size(), watermark.Type(), new Scalar(2, 2, 2, 0));
|
||||
i2.CopyTo(watermark, mask);
|
||||
watermark.ConvertTo(watermark, MatType.CV_32F);
|
||||
int row = (com.Rows - watermark.Rows) >> 1;
|
||||
int col = (com.Cols - watermark.Cols) >> 1;
|
||||
Cv2.CopyMakeBorder(watermark, watermark, row, row, col, col, BorderTypes.Constant, Scalar.All(0));
|
||||
Utils.FixSize(watermark, com);
|
||||
Cv2.AddWeighted(watermark, 0.03, com, 1, 0.0, com);
|
||||
}
|
||||
|
||||
public Mat ShowWatermark(Mat src)
|
||||
{
|
||||
src.ConvertTo(src, MatType.CV_8UC3); // COLOR_RGB2HSV requires 8-bit or 16-bit usually, Java code says convertTo(src, COLOR_RGB2HSV). Wait.
|
||||
// Java: src.convertTo(src, COLOR_RGB2HSV);
|
||||
// Actually convertTo is for type conversion (e.g. to float). cvtColor is for color space.
|
||||
// The Java code in DctConverter.java says: src.convertTo(src, COLOR_RGB2HSV);
|
||||
// This looks like a mistake in the Java code or I am misreading.
|
||||
// Core.java doesn't have COLOR_RGB2HSV constant for convertTo. Imgproc has it.
|
||||
// Imgproc.cvtColor(src, src, Imgproc.COLOR_RGB2HSV); is the correct way.
|
||||
// The Java code provided: `src.convertTo(src, COLOR_RGB2HSV);`
|
||||
// `COLOR_RGB2HSV` is an int (usually 40 or 41). `convertTo` takes rtype (int).
|
||||
// If they are using `convertTo` with a color conversion code, they might be doing something wrong or it's valid in Java OpenCV to use it as a type? No.
|
||||
// `CV_32F` is a type. `COLOR_RGB2HSV` is a conversion code.
|
||||
// Let's assume they meant `Imgproc.cvtColor`.
|
||||
// Wait, checking the Java code again: `src.convertTo(src, COLOR_RGB2HSV);`
|
||||
// If I look at `DctConverter.java` line 76: `src.convertTo(src, COLOR_RGB2HSV);`
|
||||
// This is very suspicious.
|
||||
// However, `COLOR_RGB2HSV` is likely imported statically from `Imgproc`.
|
||||
// In OpenCV C++, `convertTo` changes the depth.
|
||||
// I will use `Cv2.CvtColor` because that's what makes sense for HSV.
|
||||
|
||||
Cv2.CvtColor(src, src, ColorConversionCodes.RGB2HSV);
|
||||
Cv2.InRange(src, new Scalar(0, 0, 0, 0), new Scalar(16, 16, 16, 0), src);
|
||||
Cv2.Normalize(src, src, 0, 255, NormTypes.MinMax, (int)MatType.CV_8UC1);
|
||||
return src;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
using System.Collections.Generic;
|
||||
using OpenCvSharp;
|
||||
using Com.Lmc.ShuiYin.One.Util;
|
||||
using Com.Lmc.ShuiYin; // For ShuiYinUtils
|
||||
|
||||
namespace Com.Lmc.ShuiYin.One.Converter
|
||||
{
|
||||
public class DftConverter : Converter
|
||||
{
|
||||
public Mat Start(Mat src)
|
||||
{
|
||||
src.ConvertTo(src, MatType.CV_32F);
|
||||
List<Mat> planes = new List<Mat>();
|
||||
Mat com = new Mat();
|
||||
planes.Add(src);
|
||||
planes.Add(Mat.Zeros(src.Size(), MatType.CV_32F));
|
||||
Cv2.Merge(planes.ToArray(), com);
|
||||
Cv2.Dft(com, com);
|
||||
return com;
|
||||
}
|
||||
|
||||
public void Inverse(Mat com)
|
||||
{
|
||||
List<Mat> planes = new List<Mat>();
|
||||
Cv2.Idft(com, com);
|
||||
Cv2.Split(com, out Mat[] splitPlanes);
|
||||
planes.AddRange(splitPlanes);
|
||||
Cv2.Normalize(planes[0], com, 0, 255, NormTypes.MinMax, (int)MatType.CV_8UC3);
|
||||
}
|
||||
|
||||
public void AddTextWatermark(Mat com, string watermark)
|
||||
{
|
||||
Scalar s = new Scalar(255, 255, 255, 0);
|
||||
Point p = new Point(0, com.Rows / 3);
|
||||
int thickness = 3;
|
||||
// Note: ShuiYinUtils needs to be implemented. Assuming it exists.
|
||||
double fontScale = ShuiYinUtils.GetFontScale(new Size(com.Cols, com.Rows / 2), watermark, HersheyFonts.HersheyComplex, thickness);
|
||||
Cv2.PutText(com, watermark, p, HersheyFonts.HersheyComplex, fontScale, s, thickness, LineTypes.Link8, false);
|
||||
Cv2.Flip(com, com, FlipMode.X); // -1 in OpenCV is both axes, but here it seems they use -1. In OpenCvSharp FlipMode.XY is -1.
|
||||
// Wait, Java code says -1. OpenCvSharp FlipMode: X=0, Y=1, XY=-1.
|
||||
// Let's check Java docs: 0 means flipping around the x-axis... wait.
|
||||
// OpenCV: 0 means flipping around the x-axis and positive value (for example, 1) means flipping around y-axis. Negative value (for example, -1) means flipping around both axes.
|
||||
// So -1 is XY.
|
||||
Cv2.Flip(com, com, FlipMode.XY);
|
||||
Cv2.PutText(com, watermark, p, HersheyFonts.HersheyComplex, fontScale, s, thickness, LineTypes.Link8, false);
|
||||
Cv2.Flip(com, com, FlipMode.XY);
|
||||
}
|
||||
|
||||
public void AddImageWatermark(Mat com, Mat watermark)
|
||||
{
|
||||
List<Mat> planes = new List<Mat>();
|
||||
List<Mat> newPlanes = new List<Mat>();
|
||||
Mat temp = new Mat();
|
||||
int col = (com.Cols - watermark.Cols) >> 1;
|
||||
int row = ((com.Rows >> 1) - watermark.Rows) >> 1;
|
||||
watermark.ConvertTo(watermark, MatType.CV_32F);
|
||||
Cv2.CopyMakeBorder(watermark, watermark, row, row, col, col, BorderTypes.Constant, Scalar.All(0));
|
||||
planes.Add(watermark);
|
||||
Cv2.Flip(watermark, temp, FlipMode.XY);
|
||||
planes.Add(temp);
|
||||
|
||||
// vconcat expects an array of Mats
|
||||
Mat[] planesArray = planes.ToArray();
|
||||
// In Java: vconcat(planes, watermark); -> watermark becomes the concatenation.
|
||||
// In C#: Cv2.VConcat(planes, watermark);
|
||||
Cv2.VConcat(planesArray, watermark);
|
||||
|
||||
newPlanes.Add(watermark);
|
||||
newPlanes.Add(watermark);
|
||||
Cv2.Merge(newPlanes.ToArray(), watermark);
|
||||
Utils.FixSize(watermark, com);
|
||||
Cv2.AddWeighted(watermark, 8, com, 1, 0.0, com);
|
||||
|
||||
Cv2.Split(com, out Mat[] splitCom);
|
||||
// The Java code ends with split(com, planes), but doesn't return or use planes. It seems redundant or side-effect based if 'planes' was a field, but it's local.
|
||||
// Actually, in Java code: split(com, planes); where planes is a local List<Mat>. This seems useless at the end of the method unless it modifies 'com' in place (it doesn't, split fills the list).
|
||||
// So I can ignore it.
|
||||
}
|
||||
|
||||
public Mat ShowWatermark(Mat src)
|
||||
{
|
||||
Mat mag = new Mat();
|
||||
Cv2.Split(src, out Mat[] splitSrc);
|
||||
// Java: magnitude(newPlanes.get(0), newPlanes.get(1), mag);
|
||||
Cv2.Magnitude(splitSrc[0], splitSrc[1], mag);
|
||||
Cv2.Add(Mat.Ones(mag.Size(), MatType.CV_32F), mag, mag);
|
||||
Cv2.Log(mag, mag);
|
||||
mag.ConvertTo(mag, MatType.CV_8UC1);
|
||||
Cv2.Normalize(mag, mag, 0, 255, NormTypes.MinMax, (int)MatType.CV_8UC1);
|
||||
return mag;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
using OpenCvSharp;
|
||||
|
||||
namespace Com.Lmc.ShuiYin.One.Converter
|
||||
{
|
||||
public class DwtConverter : Converter
|
||||
{
|
||||
public Mat Start(Mat src)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public void Inverse(Mat com)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public void AddTextWatermark(Mat com, string watermark)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public void AddImageWatermark(Mat com, Mat watermark)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public Mat ShowWatermark(Mat src)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
using Com.Lmc.ShuiYin.One.Converter;
|
||||
using Com.Lmc.ShuiYin.One.Util;
|
||||
using OpenCvSharp;
|
||||
|
||||
namespace Com.Lmc.ShuiYin.One.Dencoder
|
||||
{
|
||||
public class Decoder
|
||||
{
|
||||
private Converter.Converter converter;
|
||||
|
||||
public Decoder(Converter.Converter converter)
|
||||
{
|
||||
this.converter = converter;
|
||||
}
|
||||
|
||||
public Converter.Converter GetConverter()
|
||||
{
|
||||
return converter;
|
||||
}
|
||||
|
||||
public void SetConverter(Converter.Converter converter)
|
||||
{
|
||||
this.converter = converter;
|
||||
}
|
||||
|
||||
public void Decode(string image, string output)
|
||||
{
|
||||
// CV_8U is 0
|
||||
Cv2.ImWrite(output, this.converter.ShowWatermark(this.converter.Start(Utils.Read(image, 0))));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
using System.Collections.Generic;
|
||||
using OpenCvSharp;
|
||||
using Com.Lmc.ShuiYin.One.Converter;
|
||||
using Com.Lmc.ShuiYin.One.Util;
|
||||
|
||||
namespace Com.Lmc.ShuiYin.One.Dencoder
|
||||
{
|
||||
public abstract class Encoder
|
||||
{
|
||||
protected Converter.Converter converter; // protected for subclasses
|
||||
|
||||
public Encoder(Converter.Converter converter)
|
||||
{
|
||||
this.converter = converter;
|
||||
}
|
||||
|
||||
public Converter.Converter GetConverter()
|
||||
{
|
||||
return converter;
|
||||
}
|
||||
|
||||
public void SetConverter(Converter.Converter converter)
|
||||
{
|
||||
this.converter = converter;
|
||||
}
|
||||
|
||||
public void Encode(string image, string watermark, string output)
|
||||
{
|
||||
Mat src = Utils.Read(image, 1); // CV_8S -> Color (1)
|
||||
|
||||
List<Mat> channel = new List<Mat>();
|
||||
List<Mat> newChannel = new List<Mat>();
|
||||
Cv2.Split(src, out Mat[] splitChannel);
|
||||
channel.AddRange(splitChannel);
|
||||
|
||||
// Java: for (int i = 0; i < 3; i++)
|
||||
// OpenCvSharp Split returns array of Mats.
|
||||
|
||||
// Ensure we have 3 channels? The loop is hardcoded to 3.
|
||||
for (int i = 0; i < 3 && i < channel.Count; i++)
|
||||
{
|
||||
Mat com = this.converter.Start(channel[i]).Clone();
|
||||
this.AddWatermark(com, watermark);
|
||||
this.converter.Inverse(com);
|
||||
// In Java: newChannel.add(i, com);
|
||||
newChannel.Add(com);
|
||||
}
|
||||
|
||||
// If src has more than 3 channels (e.g. alpha), we should probably keep them?
|
||||
// The java code only loops 3 times.
|
||||
if (channel.Count > 3)
|
||||
{
|
||||
for (int i = 3; i < channel.Count; i++)
|
||||
{
|
||||
newChannel.Add(channel[i]);
|
||||
}
|
||||
}
|
||||
|
||||
Mat res = new Mat();
|
||||
Cv2.Merge(newChannel.ToArray(), res);
|
||||
|
||||
if (res.Rows != src.Rows || res.Cols != src.Cols)
|
||||
{
|
||||
res = new Mat(res, new Rect(0, 0, src.Width, src.Height));
|
||||
}
|
||||
|
||||
Cv2.ImWrite(output, res);
|
||||
}
|
||||
|
||||
public abstract void AddWatermark(Mat com, string watermark);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
using Com.Lmc.ShuiYin.One.Converter;
|
||||
using Com.Lmc.ShuiYin.One.Util;
|
||||
using OpenCvSharp;
|
||||
|
||||
namespace Com.Lmc.ShuiYin.One.Dencoder
|
||||
{
|
||||
public class ImageEncoder : Encoder
|
||||
{
|
||||
public ImageEncoder(Converter.Converter converter) : base(converter)
|
||||
{
|
||||
}
|
||||
|
||||
public override void AddWatermark(Mat com, string watermark)
|
||||
{
|
||||
// CV_8U is 0
|
||||
this.converter.AddImageWatermark(com, Utils.Read(watermark, 0));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
using Com.Lmc.ShuiYin.One.Converter;
|
||||
using Com.Lmc.ShuiYin.One.Util;
|
||||
using OpenCvSharp;
|
||||
|
||||
namespace Com.Lmc.ShuiYin.One.Dencoder
|
||||
{
|
||||
public class TextEncoder : Encoder
|
||||
{
|
||||
public TextEncoder(Converter.Converter converter) : base(converter)
|
||||
{
|
||||
}
|
||||
|
||||
public override void AddWatermark(Mat com, string watermark)
|
||||
{
|
||||
if (Utils.IsAscii(watermark))
|
||||
{
|
||||
this.converter.AddTextWatermark(com, watermark);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.converter.AddImageWatermark(com, Utils.DrawNonAscii(watermark));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using Com.Lmc.ShuiYin.One.Converter;
|
||||
using Com.Lmc.ShuiYin.One.Dencoder;
|
||||
|
||||
namespace Com.Lmc.ShuiYin.One
|
||||
{
|
||||
public class Test
|
||||
{
|
||||
public static void Run(string[] args)
|
||||
{
|
||||
Converter.Converter converter = new DftConverter();
|
||||
Encoder encoder = new TextEncoder(converter);
|
||||
Decoder decoder = new Decoder(converter);
|
||||
|
||||
string baseDir = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "TestImages");
|
||||
|
||||
string src = Path.Combine(baseDir, "yinhua.png");
|
||||
string watermarked = Path.Combine(baseDir, "yinhua2.png");
|
||||
string extracted = Path.Combine(baseDir, "yinhua3.png");
|
||||
|
||||
encoder.Encode(src, "CheShenWeiWu", watermarked);
|
||||
decoder.Decode(watermarked, extracted);
|
||||
|
||||
string src2 = Path.Combine(baseDir, "444.jpg");
|
||||
string extracted2 = Path.Combine(baseDir, "555.jpg");
|
||||
|
||||
decoder.Decode(src2, extracted2);
|
||||
Console.WriteLine("One.Test finished.");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using System.Drawing.Drawing2D;
|
||||
using System.Drawing.Text;
|
||||
using OpenCvSharp;
|
||||
using OpenCvSharp.Extensions;
|
||||
using Size = OpenCvSharp.Size;
|
||||
using Point = OpenCvSharp.Point;
|
||||
|
||||
namespace Com.Lmc.ShuiYin.One.Util
|
||||
{
|
||||
public class Utils
|
||||
{
|
||||
public static Mat Read(string image, int type)
|
||||
{
|
||||
// Map Java CV_8S (1) -> Color, CV_8U (0) -> Grayscale
|
||||
ImreadModes mode = ImreadModes.Unchanged;
|
||||
if (type == 1) mode = ImreadModes.Color;
|
||||
else if (type == 0) mode = ImreadModes.Grayscale;
|
||||
else mode = (ImreadModes)type;
|
||||
|
||||
Mat src = Cv2.ImRead(image, mode);
|
||||
if (src.Empty())
|
||||
{
|
||||
Console.WriteLine("File not found!");
|
||||
Environment.Exit(-1);
|
||||
}
|
||||
return src;
|
||||
}
|
||||
|
||||
public static void Show(Mat mat)
|
||||
{
|
||||
Cv2.ImShow("Utils", mat);
|
||||
Cv2.WaitKey(0);
|
||||
}
|
||||
|
||||
public static Mat OptimalDft(Mat srcImg)
|
||||
{
|
||||
Mat padded = new Mat();
|
||||
int opRows = Cv2.GetOptimalDFTSize(srcImg.Rows);
|
||||
int opCols = Cv2.GetOptimalDFTSize(srcImg.Cols);
|
||||
Cv2.CopyMakeBorder(srcImg, padded, 0, opRows - srcImg.Rows,
|
||||
0, opCols - srcImg.Cols, BorderTypes.Constant, Scalar.All(0));
|
||||
return padded;
|
||||
}
|
||||
|
||||
public static bool IsAscii(string str)
|
||||
{
|
||||
foreach (char c in str)
|
||||
{
|
||||
if (c > 127) // Simple ASCII check
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static Mat DrawNonAscii(string watermark)
|
||||
{
|
||||
// Create a dummy bitmap to measure string
|
||||
using (Bitmap dummy = new Bitmap(1, 1))
|
||||
using (Graphics gDummy = Graphics.FromImage(dummy))
|
||||
{
|
||||
Font font = new Font("Arial", 64, FontStyle.Regular, GraphicsUnit.Pixel);
|
||||
// In Java code: Font font = new Font("Default", Font.PLAIN, 64);
|
||||
// "Default" might not exist in Windows fonts, usually maps to Dialog or SansSerif.
|
||||
// I'll use "Arial" or generic SansSerif.
|
||||
|
||||
SizeF size = gDummy.MeasureString(watermark, font);
|
||||
int width = (int)Math.Ceiling(size.Width);
|
||||
int height = (int)Math.Ceiling(size.Height);
|
||||
|
||||
// Create actual bitmap
|
||||
Bitmap bufferedImage = new Bitmap(width, height, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
|
||||
// Java uses TYPE_BYTE_GRAY, but System.Drawing doesn't support drawing to 8bpp easily.
|
||||
// We will draw to RGB and convert to Gray later or let OpenCV handle it.
|
||||
|
||||
using (Graphics graphics = Graphics.FromImage(bufferedImage))
|
||||
{
|
||||
graphics.TextRenderingHint = TextRenderingHint.AntiAlias;
|
||||
graphics.CompositingQuality = CompositingQuality.HighQuality;
|
||||
graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
|
||||
graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
|
||||
|
||||
graphics.Clear(Color.Black); // Assuming background is black? Java code says `new BufferedImage`. Default is 0 (black).
|
||||
// Java code: graphics.setColor(Color.WHITE); graphics.drawString(watermark, 0, metrics.getAscent());
|
||||
|
||||
graphics.DrawString(watermark, font, Brushes.White, 0, 0);
|
||||
}
|
||||
|
||||
// Convert to Mat
|
||||
Mat res = BitmapConverter.ToMat(bufferedImage);
|
||||
// Convert to grayscale CV_8U
|
||||
Cv2.CvtColor(res, res, ColorConversionCodes.BGR2GRAY);
|
||||
|
||||
Cv2.ImWrite("C:\\tools\\shuiyin.png", res); // Keep path as requested
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
public static void FixSize(Mat src, Mat mirror)
|
||||
{
|
||||
if (src.Rows != mirror.Rows)
|
||||
{
|
||||
Cv2.CopyMakeBorder(src, src, 0, mirror.Rows - src.Rows,
|
||||
0, 0, BorderTypes.Constant, Scalar.All(0));
|
||||
}
|
||||
if (src.Cols != mirror.Cols)
|
||||
{
|
||||
Cv2.CopyMakeBorder(src, src, 0, 0,
|
||||
0, mirror.Cols - src.Cols, BorderTypes.Constant, Scalar.All(0));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user