using OpenCvSharp; using OpenCvSharp.XImgProc; using PaintDotNet.Adjust.BaseImage; using PaintDotNet.Base; using PaintDotNet.Base.Enum; using PaintDotNet.Base.Functionodel; using PaintDotNet.Base.CommTool; using System; using System.Collections.Generic; using System.Linq; using System.Windows.Forms; namespace PaintDotNet.Adjust { /// /// 形态学处理 /// public class MorphologyIntent { #region 腐蚀 /// /// 腐蚀 /// /// /// /// public static Mat ImageErosion(Mat mat, List lists) { int count = 1; Structure structure = Structure.horizon; for (int i = 0; i < lists.Count; i++) { Args args = lists[i]; switch (args.Key) { case "Count": count = int.Parse(args.Value.ToString()); break; case "Structures": structure = (Structure)args.Value; break; default: break; } } Mat element = null; switch (structure) { case Structure.horizon: InputArray kernel1 = InputArray.Create(new int[1, 3] { { 1, 1, 1 } }); element = kernel1.GetMat(); break; case Structure.angle45: InputArray kernel2 = InputArray.Create(new int[3, 3] { { 0, 0, 1 }, { 0, 1, 0 }, { 1, 0, 0 } }); element = kernel2.GetMat(); break; case Structure.vertical: InputArray kernel3 = InputArray.Create(new int[3, 1] { { 1 }, { 1 }, { 1 } }); element = kernel3.GetMat(); break; case Structure.angle135: InputArray kernel4 = InputArray.Create(new int[3, 3] { { 1, 0, 0 }, { 0, 1, 0 }, { 0, 0, 1 } }); element = kernel4.GetMat(); break; case Structure.cross: element = Cv2.GetStructuringElement(MorphShapes.Cross, new Size(3, 3)); break; case Structure.square: element = Cv2.GetStructuringElement(MorphShapes.Rect, new Size(3, 3)); break; case Structure.octagon: InputArray kernel7 = InputArray.Create(new int[7, 7] { { 0, 0, 1, 1, 1, 0, 0 }, { 0, 1, 1, 1, 1, 1, 0 }, { 1, 1, 1, 1, 1, 1, 1 }, { 1, 1, 1, 1, 1, 1, 1 }, { 1, 1, 1, 1, 1, 1, 1 }, { 0, 1, 1, 1, 1, 1, 0 }, { 0, 0, 1, 1, 1, 0, 0 } }); element = kernel7.GetMat(); break; } if (structure == Structure.conventional) { Cv2.Erode(mat, mat, null, null, count); } else { element.ConvertTo(element, MatType.CV_8UC1); Cv2.Erode(mat, mat, element, null, count, BorderTypes.Constant); if (element != null) { element.Dispose(); } } return mat; } #endregion #region 膨胀 /// /// 膨胀 /// /// /// /// public static Mat ImageDilation(Mat mat, List lists) { int count = 1; Structure structure = Structure.horizon; for (int i = 0; i < lists.Count; i++) { Args args = lists[i]; switch (args.Key) { case "Count": count = int.Parse(args.Value.ToString()); break; case "Structures": structure = (Structure)args.Value; break; default: break; } } Mat element = null; switch (structure) { case Structure.horizon: InputArray kernel1 = InputArray.Create(new int[1, 3] { { 1, 1, 1 } }); element = kernel1.GetMat(); break; case Structure.angle45: InputArray kernel2 = InputArray.Create(new int[3, 3] { { 0, 0, 1 }, { 0, 1, 0 }, { 1, 0, 0 } }); element = kernel2.GetMat(); break; case Structure.vertical: InputArray kernel3 = InputArray.Create(new int[3, 1] { { 1 }, { 1 }, { 1 } }); element = kernel3.GetMat(); break; case Structure.angle135: InputArray kernel4 = InputArray.Create(new int[3, 3] { { 1, 0, 0 }, { 0, 1, 0 }, { 0, 0, 1 } }); element = kernel4.GetMat(); break; case Structure.cross: element = Cv2.GetStructuringElement(MorphShapes.Cross, new Size(3, 3)); break; case Structure.square: element = Cv2.GetStructuringElement(MorphShapes.Rect, new Size(3, 3)); break; case Structure.octagon: InputArray kernel7 = InputArray.Create(new int[7, 7] { { 0, 0, 1, 1, 1, 0, 0 }, { 0, 1, 1, 1, 1, 1, 0 }, { 1, 1, 1, 1, 1, 1, 1 }, { 1, 1, 1, 1, 1, 1, 1 }, { 1, 1, 1, 1, 1, 1, 1 }, { 0, 1, 1, 1, 1, 1, 0 }, { 0, 0, 1, 1, 1, 0, 0 } }); element = kernel7.GetMat(); break; } if (structure == Structure.conventional) { Cv2.Dilate(mat, mat, null, null, count); } else { element.ConvertTo(element, MatType.CV_8UC1); Cv2.Dilate(mat, mat, element, null, count, BorderTypes.Constant); } return mat; } #endregion #region 开运算 /// /// 开运算 /// /// /// /// public static Mat ImageOpen(Mat mat, List lists) { int count = 1; Structure structure = Structure.horizon; for (int i = 0; i < lists.Count; i++) { Args args = lists[i]; switch (args.Key) { case "Count": count = int.Parse(args.Value.ToString()); break; case "Structures": structure = (Structure)args.Value; break; default: break; } } Mat element = null; switch (structure) { case Structure.horizon: InputArray kernel1 = InputArray.Create(new int[1, 3] { { 1, 1, 1 } }); element = kernel1.GetMat(); break; case Structure.angle45: InputArray kernel2 = InputArray.Create(new int[3, 3] { { 0, 0, 1 }, { 0, 1, 0 }, { 1, 0, 0 } }); element = kernel2.GetMat(); break; case Structure.vertical: InputArray kernel3 = InputArray.Create(new int[3, 1] { { 1 }, { 1 }, { 1 } }); element = kernel3.GetMat(); break; case Structure.angle135: InputArray kernel4 = InputArray.Create(new int[3, 3] { { 1, 0, 0 }, { 0, 1, 0 }, { 0, 0, 1 } }); element = kernel4.GetMat(); break; case Structure.cross: element = Cv2.GetStructuringElement(MorphShapes.Cross, new Size(3, 3)); break; case Structure.square: element = Cv2.GetStructuringElement(MorphShapes.Rect, new Size(3, 3)); break; case Structure.octagon: InputArray kernel7 = InputArray.Create(new int[7, 7] { { 0, 0, 1, 1, 1, 0, 0 }, { 0, 1, 1, 1, 1, 1, 0 }, { 1, 1, 1, 1, 1, 1, 1 }, { 1, 1, 1, 1, 1, 1, 1 }, { 1, 1, 1, 1, 1, 1, 1 }, { 0, 1, 1, 1, 1, 1, 0 }, { 0, 0, 1, 1, 1, 0, 0 } }); element = kernel7.GetMat(); break; } if (structure == Structure.conventional) { Cv2.MorphologyEx(mat, mat, MorphTypes.Open, null, null, count, BorderTypes.Constant); } else { element.ConvertTo(element, MatType.CV_8UC1); Cv2.MorphologyEx(mat, mat, MorphTypes.Open, element, null, count, BorderTypes.Constant); if (element != null) { element.Dispose(); } } return mat; } #endregion #region 闭运算 /// /// 闭运算 /// /// /// /// public static Mat ImageClose(Mat mat, List lists) { int count = 1; Structure structure = Structure.horizon; for (int i = 0; i < lists.Count; i++) { Args args = lists[i]; switch (args.Key) { case "Count": count = int.Parse(args.Value.ToString()); break; case "Structures": structure = (Structure)args.Value; break; default: break; } } Mat element = null; switch (structure) { case Structure.horizon: InputArray kernel1 = InputArray.Create(new int[1, 3] { { 1, 1, 1 } }); element = kernel1.GetMat(); break; case Structure.angle45: InputArray kernel2 = InputArray.Create(new int[3, 3] { { 0, 0, 1 }, { 0, 1, 0 }, { 1, 0, 0 } }); element = kernel2.GetMat(); break; case Structure.vertical: InputArray kernel3 = InputArray.Create(new int[3, 1] { { 1 }, { 1 }, { 1 } }); element = kernel3.GetMat(); break; case Structure.angle135: InputArray kernel4 = InputArray.Create(new int[3, 3] { { 1, 0, 0 }, { 0, 1, 0 }, { 0, 0, 1 } }); element = kernel4.GetMat(); break; case Structure.cross: element = Cv2.GetStructuringElement(MorphShapes.Cross, new Size(3, 3)); break; case Structure.square: element = Cv2.GetStructuringElement(MorphShapes.Rect, new Size(3, 3)); break; case Structure.octagon: InputArray kernel7 = InputArray.Create(new int[7, 7] { { 0, 0, 1, 1, 1, 0, 0 }, { 0, 1, 1, 1, 1, 1, 0 }, { 1, 1, 1, 1, 1, 1, 1 }, { 1, 1, 1, 1, 1, 1, 1 }, { 1, 1, 1, 1, 1, 1, 1 }, { 0, 1, 1, 1, 1, 1, 0 }, { 0, 0, 1, 1, 1, 0, 0 } }); element = kernel7.GetMat(); break; } if (structure == Structure.conventional) { Cv2.MorphologyEx(mat, mat, MorphTypes.Close, null, null, count, BorderTypes.Constant); } else { element.ConvertTo(element, MatType.CV_8UC1); Cv2.MorphologyEx(mat, mat, MorphTypes.Close, element, null, count, BorderTypes.Constant); if (element != null) { element.Dispose(); } } return mat; } #endregion #region 粗化/细化 private static int GetArgCount(List lists) { for (int i = 0; i < lists.Count; i++) { Args args = lists[i]; if (args.Key == "Count") { return int.Parse(args.Value.ToString()); } } return 0; } /// /// 粗化,需要二值化的图像 /// /// /// /// public static Mat Thickening(Mat mat, List lists, int color) { System.Drawing.Color color1 = System.Drawing.Color.FromArgb(color); Mat dstC1 = null; Mat dstC4 = null; Mat scrC1 = null; try { scrC1 = new Mat(mat.Size(), MatType.CV_8UC1); for (int o = 0; o < scrC1.Height; o++) for (int p = 0; p < scrC1.Width; p++) { var v = mat.At(o, p).Item3; if (v == 0) { scrC1.Set(o, p, 255); } else { scrC1.Set(o, p, 0); } } //扩充图像边界,方便处理,最后在统一截掉 var times = GetArgCount(lists);//细化执行次数 Thinning(scrC1, out dstC1, times); dstC4 = new Mat(mat.Size(), OpenCvSharp.MatType.CV_8UC4); for (int o = 0; o < dstC1.Height; o++) { for (int p = 0; p < dstC1.Width; p++) { byte v = dstC1.At(o, p); if (v > 0) { dstC4.Set(o, p, new Vec4b(0, 0, 0, 0)); } else { dstC4.Set(o, p, new Vec4b(color1.B, color1.G, color1.R, 255)); } } } dstC4.CopyTo(mat); return mat; } catch (Exception) { return mat; } finally { if (dstC1 != null && !dstC1.IsDisposed) dstC1.Dispose(); if (scrC1 != null && !scrC1.IsDisposed) scrC1.Dispose(); if (dstC4 != null && !dstC4.IsDisposed) dstC4.Dispose(); GC.Collect(); } } /// /// 细化,需要黑白二值图 /// /// /// /// public static Mat Thinning(Mat mat, List lists, int color) { System.Drawing.Color color1 = System.Drawing.Color.FromArgb(color); Mat dstC4 = null; Mat dstC1 = null; Mat scrC1 = null; try { scrC1 = new Mat(mat.Size(), MatType.CV_8UC1); for (int o = 0; o < scrC1.Height; o++) for (int p = 0; p < scrC1.Width; p++) { var v = mat.At(o, p).Item3; if (v > 0) { scrC1.Set(o, p, 255); } else { scrC1.Set(o, p, 0); } } var times = GetArgCount(lists);//细化执行次数 Thinning(scrC1, out dstC1, times); dstC4 = new Mat(mat.Size(), OpenCvSharp.MatType.CV_8UC4); for (int o = 0; o < mat.Height; o++) { for (int p = 0; p < mat.Width; p++) { byte v = dstC1.At(o, p); if (v > 0) { dstC4.Set(o, p, new Vec4b(color1.B, color1.G, color1.R, 255)); } else { dstC4.Set(o, p, new Vec4b(0, 0, 0, 0)); } } } dstC4.CopyTo(mat); return mat; } catch (Exception) { return mat; } finally { if (dstC4 != null && !dstC4.IsDisposed) dstC4.Dispose(); if (dstC1 != null && !dstC1.IsDisposed) dstC1.Dispose(); if (scrC1 != null && !scrC1.IsDisposed) scrC1.Dispose(); } } /// /// /// /// Source 8-bit single-channel image, containing binary blobs, with blobs having /// Destination image of the same size and the same type as src. The function can private static unsafe int Thinning(Mat src, out Mat dst, int times = 0) { int i, j, n; int width, height; int count = 0; int edge = 2; //之所以减1,是方便处理8邻域,防止越界 int p2, p3, p4, p5, p6, p7, p8, p9; byte* img; bool ifEnd; var temp = new Mat(new OpenCvSharp.Size(src.Width + 2 * edge, src.Height + 2 * edge), MatType.CV_8U); Cv2.CopyMakeBorder(src, temp, edge, edge, edge, edge, BorderTypes.Constant, Scalar.All(255)); dst = temp; int step = (int)dst.Step(); int[] dir = new int[4] { -step, step, 1, -1 }; width = dst.Cols - 2; height = dst.Rows - 2; using (Mat tmpimg = new Mat()) { do { count++; //分四个子迭代过程,分别对应北,南,东,西四个边界点的情况 ifEnd = false; for (n = 0; n < 4; n++) { dst.CopyTo(tmpimg); img = tmpimg.DataPointer; for (i = 1; i < height; i++) { img += step; for (j = 1; j < width; j++) { byte* p = img + j; //如果p点是背景点或者且为方向边界点,依次为北南东西,继续循环 if (p[0] == 0 || p[dir[n]] > 0) continue; p2 = p[-step] > 0 ? 1 : 0; p3 = p[-step + 1] > 0 ? 1 : 0; p4 = p[1] > 0 ? 1 : 0; p5 = p[step + 1] > 0 ? 1 : 0; p6 = p[step] > 0 ? 1 : 0; p7 = p[step - 1] > 0 ? 1 : 0; p8 = p[-1] > 0 ? 1 : 0; p9 = p[-step - 1] > 0 ? 1 : 0; int adjsum; adjsum = p2 + p3 + p4 + p5 + p6 + p7 + p8 + p9; //判断是否是邻接点或孤立点,0,1分别对于那个孤立点和端点 if (adjsum > 1) { //8 simple判定 int is8simple = 1; if ((p2 == 0 && p6 == 0) && ((p9 == 1 || p8 == 1 || p7 == 1) && (p3 == 1 || p4 == 1 || p5 == 1))) is8simple = 0; else if ((p4 == 0 && p8 == 0) && ((p9 == 1 || p2 == 1 || p3 == 1) && (p5 == 1 || p6 == 1 || p7 == 1))) is8simple = 0; else if ((p8 == 0 && p2 == 0) && (p9 == 1 && (p3 == 1 || p4 == 1 || p5 == 1 || p6 == 1 || p7 == 1))) is8simple = 0; else if ((p4 == 0 && p2 == 0) && (p3 == 1 && (p5 == 1 || p6 == 1 || p7 == 1 || p8 == 1 || p9 == 1))) is8simple = 0; else if ((p8 == 0 && p6 == 0) && (p7 == 1 && (p3 == 9 || p2 == 1 || p3 == 1 || p4 == 1 || p5 == 1))) is8simple = 0; else if ((p4 == 0 && p6 == 0) && (p5 == 1 && (p7 == 1 || p8 == 1 || p9 == 1 || p2 == 1 || p3 == 1))) is8simple = 0; if (is8simple == 1) { dst.Set(i, j, 0); //满足删除条件,设置当前像素为0 ifEnd = true; } } } } } } while (ifEnd && (times == 0 || count < times));//已经没有可以细化的像素了,则退出迭代 } dst = new Mat(dst, new Rect(edge, edge, src.Width, src.Height)); return count; } /// /// OpenCV's Thinning /// private static Mat ThinningCv(Mat src) { Mat dst = new Mat(src.Size(), src.Type()); Mat[] arr = src.Split(); CvXImgProc.Thinning(arr[0], dst, ThinningTypes.ZHANGSUEN); return dst; } #endregion #region 划痕处理 & 污迹处理 /// /// 污迹处理 /// /// /// /// 除了需要修复的部分之外其他部分的像素值全部为0 /// public static Mat SmudgeTreatment(Mat mat, List lists, Mat mask) { //获取通道数量 int channels = mat.Channels(); //如果是四通道转成三通道 if (channels == 4) OpenCvSharp.Cv2.CvtColor(mat, mat, OpenCvSharp.ColorConversionCodes.BGRA2BGR); //处理因子 double inpaintRadius = 5.0; //读取参数信息 for (int i = 0; i < lists.Count; i++) { Args args = lists[i]; switch (args.Key) { case "InpaintRadius": inpaintRadius = double.Parse(args.Value.ToString()); break; } } Mat matCopy = mat.Clone(); //图像修复 Cv2.Inpaint(mat, mask, matCopy, inpaintRadius, InpaintMethod.Telea); //如果原图是四通道,将处理完之后mat转为四通道 if (channels == 4) OpenCvSharp.Cv2.CvtColor(matCopy, matCopy, OpenCvSharp.ColorConversionCodes.BGR2BGRA); return matCopy; } /// /// 划痕处理 /// /// /// /// 除了需要修复的部分之外其他部分的像素值全部为0 /// public static Mat ScratchTreatment(Mat mat, List lists, Mat mask) { //获取通道数量 int channels = mat.Channels(); //如果是四通道转成三通道 if (channels == 4) OpenCvSharp.Cv2.CvtColor(mat, mat, OpenCvSharp.ColorConversionCodes.BGRA2BGR); Mat matCopy = mat.Clone(); ////痕宽 //double inpaintRadius = 5.0; ////读取参数信息 //for (int i = 0; i < lists.Count; i++) //{ // Args args = lists[i]; // switch (args.Key) // { // case "InpaintRadius": // inpaintRadius = double.Parse(args.Value.ToString()); // break; // } //} //Cv2.ImShow("mat", mat); //图像修复 Cv2.Inpaint(mat, mask, matCopy/*, inpaintRadius*/, 5.0, InpaintMethod.Telea); //Cv2.ImShow("matCopy", matCopy); //如果原图是四通道,将处理完之后mat转为四通道 if (channels == 4) OpenCvSharp.Cv2.CvtColor(matCopy, matCopy, OpenCvSharp.ColorConversionCodes.BGR2BGRA); return matCopy; } #endregion #region 分水岭分割 /** the following constants are used to set bits corresponding to pixel types */ static byte MAXIMUM = (byte)1; // marks local maxima (irrespective of noise tolerance) static byte LISTED = (byte)2; // marks points currently in the list static byte PROCESSED = (byte)4; // marks points processed previously static byte MAX_AREA = (byte)8; // marks areas near a maximum, within the tolerance //static byte EQUAL = (byte)16; // marks contigous maximum points of equal level //static byte MAX_POINT = (byte)32; // marks a single point standing for a maximum static byte ELIMINATED = (byte)64; // marks maxima that have been eliminated before watershed /// /// 分水岭分割 /// /// /// /// public static Mat WatershedSegment(Mat mat, PhaseModel phase, List lists) { //中间变量 //Mat temp = new Mat(); int paramCount = 0; //读取参数信息 for (int i = 0; i < lists.Count; i++) { Args args = lists[i]; switch (args.Key) { case "Count": paramCount = int.Parse(args.Value.ToString()); break; } } //灰度图 Mat Inmat = phase.mat.CvtColor(ColorConversionCodes.BGR2GRAY); int width = Inmat.Width; int height = Inmat.Height; Mat distance_temp = new Mat(); //Cv2.ImShow("Inmat", Inmat); Cv2.DistanceTransform(Inmat, distance_temp, DistanceTypes.L2, DistanceMaskSize.Precise); //Mat dst_temp = new Mat(); //Cv2.Normalize(src_temp, dst_temp, 0, 255, NormTypes.MinMax); //Mat dst = new Mat(); //dst_temp.ConvertTo(dst, MatType.CV_8UC1); //Cv2.ImShow("DistanceTransform", dst); //getSortedMaxPoints............ BitMap2d ip = new BitMap2d(distance_temp); MaximunFinder maximunFinder = new MaximunFinder(ip); List maxPoints = maximunFinder.FindMaxima(); //AnalyzeAndMarkMaxima............. BitMap2d types = new BitMap2d(width, height, 0); float globalMin = float.MaxValue; float globalMax = float.MinValue; foreach (Int16DoubleWithValue item in maxPoints) { if (item.V < globalMin) globalMin = item.V; if (item.V > globalMax) globalMax = item.V; types.SetPixel(item.O, MAXIMUM); } int nMax = maxPoints.Count(); int[] pList = new int[width * height]; //here we enter points starting from a maximum int[] dirOffset = new int[] { -width, -width + 1, +1, +width + 1, +width, +width - 1, -1, -width - 1 }; float maxSortingError = (float)(1.1 * (Math.Sqrt(2) / 2));//sorted sequence may be inaccurate by this value float tolerance = (float)0.5;// 05; for (int iMax = nMax - 1; iMax >= 0; iMax--) { //process all maxima now, starting from the highest int offset0 = maxPoints[iMax].O; if (((byte)types.GetPixel(offset0) & PROCESSED) != 0) //this maximum has been reached from another one, skip it continue; float v0 = ip.GetPixel(offset0); Boolean sortingError = false; do { pList[0] = offset0; types.SetPixel(offset0, (byte)((byte)types.GetPixel(offset0) | LISTED)); //mark first point as equal height (to itself) and listed int listLen = 1; //number of elements in the list int listI = 0; //index of current element in the list sortingError = false; Boolean maxPossible = true; //it may be a true maximum do { //while neigbor list is not fully processed (to listLen) int offset = pList[listI]; int x = offset % width; int y = offset / width; Boolean isInner = (y != 0 && y != height - 1) && (x != 0 && x != width - 1); //not necessary, but faster than isWithin for (int d = 0; d < 8; d++) { //analyze all neighbors (in 8 directions) at the same level int offset2 = offset + dirOffset[d]; if ((isInner || isWithin(x, y, d, width, height)) && ((byte)types.GetPixel(offset2) & LISTED) == 0) { float v2 = ip.GetPixel(offset2); if (ip.GetPixel(offset2) <= 0) { continue; //ignore the background (non-particles) } if (((byte)types.GetPixel(offset2) & PROCESSED) != 0) { maxPossible = false; //we have reached a point processed previously, thus it is no maximum now break; } if (v2 > v0 + maxSortingError) { maxPossible = false; //we have reached a higher point, thus it is no maximum break; } else if (v2 >= v0 - tolerance) { if (v2 > v0) { sortingError = true; offset0 = offset2; v0 = v2; } pList[listLen] = offset2; listLen++; //we have found a new point within the tolerance types.SetPixel(offset2, (byte)((byte)types.GetPixel(offset2) | LISTED)); } } // if isWithin & not LISTED } // for directions d listI++; } while (listI < listLen); if (sortingError) { //if x0,y0 was not the true maximum but we have reached a higher one for (listI = 0; listI < listLen; listI++) types.SetPixel(pList[listI], (byte)0); //reset all points encountered, then retry } else { //...............................................................................// int resetMask = ~LISTED; for (listI = 0; listI < listLen; listI++) { int offset = pList[listI]; types.SetPixel(offset, (byte)((byte)types.GetPixel(offset) & resetMask)); //reset attributes no longer needed types.SetPixel(offset, (byte)((byte)types.GetPixel(offset) | PROCESSED)); //mark as processed if (maxPossible) { types.SetPixel(offset, (byte)((byte)types.GetPixel(offset) | MAX_AREA)); //reset attributes no longer needed } } // for listI } } while (sortingError); }// for all maxima iMax //if (nMax == 0) //no initial maxima at all? then consider all as 'within tolerance' // Arrays.fill(types, (byte)(PROCESSED | MAX_AREA)); //makeUEPs::: double threshold = 0.5; //make8bit............ double offset01 = globalMin - (globalMax - globalMin) * (1.0 / 253 / 2 - 1e-6); //everything above minValue should become >(byte)0 double factor = 253 / (globalMax - globalMin); BitMap2d pixels = new BitMap2d(width, height, 0); int dataLen = height * width; for (int offset = 0; offset < dataLen; offset++) { float rawValue = ip.GetPixel(offset); if (rawValue < threshold) { } else if (((byte)types.GetPixel(offset) & MAX_AREA) != 0) pixels.SetPixel(offset, (byte)255); //prepare watershed by setting "true" maxima+surroundings to 255 else { long v = (long)(1 + Math.Round((rawValue - offset01) * factor)); if (v < 1) pixels.SetPixel(offset, (byte)1); else if (v <= 254) pixels.SetPixel(offset, (byte)(v & 255)); else pixels.SetPixel(offset, (byte)254); } } //cleanupMaxima............ for (int iMax = nMax - 1; iMax >= 0; iMax--) { int offset0 = maxPoints[iMax].O; //type cast gets lower 32 bits where pixel offset is encoded if (((int)types.GetPixel(offset0) & (MAX_AREA | ELIMINATED)) != 0) continue; int level = (byte)pixels.GetPixel(offset0) & 255; int loLevel = level + 1; pList[0] = offset0; //we start the list at the current maximum types.SetPixel(offset0, (byte)((byte)types.GetPixel(offset0) | LISTED)); //mark first point as listed int listLen = 1; //number of elements in the list int lastLen = 1; int listI = 0; //index of current element in the list Boolean saddleFound = false; while (!saddleFound && loLevel > 0) { loLevel--; lastLen = listLen; //remember end of list for previous level listI = 0; //in each level, start analyzing the neighbors of all pixels do { //for all pixels listed so far int offset = pList[listI]; int x = offset % width; int y = offset / width; Boolean isInner = (y != 0 && y != height - 1) && (x != 0 && x != width - 1); //not necessary, but faster than isWithin for (int d = 0; d < 8; d++) { //analyze all neighbors (in 8 directions) at the same level int offset2 = offset + dirOffset[d]; if ((isInner || isWithin(x, y, d, width, height)) && ((byte)types.GetPixel(offset2) & LISTED) == 0) { if (((byte)types.GetPixel(offset2) & MAX_AREA) != 0 || ((((byte)types.GetPixel(offset2) & ELIMINATED) != 0) && ((byte)pixels.GetPixel(offset2) & 255) >= loLevel)) { saddleFound = true; //we have reached a point touching a "true" maximum... break; //...or a level not lower, but touching a "true" maximum } else if (((byte)pixels.GetPixel(offset2) & 255) >= loLevel && ((byte)types.GetPixel(offset2) & ELIMINATED) == 0) { pList[listLen] = offset2; listLen++; //we have found a new point to be processed types.SetPixel(offset2, (byte)((byte)types.GetPixel(offset2) | LISTED)); } } // if isWithin & not LISTED } // for directions d if (saddleFound) break; //no reason to search any further listI++; } while (listI < listLen); } // while !levelFound && loLevel>=0 for (listI = 0; listI < listLen; listI++) //reset attribute since we may come to this place again types.SetPixel(pList[listI], (byte)((byte)types.GetPixel(pList[listI]) & ~LISTED)); for (listI = 0; listI < lastLen; listI++) { //for all points higher than the level of the saddle point int offset = pList[listI]; pixels.SetPixel(offset, (byte)loLevel); //set pixel value to the level of the saddle point types.SetPixel(offset, (byte)((byte)types.GetPixel(offset) | ELIMINATED)); //mark as processed: there can't be a local maximum in this area } } // for all maxima iMax //watershedSegment int[] histogram = new int[256]; for (int v1 = 0; v1 < 255; v1++) { histogram[v1] = 0; } for (int offset = 0; offset < dataLen; offset++) { int v1 = (byte)pixels.GetPixel(offset) & 255; if (v1 > 0 && v1 < 255) { histogram[v1]++; } } int arraySize = width * height - histogram[0] - histogram[255]; int[] coordinates = new int[arraySize]; //from pixel coordinates, low bits x, high bits y int highestValue = 0; int maxBinSize = 0; int offset02 = 0; int[] levelStart = new int[256]; for (int v1 = 1; v1 < 255; v1++) { levelStart[v1] = offset02; offset02 += histogram[v1]; if (histogram[v1] > 0) highestValue = v1; if (histogram[v1] > maxBinSize) maxBinSize = histogram[v1]; } int[] levelOffset = new int[highestValue + 1]; for (int y = 0, i = 0; y < height; y++) { for (int x = 0; x < width; x++, i++) { int v1 = (byte)pixels.GetPixel(i) & 255; if (v1 > 0 && v1 < 255) { offset02 = levelStart[v1] + levelOffset[v1]; coordinates[offset02] = x + y * width; levelOffset[v1]++; } } //for x } //for y // Create an array of the points (pixel offsets) that we set to 255 in one pass. // If we remember this list we need not create a snapshot of the ImageProcessor. int[] setPointList = new int[Math.Min(maxBinSize, (width * height + 2) / 3)]; // now do the segmentation, starting at the highest level and working down. // At each level, dilate the particle (set pixels to 255), constrained to pixels // whose values are at that level and also constrained (by the fateTable) // to prevent features from merging. int[] table = makeFateTable(); //IJ.showStatus("Segmenting (Esc to cancel)"); /*final */ int[] directionSequence = new int[] { 7, 3, 1, 5, 0, 4, 2, 6 }; // diagonal directions first for (int level = highestValue; level >= 1; level--) { int remaining = histogram[level]; //number of points in the level that have not been processed int idle = 0; while (remaining > 0 && idle < 8) { int sumN = 0; int dIndex = 0; do { // expand each level in 8 directions int n = processLevel(directionSequence[dIndex % 8], pixels, table, levelStart[level], remaining, coordinates, setPointList, width, height); //IJ.log("level="+level+" direction="+directionSequence[dIndex%8]+" remain="+remaining+"-"+n); remaining -= n; // number of points processed sumN += n; if (n > 0) idle = 0; // nothing processed in this direction? dIndex++; } while (remaining > 0 && idle++ < 8); } if (remaining > 0 && level > 1) { // any pixels that we have not reached? int nextLevel = level; // find the next level to process do nextLevel--; while (nextLevel > 1 && histogram[nextLevel] == 0); // in principle we should add all unprocessed pixels of this level to the // tasklist of the next level. This would make it very slow for some images, // however. Thus we only add the pixels if they are at the border (of the // image or a thresholded area) and correct unprocessed pixels at the very // end by CleanupExtraLines if (nextLevel > 0) { int newNextLevelEnd = levelStart[nextLevel] + histogram[nextLevel]; for (int i = 0, p = levelStart[level]; i < remaining; i++, p++) { int pOffset = coordinates[p]; int x = pOffset % width; int y = pOffset / width; //if ((pixels[pOffset] & 255) == 255) IJ.log("ERROR"); Boolean addToNext = false; if (x == 0 || y == 0 || x == width - 1 || y == height - 1) addToNext = true; //image border else for (int d = 0; d < 8; d++) if (isWithin(x, y, d, width, height) && pixels.GetPixel(pOffset + dirOffset[d]) == 0) { addToNext = true; //border of area below threshold break; } if (addToNext) coordinates[newNextLevelEnd++] = pOffset; } //tasklist for the next level to process becomes longer by this: histogram[nextLevel] = newNextLevelEnd - levelStart[nextLevel]; } } } System.Drawing.Color color = System.Drawing.Color.FromArgb(phase.color); Mat matPixels = OpenCvSharp.Extensions.BitmapConverter.ToMat(pixels.MakeBmp()); for (int h = 0; h < matPixels.Height; h++) { for (int w = 0; w < matPixels.Width; w++) { if (matPixels.At(h, w) == 0) matPixels.Set(h, w, new Vec4b(color.B, color.G, color.R, 255)); else matPixels.Set(h, w, new Vec4b(0, 0, 0, 0)); } } return matPixels; } /** returns whether the neighbor in a given direction is within the image * NOTE: it is assumed that the pixel x,y itself is within the image! * Uses class variables width, height: dimensions of the image * @param x x-coordinate of the pixel that has a neighbor in the given direction * @param y y-coordinate of the pixel that has a neighbor in the given direction * @param direction the direction from the pixel towards the neighbor (see makeDirectionOffsets) * @return true if the neighbor is within the image (provided that x, y is within) */ public static Boolean isWithin(int x, int y, int direction, int width, int height) { int xmax = width - 1; int ymax = height - 1; switch (direction) { case 0: return (y > 0); case 1: return (x < xmax && y > 0); case 2: return (x < xmax); case 3: return (x < xmax && y < ymax); case 4: return (y < ymax); case 5: return (x > 0 && y < ymax); case 6: return (x > 0); case 7: return (x > 0 && y > 0); } return false; //to make the compiler happy :-) } // isWithin /** dilate the UEP on one level by one pixel in the direction specified by step, i.e., set pixels to 255 * @param pass gives direction of dilation, see makeFateTable * @param ip the EDM with the segmeted blobs successively getting set to 255 * @param table The fateTable * @param levelStart offsets of the level in pixelPointers[] * @param levelNPoints number of points in the current level * @param pixelPointers[] list of pixel coordinates (x+y*width) sorted by level (in sequence of y, x within each level) * @param xCoordinates list of x Coorinates for the current level only (no offset levelStart) * @return number of pixels that have been changed */ public static int processLevel(int pass, BitMap2d pixels, int[] fateTable, int levelStart, int levelNPoints, int[] coordinates, int[] setPointList, int width, int height) { int xmax = width - 1; int ymax = height - 1; //byte[] pixels = (byte[])ip.getPixels(); ////byte[] pixels2 = (byte[])ip2.getPixels(); int nChanged = 0; int nUnchanged = 0; for (int i = 0, p = levelStart; i < levelNPoints; i++, p++) { int offset = coordinates[p]; int x = offset % width; int y = offset / width; int index = 0; //neighborhood pixel ocupation: index in fateTable if (y > 0 && ((byte)pixels.GetPixel(offset - width) & 255) == 255) index ^= 1; if (x < xmax && y > 0 && ((byte)pixels.GetPixel(offset - width + 1) & 255) == 255) index ^= 2; if (x < xmax && ((byte)pixels.GetPixel(offset + 1) & 255) == 255) index ^= 4; if (x < xmax && y < ymax && ((byte)pixels.GetPixel(offset + width + 1) & 255) == 255) index ^= 8; if (y < ymax && ((byte)pixels.GetPixel(offset + width) & 255) == 255) index ^= 16; if (x > 0 && y < ymax && ((byte)pixels.GetPixel(offset + width - 1) & 255) == 255) index ^= 32; if (x > 0 && ((byte)pixels.GetPixel(offset - 1) & 255) == 255) index ^= 64; if (x > 0 && y > 0 && ((byte)pixels.GetPixel(offset - width - 1) & 255) == 255) index ^= 128; int mask = 1 << pass; if ((fateTable[index] & mask) == mask) setPointList[nChanged++] = offset; //remember to set pixel to 255 else coordinates[levelStart + (nUnchanged++)] = offset; //keep this pixel for future passes } // for pixel i //IJ.log("pass="+pass+", changed="+nChanged+" unchanged="+nUnchanged); for (int i = 0; i < nChanged; i++) pixels.SetPixel(setPointList[i], (byte)255); return nChanged; } //processLevel static public int[] makeFateTable() { int[] table = new int[256]; Boolean[] isSet = new Boolean[8]; for (int item = 0; item < 256; item++) { //dissect into pixels for (int i = 0, mask = 1; i < 8; i++) { isSet[i] = (item & mask) == mask; mask *= 2; } for (int i = 0, mask = 1; i < 8; i++) { //we dilate in the direction opposite to the direction of the existing neighbors if (isSet[(i + 4) % 8]) table[item] |= mask; mask *= 2; } for (int i = 0; i < 8; i += 2) //if side pixels are set, for counting transitions it is as good as if the adjacent edges were also set if (isSet[i]) { isSet[(i + 1) % 8] = true; isSet[(i + 7) % 8] = true; } int transitions = 0; for (int i = 0; i < 8; i++) { if (isSet[i] != isSet[(i + 1) % 8]) transitions++; } if (transitions >= 4) { //if neighbors contain more than one region, dilation ito this pixel is forbidden table[item] = 0; } else { } } return table; } // int[] makeFateTable /// /// 分水岭分割 /// /// /// /// public static Mat WatershedSegment(Mat mat, List lists) { //中间变量 //Mat temp = new Mat(); int paramCount = 0; //读取参数信息 for (int i = 0; i < lists.Count; i++) { Args args = lists[i]; switch (args.Key) { case "Count": paramCount = int.Parse(args.Value.ToString()); break; } } //灰度图 Mat Inmat = mat.CvtColor(ColorConversionCodes.BGR2GRAY); //BGR Mat Src_3 = mat.CvtColor(ColorConversionCodes.BGRA2BGR);// .Clone(); //二值图 Mat mat_bw = GetBW(Inmat); //Cv2.ImShow("mat_bw00", mat_bw); Mat result = Oper_Deal(mat_bw, Src_3); //mat.CopyTo(temp); return result;// temp; } private static Mat Oper_Deal(Mat ImIn, Mat src_3) { #region 距离变换 Mat Im1 = new Mat(ImIn.Size(), ImIn.Type()); //归一化 Cv2.Normalize(ImIn, Im1, 0, 1, NormTypes.MinMax); Mat Im_dis = new Mat(ImIn.Size(), MatType.CV_32FC1); Cv2.DistanceTransform(ImIn, Im_dis, DistanceTypes.L2, DistanceMaskSize.Precise); Cv2.Normalize(Im_dis, Im_dis, 0, 1, NormTypes.MinMax); Mat conv = new Mat(Im_dis.Size(), MatType.CV_8UC1); Im_dis.ConvertTo(conv, MatType.CV_8UC1); Cv2.Threshold(conv, conv, 0, 255, ThresholdTypes.Otsu | ThresholdTypes.Binary); #endregion #region 轮廓标记 Mat dis_8U = conv; //提取标记 Point[][] contours; HierarchyIndex[] hierarchy; Cv2.FindContours(dis_8U, out contours, out hierarchy, RetrievalModes.Tree, ContourApproximationModes.ApproxSimple, new Point()); #endregion #region 将标记存入markers,作分水岭注水点用 var markers = new Mat(ImIn.Size(), MatType.CV_32SC1, Scalar.All(0)); for (int i = 0; i < contours.Length; i++) { Cv2.DrawContours(markers, contours, i, Scalar.All(i + 1), 10, LineTypes.Link8, hierarchy); } #endregion Cv2.Watershed(src_3, markers); #region 分水岭结果图处理 //连通域为1,线条为0 var markers_ind = markers.GetGenericIndexer(); for (int i = 0; i < markers.Rows; i++) { for (int j = 0; j < markers.Cols; j++) { if (markers_ind[i, j] != -1) { markers_ind[i, j] = int.MaxValue; } else if (markers_ind[i, j] == -1) { markers_ind[i, j] = 0; } } } //结合初始二值图背景为0 var ImIn_ind = ImIn.GetGenericIndexer(); for (int i = 0; i < markers.Rows; i++) { for (int j = 0; j < markers.Cols; j++) { if (ImIn_ind[i, j] == 0) { markers_ind[i, j] = 0; } } } #endregion #region 连通域信息 Mat water = new Mat(markers.Size(), MatType.CV_8UC1); markers.ConvertTo(water, MatType.CV_8UC1); var water_ind = water.GetGenericIndexer(); Mat water_bw = new Mat(); Cv2.Threshold(water, water_bw, 0, 255, ThresholdTypes.Otsu); //连通图的信息获取 Mat labels = new Mat(); Mat stats = new Mat(); Mat centroids = new Mat(); int num = Cv2.ConnectedComponentsWithStats(water_bw, labels, stats, centroids); #endregion #region Vec3b[] color = new Vec3b[num + 1]; color[0] = new Vec3b(0, 0, 0); var stats_ind = stats.GetGenericIndexer(); Random rand = new Random(); byte ranB = (byte)rand.Next(100, 255); byte ranG = (byte)rand.Next(100, 255); byte ranR = (byte)rand.Next(100, 255); for (int i = 1; i < num; i++) { color[i] = new Vec3b(ranB, ranG, ranR); int S = stats_ind[i, 4]; if (S < 200) { color[i] = new Vec3b(0, 0, 0); } } #endregion #region mat填色 Mat final = new Mat(water.Size(), MatType.CV_8UC3); for (int i = 0; i < final.Rows; i++) { for (int j = 0; j < final.Cols; j++) { int label = labels.Get(i, j); final.Set(i, j, color[label]); } } #endregion return final; } private static Mat GetBW(Mat Src) { //(最大类间方差)二值化 //Mat result = Src.Threshold(109.242, 255, ThresholdTypes.Otsu); Mat result = Src.AdaptiveThreshold(255, AdaptiveThresholdTypes.MeanC, ThresholdTypes.Binary, 35, 5); //Cv2.ImShow("test", result); Cv2.WaitKey(); return result; } #endregion #region 去碎屑 /// /// 去碎屑(功能实际是颗粒筛选的功能) /// 需要传进来二值的图 /// /// /// /// public static Mat Debris(Mat mat, List lists, int color, double rule) { //相颜色 System.Drawing.Color color1 = System.Drawing.Color.FromArgb(color); //原始轮廓信息 OpenCvSharp.Point[][] contours; //轮廓的拓扑信息 HierarchyIndex[] hierachy; //筛选参数 FilterParameters filterParameters = FilterParameters.Area; //筛选范围 double min = 0, max = 0; //筛选单位 MeasurementUnit measurementUnit = MeasurementUnit.Pixel; //筛选单方式 FunctionParameters functionParameters = FunctionParameters.Choise; //边界保留 bool boundaryPreservation = false; //中间变量 Mat temp = new Mat(); //轮廓总面积 //double areas = 0; //删除的边界 List deleteIndexs = new List(); //读取参数信息 for (int i = 0; i < lists.Count; i++) { Args args = lists[i]; switch (args.Key) { case "FilterParameters": filterParameters = (FilterParameters)args.Value; break; case "Scope": min = ((List)args.Value)[0]; max = ((List)args.Value)[1]; break; case "UnitParameters": measurementUnit = (MeasurementUnit)args.Value; break; case "FunctionParameters": functionParameters = (FunctionParameters)args.Value; break; case "BoundaryPreservation": boundaryPreservation = (bool)args.Value; break; } } mat.CopyTo(temp); Cv2.FindContours(mat.CvtColor(ColorConversionCodes.BGR2GRAY), out contours, out hierachy, RetrievalModes.CComp, ContourApproximationModes.ApproxNone); //没有选中边界保留 if (!boundaryPreservation) { if (contours.Length > 0) { for (int i = 0; i < contours.Length; i++) { for (int y = 0; y < contours[i].Length; y++) { if (contours[i][y].X == 0 || contours[i][y].X == temp.Width - 1 || contours[i][y].Y == 0 || contours[i][y].Y == temp.Height - 1) { deleteIndexs.Add(i); List pointsTemp = new List(); RecursiveFindChildContours(contours.ToList(), hierachy, i, deleteIndexs); break; } } } } //用于绘制的轮廓 List drawContours = contours.ToList(); //循环处理轮廓,过滤到被删除的轮廓及其子轮廓 if (deleteIndexs.Count > 0) { drawContours.Clear(); for (int i = 0; i < contours.Length; i++) { if (!deleteIndexs.Exists(a => a == i))// && !deleteIndexs.Exists(a => a == hierachy[i].Parent) { drawContours.Add(contours[i]); } } } temp = new Mat(mat.Size(), mat.Type()); Cv2.FillPoly(temp, drawContours, new Scalar(color1.B, color1.G, color1.R, 255), LineTypes.Link8); } /*//计算总面积 foreach (Point[] points in contours) { areas += Math.Abs(Cv2.ContourArea(points)); } if (measurementUnit == MeasurementUnit.Micron) areas = areas * rule;*/ List> ps = new List>(); for (int i = 0; i < hierachy.Length; i++) { if (deleteIndexs.Exists(a => a == i)) continue; //计算面积 if (filterParameters == FilterParameters.Area) { double area = Math.Abs(Cv2.ContourArea(contours[i])); if (measurementUnit == MeasurementUnit.Micron) area = area * rule * rule; if (functionParameters == FunctionParameters.Remove) { if (area >= min && area <= max && hierachy[i].Parent == -1) { ps.Add(contours[i].ToList()); } } else if (functionParameters == FunctionParameters.Choise) { if (area <= min || area >= max && hierachy[i].Parent == -1) { ps.Add(contours[i].ToList()); } } } //计算面积比 else if (filterParameters == FilterParameters.AreaRatio) { double area = Math.Abs(Cv2.ContourArea(contours[i])); Point2f center; float radius; Cv2.MinEnclosingCircle(contours[i], out center, out radius); double areas = Math.PI * radius * radius; if (measurementUnit == MeasurementUnit.Micron) { area = area * rule * rule; areas = areas * rule * rule; } if (functionParameters == FunctionParameters.Remove) { if (area / areas > min && area / areas < max && hierachy[i].Parent == -1) { ps.Add(contours[i].ToList()); } } else if (functionParameters == FunctionParameters.Choise) { if (area / areas < min || area / areas > max && hierachy[i].Parent == -1) { ps.Add(contours[i].ToList()); } } } //计算宽高比 else if (filterParameters == FilterParameters.AspectRatio) { double area = BasicCalculationHelper.CalcAspectRatio(contours[i]); if (functionParameters == FunctionParameters.Remove) { if (area > min && area < max && hierachy[i].Parent == -1) { ps.Add(contours[i].ToList()); } } else if (functionParameters == FunctionParameters.Choise) { if (area < min || area > max && hierachy[i].Parent == -1) { ps.Add(contours[i].ToList()); } } } //计算最大卡规直径(长径) else if (filterParameters == FilterParameters.LongTrail) { double area = BasicCalculationHelper.CalcLongTrail(contours[i]) * 2; if (measurementUnit == MeasurementUnit.Micron) area = area * rule; if (functionParameters == FunctionParameters.Remove) { if (area > min && area < max && hierachy[i].Parent == -1) { ps.Add(contours[i].ToList()); } } else if (functionParameters == FunctionParameters.Choise) { if (area < min || area > max && hierachy[i].Parent == -1) { ps.Add(contours[i].ToList()); } } } } if (functionParameters == FunctionParameters.Choise) Cv2.FillPoly(temp, ps, Scalar.Red); else if (functionParameters == FunctionParameters.Remove) Cv2.FillPoly(temp, ps, new Scalar(0, 0, 0, 0)); return temp; } private static void RecursiveFindChildContours( List drawContours, HierarchyIndex[] hierachy, int position, List points ) { int m = 0; foreach (HierarchyIndex index in hierachy) { if (index.Parent == position) { points.Add(m); RecursiveFindChildContours(drawContours, hierachy, m, points); } m++; } } #endregion #region 孔洞删除 /// /// 孔洞删除,用其它颜色进行填充 /// /// /// /// public static Mat HoleRemoval(Mat mat, List lists, int pColor, double rule, out double outmin, out double outmax) { //原始轮廓信息 OpenCvSharp.Point[][] contours; //轮廓的拓扑信息 HierarchyIndex[] hierachy; //筛选参数 FilterParameters filterParameters = FilterParameters.Area; //筛选范围 double min = 0, max = 0; //输出的值 outmin = -1; outmax = -1; //筛选单位 MeasurementUnit measurementUnit = MeasurementUnit.Pixel; //孔洞颜色 int color = 0; //面积合计大小 //double areas = 0; //读取参数信息 for (int i = 0; i < lists.Count; i++) { Args args = lists[i]; switch (args.Key) { case "FilterParameters": filterParameters = (FilterParameters)args.Value; break; case "Scope": min = ((List)args.Value)[0]; max = ((List)args.Value)[1]; break; case "UnitParameters": measurementUnit = (MeasurementUnit)args.Value; break; case "HoleColor": color = (int)args.Value; break; } } System.Drawing.Color color1 = System.Drawing.Color.FromArgb(color); System.Drawing.Color color2 = System.Drawing.Color.FromArgb(pColor); Mat dst = new Mat(); mat.CopyTo(dst); Cv2.FindContours(dst.CvtColor(ColorConversionCodes.BGR2GRAY), out contours, out hierachy, RetrievalModes.Tree, ContourApproximationModes.ApproxNone); /*//计算总面积 foreach (Point[] points in contours) { areas += Math.Abs(Cv2.ContourArea(points)); } if (measurementUnit == MeasurementUnit.Micron) areas = areas * rule * rule;*/ List ints = new List(); List> ps = new List>(); List> ps1 = new List>(); for (int i = 0; i < hierachy.Length; i++) { if (hierachy[i].Parent > -1) { ps1.Add(contours[i].ToList()); //计算面积 if (filterParameters == FilterParameters.Area) { double area = Math.Abs(Cv2.ContourArea(contours[i])); //double area = BasicCalculationHelper.DecimalCeiling(Math.Abs(Cv2.ContourArea(contours[i]))); if (measurementUnit == MeasurementUnit.Micron) area = area * rule * rule; if (outmin == -1) { outmin = area; outmax = area; } else { if (outmin > area) outmin = BasicCalculationHelper.DecimalFloor(area); if (outmax < area) outmax = BasicCalculationHelper.DecimalCeiling(area); } if (area >= min && area <= max) { ints.Add(i); //ps.Add(contours[i].ToList()); } } //计算面积比 else if (filterParameters == FilterParameters.AreaRatio) { double area = Math.Abs(Cv2.ContourArea(contours[i])); Point2f point; float radius; Cv2.MinEnclosingCircle(contours[i], out point, out radius); double areas = Math.PI * radius * radius; if (outmin == -1) { outmin = area / areas; outmax = Math.Round(area / areas, 2); } else { if (outmin > area / areas) outmin = area / areas; if (outmax < area / areas) outmax = Math.Round(area / areas, 2); } if (area / areas >= min && Math.Round(area / areas, 2) <= max) { ints.Add(i); //ps.Add(contours[i].ToList()); } } //计算宽高比 else if (filterParameters == FilterParameters.AspectRatio) { double area = Math.Round(BasicCalculationHelper.CalcAspectRatio(contours[i]), 2); if (outmin == -1) { outmin = area; outmax = area; } else { if (outmin > area) outmin = area; if (outmax < area) outmax = area; } if (area >= min && area <= max) { ints.Add(i); //ps.Add(contours[i].ToList()); } } //计算最大卡规直径(长径) else if (filterParameters == FilterParameters.LongTrail) { double area = BasicCalculationHelper.CalcLongTrail(contours[i]) * 2; if (measurementUnit == MeasurementUnit.Micron) area = area * rule; if (outmin == -1) { outmin = area; outmax = area; } else { if (outmin > area) outmin = BasicCalculationHelper.DecimalFloor(area); if (outmax < area) outmax = BasicCalculationHelper.DecimalCeiling(area); } if (area >= min && area <= max) { ints.Add(i); //his.Add(hierachy[i]); //ps.Add(contours[i].ToList()); } } } } Cv2.FillPoly(mat, ps1, new Scalar(color1.B, color1.G, color1.R, color1.A)); for (int k = 0; k < ints.Count(); k++) { Cv2.DrawContours(mat, contours, ints[k], new Scalar(color2.B, color2.G, color2.R, 255), -1, LineTypes.Link8, hierachy, 0); } //foreach (List points in ps) //{ //Cv2.FillPoly(mat, InputArray.Create(ps[0]), new Scalar(color2.B, color2.G, color2.R, 255)); //} //Cv2.DrawContours(mat, ps, -1, new Scalar(color2.B, color2.G, color2.R, 255), -1, LineTypes.Link8, his, 1); //Cv2.FillPoly(mat, ps, new Scalar(color2.B, color2.G, color2.R, 255)); return mat; } #endregion #region 抽骨架 unsafe byte* pixels; int foreground; int width, height; int xMin, xMax, yMin, yMax; Mat dst; /// /// 抽骨架 /// 使用demo里面的form26里面的ImageJ查表法 /// /// /// /// public unsafe Mat ImageSkeleton(Mat mat, List lists, int color) { //如果不是单通道,转单通道 if (mat.Type() != MatType.CV_8UC1) { Cv2.CvtColor(mat, mat, ColorConversionCodes.BGR2GRAY); } //判断是否二值化图像 if (!BaseTools.DetermineBinaryImageByHist(mat)) { MessageBox.Show("Please enter abinarization image"); return mat; } //判断是否黑白的二值图 if (!BaseTools.DetermineBinaryImage(mat)) { //处理为黑/白二值化的图像 for (int o = 0; o < mat.Height; o++) { for (int p = 0; p < mat.Width; p++) { byte v = mat.At(o, p); if (v > 0) { mat.Set(o, p, 255); } } } } //背景颜色 int fg = 0; //前景颜色 foreground = 255 - fg; int pass = 0; int pixelsRemoved; dst = new Mat(); bool edgePixels = hasEdgePixels(mat); if (edgePixels) { dst = new Mat(new OpenCvSharp.Size(mat.Width + 2, mat.Height + 2), MatType.CV_8UC1, Scalar.All(0)); Cv2.CopyMakeBorder(mat, dst, 1, 1, 1, 1, BorderTypes.Constant, Scalar.All(0)); } else { mat.CopyTo(dst); } width = dst.Width; height = dst.Height; xMin = 1; xMax = width - 2; yMin = 1; yMax = height - 2; pixels = (byte*)dst.Data; do { pixelsRemoved = thin(pass++, BaseTools.table); pixelsRemoved += thin(pass++, BaseTools.table); } while (pixelsRemoved > 0); do { pixelsRemoved = thin(pass++, BaseTools.table2); pixelsRemoved += thin(pass++, BaseTools.table2); } while (pixelsRemoved > 0); BaseTools.shrink(mat, dst, edgePixels); System.Drawing.Color color1 = System.Drawing.Color.FromArgb(color); Mat temp1 = new Mat(mat.Size(), OpenCvSharp.MatType.CV_8UC4); for (int o = 0; o < mat.Height; o++) { for (int p = 0; p < mat.Width; p++) { byte v = dst.At(o, p); if (v > 0) { temp1.Set(o, p, new Vec4b(color1.B, color1.G, color1.R, 255)); } else { temp1.Set(o, p, new Vec4b(0, 0, 0, 0)); } } } temp1.CopyTo(mat); return mat; } bool hasEdgePixels(Mat ip) { int width = ip.Width; int height = ip.Height; bool edgePixels = false; for (int x = 0; x < width; x++) { // top edge if (ip.At(0, x) == foreground) edgePixels = true; } for (int x = 0; x < width; x++) { // bottom edge if (ip.At(height - 1, x) == foreground) edgePixels = true; } for (int y = 0; y < height; y++) { // left edge if (ip.At(y, 0) == foreground) edgePixels = true; } for (int y = 0; y < height; y++) { // right edge if (ip.At(y, width - 1) == foreground) edgePixels = true; } return edgePixels; } unsafe int thin(int pass, int[] table) { int p1, p2, p3, p4, p5, p6, p7, p8, p9; int bgColor = 0; //255 //if (parent.isInvertedLut()) // bgColor = 0; Mat temp = new Mat(); dst.CopyTo(temp); byte* pixels2 = (byte*)temp.Data; int v, index, code; int offset, rowOffset = temp.Width; int pixelsRemoved = 0; for (int y = yMin; y <= yMax; y++) { offset = xMin + y * temp.Width; for (int x = xMin; x <= xMax; x++) { p5 = pixels2[offset]; v = p5; if (v != bgColor) { p1 = pixels2[offset - rowOffset - 1]; p2 = pixels2[offset - rowOffset]; p3 = pixels2[offset - rowOffset + 1]; p4 = pixels2[offset - 1]; p6 = pixels2[offset + 1]; p7 = pixels2[offset + rowOffset - 1]; p8 = pixels2[offset + rowOffset]; p9 = pixels2[offset + rowOffset + 1]; index = 0; if (p1 != bgColor) index |= 1; if (p2 != bgColor) index |= 2; if (p3 != bgColor) index |= 4; if (p6 != bgColor) index |= 8; if (p9 != bgColor) index |= 16; if (p8 != bgColor) index |= 32; if (p7 != bgColor) index |= 64; if (p4 != bgColor) index |= 128; code = table[index]; if ((pass & 1) == 1) { //odd pass if (code == 2 || code == 3) { v = bgColor; pixelsRemoved++; } } else { //even pass if (code == 1 || code == 3) { v = bgColor; pixelsRemoved++; } } } pixels[offset++] = (byte)v; } } return pixelsRemoved; } #endregion #region 空洞填充 /// /// 孔洞填充 /// /// /// /// public static Mat HolesFill(Mat mat, int c) { Mat temp = null; try { //mat的颜色 System.Drawing.Color color = System.Drawing.Color.FromArgb(c); //原始轮廓信息 OpenCvSharp.Point[][] contours; //轮廓的拓扑信息 HierarchyIndex[] hierachy; //中间变量 temp = new Mat(); mat.CopyTo(temp); Cv2.FindContours(temp.CvtColor(ColorConversionCodes.BGRA2GRAY), out contours, out hierachy, RetrievalModes.Tree, ContourApproximationModes.ApproxNone); if (hierachy != null) { for (int i = 0; i < hierachy.Length; i++) { //如果需要还可以判断面积范围 if (hierachy[i].Parent > -1) { Vec3b vec3B = mat.At(contours[i][0].Y, contours[i][0].X); if ((vec3B.Item0 == (byte)color.B) && (vec3B.Item1 == (byte)color.G) && (vec3B.Item2 == (byte)(color.R))) { List> ps = new List>(); ps.Add(contours[i].ToList()); Cv2.FillPoly(mat, ps, new Scalar(color.B, color.G, color.R, 255)); } } } } } catch (Exception) { System.Windows.Forms.MessageBox.Show("Abnormal program"); } finally { if (temp != null && !temp.IsDisposed) temp.Dispose(); } return mat; } #endregion #region 形态学分割 public static Mat MorphologySegment(Mat mat, PhaseModel model, List lists) { //获取参数 int paramCount = 0; //读取参数信息 for (int i = 0; i < lists.Count; i++) { Args args = lists[i]; switch (args.Key) { case "Count": paramCount = int.Parse(args.Value.ToString()); break; } } //处理相 for (int h = 0; h < model.mat.Height; h++) { for (int w = 0; w < model.mat.Width; w++) { Vec4b vec4B = model.mat.At(h, w); if (vec4B.Item3 == 0) { model.mat.Set(h, w, new Vec4b(255, 255, 255, 255)); } else { model.mat.Set(h, w, new Vec4b(0, 0, 0, 255)); } } } //构造八边形的卷积核 InputArray kernel = InputArray.Create(new int[5, 5] { { 0, 1, 1, 1, 0 }, { 1, 1, 1, 1, 1 }, { 1, 1, 1, 1, 1 }, { 1, 1, 1, 1, 1 }, { 0, 1, 1, 1, 0 } }); Mat element = kernel.GetMat(); element.ConvertTo(element, MatType.CV_8UC1); Mat lab1Img = new Mat(); model.mat.CopyTo(lab1Img); //原图灰度化 Mat img = lab1Img.CvtColor(ColorConversionCodes.BGR2GRAY); //用来判断极限腐蚀是否完成的中间变量 Mat erode = new Mat(); img.CopyTo(erode); //用来观察腐蚀次数 int h1 = 0; Mat temp = new Mat(); //开始 while (Cv2.CountNonZero(erode) > 0) { //寻找并标记区域个数 Mat labelMat = new Mat(); Mat stats = new Mat(); Mat centroids = new Mat(); int num = Cv2.ConnectedComponentsWithStats(erode, labelMat, stats, centroids, PixelConnectivity.Connectivity8); //进行腐蚀 temp = erode.Erode(element, null, 1); //跳出循环的条件 int count = Cv2.CountNonZero(temp); //循环质心,和腐蚀后的图片进行比较,如果腐蚀后的图片质心为黑色了,那说明被腐蚀掉了,则在下面需要被还原回来 for (int h = 1; h < centroids.Height; h++) { double a = centroids.At(h, 0); double b = centroids.At(h, 1); OpenCvSharp.Point point = new OpenCvSharp.Point(a, b); //计算每个stats范围内的像素和是否为0 int x = stats.At(h, 0); int y = stats.At(h, 1); int width = stats.At(h, 2); int height = stats.At(h, 3); System.Console.WriteLine("x:" + x + " y:" + y); Rect roi1 = new Rect(x, y, width, height); Mat ImageROI1 = new Mat(temp, roi1); int cc1 = Cv2.CountNonZero(ImageROI1); //代表区域已经消失了,需要还原回来 if (cc1 == 0) { Rect roi = new Rect(x, y, width, height); Mat ImageROI = new Mat(erode, roi); Rect rect = new Rect(x, y, width, height); Mat pos = new Mat(temp, rect); ImageROI.CopyTo(pos); } } if (h1 == paramCount && paramCount > 0) break; temp.CopyTo(erode); h1++; if (count == 0) { break; } } Mat fffff = new Mat(); Mat labels = new Mat(); Cv2.DistanceTransformWithLabels(~temp, fffff, labels, DistanceTypes.L1, DistanceMaskSize.Precise); //labels.ConvertTo(labels, MatType.CV_8UC3); //找到轮廓 OpenCvSharp.Point[][] contours; HierarchyIndex[] hierachy; Cv2.FindContours(labels, out contours, out hierachy, RetrievalModes.CComp, ContourApproximationModes.ApproxNone); Mat lk = new Mat(labels.Size(), MatType.CV_8UC1, Scalar.All(255)); Cv2.DrawContours(lk, contours, -1, Scalar.All(0), 1, LineTypes.Link8, hierachy); //Cv2.MorphologyEx(img, img, MorphTypes.Open, element, null, 1); //Cv2.Dilate(img, img, element, null, 1); System.Drawing.Color color = System.Drawing.Color.FromArgb(model.color); for (int h = 0; h < lab1Img.Height; h++) { for (int w = 0; w < lab1Img.Width; w++) { if (lab1Img.At(h, w) == 0) lab1Img.Set(h, w, new Vec3b(color.B, color.G, color.R)); /*if (lab1Img.At(h, w) == 0 || lk.At(h, w) == 0) lab1Img.Set(h, w, new Vec4b(color.B, color.G, color.R, 255)); else lab1Img.Set(h, w, new Vec4b(0, 0, 0, 0));*/ } } for (int h = 0; h < lk.Height; h++) { for (int w = 0; w < lk.Width; w++) { if (lk.At(h, w) == 0) lab1Img.Set(h, w, new Vec3b(color.B, color.G, color.R)); } } Cv2.Erode(lab1Img, lab1Img, null, null, 1); Cv2.Dilate(lab1Img, lab1Img, null, null, 1); //Cv2.ImShow("dddd", lk); //Cv2.ImShow("粗化操作1", lab1Img); return lab1Img; } #endregion } public struct Int16DoubleWithValue : IComparable { public int O; public float V; public Int16DoubleWithValue(int offset, float value) { O = offset; V = value; } public int CompareTo(Int16DoubleWithValue other) { return this.V.CompareTo(other.V); } } public class BitMap2d { public float[] data; public int width; public int height; /// /// 初始化操作对象 /// /// /// /// 初始值 public BitMap2d(int width, int height, float v) { this.width = width; this.height = height; data = new float[width * height]; for (int i = 0; i < width * height; i++) data[i] = v; } /// /// 根据mat初始化 /// /// 初始EDM对象 public unsafe BitMap2d(Mat mat) { this.width = mat.Width; this.height = mat.Height; data = new float[width * height]; mat.ForEachAsFloat(FunctionForEachAsFloat); } private unsafe void FunctionForEachAsFloat(float* value, int* position) { data[position[0] * width + position[1]] = *value; } public void SetPixel(int x, int y, byte v) { data[x + y * width] = v; } public float GetPixel(int x, int y) { return data[x + y * width]; } public void SetPixel(int offset, byte v) { data[offset] = v; } public float GetPixel(int offset) { return data[offset]; } public System.Drawing.Bitmap MakeBmp() { float min = float.MaxValue; float max = float.MinValue; for (int i = 0; i < width; i++) { for (int j = 0; j < height; j++) { float r = this.GetPixel(i, j); if (r > max) max = r; if (r < min) min = r; } } float delta = max - min; System.Drawing.Bitmap bmp = new System.Drawing.Bitmap(this.width, this.height, System.Drawing.Imaging.PixelFormat.Format32bppArgb); for (int i = 0; i < width; i++) { for (int j = 0; j < height; j++) { float r = this.GetPixel(i, j); int b = 255 - (int)(255 * (r - min) / delta); System.Drawing.Color c = System.Drawing.Color.FromArgb((byte)b, (byte)b, (byte)b); bmp.SetPixel(i, j, c); } } return bmp; } } public class MaximunFinder { BitMap2d bmp; int width; int height; public MaximunFinder(BitMap2d bmp) { this.bmp = bmp; this.width = bmp.width; this.height = bmp.height; } public List FindMaxima() { List list = new List(); int offset = 0; for (int j = 0; j < height; j++) { for (int i = 0; i < width; i++, offset++) { if (IsMaxima(i, j, offset)) { list.Add(new Int16DoubleWithValue(offset, bmp.GetPixel(offset))); } } } list.Sort(); return list; } private bool IsMaxima(int i, int j, int offset) { float v = bmp.GetPixel(offset); if (v == 0) return false; bool b1 = v >= bmp.GetPixel(Math.Max(0, i - 1), Math.Max(0, j - 1)); bool b2 = v >= bmp.GetPixel(i, Math.Max(0, j - 1)); bool b3 = v >= bmp.GetPixel(Math.Min(width - 1, i + 1), Math.Max(0, j - 1)); bool b4 = v >= bmp.GetPixel(Math.Max(0, i - 1), j); bool b5 = v >= bmp.GetPixel(Math.Min(width - 1, i + 1), j); bool b6 = v >= bmp.GetPixel(Math.Max(0, i - 1), Math.Min(height - 1, j + 1)); bool b7 = v >= bmp.GetPixel(i, Math.Min(height - 1, j + 1)); bool b8 = v >= bmp.GetPixel(Math.Min(width - 1, i + 1), Math.Min(height - 1, j + 1)); return b1 && b2 && b3 && b4 && b5 && b6 && b7 && b8 && (v > 0); } } }