如何利用cv2.findContours定位轮廓斜率突变的背鳍起始点
轮廓斜率突变点(背鳍起始点)定位方案
核心逻辑
背鳍起始点是轮廓走向从平缓(躯干部分)突然转向陡峭(背鳍部分)的位置,通过计算相邻轮廓点的斜率变化率,就能定位这个突变节点。
分步实现
1. 轮廓预处理(降噪+精简)
先对提取到的轮廓做精简,去掉冗余点,避免噪声干扰斜率计算:
import cv2 import numpy as np # 假设已通过cv2.findContours获取目标轮廓(取面积最大的轮廓) contours, _ = cv2.findContours(thresh_img, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) target_contour = max(contours, key=cv2.contourArea) # 多边形近似降采样,减少冗余点 epsilon = 0.002 * cv2.arcLength(target_contour, True) approx_points = [p[0] for p in cv2.approxPolyDP(target_contour, epsilon, True)] # 只保留上半部分轮廓(背鳍在目标上方,缩小计算范围) x, y, w, h = cv2.boundingRect(target_contour) mid_y = y + h // 2 upper_points = [p for p in approx_points if p[1] < mid_y]
2. 计算斜率与突变值
用角度表示斜率(避免垂直线段的除零问题),再计算相邻斜率的差值绝对值:
# 计算每段轮廓的角度(替代斜率,更稳定) slopes = [] for i in range(1, len(upper_points)): dx = upper_points[i][0] - upper_points[i-1][0] dy = upper_points[i][1] - upper_points[i-1][1] # arctan2返回[-π, π]区间的角度,规避垂直段的除零异常 slopes.append(np.arctan2(dy, dx)) # 计算相邻斜率的变化幅度 slope_changes = [abs(slopes[i] - slopes[i-1]) for i in range(1, len(slopes))]
3. 定位背鳍起始点
通过阈值筛选出变化幅度最大的点,就是斜率突变的位置:
# 用90分位数作为突变阈值,过滤小波动干扰 threshold = np.percentile(slope_changes, 90) # 找到最大变化幅度的索引,对应upper_points中的位置 max_change_idx = np.argmax(slope_changes) fin_start_point = upper_points[max_change_idx + 1] # 在原图标记结果 cv2.circle(input_img, fin_start_point, 6, (0, 0, 255), -1) cv2.imshow("Fin Start Point", input_img) cv2.waitKey(0)
优化技巧
- 若阈值处理后的图像有细碎噪声,先执行形态学闭运算:
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (3,3)),thresh_img = cv2.morphologyEx(thresh_img, cv2.MORPH_CLOSE, kernel),再提取轮廓。 - 若轮廓是闭合的,可先找到最左侧点作为遍历起点,确保从目标头部开始计算,避免遍历方向错误。
内容的提问来源于stack exchange,提问作者Mirote
相关产品推荐
相关产品推荐

