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

如何在Kivy控件/窗口中使用OpenCV setMouseCallback实现ROI选择?

Kivy与OpenCV集成实现ROI选择的可行方案

方案一:在Kivy界面内原生实现ROI选择(推荐)

完全抛弃OpenCV的独立窗口,直接在Kivy的图像组件上处理鼠标/触摸事件,实现ROI选择逻辑,步骤如下:

  • 将OpenCV读取的图像转换为Kivy支持的Texture格式
  • 绑定触摸事件,记录ROI的起点和终点坐标
  • 将Kivy界面坐标映射回OpenCV图像的原始坐标(需处理图像缩放比例)
  • 传递ROI坐标给原有分析函数

核心代码示例

from kivy.app import App
from kivy.uix.image import Image
from kivy.graphics import Color, Rectangle
from kivy.core.image import Image as CoreImage
import cv2
import numpy as np

class ROIselector(Image):
    def __init__(self, cv_image, **kwargs):
        super().__init__(**kwargs)
        # 转换OpenCV图像到Kivy Texture
        buf = cv2.flip(cv_image, 0).tobytes()
        texture = CoreImage(buf, colorfmt='bgr', size=cv_image.shape[:2][::-1]).texture
        self.texture = texture
        self.cv_image = cv_image
        self.cv_shape = cv_image.shape[:2]  # 原始图像的高、宽
        self.start_pos = None
        self.end_pos = None
        # 绑定触摸事件
        self.bind(on_touch_down=self.on_touch_down, on_touch_move=self.on_touch_move, on_touch_up=self.on_touch_up)

    def on_touch_down(self, _, touch):
        if self.collide_point(*touch.pos):
            # 把Kivy界面坐标转成原始图像坐标
            x = int(touch.x / self.width * self.cv_shape[1])
            y = int(touch.y / self.height * self.cv_shape[0])
            self.start_pos = (x, y)
            return True
        return False

    def on_touch_move(self, _, touch):
        if self.start_pos and self.collide_point(*touch.pos):
            x = int(touch.x / self.width * self.cv_shape[1])
            y = int(touch.y / self.height * self.cv_shape[0])
            self.end_pos = (x, y)
            # 绘制半透明红色ROI矩形
            self.canvas.after.clear()
            with self.canvas.after:
                Color(1, 0, 0, 0.5)
                # 转回Kivy界面坐标绘制矩形
                kivy_x1 = self.start_pos[0] / self.cv_shape[1] * self.width
                kivy_y1 = self.start_pos[1] / self.cv_shape[0] * self.height
                kivy_x2 = x / self.cv_shape[1] * self.width
                kivy_y2 = y / self.cv_shape[0] * self.height
                Rectangle(pos=(min(kivy_x1, kivy_x2), min(kivy_y1, kivy_y2)), 
                          size=(abs(kivy_x2 - kivy_x1), abs(kivy_y2 - kivy_y1)))
            return True
        return False

    def on_touch_up(self, _, touch):
        if self.start_pos and self.end_pos:
            # 整理ROI的标准坐标(左上角+宽高)
            x1 = min(self.start_pos[0], self.end_pos[0])
            y1 = min(self.start_pos[1], self.end_pos[1])
            w = abs(self.end_pos[0] - self.start_pos[0])
            h = abs(self.end_pos[1] - self.start_pos[1])
            if w > 0 and h > 0:
                roi_image = self.cv_image[y1:y1+h, x1:x1+w]
                # 调用原有分析函数
                self.analyze_roi(roi_image)
            self.start_pos = None
            self.end_pos = None
            self.canvas.after.clear()
            return True
        return False

    def analyze_roi(self, roi_img):
        # 替换为你的原有主分析逻辑
        # 示例:保存ROI图像
        cv2.imwrite("selected_roi.jpg", roi_img)

class MyApp(App):
    def build(self):
        cv_image = cv2.imread("test_image.jpg")
        return ROIselector(cv_image)

if __name__ == "__main__":
    MyApp().run()

方案二:保留OpenCV ROI窗口,同步结果到Kivy

如果不想大幅修改原有ROI选择逻辑,可以临时弹出OpenCV窗口完成选择,再将结果同步到Kivy界面:

  • 点击Kivy按钮触发OpenCV的selectROI窗口
  • 选择完成后关闭OpenCV窗口,提取ROI图像
  • 将ROI图像转换为Kivy格式显示,同时调用分析函数

核心代码示例

import cv2
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.button import Button
from kivy.uix.image import Image
from kivy.core.image import Image as CoreImage

class OpenCVROIApp(App):
    def build(self):
        self.cv_image = cv2.imread("test_image.jpg")
        layout = BoxLayout(orientation='vertical')
        self.kivy_image = Image()
        self.update_kivy_display(self.cv_image)
        select_btn = Button(text="选择ROI", on_press=self.open_roi_window)
        layout.add_widget(self.kivy_image)
        layout.add_widget(select_btn)
        return layout

    def open_roi_window(self, _):
        roi = cv2.selectROI("选择感兴趣区域", self.cv_image, showCrosshair=True)
        cv2.destroyWindow("选择感兴趣区域")
        x, y, w, h = roi
        if w > 0 and h > 0:
            roi_image = self.cv_image[y:y+h, x:x+w]
            # 调用分析函数
            self.run_analysis(roi_image)
            # 在Kivy显示选中的ROI
            self.update_kivy_display(roi_image)

    def update_kivy_display(self, cv_img):
        buf = cv2.flip(cv_img, 0).tobytes()
        texture = CoreImage(buf, colorfmt='bgr', size=cv_img.shape[:2][::-1]).texture
        self.kivy_image.texture = texture

    def run_analysis(self, roi_img):
        # 替换为你的原有分析逻辑
        pass

if __name__ == "__main__":
    OpenCVROIApp().run()

方案三:优化Kivy图像交互支持缩放平移后选ROI

如果需要支持图像缩放、平移后选择ROI,可使用Kivy的Scatter组件包裹图像,再处理坐标映射:

  • 用Scatter组件实现图像的缩放、平移
  • 在Scatter上绑定触摸事件,结合缩放比例计算原始图像坐标
  • 其余逻辑同方案一

内容的提问来源于stack exchange,提问作者Daniel do Amaral Denardi

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.06 01:45:43