您好,登錄后才能下訂單哦!
.tkinter
tkinter是Python下面向tk的圖形界面接口庫,可以方便地進行圖形界面設計和交互操作編程。tkinter的優點是簡單易用、與Python的結合度好。tkinter在Python 3.x下默認集成,不需要額外的安裝操作;不足之處為缺少合適的可視化界面設計工具,需要通過代碼來完成窗口設計和元素布局。
Python tkinter實現圖片標注代碼,代碼如下所述:
#!/usr/bin/python # -*- coding: UTF-8 -*- import os import sys if sys.version_info < (3, 0): import Tkinter as tk # 導入 Tkinter 庫 from tkFileDialog import askopenfilename, asksaveasfilename else : import tkinter as tk # 導入 Tkinter 庫 from tkinter.filedialog import askopenfilename, asksaveasfilename from PIL import Image, ImageTk, ImageDraw from time import sleep import numpy as np import cv2 as cv DEF_WIDTH = 1080 DEF_HEIGHT = 720 IMAGE_HEIGHT = 720 FRAME_LEFT_WIDTH = 360 # 太小的選定區域我們需要丟棄,防止誤操作 MINI_RECT_AREA = 20 class RawImageEditor: def __init__(self, win, img, rects): #變量X和Y用來記錄鼠標左鍵按下的位置 self.X = tk.IntVar(value=0) self.Y = tk.IntVar(value=0) self.sel = False self.lastDraw = None self.lastDraws = [] self.imageScale = 1.0 self.dispWidth = DEF_WIDTH # 圖片顯示區域的最大高度,寬度 self.dispHeight = DEF_HEIGHT self.rawImage = img self.calcImageScale(self.rawImage) self.dispWidth = int(self.imageScale * self.rawImage.width) self.dispHeight = int(self.imageScale * self.rawImage.height) # 圖片縮放 self.dispImage = self.rawImage.resize((self.dispWidth, self.dispHeight)) # 選擇區域 self.selPositions = [] for r in rects : self.selPositions.append((r[0] * self.imageScale, r[1] * self.imageScale, r[2] * self.imageScale, r[3] * self.imageScale)) #創建頂級組件容器 self.top = tk.Toplevel(win, width=self.dispWidth, height=self.dispHeight) #不顯示最大化、最小化按鈕 self.top.overrideredirect(True) # Make topLevelWindow remain on top until destroyed, or attribute changes. self.top.attributes('-topmost', 'true') self.canvas = tk.Canvas(self.top, bg='white', width=self.dispWidth, height=self.dispHeight) self.tkImage = ImageTk.PhotoImage(self.dispImage) self.canvas.create_image(self.dispWidth//2, self.dispHeight//2, image=self.tkImage) for r in self.selPositions : draw = self.canvas.create_rectangle(r[0], r[1], r[2], r[3], outline='green') self.lastDraws.append(draw) #鼠標左鍵按下的位置 def onLeftButtonDown(event): self.X.set(event.x) self.Y.set(event.y) #開始截圖 self.sel = True #重新繪制已經選擇的區域 for draw in self.lastDraws : self.canvas.delete(draw) self.lastDraws = [] for r in self.selPositions : draw = self.canvas.create_rectangle(r[0], r[1], r[2], r[3], outline='green') self.lastDraws.append(draw) self.canvas.bind('<Button-1>', onLeftButtonDown) #鼠標左鍵移動,顯示選取的區域 def onLeftButtonMove(event): if not self.sel: return try: #刪除剛畫完的圖形,要不然鼠標移動的時候是黑乎乎的一片矩形 self.canvas.delete(self.lastDraw) except Exception as e: pass self.lastDraw = self.canvas.create_rectangle(self.X.get(), self.Y.get(), event.x, event.y, outline='green') self.canvas.bind('<B1-Motion>', onLeftButtonMove) #獲取鼠標左鍵抬起的位置,保存區域截圖 def onLeftButtonUp(event): self.sel = False sleep(0.1) #考慮鼠標左鍵從右下方按下而從左上方抬起的截圖 left, right = sorted([self.X.get(), event.x]) top, bottom = sorted([self.Y.get(), event.y]) if (right - left) * (bottom - top) > MINI_RECT_AREA : self.selPositions.append((left,top,right,bottom)) #self.top.destroy() #鼠標右鍵按下 def onRightButtonDown(event): self.sel = False self.top.destroy() self.canvas.bind('<Button-2>', onRightButtonDown) self.canvas.bind('<ButtonRelease-1>', onLeftButtonUp) self.canvas.pack(fill=tk.BOTH, expand=tk.YES) def calcImageScale(self, image) : w = image.width h = image.height self.imageScale = 1.0 # 計算最小的縮放比例,保證原始寬高比 if w > self.dispWidth and h > self.dispHeight : ws = self.dispWidth * 1.0 / w hs = self.dispHeight * 1.0 / h if ws < hs : self.imageScale = ws else : self.imageScale = hs elif w > self.dispWidth and h < self.dispHeight : self.imageScale = self.dispWidth * 1.0 / w elif w < self.dispWidth and h > self.dispHeight : self.imageScale = self.dispHeight * 1.0 / h def waitForWindow(self, win) : win.wait_window(self.top) def selectedPositions(self) : # 轉換為原始像素位置 realPos = [] for r in self.selPositions : realPos.append((r[0] / self.imageScale, r[1] / self.imageScale, r[2] / self.imageScale, r[3] / self.imageScale)) return realPos class MainWin(tk.Tk): def __init__(self): if sys.version_info >= (3, 0): super().__init__() else : tk.Tk.__init__(self) self.title('圖像處理工具') self.geometry('{}x{}'.format(DEF_WIDTH, DEF_HEIGHT)) self.rawImagePath = '' self.rawImage = None # self.rawImage 原始圖像,未經過縮放處理 self.transRawImage = None # self.transRawImage 經過轉換處理之后的原始圖像,沒有經過縮放處理 self.dispImage = None # self.dispImage 顯示圖像,可能經過縮放處理 self.imageScale = 1.0 # 圖片縮放比例,根據縮放比例進行顯示的時候的縮放處理,后期選擇區域的時候,需要進行縮放還原 self.leftFrameWidth = FRAME_LEFT_WIDTH self.frameDispHeight = DEF_HEIGHT # 整個窗口的高度 self.labelTextHeight = 20 # 文本標簽的高度 self.btnHeight = 40 # 按鈕的高度 self.imageDispWidth = IMAGE_HEIGHT # 圖片顯示區域的最大高度,寬度 self.imageDispHeight = self.frameDispHeight / 2 - self.labelTextHeight * 2 # 選擇區域 self.liRect = [] self.rawImageEditor = None self.setupUI() def scaleDisplayImage(self, image) : w = image.width h = image.height self.imageScale = 1.0 # 計算最小的縮放比例,保證原始寬高比 if w > self.imageDispWidth and h > self.imageDispHeight : ws = self.imageDispWidth * 1.0 / w hs = self.imageDispHeight * 1.0 / h if ws < hs : self.imageScale = ws else : self.imageScale = hs elif w > self.imageDispWidth and h < self.imageDispHeight : self.imageScale = self.imageDispWidth * 1.0 / w elif w < self.imageDispWidth and h > self.imageDispHeight : self.imageScale = self.imageDispHeight * 1.0 / h # 圖片縮放 return image.resize((int(self.imageScale * w), int(self.imageScale * h))) # 打開圖片時使用,傳值(圖)給展示函數 def openAndDisplayImage(self): self.rawImagePath = self.selectImageFile() if '' != self.rawImagePath : self.rawImage = Image.open(self.rawImagePath) self.rawImage = self.rawImage.convert('RGBA') self.drawRawImageDisp() def drawListBox(self): self.l_box.delete(0,tk.END) for item in self.liRect: r = '{},{},{},{}'.format(round(item[0],1), round(item[1],1), round(item[2],1), round(item[3],1)) self.l_box.insert(0, r) def drawRawImageDisp(self, selItems=[]): self.dispImage = self.scaleDisplayImage(self.rawImage) self.dispImage = self.dispImage.convert('RGB') draw = ImageDraw.Draw(self.dispImage) for i in range(len(self.liRect)) : r = self.liRect[i] if i in selItems : draw.rectangle((r[0] * self.imageScale, r[1] * self.imageScale, r[2] * self.imageScale, r[3] * self.imageScale), outline = "red") else : draw.rectangle((r[0] * self.imageScale, r[1] * self.imageScale, r[2] * self.imageScale, r[3] * self.imageScale), outline = "green") img = ImageTk.PhotoImage(self.dispImage) self.image_l_raw.config(image=img) self.image_l_raw.image = img def deleteSelectedItemFromListBox(self): #print(self.l_box.get(self.l_box.curselection())) idx = self.l_box.curselection() if len(idx) > 0 : kp = [] for v in range(len(self.liRect)) : if v not in idx : kp.append(self.liRect[v]) self.liRect = kp self.drawListBox() self.drawRawImageDisp() # 打開圖片時使用,獲得地址 def selectImageFile(self): path = tk.StringVar() file_entry = tk.Entry(self, state='readonly', text=path) path_ = askopenfilename() path.set(path_) return file_entry.get() def rawImageLabelClicked(self, event): if None != self.rawImage : if None == self.rawImageEditor : self.rawImageEditor = RawImageEditor(self, self.rawImage, self.liRect) self.rawImageEditor.waitForWindow(self.image_l_raw) self.liRect = self.rawImageEditor.selectedPositions() self.rawImageEditor = None self.drawListBox() self.drawRawImageDisp() def onRectListboxSelect(self, event): idx = self.l_box.curselection() if len(idx) > 0 : self.drawRawImageDisp(idx) def drawTransImageDisp(self): transImage = self.scaleDisplayImage(self.transRawImage) transImage = transImage.convert('L') img = ImageTk.PhotoImage(transImage) self.image_l_trans.config(image=img) self.image_l_trans.image = img def doTransRawImage(self): self.transRawImage = Image.new('L', (self.rawImage.width, self.rawImage.height)) for r in self.liRect : im = self.rawImage.crop(r) cv_im = cv.cvtColor(np.asarray(im), cv.COLOR_RGB2BGR) hsv = cv.cvtColor(cv_im, cv.COLOR_BGR2HSV) _, _, v = cv.split(hsv) avg = np.average(v.flatten()) pixels = im.load() for j in range(im.height) : for i in range(im.width) : hv = v[j,i] if hv < avg * 1.2: #im.putpixel((i, j), 0) # slow pixels[i, j] = 0 '''else : im.putpixel((i, j), (255, 255, 255, 255))''' self.transRawImage.paste(im, (int(r[0]),int(r[1])), mask = None) self.drawTransImageDisp() def onTransRawImageBtnClicked(self): if None != self.rawImage : self.doTransRawImage() def onSaveTransRawImageBtnClicked(self): if None != self.transRawImage : ext = os.path.splitext(self.rawImagePath)[-1] (path,name) = os.path.split(self.rawImagePath) filename = asksaveasfilename(title = '保存圖片', initialfile = name, filetypes = (("jpeg files","*{}".format(ext)), ("all files","*.*"))) if '' != filename : self.transRawImage.save(filename) def setupUI(self): # 左邊菜單欄 left_f = tk.Frame(self, height=self.frameDispHeight, width=self.leftFrameWidth) left_f.pack(side=tk.LEFT) # 各種功能按鈕名稱及位置 btnOpen = tk.Button(left_f, text='打開圖像', command=self.openAndDisplayImage) btnOpen.place(y=25, x=30, width=300, height=self.btnHeight) btnTrans = tk.Button(left_f, text='處理圖像', command=self.onTransRawImageBtnClicked) btnTrans.place(y=85, x=30, width=300, height=self.btnHeight) l_selRect = tk.Label(left_f, text = '鼠標選定區域') l_selRect.place(x=0, y=165, width=self.leftFrameWidth, height=self.labelTextHeight) '''列表''' self.l_box = tk.Listbox(left_f) # 創建兩個列表組件 self.l_box.place(x=0, y=165+self.labelTextHeight, width=self.leftFrameWidth, height=270) self.l_box.bind('<<ListboxSelect>>', self.onRectListboxSelect) self.drawListBox() # 刪除選定項 btnDel = tk.Button(left_f, text='刪除選定項', command=self.deleteSelectedItemFromListBox) btnDel.place(y=460, x=30, width=300, height=self.btnHeight) btnSave = tk.Button(left_f, text='保存結果', command=self.onSaveTransRawImageBtnClicked) btnSave.place(y=550, x=30, width=300, height=self.btnHeight) # 右側圖像顯示欄 right_f = tk.Frame(self, height=self.frameDispHeight, width=self.imageDispWidth) right_f.pack(side=tk.RIGHT) l_rawT = tk.Label(right_f, text = '原始圖片') l_rawT.place(x=0, y=0, width=self.imageDispWidth, height=self.labelTextHeight) self.image_l_raw = tk.Label(right_f, relief='ridge') self.image_l_raw.place(x=0, y=self.labelTextHeight, width=self.imageDispWidth, height=self.imageDispHeight) self.image_l_raw.bind("<Button-1>",self.rawImageLabelClicked) l_transT = tk.Label(right_f, text = '處理后圖片') l_transT.place(x=0, y=self.labelTextHeight + self.imageDispHeight, width=self.imageDispWidth, height=self.labelTextHeight) self.image_l_trans = tk.Label(right_f, relief='ridge') self.image_l_trans.place(x=0, y=self.labelTextHeight + self.imageDispHeight + self.labelTextHeight, width=self.imageDispWidth, height=self.imageDispHeight) if __name__ == '__main__' : win = MainWin() # 進入消息循環 win.mainloop()
總結
以上所述是小編給大家介紹的Python tkinter實現圖片標注功能,希望對大家有所幫助,如果大家有任何疑問歡迎給我留言,小編會及時回復大家的!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。