OpenCvSharp函数:ContourArea、ArcLength、BoundingRect、MinAreaRect、BoxPoints
ContourArea计算轮廓面积
//函数原型1
double ContourArea(InputArray contour,
bool oriented = false)
//函数原型2
double ContourArea(IEnumerable<Point> contour,
bool oriented = false)
//函数原型3
double ContourArea(IEnumerable<Point2f> contour,
bool oriented = false)
ContourArea计算轮廓面积:通过格林公式(Green formula)计算轮廓面积,返回的面积与非零像素与DrawContours或FillPoly可能不同。另外,如果轮廓自相互,计算的面积是有误(见图例中的3) | |
参数 | 说明 |
InputArray contour IEnumerable<Point> contour IEnumerable<Point2f> contour | 待计算轮廓坐标组:若oriented为true,坐标的顺序会影响返回结果值的正负号 |
bool oriented | 是否忽略方向:为false时,返回面积绝对值。 为true时,若轮廓坐标为逆时针时,值为负,若轮廓坐标为顺时针时,值为正。 FindContours的外轮廓为逆时针,内轮廓为顺时针。 |
ArcLength计算弧长
//函数原型1
double ArcLength(InputArray curve,
bool closed)
//函数原型2
double ArcLength(IEnumerable<Point> curve,
bool closed)
//函数原型3
double ArcLength(IEnumerable<Point2f> curve,
bool closed)
ArcLength计算轮廓周长或曲线长度(两点的L2距离累加) | |
参数 | 说明 |
InputArray curve IEnumerable<Point> curve IEnumerable<Point2f> curve | 轮廓/曲线坐标数组 |
bool closed | 是否封闭 |
BoundingRect轮廓外接矩形
//函数原型1
Rect BoundingRect(InputArray curve)
//函数原型2
Rect BoundingRect(IEnumerable<Point> curve)
//函数原型3
Rect BoundingRect(IEnumerable<Point2f> curve)
BoundingRect返回坐标点集的外接矩形 X=minX,Y=minY Width=maxX-minX,Height=maxY-minY | |
参数 | 说明 |
InputArray curve IEnumerable<Point> curveIEnumerable<Point2f> curve | 待计算外接矩形的坐标点集:必须为CV_32SC2或 CV_32FC2 |
MinAreaRect查找包含轮廓的最小外接矩形
//函数原型1
RotatedRect MinAreaRect(InputArray points)
//函数原型2
RotatedRect MinAreaRect(IEnumerable<Point> points)
//函数原型3
RotatedRect MinAreaRect(IEnumerable<Point2f> points)
MinAreaRect查找包含轮廓的最小面积旋转矩形 | |
参数 | 说明 |
InputArray points IEnumerable<Point> points IEnumerable<Point2f> points | 待检测的轮廓(坐标点集) |
返回值 | 包含了轮廓(坐标点集)的最小旋转矩形 |
RotatedRect旋转矩形结构体 | |
属性、方法 | 说明 |
Point2f Center | 矩形质心 |
Size2f Size | 矩形的宽、高 |
float Angle | 矩形旋转角度:旋转角度θ是水平轴(x轴)顺时针旋转,直到碰到矩形的第一条边停住,此时该边与水平轴的夹角。并且这个边的边长是width,另一条边边长是height。也就是说,在这里,width与height不是按照长短来定义的。 |
Point2f[] Points() | 旋转矩形的四个顶点 |
Rect BoundingRect() | 包含旋转矩形的最小右上矩形 |
BoxPoints查找旋转矩形的四个顶点
//函数原型1
void BoxPoints(RotatedRect box,
OutputArray points)
//函数原型2
Point2f[] BoxPoints(RotatedRect box)
BoxPoints查找旋转矩形的四个顶点(用RotatedRect对象的Points属性即可) | |
参数 | 说明 |
RotatedRect box | 输入的旋转矩形 |
OutputArray points 返回值 | 旋转矩形的四个顶点坐标数组 |
原图顺时针旋转20度
源码示例
public void Run(ParamBase paramBase)
{
#region 测试ContourArea的参数oriented=true时,顺、逆时针方向的正负值
Point[][] testContourAreaOriented = new Point[][] { new Point[] { new Point(50,50),new Point(150,50),new Point(150,150),new Point(50,150)},
new Point[] { new Point(50,200), new Point(50, 300),new Point(150,300),new Point(150, 200) },
new Point[] { new Point(50,350), new Point(150, 450),new Point(150,350),new Point(50,450)}};
using (Mat canvas = Mat.Zeros(new Size(400, 482), MatType.CV_8UC3))
{
foreach (var Points in testContourAreaOriented)
{
var color = Scalar.RandomColor();
for (int index = 1; index <= Points.Length; index++)
{
canvas.Line(Points[index - 1], Points[index % 4], color);
//在起点标上数字,方便判断顺时针还是逆时针
canvas.PutText($"{index - 1}", Points[index - 1], HersheyFonts.HersheySimplex, 1, color, 2);
ImShowAndWait("Verify ContourArea return value", canvas);
}
//计算轮廓面积
var area = Cv2.ContourArea(Points, true);
//顺时针,还是逆时針
var direction = Points[1].X > Points[0].X ? "clockwise" : "counter-clockwise";
canvas.PutText($"{direction},area={area}", Points[0], HersheyFonts.HersheySimplex, 0.5, color, 1);
//计算轮廓弧长
var arc = Cv2.ArcLength(Points, true);
canvas.PutText($"arc={arc}", GetOffsetPoint(Points[0]), HersheyFonts.HersheySimplex, 0.5, color, 1);
ImShowAndWait("Verify ContourArea return value", canvas);
}//顺时针为正,逆时针为负
}
#endregion
#region ContourArea
using (var srcGray = Cv2.ImRead(ImagePath.Contours, ImreadModes.Grayscale))
{
if (srcGray.Empty()) throw new Exception("图像读取有误");
Cv2.ImShow("srcGray", srcGray);
//用大律法二值化
Cv2.Threshold(srcGray, srcGray, 0, 255, ThresholdTypes.Otsu | ThresholdTypes.BinaryInv);
//检索轮廓
Cv2.FindContours(srcGray, out Point[][] contours, out _, RetrievalModes.CComp, ContourApproximationModes.ApproxSimple);
using (var srcColor = srcGray.CvtColor(ColorConversionCodes.GRAY2BGR))
{
foreach (Point[] Points in contours)
{
//外轮廓为负,内轮廓为正
var area = Cv2.ContourArea(Points, true);
srcColor.PutText($"area={area}", Points[0], HersheyFonts.HersheySimplex, 0.5, Scalar.Red);
var arc = Cv2.ArcLength(Points, true);
srcColor.PutText($"arc={arc}", GetOffsetPoint(Points[0]), HersheyFonts.HersheySimplex, 0.5, Scalar.Red, 1);
}
ImShowAndWait("ContourArea", srcColor);
}
}
#endregion
using (var srcGray = Cv2.ImRead(ImagePath.Contours, ImreadModes.Grayscale))
{
if (srcGray.Empty()) throw new Exception("图像读取有误");
Cv2.ImShow("srcGray", srcGray);
//用大律法二值化
Cv2.Threshold(srcGray, srcGray, 0, 255, ThresholdTypes.Otsu | ThresholdTypes.BinaryInv);
//正逆时针,负顺时针
var angle = -20;
var rotMat = Cv2.GetRotationMatrix2D(new Point2f(srcGray.Width / 2f, srcGray.Height / 2f), angle, 1);
var sqrt2 = Math.Sqrt(2);
//中点偏移
var offsetMat = new Mat<double>(2, 3, new double[] { 0,0, (srcGray.Width /2d),
0,0, (srcGray.Height /2d)});
//顺时针旋转,加中心偏移
Cv2.WarpAffine(srcGray, srcGray, rotMat + offsetMat, new Size(srcGray.Width * 2, srcGray.Height * 2));
//检索轮廓
Cv2.FindContours(srcGray, out Point[][] contours, out _, RetrievalModes.External, ContourApproximationModes.ApproxSimple);
using (var srcColor = srcGray.CvtColor(ColorConversionCodes.GRAY2BGR))
{
foreach (Point[] Points in contours)
{
//最小外接面积旋转旋转矩形
var minRect = Cv2.MinAreaRect(Points);
//查找最小面积旋转矩形的四个顶点
var boxPointfs = minRect.Points();
//var boxPointfs2 = Cv2.BoxPoints(minRect);与minRect.Points()等价
var boxPoints = Array.ConvertAll<Point2f, Point>(boxPointfs, z => new Point(z.X, z.Y));
//绘制最小外接旋转矩形
Cv2.DrawContours(srcColor, new Point[][] { boxPoints }, -1, Scalar.OrangeRed);
Cv2.PutText(srcColor,
$"MinAreaRect,Width={minRect.Size.Width.ToString("0")},Height={minRect.Size.Height.ToString("0")},Angle={minRect.Angle.ToString("0.0")}",
boxPointfs[0].ToPoint(),
HersheyFonts.HersheySimplex,
0.5,
Scalar.OrangeRed);
//最小外接矩形质点
Cv2.Circle(srcColor, (int)minRect.Center.X, (int)minRect.Center.Y, 2, Scalar.OrangeRed, -1);
//包含最小外接旋转矩形的右上矩形
var minBoundingRect = minRect.BoundingRect();
Cv2.Rectangle(srcColor, minBoundingRect, Scalar.LightYellow);
srcColor.PutText($"minRect.BoundingRect,Size=({(int)(minBoundingRect.Size.Width)},{(int)(minBoundingRect.Size.Height)})", minBoundingRect.Location, HersheyFonts.HersheySimplex, 0.5, Scalar.LightYellow);
//外接矩形
var rect = Cv2.BoundingRect(Points);
Cv2.Rectangle(srcColor, rect, Scalar.Green);
Cv2.PutText(srcColor, $"BoundingRect,Width={rect.Width},Height={rect.Height}", rect.Location, HersheyFonts.HersheySimplex, 0.5, Scalar.Green);
}
ImShowAndWait("Rect", srcColor);
}
}
Cv2.DestroyAllWindows();
}
/// <summary>
/// 返回一个偏移坐标
/// </summary>
/// <param name="point"></param>
/// <returns></returns>
private Point GetOffsetPoint(Point point)
{
return new Point(point.X, point.Y - 15);
}
/// <summary>
/// 显示窗口并按任意键继续
/// </summary>
/// <param name="winName"></param>
/// <param name="canvas"></param>
private void ImShowAndWait(string winName, Mat canvas)
{
Cv2.ImShow(winName, canvas);
Cv2.WaitKey();
}
参考
更多推荐
所有评论(0)