亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 開發 > Python > 正文

Python tkinter實現圖片標注功能(完整代碼)

2024-09-09 19:03:06
字體:
來源:轉載
供稿:網友

.tkinter

tkinter是Python下面向tk的圖形界面接口庫,可以方便地進行圖形界面設計和交互操作編程。tkinter的優點是簡單易用、與Python的結合度好。tkinter在Python 3.x下默認集成,不需要額外的安裝操作;不足之處為缺少合適的可視化界面設計工具,需要通過代碼來完成窗口設計和元素布局。

Python tkinter實現圖片標注代碼,代碼如下所述:

#!/usr/bin/python# -*- coding: UTF-8 -*-import osimport sysif sys.version_info < (3, 0):   import Tkinter as tk # 導入 Tkinter 庫  from tkFileDialog import askopenfilename, asksaveasfilenameelse :  import tkinter as tk # 導入 Tkinter 庫  from tkinter.filedialog import askopenfilename, asksaveasfilenamefrom PIL import Image, ImageTk, ImageDrawfrom time import sleepimport numpy as npimport cv2 as cvDEF_WIDTH = 1080DEF_HEIGHT = 720IMAGE_HEIGHT = 720FRAME_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()
發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
国产乱人伦真实精品视频| 欧美视频在线免费看| 国内精品小视频| 精品露脸国产偷人在视频| 精品精品国产国产自在线| 亚洲综合av影视| 青青久久av北条麻妃海外网| 久久夜色精品国产欧美乱| 国内精品久久久久伊人av| 成人久久久久爱| 热久久美女精品天天吊色| 欧美性猛交xxxx免费看久久久| 4k岛国日韩精品**专区| 日韩美女福利视频| 国产在线精品自拍| 成人激情电影一区二区| 尤物99国产成人精品视频| 欧美一级黄色网| 91精品国产91久久久久福利| 国产成人精品午夜| 国产精品日韩在线观看| 国产亚洲欧洲黄色| 国产香蕉97碰碰久久人人| 色噜噜亚洲精品中文字幕| 欧美大胆在线视频| 91色视频在线观看| 色悠悠久久久久| 久久韩国免费视频| 91麻豆国产语对白在线观看| 中文字幕亚洲自拍| 日韩av第一页| 国产成人自拍视频在线观看| 国产免费一区二区三区香蕉精| 欧美高清在线视频观看不卡| 欧美午夜精品伦理| 欧美激情2020午夜免费观看| 亚洲自拍高清视频网站| 久久久91精品| 亚洲国产另类 国产精品国产免费| 久久免费国产精品1| 国产欧美亚洲视频| 黄色精品在线看| 一本一本久久a久久精品综合小说| 日韩精品日韩在线观看| 日韩精品极品视频免费观看| 欧亚精品在线观看| 精品亚洲国产成av人片传媒| 色婷婷久久av| 亚洲国产精品va在线看黑人动漫| 欧美电影电视剧在线观看| 青青a在线精品免费观看| 国产成人精品在线视频| 国产一区二区在线免费视频| 欧美俄罗斯乱妇| 国产精品96久久久久久| 亚洲精品国产精品国自产观看浪潮| 欧美午夜宅男影院在线观看| 久久综合久久美利坚合众国| 欧美午夜精品久久久久久浪潮| 亚洲自拍高清视频网站| 91中文在线视频| 欧美激情一二区| …久久精品99久久香蕉国产| 国产香蕉一区二区三区在线视频| 亚洲精品98久久久久久中文字幕| 国产精品天天狠天天看| 国产精品91免费在线| 亚洲精品成a人在线观看| 久久久久亚洲精品| 久久久久久久国产精品视频| 亚洲成人精品在线| www.欧美精品一二三区| 欧美一级高清免费| 亚洲亚裔videos黑人hd| 国产成人精品久久| 日韩网站免费观看| 91九色国产在线| 亚洲图片在线综合| 欧美性jizz18性欧美| 久久九九有精品国产23| 欧美亚洲另类制服自拍| 欧美日韩亚洲成人| 欧美性猛交xxxxx水多| 精品亚洲国产成av人片传媒| 日本高清不卡的在线| 最近2019中文免费高清视频观看www99| 日韩精品中文字幕在线播放| 国产精品午夜一区二区欲梦| 国产丝袜一区二区三区| 国产精品亚洲一区二区三区| 国产日本欧美在线观看| 亚洲国产日韩欧美在线99| 91精品国产高清久久久久久| 欧美人成在线视频| 欧美日韩人人澡狠狠躁视频| 欧美亚洲在线播放| 日韩精品高清视频| 午夜精品蜜臀一区二区三区免费| 亚洲综合一区二区不卡| 亚洲成人免费网站| 国产精品一区二区三区久久久| 欧美日韩亚洲精品一区二区三区| 韩国三级电影久久久久久| 久久精品在线播放| 欧美成人精品一区二区| 久久久久久久久久久久av| 国产亚洲精品激情久久| 精品女厕一区二区三区| 欧美日韩国产精品一区二区三区四区| 2025国产精品视频| 亚洲黄色有码视频| 91精品国产乱码久久久久久蜜臀| 98精品国产自产在线观看| 日韩av不卡电影| 88国产精品欧美一区二区三区| 国产suv精品一区二区三区88区| 国产精品永久免费在线| 欧美午夜www高清视频| 国内精品久久久久伊人av| 日韩中文字在线| 久久精品电影一区二区| 欧美日韩免费一区| 疯狂做受xxxx高潮欧美日本| 一本色道久久综合亚洲精品小说| 日韩一区av在线| 亚洲美女免费精品视频在线观看| 中文字幕国产日韩| 国产一区二区丝袜高跟鞋图片| 久久福利视频导航| 精品国产1区2区| 91午夜理伦私人影院| 亚洲色图校园春色| 成人福利在线视频| 永久免费看mv网站入口亚洲| 欧美精品制服第一页| 国产精品青草久久久久福利99| 久久久久久网址| 国产一区二区三区在线视频| 久久99国产精品自在自在app| 国产精品福利在线观看网址| 欧洲亚洲女同hd| 日本精品在线视频| 91在线视频精品| 精品色蜜蜜精品视频在线观看| 大伊人狠狠躁夜夜躁av一区| 亚洲精品久久久久久久久久久| 亚洲无av在线中文字幕| 欧美日韩国产第一页| 不卡av电影在线观看| 久久黄色av网站| 久久久99久久精品女同性| 国产精品久久久久免费a∨大胸| 精品国产一区二区三区久久久| 精品伊人久久97| 成人中文字幕在线观看| 国产+人+亚洲| 亚洲黄色www| 久久久欧美一区二区| 中文字幕日韩av综合精品| 在线观看免费高清视频97| 国产精品av网站| 91免费电影网站| 亚洲精品第一国产综合精品| 8x海外华人永久免费日韩内陆视频|