Kivy中如何获取点击位置对应的图像像素坐标
Kivy获取图像像素坐标问题
我正在使用Kivy开发一款应用,旨在在实时视频流上绘制n边形以标记感兴趣区域。当前遇到的问题是Kivy提供的是相对于整个窗口的坐标,而非仅图像的像素坐标。我希望获取点击位置的x、y像素坐标,尝试过to_local()方法但未得到理想效果,以下是最小可复现示例代码:
from kivy.app import App from kivy.uix.image import Image from kivy.uix.boxlayout import BoxLayout from kivy.graphics import Color, Ellipse, Line from random import random class ImageView(Image): def on_touch_down(self, touch): ## # This provides touch cords for the entire app and not just the location of the pixel clicked# print("Touch Cords", touch.x, touch.y) ## color = (random(), 1, 1) with self.canvas: Color(*color, mode='hsv') d = 30. Ellipse(pos=(touch.x - d / 2, touch.y - d / 2), size=(d, d)) touch.ud['line'] = Line(points=(touch.x, touch.y)) def on_touch_move(self, touch): touch.ud['line'].points += [touch.x, touch.y] class DMSApp(App): def build(self): imagewidget = ImageView(source="/home/red/Downloads/600.png") imagewidget.size_hint = (1, .5) imagewidget.pos_hint = {"top": 1} layout = BoxLayout(size_hint=(1, 1)) layout.add_widget(imagewidget) return layout if __name__ == '__main__': DMSApp().run()
解决方案
要将窗口坐标转换为图像的像素坐标,需要考虑Image组件的缩放规则和偏移位置(图像默认会保持宽高比居中显示)。修改后的代码如下:
from kivy.app import App from kivy.uix.image import Image from kivy.uix.boxlayout import BoxLayout from kivy.graphics import Color, Ellipse, Line from random import random class ImageView(Image): def on_touch_down(self, touch): # 过滤组件外的触摸事件 if not self.collide_point(*touch.pos): return super().on_touch_down(touch) # 转换为组件本地坐标(相对于组件左上角) local_x, local_y = self.to_local(*touch.pos) # 获取图像原始像素尺寸 img_width, img_height = self.norm_image_size # 计算实际缩放比例(适配保持宽高比的显示逻辑) scale_x = self.width / img_width scale_y = self.height / img_height scale = min(scale_x, scale_y) # 计算图像在组件内的居中偏移量 offset_x = (self.width - img_width * scale) / 2 offset_y = (self.height - img_height * scale) / 2 # 转换为图像像素坐标,反转y轴(Kivy坐标系y轴向上,图像像素y轴向下) pixel_x = (local_x - offset_x) / scale pixel_y = img_height - (local_y - offset_y) / scale # 确保坐标在图像有效范围内 pixel_x = max(0, min(pixel_x, img_width)) pixel_y = max(0, min(pixel_y, img_height)) print("图像像素坐标", pixel_x, pixel_y) color = (random(), 1, 1) with self.canvas: Color(*color, mode='hsv') d = 30. # 绘制图形仍使用窗口坐标 Ellipse(pos=(touch.x - d/2, touch.y - d/2), size=(d, d)) touch.ud['line'] = Line(points=(touch.x, touch.y)) return True def on_touch_move(self, touch): if 'line' in touch.ud and self.collide_point(*touch.pos): touch.ud['line'].points += [touch.x, touch.y] return True return super().on_touch_move(touch) class DMSApp(App): def build(self): imagewidget = ImageView(source="/home/red/Downloads/600.png") imagewidget.size_hint = (1, .5) imagewidget.pos_hint = {"top": 1} # 显式声明保持宽高比 imagewidget.allow_stretch = False imagewidget.keep_ratio = True layout = BoxLayout(size_hint=(1, 1)) layout.add_widget(imagewidget) return layout if __name__ == '__main__': DMSApp().run()
关键说明:
self.collide_point(*touch.pos):确保只处理当前Image组件内的触摸事件self.norm_image_size:获取图像的原始像素尺寸,不受缩放影响- 缩放比例计算:取宽、高缩放值的最小值,匹配Kivy默认的保持宽高比显示逻辑
- 偏移量计算:处理图像在组件内居中显示的空白区域,修正坐标起点
- y轴反转:转换Kivy坐标系与图像像素坐标系的y轴方向差异
内容的提问来源于stack exchange,提问作者Hood Khizer
相关产品推荐
相关产品推荐

