实时数据采集下代码性能优化及亮度值异常问题求助
问题描述
- 实验场景:基于2.5Hz采集频率的相机搭建物理实验实时演化追踪系统,核心需求是计算批次图像平均亮度并绘制实时曲线,系统效率需匹配实验节奏。
- 程序逻辑:读取目标文件夹首张图像供用户选择感兴趣区域(ROI),随后计算单张图像及每
img_round张图像批次的像素平均亮度,实时绘制批次平均亮度随迭代次数的变化曲线。 - 异常表现:静态数据集上运行正常,但图像持续新增至文件夹的实验场景中,绘制的亮度值出现异常;同时希望优化代码运行性能。
用户提供的原始代码:
import os import cv2 import numpy as np import pyqtgraph as pg from scipy.optimize import minimize_scalar from pyqtgraph.Qt import QtCore, QtGui, QtWidgets import time center = (0, 0) radius = (0) is_dragging_center = False is_dragging_radius = False global avg_brightness_per_img_round avg_brightness_per_img_round = 0 img_round = 5 run_count = 0 brightness_history = [] std_history = [] func_history = [] global scatter_item scatter_item = None def update_display_image(): global resized_image if resized_image is not None: display_image = resized_image.copy() cv2.circle(display_image, center, radius, (0, 255, 0), 2) cv2.circle(display_image, center, 5, (0, 0, 255), thickness=cv2.FILLED) class UpdateDisplaySignal(QtCore.QObject): update_display_signal = QtCore.pyqtSignal() update_display_signal_obj = UpdateDisplaySignal() update_display_signal_obj.update_display_signal.connect(update_display_image) def on_mouse(event, x, y, flags, param): global center, radius, is_dragging_center, is_dragging_radius if event == cv2.EVENT_LBUTTONDOWN: if np.sqrt((x - center[0]) ** 2 + (y - center[1]) ** 2) < 20: is_dragging_center = True else: is_dragging_radius = True elif event == cv2.EVENT_LBUTTONUP: is_dragging_center = False is_dragging_radius = False elif event == cv2.EVENT_MOUSEMOVE: if is_dragging_center: center = (x, y) elif is_dragging_radius: radius = int(np.sqrt((x - center[0]) ** 2 + (y - center[1]) ** 2)) app = QtWidgets.QApplication([]) pw = pg.PlotWidget(title='Mean Brightness vs image round') pw.setLabel('left', 'Mean Brightness') pw.setLabel('bottom', 'Image round') scatter = pg.ScatterPlotItem(size=10, pen=pg.mkPen(None), brush=pg.mkBrush(255, 0, 0, 120)) line = pg.PlotDataItem(pen=pg.mkPen(color=(0,0,255), width=2)) pw.addItem(line) pw.addItem(scatter) def update_scatter(): global scatter_item indices, values = zip(*enumerate(brightness_history, start=1)) x = list(indices) y = list(values) if scatter_item is None: scatter_item = pg.ScatterPlotItem(size=10, pen=pg.mkPen(None), brush=pg.mkBrush(255, 0, 0, 120)) pw.addItem(scatter_item) if isinstance(x, int): x = [x] if len(x) > 1: line.setData(x=x, y=y) scatter.setData(x=x, y=y, symbol='o', size=10, pen=pg.mkPen(None), brush=pg.mkBrush(255, 0, 0, 120)) for i, (xi, yi) in enumerate(zip(x, y)): label = pg.TextItem(text=f'{yi:.2f}', anchor=(0, 0)) label.setPos(xi, yi) pw.addItem(label) win = QtWidgets.QMainWindow() win.setCentralWidget(pw) win.show() path = r'C:\Users\blehe\Desktop\Betatron\images' def calc_xray_count(image_path, center, radius): original_image = cv2.imread(image_path, cv2.IMREAD_ANYDEPTH) median_filtered_image = cv2.medianBlur(original_image, 5) mask = np.zeros(original_image.shape, dtype=np.uint8) cv2.circle(mask, center, radius, 255, thickness=cv2.FILLED) median_filtered_image += 1 # Avoid not counting black pixels in image result = cv2.bitwise_and(median_filtered_image, median_filtered_image, mask=mask) pixel_count = np.count_nonzero(result) img_brightness_sum = np.sum(result) img_var = np.var(result) if (pixel_count > 0): img_avg_brightness = (img_brightness_sum/pixel_count) -1 # Subtract back to real data else: img_avg_brightness = 0 return img_avg_brightness, img_var #----------------------------------------------------------------------- image_files = [] for filename in os.listdir(path): if filename.endswith('.TIF'): image_files.append(os.path.join(path, filename)) first_image_path = image_files[0] image = cv2.imread(first_image_path) scale_percent = 60 width = int(image.shape[1] * scale_percent / 100) height = int(image.shape[0] * scale_percent / 100) dim = (width, height) gray_img = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) colored_image = cv2.applyColorMap(gray_img, cv2.COLORMAP_PINK) resized_image = cv2.resize(colored_image, dim, interpolation=cv2.INTER_AREA) center = (resized_image.shape[1] // 2, resized_image.shape[0] // 2) radius = min(resized_image.shape[1] // 3, resized_image.shape[0] // 3) cv2.namedWindow("Adjust the circle (press 'Enter' to proceed)") cv2.setMouseCallback("Adjust the circle (press 'Enter' to proceed)", on_mouse) while True: display_image = resized_image.copy() cv2.circle(display_image, center, radius, (0, 255, 0), 2) cv2.circle(display_image, center, 5, (0, 0, 255), thickness=cv2.FILLED) cv2.imshow("Adjust the circle (press 'Enter' to proceed)", display_image) key = cv2.waitKey(1) & 0xFF if key == 13: break cv2.destroyAllWindows() center = (int(center[0] / scale_percent * 100), int(center[1] / scale_percent * 100)) radius = int(radius / scale_percent * 100) img_round_brightness_sum = 0 img_round_var_sum = 0 def process_images(): global run_count, img_round_brightness_sum, img_round_var_sum while run_count < len(os.listdir(path)): for i, image_path in enumerate(image_files, start=1): img_avg_brightness, img_var = calc_xray_count(image_path, center, radius) img_round_brightness_sum += img_avg_brightness img_round_var_sum += img_var run_count += 1 if run_count % img_round == 0: avg_brightness_per_img_round = (img_round_brightness_sum/img_round) deviation_per_img_round = np.sqrt(img_round_var_sum/img_round) brightness_history.append(avg_brightness_per_img_round) std_history.append(deviation_per_img_round) update_scatter() img_round_brightness_sum = 0 img_round_var_sum = 0 img_avg_brightness = 0 img_var = 0 QtCore.QCoreApplication.processEvents() QtCore.QThread.msleep(100) if __name__ == "__main__": timer = QtCore.QTimer() timer.timeout.connect(process_images) timer.start(100) app.exec_()
问题排查与修复
1. 实时图像新增未被检测的核心问题
原代码仅在启动时扫描一次文件夹生成image_files列表,后续新增的图像不会被纳入处理队列,导致程序反复处理旧图像,最终出现亮度值异常。修复方案:
- 在处理函数内每次循环都重新扫描文件夹,获取最新图像列表
- 记录已处理的文件名,避免重复处理同一图像
2. 绘图逻辑的冗余错误
原update_scatter函数每次都会重新创建所有文本标签,导致界面卡顿、标签重叠;同时存在scatter_item和全局scatter重复定义的问题。修复方案:
- 仅更新新增数据对应的曲线和散点,避免重复创建绘图元素
- 移除冗余的全局
scatter_item,复用已创建的scatter对象 - 若需要显示数值标签,仅添加最新批次的标签,而非全部重绘
性能优化建议
1. 图像处理优化
- 预生成ROI掩码:掩码仅需根据用户选定的ROI创建一次,无需每次处理图像都重新生成,减少重复计算
- 简化亮度计算逻辑:直接用掩码提取区域后计算均值,去掉
median_filtered_image +=1再减1的冗余操作 - 多线程分离:将图像读取、计算逻辑放在单独QThread中,避免阻塞UI线程,保证界面流畅
2. 循环与逻辑优化
- 匹配采集频率设置扫描间隔:相机采集频率为2.5Hz,可将文件夹扫描间隔设为400ms,避免过度扫描浪费资源
- 减少全局变量使用:用类封装程序状态(如已处理文件列表、批次累计值等),提升代码可维护性
修改后的完整代码
import os import cv2 import numpy as np import pyqtgraph as pg from pyqtgraph.Qt import QtCore, QtGui, QtWidgets class ImageProcessor(QtCore.QObject): update_plot_signal = QtCore.pyqtSignal(float) def __init__(self, path, center, radius, img_round=5): super().__init__() self.path = path self.center = center self.radius = radius self.img_round = img_round self.processed_files = set() self.batch_sum = 0.0 self.batch_var_sum = 0.0 self.batch_count = 0 # 预生成掩码(后续根据图像尺寸动态调整) self.mask = None def process_new_images(self): current_files = {os.path.join(self.path, f) for f in os.listdir(self.path) if f.endswith('.TIF')} new_files = current_files - self.processed_files for img_path in sorted(new_files): try: img = cv2.imread(img_path, cv2.IMREAD_ANYDEPTH) if img is None: continue # 动态生成掩码(适配不同图像尺寸) if self.mask is None or self.mask.shape != img.shape: self.mask = np.zeros(img.shape, dtype=np.uint8) cv2.circle(self.mask, self.center, self.radius, 255, thickness=cv2.FILLED) # 中值滤波+提取ROI filtered = cv2.medianBlur(img, 5) roi = filtered[self.mask == 255] if len(roi) == 0: avg_brightness = 0.0 var = 0.0 else: avg_brightness = np.mean(roi) var = np.var(roi) # 批次累计 self.batch_sum += avg_brightness self.batch_var_sum += var self.batch_count += 1 # 批次完成,发送更新信号 if self.batch_count >= self.img_round: batch_avg = self.batch_sum / self.img_round self.update_plot_signal.emit(batch_avg) # 重置批次统计 self.batch_sum = 0.0 self.batch_var_sum = 0.0 self.batch_count = 0 self.processed_files.add(img_path) except Exception as e: print(f"处理图像 {img_path} 出错: {e}") continue class MainWindow(QtWidgets.QMainWindow): def __init__(self, path): super().__init__() self.path = path self.center = (0,0) self.radius = 0 self.init_ui() self.select_roi() self.start_processing() def init_ui(self): self.setWindowTitle("实时亮度追踪") self.pw = pg.PlotWidget(title='Mean Brightness vs Image Round') self.pw.setLabel('left', 'Mean Brightness') self.pw.setLabel('bottom', 'Image Round') self.line = pg.PlotDataItem(pen=pg.mkPen(color=(0,0,255), width=2)) self.scatter = pg.ScatterPlotItem(size=10, pen=pg.mkPen(None), brush=pg.mkBrush(255,0,0,120)) self.pw.addItem(self.line) self.pw.addItem(self.scatter) self.setCentralWidget(self.pw) self.brightness_history = [] self.round_count = 0 def select_roi(self): # 获取首张图像 image_files = [os.path.join(self.path, f) for f in os.listdir(self.path) if f.endswith('.TIF')] if not image_files: raise ValueError("文件夹中无TIF图像") first_img = cv2.imread(image_files[0]) scale_percent = 60 dim = (int(first_img.shape[1]*scale_percent/100), int(first_img.shape[0]*scale_percent/100)) resized = cv2.resize(cv2.applyColorMap(cv2.cvtColor(first_img, cv2.COLOR_BGR2GRAY), cv2.COLORMAP_PINK), dim) # 初始化ROI self.center = (resized.shape[1]//2, resized.shape[0]//2) self.radius = min(resized.shape[1]//3, resized.shape[0]//3) is_dragging_center = False is_dragging_radius = False def mouse_callback(event, x, y, flags, param): nonlocal is_dragging_center, is_dragging_radius if event == cv2.EVENT_LBUTTONDOWN: if np.sqrt((x-self.center[0])**2 + (y-self.center[1])**2) <20: is_dragging_center = True else: is_dragging_radius = True elif event == cv2.EVENT_LBUTTONUP: is_dragging_center = False is_dragging_radius = False elif event == cv2.EVENT_MOUSEMOVE: if is_dragging_center: self.center = (x,y) elif is_dragging_radius: self.radius = int(np.sqrt((x-self.center[0])**2 + (y-self.center[1])**2)) cv2.namedWindow("Adjust ROI (Press Enter to confirm)") cv2.setMouseCallback("Adjust ROI (Press Enter to confirm)", mouse_callback) while True: display = resized.copy() cv2.circle(display, self.center, self.radius, (0,255,0),2) cv2.circle(display, self.center,5,(0,0,255),cv2.FILLED) cv2.imshow("Adjust ROI (Press Enter to confirm)", display) if cv2.waitKey(1) &0xFF ==13: break cv2.destroyAllWindows() # 还原ROI到原始图像尺寸 self.center = (int(self.center[0]/scale_percent*100), int(self.center[1]/scale_percent*100)) self.radius = int(self.radius/scale_percent*100) def start_processing(self): self.processor = ImageProcessor(self.path, self.center, self.radius) self.processor.update_plot_signal.connect(self.update_plot) # 设置定时器,匹配相机2.5Hz采集频率,每400ms扫描一次 self.timer = QtCore.QTimer() self.timer.timeout.connect(self.processor.process_new_images) self.timer.start(400) def update_plot(self, batch_avg): self.round_count +=1 self.brightness_history.append(batch_avg) x = list(range(1, self.round_count+1))
相关产品推荐
相关产品推荐

