public static void LineSpreadFunction(Image <Gray, byte> workImage, out WaferEdgeFit leftLineData, out WaferEdgeFit rightLineData, out WaferEdgeFit topLineData, out WaferEdgeFit bottomLineData) { int width = workImage.Width; int height = workImage.Height; //Recipe? Waferareapercent? int darkLimit = 20; CvInvoke.Threshold(workImage, workImage, darkLimit, 0, ThresholdType.ToZero); FitEdge(workImage, height / 3, 2 * height / 3, 0, width / 8, false, out leftLineData); FitEdge(workImage, height / 3, 2 * height / 3, 7 * width / 8, width, false, out rightLineData); FitEdge(workImage, 0, height / 8, width / 3, 2 * width / 3, true, out topLineData); FitEdge(workImage, 7 * height / 8, height, width / 3, 2 * width / 3, true, out bottomLineData); }
public static void FitEdge(Image <Gray, byte> inputImage, int startRow, int endRow, int startCol, int endCol, bool isTopBottom, out WaferEdgeFit edge) { edge = new WaferEdgeFit(); Rectangle origRoi = inputImage.ROI; Rectangle sideRoi = new Rectangle(startCol, startRow, endCol - startCol, endRow - startRow); bool startFromRight = !isTopBottom && startCol > origRoi.Width / 2 || isTopBottom && startRow > origRoi.Height / 2; inputImage.ROI = sideRoi; int workingWidth = isTopBottom ? sideRoi.Height : sideRoi.Width; int workingHeight = isTopBottom ? sideRoi.Width : sideRoi.Height; using (Image <Gray, float> workImage = new Image <Gray, float>(workingWidth, workingHeight)) { double gradientLimit; using (Image <Gray, float> sobelImage = isTopBottom ? inputImage.Sobel(0, 1, 3) : inputImage.Sobel(1, 0, 3)) { using (Image <Gray, float> nullImage = new Image <Gray, float>(sobelImage.Size)) { CvInvoke.AbsDiff(sobelImage, nullImage, sobelImage); } using (Image <Gray, byte> mask = new Image <Gray, byte>(sideRoi.Width, sideRoi.Height)) { CvInvoke.Threshold(inputImage, mask, 0, 1, ThresholdType.Binary); MCvScalar gradientMean = new MCvScalar(); MCvScalar gradientStd = new MCvScalar(); CvInvoke.MeanStdDev(sobelImage, ref gradientMean, ref gradientStd, mask); double nSigma = 5; gradientLimit = gradientMean.V0 + nSigma * gradientStd.V0; } if (isTopBottom) { CvInvoke.Transpose(sobelImage, workImage); } else { sobelImage.CopyTo(workImage); } } inputImage.ROI = origRoi; List <PointF> edgePoints = new List <PointF>(); List <float> fullWidthHalfMaximumVals = new List <float>(); var sobelData = workImage.Data; int stride = 1; for (int r = 0; r < workingHeight; r += stride) { int approxEdgeCol = 0; if (!startFromRight) { for (int c = 0; c < workingWidth; c++) { var currentValue = sobelData[r, c, 0]; if (currentValue > gradientLimit) { approxEdgeCol = c; break; } } } else { for (int c = workingWidth - 1; c > 0; c--) { var currentValue = sobelData[r, c, 0]; if (currentValue > gradientLimit) { approxEdgeCol = c; break; } } } int meanEdgeCol = 0; float maxValue = 0; var currentStartCol = Math.Max(approxEdgeCol - 5, 1); var currentEndCol = Math.Min(approxEdgeCol + 5 + 1, workingWidth - 1); for (int c = currentStartCol; c < currentEndCol; c++) { if (sobelData[r, c, 0] > maxValue) { maxValue = sobelData[r, c, 0]; meanEdgeCol = c; } } if (!(maxValue > 0)) { continue; } float halfMaxLeftValue = maxValue; float halfMaxRightValue = maxValue; int halfMaxLeftCol = meanEdgeCol; int halfMaxRightCol = meanEdgeCol; while (halfMaxLeftValue > maxValue / 2 && halfMaxLeftCol >= 0) { halfMaxLeftCol--; halfMaxLeftValue = sobelData[r, halfMaxLeftCol, 0]; } while (halfMaxRightValue > maxValue / 2 && halfMaxRightCol < workingWidth) { halfMaxRightCol++; halfMaxRightValue = sobelData[r, halfMaxRightCol, 0]; } float fwhm = halfMaxRightCol - halfMaxLeftCol; //Interpolation float dPixel = (maxValue / 2 - halfMaxLeftValue) / (sobelData[r, halfMaxLeftCol + 1, 0] - halfMaxLeftValue); fwhm -= dPixel; dPixel = (maxValue / 2 - halfMaxRightValue) / (sobelData[r, halfMaxRightCol - 1, 0] - halfMaxRightValue); fwhm -= dPixel; fullWidthHalfMaximumVals.Add(fwhm); edgePoints.Add(isTopBottom ? new PointF(r + sideRoi.X, meanEdgeCol + sideRoi.Y) : new PointF(r + sideRoi.Y, meanEdgeCol + sideRoi.X)); } VectorOfPointF yvector = new VectorOfPointF(); VectorOfFloat parameters = new VectorOfFloat(); yvector.Push(edgePoints.ToArray()); CvInvoke.FitLine(yvector, parameters, DistType.L12, 0, 0.01, 0.01); float vx = parameters[0]; float vy = parameters[1]; float x0 = parameters[2]; float y0 = parameters[3]; edge.FitParams = parameters; edge.Slope = vy / vx; edge.Intercept = y0 - edge.Slope * x0; fullWidthHalfMaximumVals.Sort(); int length = fullWidthHalfMaximumVals.Count; if (length % 2 == 0) { edge.LineSpread = (fullWidthHalfMaximumVals[length / 2 - 1] + fullWidthHalfMaximumVals[length / 2]) / 2; } else { edge.LineSpread = fullWidthHalfMaximumVals[length / 2]; } if (!isTopBottom) { edge.InvertedRepresentation = true; } } }