Source code for concepts.gui.tk.point_picker
#! /usr/bin/env python3
# -*- coding: utf-8 -*-
# File : point_picker.py
# Author : Jiayuan Mao
# Email : maojiayuan@gmail.com
# Date : 07/29/2024
#
# This file is part of Project Concepts.
# Distributed under terms of the MIT license.
from typing import Tuple
from PIL import Image, ImageTk
import numpy as np
import tkinter as tk
[docs]
def get_click_coordinates_from_image(image: Image.Image, min_dimension: int = 0) -> Tuple[int, int]:
"""Opens an image in a UI window and waits for the user to click on the image.
Returns the coordinates of the click and closes the window.
Args:
image: a PIL Image object.
min_dimension: minimum dimension of the image window. If the image is smaller than this, it will be
resized to fit this dimension while maintaining the aspect ratio.
Returns:
a tuple containing the u and v coordinates of the click (axis 1 and axis 0).
"""
# Define a simple class to hold the application's state.
class ImageClickApp:
def __init__(self, master, img):
self.master = master
self.clicked_coords = None
# Load and display the image.
img_tk = ImageTk.PhotoImage(img)
self.lbl = tk.Label(master, image=img_tk)
self.lbl.pack()
# Bind the click event.
self.lbl.bind("<Button-1>", self.on_click)
# Keep a reference to prevent garbage-collection.
self.lbl.img_tk = img_tk
def on_click(self, event):
# Store the coordinates and close the window.
self.clicked_coords = (event.x, event.y)
self.master.destroy()
# Resize the image if it is smaller than the minimum dimension.
scaling_factor = (1, 1)
if min(image.size) < min_dimension:
old_size = image.size
aspect_ratio = image.size[0] / image.size[1]
new_size = (min_dimension, int(min_dimension / aspect_ratio))
image = image.resize(new_size)
scaling_factor = new_size[0] / old_size[0], new_size[1] / old_size[1]
# Create the Tkinter window.
root = tk.Tk()
app = ImageClickApp(root, image)
# Run the event loop and wait for it to finish.
root.mainloop()
# Return the coordinates after the window has been closed.
coords = app.clicked_coords
if coords is None:
raise ValueError("No coordinates were clicked.")
return int(round(coords[0] / scaling_factor[0])), int(round(coords[1] / scaling_factor[1]))
[docs]
def get_click_coordinates_from_image_path(image_path: str, min_dimension: int = 0) -> Tuple[int, int]:
"""Opens an image in a UI window and waits for the user to click on the image.
Returns the coordinates of the click and closes the window.
Args:
image_path: path to the image file.
min_dimension: minimum dimension of the image window. If the image is smaller than this, it will be
resized to fit this dimension while maintaining the aspect ratio.
Returns:
a tuple containing the u and v coordinates of the click (axis 1 and axis 0).
"""
image = Image.open(image_path)
return get_click_coordinates_from_image(image, min_dimension)
[docs]
def get_click_coordinates_from_array(image_array: np.ndarray, min_dimension: int = 0) -> Tuple[int, int]:
"""Opens an image (from a numpy array) in a UI window and waits for the user to click on the image.
Returns the coordinates of the click and closes the window.
Args:
image_array: a numpy array representing the image.
min_dimension: minimum dimension of the image window. If the image is smaller than this, it will be
resized to fit this dimension while maintaining the aspect ratio.
Returns:
a tuple containing the u and v coordinates of the click (axis 1 and axis 0).
"""
image = Image.fromarray(image_array)
return get_click_coordinates_from_image(image, min_dimension)
# Example usage:
if __name__ == "__main__":
image_path = "image_scene/hook_with_ball.png" # Change this to the path of your image.
coordinates = get_click_coordinates_from_image_path(image_path)
print(f"Clicked coordinates: {coordinates}")