using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; using OpenCvSharp; namespace ceju { class SubFunction { /* * 摘要: * 计算图片非零像素的平均阈值 * 参数: * image: * 输入图片,(单通道) * threshold: * 输出阈值 */ public static void AverageThreshold(Mat image, out double threshould) { // 计算非零像素的个数 int count = 0; BasFunction.Count(image, out count); // 计算像素和 double sum = 0; for (int i = 0; i < image.Rows; i++) { for (int j = 0; j < image.Cols; j++) { sum += image.Get(i, j); } } // 求平均阈值 threshould = sum / count; } /* * 摘要: * 提取图像的中部区域的边界 * 参数: * image: * 阈值分割后的二值0,1图像 * 返回: * 数组middleArea; * middleArea[0]:中部左边界 * middleArea[1]:中部右边界 * 方法: * 对每列求和,得到行向量; * 得到行向量的最大值; * 分别对行向量从左往右和从右往左遍历,找到大于最大值-10的列 */ public static int[] GetMiddleArea(Mat image) { //求行数和列数 int rows = image.Rows; int cols = image.Cols; //将Mat类中的数据放到数组中 int[,] arr = BasFunction.Mat2Array(image); //将数组转换成0,1数组 arr = BasFunction.ConversionRange(arr); //中部边界的数组 int[] middleArea = new int[2]; //对数组每列求和得到行向量 int[] sumC = BasFunction.Sum(arr, 1); //得到该行向量的最大值 int sumCMax = BasFunction.Max(sumC); //从左向右遍历,大于临界值时,得到中部左边界 for (int j = 0; j < cols; j++) { if (sumC[j] > sumCMax - 10) { middleArea[0] = j; break; } } //从右向左遍历,大于临界值时,得到中部右边界 for (int j = cols - 1; j >= 0; j--) { if (sumC[j] > sumCMax - 10) { middleArea[1] = j; break; } } return middleArea; } /* * 摘要: * 得到数据提取区域的边界 * 参数: * image: * 输入阈值分割后的图像 * middleArea: * 输入中间区域的边界, * middleArea[0]:中部左边界 * middleArea[1]:中部右边界 * 返回: * dataArea: * 数据提取区域的边界 * dataArea[0]:左半边的左边界 * dataArea[1]:左半边的右边界 * dataArea[2]:右半边的左边界 * dataArea[3]:右半边的右边界 */ public static int[] GetDataArea(Mat image, int[] middleArea) { //图片行列数 int rows = image.Rows; int cols = image.Cols; //图片填充的上界 int y = 0; //将Mat类数据存入数组中 int[,] arr = BasFunction.Mat2Array(image); //将数组转换成0,1数组 arr = BasFunction.ConversionRange(arr); //对二维数组每行求和,输出列向量 int[] sumR = BasFunction.Sum(arr, 2); //列向量的最大值 int sumRMax = BasFunction.Max(sumR); //当列向量某行的值大于最大值的一半时,得到填充上界 for (int i = 1; i < rows; i++) { if (sumR[i] > sumRMax / 2) { y = i; break; } } //将二维数组,y以下的所有行都变为1 int[,] arrFill = arr; for (int i = y; i < rows; i++) { for (int j = 1; j < cols; j++) { arrFill[i, j] = 1; } } //对填充后的数组每列求和,得到行向量 int[] sumCArrayFill = BasFunction.Sum(arrFill, 1); //该行向量的middleArea[0] - 500到middleArea[0]区间,左半边 int[] sumCArrayFillLeft = BasFunction.Intercept(sumCArrayFill, middleArea[0] - 500, middleArea[0]); //该行向量的middleArea[1]到middleArea[1] + 500区间,右半边 int[] sumCArrayFillRight = BasFunction.Intercept(sumCArrayFill, middleArea[1], middleArea[1] + 500); //左半边的最大值 int sumCArrayFillLeftMax = BasFunction.Max(sumCArrayFillLeft); //右半边最大值 int sumCArrayFillRightMax = BasFunction.Max(sumCArrayFillRight); //数据提取区域,分别是左半边左右边界,右半边左右边界 int[] dataArea = new int[4]; for (int j = middleArea[0] - 500; j < middleArea[0]; j++) { if (sumCArrayFill[j] > sumCArrayFillLeftMax - 10) { dataArea[0] = j; break; } } for (int j = middleArea[0]; j > middleArea[0] - 500; j--) { if (sumCArrayFill[j] > sumCArrayFillLeftMax - 5) { dataArea[1] = j; break; } } for (int j = middleArea[1]; j < middleArea[1] + 500; j++) { if (sumCArrayFill[j] > sumCArrayFillRightMax - 5) { dataArea[2] = j; break; } } for (int j = middleArea[1] + 500; j > middleArea[1]; j--) { if (sumCArrayFill[j] > sumCArrayFillRightMax - 10) { dataArea[3] = j; break; } } return dataArea; } /* * 摘要: * 提取线条,保存每列第一个点,需要没有间断点 * 参数: * array: * 输入二维数组,0,1二值 * line: * 输出线条,0,1二值 * averageCoordinate: * 输出平均纵坐标 * leftBorder: * 左边界 * rightBorder: * 右边界 */ public static void ExtractLines(int[,] array, out int[,] line, out double averageCoordinate, int leftBorder, int rightBorder) { //数组长宽 int rows = array.GetLength(0); int cols = array.GetLength(1); //输出的线条数组 line = new int[rows, cols]; //记录线条上点的纵坐标数组 int[] coordinate = new int[cols]; //线条平均纵坐标 averageCoordinate = 0; //纵坐标之和 int sum = 0; //线条上点的个数 int count = 0; //遍历,原数组上该位置有点时,线条上的该位置置1 for (int j = leftBorder; j < rightBorder; j++) { for (int i = 0; i < rows; i++) { if (array[i, j] == 1) { line[i, j] = 1; coordinate[j] = i; break; } } } //计算纵坐标之和 BasFunction.Sum(coordinate, out sum); //计算线条内点的个数 BasFunction.Count(coordinate, out count); //平均纵坐标 averageCoordinate = sum / count; } /* * 摘要: * 提取线条,保存每列第一个点,需要没有间断点,不输出线条 * 参数: * array: * 输入二维数组,0,1二值 * averageCoordinate: * 输出平均纵坐标 * leftBorder: * 左边界 * rightBorder: * 右边界 */ public static void ExtractLines(int[,] array, out double averageCoordinate, int leftBorder, int rightBorder) { //数组长宽 int rows = array.GetLength(0); int cols = array.GetLength(1); //记录线条上点的纵坐标数组 int[] coordinate = new int[cols]; //线条平均纵坐标 averageCoordinate = 0; //纵坐标之和 int sum = 0; //线条上点的个数 int count = 0; //遍历,查询有点的个数 for (int j = leftBorder; j < rightBorder; j++) { for (int i = 0; i < rows; i++) { if (array[i, j] == 1) { coordinate[j] = i; break; } } } //计算纵坐标之和 BasFunction.Sum(coordinate, out sum); //计算线条内点的个数 BasFunction.Count(coordinate, out count); //平均纵坐标 averageCoordinate = sum / count; } /* * 摘要: * 提取线条,保存每列第一个点,需要没有间断点 * 参数: * array: * 输入二维数组,0,1二值 * line: * 输出线条,0,1二值 * averageCoordinateNew: * 输出平均纵坐标 * leftBorder: * 左边界 * rightBorder: * 右边界 * averageCoordinate: * 作为基准的上一条线的平均纵坐标 */ public static void ExtractLines(int[,] array, out int[,] line, out double averageCoordinateNew, int leftBorder, int rightBorder, double averageCoordinate) { //数组长宽 int rows = array.GetLength(0); int cols = array.GetLength(1); //输出的线条数组 line = new int[rows, cols]; //新的记录线条纵坐标的数组 int[] coordinateNew = new int[cols]; //遍历范围的上界和下界 int upperBound = (int)averageCoordinate + 15; int lowerBound = (int)averageCoordinate + 45; //初始化,记录纵坐标的数组全为0 for (int j = 0; j < cols; j++) { coordinateNew[j] = 0; } //计算线条内点的个数 int count = 0; BasFunction.Count(coordinateNew, out count); //当点的个数少于5的时候循环 while (count < 5) { //每次循环,寻找范围向下移动5个像素 upperBound = upperBound + 5; lowerBound = lowerBound + 5; //遍历,寻找线条上的点,并记录纵坐标 for (int j = leftBorder; j < rightBorder; j++) { for (int i = upperBound; i < lowerBound; i++) { if (array[i, j] > 0) { line[i, j] = 1; coordinateNew[j] = i; break; } } } //重新计算线条内点的个数 BasFunction.Count(coordinateNew, out count); } //纵坐标之和 int sum = 0; BasFunction.Sum(coordinateNew, out sum); //平均纵坐标 averageCoordinateNew = 0; averageCoordinateNew = sum / count; } /* * 摘要: * 提取线条,保存每列第一个点,需要没有间断点,不输出线条,只输出纵坐标 * 参数: * array: * 输入二维数组,0,1二值 * averageCoordinateNew: * 输出平均纵坐标 * leftBorder: * 左边界 * rightBorder: * 右边界 * averageCoordinate: * 作为基准的上一条线的平均纵坐标 */ public static void ExtractLines(int[,] array, out double averageCoordinateNew, int leftBorder, int rightBorder, double averageCoordinate) { //数组长宽 int rows = array.GetLength(0); int cols = array.GetLength(1); //新的记录线条纵坐标的数组 int[] coordinateNew = new int[cols]; //遍历范围的上界和下界 int upperBound = (int)averageCoordinate + 15; int lowerBound = (int)averageCoordinate + 45; //初始化,记录纵坐标的数组全为0 for (int j = 0; j < cols; j++) { coordinateNew[j] = 0; } //计算线条内点的个数 int count = 0; BasFunction.Count(coordinateNew, out count); //当点的个数少于5的时候循环 while (count < 5) { //每次循环,寻找范围向下移动5个像素 upperBound = upperBound + 5; lowerBound = lowerBound + 5; //遍历,寻找线条上的点,并记录纵坐标 for (int j = leftBorder; j < rightBorder; j++) { for (int i = upperBound; i < lowerBound; i++) { if (array[i, j] > 0) { coordinateNew[j] = i; break; } } } //重新计算线条内点的个数 BasFunction.Count(coordinateNew, out count); } //纵坐标之和 int sum = 0; BasFunction.Sum(coordinateNew, out sum); //平均纵坐标 averageCoordinateNew = 0; averageCoordinateNew = sum / count; } /* * 摘要: * 提取线条,保存每列第一个点,需要没有间断点,不输出线条,只输出纵坐标(从下往上) * 参数: * array: * 输入二维数组,0,1二值 * averageCoordinateNew: * 输出平均纵坐标 * leftBorder: * 左边界 * rightBorder: * 右边界 * averageCoordinate: * 作为基准的上一条线的平均纵坐标 */ public static void ExtractLines2(int[,] array, out double averageCoordinateNew, int leftBorder, int rightBorder, double averageCoordinate) { //数组长宽 int rows = array.GetLength(0); int cols = array.GetLength(1); //新的记录线条纵坐标的数组 int[] coordinateNew = new int[cols]; //遍历范围的上界和下界 int upperBound = (int)averageCoordinate - 45; int lowerBound = (int)averageCoordinate - 10; //初始化,记录纵坐标的数组全为0 for (int j = 0; j < cols; j++) { coordinateNew[j] = 0; } //计算线条内点的个数 int count = 0; BasFunction.Count(coordinateNew, out count); //当点的个数少于5的时候循环 while (count < 5) { //每次循环,寻找范围向下移动5个像素 upperBound = upperBound - 5; lowerBound = lowerBound - 5; //遍历,寻找线条上的点,并记录纵坐标 for (int j = leftBorder; j < rightBorder; j++) { for (int i = lowerBound; i > upperBound; i--) { if (array[i, j] > 0) { coordinateNew[j] = i; break; } } } //重新计算线条内点的个数 BasFunction.Count(coordinateNew, out count); } //纵坐标之和 int sum = 0; BasFunction.Sum(coordinateNew, out sum); //平均纵坐标 averageCoordinateNew = 0; averageCoordinateNew = sum / count; } /* * 摘要: * 计算孔径的起止点 * 参数: * array: * 阈值分割后的目标区域数组 * aperture: * 输出孔径起止点 * aperture[0]:左起始点 * aperture[1]:右结束点 * ordinate1: * 左部分纵坐标 * ordinate2: * 右部分纵坐标 * middleArea: * 中部区域边界 * middleArea[0]:中部左边界 * middleArea[1]:中部右边界 * dataArea: * 数据提取区域边界 * dataArea[0]:左部分左边界 * dataArea[1]:左部分右边界 * dataArea[2]:右部分左边界 * dataArea[3]:右部分右边界 */ public static void GetAperture(int[,] array, out int[] aperture, int ordinate1, int ordinate2, int[] middleArea, int[] dataArea) { //孔径起始点 aperture = new int[2]; //数组行列数 int rows = array.GetLength(0); int cols = array.GetLength(1); //中部区域的中心 int middle = (middleArea[0] + middleArea[1]) / 2; //左右截取的区域 int[,] leftSeg = new int[rows, cols]; int[,] rightSeg = new int[rows, cols]; leftSeg = BasFunction.InterceptArray(array, leftSeg, ordinate1 - 10, ordinate1 + 10, dataArea[0], middleArea[0] + 5, 1); rightSeg = BasFunction.InterceptArray(array, rightSeg, ordinate2 - 10, ordinate2 + 10, middleArea[1] - 5, dataArea[3], 1); //左部分列求和,行向量 int[] sum1 = BasFunction.Sum(leftSeg, 1); //右部分列求和,行向量 int[] sum2 = BasFunction.Sum(rightSeg, 1); //左部分遍历,从中间到左边界,当大于0时记录为孔径起始点 for (int j = middle; j > dataArea[0]; j--) { if (sum1[j] > 0) { aperture[0] = j; break; } } //右部分遍历,从中间到右边界,当大于0时记录为孔径结束点 for (int j = middle; j < dataArea[3]; j++) { if (sum2[j] > 0) { aperture[1] = j; break; } } } /* * 摘要: * 计算孔铜距离并得到对应的曲面上的点坐标 * 参数: * line: * 第一条线 * apertureBeign: * 孔径左起始点坐标,先纵坐标后横坐标 * apertureEnd: * 孔径右截止点坐标,先纵坐标后横坐标 * kongtong: * 输出孔铜数据数组,先左后右 * pointLeft: * 曲面上左面的点坐标,先纵坐标后横坐标 * pointRight: * 曲面上右面的点坐标,先纵坐标后横坐标 */ public static void GetKongtong(int[,] line, int[] apertureBegin, int[] apertureEnd, out double[] kongtong, out int[] pointLeft, out int[] pointRight) { //数组行列数 int rows = line.GetLength(0); int cols = line.GetLength(1); //截取曲面线条 int[,] lineSeg = new int[rows, cols]; lineSeg = BasFunction.InterceptArray(line, lineSeg, 0, rows, apertureBegin[1], apertureEnd[1]); //曲面上点的个数 int count = 0; BasFunction.Count(lineSeg, out count); //得到曲面线条上点的坐标位置 int[,] coordinate = new int[count, 2]; BasFunction.Find(lineSeg, out coordinate); //计算距离,最小的距离分别为左孔铜和右孔铜 double leftKongtong = 1000; double rightKongtong = 1000; double distance1 = 0; double distance2 = 0; //当距离最短是曲线上点的坐标,第一列是纵坐标(行数),第二列是横坐标(列数) pointLeft = new int[2]; pointRight = new int[2]; for (int i = 0; i < count; i++) { //计算曲面点到左起始点的距离 distance1 = Math.Sqrt(Math.Pow((coordinate[i, 0] - apertureBegin[0]), 2) + Math.Pow((coordinate[i, 1] - apertureBegin[1]), 2)); //计算曲面点到右截止点的距离 distance2 = Math.Sqrt(Math.Pow((coordinate[i, 0] - apertureEnd[0]), 2) + Math.Pow((coordinate[i, 1] - apertureEnd[1]), 2)); //得到左右孔铜以及对应的曲面坐标 if (leftKongtong > distance1) { leftKongtong = distance1; pointLeft[0] = coordinate[i, 0]; pointLeft[1] = coordinate[i, 1]; } if (rightKongtong > distance2) { rightKongtong = distance2; pointRight[0] = coordinate[i, 0]; pointRight[1] = coordinate[i, 1]; } } kongtong = new double[2] { leftKongtong, rightKongtong }; } /* * 摘要: * 得到孔深,并且得到曲面最凸点或者最凹点的坐标 * 参数: * line: * 线条 * apertureBegin: * 孔径左侧起始点坐标,先纵后横 * apertureEnd: * 孔径右侧结束点坐标,先纵后横 * curveVertex: * 曲面凸点的坐标 */ public static void CurveVertex(int[,] line, int[] apertureBegin, int[] apertureEnd, out int[] curveVertex, out double middleApertureY) { //曲面顶点的坐标 curveVertex = new int[2]; //数组行列数 int rows = line.GetLength(0); int cols = line.GetLength(1); //孔径平均横坐标 double middleAperture = (apertureBegin[1] + apertureEnd[1]) / 2; //孔径平均高度 int[] apertureY = new int[2]; for (int i = 0; i < rows; i++) { if (line[i, apertureBegin[1]] > 0) { apertureY[0] = i; break; } } for (int i = 0; i < rows; i++) { if (line[i, apertureEnd[1]] > 0) { apertureY[1] = i; break; } } middleApertureY = (apertureY[0] + apertureY[1]) / 2; //截取曲面线条 int[,] lineSeg = new int[rows, cols]; lineSeg = BasFunction.InterceptArray(line, lineSeg, 0, rows, apertureBegin[1], apertureEnd[1]); //曲面上点的个数 int count = 0; BasFunction.Count(lineSeg, out count); //得到曲面线条上点的坐标位置 int[,] coordinate = new int[count, 2]; BasFunction.Find(lineSeg, out coordinate); //孔深,取曲面上的点到孔径平均高度只差的最大值 double kongshenAomian = 0; double kongshenTumian = 0; int[] coordinateAomian = new int[2]; int[] coordinateTumian = new int[2]; double tAomian = 0; double tTumian = 0; for (int i = 0; i < count; i++) { tAomian = coordinate[i, 0] - middleApertureY; tTumian = middleApertureY - coordinate[i, 0]; if (tAomian > kongshenAomian) { kongshenAomian = tAomian; //纵坐标 coordinateAomian[0] = coordinate[i, 0]; //横坐标 coordinateAomian[1] = coordinate[i, 1]; } if (tTumian > kongshenTumian) { kongshenTumian = tTumian; coordinateTumian[0] = coordinate[i, 0]; coordinateTumian[1] = coordinate[i, 1]; } } //判断哪个点离孔径中心更近,更近的为曲面坐标 double absAomian = Math.Abs(coordinateAomian[1] - middleAperture); double absTumian = Math.Abs(coordinateTumian[1] - middleAperture); if (absAomian < absTumian) { curveVertex = coordinateAomian; } else { curveVertex = coordinateTumian; } } /* * 摘要: * 得到内部线条,将内部线条进行腐蚀后,求平均纵坐标 * 参数: * image: * 输入图像,一般是绿色通道 * upperBound, lowerBound, leftBoundary, rightBoundary: * 内部区域的上下左右边界 * meanOrdinate: * 输出的纵坐标 * */ public static void InsideLine(Mat image, int upperBound, int lowerBound, int leftBoundary, int rightBoundary, out double meanOrdinate) { // 内部区域的行列数 int rows = lowerBound - upperBound; int cols = rightBoundary - leftBoundary; // 内部区域的图像 Mat inside = new Mat(rows, cols, image.Type()); // 滤波提高对比度后的图像 Mat filter = new Mat(rows, cols, MatType.CV_8UC1); // 绘制内部区域像素 for (int i = 0; i < rows; i++) { for (int j = 0; j < cols; j++) { int value = image.Get(i + upperBound, j + leftBoundary); inside.Set(i, j, 255 - value); } } // 滤波核 InputArray kernel = InputArray.Create(new int[3, 3] { { 0, -1, 0 }, { -1, 5, -1 }, { 0, -1, 0 } }); // 滤波 Cv2.Filter2D(inside, filter, -1, kernel); Cv2.ConvertScaleAbs(filter, filter); // 腐蚀宽度 double width = (rightBoundary - leftBoundary) * 0.3; // 腐蚀结构元素 Mat seErode = Cv2.GetStructuringElement(MorphShapes.Rect, new Size((int)width, 1)); // 腐蚀 Mat erode = new Mat(); //Cv2.Erode(filter, erode, seErode); // 二值化 Mat thresh = new Mat(filter.Size(), filter.Type()); thresh = filter.Threshold(0, 255, ThresholdTypes.Otsu); // 再次腐蚀 Cv2.Erode(thresh, erode, seErode); // 边缘检测 Mat grad_y = new Mat(); Mat grad_y2 = new Mat(); Cv2.Sobel(erode, grad_y, MatType.CV_16S, 0, 1); Cv2.ConvertScaleAbs(grad_y, grad_y2); //new Window("zengqiang", WindowMode.Normal, filter); //new Window("erzhihua", WindowMode.Normal, thresh); //new Window("fushi", WindowMode.Normal, erode); //new Window("bianyuanjiance", WindowMode.Normal, grad_y2); //Cv2.WaitKey(0); int[,] arraySobel = new int[rows, cols]; arraySobel = BasFunction.Mat2Array(grad_y2); arraySobel = BasFunction.ConversionRange(arraySobel); int[,] line6 = new int[rows, cols]; meanOrdinate = 0; //SubFunction.ExtractLines(arraySobel, out line6, out meanOrdinateL6, 0, 180,1); //meanOrdinateL6 = meanOrdinateL6-3 + upperBound; // 非零元素坐标 int[,] coordianate; BasFunction.Find(arraySobel, out coordianate); // 非零元素个数 int count = coordianate.GetLength(0); double sum = 0; for (int i = 0; i < count; i++) { sum += coordianate[i, 0]; } // 得到内部线的纵坐标(平均坐标加上上界) meanOrdinate = sum / count + upperBound; //new Window("inside", WindowMode.AutoSize, inside); //new Window("filter", WindowMode.AutoSize, filter); //new Window("thresh", WindowMode.AutoSize, thresh); //new Window("sobel", WindowMode.AutoSize, grad_y2); //Cv2.WaitKey(0); } public static double AverageBrightness(double[,] array) { //数组中大于0的个数 int count = 0; BasFunction.Count(array, out count); //数组中所有亮度之和 double sum = 0; BasFunction.Sum(array, out sum); //数组中的平均亮度 double result = sum / count; return result; } /* * 摘要: * 绘制线条 * 参数: * image: * 绘制的图片 * x1:点1的横坐标 * y1:点1的纵坐标 * x2:点2的横坐标 * y2:点2的纵坐标 */ public static void LineShow(Mat image, int x1, int y1, int x2, int y2) { Point p1 = new Point(); Point p2 = new Point(); p1.X = x1; p1.Y = y1; p2.X = x2; p2.Y = y2; Scalar color = new Scalar(0, 0, 255); //颜色 Cv2.Line(image, p1, p2, color, 2, LineTypes.Link8); } /* * 摘要: * 将三通道图片分割成单通道 * 参数: * image: * 三通道图片 * imageBlue,imageGreen,imageRed: * 蓝绿红图片 * colorChoose: * 选择线条颜色,blue,green,red */ public static void LineShow(Mat image, int x1, int y1, int x2, int y2, string colorChoose) { Point p1 = new Point(); Point p2 = new Point(); p1.X = x1; p1.Y = y1; p2.X = x2; p2.Y = y2; Scalar color = new Scalar();//颜色 switch (colorChoose) { case "blue": color = new Scalar(255, 0, 0); break; case "green": color = new Scalar(0, 255, 0); break; case "red": color = new Scalar(0, 0, 255); break; default: break; } Cv2.Line(image, p1, p2, color, 2, LineTypes.Link8); } /* * 摘要: * 显示文字 * 参数: * image: * 背景图片 * data: * 显示数字 * x,y: * 显示坐标 */ public static void TextShow(Mat image, double data, int x, int y) { data = Math.Round(data, 2); Scalar color = new Scalar(255, 0, 0); Point p = new Point(); p.X = x; p.Y = y; Cv2.PutText(image, data.ToString() + "um", p, HersheyFonts.HersheyComplex, 0.8, color, 2, LineTypes.Link8); } /* * 摘要: * 将三通道图片分割成单通道 * 参数: * image: * 三通道图片 * imageBlue,imageGreen,imageRed: * 蓝绿红图片 */ public static void SeparateRGB(Mat image, out Mat imageBlue, out Mat imageGreen, out Mat imageRed) { int rows = image.Rows; int cols = image.Cols; imageBlue = new Mat(rows, cols, MatType.CV_8UC1); imageGreen = new Mat(rows, cols, MatType.CV_8UC1); imageRed = new Mat(rows, cols, MatType.CV_8UC1); for (int i = 0; i < rows; i++) { for (int j = 0; j < cols; j++) { int b = image.Get(i, j)[0]; int g = image.Get(i, j)[1]; int r = image.Get(i, j)[2]; imageBlue.Set(i, j, b); imageGreen.Set(i, j, g); imageRed.Set(i, j, r); } } } /* * 摘要: * 对图片进行Sobel边缘检测,输出的是横向检测与纵向检测混合之后的结果 */ public static void Sobel(Mat imageContour,out Mat imageSobel) { //横向检测 Mat grad_x = new Mat(); Mat grad_x2 = new Mat(); Cv2.Sobel(imageContour, grad_x, MatType.CV_16S, 1, 0); Cv2.ConvertScaleAbs(grad_x, grad_x2); //纵向检测 Mat grad_y = new Mat(); Mat grad_y2 = new Mat(); Cv2.Sobel(imageContour, grad_y, MatType.CV_16S, 0, 1); Cv2.ConvertScaleAbs(grad_y, grad_y2); //横纵向混合平均 imageSobel = new Mat(); Cv2.AddWeighted(grad_x2, 0.5, grad_y2, 0.5, 0, imageSobel); } /* * 摘要: * 标注线条(竖向的标记线条) * 参数: * image: * 画线条的图片 * x1,y1,x2,y2: * 线条端点的横纵坐标 * data: * 显示数据 */ public static void LabelVertical(Mat image, int x1, int y1, int x2, int y2, double data) { double proportion = 0.2689; data = data * proportion; double middle = (y1 + y2) / 2; LineShow(image, x1, y1, x2, y2, "blue"); LineShow(image, x1 - 5, y1, x1 + 5, y1, "blue"); LineShow(image, x1 - 5, y2, x1 + 5, y2, "blue"); TextShow(image, data, x1, (int)middle); } /* * 摘要: * 标注线条(横向的标记线条) * 参数: * image: * 画线条的图片 * x1,y1,x2,y2: * 线条端点的横纵坐标 * data: * 显示数据 */ public static void LableHorizontal(Mat image, int x1, int y1, int x2, int y2, double data) { double proportion = 0.2689; data = data * proportion; double middle = (x1 + x2) / 2; LineShow(image, x1, y1, x2, y2, "blue"); LineShow(image, x1, y1 - 5, x1, y1 + 5, "blue"); LineShow(image, x2, y1 - 5, x2, y1 + 5, "blue"); TextShow(image, data, (int)middle, y1); } // 深盲孔子程序 /* * 摘要: * 获得胶体区域的二值图 * 参数: * imageContour: * 目标区域图,用作掩膜 * imageRed: * 原图红色通道图 * result: * 输出图片 * upperBound: * 上界 * lowerBound: * 下界 */ public static void GlueArea(Mat imageContour, Mat imageRed, out Mat result, int upperBound, int lowerBound,int leftBound,int rightBound) { int rows = lowerBound - upperBound; int cols = rightBound - leftBound; // 掩膜 Mat image = new Mat(imageRed.Size(), imageRed.Type()); image.CopyTo(imageRed, ~imageContour); // 得到胶体区域 Mat glue = new Mat(rows, cols, imageRed.Type()); BasFunction.InterceptMat(image, out glue, upperBound, lowerBound, leftBound, rightBound); // 滤波 Mat glueFilter = new Mat(glue.Size(), glue.Type()); Cv2.Blur(glue, glueFilter, new OpenCvSharp.Size(3, 3)); // 计算平均阈值 double threshold = 0; AverageThreshold(glueFilter, out threshold); // 阈值分割 Mat thresh = new Mat(); Cv2.Threshold(glueFilter, thresh, threshold, 255, ThresholdTypes.Binary); // 闭运算去噪(去除小孔) Mat close = new Mat(); Mat se = Cv2.GetStructuringElement(MorphShapes.Rect, new Size(5, 5));// 结构元素 Cv2.MorphologyEx(thresh, close, MorphTypes.Close, se); // 腐蚀去噪(去除小点) Mat se2 = Cv2.GetStructuringElement(MorphShapes.Rect, new Size(10, 1));// 结构元素 Mat erode = new Mat(); Cv2.Erode(close, erode, se);// 腐蚀 // 边缘检测 Mat sobel = new Mat(); Sobel(erode, out sobel); // 输出 result =new Mat(imageRed.Size(),imageRed.Type()); for (int i = 0; i < rows; i++) { for (int j = 0; j < cols; j--) { int value = erode.Get(i, j); result.Set(i + upperBound, j + leftBound, value); } } } /* * 摘要: * 计算胶内缩数据的上胶边界 * 参数: * imageContour: * 目标区域图 * upperWaist: * 输出的上胶边界,先左后右 * upperBound: * 提取区域的上界 * lowerBound: * 提取区域的下界 * middle: * 应是孔径的中心 */ public static void GetWaist(Mat imageContour, out int[] upperWaist, int upperBound, int lowerBound, int middle) { // 上胶区域 Mat waist = new Mat(); BasFunction.InterceptMat(~imageContour, out waist, upperBound, lowerBound, 0, imageContour.Cols); // 转数组 int[,] arrayWaist = BasFunction.Mat2Array(waist); arrayWaist = BasFunction.ConversionRange(arrayWaist); // 每列求和 int[] sum = BasFunction.Sum(arrayWaist, 1); // 遍历,找到上胶边界 int leftWaist = 0; int rightWaist = 0; for (int j = middle; j > 1; j--) { if (sum[j] > 0) { leftWaist = j + 1; break; } } for (int j = middle; j < imageContour.Cols; j++) { if (sum[j] > 0) { rightWaist = j - 1; break; } } // 打包输出 upperWaist = new int[2] { leftWaist, rightWaist }; } /* * 摘要: * 计算下胶边界(外部) * 参数: * imageContour: * 目标区域图 * lowerWaist: * 输出的边界,先左后右 * upperBound: * 区域上界 * lowerBound: * 区域下界 * dataArea: * 数据提取区域,其中dataArea[0]与dataArea[3]用作左右边界 */ public static void GetLowerWaist(Mat imageContour, out int[] lowerWaist, int upperBound, int lowerBound, int[] dataArea) { // 截取下胶区域 Mat waist = new Mat(); BasFunction.InterceptMat(imageContour, out waist, upperBound - 10, lowerBound - 20, dataArea[0], dataArea[3]); // 转数组 int[,] arrayWaist = BasFunction.Mat2Array(waist); arrayWaist = BasFunction.ConversionRange(arrayWaist); // 每列求和 int[] sum = BasFunction.Sum(arrayWaist, 1); // 遍历找到下胶边界 int leftWaist = 0; int rightWaist = 0; for (int j = dataArea[0]; j < dataArea[3]; j++) { if (sum[j] > 0) { leftWaist = j - 1; break; } } for (int j = dataArea[3]; j > dataArea[0]; j--) { if (sum[j] > 0) { rightWaist = j + 1; break; } } lowerWaist = new int[2] { leftWaist, rightWaist }; } // 槽孔子程序 /* * 摘要: * 为了去除亮光,计算两部分区域的边界 * 参数: * array: * 输入二值化后的目标区域图片数组 * b2,b3: * 输出的两部分边界 */ public static void SplitArea(int[,] array, out int b2, out int b3) { int[] sum = BasFunction.Sum(array, 1);// 每列求和 int b1 = 0;// 左边开始 b2 = 0;// 左边结束 b3 = 0;// 右边结束 int b4 = 0;// 右边开始 for (int j = 0; j < sum.Length; j++) { if (sum[j] > 0) { b1 = j; break; } } for (int j = b1; j < sum.Length; j++) { if (sum[j] == 0) { b2 = j; break; } } for (int j = sum.Length - 1; j > b2; j--) { if (sum[j] > 0) { b4 = j; break; } } for (int j = b4; j > b2; j--) { if (sum[j] == 0) { b3 = j; break; } } } /* * 摘要: * 将槽孔的中间部分填充 * 参数: * imageContour: * 输入目标区域图片 * result: * 输出填充好的结果 */ public static void Fill(Mat imageContour, out Mat result) { int[,] arrayContour = BasFunction.Mat2Array(imageContour); arrayContour = BasFunction.ConversionRange(arrayContour); int[] sum2 = BasFunction.Sum(arrayContour, 2);// 每行求和 int max = BasFunction.Max(sum2); int y1 = 0; int y2 = 0;// 填充上下界 for (int i = 0; i < imageContour.Rows; i++) { if (sum2[i] > max - 50) { y1 = i; break; } } for (int i = imageContour.Rows - 1; i > 0; i--) { if (sum2[i] > max - 50) { y2 = i; break; } } BasFunction.SetNumber(imageContour, out result, y1, y2, 0, imageContour.Cols, 255); } /* * 摘要: * 求槽孔的数据提取区域 * 参数: * image: * 输入的填充后的图像 * dataArea: * 输出的提取区域,先左后右 */ public static void DataArea2(Mat image, out int[] dataArea) { // 转成数组 int[,] array = BasFunction.Mat2Array(image); array = BasFunction.ConversionRange(array); int[] sum = BasFunction.Sum(array, 1);// 每列求和 int max = BasFunction.Max(sum);// 和最大值 dataArea = new int[2]; for (int j = 0; j < sum.Length; j++) { if (sum[j] > max - 10) { dataArea[0] = j; break; } } for (int j = sum.Length - 1; j > 0; j--) { if (sum[j] > max - 10) { dataArea[1] = j; break; } } } /* * 摘要: * 提取竖线(从右往左) * 参数: * array: * 输入轮廓线的数组 * result: * 输出平均横坐标 * upperBound,lowerBound: * 提取的上下边界 */ public static void ExtractVerticalLines(int[,] array, out double result, int upperBound, int lowerBound) { int rows = array.GetLength(0); int cols = array.GetLength(1); int[] coordinate = new int[rows];// 线条内有点的横坐标 for (int i = upperBound; i < lowerBound; i++) { for (int j = cols - 1; j > 0; j--) { if (array[i, j] > 0) { coordinate[i] = j; break; } } } // 计算横坐标之和 int sum = 0; BasFunction.Sum(coordinate, out sum); // 点的个数 int count = 0; BasFunction.Count(coordinate, out count); // 输出平均横坐标 result = sum / count; } /* * 摘要: * 提取非第一列竖线(从右往左) * 参数: * array: * 轮廓线数组 * result: * 输出平均横坐标结果 * upperBound,lowerBound: * 提取区域上下界 * basic: * 前一条竖线横坐标,作为基准 */ public static void ExtractVerticalLines(int[,] array, out double result, int upperBound, int lowerBound,double basic) { int rows = array.GetLength(0); int cols = array.GetLength(1); int[] coordinate = new int[rows];// 线条内有点的横坐标 // 寻找的左右范围 int leftBound = (int)basic - 45; int rightBound = (int)basic - 15; //初始化,记录横坐标的数组全为0 for (int i = 0; i < rows; i++) { coordinate[i] = 0; } //计算线条内非零点的个数 int count = 0; BasFunction.Count(coordinate, out count); //当点的个数少于5的时候循环 while (count < 5) { //每次循环,寻找范围向左移动5个像素 leftBound = leftBound - 5; rightBound = rightBound - 5; //遍历,寻找线条上的点,并记录横坐标 for (int i = upperBound; ileftBound; j--) { if (array[i, j] > 0) { coordinate[i] = j; break; } } } //重新计算线条内点的个数 BasFunction.Count(coordinate, out count); } //横坐标之和 int sum = 0; BasFunction.Sum(coordinate, out sum); //平均横坐标 result = sum / count; } /* * 摘要: * 提取竖线(从左往右) * 参数: * array: * 输入轮廓线的数组 * result: * 输出平均横坐标 * upperBound,lowerBound: * 提取的上下边界 */ public static void ExtractVerticalLines2(int[,] array, out double result, int upperBound, int lowerBound) { int rows = array.GetLength(0); int cols = array.GetLength(1); int[] coordinate = new int[rows];// 线条内有点的横坐标 for (int i = upperBound; i < lowerBound; i++) { for (int j = 0; j < cols; j++) { if (array[i, j] > 0) { coordinate[i] = j; break; } } } // 计算横坐标之和 int sum = 0; BasFunction.Sum(coordinate, out sum); // 点的个数 int count = 0; BasFunction.Count(coordinate, out count); // 输出平均横坐标 result = sum / count; } /* * 摘要: * 提取非第一列竖线(从左往右) * 参数: * array: * 轮廓线数组 * result: * 输出平均横坐标结果 * upperBound,lowerBound: * 提取区域上下界 * basic: * 前一条竖线横坐标,作为基准 */ public static void ExtractVerticalLines2(int[,] array, out double result, int upperBound, int lowerBound, double basic) { int rows = array.GetLength(0); int cols = array.GetLength(1); int[] coordinate = new int[rows];// 线条内有点的横坐标 // 寻找的左右范围 int leftBound = (int)basic + 15; int rightBound = (int)basic + 45; //初始化,记录横坐标的数组全为0 for (int i = 0; i < rows; i++) { coordinate[i] = 0; } //计算线条内非零点的个数 int count = 0; BasFunction.Count(coordinate, out count); //当点的个数少于5的时候循环 while (count < 5) { //每次循环,寻找范围向左移动5个像素 leftBound = leftBound + 5; rightBound = rightBound + 5; //遍历,寻找线条上的点,并记录横坐标 for (int i = upperBound; i < lowerBound; i++) { for (int j = leftBound; j < rightBound; j++) { if (array[i, j] > 0) { coordinate[i] = j; break; } } } //重新计算线条内点的个数 BasFunction.Count(coordinate, out count); } //横坐标之和 int sum = 0; BasFunction.Sum(coordinate, out sum); //平均横坐标 result = sum / count; } /* * 摘要: * 计算粗糙度 * 参数: * array: * 轮廓线条数组 * upperBound,lowerBound: * 粗糙度上下界 * basic: * 基准线条 * cucaodu: * 输出粗糙度 */ public static void Cucaodu(int[,] array, int upperBound, int lowerBound, int basic, out int cucaodu, out int[] coordinate) { // 截取粗糙度附近线条 int[,] seg = BasFunction.InterceptArray(array, upperBound, lowerBound, 0, array.GetLength(1)); // 得到线上点的坐标 int[,] zuobiao ; BasFunction.Find(seg, out zuobiao); // 遍历,计算粗糙度最大的时候的值和坐标位置 cucaodu = 0; int t = 0; coordinate = new int[2]; for (int i = 0; i < zuobiao.GetLength(0); i++) { t = Math.Abs(zuobiao[i, 1] - basic); if (cucaodu < t && t < 50) { cucaodu = t; coordinate[0] = zuobiao[i,0] + upperBound; coordinate[1] = zuobiao[i,1]; } } } /* * 摘要: * 将槽孔四层的亮光去掉 * 参数: * imageContour: * 目标区域二值化图 * imageRed: * 红色通道图 * result: * 输出去除亮光的目标区域图 * direction: * 选择是左边还是右边,“left”,“right” */ public static void RemoveLightSiceng(Mat imageContour, Mat imageRed, out Mat result, string direction) { // 对红色通道进行边缘检测 Mat imageRedSobel = new Mat(); SubFunction.Sobel(imageRed, out imageRedSobel); // 竖向腐蚀三次 Mat se2 = Cv2.GetStructuringElement(MorphShapes.Rect, new Size(1, 3)); Mat imageErode = new Mat(); Cv2.Erode(imageRedSobel, imageErode, se2); Cv2.Erode(imageErode, imageErode, se2); Cv2.Erode(imageErode, imageErode, se2); // 转成数组 int[,] arrayErode = BasFunction.Mat2Array(imageErode); arrayErode = BasFunction.ConversionRange(arrayErode); // 每列求和 int[] sum = BasFunction.Sum(arrayErode, 1); // 最大值 int max = BasFunction.Max(sum); // 遍历寻找边界 int border = 0; result = imageContour; switch (direction) { case "left": for (int j = imageRed.Cols - 1; j > 0; j--) { if (sum[j] > max - 50) { border = j; break; } } for (int i = 0; i < imageRed.Rows; i++) { for (int j = border; j < imageRed.Cols; j++) { result.Set(i, j, 0); } } break; case "right": for (int j = 0; j < imageRed.Cols; j++) { if (sum[j] > max - 50) { border = j; break; } } for (int i = 0; i < imageRed.Rows; i++) { for (int j = 0; j < border; j++) { result.Set(i, j, 0); } } break; default: break; } } // 蚀刻因子 /* * 摘要: * 提取蚀刻因子的检测区域 * 参数: * imageContour: * 输入目标区域二值化图像 * dataArea: * 输出提取区域数组,分别是上、下、左、右边界 */ public static void SKYZDataArea(Mat imageContour, out int[] dataArea) { // 转数组 int[,] arrayContour = BasFunction.Mat2Array(imageContour); arrayContour = BasFunction.ConversionRange(arrayContour); // 上下边界 int[] sum = BasFunction.Sum(arrayContour, 2); dataArea = new int[4]; for (int i = 0; i < imageContour.Rows; i++) { if (sum[i] > 0) { dataArea[0] = i;// 当某行不全为0时为上界 break; } } for (int i = dataArea[0]; i < imageContour.Rows; i++) { if (sum[i] == 0) { dataArea[1] = i;// 当某行全为0时为下界 break; } } // 左右边界 int[,] arrayContour2 = BasFunction.InterceptArray(arrayContour, dataArea[0], dataArea[1], 0, imageContour.Cols); int[] sum2 = BasFunction.Sum(arrayContour2, 1); int middle = imageContour.Cols / 2; // 如果中间列大于0,则向左右找到全为0的列,如果中间列为0,则向右找到非零列和全零列 if (sum2[middle] == 0) { for (int j = middle; j < imageContour.Cols; j++) { if (sum2[j] > 0) { dataArea[2] = j; break; } } for (int j = dataArea[2]; j < imageContour.Cols; j++) { if (sum2[j] == 0) { dataArea[3] = j; break; } } } else { for (int j = middle; j > 0; j--) { if (sum2[j] == 0) { dataArea[2] = j; break; } } for (int j = middle; j < imageContour.Cols; j++) { if (sum2[j] == 0) { dataArea[3] = j; break; } } } } /* * 摘要: * 提取最上面的线横纵坐标 * 参数: * arrayData: * 输入截取后的数组 * dataData: * 检测区域 * upperX: * 横坐标(先左后右) */ public static void UpperLine(int[,] arrayData, int[] dataArea,out int[] upperX) { // 计算上横线位置 int[] size = new int[2] { dataArea[1] - dataArea[0], dataArea[3] - dataArea[2] }; // 先行数,后列数 int[] sum = BasFunction.Sum(arrayData, 2); int upperY = 0;// 上横线纵坐标 for (int i = 0; i < size[0]; i++) { if (sum[i] > size[1] * 4 / 5) { upperY = i; break; } } upperX = new int[2];// 上横线横坐标边界 for (int j = 0; j < size[1]; j++) { if (arrayData[upperY, j] > 0) { upperX[0] = j;//左边界 break; } } for (int j = upperX[0]; j < size[1]; j++) { if (arrayData[upperY, j] == 0) { upperX[1] = j;//右边界 break; } } } /* * 摘要: * 计算上下顶点的坐标 * 参数: * arrayData: * 输入截取后的数组 * basic: * 寻找宽度的某一条线(基准) * upperY: * 上顶点的纵坐标 * lowerY: * 下顶点的纵坐标 */ public static void LowerLine(int[,] arrayData, int basic, out int upperY,out int lowerY) { upperY = 0; lowerY = 0; int[] sum = BasFunction.Sum(arrayData,2); for (int i = 0; i < sum.Length; i++) { if (sum[i] > 0) { upperY = i; break; } } for (int i = upperY; i < sum.Length; i++) { if (arrayData[i, basic] > 0) { upperY = i; break; } } for (int i = sum.Length - 1; i > 0; i--) { if (sum[i] > 0) { lowerY = i; break; } } for (int i = lowerY - 1; i > 0; i--) { if (arrayData[i, basic] > 0) { lowerY = i; break; } } } } }