pencvsharp4.6 编写采亚像素快速找圆心函数, 这个函数会输入一张图、一个圆心中心位置的大约位置、输入找圆扇形区域的最小半径和最大半径、设定扇形区域起始角度、扇形区域结束角度、扇形区域的法线上找交点个数 、找边沿点宽度、从内到外找圆心、从黑到白找圆惦、找圆分数、 canny参数 根据给定的圆心位置和半径范围定义一个圆环区域。 对轮廓上的点进行拟合,得到圆心和半径。 将圆环区域分成多个扇形区域并在扇形区域,然后在每个扇形区域内找到边缘点加到一个点集中,并拟合出一个圆。最后,从所有拟合出的圆中选出得分最并判断是否达到设定的标定。为了加快计算速度,使用了并行计算,并将圆环区域分成了多个部分。这个函数的实现中还使用了许多 OpenCV 的函数,如 Canny 边缘检测、轮廓查找、矩阵计算等 请编写函数代码
时间: 2024-01-24 15:20:38 浏览: 123
以下是基于 OpenCVSharp4.6 的代码实现:
```csharp
using System;
using System.Collections.Generic;
using System.Linq;
using OpenCvSharp;
public static class CircleDetector
{
public static CircleResult FindCircle(Mat image, Point2f approxCenter, float minRadius, float maxRadius, float startAngle, float endAngle, int numIntersectionPoints, int edgeWidth, float minScore, double cannyThreshold1 = 50, double cannyThreshold2 = 150)
{
// 定义圆心区域
var innerRadius = minRadius * 0.9f;
var outerRadius = maxRadius * 1.1f;
var centerRect = new Rect(
(int)(approxCenter.X - outerRadius),
(int)(approxCenter.Y - outerRadius),
(int)(outerRadius * 2),
(int)(outerRadius * 2));
// 裁剪图像,只处理圆心区域
var centerImage = new Mat(image, centerRect);
// Canny 边缘检测
var edges = new Mat();
Cv2.Canny(centerImage, edges, cannyThreshold1, cannyThreshold2, 3, false);
// 轮廓查找
var contours = new List<Point[]>();
var hierarchy = new Mat();
Cv2.FindContours(edges, contours, hierarchy, RetrievalModes.List, ContourApproximationModes.ApproxSimple);
// 将轮廓点集转换为矩形区域
var contourRects = contours.Select(c => Cv2.BoundingRect(c)).ToList();
// 将圆环区域分成多个扇形区域
var sectorRects = SplitSector(centerRect, approxCenter, minRadius, maxRadius, startAngle, endAngle);
// 在每个扇形区域内找到边缘点加到一个点集中
var edgePoints = new List<Point>();
foreach (var sectorRect in sectorRects)
{
var sectorEdges = new Mat(edges, sectorRect);
var sectorContours = new List<Point[]>();
Cv2.FindContours(sectorEdges, sectorContours, hierarchy, RetrievalModes.List, ContourApproximationModes.ApproxSimple);
// 获取轮廓点的坐标
var sectorContourPoints = sectorContours.SelectMany(c => c).ToList();
if (sectorContourPoints.Count == 0)
{
continue;
}
// 找到每个扇形区域内的边缘点
var sectorEdgePoints = new List<Point>();
foreach (var intersectionPoint in GetIntersectionPoints(sectorRect, approxCenter, numIntersectionPoints))
{
var edgePoint = FindEdgePoint(sectorEdges, intersectionPoint, edgeWidth);
if (edgePoint != null)
{
sectorEdgePoints.Add(edgePoint.Value + sectorRect.Location);
}
}
// 将扇形区域内的边缘点加入点集
if (sectorEdgePoints.Count > 0)
{
edgePoints.AddRange(sectorEdgePoints);
}
}
// 拟合圆心和半径
var circle = Cv2.MinEnclosingCircle(edgePoints);
// 计算拟合出来的圆是否符合要求
var score = CalculateCircleScore(image, circle.Center, circle.Radius, minScore);
return new CircleResult
{
Center = circle.Center + centerRect.Location,
Radius = circle.Radius,
Score = score
};
}
// 将圆环区域分成多个扇形区域
private static List<Rect> SplitSector(Rect centerRect, Point2f center, float minRadius, float maxRadius, float startAngle, float endAngle)
{
var sectorRects = new List<Rect>();
// 以圆心为原点,计算起始角度和结束角度的弧度值
var startRad = startAngle / 180 * Math.PI;
var endRad = endAngle / 180 * Math.PI;
// 计算每个扇形区域的角度范围和矩形区域
for (var i = 0; i < 8; i++)
{
var sectorStartRad = startRad + i * Math.PI / 4;
var sectorEndRad = endRad + i * Math.PI / 4;
var sectorStart = new Point2f((float)Math.Cos(sectorStartRad), (float)Math.Sin(sectorStartRad));
var sectorEnd = new Point2f((float)Math.Cos(sectorEndRad), (float)Math.Sin(sectorEndRad));
var sectorPoints = new[] { sectorStart * maxRadius + center, sectorEnd * maxRadius + center, sectorEnd * minRadius + center, sectorStart * minRadius + center };
var sectorRect = Cv2.BoundingRect(sectorPoints);
// 将扇形区域矩形加入列表
if (sectorRect.Intersect(centerRect) != Rect.Empty)
{
sectorRects.Add(sectorRect);
}
}
return sectorRects;
}
// 获取扇形区域的交点坐标
private static List<Point> GetIntersectionPoints(Rect sectorRect, Point2f center, int numPoints)
{
var circlePoints = Enumerable.Range(0, numPoints).Select(i => (float)i / numPoints * Math.PI * 2).Select(rad => new Point2f((float)Math.Cos(rad), (float)Math.Sin(rad)) * sectorRect.Width / 2 + center).ToList();
var sectorPoints = new[] { new Point2f(sectorRect.Left, sectorRect.Top), new Point2f(sectorRect.Right, sectorRect.Top), new Point2f(sectorRect.Right, sectorRect.Bottom), new Point2f(sectorRect.Left, sectorRect.Bottom) };
var intersectionPoints = new List<Point>();
foreach (var cp in circlePoints)
{
foreach (var sp in sectorPoints)
{
var d = cp - sp;
var r = sectorRect.Width / 2;
if (Math.Abs(d.X * d.X + d.Y * d.Y - r * r) < r * 0.01)
{
intersectionPoints.Add(cp.Round());
break;
}
}
}
return intersectionPoints;
}
// 找到边缘点
private static Point? FindEdgePoint(Mat edges, Point start, int width)
{
var dx = new[] { -1, 0, 1, 1, 1, 0, -1, -1 };
var dy = new[] { -1, -1, -1, 0, 1, 1, 1, 0 };
var p = start;
var dir = 0;
for (var i = 0; i < 8; i++)
{
var d = new Point(dx[i], dy[i]);
var np = p + d;
if (np.X < 0 || np.Y < 0 || np.X >= edges.Width || np.Y >= edges.Height)
{
continue;
}
if (edges.Get<byte>(np) > 0)
{
dir = i;
break;
}
}
if (dir == 0)
{
return null;
}
while (true)
{
var a = dir - width / 2 + 8;
var b = dir + width / 2 + 8;
var sum = 0;
var count = 0;
for (var i = a; i <= b; i++)
{
var d = new Point(dx[i % 8], dy[i % 8]);
var np = p + d;
if (np.X < 0 || np.Y < 0 || np.X >= edges.Width || np.Y >= edges.Height)
{
continue;
}
sum += edges.Get<byte>(np);
count++;
}
if (count == 0)
{
break;
}
if (sum / count > 128)
{
dir--;
}
else
{
dir++;
}
dir = (dir + 8) % 8;
var np2 = p + new Point(dx[dir], dy[dir]);
if (np2.X < 0 || np2.Y < 0 || np2.X >= edges.Width || np2.Y >= edges.Height)
{
break;
}
if (edges.Get<byte>(np2) == 0)
{
break;
}
p = np2;
}
return p;
}
// 计算拟合出来的圆是否符合要求
private static float CalculateCircleScore(Mat image, Point2f center, float radius, float minScore)
{
var score = 0f;
var count = 0;
var circlePoints = Enumerable.Range(0, 360).Select(i => (float)i / 180 * Math.PI).Select(rad => new Point2f((float)Math.Cos(rad), (float)Math.Sin(rad)) * radius + center).ToList();
foreach (var p in circlePoints)
{
if (p.X < 0 || p.Y < 0 || p.X >= image.Width || p.Y >= image.Height)
{
continue;
}
var val = image.Get<byte>((int)p.Y, (int)p.X);
if (val < 128)
{
score += 1 - (float)val / 128;
count++;
}
}
if (count == 0)
{
return 0;
}
score /= count;
return score >= minScore ? score : 0;
}
}
public class CircleResult
{
public Point2f Center { get; set; }
public float Radius { get; set; }
public float Score { get; set; }
}
```
使用时,可以按如下方式调用:
```csharp
var image = new Mat("input.png", ImreadModes.Grayscale);
var approxCenter = new Point2f(300, 300);
var minRadius = 100;
var maxRadius = 200;
var startAngle = 30;
var endAngle = 150;
var numIntersectionPoints = 8;
var edgeWidth = 20;
var minScore = 0.8f;
var result = CircleDetector.FindCircle(image, approxCenter, minRadius, maxRadius, startAngle, endAngle, numIntersectionPoints, edgeWidth, minScore);
if (result.Score >= minScore)
{
Cv2.Circle(image, result.Center, (int)result.Radius, Scalar.Red, 2);
}
```
阅读全文