public void ApplyFilter(Mat src) { CvInvoke.CvtColor(src, src, ColorConversion.Bgr2Hsv); Mat threshold = new Mat(src.Height, src.Width, src.Depth, src.NumberOfChannels); MCvScalar min = new MCvScalar(m_hmin, m_smin, m_vmin); MCvScalar max = new MCvScalar(m_hmax, m_smax, m_vmax); CvInvoke.InRange(src, new ScalarArray(min), new ScalarArray(max), threshold); Mat element = CvInvoke.GetStructuringElement(ElementShape.Rectangle, new Size(3,3), Point.Empty); CvInvoke.Erode(threshold, threshold, element, Point.Empty, 1, BorderType.Constant, new MCvScalar(1.0f)); CvInvoke.Canny(threshold, threshold, 100, 255); VectorOfVectorOfPoint contours = new VectorOfVectorOfPoint(); Mat hierarchy = new Mat(); CvInvoke.FindContours(threshold, contours, hierarchy, RetrType.Tree, ChainApproxMethod.ChainApproxSimple, Point.Empty); Mat draw = new Mat(src.Height, src.Width, src.Depth, 1); draw.SetTo(new MCvScalar(0.0)); int i = 0; //Debug.Log("CONTOURS"); var contoursArray = contours.ToArrayOfArray(); foreach(Point[] contour in contoursArray) { CvInvoke.DrawContours(draw, contours, i, new MCvScalar(255.0), 1, LineType.EightConnected, null, int.MaxValue, Point.Empty); double a = CvInvoke.ContourArea(new VectorOfPoint(contour)); //Debug.Log("Contour: " + a); i++; } //Emgu.CV.UI.ImageViewer.Show(draw, "test"); if(m_onFrame != null) m_onFrame.Invoke(draw); }
public void Process(string imagePath) { var im = CvInvoke.Imread(imagePath, Emgu.CV.CvEnum.ImreadModes.Grayscale); Mat imx = new Mat(), th = new Mat(); CvInvoke.MedianBlur(im, imx, 3); CvInvoke.Resize(imx, th, new Size(50, 50), 0, 0, Inter.Area); CvInvoke.GaussianBlur(th, im, new Size(7, 7), 0); CvInvoke.Resize(im, th, imx.Size, 0, 0, Inter.Linear); CvInvoke.Compare(imx, th, im, CmpType.GreaterThan); var imrgb = new Mat(im.Rows, im.Cols, DepthType.Cv8U, 3); CvInvoke.CvtColor(im, imrgb, ColorConversion.Gray2Bgr); var contours = new Emgu.CV.Util.VectorOfVectorOfPoint(); CvInvoke.FindContours(im, contours, null, RetrType.Ccomp, ChainApproxMethod.ChainApproxSimple); var ca = contours.ToArrayOfArray(); var cells = new List <Cell>(); List <double> ca_aa = new List <double>(ca.Length); for (int i = 0; i < ca.Length; i++) { var c = new Cell(ca[i]); cells.Add(c); ca_aa.Add(c.sarea); } ca_aa.Sort(); double ca_max = 0, ca_min = 0; for (int i = 100; i >= 0 && ca_aa[i] / ca_aa[i + 1] < 1.01; i--) { ca_max = -ca_aa[i]; } for (int i = 100; i < ca_aa.Count && ca_aa[i] / ca_aa[i + 1] < 1.01; i++) { ca_min = -ca_aa[i]; } double side_avg = Math.Sqrt((ca_min + ca_max) / 2); List <Cell> gridCells = new List <Cell>(); for (int i = 0; i < ca.Length; i++) { var col = colors[i % colors.Count]; var c = cells[i]; if (c.area > ca_max || c.area < ca_min) { continue; } c.FindCenter(); double minR = c.sidelength / 2.25; //CvInvoke.Circle(imrgb, c.cp, (int)minR, col, 1); bool ok = true; var minR2 = minR * minR; for (int j = 0; j < c.ca.Length; j++) { if (c.Dist2(c.ca[j]) < minR2) { ok = false; } } //for (int j = 1; j < c.ca.Length; j++) // CvInvoke.Line(imrgb, c.ca[j - 1], c.ca[j], col, ok ? 9 : 3); if (ok) { gridCells.Add(c); } } double maxCentDist2 = 2 * side_avg * side_avg; for (int i = 0; i < gridCells.Count; i++) { var a = gridCells[i]; for (int j = 0; j < gridCells.Count; j++) { var b = gridCells[j]; if (i == j) { continue; } if (a.Dist2(b) < maxCentDist2) { var d = a.Diff(b); bool isX = Math.Abs(d.X) > Math.Abs(d.Y); Cell.Dir dir = isX ? d.X < 0 ? Cell.Dir.Left : Cell.Dir.Right : d.Y < 0 ? Cell.Dir.Up : Cell.Dir.Down; a.Link(b, dir); //CvInvoke.Line(imrgb, a.cp, b.cp, new MCvScalar(0, 255, isX ? 255 : 0), 3); } } } Queue <Cell> qc = new Queue <Cell>(); gridCells[0].SetGridIndex(new Point()); // Arbitrary Origo qc.Enqueue(gridCells[0]); while (qc.Count > 0) { var c = qc.Dequeue(); foreach (var nc in c.neighbours) { if (nc != null && !nc.hasGridIndex) { qc.Enqueue(nc); } } c.CalcNeighboursGridIndex(); } int gixr = (from c in gridCells select c.gi.X).Min(); int giyr = (from c in gridCells select c.gi.Y).Min(); foreach (var c in cells) { c.gi = new Point(c.gi.X - gixr, c.gi.Y - giyr); } gixr = (from c in gridCells select c.gi.X).Max() + 1; giyr = (from c in gridCells select c.gi.Y).Max() + 1; var gridEst = new GridEstimator(); foreach (var c in gridCells) { gridEst.Add(c.gi, c.cpf); } gridEst.Process(); for (int Xi = 0; Xi < gixr; Xi++) { Point p1 = gridEst.GetP(Xi + 0.5f, 0.5f); Point p2 = gridEst.GetP(Xi + 0.5f, giyr - 0.5f); CvInvoke.Line(imrgb, p1, p2, new MCvScalar(0, 100, 255), 5); } for (int Yi = 0; Yi < giyr; Yi++) { Point p1 = gridEst.GetP(0.5f, Yi + 0.5f); Point p2 = gridEst.GetP(gixr - 0.5f, Yi + 0.5f); CvInvoke.Line(imrgb, p1, p2, new MCvScalar(0, 100, 255), 5); } Cell[,] cg = new Cell[gixr, giyr]; foreach (var c in gridCells) { cg[c.gi.X, c.gi.Y] = c; } for (int xi = 0; xi < gixr; xi++) { for (int yi = 0; yi < giyr; yi++) { var c = cg[xi, yi]; if (c == null) { continue; } var col = colors[(xi + yi) % colors.Count]; double minR = c.sidelength / 2.25; //CvInvoke.Circle(imrgb, c.cp, (int)minR, col, 4); } } var sidesize = (int)(side_avg * 0.8); for (int xi = 0; xi < gixr; xi++) { for (int yi = 0; yi < giyr; yi++) { var c = cg[xi, yi]; if (c == null) { continue; } c.image = c.ExtractImage(imx, sidesize); c.imMask = c.ExtractImage(im, sidesize); } } List <float> diffs = new List <float>(); for (int xi = 0; xi < gixr; xi++) { for (int yi = 0; yi < giyr; yi++) { var c = cg[xi, yi]; if (c == null) { continue; } var t = cg[10, 10]; var r = c.Match(t); diffs.Add(r); } } float diffMax = diffs.Max(); Mat gridImage = new Mat(sidesize * giyr, sidesize * gixr, DepthType.Cv8U, 3); gridImage.SetTo(new MCvScalar(128, 128, 128)); Mat gridMatchImage = new Mat(sidesize * giyr, sidesize * gixr, DepthType.Cv8U, 3); gridMatchImage.SetTo(new MCvScalar(128, 128, 128)); var templateCell = cg[10, 10]; for (int xi = 0; xi < gixr; xi++) { for (int yi = 0; yi < giyr; yi++) { var c = cg[xi, yi]; if (c == null) { continue; } var r = c.Match(templateCell); var imrgb2 = new Mat(); CvInvoke.CvtColor(c.image, imrgb2, ColorConversion.Gray2Bgr); Mat w = new Mat(gridImage, new Rectangle(new Point(xi * sidesize, yi * sidesize), c.image.Size)); imrgb2.CopyTo(w); w = new Mat(gridMatchImage, new Rectangle(new Point(xi * sidesize, yi * sidesize), c.image.Size)); int rr = (int)(r / diffMax * 255); w.SetTo(new MCvScalar(0, 255 - rr, rr)); imrgb2.CopyTo(w, c.imMask); } } new Emgu.CV.UI.ImageViewer(gridImage, "gridImage").Show(); // Allows zoom and pan //CvInvoke.Imshow("gridImage", gridImage); CvInvoke.Imshow("gridMatchImage", gridMatchImage); new Emgu.CV.UI.ImageViewer(imrgb, "work grid").Show(); // Allows zoom and pan // CvInvoke.Imshow("work grid", imrgb); CvInvoke.Imwrite("work grid.png", imrgb); while (CvInvoke.WaitKey(100) == -1) { ; } }
async Task BinaryImage(Mat gray, int number, Mat canny = null, Mat color = null) { var image = gray; var imageData = EdgePreservingSmoothingBW(gray, 5); using (Matrix<byte> edgeSmoothingImage = new Matrix<byte>(imageData)) //using (Mat image2 = new Mat(@"IMG_0041-Gray.jpg", LoadImageType.Grayscale)) using (Mat cannyImage = new Mat()) { await Dispatcher.BeginInvoke(_addImageToTheList, edgeSmoothingImage.Mat); edgeSmoothingImage.Save("edgeSmoothingImage" + number + ".jpg"); await Dispatcher.BeginInvoke(_addImageToTheList, image); //await Dispatcher.BeginInvoke(_addImageToTheList, // image2); var increasedContrasstArray = ChangeContrast(image, 80); using (var changedContrastImg = new Image<Gray, byte>(increasedContrasstArray)) { await Dispatcher.BeginInvoke(_addImageToTheList, changedContrastImg.Mat); changedContrastImg.Save("changedContrastImg" + number + ".jpg"); //CvInvoke.Threshold(changedContrastImg, cannyImage, 200, 255, ThresholdType.Binary); //using(Mat sobel = new Mat()) //{ //} Matrix<byte> sobelMatrix = new Matrix<byte>(image.Size); var sobelX = new Mat(changedContrastImg.Size, DepthType.Cv8U, 1); var sobelY = new Mat(changedContrastImg.Size, DepthType.Cv8U, 1); CvInvoke.Sobel(changedContrastImg, sobelX, DepthType.Cv8U, 1, 0); CvInvoke.Sobel(changedContrastImg, sobelY, DepthType.Cv8U, 0, 1); for (int rowIndex = 0; rowIndex < changedContrastImg.Rows; rowIndex++) { for (int columnIndex = 0; columnIndex < changedContrastImg.Cols; columnIndex++) { var rX = sobelX.GetData(rowIndex, columnIndex)[0]; var rY = sobelY.GetData(rowIndex, columnIndex)[0]; sobelMatrix[rowIndex, columnIndex] = ToByte(Math.Sqrt(rX * rX + rY * rY)); } } //CvInvoke.Threshold(sobelMatrix, sobelMatrix, 170, 255, ThresholdType.Binary); await Dispatcher.BeginInvoke(_addImageToTheList, sobelMatrix.Mat); sobelMatrix.Save("sobelMatrix" + number + ".bmp"); CvInvoke.Laplacian(image, sobelMatrix, DepthType.Cv8U, 3, 1, 0, BorderType.Default); sobelMatrix.Save("laplacian" + number + ".bmp"); //CvInvoke.Threshold(sobelMatrix, sobelMatrix, 170, 255, ThresholdType.Binary);//170-190 //CvInvoke.AdaptiveThreshold(sobelMatrix, sobelMatrix, 255, AdaptiveThresholdType.MeanC, ThresholdType.Binary, 11, 2); var edgeSmoothBWAray = EdgePreservingSmoothingBW(sobelMatrix.Mat, 5); var edgeMatrix = new Matrix<byte>(edgeSmoothBWAray); edgeMatrix.Save("laplacian-edgeMatrix" + number + ".bmp"); var thresholdEdge = CustomThreshold(edgeMatrix, 13); Matrix<byte> thresholdEdgeMatrix = new Matrix<byte>(thresholdEdge); thresholdEdgeMatrix.Save("laplacian-threshold-2-" + number + ".bmp"); sobelMatrix.Save("laplacian-threshold" + number + ".bmp"); CvInvoke.Canny(changedContrastImg, cannyImage, 150, 224, 3, false); await Dispatcher.BeginInvoke(_addImageToTheList, cannyImage); Matrix<byte> mask = new Matrix<byte>(image.Size); int dilSize = 2; Mat se1 = CvInvoke.GetStructuringElement(Emgu.CV.CvEnum.ElementShape.Rectangle, new System.Drawing.Size(2 * dilSize + 1, 2 * dilSize + 1), new System.Drawing.Point(dilSize, dilSize)); dilSize = 1; Mat se2 = CvInvoke.GetStructuringElement(Emgu.CV.CvEnum.ElementShape.Rectangle, new System.Drawing.Size(2 * dilSize + 1, 2 * dilSize + 1), new System.Drawing.Point(dilSize, dilSize)); //CvInvoke.MorphologyEx(sobelMatrix, mask, MorphOp.Close, se1, new System.Drawing.Point(0, 0), 1, BorderType.Default, new MCvScalar(255, 0, 0, 255)); //await Dispatcher.BeginInvoke(_addImageToTheList, // mask.Mat); CvInvoke.MorphologyEx(sobelMatrix, mask, MorphOp.Open, se2, new System.Drawing.Point(0, 0), 1, BorderType.Default, new MCvScalar(255, 0, 0, 255)); //CvInvoke.Erode(sobelMatrix, sobelMatrix, se1, new System.Drawing.Point(0, 0), 1, BorderType.Default, new MCvScalar(255, 0, 0, 255)); //await Dispatcher.BeginInvoke(_addImageToTheList, // mask.Mat); var maskedSobel = new Matrix<byte>(image.Size); for (int rowIndex = 0; rowIndex < image.Rows; rowIndex++) { for (int columnIndex = 0; columnIndex < image.Cols; columnIndex++) { maskedSobel[rowIndex, columnIndex] = ToByte(thresholdEdgeMatrix[rowIndex, columnIndex] * (mask[rowIndex, columnIndex] / 255)); } } //CvInvoke.Threshold(sobelMatrix, sobelMatrix, 160, 255, ThresholdType.Binary); //await Dispatcher.BeginInvoke(_addImageToTheList, // maskedSobel.Mat); //CvInvoke.Erode(sobelMatrix, sobelMatrix, aaa, new System.Drawing.Point(0, 0), 1, BorderType.Default, new MCvScalar(255, 0, 0, 255)); //await Dispatcher.BeginInvoke(_addImageToTheList, // sobelMatrix.Mat); cannyImage.Save("canny" + number + ".bmp"); Matrix<byte> imageMatrix = new Matrix<byte>(image.Size); image.CopyTo(imageMatrix); Matrix<byte> cannyMatrix = new Matrix<byte>(cannyImage.Size); //CvInvoke.Threshold(sobelMatrix, sobelMatrix, 80, 255, ThresholdType.Binary); if (canny == null) { thresholdEdgeMatrix.CopyTo(cannyMatrix); } else { thresholdEdgeMatrix.CopyTo(cannyMatrix); } var skeletonMatrix = new Matrix<double>(image.Size); var skeletonMatrixByte = new Matrix<byte>(image.Size); //imageMatrix.Mul(cannyMatrix); for (int rowIndex = 0; rowIndex < image.Rows; rowIndex++) { cannyMatrix[rowIndex, 0] = 255; cannyMatrix[rowIndex, image.Cols - 1] = 255; } for (int columnIndex = 0; columnIndex < image.Cols; columnIndex++) { cannyMatrix[0, columnIndex] = 255; cannyMatrix[image.Rows - 1, columnIndex] = 255; } await Dispatcher.BeginInvoke(_addImageToTheList, cannyMatrix.Mat); for (int rowIndex = 0; rowIndex < image.Rows; rowIndex++) { for (int columnIndex = 0; columnIndex < image.Cols; columnIndex++) { var skeletonvalue = imageMatrix[rowIndex, columnIndex] * (cannyMatrix[rowIndex, columnIndex] / 255); skeletonMatrix[rowIndex, columnIndex] = skeletonvalue; skeletonMatrixByte[rowIndex, columnIndex] = ToByte(skeletonvalue); } } await Dispatcher.BeginInvoke(_addImageToTheList, skeletonMatrixByte.Mat); skeletonMatrixByte.Save("skeletonMatrixByte" + number + ".bmp"); using (VectorOfVectorOfPoint contours = new VectorOfVectorOfPoint()) { CvInvoke.FindContours(sobelMatrix, contours, null, RetrType.List, ChainApproxMethod.ChainApproxNone); foreach (var contour in contours.ToArrayOfArray()) { int meanColor = 0; foreach (var point in contour) { meanColor += image.GetData(point.Y, point.X)[0]; } meanColor = meanColor / contour.Length; foreach (var point in contour) { skeletonMatrix[point.Y, point.X] = meanColor; skeletonMatrixByte[point.Y, point.X] = ToByte(meanColor); } } //CvInvoke.DrawContours(smoothImage.Mat, contours, -1, new MCvScalar(255, 0, 0), -1, LineType.EightConnected, null, 200); } await Dispatcher.BeginInvoke(_addImageToTheList, skeletonMatrixByte.Mat); skeletonMatrixByte.Save("skeletonMatrixByte-norm" + number + ".bmp"); var intersectionSurfaceMatrix = CreateIntersectionSurfaceMatrix(cannyMatrix, skeletonMatrixByte, skeletonMatrix); var intersectionMatrixByte = new Matrix<byte>(intersectionSurfaceMatrix.Size); for (int rowIndex = 0; rowIndex < image.Rows; rowIndex++) { for (int columnIndex = 0; columnIndex < image.Cols; columnIndex++) { intersectionMatrixByte[rowIndex, columnIndex] = ToByte(intersectionSurfaceMatrix[rowIndex, columnIndex]); } } await Dispatcher.BeginInvoke(_addImageToTheList, intersectionMatrixByte.Mat); intersectionMatrixByte.Save("intersectionMatrixByte" + number + ".bmp"); var doubleImageMatrix = imageMatrix.Convert<double>(); var differenceMatrix = intersectionSurfaceMatrix - doubleImageMatrix; var binary1Matrix = new Matrix<byte>(image.Size); var binary2Matrix = new Matrix<byte>(image.Size); double pt = 15; double nt = 15; for (int rowIndex = 0; rowIndex < image.Rows; rowIndex++) { for (int columnIndex = 0; columnIndex < image.Cols; columnIndex++) { var diffValue = differenceMatrix[rowIndex, columnIndex]; if (diffValue > pt || cannyMatrix[rowIndex, columnIndex] == 255) { binary1Matrix[rowIndex, columnIndex] = 255; } else { binary1Matrix[rowIndex, columnIndex] = 0; } if (diffValue < -nt || cannyMatrix[rowIndex, columnIndex] == 255) { binary2Matrix[rowIndex, columnIndex] = 255; } else { binary2Matrix[rowIndex, columnIndex] = 0; } } } VectorOfVectorOfPoint contoursVector = new VectorOfVectorOfPoint(); //var connectedComponents = FindBlobs(binary1Matrix.Mat, number); CvInvoke.FindContours(binary1Matrix.Mat.Clone(), contoursVector, null, RetrType.List, ChainApproxMethod.ChainApproxNone); var minWidth = 0.045 * gray.Width; var maxWidth = 0.25 * gray.Width; var minHeight = 0.045 * gray.Height; var maxHeight = 0.25 * gray.Height; Mat detectedDigits = new Mat(); //(gray.Size, DepthType.Cv32S, 3); //color.ConvertTo(colorComponents, DepthType.Cv32S); gray.ConvertTo(detectedDigits, DepthType.Cv32S); for (int compIndex = 0; compIndex < contoursVector.Size; compIndex++) { var component = contoursVector[compIndex]; var subSampleData = await SubSampling(color, component); byte[,,] colorComponents = new byte[color.Rows, color.Cols, 3]; for(int row = 0; row < subSampleData.GetLength(0); row++) { for(int column = 0; column < subSampleData.GetLength(1); column++) { var r = subSampleData[row, column, 2]; var g = subSampleData[row, column, 1]; var b = subSampleData[row, column, 0]; if(r > 0) { colorComponents[row, column, 2] = color.GetData(row, column)[2]; colorComponents[row, column, 1] = color.GetData(row, column)[1]; colorComponents[row, column, 0] = color.GetData(row, column)[0]; } } } var compRectangle = CvInvoke.BoundingRectangle(component); var ratio = (double)compRectangle.Width / compRectangle.Height; var inversedRatio = (double) 1 / ratio; using (var colorImage = new Image<Bgr, byte>(subSampleData)) { colorImage.Save("colorComponents-" + number + "-" + compIndex + ".bmp"); } using (var colorImage = new Image<Bgr, byte>(colorComponents)) { colorImage.Save("colorComponents-def-" + number + "-" + compIndex + ".bmp"); var data = EdgePreservingSmoothing(colorImage.Mat, component, 12); using (var colorImage2 = new Image<Bgr, byte>(data)) { colorImage2.Save("colorComponents-edge-smooth-" + number + "-" + compIndex + ".bmp"); var subSampleData2 = await SubSampling(colorImage2.Mat, component); using (var colorImage3 = new Image<Bgr, byte>(subSampleData2)) { colorImage3.Save("colorComponents-edge-smooth-sub" + number + "-" + compIndex + ".bmp"); } } } if(compRectangle.Width >= minWidth && compRectangle.Width <= maxWidth && compRectangle.Height >= minHeight && compRectangle.Height <= maxHeight && (ratio <= 0.9 && inversedRatio <= 1.5)) { CvInvoke.Rectangle(detectedDigits, compRectangle, new MCvScalar(100, 100, 100)); } } detectedDigits.Save("detectedDigits" + number + ".bmp"); await Dispatcher.BeginInvoke(_addImageToTheList, binary1Matrix.Mat); binary1Matrix.Save("binary1Matrix" + number + ".bmp"); await Dispatcher.BeginInvoke(_addImageToTheList, binary2Matrix.Mat); binary2Matrix.Save("binary2Matrix" + number + ".bmp"); } //CvInvoke.BilateralFilter(image, cannyImage, 4, 4, 4); //CvInvoke.Laplacian(image, cannyImage, DepthType.Cv8U); //CvInvoke.Threshold(cannyImage, cannyImage, 40, 255, ThresholdType.Binary); //await Dispatcher.BeginInvoke(_addImageToTheList, // cannyImage); //using(Mat skeletonMat = new Mat()) //{ // skeletonMat = //} } }