using OpenCvSharp; using PaintDotNet.Adjust.BaseImage; using PaintDotNet.Base; using System; using System.Collections.Generic; using System.Drawing; namespace PaintDotNet.Adjust { /// /// 二值提取的处理程序 /// public unsafe class BinaryExtractionIntent { /// /// 全局中间变量 /// 不带视场的时候就是原图/二值图 /// 带视场的时候就是原图/二值图 /// public static Mat temp, temp_view; /// /// 用来存储透明图 /// public static Mat alpha; /// /// 1个颜色区间还是2个 /// public static int colorInterval; /// /// 1个颜色区间时的起始值 /// public static int colorOneStart; /// /// 1个颜色区间时的结止值 /// public static int colorOneEnd; /// /// 2个颜色区间时的第1个区间的起始值 /// public static int colorTwoStart = 0; /// /// 2个颜色区间时的第1个区间的结止值 /// public static int colorTwoEnd = 0; /// /// 2个颜色区间时的第2个区间的起始值 /// public static int colorThreeStart = 0; /// /// 2个颜色区间时的第2个区间的起始值 /// public static int colorThreeEnd = 0; //碎屑删除面积起 public static int debrisAreaStart = 0; //碎屑删除面积止 public static int debrisAreaEnd = 0; /// /// 默认相颜色 /// public static Vec4b vec4B = new Vec4b(255, 0, 0, 255); /// /// 新增的参数,用于FloodFill方法 /// public static Rect rect; /// /// 各个连通域的代表点 /// public static List points; /// /// 用来记录需要删除的连通区域的编号 /// public static Dictionary keyValuePairs = new Dictionary(); public static Mat rgb_ap; public static Mat temp_ap; #region 无视场 二值提取 /// /// 二值操作-二值提取-无视场 /// /// 源图像 /// 参数集合 /// public static Mat ImageBinaryExtraction(Mat src, List lists, bool findContours = false) { //以下是参数信息--------------------- ///1个颜色区间还是2个 colorInterval = 1; //第一个二值区间 colorOneStart = 0; colorOneEnd = 0; //第二个二值区间 colorTwoStart = 0; colorTwoEnd = 0; //第三个二值区间 colorThreeStart = 0; colorThreeEnd = 0; //处理细节 //删除边界对象 bool deleteBoundaryObject = false; //孔洞填充 bool holeFilling = false; //碎屑删除 bool debrisRemoval = false; //碎屑删除面积起 debrisAreaStart = 0; //碎屑删除面积止 debrisAreaEnd = 0; //二值样式 //1实心 2边线 int binaryStyle = 0; //相颜色 Color phaseColor = Color.Red; //目标选择 int targetSelection = 1; for (int i = 0; i < lists.Count; i++) { Args args = lists[i]; switch (args.Key) { case "colorInterval": { if (args.Value is Boolean) colorInterval = (Boolean)args.Value ? 2 : 1; else colorInterval = (int)args.Value; } break; case "scope1": colorOneStart = (int)((List)args.Value)[0]; colorOneEnd = (int)((List)args.Value)[1]; break; case "scope2": colorTwoStart = (int)((List)args.Value)[0]; colorTwoEnd = (int)((List)args.Value)[1]; break; case "scope3": colorThreeStart = (int)((List)args.Value)[0]; colorThreeEnd = (int)((List)args.Value)[1]; break; case "deleteBoundaryObject": deleteBoundaryObject = (bool)args.Value; break; case "holeFilling": holeFilling = (bool)args.Value; break; case "debrisRemoval": debrisRemoval = (bool)args.Value; break; case "binaryStyle": binaryStyle = (int)args.Value; break; case "phaseColor": phaseColor = Color.FromArgb((int)args.Value); break; case "targetSelection": targetSelection = (int)args.Value; break; case "scope4": debrisAreaStart = (int)((List)args.Value)[0]; debrisAreaEnd = (int)((List)args.Value)[1]; findContours = true; break; default: break; } } if (phaseColor.A < 1)//#21321 phaseColor = Color.FromArgb(1, phaseColor.R, phaseColor.G, phaseColor.B); vec4B.Item0 = phaseColor.B; vec4B.Item1 = phaseColor.G; vec4B.Item2 = phaseColor.R; vec4B.Item3 = phaseColor.A; //中值滤波 //Cv2.MedianBlur(src, src, 3); //二值提取 src = Binarization(src); //删除边界对象 if (deleteBoundaryObject) src = DeleteContours_New(src); //碎屑删除,参考冈萨雷斯,414,9.5.3,连通分量提取 if (debrisRemoval && debrisAreaEnd > 0) src = DebrisRemoval_New(src); //孔洞填充,参考冈萨雷斯,413,9.5.2 if (holeFilling) src = HoleFilling_New(src); //实心/边线,参考冈萨雷斯,412,9.5.1,边界提取 if (binaryStyle == 2) { Cv2.Erode(src, temp, null); src = src - temp; src = BaseTools.MergeMatFromMatArr(src, vec4B); } return src; } /// /// 二值提取 /// /// 源 /// 1个或2个颜色区间 /// /// /// /// /// /// /// private static Mat Binarization(Mat source) { Mat srcGray; if (source.Type() == MatType.CV_8UC1) { srcGray = source; } else { srcGray = source.CvtColor(ColorConversionCodes.BGR2GRAY); } temp = new Mat(source.Rows, source.Cols, MatType.CV_8UC4, new Scalar(0, 0, 0, 0)); srcGray.ForEachAsByte(GrayForEachAsByte); return temp.Clone(); } /// /// 并行循环 /// /// /// private static void GrayForEachAsByte(byte* value, int* position) { int y = position[0]; int x = position[1]; byte v = *value; if (colorInterval == 1) { if (v >= colorOneStart && v <= colorOneEnd) { temp.Set(y, x, vec4B); } } else { if ((v >= colorTwoStart && v <= colorTwoEnd) || (v >= colorThreeStart && v <= colorThreeEnd)) { temp.Set(y, x, vec4B); } } } /// /// 新版本删除边界 /// /// /// private static Mat DeleteContours_New(Mat src) { Mat[] arr = src.Split(); Mat rgb = BaseTools.MergeMatFromMatArr(arr); Mat temp_1 = arr[3].Clone(); for (int h = 0; h < temp_1.Width; h++) { byte vec4B_top = temp_1.At(0, h); if (vec4B_top > 0) { Cv2.FloodFill(rgb, new OpenCvSharp.Point(h, 0), new Scalar(0), out rect, null, null, FloodFillFlags.Link8); Cv2.FloodFill(temp_1, new OpenCvSharp.Point(h, 0), new Scalar(0), out rect, null, null, FloodFillFlags.Link8); } } for (int h = 0; h < temp_1.Width; h++) { byte vec4B_bottom = temp_1.At(temp_1.Height - 1, h); if (vec4B_bottom > 0) { Cv2.FloodFill(rgb, new OpenCvSharp.Point(h, temp_1.Height - 1), new Scalar(0), out rect, null, null, FloodFillFlags.Link8); Cv2.FloodFill(temp_1, new OpenCvSharp.Point(h, temp_1.Height - 1), new Scalar(0), out rect, null, null, FloodFillFlags.Link8); } } for (int y = 0; y < temp_1.Height; y++) { Vec4b vec4B_top = temp_1.At(y, 0); if (vec4B_top.Item3 > 0) { Cv2.FloodFill(rgb, new OpenCvSharp.Point(0, y), new Scalar(0), out rect, null, null, FloodFillFlags.Link8); Cv2.FloodFill(temp_1, new OpenCvSharp.Point(0, y), new Scalar(0), out rect, null, null, FloodFillFlags.Link8); } } for (int y = 0; y < temp_1.Height; y++) { byte vec4B_top = temp_1.At(y, temp_1.Width - 1); if (vec4B_top > 0) { Cv2.FloodFill(rgb, new OpenCvSharp.Point(temp_1.Width - 1, y), new Scalar(0), out rect, null, null, FloodFillFlags.Link8); Cv2.FloodFill(temp_1, new OpenCvSharp.Point(temp_1.Width - 1, y), new Scalar(0), out rect, null, null, FloodFillFlags.Link8); } } Mat[] mats = rgb.Split(); arr[0] = mats[0]; arr[1] = mats[1]; arr[2] = mats[2]; arr[3] = temp_1; Cv2.Merge(arr, src); return src; } /// /// 新孔洞填充,采用形态学填充 /// /// /// private static Mat HoleFilling_New(Mat src) { Mat[] arr2 = src.Split(); Mat matc = arr2[3]; //去掉透明层 Mat mat = src.CvtColor(ColorConversionCodes.BGRA2BGR); //填充孔洞 mat = BaseTools.FillHole(mat, new Scalar(255-vec4B.Item0, 255 - vec4B.Item1, 255 - vec4B.Item2)); matc = BaseTools.FillHole(matc, new Scalar(255)); //循环处理 mat.ForEachAsVec3b(RGBForEachAsByteForHoleFillingNew); Mat[] arr1 = mat.Split(); arr2[0] = arr1[0]; arr2[1] = arr1[1]; arr2[2] = arr1[2]; arr2[3] = matc; Cv2.Merge(arr2, src); return src; } private static void RGBForEachAsByteForHoleFillingNew(Vec3b* value, int* position) { int y = position[0]; int x = position[1]; if (value->Item0==255 && value->Item1==255 && value->Item2==255) { value->Item0 = vec4B.Item0; value->Item1 = vec4B.Item1; value->Item2 = vec4B.Item2; } } /// /// 新碎屑删除,使用连通分量找寻碎屑 /// /// /// private static Mat DebrisRemoval_New(Mat src) { Mat[] arr = src.Split(); rgb_ap = BaseTools.MergeMatFromMatArr(arr); temp_ap = arr[3].Clone(); //寻找连通分量 Mat labelMat = new Mat(); Mat stats = new Mat(); Mat centroids = new Mat(); int nonenum = Cv2.ConnectedComponentsWithStats(temp_ap, labelMat, stats, centroids, PixelConnectivity.Connectivity8); //寻找在范围内需要删除的碎屑 keyValuePairs.Clear(); for (int h = 1; h < centroids.Height; h++) { int areaField1 = stats.At(h, 4); if (debrisAreaStart <= areaField1 && areaField1 <= debrisAreaEnd) { keyValuePairs.Add(h, 1); } } labelMat.ForEachAsInt32(CommonForEachForInt32); //合并rgb和透明度 Mat[] mats = rgb_ap.Split(); arr[0] = mats[0]; arr[1] = mats[1]; arr[2] = mats[2]; arr[3] = temp_ap; Cv2.Merge(arr, src); return src; } #endregion #region 有视场 二值提取 /// /// 二值操作-二值提取-有视场 /// /// 视场mat /// 原图mat /// 参数列表 /// public static Mat ImageBinaryExtraction(Mat source, Mat mat, List lists, bool findContours = false) { //以下是参数信息--------------------- ///1个颜色区间还是2个 colorInterval = 1; //第一个二值区间 colorOneStart = 0; colorOneEnd = 0; //第二个二值区间 colorTwoStart = 0; colorTwoEnd = 0; //第三个二值区间 colorThreeStart = 0; colorThreeEnd = 0; //处理细节 //删除边界对象 bool deleteBoundaryObject = false; //孔洞填充 bool holeFilling = false; //碎屑删除 bool debrisRemoval = false; //碎屑删除面积起 debrisAreaStart = 0; //碎屑删除面积止 debrisAreaEnd = 0; //二值样式 //1实心 2边线 int binaryStyle = 0; //相颜色 Color phaseColor = Color.Red; //目标选择 int targetSelection = 1; for (int i = 0; i < lists.Count; i++) { Args args = lists[i]; switch (args.Key) { case "colorInterval": { if (args.Value is Boolean) colorInterval = (Boolean)args.Value ? 2 : 1; else colorInterval = (int)args.Value; } break; case "scope1": colorOneStart = (int)((List)args.Value)[0]; colorOneEnd = (int)((List)args.Value)[1]; break; case "scope2": colorTwoStart = (int)((List)args.Value)[0]; colorTwoEnd = (int)((List)args.Value)[1]; break; case "scope3": colorThreeStart = (int)((List)args.Value)[0]; colorThreeEnd = (int)((List)args.Value)[1]; break; case "deleteBoundaryObject": deleteBoundaryObject = (bool)args.Value; break; case "holeFilling": holeFilling = (bool)args.Value; break; case "debrisRemoval": debrisRemoval = (bool)args.Value; break; case "binaryStyle": binaryStyle = (int)args.Value; break; case "phaseColor": phaseColor = Color.FromArgb((int)args.Value); break; case "targetSelection": targetSelection = (int)args.Value; break; case "scope4": debrisAreaStart = (int)((List)args.Value)[0]; debrisAreaEnd = (int)((List)args.Value)[1]; break; default: break; } } vec4B.Item0 = phaseColor.B; vec4B.Item1 = phaseColor.G; vec4B.Item2 = phaseColor.R; vec4B.Item3 = phaseColor.A; //二值提取 Mat src = BinarizationWithView(source); //目标选择 if (targetSelection != 2) src = CalcContoursByTargetSelection_New(temp_view, targetSelection, source); //碎屑删除,参考冈萨雷斯,414,9.5.3,连通分量提取 if (debrisRemoval && debrisAreaEnd > 0) src = DebrisRemoval_NewWithView(src); //孔洞填充,参考冈萨雷斯,413,9.5.2 if (holeFilling) src = HoleFilling_NewWithView(src); //实心/边线,参考冈萨雷斯,412,9.5.1,边界提取 if (binaryStyle == 2) { Cv2.Erode(src, temp, null); src = src - temp; src = BaseTools.MergeMatFromMatArr(src, vec4B); } return src; } /// /// 二值提取 /// /// 视场图像 /// 原图像 /// 1个或2个颜色区间 /// /// /// /// /// /// /// 相颜色 /// private static Mat BinarizationWithView(Mat source) { Mat[] arr = source.Split(); alpha = arr[3]; Mat srcGray; if (source.Type() == MatType.CV_8UC1) { srcGray = source; } else { srcGray = source.CvtColor(ColorConversionCodes.BGR2GRAY); } temp_view = new Mat(source.Rows, source.Cols, MatType.CV_8UC4, new Scalar(0, 0, 0, 0)); temp = new Mat(source.Rows, source.Cols, MatType.CV_8UC4, new Scalar(0, 0, 0, 0)); srcGray.ForEachAsByte(GrayForEachAsByteWithView); return temp.Clone(); } /// /// 并行循环 /// /// /// private static void GrayForEachAsByteWithView(byte* value, int* position) { int y = position[0]; int x = position[1]; byte v = *value; if (colorInterval == 1) { if (v >= colorOneStart && v <= colorOneEnd) { if(alpha.At(y, x) > 0) { temp.Set(y, x, vec4B); temp_view.Set(y, x, vec4B); } else { temp_view.Set(y, x, new Vec4b(vec4B.Item0, vec4B.Item1, vec4B.Item2,0)); } } } else { if (((v >= colorTwoStart && v <= colorTwoEnd) || (v >= colorThreeStart && v <= colorThreeEnd))) { if (alpha.At(y, x) > 0) { temp.Set(y, x, vec4B); temp_view.Set(y, x, vec4B); } else { temp_view.Set(y, x, new Vec4b(vec4B.Item0, vec4B.Item1, vec4B.Item2, 0)); } } } } /// /// 新碎屑删除,使用连通分量找寻碎屑 /// /// /// private static Mat DebrisRemoval_NewWithView(Mat src) { Mat[] arr = src.Split(); rgb_ap = BaseTools.MergeMatFromMatArr(arr); temp_ap = arr[3].Clone(); //寻找连通分量 Mat labelMat = new Mat(); Mat stats = new Mat(); Mat centroids = new Mat(); int nonenum = Cv2.ConnectedComponentsWithStats(temp_ap, labelMat, stats, centroids, PixelConnectivity.Connectivity8); //寻找在范围内需要删除的碎屑 keyValuePairs.Clear(); for (int h = 1; h < centroids.Height; h++) { int areaField1 = stats.At(h, 4); if (debrisAreaStart <= areaField1 && areaField1 <= debrisAreaEnd) { keyValuePairs.Add(h, 1); } } labelMat.ForEachAsInt32(CommonForEachForInt32); //合并rgb和透明度 Mat[] mats = rgb_ap.Split(); arr[0] = mats[0]; arr[1] = mats[1]; arr[2] = mats[2]; arr[3] = temp_ap; Cv2.Merge(arr, src); return src; } /// /// 新孔洞填充,采用形态学填充 /// /// /// private static Mat HoleFilling_NewWithView(Mat src) { Mat[] arr2 = src.Split(); Mat tempc = arr2[3]; //去掉透明层 Mat mat = src.CvtColor(ColorConversionCodes.BGRA2BGR); //填充孔洞 mat = BaseTools.FillHole(mat, new Scalar(255 - vec4B.Item0, 255 - vec4B.Item1, 255 - vec4B.Item2)); tempc = BaseTools.FillHole(tempc, new Scalar(255)); //循环处理 mat.ForEachAsVec3b(RGBForEachAsByteForHoleFillingNewWithView); Mat[] arr1 = mat.Split(); arr2[0] = arr1[0]; arr2[1] = arr1[1]; arr2[2] = arr1[2]; arr2[3] = tempc; Cv2.Merge(arr2, src); return src; } private static void RGBForEachAsByteForHoleFillingNewWithView(Vec3b* value, int* position) { int y = position[0]; int x = position[1]; if (value->Item0 == 255 && value->Item1 == 255 && value->Item2 == 255) { value->Item0 = vec4B.Item0; value->Item1 = vec4B.Item1; value->Item2 = vec4B.Item2; } } /// /// 目标选择 /// /// /// /// /// /// public static Mat CalcContoursByTargetSelection_New(Mat src, int targetSelection, Mat source) { Mat[] arr = src.Split(); alpha = arr[3].Clone(); Mat rgb = BaseTools.MergeMatFromMatArr(arr); Mat alphatemp = arr[3].Clone(); Mat mask = Mat.Zeros(rgb.Rows + 2, rgb.Cols + 2, MatType.CV_8UC1); //考虑循环填充,填充视场的透明层和原图的透明层,然后比较填充区域的大小 //但是循环的时候需要少循环像素,可以处理视场的透明层,减少循环量 //同时记录种子点,然后根据targetSelection判断是填充透明还是不透明 Mat rgbClone = rgb.Clone(); List points = new List(); for(int h=0; h< alphatemp.Height; h++) { for (int w = 0; w < alphatemp.Width; w++) { int v = alphatemp.At(h, w); if(v>0) { int a = Cv2.FloodFill(alphatemp, new OpenCvSharp.Point(w, h), new Scalar(0), out rect, null, null, FloodFillFlags.Link8); int b = Cv2.FloodFill(rgbClone, new OpenCvSharp.Point(w, h), new Scalar(0, 0, 0), out rect, null, null, FloodFillFlags.Link8); if(a !=b) { points.Add(new OpenCvSharp.Point(w, h)); } } } } if(points.Count>0) { foreach(OpenCvSharp.Point p in points) { if(targetSelection == 1) { Cv2.FloodFill(alpha, p, new Scalar(0), out rect, null, null, FloodFillFlags.Link8); } else if(targetSelection == 3) { Cv2.FloodFill(rgb, mask, p, new Scalar(vec4B.Item0, vec4B.Item1, vec4B.Item2), out rect, null, null, FloodFillFlags.Link8); } } } if (targetSelection == 3) { Mat t = new Mat(mask, new Rect(1, 1, rgb.Width, rgb.Height)); alpha = alpha + t * 255; } //合并rgb和透明度 Mat[] mats = rgb.Split(); arr[0] = mats[0]; arr[1] = mats[1]; arr[2] = mats[2]; arr[3] = alpha; Cv2.Merge(arr, src); return src; } #endregion private static void CommonForEachForInt32(int* value, int* position) { int y = position[0]; int x = position[1]; int v = *value; if (v > 0 && keyValuePairs.ContainsKey(v)) { rgb_ap.Set(y, x, new Vec3b(0, 0, 0)); temp_ap.Set(y, x, 0); } } } }