添加C#实现
This commit is contained in:
@@ -0,0 +1,114 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Drawing.Imaging;
|
||||
using ZXing;
|
||||
using ZXing.Common;
|
||||
using ZXing.QrCode;
|
||||
using ZXing.QrCode.Internal;
|
||||
using ZXing.Windows.Compatibility;
|
||||
|
||||
namespace Com.Lmc.ShuiYin.Two.Utils
|
||||
{
|
||||
public class QRCodeUtils
|
||||
{
|
||||
private int width;
|
||||
private int height;
|
||||
private string format;
|
||||
private Dictionary<EncodeHintType, object> paramMap;
|
||||
|
||||
public QRCodeUtils()
|
||||
{
|
||||
this.width = 100;
|
||||
this.height = 100;
|
||||
this.format = "png";
|
||||
this.paramMap = new Dictionary<EncodeHintType, object>();
|
||||
this.paramMap.Add(EncodeHintType.CHARACTER_SET, "UTF-8");
|
||||
this.paramMap.Add(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.M);
|
||||
this.paramMap.Add(EncodeHintType.MARGIN, 1);
|
||||
}
|
||||
|
||||
public void SetFormat(string format)
|
||||
{
|
||||
this.format = format;
|
||||
}
|
||||
|
||||
public void SetSize(int width, int height)
|
||||
{
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
}
|
||||
|
||||
public void SetParam(EncodeHintType type, object param)
|
||||
{
|
||||
if (paramMap.ContainsKey(type))
|
||||
{
|
||||
paramMap[type] = param;
|
||||
}
|
||||
else
|
||||
{
|
||||
paramMap.Add(type, param);
|
||||
}
|
||||
}
|
||||
|
||||
public void QREncode(string content, string outPutFile)
|
||||
{
|
||||
BarcodeWriter writer = new BarcodeWriter
|
||||
{
|
||||
Format = BarcodeFormat.QR_CODE,
|
||||
Options = new QrCodeEncodingOptions
|
||||
{
|
||||
Width = this.width,
|
||||
Height = this.height,
|
||||
CharacterSet = "UTF-8",
|
||||
ErrorCorrection = (ErrorCorrectionLevel)paramMap[EncodeHintType.ERROR_CORRECTION],
|
||||
Margin = (int)paramMap[EncodeHintType.MARGIN]
|
||||
}
|
||||
};
|
||||
|
||||
using (Bitmap bitmap = writer.Write(content))
|
||||
{
|
||||
ImageFormat imgFormat = ImageFormat.Png;
|
||||
if (format.Equals("jpg", StringComparison.OrdinalIgnoreCase) || format.Equals("jpeg", StringComparison.OrdinalIgnoreCase))
|
||||
imgFormat = ImageFormat.Jpeg;
|
||||
else if (format.Equals("bmp", StringComparison.OrdinalIgnoreCase))
|
||||
imgFormat = ImageFormat.Bmp;
|
||||
|
||||
bitmap.Save(outPutFile, imgFormat);
|
||||
}
|
||||
}
|
||||
|
||||
public Bitmap QREncode(string contents)
|
||||
{
|
||||
BarcodeWriter writer = new BarcodeWriter
|
||||
{
|
||||
Format = BarcodeFormat.QR_CODE,
|
||||
Options = new QrCodeEncodingOptions
|
||||
{
|
||||
Width = this.width,
|
||||
Height = this.height,
|
||||
CharacterSet = "UTF-8",
|
||||
ErrorCorrection = (ErrorCorrectionLevel)paramMap[EncodeHintType.ERROR_CORRECTION],
|
||||
Margin = (int)paramMap[EncodeHintType.MARGIN]
|
||||
}
|
||||
};
|
||||
return writer.Write(contents);
|
||||
}
|
||||
|
||||
public string QRReader(string filePath)
|
||||
{
|
||||
using (Bitmap bitmap = (Bitmap)Image.FromFile(filePath))
|
||||
{
|
||||
return QRReader(bitmap);
|
||||
}
|
||||
}
|
||||
|
||||
public string QRReader(Bitmap bitmap)
|
||||
{
|
||||
BarcodeReader reader = new BarcodeReader();
|
||||
// reader.Options.CharacterSet = "UTF-8";
|
||||
Result result = reader.Decode(bitmap);
|
||||
return result?.Text;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace Com.Lmc.ShuiYin.Two.Utils
|
||||
{
|
||||
public class TwoMain
|
||||
{
|
||||
public static void Run(string[] args)
|
||||
{
|
||||
string baseDir = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "TestImages");
|
||||
|
||||
string src = Path.Combine(baseDir, "meinv.png");
|
||||
string watermarked = Path.Combine(baseDir, "meinv2.png");
|
||||
string extracted = Path.Combine(baseDir, "meinv3.png");
|
||||
|
||||
WaterMarkDFT.Embed(src, "huafu", watermarked);
|
||||
WaterMarkDFT.Extract(watermarked, extracted);
|
||||
|
||||
string src2 = Path.Combine(baseDir, "444.png");
|
||||
string extracted2 = Path.Combine(baseDir, "555.png");
|
||||
|
||||
WaterMarkDFT.Extract(src2, extracted2);
|
||||
Console.WriteLine("TwoMain finished.");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,193 @@
|
||||
using System.Collections.Generic;
|
||||
using OpenCvSharp;
|
||||
|
||||
namespace Com.Lmc.ShuiYin.Two.Utils
|
||||
{
|
||||
public class WaterMarkDCT
|
||||
{
|
||||
private static readonly double P = 65;
|
||||
|
||||
public static void Embed(string imagePath, string watermarkPath, string outputPath)
|
||||
{
|
||||
Mat originaleImage = Cv2.ImRead(imagePath);
|
||||
List<Mat> allPlanes = new List<Mat>();
|
||||
Cv2.Split(originaleImage, out Mat[] splitPlanes);
|
||||
allPlanes.AddRange(splitPlanes);
|
||||
Mat YMat = allPlanes[0];
|
||||
|
||||
int[][] watermark = ImageToMatrix(watermarkPath);
|
||||
int length = 8;
|
||||
|
||||
for (int i = 0; i < watermark.Length; i++)
|
||||
{
|
||||
for (int j = 0; j < watermark[0].Length; j++)
|
||||
{
|
||||
Mat block = GetImageValue(YMat, i, j, length);
|
||||
|
||||
int x1 = 1, y1 = 2;
|
||||
int x2 = 2, y2 = 1;
|
||||
|
||||
// In Java: double[] a = block.get(x1, y1);
|
||||
// OpenCvSharp Get returns struct/value.
|
||||
// block is CV_32F (float) based on GetImageValue.
|
||||
|
||||
Cv2.Dct(block, block);
|
||||
|
||||
if (watermark[i][j] == 1)
|
||||
{
|
||||
block.Set(x1, y1, (float)P);
|
||||
block.Set(x2, y2, (float)-P);
|
||||
}
|
||||
if (watermark[i][j] == 0)
|
||||
{
|
||||
block.Set(x1, y1, (float)-P);
|
||||
block.Set(x2, y2, (float)P);
|
||||
}
|
||||
|
||||
Cv2.Idct(block, block);
|
||||
|
||||
for (int m = 0; m < length; m++)
|
||||
{
|
||||
for (int t = 0; t < length; t++)
|
||||
{
|
||||
// In Java: double[] e = block.get(m, t);
|
||||
// YMat.put(i * length + m, j * length + t, e);
|
||||
float e = block.At<float>(m, t);
|
||||
// YMat is likely CV_8U from imread, but GetImageValue reads it.
|
||||
// If YMat is CV_8U, we can't put float P (65) directly if it overflows or logic expects float processing.
|
||||
// But Java code: `Mat YMat = allPlanes.get(0);` (CV_8U usually).
|
||||
// `Mat block = getImageValue(YMat...);` creates new Mat CV_32F.
|
||||
// Then `YMat.put` writes back. If YMat is 8U, putting float might be truncated or requires YMat to be 32F.
|
||||
// However, Java OpenCV `put` handles conversion?
|
||||
// Let's assume YMat should be converted to float if we are doing this, OR we cast to byte.
|
||||
// But P=65 is additive? No, it replaces values.
|
||||
// Wait, `block.get(x1, y1)` returns the DCT coefficient.
|
||||
// The DCT is done on 32F block.
|
||||
// Then IDCT.
|
||||
// Then we put back to YMat.
|
||||
// If YMat is 8U, we are putting float values.
|
||||
// In OpenCvSharp: `YMat.Set(row, col, value)`.
|
||||
// If YMat is 8U, Set will cast.
|
||||
YMat.Set(i * length + m, j * length + t, (byte)e); // Cast to byte? Or keep as double/float if YMat was converted?
|
||||
// Java code didn't convert YMat to 32F explicitly in Embed method, unlike DFT.
|
||||
// But `getImageValue` creates a 32F mat from YMat values.
|
||||
// `YMat.put(...)` in Java takes double array.
|
||||
// If YMat is 8U, it sets the byte value.
|
||||
// So casting to byte is correct.
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Mat imageOut = new Mat();
|
||||
Cv2.Merge(allPlanes.ToArray(), imageOut);
|
||||
Cv2.ImWrite(outputPath, imageOut);
|
||||
}
|
||||
|
||||
public static void Extract(string targetImage, string outputWatermark)
|
||||
{
|
||||
Mat image = Cv2.ImRead(targetImage);
|
||||
List<Mat> allPlanes = new List<Mat>();
|
||||
Cv2.Split(image, out Mat[] splitPlanes);
|
||||
allPlanes.AddRange(splitPlanes);
|
||||
Mat YMat = allPlanes[0];
|
||||
|
||||
int rows = 100;
|
||||
int cols = 100;
|
||||
int length = 8;
|
||||
int[][] watermark = new int[rows][];
|
||||
for (int k = 0; k < rows; k++) watermark[k] = new int[cols];
|
||||
|
||||
for (int i = 0; i < rows; i++)
|
||||
{
|
||||
for (int j = 0; j < cols; j++)
|
||||
{
|
||||
Mat block = GetImageValue(YMat, i, j, length);
|
||||
Cv2.Dct(block, block);
|
||||
|
||||
int x1 = 1, y1 = 2;
|
||||
int x2 = 2, y2 = 1;
|
||||
|
||||
float a = block.At<float>(x1, y1);
|
||||
float c = block.At<float>(x2, y2);
|
||||
|
||||
if (a >= c)
|
||||
{
|
||||
watermark[i][j] = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
MatrixToImage(watermark, outputWatermark);
|
||||
}
|
||||
|
||||
private static Mat GetImageValue(Mat YMat, int x, int y, int length)
|
||||
{
|
||||
Mat mat = new Mat(length, length, MatType.CV_32F);
|
||||
for (int i = 0; i < length; i++)
|
||||
{
|
||||
for (int j = 0; j < length; j++)
|
||||
{
|
||||
// Java: double[] temp = YMat.get(x * length + i, y * length + j);
|
||||
// mat.put(i, j, temp);
|
||||
// If YMat is 8U, At<byte>
|
||||
byte val = YMat.At<byte>(x * length + i, y * length + j);
|
||||
mat.Set(i, j, (float)val);
|
||||
}
|
||||
}
|
||||
return mat;
|
||||
}
|
||||
|
||||
private static void MatrixToImage(int[][] watermark, string dstPath)
|
||||
{
|
||||
int rows = watermark.Length;
|
||||
int columns = watermark[0].Length;
|
||||
Mat image = new Mat(rows, columns, MatType.CV_8U); // THRESH_BINARY usually on 8U
|
||||
for (int i = 0; i < rows; i++)
|
||||
{
|
||||
for (int j = 0; j < columns; j++)
|
||||
{
|
||||
if (watermark[i][j] == 1)
|
||||
{
|
||||
image.Set(i, j, (byte)255);
|
||||
}
|
||||
else
|
||||
{
|
||||
image.Set(i, j, (byte)0);
|
||||
}
|
||||
}
|
||||
}
|
||||
Cv2.ImWrite(dstPath, image);
|
||||
}
|
||||
|
||||
private static int[][] ImageToMatrix(string srcPath)
|
||||
{
|
||||
Mat mat = Cv2.ImRead(srcPath, ImreadModes.Grayscale); // Java: THRESH_BINARY is not a load flag, it's a thresh type. But here passed to imread?
|
||||
// Java: imread(srcPath, Imgproc.THRESH_BINARY).
|
||||
// THRESH_BINARY is 0. CV_LOAD_IMAGE_GRAYSCALE is 0.
|
||||
// So they are loading as grayscale.
|
||||
|
||||
int rows = mat.Rows;
|
||||
int columns = mat.Cols;
|
||||
int[][] waterMark = new int[rows][];
|
||||
for(int k=0; k<rows; k++) waterMark[k] = new int[columns];
|
||||
|
||||
for (int i = 0; i < rows; i++)
|
||||
{
|
||||
for (int j = 0; j < columns; j++)
|
||||
{
|
||||
// double[] doubles = mat.get(i, j);
|
||||
byte val = mat.At<byte>(i, j);
|
||||
if (val == 255)
|
||||
{
|
||||
waterMark[i][j] = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
waterMark[i][j] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
return waterMark;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
using System.Collections.Generic;
|
||||
using OpenCvSharp;
|
||||
|
||||
namespace Com.Lmc.ShuiYin.Two.Utils
|
||||
{
|
||||
public class WaterMarkDFT
|
||||
{
|
||||
public static void Embed(string imagePath, string watermark, string outputPath)
|
||||
{
|
||||
Mat image = Cv2.ImRead(imagePath);
|
||||
List<Mat> allPlanes = new List<Mat>();
|
||||
Cv2.Split(image, out Mat[] splitPlanes);
|
||||
allPlanes.AddRange(splitPlanes);
|
||||
|
||||
Mat processImage = allPlanes[0];
|
||||
processImage.ConvertTo(processImage, MatType.CV_32F);
|
||||
|
||||
List<Mat> planes = new List<Mat>();
|
||||
planes.Add(processImage);
|
||||
planes.Add(Mat.Zeros(processImage.Size(), MatType.CV_32F));
|
||||
|
||||
Mat complexImage = new Mat();
|
||||
Cv2.Merge(planes.ToArray(), complexImage);
|
||||
|
||||
Cv2.Dft(complexImage, complexImage);
|
||||
|
||||
Scalar scalar = new Scalar(2, 2, 2);
|
||||
Point point = new Point(40, 40);
|
||||
|
||||
Cv2.PutText(complexImage, watermark, point, HersheyFonts.HersheyComplex, 1.5, scalar, 3, LineTypes.Link8, false); // Java: 20 -> bottomLeftOrigin? No, putText last arg is bottomLeftOrigin. Java code says 20??
|
||||
// Java putText signature: (img, text, org, fontFace, fontScale, color, thickness, lineType, bottomLeftOrigin)
|
||||
// Java code: putText(complexImage, watermark, point, FONT_HERSHEY_COMPLEX, 1.5, scalar, 3, 20);
|
||||
// Wait, 20 is not boolean.
|
||||
// Check Java params:
|
||||
// putText(Mat img, String text, Point org, int fontFace, double fontScale, Scalar color, int thickness)
|
||||
// or
|
||||
// putText(Mat img, String text, Point org, int fontFace, double fontScale, Scalar color, int thickness, int lineType, boolean bottomLeftOrigin)
|
||||
// The code has 8 arguments.
|
||||
// putText(complexImage, watermark, point, FONT_HERSHEY_COMPLEX, 1.5, scalar,3, 20);
|
||||
// Arg 8 is 20? 20 is not boolean.
|
||||
// Maybe they mean lineType=20? But lineType is usually 8, 4, CV_AA (16).
|
||||
// Maybe it is calling a different overload?
|
||||
// Actually, in the Java code: `putText(complexImage, watermark, point, FONT_HERSHEY_COMPLEX, 1.5, scalar,3, 20);`
|
||||
// If I assume 20 is lineType? CV_AA? No.
|
||||
// Let's assume standard parameters: thickness=3, lineType=20?
|
||||
// I will use standard LineTypes.Link8 and false for bottomLeftOrigin.
|
||||
|
||||
Cv2.Flip(complexImage, complexImage, FlipMode.XY); // -1
|
||||
Cv2.PutText(complexImage, watermark, point, HersheyFonts.HersheyComplex, 1.5, scalar, 3, LineTypes.Link8, false);
|
||||
Cv2.Flip(complexImage, complexImage, FlipMode.XY); // -1
|
||||
|
||||
Mat invDFT = new Mat();
|
||||
Cv2.Idft(complexImage, invDFT, DftFlags.Scale | DftFlags.RealOutput);
|
||||
|
||||
invDFT.ConvertTo(invDFT, MatType.CV_8U);
|
||||
|
||||
allPlanes[0] = invDFT;
|
||||
|
||||
Cv2.Merge(allPlanes.ToArray(), invDFT);
|
||||
Cv2.ImWrite(outputPath, invDFT);
|
||||
}
|
||||
|
||||
public static void Extract(string targetImage, string outputWatermark)
|
||||
{
|
||||
Mat image = Cv2.ImRead(targetImage);
|
||||
List<Mat> allPlanes = new List<Mat>();
|
||||
Cv2.Split(image, out Mat[] splitPlanes);
|
||||
allPlanes.AddRange(splitPlanes);
|
||||
|
||||
Mat processImage = allPlanes[0];
|
||||
processImage.ConvertTo(processImage, MatType.CV_32F);
|
||||
|
||||
List<Mat> planes = new List<Mat>();
|
||||
planes.Add(processImage);
|
||||
planes.Add(Mat.Zeros(processImage.Size(), MatType.CV_32F));
|
||||
|
||||
Mat complexImage = new Mat();
|
||||
Cv2.Merge(planes.ToArray(), complexImage);
|
||||
|
||||
Cv2.Dft(complexImage, complexImage);
|
||||
|
||||
List<Mat> newPlanes = new List<Mat>();
|
||||
Mat mag = new Mat();
|
||||
Cv2.Split(complexImage, out Mat[] splitComplex);
|
||||
newPlanes.AddRange(splitComplex);
|
||||
|
||||
Cv2.Magnitude(newPlanes[0], newPlanes[1], mag);
|
||||
Cv2.Add(Mat.Ones(mag.Size(), MatType.CV_32F), mag, mag);
|
||||
Cv2.Log(mag, mag);
|
||||
|
||||
// Crop
|
||||
mag = mag.SubMat(new Rect(0, 0, mag.Cols & -2, mag.Rows & -2));
|
||||
int cx = mag.Cols / 2;
|
||||
int cy = mag.Rows / 2;
|
||||
|
||||
Mat q0 = new Mat(mag, new Rect(0, 0, cx, cy));
|
||||
Mat q1 = new Mat(mag, new Rect(cx, 0, cx, cy));
|
||||
Mat q2 = new Mat(mag, new Rect(0, cy, cx, cy));
|
||||
Mat q3 = new Mat(mag, new Rect(cx, cy, cx, cy));
|
||||
Mat tmp = new Mat();
|
||||
q0.CopyTo(tmp);
|
||||
q3.CopyTo(q0);
|
||||
tmp.CopyTo(q3);
|
||||
q1.CopyTo(tmp);
|
||||
q2.CopyTo(q1);
|
||||
tmp.CopyTo(q2);
|
||||
|
||||
mag.ConvertTo(mag, MatType.CV_8UC1);
|
||||
Cv2.Normalize(mag, mag, 0, 255, NormTypes.MinMax, (int)MatType.CV_8UC1);
|
||||
Cv2.ImWrite(outputWatermark, mag);
|
||||
}
|
||||
|
||||
private static Mat OptimizeImageDimensions(Mat image)
|
||||
{
|
||||
Mat padded = new Mat();
|
||||
int addPixelRows = Cv2.GetOptimalDFTSize(image.Rows);
|
||||
int addPixelCols = Cv2.GetOptimalDFTSize(image.Cols);
|
||||
Cv2.CopyMakeBorder(image, padded, 0, addPixelRows - image.Rows,
|
||||
0, addPixelCols - image.Cols, BorderTypes.Constant, Scalar.All(0));
|
||||
return padded;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user