如何在Python OpenCV中调整视频尺寸且不改变坐标?
问题解决:视频缩放适配屏幕时保持ROI跟踪坐标正确
问题核心
调整视频尺寸适配屏幕时,直接缩放帧会导致ROI的像素坐标偏移,而跟踪器基于原始帧坐标初始化,最终引发跟踪错位。解决关键是让跟踪、计算等核心逻辑始终基于原始视频分辨率,仅在显示环节进行缩放,或把缩放后的ROI坐标反向映射回原始坐标体系。
解决方案代码示例
以下是修改后的代码,关键调整点已标注:
import cv2 import numpy as np import tkinter as tk from tkinter import filedialog import pandas as pd # 补充原代码未定义的全局变量 fps = 0 desiredvideo = 0 startframe = 0 ROIsize = 25 org = (10, 30) font = cv2.FONT_HERSHEY_SIMPLEX fontScale = 0.7 font_color = (255, 255, 255) thickness = 2 tracker = cv2.TrackerCSRT_create() backup = cv2.TrackerCSRT_create() def framebyframe(file_path): global fps framecount = 0 framenumber = [] B = [] G = [] R = [] A = [] if desiredvideo == 1: root = tk.Tk() csv_file_path = filedialog.askopenfilename() root.withdraw() temp = pd.read_csv(csv_file_path, delimiter=',') Avfiltered = temp['Filtered signal'].tolist() roi = temp['ROI'].tolist() else: cap = cv2.VideoCapture(file_path) # 获取原始视频分辨率 original_width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH)) original_height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT)) # 设置目标显示尺寸(可根据屏幕调整) display_width = 1280 display_height = 720 # 计算等比缩放比例,避免拉伸 scale_w = display_width / original_width scale_h = display_height / original_height scale = min(scale_w, scale_h) target_display_size = (int(original_width * scale), int(original_height * scale)) fourcc = cv2.VideoWriter_fourcc(*"XVID") # 输出视频保留原始分辨率 out = cv2.VideoWriter("output.avi", fourcc, 20.0, (original_width, original_height)) if not cap.isOpened(): print("无法打开视频文件") fps = cap.get(cv2.CAP_PROP_FPS) cap.set(cv2.CAP_PROP_POS_FRAMES, startframe) totalframenumber = int(cap.get(cv2.CAP_PROP_FRAME_COUNT)) stopframe = 500 print("总帧数", totalframenumber) if cap.isOpened(): ret, frame = cap.read() if ret: framecount += 1 framenumber.append(framecount) if desiredvideo == 0: # 先显示缩放后的帧供用户选择ROI display_frame = cv2.resize(frame, target_display_size) roi_display = cv2.selectROI("选择ROI", display_frame) # 将缩放后的ROI坐标反向映射回原始视频坐标 roi = ( int(roi_display[0] / scale), int(roi_display[1] / scale), int(roi_display[2] / scale), int(roi_display[3] / scale) ) cv2.destroyWindow("选择ROI") else: root = tk.Tk() csv_file_path = filedialog.askopenfilename() root.withdraw() temp = pd.read_csv(csv_file_path, delimiter=',') roi = tuple(temp['ROI'].tolist()) frame_cropped = frame[int(roi[1]):int(roi[1]+roi[3]), int(roi[0]):int(roi[0]+roi[2]), :] tracker.init(frame, roi) backup.init(frame, roi) channelB, channelG, channelR = cv2.split(frame_cropped) B.append(np.mean(channelB)) G.append(np.mean(channelG)) R.append(np.mean(channelR)) A.append((B[-1] + G[-1] + R[-1])/3) while cap.isOpened(): ret, frame = cap.read() if ret: ret, roi = tracker.update(frame) else: print("帧缓冲失败:", framenumber[-1]) break if ret: framecount += 1 framenumber.append(framecount) roi = list(roi) if roi[1] < 0: roi[1] = 1 # 基于原始帧裁剪ROI,保证坐标准确性 frame_cropped = frame[int(roi[1]):int(roi[1]+roi[3]), int(roi[0]):int(roi[0]+roi[2]), :] c1 = int(roi[3]/2) c2 = int(roi[2]/2) if c1 < ROIsize: c1 = ROIsize if c2 < ROIsize: c2 = ROIsize frame_inner = frame_cropped[c1-ROIsize:c1+ROIsize, c2-ROIsize:c2+ROIsize, :] channelB, channelG, channelR = cv2.split(frame_inner) B.append(np.mean(channelB)) G.append(np.mean(channelG)) R.append(np.mean(channelR)) A.append((B[-1] + G[-1] + R[-1])/3) # 在原始尺寸的裁剪帧上绘制标记 frame_cropped = cv2.circle(frame_cropped, (c2, c1), 10, (0,0,255), -1) frame_cropped = cv2.putText(frame_cropped, f"Frame number: {framecount}", org, font, fontScale, font_color, thickness, cv2.LINE_AA) # 缩放后再显示,适配屏幕 display_cropped = cv2.resize(frame_cropped, (int(frame_cropped.shape[1]*scale), int(frame_cropped.shape[0]*scale))) display_inner = cv2.resize(frame_inner, (int(frame_inner.shape[1]*scale), int(frame_inner.shape[0]*scale))) cv2.imshow('Frame', display_cropped) cv2.imshow('Inner frame', display_inner) if cv2.waitKey(1) & 0xFF == ord('Q'): break if stopframe > 0 and framenumber[-1] >= stopframe: break cap.release() out.release() cv2.destroyAllWindows() return A, framenumber, roi
关键调整说明
- 分离处理与显示逻辑:跟踪、裁剪、均值计算全部基于原始分辨率帧,仅在显示前对帧进行缩放,确保跟踪坐标始终对应原始视频。
- ROI坐标映射:若需在缩放画面选ROI,将用户选择的缩放后坐标按比例反向计算,得到原始视频的ROI坐标后再初始化跟踪器。
- 保留原始输出分辨率:输出视频使用原始尺寸,避免后续处理出现坐标偏差。
内容的提问来源于stack exchange,提问作者Stefani Dimitrova
相关产品推荐
相关产品推荐

