Home > Blockchain >  Get location of pixel upon click in Kivy
Get location of pixel upon click in Kivy

Time:11-30

I am using Kivy to design an application to draw an n-sided polygon over a live video stream to demarcate regions of interest. The problem I have is that Kivy provides coordinates w.r.t to the whole window and not just the image. What I would like is to have the location of the pixel (in x and y cords) clicked. I have looked at to_local() method but it didn't make much sense, neither did it produce desired results. Any help would be appreciated, below is the MRE.

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()

CodePudding user response:

You can calculate the coordinates of the touch relative to the lower left corner of the actual image that appears in the GUI. Those coordinates can then be scaled to the actual size of the source image to get a reasonable estimate of the actual pixel coordinates within the source. Here is a modified version of your in_touch_down() method that does that (only minimal testing performed):

def on_touch_down(self, touch):
    if not self.collide_point(*touch.pos):
        return super(ImageView, self).on_touch_down(touch)

    lr_space = (self.width - self.norm_image_size[0]) / 2  # empty space in Image widget left and right of actual image
    tb_space = (self.height - self.norm_image_size[1]) / 2  # empty space in Image widget above and below actual image
    print('lr_space =', lr_space, ', tb_space =', tb_space)
    print("Touch Cords", touch.x, touch.y)
    print('Size of image within ImageView widget:', self.norm_image_size)
    print('ImageView widget:, pos:', self.pos, ', size:', self.size)
    print('image extents in x:', self.x   lr_space, self.right - lr_space)
    print('image extents in y:', self.y   tb_space, self.top - tb_space)
    pixel_x = touch.x - lr_space - self.x  # x coordinate of touch measured from lower left of actual image
    pixel_y = touch.y - tb_space - self.y  # y coordinate of touch measured from lower left of actual image
    if pixel_x < 0 or pixel_y < 0:
        print('clicked outside of image\n')
        return True
    elif pixel_x > self.norm_image_size[0] or \
            pixel_y > self.norm_image_size[1]:
        print('clicked outside of image\n')
        return True
    else:
        print('clicked inside image, coords:', pixel_x, pixel_y)

        # scale coordinates to actual pixels of the Image source
        print('actual pixel coords:',
              pixel_x * self.texture_size[0] / self.norm_image_size[0],
              pixel_y * self.texture_size[1] / self.norm_image_size[1], '\n')

    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
  • Related