[Python] Spécifiez la plage de l'image en faisant glisser la souris

Chose que tu veux faire

"Spécifiez la plage de l'image" que vous voyez souvent avec les outils de rognage etc. Je veux rendre cela possible en faisant glisser la souris tout en affichant la ligne de guidage. ↓ L'image ressemble à ceci ↓ 画像領域取得デモ.gif

environnement

Windows10 Home Python3.8.2(32bit) Visual Studio Code 1.45.1

Ce que j'ai étudié (flow)

  1. Affichez n'importe quelle image dans la fenêtre tkinter
  2. Dessinez un rectangle sur l'image affichée
  3. Transformez le rectangle en faisant glisser la souris
  4. Obtenez les coordonnées du rectangle

1. Affichez n'importe quelle image dans la fenêtre tkinter

Dans cet exemple, nous prévoyons de dessiner un rectangle (ligne directrice) pour la capture d'écran. Par conséquent, prenez d'abord une capture d'écran en utilisant screenshot () de la bibliothèque pyautogui. Puisque l'image préparée ne peut pas être utilisée telle quelle dans tkinter, ** utilisez ImageTk.PthoImage pour la convertir en un format pouvant être affiché dans tkinter **. img_tk = ImageTk.PhotoImage(img) (Notez que cette conversion donnera une erreur comme "Conversion trop tôt!" Sauf si c'est après Tk ()) (ImageTk est une méthode Pillow, elle doit donc être importée séparément)

Après cela, créez un widget Canvas avec tkinter et placez-y l'image.

canvas1 = tkinter.Canvas(root, bg="black", width=img.width, height=img.height)
canvas1.create_image(0, 0, image=img_tk, anchor=tkinter.NW)

Le canevas ne se transforme pas automatiquement pour s'adapter à l'image, vous devez donc spécifier la taille du widget pour qu'elle soit identique à l'image.

L'option ʻanchor` de create_image spécifie "où baser l'image à placer". La valeur est écrite comme «tkinter.» Et nord, sud, est et ouest, et l'image est N: Nord ⇒ nord = haut. Autre que N, écrivez W: gauche, S: bas, E: droite, CENTRE: centre. Dans le cas de l'exemple, NW = en haut à gauche de l'image de img_tk sera placé à coordonnée x = 0 et coordonnée y = 0 (spécifiée par les premier et deuxième arguments).

2. Dessinez un rectangle sur l'image affichée

Tout d'abord, spécifiez ce qui se passe lorsque vous commencez à faire glisser sur Canvas.

widget de toile.bind("<ButtonPress-1>", <Fonction de rappel>)

Le traitement des dessins de lignes directrices est effectué dans la fonction de rappel appelée. (Lectangle signifie rectangle)

Widget de toile.create_rectangle(Coordonnée X du point de départ,Coordonnée Y du point de départ,Coordonnée X du point final,Coordonnée Y du point final, outline="Couleur de la ligne" ,tag="Nom de la balise")

Comme le rectangle dessiné sera transformé ultérieurement, il est nécessaire de spécifier le nom de la balise avec l'option tag.

Si l'événement d'argument est spécifié lors de la déclaration de la fonction de rappel, ʻevent.x, event.y` Puisque vous pouvez obtenir les coordonnées de la souris au moment du clic, définissez cette valeur sur les coordonnées du point de départ (le point final est approprié)

3. Transformez le rectangle en faisant glisser la souris

Ensuite, spécifiez l'opération lorsque la souris se déplace tout en faisant glisser avec Canvas.

Widget de toile.bind("<Button1-Motion>", <Fonction de rappel>)

Décrivez le processus de transformation de la ligne directrice dans la fonction de rappel appelée.

Widget de toile.coords("Nom de tag de la figure à transformer",Coordonnée X du point de départ,Coordonnée Y du point de départ,Coordonnée X du point final,Coordonnée Y du point final)

Vous pouvez modifier la taille de la figure dessinée avec. (Coords semble signifier coordination) Lors de la modification de la taille du rectangle en faisant glisser, j'ai fait attention aux deux irrégularités suivantes.

① Que faire en cas de déplacement vers la gauche ou au-dessus du point de départ

Si vous définissez les coordonnées graphiques actuelles sur les coordonnées du point de départ sans penser à rien, Cela provoque un bug lorsque la souris glissante se déplace vers la gauche ou au-dessus du point de départ. .. (Les informations de coordonnées du point de départ seront réécrites aux coordonnées après le glissement) 画像領域取得失敗2デモ.gif Par conséquent, en stockant les informations de coordonnées dans la variable globale lorsque la souris est cliquée et en utilisant cette valeur comme coordonnée du point de départ au moment du redessiner, Remplacement empêché ↓

#Événement au début du glissement- - - - - - - - - - - - - - - - - - - - - - - - - - 
def start_point_get(event):
    global start_x, start_y #Déclaré pour écrire dans des variables globales
    :
(Dessin)
    :
    #Stocker les coordonnées dans des variables globales
    start_x, start_y = event.x, event.y

#Événements glissés- - - - - - - - - - - - - - - - - - - - - - - - - - 
def rect_drawing(event):
      :
    # "rect1"Redessiner l'image du tag
    canvas1.coords("rect1", start_x, start_y, event.x, event.y)
(2) Que faire lorsque la souris sort de la zone de l'écran

Dans ce cas, si la souris sort de la zone de dessin, les coordonnées du bord de l'écran seront conservées. Par conséquent, selon que les coordonnées de la souris sont "zone de dessin (0 <x ≤ largeur de l'image)", "en dehors de la zone de dessin (x <0)" ou "en dehors de la zone de dessin (largeur <x)". Vous devez réécrire les coordonnées du point final. Vous pouvez chevaucher deux instructions if, mais cette fois, j'ai essayé de raccourcir le code en utilisant la fonction min. (Si vous le réorganisez, vous pouvez utiliser la fonction max)

    if event.x < 0:
        end_x = 0 #Définir 0 si les coordonnées acquises sont égales ou inférieures à 0
    else:
        end_x = min(img.width, event.x) #Définissez la largeur de l'image ou les coordonnées acquises, selon la plus petite des deux

4. Obtenez les coordonnées du rectangle

Spécifiez l'opération pour que les coordonnées puissent être acquises lorsque le glissement est terminé.

Widget de toile.bind("<ButtonRelease-1>", <Fonction de rappel>)

Vous pouvez utiliser la méthode coords décrite ci-dessus pour obtenir les coordonnées de la figure.

start_x, start_y, end_x, end_y =Widget de toile.coords("Nom de la balise")

Dans ce cas, il est difficile d'afficher une capture d'écran en plein écran dans la fenêtre tk, donc J'affiche une version un peu réduite et je dessine un rectangle dessus. Par conséquent, si vous voulez obtenir des coordonnées de taille réelle, vous devez multiplier les coordonnées obtenues par coords par le grossissement de réduction. Cette fois, j'ai utilisé la notation d'inclusion de liste et j'ai écrit le code aussi court que possible.

    start_x, start_y, end_x, end_y = [
        round(n * RESIZE_RETIO) for n in canvas1.coords("rect1")]

Ce qui précède est une série d'étapes. Je vous remercie pour votre travail acharné.

Code complété

import tkinter
import time
import pyautogui  #Bibliothèque externe
from PIL import Image, ImageTk  #Bibliothèque externe

RESIZE_RETIO = 2 #Régulation du rapport de réduction

#Événement au début du glissement- - - - - - - - - - - - - - - - - - - - - - - - - - 
def start_point_get(event):
    global start_x, start_y #Déclaré pour écrire dans des variables globales

    canvas1.delete("rect1")  #Déjà"rect1"Supprimer n'importe quelle forme d'étiquette

    #Dessinez un rectangle sur canvas1 (rectangle signifie rectangle)
    canvas1.create_rectangle(event.x,
                             event.y,
                             event.x + 1,
                             event.y + 1,
                             outline="red",
                             tag="rect1")
    #Stocker les coordonnées dans des variables globales
    start_x, start_y = event.x, event.y

#Événements glissés- - - - - - - - - - - - - - - - - - - - - - - - - - 
def rect_drawing(event):

    #Traitement lorsque le pointeur de la souris lors du glissement sort de la zone
    if event.x < 0:
        end_x = 0
    else:
        end_x = min(img_resized.width, event.x)
    if event.y < 0:
        end_y = 0
    else:
        end_y = min(img_resized.height, event.y)

    # "rect1"Redessiner l'image du tag
    canvas1.coords("rect1", start_x, start_y, end_x, end_y)

#Événement lorsque la traînée est relâchée- - - - - - - - - - - - - - - - - - - - - - - - - - 
def release_action(event):

    # "rect1"Récupérez les coordonnées de l'image de l'étiquette à l'échelle d'origine
    start_x, start_y, end_x, end_y = [
        round(n * RESIZE_RETIO) for n in canvas1.coords("rect1")
    ]

    #Afficher les coordonnées acquises
    pyautogui.alert("start_x : " + str(start_x) + "\n" + "start_y : " +
                    str(start_y) + "\n" + "end_x : " + str(end_x) + "\n" +
                    "end_y : " + str(end_y))

#Traitement principal- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
if __name__ == "__main__":

    #Acquisition de l'image à afficher (capture d'écran)
    img = pyautogui.screenshot()
    #Redimensionnement de l'image car l'image de la capture d'écran ne peut pas être affichée
    img_resized = img.resize(size=(int(img.width / RESIZE_RETIO),
                                   int(img.height / RESIZE_RETIO)),
                             resample=Image.BILINEAR)

    root = tkinter.Tk()
    root.attributes("-topmost", True) #Amenez toujours la fenêtre tkinter au premier plan

    #Conversion d'image pour qu'elle puisse être affichée avec tkinter
    img_tk = ImageTk.PhotoImage(img_resized)

    #Widget de dessin de canevas
    canvas1 = tkinter.Canvas(root,
                             bg="black",
                             width=img_resized.width,
                             height=img_resized.height)
    #Dessinez l'image acquise sur le widget Canvas
    canvas1.create_image(0, 0, image=img_tk, anchor=tkinter.NW)

    #Placez le widget Canvas et définissez divers événements
    canvas1.pack()
    canvas1.bind("<ButtonPress-1>", start_point_get)
    canvas1.bind("<Button1-Motion>", rect_drawing)
    canvas1.bind("<ButtonRelease-1>", release_action)

    root.mainloop()

Plans futurs

Maintenant que nous avons une interface pour spécifier la plage de l'image, je voudrais la connecter à divers outils. Par exemple, un outil qui "coupe en continu de l'écran de capture d'écran ⇒ enregistre comme nom à l'heure actuelle" Outils tels que "OCR à partir de la plage spécifiée". Non seulement l'écran de capture d'écran, mais aussi les outils qui traitent les images dans le presse-papiers peuvent être intéressants.

Le site que j'ai utilisé comme référence

Transformez le rectangle affiché sur le canevas Python

Recommended Posts

[Python] Spécifiez la plage de l'image en faisant glisser la souris
Supprimer le cadre de l'image
Traitement d'image par python (Pillow)
Appelez l'API Bing Image Search v5 depuis Python pour collecter des images
Recherche par image de la pellicule en utilisant Pythonista3
Utilisez le code python pour détecter la posture par openpose à partir de l'image de la caméra USB
Existence du point de vue de Python
Utilisez l'API Flickr de Python
Coupons le visage de l'image
Python3> Générer une liste à partir de itérable> liste (plage (5))
[Python Kivy] Comment obtenir le chemin du fichier par glisser-déposer
[Python] Spécifiez la zone d'image et appliquez l'OCR (dispositif d'enregistrement automatique du moniteur)
[python] Envoyez l'image capturée de la caméra Web au serveur et enregistrez-la
[Python] Définissez la plage du graphique avec matplotlib
Traitement d'image par le remplacement du canal Python 100 Knock # 1
[Python] Visualisez les informations acquises par Wireshark
Notes d'apprentissage depuis le début de Python 1
Mémo Python efficace Élément 10 Énumérer à partir de la plage
Échelle de gris par matrice-Reinventor of Python image processing-
Traitement d'image par Python 100 Knock # 6 Traitement de réduction de couleur
Acquisition d'images depuis une caméra avec Python + OpenCV
Lisez le fichier ligne par ligne avec Python
Lisez le fichier ligne par ligne avec Python
Lancez l'interpréteur Python depuis Git bash
Pandas du débutant, par le débutant, pour le débutant [Python]
Analyse d'image de microtomographie à rayons X par Python
Depuis Python 3.4, pip devient le programme d'installation standard! ??
Notes d'apprentissage depuis le début de Python 2
[Python] Obtenez la couleur principale de la capture d'écran
Rendre la bibliothèque créée par Eigen of C ++ disponible à partir de Python avec Boost.Numpy.
Méthode d'extraction de zone à l'aide de l'automate cellulaire Essayez l'extraction de zone de l'image avec growcut (Python)
Trouvez la position dans l'image d'origine à partir des coordonnées après conversion affine (Python + OpenCV)
Lorsque vous passez la souris sur Matplotlib, l'image correspondante s'affiche.
[Python] Téléchargez l'image d'origine à partir de la recherche d'images Google
Récupérer le contenu de git diff depuis python
La première application Web créée par des débutants en Python
J'ai essayé de "différencier" l'image avec Python + OpenCV
Traitement d'image à partir de zéro avec python (5) Transformation de Fourier
Télécharger des images à partir d'un fichier texte contenant l'URL
Qu'est-ce que wheezy dans l'image Docker Python?
Spécifiez le fichier exécutable Python à utiliser avec virtualenv
Spécification de la plage des tableaux ruby et python
[Python] Trier la table par sort_values (pandas DataFrame)
Traitement d'image à partir de zéro avec python (4) Extraction de contour
Traitement d'image? L'histoire du démarrage de Python pour
J'ai essayé de "binariser" l'image avec Python + OpenCV
Comment effacer les caractères générés par Python
J'ai essayé d'utiliser le module Datetime de Python
[Python numpy] Spécifiez dynamiquement l'index du tableau
Traitement d'image par filtre de lissage Python 100 knock # 11 (filtre moyen)
Lire ligne par ligne à partir d'un fichier avec Python
Utilisez le module Python nghttp2 de Homebrew de Python de pyenv
Appeler Polly à partir du kit SDK AWS pour Python
Essayez d'accéder à l'API YQL directement depuis Python 3
[Python + OpenCV] Peignez la partie transparente de l'image en blanc
Suivi d'objets à l'aide d'OpenCV3 et de Python3 (suivi des points caractéristiques spécifiés par la souris à l'aide de la méthode Lucas-Kanade)