You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于树结构的鱼类头尾最长距离提取:如何规避鱼鳍干扰?

鱼类最长线提取(规避鱼鳍干扰)

我正尝试用树结构实现鱼类最长线提取,目标是规避鱼鳍干扰,找到鱼头到鱼尾的最长距离。目前用最大距离法定位的两点分别在鱼鳍和尾部,不符合需求——我需要的是鱼头和鱼尾的两点来计算最长线。已经实现了骨架提取,但鱼鳍导致骨架中心线存在额外分支,现有实现代码如下:

binary_fish = cv2.imread('/content/fish4-masked.jpg', cv2.IMREAD_GRAYSCALE)

# Step 1: Preprocessing
# Apply erosion and dilation to the binary fish image
kernel = np.ones((3, 3), np.uint8)
binary_fish = cv2.erode(binary_fish, kernel, iterations=1)
binary_fish = cv2.dilate(binary_fish, kernel, iterations=1)

# Step 2: Threshold to create a binary image
_, binary_fish = cv2.threshold(binary_fish, 128, 255, cv2.THRESH_BINARY)

# Step 3: Skeletonize the binary fish image
skeleton = morphology.skeletonize(binary_fish > 0)

# Step 4: Find the centerline from the skeleton
skeleton = skeleton.astype(np.uint8) * 255

# Find the contours of the skeleton
contours, _ = cv2.findContours(skeleton, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

# Extract the largest contour (assuming it's the centerline)
centerline = max(contours, key=cv2.contourArea)

# Create a blank canvas
height, width = binary_fish.shape
centerline_image = np.zeros((height, width), dtype=np.uint8)

# Draw the centerline on the canvas
cv2.drawContours(centerline_image, [centerline], -1, 255, 1)

# Find the coordinates of the points on the centerline
points = centerline.squeeze(axis=1) 

max_distance = 0
farthest_points = (0, 0), (0, 0)

# Calculate pairwise distances and find the two farthest points
for i in range(len(points)):
    for j in range(i + 1, len(points)):
        x1, y1 = points[i]
        x2, y2 = points[j]
        distance = math.sqrt((x2 - x1)**2 + (y2 - y1)**2)
        
        if distance > max_distance:
            max_distance = distance
            farthest_points = (x1, y1), (x2, y2)

farthest_point1, farthest_point2 = farthest_points

radius = 5  # Radius of the circle
color = (0, 255, 0)  # Green color (you can choose a different color)
thickness = -1  # Fill the circle

# Draw the first farthest point
cv2.circle(original_fish, farthest_point1, radius, color, thickness)

# Draw the second farthest point
cv2.circle(original_fish, farthest_point2, radius, color, thickness)

效果图描述

  • 骨架提取结果:生成的鱼类骨架包含鱼鳍带来的额外分支,干扰了主轴线识别
  • 两点定位结果:当前算法标记的最远点分别落在鱼鳍和尾部,未对应鱼头与鱼尾

解决思路与改进方案

核心问题是鱼鳍分支干扰骨架主线,导致最远点计算偏离目标。以下是两种直接可行的优化方向:

1. 修剪骨架分支(去除鱼鳍干扰)

骨架是树状结构,鱼鳍属于短侧枝,可通过节点度数(邻域非零点数)识别分叉点并修剪:

  • 主骨架节点度数多为2(中间点)或1(端点)
  • 鱼鳍分叉处节点度数≥3,从分叉点递归修剪短分支

添加修剪函数并插入原有流程:

import numpy as np
import cv2
from skimage import morphology

def prune_skeleton(skeleton):
    skel = skeleton.copy() // 255
    coords = np.argwhere(skel == 1)
    for (y, x) in coords:
        neighbors = skel[y-1:y+2, x-1:x+2]
        degree = np.sum(neighbors) - 1  # 减去自身点
        if degree > 2:
            prune_branch(skel, y, x)
    return (skel * 255).astype(np.uint8)

def prune_branch(skel, y, x):
    skel[y, x] = 0
    for dy in [-1, 0, 1]:
        for dx in [-1, 0, 1]:
            if dy == 0 and dx == 0:
                continue
            ny, nx = y + dy, x + dx
            if 0 <= ny < skel.shape[0] and 0 <= nx < skel.shape[1]:
                if skel[ny, nx] == 1:
                    neighbors = skel[ny-1:ny+2, nx-1:nx+2]
                    degree = np.sum(neighbors) - 1
                    if degree <= 2:
                        prune_branch(skel, ny, nx)

# 在原有代码Step3后插入:
skeleton = prune_skeleton(skeleton)

2. 直接提取骨架端点(替代暴力最远点计算)

修剪后的骨架为单条主线,只需提取度数为1的端点,即为鱼头和鱼尾:

def find_skeleton_endpoints(skeleton):
    skel = skeleton // 255
    coords = np.argwhere(skel == 1)
    endpoints = []
    for (y, x) in coords:
        neighbors = skel[y-1:y+2, x-1:x+2]
        degree = np.sum(neighbors) - 1
        if degree == 1:
            endpoints.append((x, y))  # 转换为OpenCV的(x,y)格式
    return endpoints

# 替换原有最远点计算逻辑:
endpoints = find_skeleton_endpoints(skeleton)
if len(endpoints) >= 2:
    farthest_point1, farthest_point2 = endpoints[0], endpoints[1]
    max_distance = np.linalg.norm(np.array(farthest_point1) - np.array(farthest_point2))

额外优化建议

  • 预处理阶段:先计算鱼类的最小外接矩形,将图像旋转至水平方向,减少骨架分支的判断难度
  • 噪声过滤:骨架提取前,用cv2.morphologyEx做开运算,去除小噪声区域

内容的提问来源于stack exchange,提问作者repo_twei

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.06 09:58:13