Python tkinter 实现本地打开图片进行标注

需求:

写一个软件,要求本地打开一张图片显示出来,并可以进行选框标注,自动生成坐标,同时给出输入框,可以手动输入内容,并保存成json文件 。

输入: 本地打开一张图片
操作: 鼠标进行选框标注,并输入选框内的文字
输出: 坐标值: x、y、w、h,json文件。 json文件包含 x、y、w、h和文字内容。

选工具

对python比较熟悉 ,Tkinter工具使用过,首选吧。 tkinter的文档比较少,但是做的过程中遇到的一些问题还是不能解决。

打开文件 并显示图片

#-*- coding:utf-8 -*-
import tkinter
from tkinter import *
from PIL import Image,ImageTk
from tkinter.filedialog import askopenfilename
import time

root = Tk()
root.geometry('500x500')
root.title('图片处理')

def choosepic():
    path_ = askopenfilename()
    path.set(path_)
    img_open = Image.open(file_entry.get())
    img = ImageTk.PhotoImage(img_open)
    image_label.config(image=img)
    image_label.image = img  # keep a reference


path = StringVar()
Button(root, text='选择图片', command=choosepic).pack()
file_entry = Entry(root, state='readonly', text=path)
#file_entry.pack() 
image_label = Label(root)
image_label.pack()
root.mainloop()

打开图片并刷新label显示,我自己写的代码一直没有成功,label打开后总是不能成功。以上代码参考下面连接。
python如何在tkinter中动态显示label图片

直接上全部代码吧

# -*- coding:utf-8 -*-
import tkinter
import tkinter.filedialog
import os
from PIL import ImageGrab
from time import sleep
from tkinter import *

from PIL import Image,ImageTk
from tkinter.filedialog import askopenfilename
import json
import codecs

# 创建tkinter主窗口
root = tkinter.Tk()
root.title('图片处理')

# 指定主窗口位置与大小
root.geometry('800x600+100+50')   # width x height + widthoffset + heightoffset

# 不允许改变窗口大小
root.resizable(False, False)
root.focusmodel()

class MyCapture:
    def __init__(self, png):

        # 变量X和Y用来记录鼠标左键按下的位置
        self.X = tkinter.IntVar(value=0)
        self.Y = tkinter.IntVar(value=0)
        self.selectPosition = None

        # 屏幕尺寸
        screenWidth = root.winfo_screenwidth()
        screenHeight = root.winfo_screenheight()

        # 创建顶级组件容器
        self.top = tkinter.Toplevel(root, width=screenWidth, height=screenWidth)

        # 不显示最大化、最小化按钮
        self.top.overrideredirect(True)
        self.canvas = tkinter.Canvas(self.top, bg='white', width=screenWidth, height=screenWidth)

        # 显示全屏截图,在全屏截图上进行区域截图
        self.image = tkinter.PhotoImage(file=png)
        self.canvas.create_image(screenWidth // 2, screenHeight // 2, image=self.image)

        # 鼠标左键按下的位置
        def onLeftButtonDown(event):
            self.X.set(event.x)
            self.Y.set(event.y)
            # 开始截图
            self.sel = True

        self.canvas.bind('<Button-1>', onLeftButtonDown)

        # 鼠标左键移动,显示选取的区域
        def onLeftButtonMove(event):
            if not self.sel:
                return
            global lastDraw
            try:
                # 删除刚画完的图形,要不然鼠标移动的时候是黑乎乎的一片矩形
                self.canvas.delete(lastDraw)
            except Exception as e:
                pass
            lastDraw = self.canvas.create_rectangle(self.X.get(), self.Y.get(), event.x, event.y, outline='red')

        self.canvas.bind('<B1-Motion>', onLeftButtonMove)

        # 获取鼠标左键抬起的位置,保存区域截图
        def onLeftButtonUp(event):
            self.sel = False
            try:
                self.canvas.delete(lastDraw)
            except Exception as e:
                pass
            sleep(0.1)

            # 更新主窗口位置坐标
            root.update()
            x = root.winfo_x()
            y = root.winfo_y()

            #考虑鼠标左键从右下方按下而从左上方抬起的截图
            myleft, myright = sorted([self.X.get(), event.x])
            mytop, mybottom = sorted([self.Y.get(), event.y])

            # Position : x , y, w, h
            self.selectPosition = (myleft-x,  mytop-y-30, myright-x,mybottom-y-30)
            print (myleft, myright)
            self.top.destroy()
            print ("destroy")
        self.canvas.bind('<ButtonRelease-1>', onLeftButtonUp)
        self.canvas.pack(fill=tkinter.BOTH, expand=tkinter.YES)
        # 开始截图

#设置截取坐标全局变量, 元组类型
CapturePosition = ()

def buttonCaptureClick():
    filename = 'temp.png'
    im = ImageGrab.grab()
    im.save(filename)
    im.close()

    # 显示全屏幕截图
    w = MyCapture(filename)
    buttonCapture.wait_window(w.top)

    xy_text.set(str(w.selectPosition))
    global  CapturePosition
    CapturePosition = w.selectPosition

    root.state('normal')
    os.remove(filename)

#获取输入的标注内容
def getstr():
    if input_valueText.get() != '':
        words = input_valueText.get()
        get_label["text"] = words


def save_jsonfile():
    json_content = {}
    #获取坐标
    json_content['x'] = CapturePosition[0]
    json_content['y'] = CapturePosition[1]
    json_content['w'] = CapturePosition[2]
    json_content['h'] = CapturePosition[3]

    # 获取输入内容
    value = get_label["text"]
    json_content['value'] = value

    # 存入json文件
    final_json = json.dumps(json_content, ensure_ascii=False)
    with codecs.open ("d:\\data_json.json",'a','utf-8') as file:
          file.write(final_json)


# 定义坐标显示位置
xy_text = StringVar()


#打开图片文件并显示
def choosepic():
    path_ = askopenfilename()
    path.set(path_)
    img_open = Image.open(file_entry.get())
    img = ImageTk.PhotoImage(img_open)
    image_label.config(image=img)
    image_label.image = img  # keep a reference

path = StringVar()
tkinter.Button(root, text = '打开文件' ,command=choosepic).place(x = 100, y = 530,w = 150, h = 40)
file_entry = Entry(root, state='readonly', text=path)
file_entry.pack()
image_label = Label(root, bg = 'gray')
image_label.place(x=0, y=0,width = 500, height = 500)


#添加截图按钮功能
buttonCapture = tkinter.Button(root, text='标注', command=buttonCaptureClick)

#坐标名称及显示坐标
label_xytitle = tkinter.Label(root,text ='标注位置坐标x, y, w, h :')
label_xytitle.place(x=520, y=50)
label = tkinter.Label(root, textvariable=xy_text,fg = 'blue')
label.place(x=520, y=70)


# 输入标注内容
input_valueTitle = StringVar()
input_valueTitle.set('请输入内容:')
label_inputTitle = tkinter.Label(root, textvariable=input_valueTitle)
label_inputTitle.place(x=520, y=90)
#输入框
input_valueText = tkinter.Entry(root,width=12)
input_valueText.place(x =520 , y=120,width = 250, height=40,anchor = NW)

#获取输入内容
comand = tkinter.Button(root, text="获取内容" ,command=getstr, width=10, height=2)
comand.place(x =520 , y=150,width = 250, height=40,anchor = NW)
get_label = tkinter.Label(root, fg = 'blue')
get_label.place(x =520 , y=200)

buttonCapture.place(x=300, y=530, width=150, height=40)

#保存数据
save = tkinter.Button(root, text="保存数据", command=save_jsonfile, width=10, height=2)
save.place(x =500 , y=530,width = 150, height=40,anchor = NW)

# 启动消息主循环
root.update()
root.mainloop()

需要整理下,看时间吧。 先记录一下。
遗留问题: 鼠标画框后,没有能把线画出来。 用canvas试了下,总有这样那样的问题。 待解决。

推荐阅读更多精彩内容