这两天看opencv-python的HSV色彩空间,在写程序时发现用HSV来提取图像区域是件令人恶心的麻烦事。拿阈值分割做个对比,阈值最多也就一两个参数需要调整;但是HSV需要对三个通道调整上下限,也就是起码有6个参数。于是乎,就一时兴起决定做个小程序,把参数都做成滑动块,这样自然方便许多。一开始,想直接用opencv来做,但是百度了一下它没有很好的GUI支持。所以决定还是用python自带的tkinter来设计UI。
UI设计最重要的还是布局,之前学CSS就差点搞得我稀里糊涂。tkinter有三种布局方式,网上说grid是常用的,然而我用了一会,感觉这种excel类型的方法有点死板,唯一的好处是不用计算坐标,所以我布局滑块时就使用了grid,其他一律使用place。
我要的程序界面得划出两块地方显示图片,tkinter没有专用的控件,不过可以利用Lable的image属性来设置背景图片。并且,image属性对图片数据流的格式有所要求,得用PIL模块里的Image和ImageTk两个工具先对图片进行处理。在处理时,我还得对它大小限制一下,要不然不同图片导进来,布局就乱了。接下来就导入滑块和按钮,调一下布局罢了,没啥难度,就是审美得纠结一下。所有控件都导入以后,为了看起来不那么单调,本来想是给整个窗口加一个高雅的背景图的,但是当我把背景改成暗灰色以后,奇迹就发生了——这东西居然还有border。暗灰色背景,配上亮白色边框,别有一番味道啊!!
最后,在给按钮和字调下颜色,然后把窗口的默认ico图标换一下,这还是跟之前的图片导入类似,所以很快就解决了。剩下的事情就是事件控制了,因为按钮挺多的,功能也越想越复杂,因此,这部分代码就留给以后写,文末贴个程序界面截图和实现代码。
1 import os 2 from tkinter import * 3 from PIL import Image, ImageTk 4 5 os.chdir('D:/programe/matlab/img') 6 7 8 9 10 class GUI(): 11 def __init__(self,window): 12 self.window = window 13 #title 14 self.window.title("HsvMaster") 15 #siz=800*600,position=(500,200) 16 self.window.geometry('1000x600+500+200') 17 self.window["bg"] = "DimGray" 18 # icon 19 self.icon = ImageTk.PhotoImage(file='hsv_icon.ico') 20 self.window.call('wm','iconphoto',self.window._w,self.icon) 21 22 23 def create_widgets(self): 24 25 #scale 26 self.hmin_label = Label(self.window,text='色调下限',fg='WhiteSmoke',bg="DimGray") 27 self.hmin_scale = Scale(self.window,orient=HORIZONTAL,from_=0,to=255,resolution=1,tickinterval=50,length=200,width=7,fg='WhiteSmoke',bg="DimGray") 28 self.hmax_label = Label(self.window,text='色调上限',fg='WhiteSmoke',bg="DimGray") 29 self.hmax_scale = Scale(self.window,orient=HORIZONTAL,from_=0,to=255,resolution=1,tickinterval=50,length=200,width=7,fg='WhiteSmoke',bg="DimGray") 30 self.smin_label = Label(self.window,text='饱和度下限',fg='WhiteSmoke',bg="DimGray") 31 self.smin_scale = Scale(self.window,orient=HORIZONTAL,from_=0,to=255,resolution=1,tickinterval=50,length=200,width=7,fg='WhiteSmoke',bg="DimGray") 32 self.smax_label = Label(self.window,text='饱和度上限',fg='WhiteSmoke',bg="DimGray") 33 self.smax_scale = Scale(self.window,orient=HORIZONTAL,from_=0,to=255,resolution=1,tickinterval=50,length=200,width=7,fg='WhiteSmoke',bg="DimGray") 34 self.vmin_label = Label(self.window,text='明度下限',fg='WhiteSmoke',bg="DimGray") 35 self.vmin_scale = Scale(self.window,orient=HORIZONTAL,from_=0,to=255,resolution=1,tickinterval=50,length=200,width=7,fg='WhiteSmoke',bg="DimGray") 36 self.vmax_label = Label(self.window,text='明度上限',fg='WhiteSmoke',bg="DimGray") 37 self.vmax_scale = Scale(self.window,orient=HORIZONTAL,from_=0,to=255,resolution=1,tickinterval=50,length=200,width=7,fg='WhiteSmoke',bg="DimGray") 38 39 # scale position 40 self.hmin_label.grid(row=0, column=0,padx=15) 41 self.hmin_scale.grid(row=0,column=1,pady=2) 42 self.hmax_label.grid(row=1, column=0) 43 self.hmax_scale.grid(row=1,column=1,pady=2) 44 self.smin_label.grid(row=2, column=0) 45 self.smin_scale.grid(row=2,column=1,pady=2) 46 self.smax_label.grid(row=3, column=0) 47 self.smax_scale.grid(row=3,column=1,pady=2) 48 self.vmin_label.grid(row=4, column=0) 49 self.vmin_scale.grid(row=4,column=1,pady=2) 50 self.vmax_label.grid(row=5, column=0) 51 self.vmax_scale.grid(row=5,column=1,pady=2) 52 53 #img 54 self.img = Image.open('man.jpg') 55 self.img = self.img.resize((300, 300)) 56 self.img = ImageTk.PhotoImage(self.img) 57 58 self.orign = Label(self.window,image=self.img,width=300,height=300) 59 self.work = Label(self.window,image=self.img,width=300,height=300) 60 #img position 61 self.orign.place(x=320, y=30) 62 self.work.place(x=650,y=30) 63 64 #Button 65 self.import_button = Button(self.window, text='导入原图',font=('隶书',45), height=2, width=9,fg='WhiteSmoke',bg="BurlyWood") 66 self.clear_button = Button(self.window, text='删除所有',font=('隶书',12), height=2, width=15,fg='WhiteSmoke',bg="BurlyWood") 67 self.delete_button = Button(self.window, text='删除当前',font=('隶书',12), height=2, width=15,fg='WhiteSmoke',bg="BurlyWood") 68 self.switch_button = Button(self.window,text='区域切换',font=('隶书',12), height=2, width=15,fg='WhiteSmoke',bg="BurlyWood") 69 self.save_button = Button(self.window, text='区域暂存',font=('隶书',12), height=2, width=15,fg='WhiteSmoke',bg="BurlyWood") 70 self.merge_button = Button(self.window, text='合并区域',font=('隶书',12), height=2, width=15,fg='WhiteSmoke',bg="BurlyWood") 71 self.picture_button = Button(self.window, text='生成图片',font=('隶书',12), height=2, width=15,fg='WhiteSmoke',bg="BurlyWood") 72 73 #button position 74 self.import_button.place(x=18,y=400) 75 self.clear_button.place(x=370,y=420) 76 self.delete_button.place(x=570,y=420) 77 self.switch_button.place(x=770,y=420) 78 self.save_button.place(x=370,y=500) 79 self.merge_button.place(x=570,y=500) 80 self.picture_button.place(x=770,y=500) 81 82 83 84 85 def gui_start(): 86 87 my_window = Tk() 88 my_gui = GUI(my_window) 89 my_gui.create_widgets() 90 my_window.mainloop() 91 92 93 gui_start()