失眠网,内容丰富有趣,生活中的好帮手!
失眠网 > 【OpenCV-Python】教程:3-9 轮廓(2)轮廓特征

【OpenCV-Python】教程:3-9 轮廓(2)轮廓特征

时间:2024-01-25 16:48:45

相关推荐

【OpenCV-Python】教程:3-9 轮廓(2)轮廓特征

OpenCV Python 轮廓特征

【目标】

轮廓矩轮廓周长、轮廓面积轮廓拟合、轮廓凸包、轮廓凹凸性检查外接矩形、最小包围圈椭圆拟合、直线拟合

【代码】

周长、面积、矩

第一幅图像为原始轮廓图像,第二幅图像为轮廓点拟合图像(精度为周长的1/10),第三幅图像为轮廓点拟合图像(精度为周长的 5/1000)

import numpy as npimport cv2# 读入图像并二值化img = cv2.imread('star1.png', 0)img_color = cv2.cvtColor(img, cv2.COLOR_GRAY2BGR)ret,thresh = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY)# 寻找轮廓,图像中第一个轮廓就是需要的轮廓,# 如果在其他程序中,需要根据一定条件进行筛选# 图像矩可以计算一些特征,例如:物体的质心,物体的面积# Cx = M10 / M00, Cy = M01 / M00contours, hierarchy = cv2.findContours(thresh, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)cnt = contours[0]M = cv2.moments(cnt)print("星号的矩为:", M )img_color0 = img_color.copy()cv2.drawContours(img_color0, [cnt], 0, (0, 128, 255), 3, maxLevel=0)cv2.imshow("contours-src", img_color0)# contour的面积可以直接使用 contourArea# 也可以用 M['m00'] 来获得area = cv2.contourArea(cnt)print("星号的面积为:", area)# 输出周长# 第二个参数如果为True,则为闭合的轮廓,否则为弧perimeter = cv2.arcLength(cnt, True)print("星号的周长为:", perimeter)# 轮廓近似# 用另一个更少点的形状去拟合原来的轮廓(使用算法为 道格拉斯-普克算法 , # 将曲线近似表示为一系列点,并减少点的数量的一种 算法)img_color1 = img_color.copy()epsilon = 0.1 * perimeterapprox = cv2.approxPolyDP(cnt, epsilon, True)cv2.drawContours(img_color1, [approx], 0, (0, 255, 0), 3, maxLevel=0)cv2.imshow("0.1-approxPolyDP", img_color1)# 拟合精度更高,需要的点回更多一些img_color2 = img_color.copy()epsilon2 = 0.005 * perimeterapprox2 = cv2.approxPolyDP(cnt, epsilon2, True)cv2.drawContours(img_color2, [approx2], 0, (0, 255, 255), 3, maxLevel=0)cv2.imshow("0.005-approxPolyDP", img_color2)cv2.waitKey(0)cv2.destroyAllWindows()

以上代码打印如下:

星号的矩为: {'m00': 130522.0, 'm10': 33632144.0, 'm01': 24005228.166666664, 'm20': 10974770282.666666, 'm11': 6224358011.416666, 'm02': 5406270574.5, 'm30': 4011914771313.0, 'm21': 2046283287276.75, 'm12': 1408108180508.5833, 'm03': 1363179635382.75, 'mu20': 2308636527.15621, 'mu11': 38831506.65739727, 'mu02': 991298544.2389975, 'mu30': -5751574310.887695, 'mu21': 7823576580.904053, 'mu12': 768507355.5574036, 'mu03': 4240497589.183838, 'nu20': 0.13551523513082395, 'nu11': 0.0022793803586064827, 'nu02': 0.058188482130994895, 'nu30': -0.0009344959758346628, 'nu21': 0.001271147765169143, 'nu12': 0.00012486442708533664, 'nu03': 0.000688981436808956}星号的面积为: 130522.0星号的周长为: 2386.9503506422043

凸包

# 什么是凸包?# 凸: 凸对象内部任意两点连接所有点都在图像内部,则为凸对象,否则为非凸或凹对象。# 包: 能够完全包住对象的最小形状import numpy as npimport cv2# 读入灰度图像,并阈值化image = cv2.imread("worldmap.png", 0)ret, thresh = cv2.threshold(image, 200, 255, cv2.THRESH_BINARY_INV)# 寻找阈值图像的轮廓contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)# # 画出所有轮廓map_color = cv2.cvtColor(image, cv2.COLOR_GRAY2BGR)cv2.drawContours(map_color, contours, -1, (0, 255, 0), 1)# 计算各个轮廓的凸包hull = []for i in range(len(contours)):hull.append(cv2.convexHull(contours[i], False))# 创建一个黑色的画布drawing = np.zeros((thresh.shape[0], thresh.shape[1], 3), np.uint8)# 画轮廓和凸包点for i in range(len(contours)):color_contours = (0, 255, 0) # green - color for contourshullcolor = (0, 0, 255) # blue - color for convex hull# 画轮廓cv2.drawContours(drawing, contours, i, color_contours, 1, 8, hierarchy)# 画凸包cv2.drawContours(drawing, hull, i, hullcolor, 1, 8)cv2.imshow("drawing", drawing)cv2.imshow("map_color", map_color)cv2.imshow("thresh", thresh)cv2.waitKey(0)cv2.destroyAllWindows()

拟合

import numpy as npimport cv2# 读入灰度图像,并阈值化image = cv2.imread("testfit.png", 0)ret, thresh = cv2.threshold(image, 127, 255, cv2.THRESH_BINARY)colorim = cv2.cvtColor(image, cv2.COLOR_GRAY2BGR)# 寻找阈值图像的轮廓contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)cnt = contours[0]# 找到最小的外接矩形boundingRectImg = colorim.copy()x,y,w,h = cv2.boundingRect(cnt)cv2.rectangle(boundingRectImg, (x,y), (x+w, y+h), (0, 255, 0), 2)# 找到最小倾斜矩形minAreaRectImg = colorim.copy()rect = cv2.minAreaRect(cnt)box = cv2.boxPoints(rect)box = np.int0(box)cv2.drawContours(minAreaRectImg, [box], 0, (0, 0, 255), 2)# 最小圆拟合minEnclosingCircleImg = colorim.copy()(x,y),radius = cv2.minEnclosingCircle(cnt)center = (int(x),int(y))radius = int(radius)cv2.circle(minEnclosingCircleImg, center, radius, (0,255,0), 2)# 最小椭圆拟合fitEllipseImg = colorim.copy()ellipse = cv2.fitEllipse(cnt)cv2.ellipse(fitEllipseImg, ellipse, (0,255,0), 2)# 直线拟合fitLineImg = colorim.copy()rows, cols = image.shape[:2][vx, vy, x, y] = cv2.fitLine(cnt, cv2.DIST_L2, 0, 0.01, 0.01)lefty = int((-x*vy/vx) + y)righty = int(((cols-x)*vy/vx)+y)cv2.line(fitLineImg, (cols-1, righty), (0, lefty), (0, 255, 0), 2)cv2.imshow("thresh", thresh)cv2.imshow("boundingRect", boundingRectImg)cv2.imshow("minAreaRect", minAreaRectImg)cv2.imshow("minEnclosingCircleImg", minEnclosingCircleImg)cv2.imshow("fitEllipseImg", fitEllipseImg)cv2.imshow("fitLineImg", fitLineImg)cv2.waitKey(0)cv2.destroyAllWindows()

【接口】

moments

Moments cv::moments(InputArray array,bool binaryImage = false );

cv2.moments(array[, binaryImage]) ->retval

计算图像最高到三阶的所有矩;

array: 数字化图像, 单通道, 8位或2D浮点矩阵或者 1xN 或者 Nx1 的二维点集binaryImage: 如果为真,则所有非零为1,该参数只适合图像。

contourArea

double cv::contourArea(InputArray contour,bool oriented = false );

cv2.contourArea(contour[, oriented]) ->retval

计算一个轮廓的面积

contour: 输入的2维点集的vector, 存储为 std::vector或者Matoriented: 定向区域标志,如果为真,则返回有符号的面积值,可以通过值的正负来确定方向,一般情况下默认为false.

arcLength

double cv::arcLength(InputArray curve,bool closed );

cv2.arcLength(curve, closed) ->retval

计算一个轮廓的周长或者一个曲线的弧长

curve: 输入的2维点集的vector, 存储为 std::vector或者Matclosed: 表明曲线是否为闭合

approxPolyDP

void cv::approxPolyDP(InputArray curve,OutputArray approxCurve,double epsilon,bool closed );

cv2.approxPolyDP(curve, epsilon, closed[, approxCurve]) ->approxCurve

近似拟合以一定精度拟合很多个点的曲线,道格拉斯-普克算法

curve: 输入的2维点集的vector, 存储为 std::vector或者MatapproxCurve: 近似拟合的曲线结果epsilon: 精度closed: 如果为真,则为闭合的曲线,首尾相连

convexHull

void cv::convexHull(InputArray points,OutputArray hull,bool clockwise = false,bool returnPoints = true );

cv2.convexHull(points[, hull[, clockwise[, returnPoints]]]) ->hull

找到点集的一个凸包

points: 输入的2维点集的vector, 存储为 std::vector或者Mathull: 输出的凸包点集clockwise: 方向标志,如果为真,则为顺时针returnPoints: 操作标记,在矩阵(二维图像)下,如果flag为真,则返回的是凸包点集,否则返回的是索引。当输出是 std::vector 时,该标记被忽略。std::vector implies returnPoints=false, std::vector implies returnPoints=true.

boundingRect

Rect cv::boundingRect(InputArray array)

cv2.boundingRect(array) ->retval

计算一个灰阶图像中点集或非零像素的外接矩形

array: 点集

minAreaRect

RotatedRect cv::minAreaRect(InputArray points);

cv2.minAreaRect(points) ->retval

找二维点集的最小面积外接矩形(可能旋转的)

points: 输入的2维点集的vector, 存储为 std::vector或者Mat

boxPoints

void cv::boxPoints(RotatedRect box,OutputArray points );

cv2.boxPoints(box[, points]) ->points

找一个旋转矩形的四个顶点,用于画旋转矩形

box: 旋转矩形points: 输出的4个顶点

fitEllipse

RotatedRect cv::fitEllipse(InputArray points)

cv2.fitEllipse(points) ->retval

拟合二维点集的椭圆

points: 输入的二维点集.

minEnclosingCircle

void cv::minEnclosingCircle(InputArray points,Point2f & center,float & radius );

cv2.minEnclosingCircle(points) ->center, radius

计算一个二维点集最小的包围圆

points: 输入的二维点集center: 输入的圆心radius: 输出的半径

fitLine

void cv::fitLine(InputArray points,OutputArray line,int distType,double param,double reps,double aeps );

cv2.fitLine(points, distType, param, reps, aeps[, line]) ->line

直线拟合二维或者三维点集

points: 输入的二维或者三维点集,存储为 std::vector<> or Mat.line: 输出的直线参数,如果是二维点集,则 (like Vec4f) - (vx, vy, x0, y0), where (vx, vy) is a normalized vector collinear to the line and (x0, y0) is a point on the line. In case of 3D fitting, it should be a vector of 6 elements (like Vec6f) - (vx, vy, vz, x0, y0, z0), where (vx, vy, vz) is a normalized vector collinear to the line and (x0, y0, z0) is a point on the line.distType: 距离计算的类型;param: Numerical parameter ( C ) for some types of distances. If it is 0, an optimal value is chosen.reps: 半径的精度(坐标轴中心到直线的距离)aeps: 角度的精度,0.01是一个比较好的默认值。

【参考】

OpenCV 官方文档【从零学习OpenCV 4】图像矩的计算与应用Convex Hull using OpenCV in Python and C++

如果觉得《【OpenCV-Python】教程:3-9 轮廓(2)轮廓特征》对你有帮助,请点赞、收藏,并留下你的观点哦!

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。