我们通过高度图找到了我们想要的点集,但是通常情况,点集过于复杂,实际生成的轮廓不够好看。所以我们需要对点集进行筛选。同时一开始我是仅生成矩形轮廓,对于部分形状特别的检测内容会出现检测轮廓的面积于实际面积相差过多的情况,所以需要生成成多边形形状,但是多边形的边数不易过多。最终选取10边形作为参考。
//输入图像
threshCropMap.ImWrite("D:\\test\\threshCropMap_BeforeFilterByBlob.bmp");
//设定我们要筛选的高度
var ResultHeight = 60;
//创建对应高度的图像,由于是高度信息图,所有要使用32位来存放数据
Mat mat = new Mat(filter.Rows, filter.Cols, MatType.CV_32F, new Scalar(ResultHeight));
mat.ImWrite("D:\\test\\mat.bmp");
Mat ResultMat = new Mat(filter.Rows, filter.Cols, MatType.CV_32F, new Scalar(0));
//将基础图像减去高度图像,将低于指定高度的图像置为负值
Cv2.Subtract(threshCropMap, mat, ResultMat);
ResultMat.ImWrite("D:\\test\\ResultMat.bmp");
//将负值的数值置为0,仅保留大于指定高度的图像
Mat mask = new Mat();
Cv2.Compare(ResultMat, new Scalar(0), mask, CmpType.GT);
mask.ImWrite("D:\\test\\mask.bmp");
Mat resultMat = new Mat();
ResultMat.CopyTo(resultMat, mask);
//将最终图像转换为8位灰度图,方便后续处理结果
resultMat.ConvertTo(resultMat, MatType.CV_8UC1);
int num = resultMat.CountNonZero();
Point[][] contours;
HierarchyIndex[] hierarchy;
Cv2.FindContours(resultMat, out contours, out hierarchy, RetrievalModes.External, ContourApproximationModes.ApproxSimple);
//判断是否生成多边形
if (Width* Height> IsPolyArea * 1.08)
{
//降低高度,重新生成轮廓,用于降低面积误差
var ResultHeight1 = 10;
Mat mat1 = new Mat(filter.Rows, filter.Cols, MatType.CV_32F, new Scalar(ResultHeight1));
Mat ResultMat1 = new Mat(filter.Rows, filter.Cols, MatType.CV_32F, new Scalar(0));
Cv2.Subtract(result1, mat1, ResultMat1);
//由于使用32F的数据格式,所以图像相减会出现负数,这里将全部负数置为0
Mat mask1 = new Mat();
Cv2.Compare(ResultMat1, new Scalar(0), mask1, CmpType.GT);
Mat resultMat1 = new Mat();
Mat ResultMat3 = new Mat();
ResultMat1.CopyTo(resultMat1, mask1);
resultMat1.ConvertTo(resultMat1, MatType.CV_8UC1);
Point[][] contour1;
HierarchyIndex[] hierarchy1;
//降低高度后,二次寻找轮廓,防止误差过大
Cv2.FindContours(resultMat1, out contour1, out hierarchy1, RetrievalModes.External, ContourApproximationModes.ApproxSimple);
var sortedArray = contour1.OrderByDescending(x => x.Length).ToList();
Mat floatContourMat = new Mat(sortedArray[0].Length, 1, MatType.CV_32FC2);
for (int i = 0; i < sortedArray[0].Length; i++)
{
floatContourMat.At<Point>(i, 0) = sortedArray[0][i];
}
//定义初始拟合系数
double epsilon = 0.0001;
Mat approxPolygonMat = new Mat();
Cv2.ApproxPolyDP(floatContourMat, approxPolygonMat, epsilon, true);
// 获取生成的多边形的点(这里确保得到10个点,可能需要进一步验证和调整epsilon)
Point[] approxPolygonPoints = GetPoints(approxPolygonMat);
//只有点集大于15点时才需要拟合,防止拟合时候出现运算过多情况
if (sortedArray[0].Length < 15)
{
approxPolygonPoints = sortedArray[0];
}
else
{
//设置最大拟合精度的超过上限,如果不设置会出现无限接近10个点,但是又无法解决的情况,造成软件始终无法运算结束
while (approxPolygonPoints.Length > 10&&epsilon < Math.Pow(1.1, 50))
{
epsilon *= 1.1; // 适当增大epsilon,再次尝试逼近
Cv2.ApproxPolyDP(floatContourMat, approxPolygonMat, epsilon, true);
approxPolygonPoints = GetPoints(approxPolygonMat);
}
while (approxPolygonPoints.Length < 10&& epsilon>Math.Pow(0.1,50))
{
epsilon *= 0.9; // 适当减小epsilon,再次尝试逼近
Cv2.ApproxPolyDP(floatContourMat, approxPolygonMat, epsilon, true);
approxPolygonPoints = GetPoints(approxPolygonMat);
}
}
approxPolygonPoints= RotatePoints(approxPolygonPoints, (Point)rotatedRect.Center, -rotatedRect.Angle);
//approxPolygonPoints为结果点集
}
实际生成的效果