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

如何在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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.28 06:33:11