Python tkinter实现图片标注功能(完整代码)
.tkinter
tkinter是Python下面向tk的图形界面接口库,可以方便地进行图形界面设计和交互操作编程。tkinter的优点是简单易用、与Python的结合度好。tkinter在Python3.x下默认集成,不需要额外的安装操作;不足之处为缺少合适的可视化界面设计工具,需要通过代码来完成窗口设计和元素布局。
Pythontkinter实现图片标注代码,代码如下所述:
#!/usr/bin/python
#-*-coding:UTF-8-*-
importos
importsys
ifsys.version_info<(3,0):
importTkinterastk#导入Tkinter库
fromtkFileDialogimportaskopenfilename,asksaveasfilename
else:
importtkinterastk#导入Tkinter库
fromtkinter.filedialogimportaskopenfilename,asksaveasfilename
fromPILimportImage,ImageTk,ImageDraw
fromtimeimportsleep
importnumpyasnp
importcv2ascv
DEF_WIDTH=1080
DEF_HEIGHT=720
IMAGE_HEIGHT=720
FRAME_LEFT_WIDTH=360
#太小的选定区域我们需要丢弃,防止误操作
MINI_RECT_AREA=20
classRawImageEditor:
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=[]
forrinrects:
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)
#MaketopLevelWindowremainontopuntildestroyed,orattributechanges.
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)
forrinself.selPositions:
draw=self.canvas.create_rectangle(r[0],r[1],r[2],r[3],outline='green')
self.lastDraws.append(draw)
#鼠标左键按下的位置
defonLeftButtonDown(event):
self.X.set(event.x)
self.Y.set(event.y)
#开始截图
self.sel=True
#重新绘制已经选择的区域
fordrawinself.lastDraws:
self.canvas.delete(draw)
self.lastDraws=[]
forrinself.selPositions:
draw=self.canvas.create_rectangle(r[0],r[1],r[2],r[3],outline='green')
self.lastDraws.append(draw)
self.canvas.bind('',onLeftButtonDown)
#鼠标左键移动,显示选取的区域
defonLeftButtonMove(event):
ifnotself.sel:
return
try:
#删除刚画完的图形,要不然鼠标移动的时候是黑乎乎的一片矩形
self.canvas.delete(self.lastDraw)
exceptExceptionase:
pass
self.lastDraw=self.canvas.create_rectangle(self.X.get(),self.Y.get(),event.x,event.y,outline='green')
self.canvas.bind('',onLeftButtonMove)
#获取鼠标左键抬起的位置,保存区域截图
defonLeftButtonUp(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()
#鼠标右键按下
defonRightButtonDown(event):
self.sel=False
self.top.destroy()
self.canvas.bind('',onRightButtonDown)
self.canvas.bind('',onLeftButtonUp)
self.canvas.pack(fill=tk.BOTH,expand=tk.YES)
defcalcImageScale(self,image):
w=image.width
h=image.height
self.imageScale=1.0
#计算最小的缩放比例,保证原始宽高比
ifw>self.dispWidthandh>self.dispHeight:
ws=self.dispWidth*1.0/w
hs=self.dispHeight*1.0/h
ifwsself.dispWidthandhself.dispHeight:
self.imageScale=self.dispHeight*1.0/h
defwaitForWindow(self,win):
win.wait_window(self.top)
defselectedPositions(self):
#转换为原始像素位置
realPos=[]
forrinself.selPositions:
realPos.append((r[0]/self.imageScale,r[1]/self.imageScale,r[2]/self.imageScale,r[3]/self.imageScale))
returnrealPos
classMainWin(tk.Tk):
def__init__(self):
ifsys.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()
defscaleDisplayImage(self,image):
w=image.width
h=image.height
self.imageScale=1.0
#计算最小的缩放比例,保证原始宽高比
ifw>self.imageDispWidthandh>self.imageDispHeight:
ws=self.imageDispWidth*1.0/w
hs=self.imageDispHeight*1.0/h
ifwsself.imageDispWidthandhself.imageDispHeight:
self.imageScale=self.imageDispHeight*1.0/h
#图片缩放
returnimage.resize((int(self.imageScale*w),int(self.imageScale*h)))
#打开图片时使用,传值(图)给展示函数
defopenAndDisplayImage(self):
self.rawImagePath=self.selectImageFile()
if''!=self.rawImagePath:
self.rawImage=Image.open(self.rawImagePath)
self.rawImage=self.rawImage.convert('RGBA')
self.drawRawImageDisp()
defdrawListBox(self):
self.l_box.delete(0,tk.END)
foriteminself.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)
defdrawRawImageDisp(self,selItems=[]):
self.dispImage=self.scaleDisplayImage(self.rawImage)
self.dispImage=self.dispImage.convert('RGB')
draw=ImageDraw.Draw(self.dispImage)
foriinrange(len(self.liRect)):
r=self.liRect[i]
ifiinselItems:
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
defdeleteSelectedItemFromListBox(self):
#print(self.l_box.get(self.l_box.curselection()))
idx=self.l_box.curselection()
iflen(idx)>0:
kp=[]
forvinrange(len(self.liRect)):
ifvnotinidx:
kp.append(self.liRect[v])
self.liRect=kp
self.drawListBox()
self.drawRawImageDisp()
#打开图片时使用,获得地址
defselectImageFile(self):
path=tk.StringVar()
file_entry=tk.Entry(self,state='readonly',text=path)
path_=askopenfilename()
path.set(path_)
returnfile_entry.get()
defrawImageLabelClicked(self,event):
ifNone!=self.rawImage:
ifNone==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()
defonRectListboxSelect(self,event):
idx=self.l_box.curselection()
iflen(idx)>0:
self.drawRawImageDisp(idx)
defdrawTransImageDisp(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
defdoTransRawImage(self):
self.transRawImage=Image.new('L',(self.rawImage.width,self.rawImage.height))
forrinself.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()
forjinrange(im.height):
foriinrange(im.width):
hv=v[j,i]
ifhv>',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("",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()
总结
以上所述是小编给大家介绍的Pythontkinter实现图片标注功能,希望对大家有所帮助,如果大家有任何疑问欢迎给我留言,小编会及时回复大家的!
声明:本文内容来源于网络,版权归原作者所有,内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎发送邮件至:czq8825#qq.com(发邮件时,请将#更换为@)进行举报,并提供相关证据,一经查实,本站将立刻删除涉嫌侵权内容。