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

如何在Kivy中对图像的指定绘制区域应用OpenCV滤镜?

Kivy图像编辑:在选定区域应用OpenCV滤镜问题

我正在开发一款Kivy应用,该应用可显示图像并允许用户使用Kivy的Rectangle属性在图像上绘制矩形。以下是实现此功能的代码:

from kivy.app import App
from kivy.uix.widget import Widget
from kivy.graphics import Color, Rectangle
from kivy.uix.image import Image


class CanvasExample(Widget):
    def __init__(self, **kwargs):
        super().__init__(**kwargs)

        self.image = Image(source='sampleImg.jpg', size_hint=(None, None), size=(800, 600))
        self.add_widget(self.image)

        # Initialize variables
        self.start_pos = None
        self.rectangles = []

    def on_touch_down(self, touch):
        if self.collide_point(*touch.pos):
            self.start_pos = touch.pos

    def on_touch_up(self, touch):
        if self.collide_point(*touch.pos):
            if self.start_pos is not None:
                # Compute the rectangle
                x, y = self.start_pos
                width = touch.pos[0] - x
                height = touch.pos[1] - y
                rect = Rectangle(pos=(x, y), size=(width, height))

                # Add the rectangle to the list
                self.rectangles.append(rect)

                # Add the rectangle to the image
                with self.image.canvas:
                    Color(1, 0, 0, 0.13)  # red color
                    Rectangle(pos=(x, y), size=(width, height))

                self.start_pos = None


class TheLab(App):
    def build(self):
        return CanvasExample()


if __name__ == '__main__':
    TheLab().run()

现在我希望隔离绘制的特定区域,并对其应用OpenCV滤镜(如Canny边缘检测),更新图像使该区域显示滤镜处理后的效果,其余部分保持原样。我尝试通过位置属性隔离该区域,但得到了负坐标,尝试的代码如下:

def on_touch_up(self, touch):
    if self.selected_rect:
        # Get coordinates of selected rectangle
        x, y = self.selected_rect.pos
        w, h = self.selected_rect.size
        x, y, w, h = int(x), int(y), int(w), int(h)
        
        # Extract portion of image within the selected rectangle
        img_subset = self.img[y:y+h, x:x+w]
        
        # Apply canny edge detection to the subset
        edges = cv2.Canny(img_subset, 100, 200)
        
        # Create mask for edges
        edge_mask = np.zeros((h, w, 3), dtype=np.uint8)
        edge_mask[:, :, 0] = edges
        edge_mask[:, :, 1] = edges
        edge_mask[:, :, 2] = edges
            
        # Replace subset in original image with the edge mask
        self.img[y:y+h, x:x+w] = np.where(edge_mask > 0, edge_mask, img_subset)
        
        # Update Kivy texture and Image widget with modified image
        self.texture = self.convert_to_texture(self.img)
        self.image.texture = self.texture
        
        # Reset selected rectangle
        self.selected_rect = None

解决方法

问题核心在于矩形坐标的正负处理和Kivy与OpenCV的坐标系差异,以下是修正后的完整实现:

完整代码

from kivy.app import App
from kivy.uix.widget import Widget
from kivy.graphics import Color, Rectangle
from kivy.uix.image import Image
from kivy.graphics.texture import Texture
import cv2
import numpy as np


class CanvasExample(Widget):
    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        
        # 用OpenCV加载图像,保存原始副本避免多次修改失真
        self.original_img = cv2.imread('sampleImg.jpg')
        self.img = self.original_img.copy()
        img_h, img_w = self.img.shape[:2]
        
        # 初始化Kivy Image组件,匹配原始图像尺寸
        self.image = Image(size_hint=(None, None), size=(img_w, img_h))
        self.image.texture = self.convert_to_texture(self.img)
        self.add_widget(self.image)

        # 初始化变量
        self.start_pos = None
        self.rectangles = []

    def convert_to_texture(self, cv_img):
        # 转换OpenCV BGR图像到Kivy支持的纹理格式
        buf = cv2.flip(cv_img, 0).tostring()
        texture = Texture.create(size=(cv_img.shape[1], cv_img.shape[0]), colorfmt='bgr')
        texture.blit_buffer(buf, colorfmt='bgr', bufferfmt='ubyte')
        return texture

    def on_touch_down(self, touch):
        if self.image.collide_point(*touch.pos):
            # 转换触摸坐标为图像组件内部的本地坐标
            local_x = touch.pos[0] - self.image.x
            local_y = touch.pos[1] - self.image.y
            self.start_pos = (local_x, local_y)

    def on_touch_up(self, touch):
        if self.image.collide_point(*touch.pos) and self.start_pos is not None:
            # 获取触摸结束的本地坐标
            end_x = touch.pos[0] - self.image.x
            end_y = touch.pos[1] - self.image.y
            
            # 计算正确的矩形左上角、右下角坐标,确保宽高为正
            x1 = min(self.start_pos[0], end_x)
            y1 = min(self.start_pos[1], end_y)
            x2 = max(self.start_pos[0], end_x)
            y2 = max(self.start_pos[1], end_y)
            
            # 转换为整数坐标
            x1, y1, x2, y2 = int(x1), int(y1), int(x2), int(y2)
            w = x2 - x1
            h = y2 - y1
            
            # 转换Kivy坐标系到OpenCV坐标系(Kivy y轴从下往上,OpenCV从上往下)
            img_h = self.img.shape[0]
            cv_y1 = img_h - y2
            cv_y2 = img_h - y1
            
            # 提取目标区域图像
            img_subset = self.img[cv_y1:cv_y2, x1:x2]
            
            # 应用Canny边缘检测并转换为3通道格式
            edges = cv2.Canny(img_subset, 100, 200)
            edge_3ch = cv2.cvtColor(edges, cv2.COLOR_GRAY2BGR)
            
            # 将处理后的区域放回原图像
            self.img[cv_y1:cv_y2, x1:x2] = edge_3ch
            
            # 更新Kivy图像纹理
            self.image.texture = self.convert_to_texture(self.img)
            
            # 在图像上绘制半透明红色矩形标记
            with self.image.canvas:
                Color(1, 0, 0, 0.13)
                Rectangle(pos=(self.image.x + x1, self.image.y + y1), size=(w, h))
            
            self.start_pos = None


class TheLab(App):
    def build(self):
        return CanvasExample()


if __name__ == '__main__':
    TheLab().run()

关键修正点

  1. 坐标标准化处理

    • 用min和max计算矩形的左上角与右下角,确保宽高为正,彻底解决负索引问题。
    • 将触摸坐标转换为图像组件内部的本地坐标,排除布局偏移带来的误差。
  2. 坐标系转换

    • Kivy的y轴原点在屏幕底部,OpenCV的y轴原点在图像顶部,通过img_h - y完成坐标映射,保证提取区域准确。
  3. 图像加载与更新优化

    • 直接用OpenCV加载原始图像并保存副本,避免多次修改导致图像失真。
    • 实现convert_to_texture函数,完成OpenCV图像到Kivy纹理的格式转换(含图像翻转与颜色通道适配)。
  4. 滤镜应用适配

    • 将单通道的Canny检测结果转换为3通道图像,确保和原图像格式一致,避免拼接时的维度错误。

内容的提问来源于stack exchange,提问作者Gaurav

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.23 01:02:53