基于中指的手部X光图像拉直(OpenCV)技术实现及问题解决
手部X光图像中指拉直预处理完整方案及问题解决
背景与已完成预处理
需处理约10000张手部X光图像,目标是将中指拉直。已完成以下预处理步骤:
- 对图像应用Gaussian Blur及Threshold(Binary + Otsu)处理;
- 执行dilation操作,将手部合并为单一对象;
- 使用
cv2.findContours()提取手部边缘轮廓; - 应用
cv2.convexHull()和cv2.convexityDefects()标记凸包最远点,筛选出距离凸包超阈值的指缝点。
已实现的代码如下:
import cv2 import numpy as np from matplotlib import pyplot as plt img_path = "sample_image.png" # 图像阈值处理 image = cv2.imread("sample_image.png") original = image.copy() blank = np.zeros(image.shape[:2], dtype = np.uint8) gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) blur = cv2.GaussianBlur(gray, (127,127), 0) thresh = cv2.threshold(blur, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)[1] # 膨胀操作合并手部区域 kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5,5)) dilate = cv2.dilate(thresh, kernel, iterations = 2) # 提取最大手部轮廓 contours, hierarchy = cv2.findContours(dilate, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE) contours = max(contours, key = lambda x: cv2.contourArea(x)) cv2.drawContours(image, [contours], -1, (255,255,0), 2) # 标记凸包缺陷点(指缝)并存储 finger_gaps = [] hull = cv2.convexHull(contours, returnPoints = False) defects = cv2.convexityDefects(contours, hull) for i in range(defects.shape[0]): _, _, farthest_point_index, distance = defects[i, 0] farthest_point = contours[farthest_point_index][0] # 改用轮廓面积比例作为动态阈值,适配不同大小图像 if distance > 0.001 * cv2.contourArea(contours): finger_gaps.append(farthest_point) circle = cv2.circle(image, farthest_point, 20, [0,0,255], -1)
下一步需求
- 从筛选出的指缝点中识别出靠近中指的中间两点;
- 沿中指方向旋转图像实现拉直。
现存问题
- 中指较短的X光图处理失效;
- 左倾手部拉直后方向倒置(已通过
ellipse_angle = ((ellipse_angle + 90) % 180) - 90修正,但需结合完整逻辑)。
完整解决方案
1. 中指区域定位与中间指缝点识别
针对中指较短的情况,结合椭圆拟合与指尖点检测补充定位逻辑:
代码实现:
# 拟合手部椭圆,获取方向信息并修正角度 ellipse = cv2.fitEllipse(contours) (x, y), (MA, ma), angle = ellipse # 修正角度,解决左倾倒置问题 angle = ((angle + 90) % 180) - 90 # 检测指尖点(凸包顶点) hull_points = cv2.convexHull(contours, returnPoints=True) # 按y坐标排序,取顶部5个点作为候选指尖(假设手臂在下,指尖在上) hull_points_sorted = sorted(hull_points, key=lambda p: p[0][1]) candidate_fingertips = hull_points_sorted[:5] # 定位中指两侧的指缝点 middle_gaps = [] if len(finger_gaps) >= 2: # 根据椭圆角度判断左右手,调整排序方向 finger_gaps_sorted = sorted(finger_gaps, key=lambda p: p[0]) if angle > 0: # 左手情况,反转排序 finger_gaps_sorted = finger_gaps_sorted[::-1] # 取中间的两个指缝点 middle_gaps = finger_gaps_sorted[1:3] if len(finger_gaps_sorted)>=3 else finger_gaps_sorted[:2] else: # 指缝点不足时,结合中间指尖生成虚拟指缝点 if len(candidate_fingertips)>=3: middle_tip = candidate_fingertips[1][0] # 基于指尖位置生成左右偏移点作为替代指缝 middle_gaps.append((middle_tip[0]-30, middle_tip[1])) middle_gaps.append((middle_tip[0]+30, middle_tip[1]))
2. 图像旋转拉直
基于中指方向(指缝连线或椭圆修正角度)旋转图像:
def rotate_image(image, angle, center=None, scale=1.0): (h, w) = image.shape[:2] if center is None: center = (w // 2, h // 2) # 获取旋转矩阵 M = cv2.getRotationMatrix2D(center, angle, scale) # 计算旋转后的图像尺寸,避免裁剪 cos = np.abs(M[0, 0]) sin = np.abs(M[0, 1]) new_w = int((h * sin) + (w * cos)) new_h = int((h * cos) + (w * sin)) # 调整平移参数,使图像居中 M[0, 2] += (new_w / 2) - center[0] M[1, 2] += (new_h / 2) - center[1] # 执行旋转,使用抗锯齿与边界填充 rotated = cv2.warpAffine(image, M, (new_w, new_h), flags=cv2.INTER_CUBIC, borderMode=cv2.BORDER_REPLICATE) return rotated # 确定旋转角度 rotate_angle = 0 if len(middle_gaps) == 2: # 计算指缝连线的角度 dx = middle_gaps[1][0] - middle_gaps[0][0] dy = middle_gaps[1][1] - middle_gaps[0][1] gap_angle = np.degrees(np.arctan2(dy, dx)) rotate_angle = -gap_angle else: # 用修正后的椭圆角度作为替代 rotate_angle = -angle # 旋转原始图像与预处理图像 rotated_original = rotate_image(original, rotate_angle) rotated_processed = rotate_image(image, rotate_angle) # 可视化结果 plt.figure(figsize=(12,6)) plt.subplot(121), plt.imshow(cv2.cvtColor(original, cv2.COLOR_BGR2RGB)), plt.title('Original') plt.subplot(122), plt.imshow(cv2.cvtColor(rotated_original, cv2.COLOR_BGR2RGB)), plt.title('Rotated') plt.show()
3. 针对中指较短情况的优化
- 动态阈值:将凸包缺陷的距离阈值改为基于轮廓面积的比例值,适配不同大小的手部图像;
- 指尖辅助:当指缝点不足时,利用椭圆长轴方向与指尖位置生成虚拟指缝点;
- 左右手自动判断:通过椭圆拟合角度与指尖分布,自动调整指缝点排序,避免定位偏差。
内容的提问来源于stack exchange,提问作者Bathtub
相关产品推荐
相关产品推荐

