'''
@Author: your name
@Date: 2020-02-13 13:30:07
@LastEditTime: 2020-02-20 16:17:34
@LastEditors: Please set LastEditors
@Description: 高斯平滑展示,边缘检测展示,
能够通过按键时时控制高斯平滑,高斯选择改变后改变高斯图和边缘检测图
边缘检测通过右侧两个滑条更改检测阈值
加入圆形检测
'''
import tkinter
import os
import cv2
import numpy
from PIL import Image,ImageTk
from tkinter import filedialog
# global start 全局变量定义初始化开始
#全局变量参数
carmela_hight = 200
carmela_width = 200
#Tkinter元素句柄参数
Source_Img_Label = None
Gray_Img_Label = None
Canny_Img_Label = None
Circles_Img_Label = None
Gaussian_Button = None
Counters_Button = None
Threshold_Min_Scale = None
Threshold_Max_Scale = None
Hough_Parm1_Scale = None
Hough_Parm2_Scale = None
#开关量参数
Gaussian_Enable = False
Counters_Enable = False
#获取当前文件路径
py_path=os.path.abspath(os.path.dirname(__file__))
#global end 全局变量定义初始化结束
#创建窗口对象以及窗口使用变量
root = tkinter.Tk()
Img_Path_String = tkinter.StringVar()
Img_Path_String.set('尚未选择文件')
Dp_Value = tkinter.StringVar()
Dp_Value.set('1')
#窗口使用变量初始化开始
screen_Width = root.winfo_screenwidth()
screen_height = root.winfo_screenheight()
width = screen_Width//2
height = screen_height//2+50
root_win_par = '%dx%d+%d+%d'% (width, height, (screen_Width-width)/2, (screen_height-height)/2)
#窗口使用变量初始化结束
def resizeImage(image, width, height, inter=cv2.INTER_AREA):
size = image.shape
h, w = size[0], size[1]
#长边缩放为min_side
min_side = min(width,height)
scale = max(w, h) / float(min_side)
new_w, new_h = int(w/scale), int(h/scale)
resize_img = cv2.resize(image, (new_w, new_h))
# 填充至min_side * min_side
if new_w % 2 != 0 and new_h % 2 == 0:
top, bottom, left, right = (min_side-new_h)/2, (min_side-new_h)/2, (min_side-new_w)/2 + 1, (min_side-new_w)/2
elif new_h % 2 != 0 and new_w % 2 == 0:
top, bottom, left, right = (min_side-new_h)/2 + 1, (min_side-new_h)/2, (min_side-new_w)/2, (min_side-new_w)/2
elif new_h % 2 == 0 and new_w % 2 == 0:
top, bottom, left, right = (min_side-new_h)/2, (min_side-new_h)/2, (min_side-new_w)/2, (min_side-new_w)/2
else:
top, bottom, left, right = (min_side-new_h)/2 + 1, (min_side-new_h)/2, (min_side-new_w)/2 + 1, (min_side-new_w)/2
pad_img = cv2.copyMakeBorder(resize_img, int(top), int(bottom), int(left), int(right), cv2.BORDER_CONSTANT, value=[0,0,0]) #从图像边界向上,下,左,右扩的像素数目
return pad_img
#窗口使用变量初始化结束
#图片获取通过图片路径显示标签,返回值为cv2.imread 缩放后
def ImageGet():
#读取图片参数,通过opencv
Source_Img = cv2.imread(Img_Path_String.get())
if (Source_Img is None):
return None
else:
pass
#图片大小更改
#Source_Img = resizeImage(image=Source_Img,width=carmela_width,height=carmela_hight)
return Source_Img
#原图片显示刷新
def SourceDisplay(Source_Img):
global Source_Img_Label
#RGB图像转换并且显示
Temp_Img = cv2.cvtColor(Source_Img,cv2.COLOR_BGR2RGB)
Temp_Img = resizeImage(image=Temp_Img,width=carmela_width,height=carmela_hight)
Temp_Img = Image.fromarray(Temp_Img)
Temp_Img = ImageTk.PhotoImage(Temp_Img)
if(Source_Img_Label is None):#检测是否是第一次输入图片,是的话创建RGB图片显示Label
Source_Img_Label = tkinter.Label(root,bg='red',image=Temp_Img,width=carmela_width,height=carmela_hight)
Source_Img_Label.image = Temp_Img
Source_Img_Label.place(x=10*1+carmela_width*0,y=40+10*0+carmela_hight*0)
else:#不是的话更改Label中的图片
Source_Img_Label.configure(image=Temp_Img)
Source_Img_Label.image = Temp_Img
#灰度图片显示刷新
def GrayDisply(Source_Img):
global Gray_Img_Label
global Gaussian_Enable
#灰度图片转换
Temp_Img = cv2.cvtColor(Source_Img,cv2.COLOR_BGR2GRAY)
if Gaussian_Enable:
Temp_Img = cv2.GaussianBlur(Temp_Img, (5,5), 0, 0, cv2.BORDER_DEFAULT)#高斯处理
Temp_Img = resizeImage(image=Temp_Img,width=carmela_width,height=carmela_hight)
Temp_Img = Image.fromarray(Temp_Img)
Temp_Img = ImageTk.PhotoImage(Temp_Img)
if (Gray_Img_Label is None):#检测是否是第一次输入图片,是的话创建GRAY图片显示Label
Gray_Img_Label = tkinter.Label(root,bg='red',image=Temp_Img,width=carmela_width,height=carmela_hight)
Gray_Img_Label.image = Temp_Img
Gray_Img_Label.place(x=10*2+carmela_width*1,y=40+10*0+carmela_hight*0)
else:#不是的话更改Label中的图片
Gray_Img_Label.configure(image=Temp_Img)
Gray_Img_Label.image = Temp_Img
#边缘函数显示刷新
def CannyDisply(Source_Img,change = True):
global Canny_Img_Label,Threshold_Min_Scale,Threshold_Max_Scale
global Gaussian_Enable,Counters_Enable
#通过灰度图片边缘提取
if change:
Temp_Img = cv2.cvtColor(Source_Img,cv2.COLOR_BGR2GRAY)
else:
Temp_Img = Source_Img
if Gaussian_Enable:
Temp_Img = cv2.GaussianBlur(Temp_Img, (5,5), 0, 0, cv2.BORDER_DEFAULT)#高斯处理
Temp_Img = cv2.Canny(Temp_Img, Threshold_Min_Scale.get(), Threshold_Max_Scale.get())
if Counters_Enable:
contours, hierarchy = cv2.findContours(Temp_Img,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)
Temp_Img = ImageGet()
cv2.drawContours(Temp_Img,contours,-1,(255,0,0),3)
for i in contours:
ShapeAnalysis(i,Temp_Img)
cv2.imshow("",Temp_Img)#调试测试
print(hierarchy)#调试测试
Temp_Img = resizeImage(image=Temp_Img,width=carmela_width,height=carmela_hight)
Temp_Img = Image.fromarray(Temp_Img)
Temp_Img = ImageTk.PhotoImage(Temp_Img)
if (Canny_Img_Label is None):#检测是否是第一次输入图片,是的话创建GRAY图片显示Label
Canny_Img_Label = tkinter.Label(root,bg='red',image=Temp_Img,width=carmela_width,height=carmela_hight)
Canny_Img_Label.image = Temp_Img
Canny_Img_Label.place(x=10*3+carmela_width*2,y=40+10*0+carmela_hight*0)
else:#不是的话更改Label中的图片
Canny_Img_Label.configure(image=Temp_Img)
Canny_Img_Label.image = Temp_Img
#寻找圆显示刷新
def CircleDisplay(Source_Img):
global Circles_Img_Label
#
Temp_Img = cv2.cvtColor(Source_Img,cv2.COLOR_BGR2GRAY)
if Gaussian_Enable:
Temp_Img = cv2.GaussianBlur(Temp_Img, (5,5), 0, 0, cv2.BORDER_DEFAULT)#高斯处理
#Temp_Img = cv2.Canny(Temp_Img, Threshold_Min_Scale.get(), Threshold_Max_Scale.get())
CannyDisply(Temp_Img,change=False)
Temp_Img,Circles = FindCircles(Temp_Img)
#
Temp_Img = cv2.cvtColor(Source_Img,cv2.COLOR_BGR2RGB)
max_index = 0
max_r = 0
if Circles is None: #显示搜索到的圆的信息
pass
else:
for index,i in enumerate(Circles[0,:]):
# draw the outer circle
cv2.circle(Temp_Img,(i[0],i[1]),i[2],(255,0,0),1)
# draw the center of the circle
cv2.circle(Temp_Img,(i[0],i[1]),2,(255,0,0),3)
cv2.putText( Temp_Img,("R=%d"%( i[2] )),(i[0],i[1]-i[2]),cv2.FONT_HERSHEY_COMPLEX,0.5,(0,255,0) )
if max_r < i[2]:
max_r = i[2]
max_index = index
#print("%d circles is max:r=%d"%(max_index,max_r))#调试测试
#FindLines(Temp_Img)
Temp_Img = resizeImage(image=Temp_Img,width=carmela_width,height=carmela_hight)
Temp_Img = Image.fromarray(Temp_Img)
Temp_Img = ImageTk.PhotoImage(Temp_Img)
if (Circles_Img_Label is None):#检测是否是第一次输入图片,是的话创建GRAY图片显示Label
Circles_Img_Label = tkinter.Label(root,bg='red',image=Temp_Img,width=carmela_width,height=carmela_hight)
Circles_Img_Label.image = Temp_Img
Circles_Img_Label.place(x=10*1+carmela_width*0,y=40+10*1+carmela_hight*1)
else:#不是的话更改Label中的图片
Circles_Img_Label.configure(image=Temp_Img)
Circles_Img_Label.image = Temp_Img
#轮廓识别
def ShapeAnalysis(contours,img):
# 轮廓逼近
epsilon = 0.01 * cv2.arcLength(contours, True)
approx = cv2.approxPolyDP(contours, epsilon, True)
# 分析几何形状
corners = len(approx)
shape_type = ""
if corners == 3:
shape_type = "三角形"
elif corners == 4:
shape_type = "矩形"
elif 4 < corners < 10:
shape_type = "多边形"
elif 10 < corners:
shape_type = "圆"
else:
shape_type = "未知"
# 求解中心位置
mm = cv2.moments(contours)
cx = int(mm['m10'] / mm['m00'])
cy = int(mm['m01'] / mm['m00'])
cv2.circle(img, (cx, cy), 3, (0, 0, 255), -1)
# 计算面积与周长
p = cv2.arcLength(contours, True)
area = cv2.contourArea(contours)
print("周长: %.3f, 面积: %.3f 形状: %s "% (p, area, shape_type))
#寻找图片中的圆返回值1为图片,返回值2为圆的
def FindCircles(Source_Img):
dp = int(Dp_Value.get(),base = 10)
circles = cv2.HoughCircles(Source_Img,cv2.HOUGH_GRADIENT,dp,20,param1=Hough_Parm1_Scale.get(),param2=Hough_Parm2_Scale.get(),minRadius=0,maxRadius=0)
if not(circles is None):
circles = numpy.around(circles)
circles = numpy.uint16(circles)
return Source_Img,circles
#图片选择按键绑定函数
def AskPicture():
#图片路径获取
Picture_Path = filedialog.askopenfilename()
#显示图片路径,在Img_Path_Text中
Img_Path_String.set(Picture_Path)
Source_Img = ImageGet()
#检测是否输入确实为图片
if (Source_Img is None):
Img_Path_String.set('文件选择错误')
return
else:
#cv2.imshow('',Source_Img)#调试测试
#原图片显示
SourceDisplay(Source_Img)
#灰度图片显示刷新
GrayDisply(Source_Img)
#边缘函数显示刷新
CannyDisply(Source_Img)
CircleDisplay(Source_Img)
#阈值绑定函数,阈值更改时调用此函数刷新第三幅图片
def ThresholdChange(self):
global Source_Img_Label,Gray_Img_Label,Canny_Img_Label,Threshold_Min_Scale,Threshold_Max_Scale
#图片获取
Source_Img = ImageGet()
#检测是否输入确实为图片
if (Source_Img is None):
return
else:
CannyDisply(Source_Img)
CircleDisplay(Source_Img)
#高斯处理案件绑定函数
def GaussianChoice():
global Gaussian_Enable,Gaussian_Button
if Gaussian_Enable:
Gaussian_Enable = False
Gaussian_Button['text']='高斯关'
else:
Gaussian_Enable = True
Gaussian_Button['text']='高斯开'
#刷新图片
Source_Img = ImageGet()
#检测是否输入确实为图片
if (Source_Img is None):
return
else:
#灰度图片显示刷新
GrayDisply(Source_Img)
#边缘函数显示刷新
CannyDisply(Source_Img)
#轮廓处理案件绑定函数
def CountersChoice():
global Counters_Enable,Counters_Button
if Counters_Enable:
Counters_Enable = False
Counters_Button['text']='轮廓关'
else:
Counters_Enable = True
Counters_Button['text']='轮廓开'
#刷新图片
Source_Img = ImageGet()
#检测是否输入确实为图片
if (Source_Img is None):
return
else:
#灰度图片显示刷新
GrayDisply(Source_Img)
#边缘函数显示刷新
CannyDisply(Source_Img)
#dp更改函数
def DpValueIncrease():
temp = int(Dp_Value.get(),base = 10)
temp = temp + 1
if temp > 10:
temp = 10
Dp_Value.set(str(temp))
def DpValueReduce():
temp = int(Dp_Value.get(),base = 10)
temp = temp - 1
if temp < 1:
temp = 1
Dp_Value.set(str(temp))
def HoughParamChange(self):
#图片获取
Source_Img = ImageGet()
#检测是否输入确实为图片
if (Source_Img is None):
pass
else:
CircleDisplay(Source_Img)
return
def HoughDpChange():
#图片获取
Source_Img = ImageGet()
#检测是否输入确实为图片
if (Source_Img is None):
pass
else:
CircleDisplay(Source_Img)
return True
#窗口大小及位置设置
root.geometry(root_win_par)
#设置窗口是否可变长、宽,True:可变,False:不可变
root.resizable(width=False, height=True)
root.title('设计')#窗口标题设置
root.iconbitmap(py_path+'\\ico.ico')#窗口图标设置
#显示路径输入框初始化及放置(禁止写入)
tkinter.Entry(root,textvariable=Img_Path_String,borderwidth=1,state=tkinter.DISABLED).place(x=10,y=10,width=width-20-40,height=20)
#路径选取按钮初始化设置
tkinter.Button(root,text='选择',command=AskPicture).place(x=width-45,y=10,width=40,height=20)
#高斯处理按钮初始化设置
Gaussian_Button = tkinter.Button(root,text='高斯关',command=GaussianChoice)
Gaussian_Button.place(x=width-45,y=40,width=40,height=20)
#轮廓处理按钮初始化设置
Counters_Button = tkinter.Button(root,text='轮廓关',command=CountersChoice)
Counters_Button.place(x=width-90,y=40,width=40,height=20)
#阈值滚动条
Threshold_Min_Scale = tkinter.Scale(root,from_=0,to=500,orient=tkinter.VERTICAL,length=height-200,width = 10,command=ThresholdChange)
Threshold_Min_Scale.place(x=width-100,y=90)
tkinter.Label(root,text='Min').place(x=width-100+20,y=60)
Threshold_Min_Scale.set(100)
Threshold_Max_Scale = tkinter.Scale(root,from_=0,to=500,orient=tkinter.VERTICAL,length=height-200,width = 10,command=ThresholdChange)
Threshold_Max_Scale.place(x=width-50,y=90)
tkinter.Label(root,text='Max').place(x=width-50+20,y=60)
Threshold_Max_Scale.set(200)
#霍夫变换参数设置
tkinter.Label(root,text="dp:").place(x=width-100,y=height-200+100)
tkinter.Entry(root,textvariable = Dp_Value ,borderwidth=1,state=tkinter.DISABLED,validate='key',validatecommand= HoughDpChange ).place(x=width-100+30,y=height-200+100,width=20,height=20)
tkinter.Button(root,text = '+',command = DpValueIncrease).place(x=width-100+55,y=height-200+100,width=20,height=20)
tkinter.Button(root,text = '-',command = DpValueReduce).place(x=width-100+78,y=height-200+100,width=20,height=20)
Hough_Parm1_Scale = tkinter.Scale(root,from_=1,to=500,orient=tkinter.HORIZONTAL,length = 100,width = 10,command = HoughParamChange)
Hough_Parm1_Scale.place(x=width-100,y=height-200+120)
Hough_Parm1_Scale.set(50)
Hough_Parm2_Scale = tkinter.Scale(root,from_=1,to=100,orient=tkinter.HORIZONTAL,length = 100,width = 10,command = HoughParamChange)
Hough_Parm2_Scale.place(x=width-100,y=height-200+160)
Hough_Parm2_Scale.set(30)
#窗口主循环
root.mainloop()