using OpenCvSharp; using OpenCvSharp.Flann; using Sunny.UI.Win32; using System; using System.Collections.Generic; using System.Linq; using System.Security.Cryptography; using System.Text; using System.Threading.Tasks; using static System.Net.Mime.MediaTypeNames; using Point = OpenCvSharp.Point; using Size = OpenCvSharp.Size; using System; using OpenCvSharp; using OpenCvSharp.Features2D; using OpenCvSharp.Flann; using System.Drawing; namespace HisenceYoloDetection { public static class CheckDiffSciHelper { public static Mat ProcessImage(Mat image, Rect fillRect) { // 获取图像尺寸 int width = image.Width; int height = image.Height; // 定义左下角 30x30 矩形框 int rectSize = 30; int rectX = 0; int rectY = height - rectSize; // 确保是左下角 // 防止越界 if (rectY < 0 || rectX < 0 || rectSize > width || rectSize > height) { Console.WriteLine("图像尺寸不足以获取指定区域"); return image; } Rect roi = new Rect(rectX, rectY, rectSize, rectSize); Mat roiMat = new Mat(image, roi); // 计算平均颜色值 Scalar meanColor = Cv2.Mean(roiMat); Vec3b fillColor = new Vec3b((byte)meanColor.Val0, (byte)meanColor.Val1, (byte)meanColor.Val2); // 防止越界 if (fillRect.X < 0 || fillRect.Y < 0 || fillRect.X + fillRect.Width > width || fillRect.Y + fillRect.Height > height) { Console.WriteLine("填充区域超出图像范围"); return image; } // 填充指定区域 for (int y = fillRect.Y; y < fillRect.Y + fillRect.Height; y++) { for (int x = fillRect.X; x < fillRect.X + fillRect.Width; x++) { image.Set(y, x, fillColor); } } return image; } /// /// /// /// 标准图像 /// 要对比的图像 /// 白板黑字为true /// 存储路径 public static bool CheckDiffSci(string path1, Mat MatDet, ref Mat ResultMat,Rect sqlrect, Rect detrect, bool IfWhiteWord, string saveDir) { // 读取和处理第一张图片。。 Mat img1 = Cv2.ImRead(path1, ImreadModes.Color); if (img1.Empty()) { Console.WriteLine($"Error loading image {path1}"); return false; } // Cv2.Resize(img1, img1, new Size(550, 270)); img1 = ProcessImage(img1, sqlrect); Mat gimg1 = new Mat(); Cv2.CvtColor(img1, gimg1, ColorConversionCodes.BGR2GRAY); Mat thr1 = new Mat(); if (IfWhiteWord) { Cv2.Threshold(gimg1, thr1, 0, 255, ThresholdTypes.BinaryInv | ThresholdTypes.Otsu); } else { Cv2.Threshold(gimg1, thr1, 0, 255, ThresholdTypes.Binary | ThresholdTypes.Otsu); } // 读取和处理第二张图片 Mat img2 = MatDet.Clone(); ResultMat= MatDet.Clone(); if (img2.Empty()) { // Console.WriteLine($"Error loading image {path2}"); return false; } // Cv2.Resize(img2, img2, new Size(550, 270)); img2 = ProcessImage(img2, detrect); Rect bottomleftRect= new Rect(0,img1.Height-30,30,30); Scalar avgColor1 = Cv2.Mean(new Mat(img1,bottomleftRect)); Mat gimg2 = new Mat(); Cv2.CvtColor(img2, gimg2, ColorConversionCodes.BGR2GRAY); Mat thr2 = new Mat(); //Cv2.Threshold(gimg2, thr2, 0, 255, ThresholdTypes.BinaryInv | ThresholdTypes.Otsu); if (IfWhiteWord) { Cv2.Threshold(gimg2, thr2, 0, 255, ThresholdTypes.BinaryInv | ThresholdTypes.Otsu); } else { Cv2.Threshold(gimg2, thr2, 0, 255, ThresholdTypes.Binary | ThresholdTypes.Otsu); } //Rect area2 = new Rect(148,30,229,222); sqlrect.Width += 20; sqlrect.Height+= 100; detrect.Width += 20; detrect.Height+=100; Mat matCutblack1 = new Mat(thr1, sqlrect); if (IfWhiteWord) { matCutblack1.SetTo(Scalar.Black); } else { matCutblack1.SetTo(Scalar.Black); } Mat matCutblack2 = new Mat(thr2, detrect); if (IfWhiteWord) { matCutblack2.SetTo(Scalar.Black); } else { matCutblack2.SetTo(Scalar.Black); } Cv2.Resize(thr1, thr1, new Size(550, 270)); Cv2.Resize(thr2, thr2, new Size(550, 270)); DateTime dt = DateTime.Now; string filename = dt.Year.ToString() + dt.Month.ToString() + dt.Day.ToString() + dt.Hour.ToString() + dt.Minute.ToString() + dt.Millisecond.ToString(); string savePath4 = Path.Combine(saveDir, Path.GetFileNameWithoutExtension(path1) + filename + "_thr1.png"); // 保存结果 Cv2.ImWrite(savePath4, thr1); string savePath3 = Path.Combine(saveDir, Path.GetFileNameWithoutExtension(path1) + filename + "_thr2.png"); // 保存结果 Cv2.ImWrite(savePath3, thr2); // 创建卷积核 Mat filter1 = new Mat(15, 15, MatType.CV_32F, new Scalar(0)); filter1.Row(7).SetTo(new Scalar(0.025)); filter1.Col(7).SetTo(new Scalar(0.025)); // 应用卷积 Mat final_result1 = new Mat(); Cv2.Filter2D(thr1, final_result1, -1, filter1, anchor: new Point(-1, -1), 0, BorderTypes.Reflect); Cv2.Filter2D(final_result1, final_result1, -1, filter1, anchor: new Point(-1, -1), 0, BorderTypes.Reflect); Cv2.Filter2D(final_result1, final_result1, -1, filter1, anchor: new Point(-1, -1), 0, BorderTypes.Reflect); Mat final_result2 = new Mat(); Cv2.Filter2D(thr2, final_result2, -1, filter1, anchor: new Point(-1, -1), 0, BorderTypes.Reflect); Cv2.Filter2D(final_result2, final_result2, -1, filter1, anchor: new Point(-1, -1), 0, BorderTypes.Reflect); Cv2.Filter2D(final_result2, final_result2, -1, filter1, anchor: new Point(-1, -1), 0, BorderTypes.Reflect); //裁剪才行 //string savePath2 = Path.Combine(saveDir, Path.GetFileNameWithoutExtension(path1) + "_final_result1.png"); ////保存结果 //string savePath = Path.Combine(saveDir, Path.GetFileNameWithoutExtension(path2) + "_diff.png"); //Cv2.ImWrite(savePath2, final_result1); //string savePath = Path.Combine(saveDir, Path.GetFileNameWithoutExtension(path1) + "_final_result2.png"); ////保存结果 //string savePath = Path.Combine(saveDir, Path.GetFileNameWithoutExtension(path2) + "_diff.png"); //Cv2.ImWrite(savePath, final_result2); // 计算图像差异 Mat devIMG = new Mat(); Mat devIMG_ = new Mat(); Cv2.Subtract(final_result1, final_result2, devIMG); Cv2.Subtract(final_result2, final_result1, devIMG_); //string savePathd = Path.Combine(saveDir, Path.GetFileNameWithoutExtension(path1) + filename + "devIMG.png"); //// 保存结果 //Cv2.ImWrite(savePathd, devIMG); //string savePathd1 = Path.Combine(saveDir, Path.GetFileNameWithoutExtension(path1) + filename + "devIMG_.png"); //// 保存结果 //Cv2.ImWrite(savePathd1, devIMG_); // 对差异图像应用阈值 Cv2.Threshold(devIMG, devIMG, 20, 255, ThresholdTypes.Binary); Cv2.Threshold(devIMG_, devIMG_, 20, 255, ThresholdTypes.Binary); // 结合差异 Mat sumIMG = new Mat(); Cv2.Add(devIMG, devIMG_, sumIMG); // 应用形态学操作 Mat kernelCL = Cv2.GetStructuringElement(MorphShapes.Rect, new Size(3, 3)); Mat blackhatImg = new Mat(); Cv2.Dilate(sumIMG, blackhatImg, kernelCL); // 处理轮廓和保存结果 Point[][] contours = new Point[10000][]; Cv2.FindContours(blackhatImg, out contours, out _, RetrievalModes.Tree, ContourApproximationModes.ApproxSimple); bool isMatch = true; foreach (var contour in contours) { if (Cv2.ContourArea(contour) <= 500) { Cv2.DrawContours(blackhatImg, new Point[][] { contour }, -1, Scalar.Black, thickness: Cv2.FILLED); // 框选轮廓 string savePath2 = Path.Combine("D:\\Hisence\\Test\\2\\ok", Path.GetFileNameWithoutExtension(path1) + filename + "_Rect.png"); // 保存结果 //string savePath = Path.Combine(saveDir, Path.GetFileNameWithoutExtension(path2) + "_diff.png"); Cv2.ImWrite(savePath2, img2); string savePath = Path.Combine("D:\\Hisence\\Test\\2\\ok", Path.GetFileNameWithoutExtension(path1) + filename + "_diff.png"); // 保存结果 //string savePath = Path.Combine(saveDir, Path.GetFileNameWithoutExtension(path2) + "_diff.png"); Cv2.ImWrite(savePath, blackhatImg); } else { Rect boundingRect = Cv2.BoundingRect(contour); Cv2.Resize(img2, img2, new Size(550, 270)); Cv2.Rectangle(img2, boundingRect, Scalar.Red, thickness: 2); isMatch = false; string savePath2 = Path.Combine("D:\\Hisence\\Test\\2\\ng", Path.GetFileNameWithoutExtension(path1) + filename + "_Rect.png"); // 保存结果 //string savePath = Path.Combine(saveDir, Path.GetFileNameWithoutExtension(path2) + "_diff.png"); Cv2.ImWrite(savePath2, img2); ResultMat = img2.Clone(); string savePath = Path.Combine("D:\\Hisence\\Test\\2\\ng", Path.GetFileNameWithoutExtension(path1) + filename + "_diff.png"); // 保存结果 //string savePath = Path.Combine(saveDir, Path.GetFileNameWithoutExtension(path2) + "_diff.png"); Cv2.ImWrite(savePath, blackhatImg); } } // 新增的白色面积占比判断 double whiteArea1 = Cv2.CountNonZero(thr1); double whiteArea2 = Cv2.CountNonZero(thr2); double ratio1 = whiteArea1 / (thr1.Rows * thr1.Cols); double ratio2 = whiteArea2 / (thr2.Rows * thr2.Cols); if (Math.Abs(ratio1 - ratio2) >= 0.95) { isMatch = true; } return isMatch; } public static Rect strChangeRect(string strrect) { if (!string.IsNullOrEmpty(strrect)) { string[] rectstr = strrect.Split(","); int areaX = int.Parse(rectstr[0]); int areaY = int.Parse(rectstr[1]); int areaWidth = int.Parse(rectstr[2]); int areaHeight = int.Parse(rectstr[3]); Rect rect = new Rect(areaX, areaY, areaWidth, areaHeight); return rect; } else { return new Rect(0, 0, 0, 0); } } public static string rectChangeStr(Rect area) { string[] rectsql = new string[4]; rectsql[0] = Convert.ToString(area.X); rectsql[1] = Convert.ToString(area.Y); rectsql[2] = Convert.ToString(area.Width); rectsql[3] = Convert.ToString(area.Height); string strrect = rectsql.Join(","); return strrect; } public static class CheckDiffSciHelper1 { /// /// /// /// 标准图像 /// 要对比的图像 /// 白板黑字为true /// 存储路径 public static bool CheckDiffSci(string path1, Mat MatDet, Rect sqlrect, Rect detrect, bool IfWhiteWord, string saveDir) { // 读取和处理第一张图片 Mat img1 = Cv2.ImRead(path1, ImreadModes.Color); if (img1.Empty()) { Console.WriteLine($"Error loading image {path1}"); return false; } // Cv2.Resize(img1, img1, new Size(550, 270)); img1 = RemoveBorders(img1); Mat gimg1 = new Mat(); Cv2.CvtColor(img1, gimg1, ColorConversionCodes.BGR2GRAY); Mat thr1 = new Mat(); if (IfWhiteWord) { Cv2.Threshold(gimg1, thr1, 0, 255, ThresholdTypes.BinaryInv | ThresholdTypes.Otsu); } else { Cv2.Threshold(gimg1, thr1, 0, 255, ThresholdTypes.Binary | ThresholdTypes.Otsu); } // 读取和处理第二张图片 Mat img2 = MatDet.Clone(); if (img2.Empty()) { // Console.WriteLine($"Error loading image {path2}"); return false; } // Cv2.Resize(img2, img2, new Size(550, 270)); img2 = RemoveBorders(img2); Mat gimg2 = new Mat(); Cv2.CvtColor(img2, gimg2, ColorConversionCodes.BGR2GRAY); Mat thr2 = new Mat(); //Cv2.Threshold(gimg2, thr2, 0, 255, ThresholdTypes.BinaryInv | ThresholdTypes.Otsu); if (IfWhiteWord) { Cv2.Threshold(gimg2, thr2, 0, 255, ThresholdTypes.BinaryInv | ThresholdTypes.Otsu); } else { Cv2.Threshold(gimg2, thr2, 0, 255, ThresholdTypes.Binary | ThresholdTypes.Otsu); } //Rect area2 = new Rect(148,30,229,222); sqlrect.Width += 20; sqlrect.Height += 20; detrect.Width += 20; detrect.Height += 20; Mat matCutblack1 = new Mat(thr1, sqlrect); if (IfWhiteWord) { matCutblack1.SetTo(Scalar.Black); } else { matCutblack1.SetTo(Scalar.Black); } Mat matCutblack2 = new Mat(thr2, detrect); if (IfWhiteWord) { matCutblack2.SetTo(Scalar.Black); } else { matCutblack2.SetTo(Scalar.Black); } Cv2.Resize(thr1, thr1, new Size(845, 498)); Cv2.Resize(thr2, thr2, new Size(845, 498)); DateTime dt = DateTime.Now; string filename = dt.Year.ToString() + dt.Month.ToString() + dt.Day.ToString() + dt.Hour.ToString() + dt.Minute.ToString() + dt.Millisecond.ToString(); string savePath4 = Path.Combine(saveDir, Path.GetFileNameWithoutExtension(path1) + filename + "_thr1.png"); // 保存结果 Cv2.ImWrite(savePath4, thr1); string savePath3 = Path.Combine(saveDir, Path.GetFileNameWithoutExtension(path1) + filename + "_thr2.png"); // 保存结果 Cv2.ImWrite(savePath3, thr2); // 创建卷积核 Mat filter1 = new Mat(15, 15, MatType.CV_32F, new Scalar(0)); filter1.Row(7).SetTo(new Scalar(0.025)); filter1.Col(7).SetTo(new Scalar(0.025)); // 应用卷积 Mat final_result1 = new Mat(); Cv2.Filter2D(thr1, final_result1, -1, filter1, anchor: new Point(-1, -1), 0, BorderTypes.Reflect); Cv2.Filter2D(final_result1, final_result1, -1, filter1, anchor: new Point(-1, -1), 0, BorderTypes.Reflect); Cv2.Filter2D(final_result1, final_result1, -1, filter1, anchor: new Point(-1, -1), 0, BorderTypes.Reflect); //Cv2.Filter2D(final_result1, final_result1, -1, filter2, anchor: new Point(-1, -1), 0, BorderTypes.Reflect); Mat final_result2 = new Mat(); Cv2.Filter2D(thr2, final_result2, -1, filter1, anchor: new Point(-1, -1), 0, BorderTypes.Reflect); Cv2.Filter2D(final_result2, final_result2, -1, filter1, anchor: new Point(-1, -1), 0, BorderTypes.Reflect); Cv2.Filter2D(final_result2, final_result2, -1, filter1, anchor: new Point(-1, -1), 0, BorderTypes.Reflect); //Cv2.Filter2D(final_result2, final_result2, -1, filter2, anchor: new Point(-1, -1), 0, BorderTypes.Reflect); //裁剪才行 //string savePath2 = Path.Combine(saveDir, Path.GetFileNameWithoutExtension(path1) + "_final_result1.png"); //// 保存结果 ////string savePath = Path.Combine(saveDir, Path.GetFileNameWithoutExtension(path2) + "_diff.png"); //Cv2.ImWrite(savePath2, final_result1); //string savePath = Path.Combine(saveDir, Path.GetFileNameWithoutExtension(path1) + "_final_result2.png"); //// 保存结果 ////string savePath = Path.Combine(saveDir, Path.GetFileNameWithoutExtension(path2) + "_diff.png"); //Cv2.ImWrite(savePath, final_result2); // 计算图像差异 Mat devIMG = new Mat(); Mat devIMG_ = new Mat(); Cv2.Subtract(final_result1, final_result2, devIMG); Cv2.Subtract(final_result2, final_result1, devIMG_); string savePathd = Path.Combine(saveDir, Path.GetFileNameWithoutExtension(path1) + filename + "devIMG.png"); // 保存结果 Cv2.ImWrite(savePathd, devIMG); string savePathd1 = Path.Combine(saveDir, Path.GetFileNameWithoutExtension(path1) + filename + "devIMG_.png"); // 保存结果 Cv2.ImWrite(savePathd1, devIMG_); // 对差异图像应用阈值 Cv2.Threshold(devIMG, devIMG, 8, 255, ThresholdTypes.Binary); Cv2.Threshold(devIMG_, devIMG_, 8, 255, ThresholdTypes.Binary); // 结合差异 Mat sumIMG = new Mat(); Cv2.Add(devIMG, devIMG_, sumIMG); string savePaths = Path.Combine(saveDir, Path.GetFileNameWithoutExtension(path1) + filename + "sumIMG.png"); // 保存结果 Cv2.ImWrite(savePaths, sumIMG); // 应用形态学操作 Mat kernelCL = Cv2.GetStructuringElement(MorphShapes.Rect, new Size(3, 3)); Mat blackhatImg = new Mat(); Cv2.Dilate(sumIMG, blackhatImg, kernelCL); // 处理轮廓和保存结果 Point[][] contours = new Point[10000][]; Cv2.FindContours(blackhatImg, out contours, out _, RetrievalModes.Tree, ContourApproximationModes.ApproxSimple); bool isMatch = true; foreach (var contour in contours) { if (Cv2.ContourArea(contour) <= 500) { Cv2.DrawContours(blackhatImg, new Point[][] { contour }, -1, Scalar.Black, thickness: Cv2.FILLED); // 框选轮廓 } else { Rect boundingRect = Cv2.BoundingRect(contour); Cv2.Rectangle(img2, boundingRect, Scalar.Red, thickness: 2); isMatch = false; } } string savePath2 = Path.Combine(saveDir, Path.GetFileNameWithoutExtension(path1) + filename + "_Rect.png"); // 保存结果 //string savePath = Path.Combine(saveDir, Path.GetFileNameWithoutExtension(path2) + "_diff.png"); Cv2.ImWrite(savePath2, img2); string savePath = Path.Combine(saveDir, Path.GetFileNameWithoutExtension(path1) + filename + "_diff.png"); // 保存结果 //string savePath = Path.Combine(saveDir, Path.GetFileNameWithoutExtension(path2) + "_diff.png"); Cv2.ImWrite(savePath, blackhatImg); return isMatch; } static Mat RemoveBorders(Mat image) { // 将图像转换为灰度图 Mat grayImage = new Mat(); Cv2.CvtColor(image, grayImage, ColorConversionCodes.BGR2GRAY); // 使用自适应二值化将图像变为黑白图 Mat binaryImage = new Mat(); Cv2.AdaptiveThreshold(grayImage, binaryImage, 255, AdaptiveThresholdTypes.MeanC, ThresholdTypes.Binary, 11, 2); // 反转颜色 Mat invertedBinaryImage = new Mat(); Cv2.BitwiseNot(binaryImage, invertedBinaryImage); // 查找轮廓 Point[][] contours; HierarchyIndex[] hierarchy; Cv2.FindContours(invertedBinaryImage, out contours, out hierarchy, RetrievalModes.External, ContourApproximationModes.ApproxSimple); // 找到包含最大面积的轮廓 double maxArea = 0; Point[] maxContour = null; foreach (var contour in contours) { double area = Cv2.ContourArea(contour); if (area > maxArea) { maxArea = area; maxContour = contour; } } if (maxContour == null) { Console.WriteLine("未找到有效轮廓!"); return image; } // 找到平行四边形的四个顶点 Point[] approx = Cv2.ApproxPolyDP(maxContour, Cv2.ArcLength(maxContour, true) * 0.02, true); Point2f[] srcPoints = approx.Select(p => new Point2f(p.X, p.Y)).ToArray(); if (srcPoints.Length != 4) { Console.WriteLine("未找到平行四边形的四个顶点!"); return image; } // 按顺时针顺序对顶点进行排序 srcPoints = OrderPoints(srcPoints); // 确定目标图像的四个顶点 Point2f[] dstPoints = new Point2f[] { new Point2f(0, 0), new Point2f(image.Width - 1, 0), new Point2f(image.Width - 1, image.Height - 1), new Point2f(0, image.Height - 1) }; // 计算透视变换矩阵 Mat transformMatrix = Cv2.GetPerspectiveTransform(srcPoints, dstPoints); // 应用透视变换 Mat warpedImage = new Mat(); Cv2.WarpPerspective(image, warpedImage, transformMatrix, new Size(image.Width, image.Height)); return warpedImage; } private static Point2f[] OrderPoints(Point2f[] points) { // 对顶点进行排序,顺时针顺序 Point2f[] orderedPoints = new Point2f[4]; // 计算质心 Point2f center = new Point2f(points.Average(p => p.X), points.Average(p => p.Y)); foreach (var point in points) { if (point.X < center.X && point.Y < center.Y) orderedPoints[0] = point; // 左上 else if (point.X > center.X && point.Y < center.Y) orderedPoints[1] = point; // 右上 else if (point.X > center.X && point.Y > center.Y) orderedPoints[2] = point; // 右下 else if (point.X < center.X && point.Y > center.Y) orderedPoints[3] = point; // 左下 } return orderedPoints; } } } }