如何提取图像中曲线的像素坐标并调用scipy.find_peaks计算峰值点
问题描述
我有如下图像:
我需要完成2个步骤:
步骤1: 获取图中每条曲线每个像素对应的(x,y)坐标,最终输出与曲线数量相等的数组,每个数组由对应曲线所有像素的{'x': x, 'y': y}对象组成。
步骤2: 将每条曲线对应的像素坐标数组输入scipy.find_peaks接口,计算得到每条曲线的峰值(即最大点)。
我目前已编写的代码如下:
from PIL import Image import cv2 img_gray = cv2.imread('photo.jpg', cv2.IMREAD_GRAYSCALE) img_binary = cv2.threshold(img_gray, 150, 255, cv2.THRESH_BINARY)[1] cv2.imwrite('save.jpg', img_binary) image = cv2.imread('save.jpg') cv2.imwrite('save.jpg', image) im = Image.open("save.jpg") im = im.convert('RGB') col_black = (0, 0, 0) col_white = (255, 255, 255) for y in range(im.size[1]): for x in range(im.size[0]): if sum(im.getpixel((x, y))) < 128: im.putpixel((x, y), (0, 0, 0)) else: im.putpixel((x, y), (255, 255, 255)) point_start_array = [] for x in range(im.size[0]): for y in range(im.size[1]): if im.getpixel((x, y)) == col_black and im.getpixel((x, y + 1)) == col_white: if len(point_start_array) > 4: break else: point_start_array.append({'x': x, 'y': y}) print(point_start_array)
当前输出:[{'x': 19, 'y': 161}, {'x': 19, 'y': 238}, {'x': 19, 'y': 260}, {'x': 19, 'y': 269}, {'x': 19, 'y': 282}]
请问是否可以针对每条曲线分别提取x坐标数组和y坐标数组,传入scipy.find_peaks接口计算得到峰值?
回答
可以实现,完整的实现逻辑和代码如下:
核心思路
你当前的代码已经正确提取到了5条曲线在最左侧x=19处的起点坐标,接下来只需要逐列向右追踪每条曲线的所有像素点,就可以得到每条曲线的完整坐标数组,之后传入find_peaks接口即可计算峰值。
需要注意:图像的y坐标是从上到下递增的,波形的峰值对应图像里更小的y值,所以计算峰值前需要把y数组反转,或者调整find_peaks的参数适配坐标规则。
完整可运行代码
from PIL import Image import cv2 from scipy.signal import find_peaks import numpy as np # 预处理部分和你的原有逻辑一致 img_gray = cv2.imread('photo.jpg', cv2.IMREAD_GRAYSCALE) img_binary = cv2.threshold(img_gray, 150, 255, cv2.THRESH_BINARY)[1] cv2.imwrite('save.jpg', img_binary) im = Image.open("save.jpg").convert('RGB') width, height = im.size col_black = (0,0,0) # 第一步:提取所有曲线的起点(优化原有逻辑避免越界) point_start_array = [] for y in range(height-1): if im.getpixel((19, y)) == col_black and im.getpixel((19, y+1)) != col_black: point_start_array.append(y) # 按y值从小到大排序,对应从上到下的5条曲线 point_start_array.sort() curve_num = len(point_start_array) print(f"检测到{curve_num}条曲线,起点y坐标:{point_start_array}") # 第二步:逐列追踪每条曲线的所有点 curves = [[] for _ in range(curve_num)] # 先初始化起点x=19的坐标 for i in range(curve_num): curves[i].append({'x':19, 'y':point_start_array[i]}) # 逐列向右遍历 search_range = 10 # 同一条曲线相邻列y值不会差太多,设置搜索范围避免串线 for x in range(20, width): # 对每条曲线,在上一个点的y附近找当前x列的黑色像素 for i in range(curve_num): last_y = curves[i][-1]['y'] # 确定搜索上下界,避免越界 y_min = max(0, last_y - search_range) y_max = min(height-1, last_y + search_range) # 找当前列范围内的黑色像素 found_y = None for y in range(y_min, y_max+1): if im.getpixel((x,y)) == col_black: found_y = y break if found_y is not None: curves[i].append({'x':x, 'y':found_y}) # 第三步:对每条曲线计算峰值 for idx, curve in enumerate(curves): # 提取y数组,因为图像y向下递增,所以取反变成向上递增,符合波形的数值大小逻辑 y_arr = np.array([-p['y'] for p in curve]) # 找峰值,可根据实际情况调整height、distance等参数 peaks, _ = find_peaks(y_arr, distance=20) # 取最大的峰值(如果有多个的话) if len(peaks) > 0: max_peak_idx = peaks[np.argmax(y_arr[peaks])] peak_point = curve[max_peak_idx] print(f"第{idx+1}条曲线的峰值坐标:x={peak_point['x']}, y={peak_point['y']}") else: print(f"第{idx+1}条曲线未检测到峰值")
代码说明
- 调整了起点提取逻辑,避免出现数组越界报错
- 相邻列搜索时加了10像素的范围限制,避免曲线交叉的时候把不同曲线的点串到一起
- 计算峰值时把y值取反,适配图像坐标和实际波形数值的对应关系,你也可以根据自己的坐标需求调整
find_peaks的参数可以根据你的曲线实际情况调整,比如设置height阈值过滤噪声峰值,distance限制相邻峰值的最小距离
内容的提问来源于stack exchange,提问作者Vadim
相关产品推荐
相关产品推荐

