From adf207d918f5976e56b0231a6d7c5281db636898 Mon Sep 17 00:00:00 2001 From: Xi Xu Date: Sun, 16 Jun 2024 21:32:22 +0800 Subject: [PATCH] Create 8 --- 8/1.py | 377 +- 8/2/__pycache__/Fourier.cpython-312.pyc | Bin 0 -> 10039 bytes 8/2/__pycache__/Histogram.cpython-312.pyc | Bin 0 -> 6940 bytes .../PointProcessing.cpython-312.pyc | Bin 0 -> 5759 bytes 8/2/__pycache__/Restore.cpython-312.pyc | Bin 0 -> 14625 bytes 8/2/__pycache__/SpatialFilter.cpython-312.pyc | Bin 0 -> 25908 bytes 8/2/__pycache__/description.cpython-312.pyc | Bin 0 -> 14485 bytes 8/2/__pycache__/geometric.cpython-312.pyc | Bin 0 -> 8022 bytes 8/2/__pycache__/globalObject.cpython-312.pyc | Bin 0 -> 647 bytes 8/2/__pycache__/morphology.cpython-312.pyc | Bin 0 -> 4199 bytes 8/2/__pycache__/segmentation.cpython-312.pyc | Bin 0 -> 9035 bytes .../spatialRestore.cpython-312.pyc | Bin 0 -> 32815 bytes .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 174 bytes .../__pycache__/predict.cpython-312.pyc | Bin 0 -> 32466 bytes 8/2/main.py | 3391 ++++++++++------- 15 files changed, 2415 insertions(+), 1353 deletions(-) create mode 100644 8/2/__pycache__/Fourier.cpython-312.pyc create mode 100644 8/2/__pycache__/Histogram.cpython-312.pyc create mode 100644 8/2/__pycache__/PointProcessing.cpython-312.pyc create mode 100644 8/2/__pycache__/Restore.cpython-312.pyc create mode 100644 8/2/__pycache__/SpatialFilter.cpython-312.pyc create mode 100644 8/2/__pycache__/description.cpython-312.pyc create mode 100644 8/2/__pycache__/geometric.cpython-312.pyc create mode 100644 8/2/__pycache__/globalObject.cpython-312.pyc create mode 100644 8/2/__pycache__/morphology.cpython-312.pyc create mode 100644 8/2/__pycache__/segmentation.cpython-312.pyc create mode 100644 8/2/__pycache__/spatialRestore.cpython-312.pyc create mode 100644 8/2/carLicense/__pycache__/__init__.cpython-312.pyc create mode 100644 8/2/carLicense/__pycache__/predict.cpython-312.pyc diff --git a/8/1.py b/8/1.py index 482621a..84b15ee 100644 --- a/8/1.py +++ b/8/1.py @@ -1,4 +1,3 @@ -# from sklearn.externals import joblib from sklearn import datasets from skimage.feature import hog from sklearn.svm import LinearSVC @@ -6,98 +5,382 @@ import numpy as np import os, math, cv2, struct import matplotlib.pyplot as plt -plt.rcParams["font.sans-serif"] = ["SimHei"] # 用来正常显示中文标签 -plt.rcParams["axes.unicode_minus"] = False # 用来正常显示负 +plt.rcParams["font.sans-serif"] = ["Times New Roman"] +plt.rcParams["axes.unicode_minus"] = False -# 读取MNIST数据 -# path是数据文件夹的路径,labelfile是图像标注文件名,datafile是数据文件名 -def load_mnist(path, labelfile, datafile): # 读取数据函数 - # Load MNIST data from path +def load_mnist(path, labelfile, datafile): labels_path = os.path.join(path, labelfile) images_path = os.path.join(path, datafile) - with open(labels_path, "rb") as lbpath: magic, n = struct.unpack(">II", lbpath.read(8)) labels = np.fromfile(lbpath, dtype=np.uint8) - with open(images_path, "rb") as imgpath: magic, num, rows, cols = struct.unpack(">IIII", imgpath.read(16)) images = np.fromfile(imgpath, dtype=np.uint8).reshape(len(labels), 784) return images, labels -# 读取训练集 features是图片数据,labels是对应的标注 features, labels = load_mnist( - r"mnist", "train-labels-idx1-ubyte", "train-images-idx3-ubyte" + "mnist", "train-labels-idx1-ubyte", "train-images-idx3-ubyte" ) print(features.shape, labels.shape) -# print(type(features),type(labels)) - -# 显示训练数据 features = features[0:6000, :] labels = labels[0:6000] print("训练集行数: %d, 列数: %d" % (features.shape[0], features.shape[1])) -x = np.array(features[0, :]) # 提取第一行数据 +x = np.array(features[0, :]) x = x.reshape([28, 28]) plt.figure(figsize=(12, 4)) -# plt.subplots_adjust(top=0,bottom=-1) plt.imshow(x, cmap="Greys") plt.show() - -# # 读取测试集 testfeatures, testlabels = load_mnist( - r"mnist", "t10k-labels-idx1-ubyte", "t10k-images-idx3-ubyte" + "mnist", "t10k-labels-idx1-ubyte", "t10k-images-idx3-ubyte" ) print("测试集行数: %d, 列数: %d" % (testfeatures.shape[0], testfeatures.shape[1])) - -# 提取训练集HOG特征 list_hog_fd = [] for feature in features: fd = hog( - feature.reshape((28, 28)), # hog 特征 + feature.reshape((28, 28)), orientations=9, pixels_per_cell=(14, 14), cells_per_block=(1, 1), visualize=False, ) list_hog_fd.append(fd) -hog_features = np.array(list_hog_fd, "float64") # 训练集的HOG特征 +hog_features = np.array(list_hog_fd, "float64") print(hog_features.shape) -# 提取测试集HOG特征 list_hog_fd = [] for feature in testfeatures: fd = hog( - feature.reshape((28, 28)), # hog 特征 + feature.reshape((28, 28)), orientations=9, pixels_per_cell=(14, 14), cells_per_block=(1, 1), visualize=False, ) list_hog_fd.append(fd) -hog_testfeatures = np.array(list_hog_fd, "float64") # 测试集的HOG +hog_testfeatures = np.array(list_hog_fd, "float64") print(hog_testfeatures.shape) +from sklearn.neighbors import KNeighborsClassifier + +k = 5 +kNN_model = KNeighborsClassifier(n_neighbors=k) +kNN_model.fit(hog_features, labels) +testnumber = kNN_model.predict(hog_testfeatures) +KNNscore = kNN_model.score(hog_testfeatures, testlabels) +print("测试集第一个图像数字是:", testnumber[0]) +print("分类精度:", KNNscore) + +g_mapping = [ + 0, + 1, + 2, + 3, + 4, + 58, + 5, + 6, + 7, + 58, + 58, + 58, + 8, + 58, + 9, + 10, + 11, + 58, + 58, + 58, + 58, + 58, + 58, + 58, + 12, + 58, + 58, + 58, + 13, + 58, + 14, + 15, + 16, + 58, + 58, + 58, + 58, + 58, + 58, + 58, + 58, + 58, + 58, + 58, + 58, + 58, + 58, + 58, + 17, + 58, + 58, + 58, + 58, + 58, + 58, + 58, + 18, + 58, + 58, + 58, + 19, + 58, + 20, + 21, + 22, + 58, + 58, + 58, + 58, + 58, + 58, + 58, + 58, + 58, + 58, + 58, + 58, + 58, + 58, + 58, + 58, + 58, + 58, + 58, + 58, + 58, + 58, + 58, + 58, + 58, + 58, + 58, + 58, + 58, + 58, + 58, + 23, + 58, + 58, + 58, + 58, + 58, + 58, + 58, + 58, + 58, + 58, + 58, + 58, + 58, + 58, + 58, + 24, + 58, + 58, + 58, + 58, + 58, + 58, + 58, + 25, + 58, + 58, + 58, + 26, + 58, + 27, + 28, + 29, + 30, + 58, + 31, + 58, + 58, + 58, + 32, + 58, + 58, + 58, + 58, + 58, + 58, + 58, + 33, + 58, + 58, + 58, + 58, + 58, + 58, + 58, + 58, + 58, + 58, + 58, + 58, + 58, + 58, + 58, + 34, + 58, + 58, + 58, + 58, + 58, + 58, + 58, + 58, + 58, + 58, + 58, + 58, + 58, + 58, + 58, + 58, + 58, + 58, + 58, + 58, + 58, + 58, + 58, + 58, + 58, + 58, + 58, + 58, + 58, + 58, + 58, + 35, + 36, + 37, + 58, + 38, + 58, + 58, + 58, + 39, + 58, + 58, + 58, + 58, + 58, + 58, + 58, + 40, + 58, + 58, + 58, + 58, + 58, + 58, + 58, + 58, + 58, + 58, + 58, + 58, + 58, + 58, + 58, + 41, + 42, + 43, + 58, + 44, + 58, + 58, + 58, + 45, + 58, + 58, + 58, + 58, + 58, + 58, + 58, + 46, + 47, + 48, + 58, + 49, + 58, + 58, + 58, + 50, + 51, + 52, + 58, + 53, + 54, + 55, + 56, + 57, +] + + +def LBP(I, radius=2, count=8): + dh = np.round([radius * math.sin(i * 2 * math.pi / count) for i in range(count)]) + dw = np.round([radius * math.cos(i * 2 * math.pi / count) for i in range(count)]) + I = I.reshape(28, 28) + height, width = I.shape + lbp = np.zeros(I.shape, dtype=int) + I1 = np.pad(I, radius, "edge") + for k in range(count): + h, w = int(radius + dh[k]), int(radius + dw[k]) + lbp += (I > I1[h : h + height, w : w + width]) << k + return lbp + + +def calLbpHistogram(lbp, hCount=7, wCount=5, maxLbpValue=255): + height, width = lbp.shape + res = np.zeros((hCount * wCount, max(g_mapping) + 1), dtype=float) + assert maxLbpValue + 1 == len(g_mapping) + + for h in range(hCount): + for w in range(wCount): + blk = lbp[ + height * h // hCount : height * (h + 1) // hCount, + width * w // wCount : width * (w + 1) // wCount, + ] + hist1 = np.bincount(blk.ravel(), minlength=maxLbpValue) + hist = res[h * wCount + w, :] + for v, k in zip(hist1, g_mapping): + hist[k] += v + hist /= hist.sum() + feature = res.reshape(res.shape[0] * res.shape[1], 1) + return res + + +lbp_features = np.array([calLbpHistogram(LBP(d)).ravel() for d in features]) +lbp_testfeatures = np.array([calLbpHistogram(LBP(d)).ravel() for d in testfeatures]) -# 使用SVM支持向量机分类器进行分类 from sklearn.svm import LinearSVC -# 建立并训练SVM模型 -svm_model = LinearSVC() # 建立SVM模型 -svm_model.fit(hog_features, labels) # 训练 -# joblib.dump(clf, "digits_cls.pkl", compress=3) # 模型保存 +svm_model = LinearSVC(dual=False) +svm_model.fit(hog_features, labels) -# #测试SVM模型 -# svm_model = joblib.load("digits_cls.pkl") -test_est = svm_model.predict(hog_testfeatures) # 测试单个图像,输出结果为图像所属类别 +test_est = svm_model.predict(hog_testfeatures) SVMscore = svm_model.score(hog_testfeatures, testlabels) print("测试集第一个图像数字是:", testlabels[0]) print("HOG+SVM分类精度:", SVMscore) - import sklearn.metrics as metrics print("--------混淆矩阵-----------") @@ -105,6 +388,28 @@ print( metrics.confusion_matrix( testlabels, test_est, labels=[0, 1, 2, 3, 4, 5, 6, 7, 8, 9] ) -) # 混淆矩阵 +) print("--------分类评估报告-----------") print(metrics.classification_report(testlabels, test_est)) + +print(testlabels.shape) + +from sklearn.svm import LinearSVC +from sklearn.ensemble import RandomForestClassifier + +rf_model = RandomForestClassifier() +rf_model.fit(hog_features, labels) +testnumber = rf_model.predict(hog_testfeatures) +rf_score = rf_model.score(hog_testfeatures, testlabels) +print("第一个图像数字是:", testnumber[0]) +print("分类精度:", rf_score) + + +from sklearn.linear_model import LogisticRegression + +lr_model = LogisticRegression(max_iter=1000) +lr_model.fit(hog_features, labels) +testnumber = lr_model.predict(hog_testfeatures) +lr_score = lr_model.score(hog_testfeatures, testlabels) +print("第一个图像数字是:", testnumber[0]) +print("分类精度:", lr_score) diff --git a/8/2/__pycache__/Fourier.cpython-312.pyc b/8/2/__pycache__/Fourier.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7c2be2921900ec216daff5642ce2f7b97094c5f8 GIT binary patch literal 10039 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 BT#f($ literal 0 HcmV?d00001 diff --git a/8/2/__pycache__/Histogram.cpython-312.pyc b/8/2/__pycache__/Histogram.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5f17e701e62df510c02b143533c621e927704718 GIT binary patch literal 6940 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 U<(GgWG^fS~JpF literal 0 HcmV?d00001 diff --git a/8/2/__pycache__/SpatialFilter.cpython-312.pyc b/8/2/__pycache__/SpatialFilter.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4faf49627a901d1cd00308fb50f9e554a5344ccc GIT binary patch literal 25908 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 S*>$z+8&BP<(iw{wTmK(<>(S}} literal 0 HcmV?d00001 diff --git a/8/2/__pycache__/description.cpython-312.pyc b/8/2/__pycache__/description.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..20bd58d9062d118a97ba7b3cde63ace4f9f10f09 GIT binary patch literal 14485 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 Geg6mS?x_#} literal 0 HcmV?d00001 diff --git a/8/2/__pycache__/geometric.cpython-312.pyc b/8/2/__pycache__/geometric.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..86af75343e6697aafa4bb9f5c43c48992fa51ee4 GIT binary patch literal 8022 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 Wh5%*P*fDnP9s5xK39i!>zV9!ea;k^` literal 0 HcmV?d00001 diff --git a/8/2/__pycache__/morphology.cpython-312.pyc b/8/2/__pycache__/morphology.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a9c57a54730ba89ce7bba4a357afb2509410fa40 GIT binary patch literal 4199 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 literal 0 HcmV?d00001 diff --git a/8/2/__pycache__/segmentation.cpython-312.pyc b/8/2/__pycache__/segmentation.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b44b470c6c39d0900eceb2c36c58068d6b1b38c4 GIT binary patch literal 9035 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 literal 0 HcmV?d00001 diff --git a/8/2/__pycache__/spatialRestore.cpython-312.pyc b/8/2/__pycache__/spatialRestore.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..32231afc8c4f6e43933b7427c361ae038c83c308 GIT binary patch literal 32815 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 literal 0 HcmV?d00001 diff --git a/8/2/carLicense/__pycache__/__init__.cpython-312.pyc b/8/2/carLicense/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3f9de641439c0a610c8efa75dba92f713c4d882b GIT binary patch literal 174 zcmX@j%ge<81dA-B;z9Id5P=Rpvj9b=GgLBYGWxA#C}INgK7-W!@=djhanCIAC{2oS z$xP2INzBpp%uP&B)eR`hPfjf^&df{KbxJHwP0`KI(+#LB$;i)(v4}B>Nlq;C$xKeo vD^87xkI&4@EQycTE2#X%VUwGmQks)$SHuc5oe_wOL5z>gjEsy$%s>_Zj7%%* literal 0 HcmV?d00001 diff --git a/8/2/carLicense/__pycache__/predict.cpython-312.pyc b/8/2/carLicense/__pycache__/predict.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..93b5726a3674532850a621e114b639dee496e60f GIT binary patch literal 32466 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 T0B--5_fwg0*&P9Laozl1{gc)H literal 0 HcmV?d00001 diff --git a/8/2/main.py b/8/2/main.py index 62faa9e..6d4a27f 100644 --- a/8/2/main.py +++ b/8/2/main.py @@ -2,8 +2,13 @@ from globalObject import * import tkinter from tkinter.simpledialog import askinteger, askfloat, askstring -from tkinter.filedialog import askopenfilename, askopenfilenames, asksaveasfilename, askdirectory -from tkinter.messagebox import showinfo, showwarning, showerror,askyesno +from tkinter.filedialog import ( + askopenfilename, + askopenfilenames, + asksaveasfilename, + askdirectory, +) +from tkinter.messagebox import showinfo, showwarning, showerror, askyesno from PIL import Image, ImageTk import os import time @@ -20,64 +25,70 @@ import segmentation import description import carLicense.predict as predict import matplotlib.pyplot as plt -plt.rcParams['font.sans-serif']=['SimHei'] #用来正常显示中文标签 -plt.rcParams['axes.unicode_minus']=False #用来正常显示负 + +plt.rcParams["font.sans-serif"] = ["SimHei"] # 用来正常显示中文标签 +plt.rcParams["axes.unicode_minus"] = False # 用来正常显示负 -def cv_imread(filename,colorMode=1): - ''' +def cv_imread(filename, colorMode=1): + """ 读取文件路径中带汉字的图片,替代cv2.imread(filename) :param filename: 需要读取的文件 :param colorMode: 彩色模式 :return: 读取的图片 - ''' + """ return cv2.imdecode(np.fromfile(filename, dtype=np.uint8), colorMode) + def cv_imwrite(path, img): - ''' + """ 解决路径中有汉字的图片保存问题 :param path: 保存的路径 :param img: 保存的图片 - ''' - cv2.imencode('.jpg', img)[1].tofile(path) + """ + cv2.imencode(".jpg", img)[1].tofile(path) -def placePic1(img_show,text1=u"原始图像"): - ''' + +def placePic1(img_show, text1="原始图像"): + """ 将打开的初始图像放置在左边窗格 :param img_show: 需要显示的图片 :param text1: 图片说明 - ''' + """ global picSize if img_show is None: - showwarning(title='警告', message='未打开图片!') + showwarning(title="警告", message="未打开图片!") return - img=img_show.copy() - if len(img.shape)>2: + img = img_show.copy() + if len(img.shape) > 2: img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) - height,width=img.shape[:2] - scaling=max(width,height)/picSize + height, width = img.shape[:2] + scaling = max(width, height) / picSize newH = int(height / scaling) - newW = int(width /scaling) + newW = int(width / scaling) img0 = Image.fromarray(img) # 由OpenCV图片转换为PIL图片格式 img0 = img0.resize((newW, newH)) img0 = ImageTk.PhotoImage(img0) myWindow.originalText.set(text1) myWindow.label3.config(image=img0) myWindow.label3.image = img0 - myWindow.label3.place(relx=0.25, rely=0.40, width=picSize, height=picSize,anchor=tkinter.CENTER) # 设置绝对座标 + myWindow.label3.place( + relx=0.25, rely=0.40, width=picSize, height=picSize, anchor=tkinter.CENTER + ) # 设置绝对座标 -def placePic2(img_show, text2="处理结果图",RGB=True): - ''' + +def placePic2(img_show, text2="处理结果图", RGB=True): + """ 将处理结果图像放置在右边窗格 :param img_show: 需要显示的图像 :param text2: 图像文字说明 :param RGB: 是否转换成RGB,默认转换 - ''' + """ global picSize, img_result if img_show is None: - showwarning(title='警告', message='没有结果图片!') + showwarning(title="警告", message="没有结果图片!") return - img=img_show.copy() + img = img_show.copy() if len(img.shape) > 2 and RGB: img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) height, width = img.shape[:2] @@ -91,29 +102,33 @@ def placePic2(img_show, text2="处理结果图",RGB=True): myWindow.resultText.set(text2) myWindow.label4.config(image=img0) myWindow.label4.image = img0 - myWindow.label4.place(relx=0.75, rely=0.40, width=picSize, height=picSize,anchor=tkinter.CENTER) + myWindow.label4.place( + relx=0.75, rely=0.40, width=picSize, height=picSize, anchor=tkinter.CENTER + ) + def Choosepic(): - '''打开文件选择对话框,并读取图片文件,存放在img_open全局变量中''' + """打开文件选择对话框,并读取图片文件,存放在img_open全局变量中""" global picSize, path_, img_open, img_gray, img_empty path_ = askopenfilename(title="打开需要处理的图片") # path.set(path_) - if path_ != 0 and path_ != '': + if path_ != 0 and path_ != "": img_open = cv_imread(path_) # 读取文件路径中有汉字的图片文件 - img_gray = cv2.cvtColor(img_open,cv2.COLOR_BGR2GRAY) + img_gray = cv2.cvtColor(img_open, cv2.COLOR_BGR2GRAY) if img_open is not None: placePic1(img_open) else: placePic1(img_empty) - showwarning(title='警告', message='无图片!') + showwarning(title="警告", message="无图片!") else: img_open = None img_gray = None placePic1(img_empty) - showwarning(title='警告', message='未选择图片!') + showwarning(title="警告", message="未选择图片!") + def initWindows(): - '''将系统界面恢复到初始界面,并不清除结果图片''' + """将系统界面恢复到初始界面,并不清除结果图片""" myWindow.setVisibleLeft() myWindow.setVisibleRight0() myWindow.hideRight1() @@ -121,14 +136,17 @@ def initWindows(): myWindow.hideFig0() myWindow.hideFig1() myWindow.hideExplain() - myWindow.explainText.set("图像处理教学演示软件实现了图像处理中的一些经典算法。\n使用此软件需要先点文件-打开命令,打开一个图像," - "原始图片将显示在左边窗格内;\n然后从菜单中调用某算法处理图片,处理的结果将显示在右边窗格内。") + myWindow.explainText.set( + "图像处理教学演示软件实现了图像处理中的一些经典算法。\n使用此软件需要先点文件-打开命令,打开一个图像," + "原始图片将显示在左边窗格内;\n然后从菜单中调用某算法处理图片,处理的结果将显示在右边窗格内。" + ) if img_result is not None: placePic2(img_result) + def Reload(): - '''将系统界面恢复到初始界面,并且清除结果图片''' - global img_open, img_gray, img_result,img_binary ,PSF,contoursMinArea + """将系统界面恢复到初始界面,并且清除结果图片""" + global img_open, img_gray, img_result, img_binary, PSF, contoursMinArea img_result = None # 存放处理结果图片 img_binary = None # 二值图像 PSF = None # 退化模糊核 @@ -138,178 +156,223 @@ def Reload(): placePic1(img_open) else: placePic1(img_empty) - showwarning(title='警告', message='未打开图片!') + showwarning(title="警告", message="未打开图片!") initWindows() placePic2(img_empty, "无处理结果") + def Convert_gray(): - '''将读入的图片转换为灰度图像,并存储到img_gray''' + """将读入的图片转换为灰度图像,并存储到img_gray""" global picSize, img_open, img_result, img_gray initWindows() if img_open is not None: # if len(img_open.shape)<3: # showwarning(title='警告', message='只能对彩色图像进行色彩转换!') img_result = cv2.cvtColor(img_open, cv2.COLOR_BGR2GRAY) - img_gray=img_result.copy() + img_gray = img_result.copy() placePic2(img_result, "灰度图像") - myWindow.explainText.set("OpenCV默认的彩色图像的颜色空间是BGR\n" - "cv::cvtColor()支持多种颜色空间之间的转换\n" - "例如:cv::COLOR_BGR2GRAY \n此功能是将三通道的彩色图片转换成单通道的灰度图片") + myWindow.explainText.set( + "OpenCV默认的彩色图像的颜色空间是BGR\n" + "cv::cvtColor()支持多种颜色空间之间的转换\n" + "例如:cv::COLOR_BGR2GRAY \n此功能是将三通道的彩色图片转换成单通道的灰度图片" + ) else: - showwarning(title='警告', message='请先在文件菜单下打开图片!') + showwarning(title="警告", message="请先在文件菜单下打开图片!") + def Convert_HSV(): - '''将色彩模式转换为HSV模式''' + """将色彩模式转换为HSV模式""" global picSize, img_open, img_result initWindows() if img_open is not None: img_result = cv2.cvtColor(img_open, cv2.COLOR_BGR2HSV) - placePic2(img_result, "HSV彩色图",False) - myWindow.explainText.set("HSV彩色模型:H(Hue)色度 、S(Saturation)饱合度、V(Value)亮度\n" \ - "RGB彩色模式转HSV彩色模式方法:V=max(R,G,B)\nS=(V-min(R,G,B))*255/V if V!=0, \nS=0 otherwise\nH=(G - B)*60/S, if V=R\nH= 180+(B - R)*60/S, if V=G \nH=240+(R - G)*60/S, if V=B\n若 H<0,则 H=H+360") + placePic2(img_result, "HSV彩色图", False) + myWindow.explainText.set( + "HSV彩色模型:H(Hue)色度 、S(Saturation)饱合度、V(Value)亮度\n" + "RGB彩色模式转HSV彩色模式方法:V=max(R,G,B)\nS=(V-min(R,G,B))*255/V if V!=0, \nS=0 otherwise\nH=(G - B)*60/S, if V=R\nH= 180+(B - R)*60/S, if V=G \nH=240+(R - G)*60/S, if V=B\n若 H<0,则 H=H+360" + ) else: - showwarning(title='警告', message='请先在文件菜单下打开图片!') + showwarning(title="警告", message="请先在文件菜单下打开图片!") + def Convert_BGR(): - '''将色彩模式转换为BGR模式''' + """将色彩模式转换为BGR模式""" global picSize, img_open, img_result initWindows() if img_open is not None: - img_result=img_open - placePic2(img_result,"BGR彩色图像",False) - myWindow.explainText.set("OpenCV默认的彩色图像的颜色空间是BGR\n" - "cv::cvtColor()支持多种颜色空间之间的转换\n" - "例如:cv::COLOR_BGR2RGB \n cv::COLOR_RGB2BGR \n cv::COLOR_RGBA2BGRA \n cv::COLOR_BGRA2RGBA") + img_result = img_open + placePic2(img_result, "BGR彩色图像", False) + myWindow.explainText.set( + "OpenCV默认的彩色图像的颜色空间是BGR\n" + "cv::cvtColor()支持多种颜色空间之间的转换\n" + "例如:cv::COLOR_BGR2RGB \n cv::COLOR_RGB2BGR \n cv::COLOR_RGBA2BGRA \n cv::COLOR_BGRA2RGBA" + ) else: - showwarning(title='警告', message='请先在文件菜单下打开图片!') + showwarning(title="警告", message="请先在文件菜单下打开图片!") + def Savepic(): - '''打开文件对话框,将img_result图片保存到指定文件''' + """打开文件对话框,将img_result图片保存到指定文件""" global img_result if img_result is not None: - fname = asksaveasfilename(title=u'保存文件', filetypes=[("JPG", ".jpg")]) + fname = asksaveasfilename(title="保存文件", filetypes=[("JPG", ".jpg")]) if fname: cv_imwrite(str(fname), img_result) - showinfo(title='提示', message='图片已保存!') + showinfo(title="提示", message="图片已保存!") else: - showwarning(title='警告', message='请先输入文件名!') + showwarning(title="警告", message="请先输入文件名!") else: - showwarning(title='警告', message='请先处理图片!') -#--------------------------------------------------------- + showwarning(title="警告", message="请先处理图片!") + + +# --------------------------------------------------------- def Negative(): - ''' 对灰度图像反转 ''' + """对灰度图像反转""" global picSize, img_gray, img_result initWindows() if img_gray is not None: placePic1(img_gray, "原灰度图像") img_result = PointProcessing.negative(img_gray.copy()) placePic2(img_result, "反转图像") - myWindow.explainText.set(" 图像反转用于增强图像暗色区域的白色或灰色细节,适用于黑色较多的图像增强。\n " - "图像反转变换的表达式为: s=(L-1)-r \n 其中,L为图像的灰度级,r为输入灰度值,s为输出灰度值。 ") + myWindow.explainText.set( + " 图像反转用于增强图像暗色区域的白色或灰色细节,适用于黑色较多的图像增强。\n " + "图像反转变换的表达式为: s=(L-1)-r \n 其中,L为图像的灰度级,r为输入灰度值,s为输出灰度值。 " + ) else: - showwarning(title='警告', message='请先在文件菜单下打开图片!') + showwarning(title="警告", message="请先在文件菜单下打开图片!") + def Global_linear_transmation(): - ''' 灰度图像全局线性变换 ''' + """灰度图像全局线性变换""" global picSize, img_gray, img_result initWindows() if img_gray is not None: placePic1(img_gray, "原灰度图像") img_result = PointProcessing.global_linear_transmation(img_gray.copy()) placePic2(img_result, "全局灰度线性变换图像") - imgExplain = cv2.imread(r'.\explain\global_linear.jpg', 0) - text="全局线性变换中,输入图像与输出图像灰度值之间的关系式:\n s = (r-a)*(d-c)/(b-a)+c \n " \ - "\n 本算法是将图像的灰度区间变换为[0,255]" + imgExplain = cv2.imread(r".\explain\global_linear.jpg", 0) + text = ( + "全局线性变换中,输入图像与输出图像灰度值之间的关系式:\n s = (r-a)*(d-c)/(b-a)+c \n " + "\n 本算法是将图像的灰度区间变换为[0,255]" + ) picLists = [["输入灰度值与输出灰度值之间的关系", imgExplain]] - myWindow.showExplain(text,picLists) + myWindow.showExplain(text, picLists) else: - showwarning(title='警告', message='请先在文件菜单下打开图片!') + showwarning(title="警告", message="请先在文件菜单下打开图片!") + def Piecewise_linear_transformation(): - '''灰度图像分段线性变换,可由用户设置分段数以及灰度值范围''' + """灰度图像分段线性变换,可由用户设置分段数以及灰度值范围""" global picSize, img_gray, img_result initWindows() if img_gray is not None: - imgExplain = cv2.imread(r'.\explain\piece_trans.jpg', 0) - text="分段灰度线性变换可以将图像的灰度值分成若干段,如图所示。\n各段采用不同的线性变换,分成三段的数学表达式如下:\n" \ - "s=(c/a)*r if 0<=r=n_int: + grayLists0 = [ + [0, 80, 0, 50], + [80, 150, 50, 200], + [150, 255, 200, 255], + ] # 初始默认为3段 + grayLists = n_int * [[0, 255, 0, 255]] # 给分段线性变换的初始参数赋值 + if len(grayLists0) >= n_int: grayLists = grayLists0[0:n_int] else: grayLists[0:3] = grayLists0 - grayLists[0][0]=grayLists[0][2]=0 #限定灰度值在0-255 - grayLists[n_int-1][1] = grayLists[n_int-1][3] = 255 - paraW=paraWindow2(root,grayLists,"分段线性变换参数设置","变换前灰度区间 变换后灰度区间") - img_result = PointProcessing.piecewise_linear_transformation(img_gray.copy(),paraW.paraLists) + grayLists[0][0] = grayLists[0][2] = 0 # 限定灰度值在0-255 + grayLists[n_int - 1][1] = grayLists[n_int - 1][3] = 255 + paraW = paraWindow2( + root, + grayLists, + "分段线性变换参数设置", + "变换前灰度区间 变换后灰度区间", + ) + img_result = PointProcessing.piecewise_linear_transformation( + img_gray.copy(), paraW.paraLists + ) placePic2(img_result, "分段灰度线性变换图像") else: - showwarning(title='警告', message='请先输入分段数!') + showwarning(title="警告", message="请先输入分段数!") else: - showwarning(title='警告', message='请先在文件菜单下打开图片!') + showwarning(title="警告", message="请先在文件菜单下打开图片!") + def Logarithmic_transformations(): - ''' 灰度图像对数变换 ''' + """灰度图像对数变换""" global picSize, img_gray, img_result initWindows() if img_gray is not None: - imgExplain = cv2.imread(r'.\explain\log_trans.jpg', 0) - text="对数变换是一种非线性的灰度变换。\n对数变换的表达式如下:\ns=a+c×log(r+1)\n此对数是以自然数e为底" + imgExplain = cv2.imread(r".\explain\log_trans.jpg", 0) + text = "对数变换是一种非线性的灰度变换。\n对数变换的表达式如下:\ns=a+c×log(r+1)\n此对数是以自然数e为底" picLists = [["对数变换的输入图像与输出图像之间关系如图所示:", imgExplain]] - myWindow.showExplain(text,picLists) + myWindow.showExplain(text, picLists) placePic1(img_gray, "原灰度图像") - paraC=askfloat("参数设置","s=C*log(1+r),请输入对数变换常数C",initialvalue=20.0) - img_result = PointProcessing.logarithmic_transformations(img_gray.copy(),paraC) + paraC = askfloat( + "参数设置", "s=C*log(1+r),请输入对数变换常数C", initialvalue=20.0 + ) + img_result = PointProcessing.logarithmic_transformations(img_gray.copy(), paraC) placePic2(img_result, "灰度对数变换图像") else: - showwarning(title='警告', message='请先在文件菜单下打开图片!') + showwarning(title="警告", message="请先在文件菜单下打开图片!") + def Power_law_transformations(): - ''' 灰度图像幂次变换 ''' + """灰度图像幂次变换""" global picSize, img_gray, img_result initWindows() if img_gray is not None: - myWindow.explainText.set("幂次变换的表达式如下:S=c×(r^γ);\n" - "其中,c取默认值1,幂次γ是可调的参数。\n" - "幂次变换随着幂次γ的不同,可以得到不同的增强效果。\n" - "当γ<1时,幂次变换相当于对数变换,此非线性变换可以扩展图像中的暗区,而压缩亮区;\n" - "当γ>1时,幂次变换的效果与对数变换的效果相反,此非线性变换可以扩展图像中的亮区,而压缩暗区。") + myWindow.explainText.set( + "幂次变换的表达式如下:S=c×(r^γ);\n" + "其中,c取默认值1,幂次γ是可调的参数。\n" + "幂次变换随着幂次γ的不同,可以得到不同的增强效果。\n" + "当γ<1时,幂次变换相当于对数变换,此非线性变换可以扩展图像中的暗区,而压缩亮区;\n" + "当γ>1时,幂次变换的效果与对数变换的效果相反,此非线性变换可以扩展图像中的亮区,而压缩暗区。" + ) placePic1(img_gray, "原灰度图像") - paraGamma = askfloat("参数设置", "s=r^γ"+",请输入幂次γ", initialvalue=1.0) - img_result = PointProcessing.power_law_transformations(img_gray.copy(), paraGamma) + paraGamma = askfloat("参数设置", "s=r^γ" + ",请输入幂次γ", initialvalue=1.0) + img_result = PointProcessing.power_law_transformations( + img_gray.copy(), paraGamma + ) placePic2(img_result, "灰度幂次变换图像") else: - showwarning(title='警告', message='请先在文件菜单下打开图片!') + showwarning(title="警告", message="请先在文件菜单下打开图片!") + def Gray_histogram(): - ''' 灰度图像直方图 ''' + """灰度图像直方图""" global picSize, img_gray, img_result initWindows() if img_gray is not None: - myWindow.explainText.set("直方图是图像的灰度值统计图,\n" - "即对于每个灰度值,统计在图像中具有该灰度值的像素个数或灰度值出现的概率,并绘制而成的图形称为灰度直方图(简称直方图)。\n直方图反映了图像的灰度分布范围等特征,在很多场合下,往往包含了图像的重要信息。 ") - binSize = askinteger(title="直方图参数设置", prompt="请输入直方图的灰度级数", initialvalue=256) + myWindow.explainText.set( + "直方图是图像的灰度值统计图,\n" + "即对于每个灰度值,统计在图像中具有该灰度值的像素个数或灰度值出现的概率,并绘制而成的图形称为灰度直方图(简称直方图)。\n直方图反映了图像的灰度分布范围等特征,在很多场合下,往往包含了图像的重要信息。 " + ) + binSize = askinteger( + title="直方图参数设置", prompt="请输入直方图的灰度级数", initialvalue=256 + ) placePic1(img_gray, "原灰度图像") - histr = Histogram.gray_histogram(img_gray.copy(),binSize) + histr = Histogram.gray_histogram(img_gray.copy(), binSize) myWindow.myFig0.clear() f_plot = myWindow.myFig0.add_subplot(111) - f_plot.bar(np.arange(binSize),histr.flatten(),color='blue') + f_plot.bar(np.arange(binSize), histr.flatten(), color="blue") myWindow.setVisibleFig0() myWindow.resultText.set("灰度直方图") else: - showwarning(title='警告', message='请先在文件菜单下打开图片!') + showwarning(title="警告", message="请先在文件菜单下打开图片!") + def Mask_histogram(): - '''灰度图像掩膜直方图,可以由用户选择掩膜图像''' + """灰度图像掩膜直方图,可以由用户选择掩膜图像""" global picSize, img_gray, img_result initWindows() if img_gray is not None: @@ -317,59 +380,79 @@ def Mask_histogram(): myWindow.explainText.set( "掩膜直方图可以统计图像某些局部区域的直方图。" "\n要生成掩膜直方图需要提供一副掩膜图像,将要统计的部分设置为白色,其余部分为黑色。" - "\n在生成直方图时,把这个掩膜图像与原图像做位与运算。 ") - binSize = askinteger(title="直方图参数设置", prompt="请输入直方图的灰度级数", initialvalue=256) + "\n在生成直方图时,把这个掩膜图像与原图像做位与运算。 " + ) + binSize = askinteger( + title="直方图参数设置", prompt="请输入直方图的灰度级数", initialvalue=256 + ) pathMask = askopenfilename(title="打开掩膜二值图像") - if pathMask != 0 and pathMask != '': - imgMask = cv_imread(pathMask,0) - histr,masked_img = Histogram.mask_histogram(img_gray.copy(), imgMask, binSize) - myWindow.showAndBarFig0(masked_img,histr) + if pathMask != 0 and pathMask != "": + imgMask = cv_imread(pathMask, 0) + histr, masked_img = Histogram.mask_histogram( + img_gray.copy(), imgMask, binSize + ) + myWindow.showAndBarFig0(masked_img, histr) myWindow.resultText.set("掩膜直方图") else: - showwarning(title='警告', message='请先打开掩膜二值图片!') + showwarning(title="警告", message="请先打开掩膜二值图片!") else: - showwarning(title='警告', message='请先在文件菜单下打开图片!') + showwarning(title="警告", message="请先在文件菜单下打开图片!") + def Equalization_histogram(): - ''' 直方图均衡化 ''' + """直方图均衡化""" global picSize, img_gray, img_result initWindows() if img_gray is not None: placePic1(img_gray, "原灰度图像") - histr,equ = Histogram.equalization_histogram(img_gray.copy()) - myWindow.showAndBarFig0(equ,histr) + histr, equ = Histogram.equalization_histogram(img_gray.copy()) + myWindow.showAndBarFig0(equ, histr) myWindow.resultText.set("直方图均衡化") - myWindow.explainText.set('''直方图均衡化是一种常用的灰度增强算法,也称为直方图均匀化。它是将原图像的直方图经过变换函数修整为均匀直方图,使得图像的灰度分布趋向均匀。 ''') + myWindow.explainText.set( + """直方图均衡化是一种常用的灰度增强算法,也称为直方图均匀化。它是将原图像的直方图经过变换函数修整为均匀直方图,使得图像的灰度分布趋向均匀。 """ + ) else: - showwarning(title='警告', message='请先在文件菜单下打开图片!') + showwarning(title="警告", message="请先在文件菜单下打开图片!") + def SML(): - ''' 直方图规定化——单映射 ''' + """直方图规定化——单映射""" global picSize, img_gray, img_result if img_gray is None: - showwarning(title='警告', message='请先在文件菜单下打开图片!') + showwarning(title="警告", message="请先在文件菜单下打开图片!") return initWindows() placePic1(img_gray, "原灰度图像") - #显示目标直方图 + # 显示目标直方图 dst = [[], [], []] - dst[0]=np.int32([a for a in range(256)]) - dst[1]= np.int32([256 - j for j in range(256)]) + dst[0] = np.int32([a for a in range(256)]) + dst[1] = np.int32([256 - j for j in range(256)]) dst[2] = np.zeros((256,), np.int32) dst[2][:] = 128 - picLists=[["1-目标直方图是正三角形",dst[0]],["2-目标直方图为倒三角形",dst[1]],["3-目标直方图为水平直线",dst[2]]] - myWindow.showFig1(picLists,type='bar') - dest=["正三角形","倒三角形","均匀分布"] - typeHistgram=askinteger("选择目标直方图","请选择目标直方图,1=正三角形,2-倒三角形,3=均匀分布", initialvalue=1,minvalue=1,maxvalue=3) - if typeHistgram==1 or typeHistgram==2 or typeHistgram==3: - histr, equ = Histogram.regulation_histogram(img_gray.copy(),typeHistgram,True) - myWindow.showAndBarFig0(equ,histr) - myWindow.resultText.set("单映射"+dest[typeHistgram-1]+"规定化") + picLists = [ + ["1-目标直方图是正三角形", dst[0]], + ["2-目标直方图为倒三角形", dst[1]], + ["3-目标直方图为水平直线", dst[2]], + ] + myWindow.showFig1(picLists, type="bar") + dest = ["正三角形", "倒三角形", "均匀分布"] + typeHistgram = askinteger( + "选择目标直方图", + "请选择目标直方图,1=正三角形,2-倒三角形,3=均匀分布", + initialvalue=1, + minvalue=1, + maxvalue=3, + ) + if typeHistgram == 1 or typeHistgram == 2 or typeHistgram == 3: + histr, equ = Histogram.regulation_histogram(img_gray.copy(), typeHistgram, True) + myWindow.showAndBarFig0(equ, histr) + myWindow.resultText.set("单映射" + dest[typeHistgram - 1] + "规定化") else: - showwarning(title='警告', message='请正确选择目标直方图!') + showwarning(title="警告", message="请正确选择目标直方图!") + def GML(): - ''' 直方图规定化——组映射 ''' + """直方图规定化——组映射""" global picSize, img_gray, img_result initWindows() if img_gray is not None: @@ -383,476 +466,582 @@ def GML(): dst[0][j] = j dst[1][j] = 256 - j dst[2][j] = 128 - picLists = [["1-目标直方图是正三角形", dst[0]], ["2-目标直方图为倒三角形", dst[1]], ["3-目标直方图为水平直线", dst[2]]] - myWindow.showFig1(picLists, type='bar') + picLists = [ + ["1-目标直方图是正三角形", dst[0]], + ["2-目标直方图为倒三角形", dst[1]], + ["3-目标直方图为水平直线", dst[2]], + ] + myWindow.showFig1(picLists, type="bar") dest = ["正三角形", "倒三角形", "均匀分布"] - typeHistgram = askinteger("选择目标直方图", "请选择目标直方图,1=正三角形,2=倒三角形,3=均匀分布", initialvalue=1) + typeHistgram = askinteger( + "选择目标直方图", + "请选择目标直方图,1=正三角形,2=倒三角形,3=均匀分布", + initialvalue=1, + ) if typeHistgram == 1 or typeHistgram == 2 or typeHistgram == 3: - histr, equ = Histogram.regulation_histogram(img_gray.copy(), typeHistgram, False) - myWindow.showAndBarFig0(equ,histr) - myWindow.resultText.set("组映射"+dest[typeHistgram-1]+"规定化") + histr, equ = Histogram.regulation_histogram( + img_gray.copy(), typeHistgram, False + ) + myWindow.showAndBarFig0(equ, histr) + myWindow.resultText.set("组映射" + dest[typeHistgram - 1] + "规定化") else: - showwarning(title='警告', message='请正确选择目标直方图!') + showwarning(title="警告", message="请正确选择目标直方图!") else: - showwarning(title='警告', message='请先在文件菜单下打开图片!') -#--------------------------------------------------------- + showwarning(title="警告", message="请先在文件菜单下打开图片!") + + +# --------------------------------------------------------- def Gaussian_filter(): - ''' 高斯平滑滤波 ''' + """高斯平滑滤波""" global picSize, img_gray, img_result initWindows() if img_gray is None: - showwarning(title='警告', message='请先在文件菜单下打开图片!') + showwarning(title="警告", message="请先在文件菜单下打开图片!") return - img = cv2.imread(r'.\explain\Gaussian_weight.jpg') - imgExplain= cv2.cvtColor(img, cv2.COLOR_BGR2RGB) - text = "高斯滤波是比较经典的加权的平滑滤波,适用于消除高斯噪声。\n"\ - "高斯滤波运用了高斯的正态分布的密度函数来计算模板上的权重。" \ - "正态分布是一种钟形曲线,越接近中心,取值越大,越远离中心,取值越小,如图所示" + img = cv2.imread(r".\explain\Gaussian_weight.jpg") + imgExplain = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) + text = ( + "高斯滤波是比较经典的加权的平滑滤波,适用于消除高斯噪声。\n" + "高斯滤波运用了高斯的正态分布的密度函数来计算模板上的权重。" + "正态分布是一种钟形曲线,越接近中心,取值越大,越远离中心,取值越小,如图所示" + ) picLists = [["滤波核权重按正态分布", imgExplain]] myWindow.showExplain(text, picLists) placePic1(img_gray, "待处理灰度图像") - kernalSize = askinteger("设置滤波核大小", "请输入高斯滤波核的大小(通常为奇数)",initialvalue=3) - if kernalSize and kernalSize%2==1: + kernalSize = askinteger( + "设置滤波核大小", "请输入高斯滤波核的大小(通常为奇数)", initialvalue=3 + ) + if kernalSize and kernalSize % 2 == 1: img_result = SpatialFilter.gaussian_filter(img_gray.copy(), kernalSize) - placePic2(img_result,"核大小为"+str(kernalSize)+"的高斯滤波") + placePic2(img_result, "核大小为" + str(kernalSize) + "的高斯滤波") else: - showwarning(title='警告', message='请先设置滤波核大小') + showwarning(title="警告", message="请先设置滤波核大小") + def Median_filter(): - ''' 中值滤波 ''' + """中值滤波""" global picSize, img_gray, img_result initWindows() if img_gray is None: - showwarning(title='警告', message='请先在文件菜单下打开图片!') + showwarning(title="警告", message="请先在文件菜单下打开图片!") return if img_result is not None: - answer = askyesno("选择待滤波图像", "Y-对打开图像进行滤波,N-对处理后图像进行滤波") + answer = askyesno( + "选择待滤波图像", "Y-对打开图像进行滤波,N-对处理后图像进行滤波" + ) if not answer: img_ = img_result.copy() else: img_ = img_gray.copy() - myWindow.explainText.set("中值滤波的方法是:对邻域内的像素灰度值按大小排序,然后取中间位置的灰度值取代当前像素的灰度值。"\ - "\n中值滤波对滤除脉冲干扰及图像扫描噪声最为有效,它可以滤除小于1/2窗口的脉冲信号。") + myWindow.explainText.set( + "中值滤波的方法是:对邻域内的像素灰度值按大小排序,然后取中间位置的灰度值取代当前像素的灰度值。" + "\n中值滤波对滤除脉冲干扰及图像扫描噪声最为有效,它可以滤除小于1/2窗口的脉冲信号。" + ) placePic1(img_, "原灰度图像") - kernalSize = askinteger("设置滤波核", "请输入中值滤波核的大小(通常为奇数)",initialvalue=3) - if kernalSize and kernalSize%2==1: - img_result=SpatialFilter.median_filter(img_.copy(),kernalSize) + kernalSize = askinteger( + "设置滤波核", "请输入中值滤波核的大小(通常为奇数)", initialvalue=3 + ) + if kernalSize and kernalSize % 2 == 1: + img_result = SpatialFilter.median_filter(img_.copy(), kernalSize) placePic2(img_result) myWindow.resultText.set("中值滤波") else: - showwarning(title='警告', message='请先设置滤波核大小,且滤波核大小应为奇数') + showwarning(title="警告", message="请先设置滤波核大小,且滤波核大小应为奇数") + def Max_filter(): - ''' 最大值滤波 ''' + """最大值滤波""" global picSize, img_gray, img_result initWindows() if img_gray is None: - showwarning(title='警告', message='请先在文件菜单下打开图片!') + showwarning(title="警告", message="请先在文件菜单下打开图片!") return if img_result is not None: - answer = askyesno("选择待滤波图像", "Y-对打开图像进行滤波,N-对处理后图像进行滤波") + answer = askyesno( + "选择待滤波图像", "Y-对打开图像进行滤波,N-对处理后图像进行滤波" + ) if not answer: img_ = img_result.copy() else: img_ = img_gray.copy() - myWindow.explainText.set("最大值滤波的方法是:对邻域内的像素灰度值按大小排序,然后取最大的灰度值取代当前像素的灰度值。"\ - "\n最大值滤波适合去除胡椒噪声。") + myWindow.explainText.set( + "最大值滤波的方法是:对邻域内的像素灰度值按大小排序,然后取最大的灰度值取代当前像素的灰度值。" + "\n最大值滤波适合去除胡椒噪声。" + ) placePic1(img_, "原灰度图像") - kernalSize = askinteger("设置滤波核", "请输入最大值滤波核的大小",initialvalue=3) + kernalSize = askinteger("设置滤波核", "请输入最大值滤波核的大小", initialvalue=3) if kernalSize: - img_result=SpatialFilter.max_filter(img_.copy(),kernalSize) + img_result = SpatialFilter.max_filter(img_.copy(), kernalSize) placePic2(img_result) myWindow.resultText.set("最大值滤波") else: - showwarning(title='警告', message='请先设置滤波核大小!') + showwarning(title="警告", message="请先设置滤波核大小!") + def Min_filter(): - ''' 最小值滤波 ''' + """最小值滤波""" global picSize, img_gray, img_result initWindows() if img_gray is None: - showwarning(title='警告', message='请先在文件菜单下打开图片!') + showwarning(title="警告", message="请先在文件菜单下打开图片!") return if img_result is not None: - answer = askyesno("选择待滤波图像", "Y-对打开图像进行滤波,N-对处理后图像进行滤波") + answer = askyesno( + "选择待滤波图像", "Y-对打开图像进行滤波,N-对处理后图像进行滤波" + ) if not answer: img_ = img_result.copy() else: img_ = img_gray.copy() - myWindow.explainText.set("最小值滤波的方法是:对邻域内的像素灰度值按大小排序,然后取最小的灰度值取代当前像素的灰度值。"\ - "\n最小值滤波适合去除盐噪声。") + myWindow.explainText.set( + "最小值滤波的方法是:对邻域内的像素灰度值按大小排序,然后取最小的灰度值取代当前像素的灰度值。" + "\n最小值滤波适合去除盐噪声。" + ) placePic1(img_, "原灰度图像") - kernalSize = askinteger("设置滤波核", "请输入最小值滤波核的大小",initialvalue=3) + kernalSize = askinteger("设置滤波核", "请输入最小值滤波核的大小", initialvalue=3) if kernalSize: - img_result=SpatialFilter.min_filter(img_.copy(),kernalSize) + img_result = SpatialFilter.min_filter(img_.copy(), kernalSize) placePic2(img_result) myWindow.resultText.set("最小值滤波") else: - showwarning(title='警告', message='请先设置滤波核大小!') + showwarning(title="警告", message="请先设置滤波核大小!") + def Smooth_filter(): - '''自定义平滑滤波,由用户定义滤波核权重''' + """自定义平滑滤波,由用户定义滤波核权重""" global picSize, img_gray, img_result initWindows() if img_gray is None: - showwarning(title='警告', message='请先在文件菜单下打开图片!') + showwarning(title="警告", message="请先在文件菜单下打开图片!") return if img_result is not None: - answer = askyesno("选择待滤波图像", "Y-对打开图像进行滤波,N-对处理后图像进行滤波") + answer = askyesno( + "选择待滤波图像", "Y-对打开图像进行滤波,N-对处理后图像进行滤波" + ) if not answer: img_ = img_result.copy() else: img_ = img_gray.copy() - myWindow.explainText.set("自定义滤波可以由用户定义滤波核的大小和权重,\n并根据定义的滤波器进行图像的滤波处理。") + myWindow.explainText.set( + "自定义滤波可以由用户定义滤波核的大小和权重,\n并根据定义的滤波器进行图像的滤波处理。" + ) placePic1(img_, "原灰度图像") - kernalSize = askinteger("设置滤波核", "请输入滤波核的大小(通常为奇数)",initialvalue=3) - if kernalSize and kernalSize%2==1: - temp=[kernalSize*[1]] - paraLists=kernalSize*temp - paraW = paraWindow2(root,paraLists,"滤波核设置","请设置"+str(kernalSize)+"x"+str(kernalSize)+"滤波核的权重") - paraLists=paraW.paraLists/np.sum(paraW.paraLists)#滤波核的权重和为1 - showinfo(title='提示', message="已调整滤波核,让平滑滤波核的权重和为1") - img_result=SpatialFilter.smooth_filter(img_.copy(),paraLists) - placePic2(img_result,"自定义平滑滤波") + kernalSize = askinteger( + "设置滤波核", "请输入滤波核的大小(通常为奇数)", initialvalue=3 + ) + if kernalSize and kernalSize % 2 == 1: + temp = [kernalSize * [1]] + paraLists = kernalSize * temp + paraW = paraWindow2( + root, + paraLists, + "滤波核设置", + "请设置" + str(kernalSize) + "x" + str(kernalSize) + "滤波核的权重", + ) + paraLists = paraW.paraLists / np.sum(paraW.paraLists) # 滤波核的权重和为1 + showinfo(title="提示", message="已调整滤波核,让平滑滤波核的权重和为1") + img_result = SpatialFilter.smooth_filter(img_.copy(), paraLists) + placePic2(img_result, "自定义平滑滤波") else: - showwarning(title='警告', message='请先设置滤波核大小,且滤波核大小应为奇数') -#--------------------------------------------------------- + showwarning(title="警告", message="请先设置滤波核大小,且滤波核大小应为奇数") + + +# --------------------------------------------------------- def Constant_border(): - ''' 图片扩展,常量法,边界补0 ''' + """图片扩展,常量法,边界补0""" global picSize, img_gray, img_result initWindows() if img_gray is None: - showwarning(title='警告', message='请先在文件菜单下打开图片!') + showwarning(title="警告", message="请先在文件菜单下打开图片!") return myWindow.hideFig1() # 隐藏画布 - myWindow.explainText.set('''BORDER_CONSTANT:常量,增加的变量值为value色 [value][value] | abcdef | [value][value] + myWindow.explainText.set( + """BORDER_CONSTANT:常量,增加的变量值为value色 [value][value] | abcdef | [value][value] BORDER_REFLICATE:直接用边界的颜色填充, aaaaaa | abcdefg | gggg BORDER_REFLECT:镜像,abcdefg | gfedcbamn | nmabcd BORDER_REFLECT_101:镜像,镜像时,会把边界空开,abcdefg | egfedcbamne | nmabcd BORDER_WRAP:环绕 类似于这种方式abcdf | mmabcdf | mmabcd -''') +""" + ) placePic1(img_gray, "原灰度图像") borderSize = askinteger("设置边框宽度", "请输入增加的边框宽度", initialvalue=10) if borderSize: - img_result = SpatialFilter.border_fill(img_gray.copy(), cv2.BORDER_CONSTANT, borderSize) + img_result = SpatialFilter.border_fill( + img_gray.copy(), cv2.BORDER_CONSTANT, borderSize + ) placePic2(img_result) myWindow.resultText.set("边界补0后的图像") else: - showwarning(title='警告', message='请先输入增加的边框宽度') + showwarning(title="警告", message="请先输入增加的边框宽度") + def Replicate_border(): - ''' 图片扩展,复制法 ''' + """图片扩展,复制法""" global picSize, img_gray, img_result initWindows() if img_gray is None: - showwarning(title='警告', message='请先在文件菜单下打开图片!') + showwarning(title="警告", message="请先在文件菜单下打开图片!") return myWindow.hideFig1() # 隐藏画布 - myWindow.explainText.set('''BORDER_CONSTANT:常量,增加的变量值为value色 [value][value] | abcdef | [value][value] + myWindow.explainText.set( + """BORDER_CONSTANT:常量,增加的变量值为value色 [value][value] | abcdef | [value][value] BORDER_REFLICATE:直接用边界的颜色填充, aaaaaa | abcdefg | gggg BORDER_REFLECT:镜像,abcdefg | gfedcbamn | nmabcd BORDER_REFLECT_101:镜像,镜像时,会把边界空开,abcdefg | egfedcbamne | nmabcd BORDER_WRAP:环绕 类似于这种方式abcdf | mmabcdf | mmabcd -''') +""" + ) placePic1(img_gray, "原灰度图像") borderSize = askinteger("设置边框宽度", "请输入增加的边框宽度", initialvalue=10) if borderSize: - img_result = SpatialFilter.border_fill(img_gray.copy(), cv2.BORDER_REPLICATE, borderSize) + img_result = SpatialFilter.border_fill( + img_gray.copy(), cv2.BORDER_REPLICATE, borderSize + ) placePic2(img_result) myWindow.resultText.set("复制边界后的图像") else: - showwarning(title='警告', message='请先输入增加的边框宽度') + showwarning(title="警告", message="请先输入增加的边框宽度") + def Reflect_border(): - ''' 图片扩展,反射法 ''' + """图片扩展,反射法""" global picSize, img_gray, img_result initWindows() if img_gray is None: - showwarning(title='警告', message='请先在文件菜单下打开图片!') + showwarning(title="警告", message="请先在文件菜单下打开图片!") return myWindow.hideFig1() # 隐藏画布 - myWindow.explainText.set('''BORDER_CONSTANT:常量,增加的变量值为value色 [value][value] | abcdef | [value][value] + myWindow.explainText.set( + """BORDER_CONSTANT:常量,增加的变量值为value色 [value][value] | abcdef | [value][value] BORDER_REFLICATE:直接用边界的颜色填充, aaaaaa | abcdefg | gggg BORDER_REFLECT:镜像,abcdefg | gfedcbamn | nmabcd BORDER_REFLECT_101:镜像,镜像时,会把边界空开,abcdefg | egfedcbamne | nmabcd BORDER_WRAP:环绕 类似于这种方式abcdf | mmabcdf | mmabcd -''') +""" + ) placePic1(img_gray, "原灰度图像") borderSize = askinteger("设置边框宽度", "请输入增加的边框宽度", initialvalue=10) if borderSize: - img_result = SpatialFilter.border_fill(img_gray.copy(), cv2.BORDER_REFLECT, borderSize) + img_result = SpatialFilter.border_fill( + img_gray.copy(), cv2.BORDER_REFLECT, borderSize + ) placePic2(img_result) myWindow.resultText.set("边界镜像处理") else: - showwarning(title='警告', message='请先输入增加的边框宽度') + showwarning(title="警告", message="请先输入增加的边框宽度") + def Reflect_border_101(): - ''' 图片扩展,反射法101 ''' + """图片扩展,反射法101""" global picSize, img_gray, img_result initWindows() if img_gray is None: - showwarning(title='警告', message='请先在文件菜单下打开图片!') + showwarning(title="警告", message="请先在文件菜单下打开图片!") return myWindow.hideFig1() # 隐藏画布 - myWindow.explainText.set('''BORDER_CONSTANT:常量,增加的变量值为value色 [value][value] | abcdef | [value][value] + myWindow.explainText.set( + """BORDER_CONSTANT:常量,增加的变量值为value色 [value][value] | abcdef | [value][value] BORDER_REFLICATE:直接用边界的颜色填充, aaaaaa | abcdefg | gggg BORDER_REFLECT:镜像,abcdefg | gfedcbamn | nmabcd BORDER_REFLECT_101:镜像,镜像时,会把边界空开,abcdefg | egfedcbamne | nmabcd BORDER_WRAP:环绕 类似于这种方式abcdf | mmabcdf | mmabcd -''') +""" + ) placePic1(img_gray, "原灰度图像") borderSize = askinteger("设置边框宽度", "请输入增加的边框宽度", initialvalue=10) if borderSize: - img_result = SpatialFilter.border_fill(img_gray.copy(), cv2.BORDER_REFLECT101, borderSize) + img_result = SpatialFilter.border_fill( + img_gray.copy(), cv2.BORDER_REFLECT101, borderSize + ) placePic2(img_result) myWindow.resultText.set("边界镜像_101") else: - showwarning(title='警告', message='请先输入增加的边框宽度') + showwarning(title="警告", message="请先输入增加的边框宽度") + def Wrap_border(): - ''' 图片扩展,外包装法 ''' + """图片扩展,外包装法""" global picSize, img_gray, img_result initWindows() if img_gray is None: - showwarning(title='警告', message='请先在文件菜单下打开图片!') + showwarning(title="警告", message="请先在文件菜单下打开图片!") return myWindow.hideFig1() # 隐藏画布 - myWindow.explainText.set('''BORDER_CONSTANT:常量,增加的变量值为value色 [value][value] | abcdef | [value][value] + myWindow.explainText.set( + """BORDER_CONSTANT:常量,增加的变量值为value色 [value][value] | abcdef | [value][value] BORDER_REFLICATE:直接用边界的颜色填充, aaaaaa | abcdefg | gggg BORDER_REFLECT:镜像,abcdefg | gfedcbamn | nmabcd BORDER_REFLECT_101:镜像,镜像时,会把边界空开,abcdefg | egfedcbamne | nmabcd BORDER_WRAP:环绕 类似于这种方式abcdf | mmabcdf | mmabcd -''') +""" + ) placePic1(img_gray, "原灰度图像") borderSize = askinteger("设置边框宽度", "请输入增加的边框宽度", initialvalue=10) if borderSize: - img_result = SpatialFilter.border_fill(img_gray.copy(), cv2.BORDER_WRAP, borderSize) + img_result = SpatialFilter.border_fill( + img_gray.copy(), cv2.BORDER_WRAP, borderSize + ) placePic2(img_result) myWindow.resultText.set("边界环绕处理") else: - showwarning(title='警告', message='请先输入增加的边框宽度') -#--------------------------------------------------------- + showwarning(title="警告", message="请先输入增加的边框宽度") + + +# --------------------------------------------------------- def Roberts_filter(): - ''' Roberts算子 ''' + """Roberts算子""" global picSize, img_gray, img_result initWindows() if img_gray is None: - showwarning(title='警告', message='请先在文件菜单下打开图片!') + showwarning(title="警告", message="请先在文件菜单下打开图片!") return - imgExplain=cv2.imread(r'.\explain\Roberts.jpg',0) - text="Roberts算子能较好的增强正负45度的图像边缘。" - picLists=[["Roberts算子模板",imgExplain]] + imgExplain = cv2.imread(r".\explain\Roberts.jpg", 0) + text = "Roberts算子能较好的增强正负45度的图像边缘。" + picLists = [["Roberts算子模板", imgExplain]] myWindow.showExplain(text, picLists) placePic1(img_gray, "原灰度图像") img_result = SpatialFilter.roberts_filter(img_gray.copy()) placePic2(img_result) myWindow.resultText.set("Roberts锐化") + def Sobel_filter(): - ''' sobel算子 ''' + """sobel算子""" global picSize, img_gray, img_result initWindows() if img_gray is None: - showwarning(title='警告', message='请先在文件菜单下打开图片!') + showwarning(title="警告", message="请先在文件菜单下打开图片!") return - imgExplain = cv2.imread(r'.\explain\sobel.jpg', 0) - text="Sobel算子给靠近中心的像素以较大的权重,使锐化的同时还有一定的平滑作用。" + imgExplain = cv2.imread(r".\explain\sobel.jpg", 0) + text = "Sobel算子给靠近中心的像素以较大的权重,使锐化的同时还有一定的平滑作用。" picLists = [["Sobel算子模板", imgExplain]] - myWindow.showExplain(text,picLists) + myWindow.showExplain(text, picLists) placePic1(img_gray, "原灰度图像") img_result = SpatialFilter.sobel_filter(img_gray.copy()) placePic2(img_result) myWindow.resultText.set("Sobel锐化") + def Scharr_filter(): - ''' Scharr算子 ''' + """Scharr算子""" global picSize, img_gray, img_result initWindows() if img_gray is None: - showwarning(title='警告', message='请先在文件菜单下打开图片!') + showwarning(title="警告", message="请先在文件菜单下打开图片!") return - imgExplain = cv2.imread(r'.\explain\scharr.jpg', 0) - text="Scharr算子是对Sobel算子差异性的增强,Scharr算子的主要思路是通过将模版中的权重系数放大来增大像素值间的差异" + imgExplain = cv2.imread(r".\explain\scharr.jpg", 0) + text = "Scharr算子是对Sobel算子差异性的增强,Scharr算子的主要思路是通过将模版中的权重系数放大来增大像素值间的差异" picLists = [["Scharr算子模板", imgExplain]] - myWindow.showExplain(text,picLists) + myWindow.showExplain(text, picLists) placePic1(img_gray, "原灰度图像") img_result = SpatialFilter.scharr_filter(img_gray.copy()) placePic2(img_result) myWindow.resultText.set("Scharr锐化") + def Prewitt_filter(): - ''' Prewitt算子 ''' + """Prewitt算子""" global picSize, img_gray, img_result initWindows() if img_gray is None: - showwarning(title='警告', message='请先在文件菜单下打开图片!') + showwarning(title="警告", message="请先在文件菜单下打开图片!") return - imgExplain = cv2.imread(r'.\explain\prewitt.jpg', 0) - text="Prewitt算子将模板大小从2x2扩大到3x3,使滤波操作在锐化图像边缘的同时能减少噪声的影响。" + imgExplain = cv2.imread(r".\explain\prewitt.jpg", 0) + text = "Prewitt算子将模板大小从2x2扩大到3x3,使滤波操作在锐化图像边缘的同时能减少噪声的影响。" picLists = [["Prewitt算子模板", imgExplain]] - myWindow.showExplain(text,picLists) + myWindow.showExplain(text, picLists) placePic1(img_gray, "原灰度图像") img_result = SpatialFilter.prewitt_filter(img_gray.copy()) placePic2(img_result) myWindow.resultText.set("Prewitt锐化") + def Laplacian_filter(): - ''' Laplacian算子 ''' + """Laplacian算子""" global picSize, img_gray, img_result initWindows() if img_gray is None: - showwarning(title='警告', message='请先在文件菜单下打开图片!') + showwarning(title="警告", message="请先在文件菜单下打开图片!") return - imgExplain1 = cv2.imread(r'.\explain\laplacian1.jpg', 0) - imgExplain2 = cv2.imread(r'.\explain\laplacian2.jpg', 0) - picLists = [["Laplacian是二阶微分算子,以下为四邻域模板", imgExplain1],["Laplacian的八邻域模板具有方向同性", imgExplain2]] + imgExplain1 = cv2.imread(r".\explain\laplacian1.jpg", 0) + imgExplain2 = cv2.imread(r".\explain\laplacian2.jpg", 0) + picLists = [ + ["Laplacian是二阶微分算子,以下为四邻域模板", imgExplain1], + ["Laplacian的八邻域模板具有方向同性", imgExplain2], + ] myWindow.showFig1(picLists) placePic1(img_gray, "原灰度图像") - select=askinteger("模板选择","请选择1--四邻域,2--八邻域", initialvalue=1) - if select==1: - img_result = SpatialFilter.laplacian_filter(img_gray.copy(),1) - elif select==2: + select = askinteger("模板选择", "请选择1--四邻域,2--八邻域", initialvalue=1) + if select == 1: + img_result = SpatialFilter.laplacian_filter(img_gray.copy(), 1) + elif select == 2: img_result = SpatialFilter.laplacian_filter(img_gray.copy(), 2) else: - showwarning(title='警告', message='必须在两个模板中选择一个!') + showwarning(title="警告", message="必须在两个模板中选择一个!") return placePic2(img_result) myWindow.resultText.set("laplacian锐化") + def Log_filter(): - ''' log算子 ''' + """log算子""" global picSize, img_gray, img_result initWindows() if img_gray is None: - showwarning(title='警告', message='请先在文件菜单下打开图片!') + showwarning(title="警告", message="请先在文件菜单下打开图片!") return - imgExplain = cv2.imread(r'.\explain\log.jpg', 0) - text="LOG算子实际上是把 Gauss滤波和Laplacian滤波结合了起来,先平滑掉噪声,再进行边缘检测。" + imgExplain = cv2.imread(r".\explain\log.jpg", 0) + text = "LOG算子实际上是把 Gauss滤波和Laplacian滤波结合了起来,先平滑掉噪声,再进行边缘检测。" picLists = [["5x5的LOG算子模板", imgExplain]] - myWindow.showExplain(text,picLists) + myWindow.showExplain(text, picLists) placePic1(img_gray, "原灰度图像") img_result = SpatialFilter.log_filter(img_gray.copy()) - placePic2(img_result,"LOG锐化") + placePic2(img_result, "LOG锐化") + def Canny_filter(): - ''' Canny算子 ''' + """Canny算子""" global picSize, img_gray, img_result initWindows() if img_gray is None: - showwarning(title='警告', message='请先在文件菜单下打开图片!') + showwarning(title="警告", message="请先在文件菜单下打开图片!") return - myWindow.hideFig1() #隐藏画布 - myWindow.explainText.set('''Canny边缘检测算子是一种多级检测算法。1986年由John F.Canny提出,同时提出了边缘检测的三大准则: + myWindow.hideFig1() # 隐藏画布 + myWindow.explainText.set( + """Canny边缘检测算子是一种多级检测算法。1986年由John F.Canny提出,同时提出了边缘检测的三大准则: •低错误率的边缘检测:检测算法应该精确地找到图像中的尽可能多的边缘,尽可能的减少漏检和误检。 •最优定位:检测的边缘点应该精确地定位于边缘的中心。 •图像中的任意边缘应该只被标记一次,同时图像噪声不应产生伪边缘。 -''') +""" + ) placePic1(img_gray, "原灰度图像") img_result = SpatialFilter.canny_filter(img_gray.copy()) placePic2(img_result) myWindow.resultText.set("canny锐化") -#--------------------------------------------------------- + + +# --------------------------------------------------------- def Magnitude_spectrum0(): - ''' 幅值谱 ''' + """幅值谱""" global picSize, img_gray, img_result initWindows() if img_gray is None: - showwarning(title='警告', message='请先在文件菜单下打开图片!') + showwarning(title="警告", message="请先在文件菜单下打开图片!") return - imgExplain=cv2.imread(r'.\explain\magnitude.jpg',0) - text="为了提高幅值谱的可视化程度,对幅值谱按公式y=20*log(1+|F(u,v)|)进行了非线性变换" + imgExplain = cv2.imread(r".\explain\magnitude.jpg", 0) + text = ( + "为了提高幅值谱的可视化程度,对幅值谱按公式y=20*log(1+|F(u,v)|)进行了非线性变换" + ) picLists = [["幅值谱计算公式", imgExplain]] - myWindow.showExplain(text,picLists) + myWindow.showExplain(text, picLists) placePic1(img_gray, "原灰度图像") dft = Fourier.fft(img_gray) - img_result=Fourier.fft_magnitude(dft) - placePic2(img_result,"高频居中的幅值谱") + img_result = Fourier.fft_magnitude(dft) + placePic2(img_result, "高频居中的幅值谱") + def Magnitude_spectrum1(): - ''' 低频移中的幅值谱 ''' + """低频移中的幅值谱""" global picSize, img_gray, img_result initWindows() if img_gray is None: - showwarning(title='警告', message='请先在文件菜单下打开图片!') + showwarning(title="警告", message="请先在文件菜单下打开图片!") return - imgExplain = cv2.imread(r'.\explain\translation.jpg', 0) - text="图像傅里叶变换的高频区是在中心点附近,而低频区在四个角。\n" \ - "但人们通常习惯将低频信号集中在中心点附近,为此我们需要对傅里叶变换进行平移。" \ - "\n傅里叶变换的平移性:频域的平移相当于空域中乘以一个复指数\n" \ - "因此在空域将像素点乘以(-1)^(x+y),可以实现低频移中" + imgExplain = cv2.imread(r".\explain\translation.jpg", 0) + text = ( + "图像傅里叶变换的高频区是在中心点附近,而低频区在四个角。\n" + "但人们通常习惯将低频信号集中在中心点附近,为此我们需要对傅里叶变换进行平移。" + "\n傅里叶变换的平移性:频域的平移相当于空域中乘以一个复指数\n" + "因此在空域将像素点乘以(-1)^(x+y),可以实现低频移中" + ) picLists = [["傅里叶变换的平移性", imgExplain]] - myWindow.showExplain(text,picLists) + myWindow.showExplain(text, picLists) placePic1(img_gray, "原灰度图像") - dft = Fourier.fft(img_gray,True) - img_result=Fourier.fft_magnitude(dft) - placePic2(img_result,"低频移中的幅值谱") + dft = Fourier.fft(img_gray, True) + img_result = Fourier.fft_magnitude(dft) + placePic2(img_result, "低频移中的幅值谱") + def Phase_spectrum0(): - ''' 相位谱 ''' + """相位谱""" global picSize, img_gray, img_result initWindows() if img_gray is None: - showwarning(title='警告', message='请先在文件菜单下打开图片!') + showwarning(title="警告", message="请先在文件菜单下打开图片!") return - imgExplain = cv2.imread(r'.\explain\phase.jpg', 0) - text="傅里叶变换的幅值谱是对信号轮廓和形状的描述,而相位谱是对信号位置的描述。\n" \ - "不同位置相同形状的信号,其幅值谱相同,而相位谱不同" + imgExplain = cv2.imread(r".\explain\phase.jpg", 0) + text = ( + "傅里叶变换的幅值谱是对信号轮廓和形状的描述,而相位谱是对信号位置的描述。\n" + "不同位置相同形状的信号,其幅值谱相同,而相位谱不同" + ) picLists = [["相位谱计算公式:", imgExplain]] - myWindow.showExplain(text,picLists) + myWindow.showExplain(text, picLists) placePic1(img_gray, "原灰度图像") dft = Fourier.fft(img_gray) - img_result=Fourier.fft_phase(dft) - placePic2(img_result,"高频居中的相位谱") + img_result = Fourier.fft_phase(dft) + placePic2(img_result, "高频居中的相位谱") + def Phase_spectrum1(): - ''' 低频移中的相位谱 ''' + """低频移中的相位谱""" global picSize, img_gray, img_result initWindows() if img_gray is None: - showwarning(title='警告', message='请先在文件菜单下打开图片!') + showwarning(title="警告", message="请先在文件菜单下打开图片!") return - imgExplain = cv2.imread(r'.\explain\phase.jpg', 0) - text="傅里叶变换的幅值谱是对信号轮廓和形状的描述,而相位谱是对信号位置的描述。\n" \ - "不同位置相同形状的信号,其幅值谱相同,而相位谱不同" + imgExplain = cv2.imread(r".\explain\phase.jpg", 0) + text = ( + "傅里叶变换的幅值谱是对信号轮廓和形状的描述,而相位谱是对信号位置的描述。\n" + "不同位置相同形状的信号,其幅值谱相同,而相位谱不同" + ) picLists = [["相位谱计算公式:", imgExplain]] - myWindow.showExplain(text,picLists) + myWindow.showExplain(text, picLists) placePic1(img_gray, "原灰度图像") - dft = Fourier.fft(img_gray,True) - img_result=Fourier.fft_phase(dft) - placePic2(img_result,"低频移中的相位谱") + dft = Fourier.fft(img_gray, True) + img_result = Fourier.fft_phase(dft) + placePic2(img_result, "低频移中的相位谱") + def Inverse_fourier(): - ''' 傅里叶反变换 ''' + """傅里叶反变换""" global picSize, img_gray, img_result initWindows() if img_gray is None: - showwarning(title='警告', message='请先在文件菜单下打开图片!') + showwarning(title="警告", message="请先在文件菜单下打开图片!") return placePic1(img_gray, "原灰度图像") - dft=Fourier.fft(img_gray.copy()) - img_result=Fourier.ifft(dft) # 由于傅里叶变换期间,使用了最优尺寸,反变换时需要恢复原图像大小 - placePic2(img_result,"傅里叶反变换") - myWindow.explainText.set("此操作是先对图像进行傅里叶变换,再进行逆变换。\n"\ - "根据图像与其傅里叶变换之间的一一对应关系,重构的图像应该与原图像相同。\n"\ - "但由于傅里叶变换期间,根据快速傅里叶变换要求,对图像大小进行了优化,\n"\ - "反变换后生成图像大小可能会有微小变化") + dft = Fourier.fft(img_gray.copy()) + img_result = Fourier.ifft( + dft + ) #  由于傅里叶变换期间,使用了最优尺寸,反变换时需要恢复原图像大小 + placePic2(img_result, "傅里叶反变换") + myWindow.explainText.set( + "此操作是先对图像进行傅里叶变换,再进行逆变换。\n" + "根据图像与其傅里叶变换之间的一一对应关系,重构的图像应该与原图像相同。\n" + "但由于傅里叶变换期间,根据快速傅里叶变换要求,对图像大小进行了优化,\n" + "反变换后生成图像大小可能会有微小变化" + ) + + def Property_fourier(): - ''' 添加周期噪声 ''' + """添加周期噪声""" global picSize, img_gray, img_result initWindows() - myWindow.explainText.set("所添加的噪声频点的位置决定了条纹的宽度和方向。\n" \ - "噪声频点应该是圆点对称的,加圆圈标注。\n" \ - "噪声频点越靠近中心,噪声频率越低,条纹越宽;反之条纹越细。\n" \ - "若在某方向上添加了噪声频点,意味着在这个方向上灰度变化较大,条纹是垂直于这个方向。") + myWindow.explainText.set( + "所添加的噪声频点的位置决定了条纹的宽度和方向。\n" + "噪声频点应该是圆点对称的,加圆圈标注。\n" + "噪声频点越靠近中心,噪声频率越低,条纹越宽;反之条纹越细。\n" + "若在某方向上添加了噪声频点,意味着在这个方向上灰度变化较大,条纹是垂直于这个方向。" + ) if img_gray is None: - showwarning(title='警告', message='请先在文件菜单下打开图片!') + showwarning(title="警告", message="请先在文件菜单下打开图片!") return # 将图片大小变为奇数的,这样有图片中心 m, n = img_gray.shape - if m%2==0: - m=m+1 - if n%2==0: - n=n+1 + if m % 2 == 0: + m = m + 1 + if n % 2 == 0: + n = n + 1 img = cv2.resize(img_gray, (m, n)) F = np.fft.fft2(img) fshift0 = np.fft.fftshift(F) @@ -861,7 +1050,10 @@ def Property_fourier(): fmin = np.min(np.abs(fshift0)) # 图片中心为u0,v0 u0, v0 = (m - 1) // 2, (n - 1) // 2 - paraLists = [["孤立频点距离中心的水平距离", -10], ["孤立频点距离中心的垂直距离", 10]] + paraLists = [ + ["孤立频点距离中心的水平距离", -10], + ["孤立频点距离中心的垂直距离", 10], + ] paraW = paraWindow(root, "请设置孤立的噪声频点在频谱上的位置", paraLists) r1 = int(paraW.paraLists[0][1]) r2 = int(paraW.paraLists[1][1]) @@ -884,623 +1076,839 @@ def Property_fourier(): # cv2.putText(img2, "注意条纹的宽度和方向!!!", (300, 300), font, 1.5, (255, 255, 255), 2) placePic2(img1, "带周期噪声的图像\n注意孤立的噪声频点与条纹的宽度、方向的关系!") + def Idle_low_pass(): - ''' 理想低通滤波器 ''' + """理想低通滤波器""" global picSize, img_gray, img_result initWindows() if img_gray is None: - showwarning(title='警告', message='请先在文件菜单下打开图片!') + showwarning(title="警告", message="请先在文件菜单下打开图片!") return - imgExplain0 = cv2.imread(r'.\explain\ILPF0.jpg', 0) - imgExplain1 = cv2.imread(r'.\explain\ILPF.jpg', 0) - picLists = [["理想低通滤波器是将小于截止频率的分量完全\n无损地通过,而大于截止频率的分量完全衰减。", imgExplain0],["理想低通滤波器示意图", imgExplain1]] + imgExplain0 = cv2.imread(r".\explain\ILPF0.jpg", 0) + imgExplain1 = cv2.imread(r".\explain\ILPF.jpg", 0) + picLists = [ + [ + "理想低通滤波器是将小于截止频率的分量完全\n无损地通过,而大于截止频率的分量完全衰减。", + imgExplain0, + ], + ["理想低通滤波器示意图", imgExplain1], + ] myWindow.showFig1(picLists) placePic1(img_gray, "原灰度图像") - row,col=img_gray.shape - maxD0=min(row,col)//2 - d0 = askinteger("理想低通滤波器参数设置","请设置截止频率,其值在["+str(1)+'~'+str(maxD0)+']之间',initialvalue=(1+maxD0)//2) - if d0 and maxD0>=d0>=1: + row, col = img_gray.shape + maxD0 = min(row, col) // 2 + d0 = askinteger( + "理想低通滤波器参数设置", + "请设置截止频率,其值在[" + str(1) + "~" + str(maxD0) + "]之间", + initialvalue=(1 + maxD0) // 2, + ) + if d0 and maxD0 >= d0 >= 1: img_result = Fourier.lpfilter(img_gray, 0, d0) placePic2(img_result, "理想的低通滤波") else: - showwarning("警告","截止频率的值应该在["+str(1)+'~'+str(maxD0)+']之间') + showwarning("警告", "截止频率的值应该在[" + str(1) + "~" + str(maxD0) + "]之间") + def Butterworth_low_pass(): - ''' 巴特沃兹低通滤波器 ''' + """巴特沃兹低通滤波器""" global picSize, img_gray, img_result initWindows() if img_gray is None: - showwarning(title='警告', message='请先在文件菜单下打开图片!') + showwarning(title="警告", message="请先在文件菜单下打开图片!") return - imgExplain1 = cv2.imread(r'.\explain\Butterworth0.jpg', 0) - imgExplain2 = cv2.imread(r'.\explain\Butterworth1.jpg', 0) - picLists = [["巴特沃斯低通滤波器的通带与阻带之间没有明显的\n不连续性。n阶巴特沃斯低通滤波器的传递函数为:", imgExplain1],["巴特沃斯低通滤波器的示意图", imgExplain2]] + imgExplain1 = cv2.imread(r".\explain\Butterworth0.jpg", 0) + imgExplain2 = cv2.imread(r".\explain\Butterworth1.jpg", 0) + picLists = [ + [ + "巴特沃斯低通滤波器的通带与阻带之间没有明显的\n不连续性。n阶巴特沃斯低通滤波器的传递函数为:", + imgExplain1, + ], + ["巴特沃斯低通滤波器的示意图", imgExplain2], + ] myWindow.showFig1(picLists) placePic1(img_gray, "原灰度图像") - row,col=img_gray.shape - maxD0=min(row,col)//2 - d0 = askinteger("巴特沃兹低通滤波器参数设置","请设置截止频率,其值在["+str(1)+'~'+str(maxD0)+']之间',initialvalue=(1+maxD0)//2) - if d0 is None or d0>maxD0 or d0<10: - showwarning("警告","截止频率的值应该在["+str(10)+'~'+str(maxD0)+']之间') + row, col = img_gray.shape + maxD0 = min(row, col) // 2 + d0 = askinteger( + "巴特沃兹低通滤波器参数设置", + "请设置截止频率,其值在[" + str(1) + "~" + str(maxD0) + "]之间", + initialvalue=(1 + maxD0) // 2, + ) + if d0 is None or d0 > maxD0 or d0 < 10: + showwarning( + "警告", "截止频率的值应该在[" + str(10) + "~" + str(maxD0) + "]之间" + ) return - nj = askinteger("巴特沃兹低通滤波器参数设置","请设置巴特沃兹低通滤波器的阶数,其值在[1~20]之间的整数",initialvalue=1) - if nj is None or nj>5 or nj<1: - showwarning("警告","巴特沃兹低通滤波器阶数应该在[1~5]之间") + nj = askinteger( + "巴特沃兹低通滤波器参数设置", + "请设置巴特沃兹低通滤波器的阶数,其值在[1~20]之间的整数", + initialvalue=1, + ) + if nj is None or nj > 5 or nj < 1: + showwarning("警告", "巴特沃兹低通滤波器阶数应该在[1~5]之间") return - img_result = Fourier.lpfilter(img_gray, 1, d0,nj) + img_result = Fourier.lpfilter(img_gray, 1, d0, nj) placePic2(img_result, "巴特沃兹低通滤波") + def Gaussian_low_pass(): - ''' 高斯低通滤波器 ''' + """高斯低通滤波器""" global picSize, img_gray, img_result initWindows() if img_gray is None: - showwarning(title='警告', message='请先在文件菜单下打开图片!') + showwarning(title="警告", message="请先在文件菜单下打开图片!") return - imgExplain1 = cv2.imread(r'.\explain\Gaussian0.jpg', 0) - imgExplain2 = cv2.imread(r'.\explain\Gaussian1.jpg', 0) - picLists = [["高斯低通滤波器在截止频率较小时,具有较陡峭的频率特征,\n随着截止频率的增大,高斯低通滤波器趋于平缓。", imgExplain1],["高斯低通滤波器的示意图", imgExplain2]] + imgExplain1 = cv2.imread(r".\explain\Gaussian0.jpg", 0) + imgExplain2 = cv2.imread(r".\explain\Gaussian1.jpg", 0) + picLists = [ + [ + "高斯低通滤波器在截止频率较小时,具有较陡峭的频率特征,\n随着截止频率的增大,高斯低通滤波器趋于平缓。", + imgExplain1, + ], + ["高斯低通滤波器的示意图", imgExplain2], + ] myWindow.showFig1(picLists) placePic1(img_gray, "原灰度图像") row, col = img_gray.shape maxD0 = min(row, col) // 4 - d0 = askinteger("高斯低通滤波器参数设置", "请设置截止频率,其值在[" + str(10) + '~' + str(maxD0) + ']之间',initialvalue=(10+maxD0)//2) + d0 = askinteger( + "高斯低通滤波器参数设置", + "请设置截止频率,其值在[" + str(10) + "~" + str(maxD0) + "]之间", + initialvalue=(10 + maxD0) // 2, + ) if d0 is None or d0 > maxD0 or d0 < 10: - showwarning("警告", "截止频率的值应该在[" + str(10) + '~' + str(maxD0) + ']之间') + showwarning( + "警告", "截止频率的值应该在[" + str(10) + "~" + str(maxD0) + "]之间" + ) return img_result = Fourier.lpfilter(img_gray, 2, d0) placePic2(img_result, "高斯低通滤波") + def Idle_high_pass(): - ''' 理想高通滤波器 ''' + """理想高通滤波器""" global picSize, img_gray, img_result initWindows() if img_gray is None: - showwarning(title='警告', message='请先在文件菜单下打开图片!') + showwarning(title="警告", message="请先在文件菜单下打开图片!") return - imgExplain0=cv2.imread(r'.\explain\Ideal_High0.jpg',0) - imgExplain1 = cv2.imread(r'.\explain\Ideal_High1.jpg', 0) - picLists = [["理想高通滤波器是将大于截止频率的分量完全\n无损地通过,而小于截止频率的分量完全衰减。", imgExplain0],["理想高通滤波器示意图", imgExplain1]] - myWindow.showFig1(picLists) - placePic1(img_gray, "原灰度图像") - row,col=img_gray.shape - maxD0=min(row,col)//4 - d0 = askinteger("理想高通滤波器参数设置","请设置截止频率,其值在["+str(1)+'~'+str(maxD0)+']之间',initialvalue=(10+maxD0)//2) - if d0 and maxD0>=d0>=1: - img_result = Fourier.hpfilter(img_gray, 0, d0) - placePic2(img_result, "理想的高通滤波") - else: - showwarning("警告","截止频率的值应该在["+str(1)+'~'+str(maxD0)+']之间') - -def Butterworth_high_pass(): - ''' 巴特沃兹高通滤波器 ''' - global picSize, img_gray, img_result - initWindows() - if img_gray is None: - showwarning(title='警告', message='请先在文件菜单下打开图片!') - return - imgExplain1 = cv2.imread(r'.\explain\Butterworth_high0.jpg', 0) - imgExplain2 = cv2.imread(r'.\explain\Butterworth_high1.jpg', 0) - picLists = [["巴特沃兹高通滤波器的传递函数为:", imgExplain1],["巴特沃斯高通滤波器的示意图", imgExplain2]] - myWindow.showFig1(picLists) - placePic1(img_gray, "原灰度图像") - row,col=img_gray.shape - maxD0=min(row,col)//4 - d0 = askinteger("巴特沃兹高通滤波器参数设置","请设置截止频率,其值在["+str(1)+'~'+str(maxD0)+']之间',initialvalue=(10+maxD0)//2) - if d0 is None or d0>maxD0 or d0<1: - showwarning("警告","截止频率的值应该在["+str(1)+'~'+str(maxD0)+']之间') - return - nj = askinteger("巴特沃兹高通滤波器参数设置","请设置巴特沃兹低通滤波器的阶数,其值在[1~5]之间",initialvalue=1) - if nj is None or nj>5 or nj<1: - showwarning("警告","巴特沃兹高通滤波器阶数应该在[1~5]之间") - return - img_result = Fourier.hpfilter(img_gray, 1, d0,nj) - placePic2(img_result, "巴特沃兹高通滤波") - -def Gaussian_high_pass(): - ''' 高斯高通滤波器 ''' - global picSize, img_gray, img_result - initWindows() - if img_gray is None: - showwarning(title='警告', message='请先在文件菜单下打开图片!') - return - imgExplain1 = cv2.imread(r'.\explain\Gaussian_high0.jpg', 0) - imgExplain2 = cv2.imread(r'.\explain\Gaussian_high1.jpg', 0) - picLists = [["高斯高通滤波器传递函数为:", imgExplain1],["高斯高通滤波器的示意图", imgExplain2]] + imgExplain0 = cv2.imread(r".\explain\Ideal_High0.jpg", 0) + imgExplain1 = cv2.imread(r".\explain\Ideal_High1.jpg", 0) + picLists = [ + [ + "理想高通滤波器是将大于截止频率的分量完全\n无损地通过,而小于截止频率的分量完全衰减。", + imgExplain0, + ], + ["理想高通滤波器示意图", imgExplain1], + ] myWindow.showFig1(picLists) placePic1(img_gray, "原灰度图像") row, col = img_gray.shape maxD0 = min(row, col) // 4 - d0 = askinteger("高斯高通滤波器参数设置", "请设置截止频率,其值在[" + str(10) + '~' + str(maxD0) + ']之间',initialvalue=(10+maxD0)//2) + d0 = askinteger( + "理想高通滤波器参数设置", + "请设置截止频率,其值在[" + str(1) + "~" + str(maxD0) + "]之间", + initialvalue=(10 + maxD0) // 2, + ) + if d0 and maxD0 >= d0 >= 1: + img_result = Fourier.hpfilter(img_gray, 0, d0) + placePic2(img_result, "理想的高通滤波") + else: + showwarning("警告", "截止频率的值应该在[" + str(1) + "~" + str(maxD0) + "]之间") + + +def Butterworth_high_pass(): + """巴特沃兹高通滤波器""" + global picSize, img_gray, img_result + initWindows() + if img_gray is None: + showwarning(title="警告", message="请先在文件菜单下打开图片!") + return + imgExplain1 = cv2.imread(r".\explain\Butterworth_high0.jpg", 0) + imgExplain2 = cv2.imread(r".\explain\Butterworth_high1.jpg", 0) + picLists = [ + ["巴特沃兹高通滤波器的传递函数为:", imgExplain1], + ["巴特沃斯高通滤波器的示意图", imgExplain2], + ] + myWindow.showFig1(picLists) + placePic1(img_gray, "原灰度图像") + row, col = img_gray.shape + maxD0 = min(row, col) // 4 + d0 = askinteger( + "巴特沃兹高通滤波器参数设置", + "请设置截止频率,其值在[" + str(1) + "~" + str(maxD0) + "]之间", + initialvalue=(10 + maxD0) // 2, + ) + if d0 is None or d0 > maxD0 or d0 < 1: + showwarning("警告", "截止频率的值应该在[" + str(1) + "~" + str(maxD0) + "]之间") + return + nj = askinteger( + "巴特沃兹高通滤波器参数设置", + "请设置巴特沃兹低通滤波器的阶数,其值在[1~5]之间", + initialvalue=1, + ) + if nj is None or nj > 5 or nj < 1: + showwarning("警告", "巴特沃兹高通滤波器阶数应该在[1~5]之间") + return + img_result = Fourier.hpfilter(img_gray, 1, d0, nj) + placePic2(img_result, "巴特沃兹高通滤波") + + +def Gaussian_high_pass(): + """高斯高通滤波器""" + global picSize, img_gray, img_result + initWindows() + if img_gray is None: + showwarning(title="警告", message="请先在文件菜单下打开图片!") + return + imgExplain1 = cv2.imread(r".\explain\Gaussian_high0.jpg", 0) + imgExplain2 = cv2.imread(r".\explain\Gaussian_high1.jpg", 0) + picLists = [ + ["高斯高通滤波器传递函数为:", imgExplain1], + ["高斯高通滤波器的示意图", imgExplain2], + ] + myWindow.showFig1(picLists) + placePic1(img_gray, "原灰度图像") + row, col = img_gray.shape + maxD0 = min(row, col) // 4 + d0 = askinteger( + "高斯高通滤波器参数设置", + "请设置截止频率,其值在[" + str(10) + "~" + str(maxD0) + "]之间", + initialvalue=(10 + maxD0) // 2, + ) if d0 is None or d0 > maxD0 or d0 < 10: - showwarning("警告", "截止频率的值应该在[" + str(10) + '~' + str(maxD0) + ']之间') + showwarning( + "警告", "截止频率的值应该在[" + str(10) + "~" + str(maxD0) + "]之间" + ) return img_result = Fourier.hpfilter(img_gray, 2, d0) placePic2(img_result, "高斯高通滤波") -#-------------------------------------------------------- + + +# -------------------------------------------------------- def Move(): - ''' 几何变换——图像平移 ''' + """几何变换——图像平移""" global picSize, img_open, img_result initWindows() if img_open is None: - showwarning(title='警告', message='请先在文件菜单下打开图片!') + showwarning(title="警告", message="请先在文件菜单下打开图片!") return - imgExplain = cv2.imread(r'.\explain\move.jpg', 0) - text="图像平移就是将图像中所有的像素点按照指定的平移量水平或者垂直移动。\n图像平移后,出现的空白区域可以统一设置为0或255。" + imgExplain = cv2.imread(r".\explain\move.jpg", 0) + text = "图像平移就是将图像中所有的像素点按照指定的平移量水平或者垂直移动。\n图像平移后,出现的空白区域可以统一设置为0或255。" picLists = [["平移变换矩阵", imgExplain]] myWindow.showExplain(text, picLists) placePic1(img_open, "原图像") - paraLists=[["水平平移距离",10],["垂直平移距离",10]] - paraW=paraWindow(root,"请设置水平和垂直方向的平移距离",paraLists) - img_result=geometric.move(img_open.copy(),paraW.paraLists[0][1],paraW.paraLists[1][1]) - if len(img_result.shape)>2: - img_result=cv2.cvtColor(img_result,cv2.COLOR_BGR2RGB) - placePic2(img_result,"图像平移") + paraLists = [["水平平移距离", 10], ["垂直平移距离", 10]] + paraW = paraWindow(root, "请设置水平和垂直方向的平移距离", paraLists) + img_result = geometric.move( + img_open.copy(), paraW.paraLists[0][1], paraW.paraLists[1][1] + ) + if len(img_result.shape) > 2: + img_result = cv2.cvtColor(img_result, cv2.COLOR_BGR2RGB) + placePic2(img_result, "图像平移") + def Rotate(): - ''' 几何变换——图像旋转 ''' + """几何变换——图像旋转""" global picSize, img_open, img_result initWindows() if img_open is None: - showwarning(title='警告', message='请先在文件菜单下打开图片!') + showwarning(title="警告", message="请先在文件菜单下打开图片!") return - imgExplain = cv2.imread(r'.\explain\rotate.jpg', 0) - text="图像的旋转是以图像的中心为原点,旋转一定的角度,即将图像上的所有像素都旋转一个相同的角度。" + imgExplain = cv2.imread(r".\explain\rotate.jpg", 0) + text = "图像的旋转是以图像的中心为原点,旋转一定的角度,即将图像上的所有像素都旋转一个相同的角度。" picLists = [["旋转变换矩阵", imgExplain]] myWindow.showExplain(text, picLists) placePic1(img_open, "原图像") - angle=askfloat("设置旋转角度","请输入旋转角度",initialvalue=45) - img_result=geometric.rotate(img_open.copy(),angle) - if len(img_result.shape)>2: - img_result=cv2.cvtColor(img_result,cv2.COLOR_BGR2RGB) - placePic2(img_result,"图像旋转") + angle = askfloat("设置旋转角度", "请输入旋转角度", initialvalue=45) + img_result = geometric.rotate(img_open.copy(), angle) + if len(img_result.shape) > 2: + img_result = cv2.cvtColor(img_result, cv2.COLOR_BGR2RGB) + placePic2(img_result, "图像旋转") + def Reflect_x(): - ''' 几何变换——图像垂直镜像 ''' + """几何变换——图像垂直镜像""" global picSize, img_open, img_result initWindows() if img_open is None: - showwarning(title='警告', message='请先在文件菜单下打开图片!') + showwarning(title="警告", message="请先在文件菜单下打开图片!") return - imgExplain = cv2.imread(r'.\explain\v-reflect.jpg', 0) - text="垂直镜像是将图像上半部分和下半部分以图像水平中轴线为中心轴进行对换。" + imgExplain = cv2.imread(r".\explain\v-reflect.jpg", 0) + text = "垂直镜像是将图像上半部分和下半部分以图像水平中轴线为中心轴进行对换。" picLists = [["垂直镜像变换矩阵", imgExplain]] myWindow.showExplain(text, picLists) placePic1(img_open, "原图像") - img_result=geometric.reflect_x(img_open.copy()) - if len(img_result.shape)>2: - img_result=cv2.cvtColor(img_result,cv2.COLOR_BGR2RGB) - placePic2(img_result,"图像垂直镜像") + img_result = geometric.reflect_x(img_open.copy()) + if len(img_result.shape) > 2: + img_result = cv2.cvtColor(img_result, cv2.COLOR_BGR2RGB) + placePic2(img_result, "图像垂直镜像") + def Reflect_y(): - ''' 几何变换——图像水平镜像 ''' + """几何变换——图像水平镜像""" global picSize, img_open, img_result initWindows() if img_open is None: - showwarning(title='警告', message='请先在文件菜单下打开图片!') + showwarning(title="警告", message="请先在文件菜单下打开图片!") return - imgExplain = cv2.imread(r'.\explain\h-reflect.jpg', 0) - text="水平镜像即将图像左半部分和右半部分以图像垂直中轴线为中心轴进行对换" + imgExplain = cv2.imread(r".\explain\h-reflect.jpg", 0) + text = "水平镜像即将图像左半部分和右半部分以图像垂直中轴线为中心轴进行对换" picLists = [["水平镜像变换矩阵", imgExplain]] myWindow.showExplain(text, picLists) placePic1(img_open, "原图像") - img_result=geometric.reflect_y(img_open.copy()) - if len(img_result.shape)>2: - img_result=cv2.cvtColor(img_result,cv2.COLOR_BGR2RGB) - placePic2(img_result,"图像水平镜像") + img_result = geometric.reflect_y(img_open.copy()) + if len(img_result.shape) > 2: + img_result = cv2.cvtColor(img_result, cv2.COLOR_BGR2RGB) + placePic2(img_result, "图像水平镜像") + def Zoom(): - ''' 几何变换——图像缩放 ''' + """几何变换——图像缩放""" global picSize, img_open, img_result initWindows() if img_open is None: - showwarning(title='警告', message='请先在文件菜单下打开图片!') + showwarning(title="警告", message="请先在文件菜单下打开图片!") return - imgExplain = cv2.imread(r'.\explain\zoom.jpg', 0) - text="图像缩放是指将给定的图像在x方向和y方向按不同的比例缩放" + imgExplain = cv2.imread(r".\explain\zoom.jpg", 0) + text = "图像缩放是指将给定的图像在x方向和y方向按不同的比例缩放" picLists = [["缩放变换矩阵", imgExplain]] myWindow.showExplain(text, picLists) placePic1(img_open, "原图像") - paraLists=[["水平方向缩放因子",1],["垂直方向缩放因子",1]] - paraW=paraWindow(root,"请设置水平和垂直方向的缩放因子",paraLists) - img_result=geometric.zoom(img_open.copy(),paraW.paraLists[0][1],paraW.paraLists[1][1]) - if len(img_result.shape)>2: - img_result=cv2.cvtColor(img_result,cv2.COLOR_BGR2RGB) - placePic2(img_result,"图像缩放") + paraLists = [["水平方向缩放因子", 1], ["垂直方向缩放因子", 1]] + paraW = paraWindow(root, "请设置水平和垂直方向的缩放因子", paraLists) + img_result = geometric.zoom( + img_open.copy(), paraW.paraLists[0][1], paraW.paraLists[1][1] + ) + if len(img_result.shape) > 2: + img_result = cv2.cvtColor(img_result, cv2.COLOR_BGR2RGB) + placePic2(img_result, "图像缩放") + def Affine(): - ''' 几何变换——图像仿射变换 ''' + """几何变换——图像仿射变换""" global picSize, img_open, img_result initWindows() if img_open is None: - showwarning(title='警告', message='请先在文件菜单下打开图片!') + showwarning(title="警告", message="请先在文件菜单下打开图片!") return placePic1(img_open, "原图像") - explainPic=cv2.imread(r'.\explain\affine.jpg') - text="仿射变换是一种线性变换,它保持了二维图形的“平直性”。根据变换前后的三角形坐标,可确定仿射变换矩阵" + explainPic = cv2.imread(r".\explain\affine.jpg") + text = "仿射变换是一种线性变换,它保持了二维图形的“平直性”。根据变换前后的三角形坐标,可确定仿射变换矩阵" picLists = [["仿射变换前后的三对坐标点", explainPic]] - myWindow.showExplain(text,picLists) - paraLists = [[50, 50, 10,100], [200, 50, 200, 50], [50, 200, 100, 250]] # 初始默认为3段 - paraW=paraWindow2(root,paraLists,"仿射变换参数设置","变换前三点坐标 变换后的目标坐标") - paraLists=paraW.paraLists - pts1=np.float32([paraLists[0][0:2],paraLists[1][0:2],paraLists[2][0:2]]) - pts2=np.float32([paraLists[0][2:4],paraLists[1][2:4],paraLists[2][2:4]]) - img_result = geometric.affine(img_open.copy(),pts1,pts2) - if len(img_result.shape)>2: - img_result=cv2.cvtColor(img_result,cv2.COLOR_BGR2RGB) + myWindow.showExplain(text, picLists) + paraLists = [ + [50, 50, 10, 100], + [200, 50, 200, 50], + [50, 200, 100, 250], + ] # 初始默认为3段 + paraW = paraWindow2( + root, paraLists, "仿射变换参数设置", "变换前三点坐标 变换后的目标坐标" + ) + paraLists = paraW.paraLists + pts1 = np.float32([paraLists[0][0:2], paraLists[1][0:2], paraLists[2][0:2]]) + pts2 = np.float32([paraLists[0][2:4], paraLists[1][2:4], paraLists[2][2:4]]) + img_result = geometric.affine(img_open.copy(), pts1, pts2) + if len(img_result.shape) > 2: + img_result = cv2.cvtColor(img_result, cv2.COLOR_BGR2RGB) placePic2(img_result, "仿射变换") + def Perspective(): - ''' 几何变换——图像透视变换 ''' + """几何变换——图像透视变换""" global picSize, img_open, img_result initWindows() if img_open is None: - showwarning(title='警告', message='请先在文件菜单下打开图片!') + showwarning(title="警告", message="请先在文件菜单下打开图片!") return placePic1(img_open, "原图像") - explainPic=cv2.imread(r'.\explain\perspective.jpg') - text="透视变换是将图片投影到一个新的视平面,也称作投影映射。图像中的直线经过后映射到另一幅图像上仍为直线,但平行关系基本不保持。根据变换前后的四点坐标,可确定透视变换矩阵" + explainPic = cv2.imread(r".\explain\perspective.jpg") + text = "透视变换是将图片投影到一个新的视平面,也称作投影映射。图像中的直线经过后映射到另一幅图像上仍为直线,但平行关系基本不保持。根据变换前后的四点坐标,可确定透视变换矩阵" picLists = [["透视变换前后的四对坐标点", explainPic]] - myWindow.showExplain(text,picLists) - paraLists = [[50, 50, 50, 50], [100, 50, 100, 50], [50, 100, 40, 90],[100, 100, 90, 110]] # 初始默认为3段 - paraW=paraWindow2(root,paraLists,"透视变换参数设置","变换前四点坐标 变换后的目标坐标") - paraLists=paraW.paraLists - pts1=np.float32([paraLists[0][0:2],paraLists[1][0:2],paraLists[2][0:2],paraLists[3][0:2]]) - pts2=np.float32([paraLists[0][2:4],paraLists[1][2:4],paraLists[2][2:4],paraLists[3][2:4]]) - img_result = geometric.perspective(img_open.copy(),pts1,pts2) - if len(img_result.shape)>2: - img_result=cv2.cvtColor(img_result,cv2.COLOR_BGR2RGB) + myWindow.showExplain(text, picLists) + paraLists = [ + [50, 50, 50, 50], + [100, 50, 100, 50], + [50, 100, 40, 90], + [100, 100, 90, 110], + ] # 初始默认为3段 + paraW = paraWindow2( + root, paraLists, "透视变换参数设置", "变换前四点坐标 变换后的目标坐标" + ) + paraLists = paraW.paraLists + pts1 = np.float32( + [paraLists[0][0:2], paraLists[1][0:2], paraLists[2][0:2], paraLists[3][0:2]] + ) + pts2 = np.float32( + [paraLists[0][2:4], paraLists[1][2:4], paraLists[2][2:4], paraLists[3][2:4]] + ) + img_result = geometric.perspective(img_open.copy(), pts1, pts2) + if len(img_result.shape) > 2: + img_result = cv2.cvtColor(img_result, cv2.COLOR_BGR2RGB) placePic2(img_result, "透视变换") + def Interpolation(): - ''' 图像灰度插值 ''' + """图像灰度插值""" global picSize, img_open, img_result initWindows() if img_open is None: - showwarning(title='警告', message='请先在文件菜单下打开图片!') + showwarning(title="警告", message="请先在文件菜单下打开图片!") return - img_=img_open.copy() - myWindow.explainText.set("图像放大后的四种插值方法比较。\nNEAREST:最近邻插值法;\nLINEAR:双线性插值法(默认); \nCUBIC:基于4x4像素邻域的3次插值法;\nAREA:基于局部像素的重采样") + img_ = img_open.copy() + myWindow.explainText.set( + "图像放大后的四种插值方法比较。\nNEAREST:最近邻插值法;\nLINEAR:双线性插值法(默认); \nCUBIC:基于4x4像素邻域的3次插值法;\nAREA:基于局部像素的重采样" + ) placePic1(img_, "原图像") - img_gray=cv2.cvtColor(img_, cv2.COLOR_BGR2GRAY) - n,m=img_gray.shape[:2] + img_gray = cv2.cvtColor(img_, cv2.COLOR_BGR2GRAY) + n, m = img_gray.shape[:2] paraLists = [["起点x坐标", 120], ["起点y坐标", 100]] paraW = paraWindow(root, "设置图像块的起点坐标", paraLists) - x0=int(paraW.paraLists[0][1]) - y0=int(paraW.paraLists[1][1]) - dx=25 - if x0+dx>=m or y0+dx>n: - showwarning(title='警告', message='图像大小为'+str(m)+'x'+str(n)+'设置的起点坐标超出图像范围范围') + x0 = int(paraW.paraLists[0][1]) + y0 = int(paraW.paraLists[1][1]) + dx = 25 + if x0 + dx >= m or y0 + dx > n: + showwarning( + title="警告", + message="图像大小为" + + str(m) + + "x" + + str(n) + + "设置的起点坐标超出图像范围范围", + ) return - cv2.rectangle(img_, (x0, y0), (x0+dx, y0+dx), (0, 0, 255), 2) + cv2.rectangle(img_, (x0, y0), (x0 + dx, y0 + dx), (0, 0, 255), 2) placePic1(img_, "标注放大区域图像") - img = img_gray[y0:y0+dx, x0:x0+dx] - img1 = cv2.resize(img,None,fx=10,fy=10,interpolation=cv2.INTER_NEAREST) - img2 = cv2.resize(img,None,fx=10,fy=10,interpolation=cv2.INTER_LINEAR) - img3 = cv2.resize(img,None,fx=10,fy=10,interpolation=cv2.INTER_CUBIC) - img4 = cv2.resize(img,None,fx=10,fy=10,interpolation=cv2.INTER_AREA) - picLists1=[['NEAREST',img1],['LINEAR',img2]] - picLists2=[['CUBIC', img3], ['AREA', img4]] - combinedImg1 = myWindow.combine_images(picLists1, explain=True, fontSize=1)#合并两张图片 - combinedImg2 = myWindow.combine_images(picLists2, explain=True, fontSize=1)#合并两张图片 - picLists=[combinedImg1,combinedImg2] - img_result=myWindow.combine_images(picLists) - if len(img_result.shape)>2: - img_result=cv2.cvtColor(img_result,cv2.COLOR_BGR2RGB) - placePic2(img_result,"放大图像后的四种插值比较") + img = img_gray[y0 : y0 + dx, x0 : x0 + dx] + img1 = cv2.resize(img, None, fx=10, fy=10, interpolation=cv2.INTER_NEAREST) + img2 = cv2.resize(img, None, fx=10, fy=10, interpolation=cv2.INTER_LINEAR) + img3 = cv2.resize(img, None, fx=10, fy=10, interpolation=cv2.INTER_CUBIC) + img4 = cv2.resize(img, None, fx=10, fy=10, interpolation=cv2.INTER_AREA) + picLists1 = [["NEAREST", img1], ["LINEAR", img2]] + picLists2 = [["CUBIC", img3], ["AREA", img4]] + combinedImg1 = myWindow.combine_images( + picLists1, explain=True, fontSize=1 + ) # 合并两张图片 + combinedImg2 = myWindow.combine_images( + picLists2, explain=True, fontSize=1 + ) # 合并两张图片 + picLists = [combinedImg1, combinedImg2] + img_result = myWindow.combine_images(picLists) + if len(img_result.shape) > 2: + img_result = cv2.cvtColor(img_result, cv2.COLOR_BGR2RGB) + placePic2(img_result, "放大图像后的四种插值比较") -#--------------------------------------------------------- + +# --------------------------------------------------------- def Add_gaussian_noise(): - ''' 添加高斯噪声 ''' + """添加高斯噪声""" global picSize, img_gray, img_result initWindows() if img_result is None: img_ = img_gray.copy() else: - answer = askyesno("选择待退化图像", "Y-对打开图像进行退化,N-继续对已退化图像进行退化") + answer = askyesno( + "选择待退化图像", "Y-对打开图像进行退化,N-继续对已退化图像进行退化" + ) if not answer: img_ = img_result.copy() else: img_ = img_gray.copy() - placePic1(img_, '待处理图像') - paraLists=[["均值",0],["方差",0.03]] - paraW=paraWindow(root,"设置高斯噪声的均值与方差",paraLists) + placePic1(img_, "待处理图像") + paraLists = [["均值", 0], ["方差", 0.03]] + paraW = paraWindow(root, "设置高斯噪声的均值与方差", paraLists) # paraW.show() - img_result=spatialRestore.add_gaussian_noise(img_,paraW.paraLists[0][1],paraW.paraLists[1][1]) + img_result = spatialRestore.add_gaussian_noise( + img_, paraW.paraLists[0][1], paraW.paraLists[1][1] + ) histr = Histogram.gray_histogram(img_result) - myWindow.showAndBarFig0(img_result,histr) + myWindow.showAndBarFig0(img_result, histr) myWindow.resultText.set("添加高斯噪声") # placePic2(img_result, "添加高斯噪声") # showinfo(title='提示', message='按任意键显示直方图,按回退键显示图像') # cv2.waitKey(0) # placePic1(img_result, "添加高斯噪声") - explainPic = cv2.imread(r'.\explain\gaosi.jpg') + explainPic = cv2.imread(r".\explain\gaosi.jpg") picLists = [["高斯噪声是一种白噪声,高斯随机变量的PDF由下式给出:", explainPic]] myWindow.showFig1(picLists) + def Add_salt_and_pepper_noise(): - ''' 添加椒盐噪声 ''' + """添加椒盐噪声""" global picSize, img_gray, img_result initWindows() if img_result is None: img_ = img_gray.copy() else: - answer = askyesno("选择待退化图像", "Y-对打开图像进行退化,N-继续对已退化图像进行退化") + answer = askyesno( + "选择待退化图像", "Y-对打开图像进行退化,N-继续对已退化图像进行退化" + ) if not answer: img_ = img_result.copy() else: img_ = img_gray.copy() - placePic1(img_, '待处理图像') - proportion = askfloat("设置噪声比例", "请输入椒盐噪声的比例,介于【0,1】之间",initialvalue=0.05,minvalue=0,maxvalue=1) + placePic1(img_, "待处理图像") + proportion = askfloat( + "设置噪声比例", + "请输入椒盐噪声的比例,介于【0,1】之间", + initialvalue=0.05, + minvalue=0, + maxvalue=1, + ) if proportion is None: - showwarning(title='警告', message='请先设置噪声强度') + showwarning(title="警告", message="请先设置噪声强度") return - img_result=spatialRestore.add_salt_and_pepper_noise(img_,proportion) + img_result = spatialRestore.add_salt_and_pepper_noise(img_, proportion) histr = Histogram.gray_histogram(img_result) - myWindow.showAndBarFig0(img_result,histr) + myWindow.showAndBarFig0(img_result, histr) myWindow.resultText.set("添加椒盐噪声") # placePic2(img_result, "添加椒盐噪声") - explainPic = cv2.imread(r'.\explain\jiaoyan.jpg') - picLists = [["椒盐噪声是一种脉冲噪声,双极脉冲噪声的概率密度函数可以如下表示:", explainPic]] + explainPic = cv2.imread(r".\explain\jiaoyan.jpg") + picLists = [ + ["椒盐噪声是一种脉冲噪声,双极脉冲噪声的概率密度函数可以如下表示:", explainPic] + ] myWindow.showFig1(picLists) + def Add_mean_noise(): - ''' 添加均匀噪声 ''' + """添加均匀噪声""" global picSize, img_gray, img_result initWindows() if img_result is None: img_ = img_gray.copy() else: - answer = askyesno("选择待退化图像", "Y-对打开图像进行退化,N-继续对已退化图像进行退化") + answer = askyesno( + "选择待退化图像", "Y-对打开图像进行退化,N-继续对已退化图像进行退化" + ) if not answer: img_ = img_result.copy() else: img_ = img_gray.copy() - placePic1(img_, '待处理图像') - paraLists=[["起始灰度值a",50],["终止灰度值b",100],["噪声比例",0.1]] - paraW=paraWindow(root,"设置均值噪声的灰度范围【0.255】",paraLists) + placePic1(img_, "待处理图像") + paraLists = [["起始灰度值a", 50], ["终止灰度值b", 100], ["噪声比例", 0.1]] + paraW = paraWindow(root, "设置均值噪声的灰度范围【0.255】", paraLists) # paraW.show() - img_result=spatialRestore.add_mean_noise(img_,paraW.paraLists[0][1],paraW.paraLists[1][1],paraW.paraLists[2][1]) + img_result = spatialRestore.add_mean_noise( + img_, paraW.paraLists[0][1], paraW.paraLists[1][1], paraW.paraLists[2][1] + ) histr = Histogram.gray_histogram(img_result) - myWindow.showAndBarFig0(img_result,histr) + myWindow.showAndBarFig0(img_result, histr) myWindow.resultText.set("添加均值噪声") # placePic2(img_result, "添加均值噪声") - explainPic = cv2.imread(r'.\explain\junzhi.jpg') + explainPic = cv2.imread(r".\explain\junzhi.jpg") picLists = [["均值噪声分布的概率密度函数为", explainPic]] myWindow.showFig1(picLists) + def Add_rayleigh_noise(): - ''' 添加瑞利噪声 ''' + """添加瑞利噪声""" global picSize, img_gray, img_result initWindows() if img_result is None: img_ = img_gray.copy() else: - answer = askyesno("选择待退化图像", "Y-对打开图像进行退化,N-继续对已退化图像进行退化") + answer = askyesno( + "选择待退化图像", "Y-对打开图像进行退化,N-继续对已退化图像进行退化" + ) if not answer: img_ = img_result.copy() else: img_ = img_gray.copy() - placePic1(img_, '待处理图像') - paraLists=[["起始灰度值a",50],["平缓度b(越大分布越平缓)",100],["噪声比例",0.1]] - paraW=paraWindow(root,"设置瑞利噪声的参数",paraLists) - img_result=spatialRestore.add_rayleigh_noise(img_,paraW.paraLists[0][1],paraW.paraLists[1][1],paraW.paraLists[2][1]) + placePic1(img_, "待处理图像") + paraLists = [ + ["起始灰度值a", 50], + ["平缓度b(越大分布越平缓)", 100], + ["噪声比例", 0.1], + ] + paraW = paraWindow(root, "设置瑞利噪声的参数", paraLists) + img_result = spatialRestore.add_rayleigh_noise( + img_, paraW.paraLists[0][1], paraW.paraLists[1][1], paraW.paraLists[2][1] + ) histr = Histogram.gray_histogram(img_result) - myWindow.showAndBarFig0(img_result,histr) + myWindow.showAndBarFig0(img_result, histr) myWindow.resultText.set("添加瑞利噪声") # placePic2(img_result, "添加瑞利噪声") - explainPic = cv2.imread(r'.\explain\ruili.jpg') + explainPic = cv2.imread(r".\explain\ruili.jpg") picLists = [["瑞利噪声的概率密度函数由下式给出:", explainPic]] myWindow.showFig1(picLists) + def Add_erlang_noise(): - ''' 添加伽马噪声 ''' + """添加伽马噪声""" global picSize, img_gray, img_result initWindows() if img_result is None: img_ = img_gray.copy() else: - answer = askyesno("选择待退化图像", "Y-对打开图像进行退化,N-继续对已退化图像进行退化") + answer = askyesno( + "选择待退化图像", "Y-对打开图像进行退化,N-继续对已退化图像进行退化" + ) if not answer: img_ = img_result.copy() else: img_ = img_gray.copy() - placePic1(img_, '待处理图像') - paraLists=[["起始灰度值a",50],["尖锐度b(越大分布越尖锐)",0.1],["噪声比例",0.1]] - paraW=paraWindow(root,"设置伽马噪声的参数",paraLists) - img_result=spatialRestore.add_erlang_noise(img_,paraW.paraLists[0][1],paraW.paraLists[1][1],paraW.paraLists[2][1]) + placePic1(img_, "待处理图像") + paraLists = [ + ["起始灰度值a", 50], + ["尖锐度b(越大分布越尖锐)", 0.1], + ["噪声比例", 0.1], + ] + paraW = paraWindow(root, "设置伽马噪声的参数", paraLists) + img_result = spatialRestore.add_erlang_noise( + img_, paraW.paraLists[0][1], paraW.paraLists[1][1], paraW.paraLists[2][1] + ) histr = Histogram.gray_histogram(img_result) - myWindow.showAndBarFig0(img_result,histr) + myWindow.showAndBarFig0(img_result, histr) myWindow.resultText.set("添加伽马噪声") # placePic2(img_result, "添加伽马噪声") - explainPic = cv2.imread(r'.\explain\gama.jpg') + explainPic = cv2.imread(r".\explain\gama.jpg") picLists = [["伽马噪声的概率密度函数由下式给出:", explainPic]] myWindow.showFig1(picLists) -#--------------------------------------------------------- + + +# --------------------------------------------------------- def Motion_blur(): - ''' 添加运动模糊 ''' - global img_gray, img_result,PSF + """添加运动模糊""" + global img_gray, img_result, PSF initWindows() if img_gray is None: - showwarning(title='警告', message='请先在文件菜单下打开图片!') + showwarning(title="警告", message="请先在文件菜单下打开图片!") return else: img_ = img_gray.copy() if img_result is not None: - answer = askyesno("选择待退化图像", "Y-对打开图像进行退化,N-继续对已退化图像进行退化") + answer = askyesno( + "选择待退化图像", "Y-对打开图像进行退化,N-继续对已退化图像进行退化" + ) if not answer: img_ = img_result.copy() - placePic1(img_,'待处理图像') - explainPic=cv2.imread(r'.\explain\motionBlur.jpg') - picLists = [["运动模糊核是由单位矩阵旋转产生的,如下图所示是一个90度方向的运动模糊核", explainPic]] + placePic1(img_, "待处理图像") + explainPic = cv2.imread(r".\explain\motionBlur.jpg") + picLists = [ + [ + "运动模糊核是由单位矩阵旋转产生的,如下图所示是一个90度方向的运动模糊核", + explainPic, + ] + ] myWindow.showFig1(picLists) - paraLists=[["运动模糊核大小",5],["运动方向(度)",45]] - paraW=paraWindow(root,"请设置运动模糊的参数",paraLists) - kernel_size=int(paraW.paraLists[0][1]) - angle=int(paraW.paraLists[1][1]) + paraLists = [["运动模糊核大小", 5], ["运动方向(度)", 45]] + paraW = paraWindow(root, "请设置运动模糊的参数", paraLists) + kernel_size = int(paraW.paraLists[0][1]) + angle = int(paraW.paraLists[1][1]) PSF = Restore.motion_PSF(kernel_size, angle) # 生成运动模糊核 - PSF = Restore.extension_PSF(img_, PSF) # 在频域进行运动模糊,需要扩展PSF,使其与图像一样大小 + PSF = Restore.extension_PSF( + img_, PSF + ) # 在频域进行运动模糊,需要扩展PSF,使其与图像一样大小 img_result = Restore.make_blurred(img_, PSF) placePic2(img_result, "运动模糊后的图像") + def Gaussian_blur(): - ''' 添加高斯模糊 ''' + """添加高斯模糊""" global picSize, img_gray, img_result, PSF initWindows() if img_gray is None: - showwarning(title='警告', message='请先在文件菜单下打开图片!') + showwarning(title="警告", message="请先在文件菜单下打开图片!") return else: img_ = img_gray.copy() if img_result is not None: - answer = askyesno("选择待退化图像", "Y-对打开图像进行退化,N-继续对已退化图像进行退化") + answer = askyesno( + "选择待退化图像", "Y-对打开图像进行退化,N-继续对已退化图像进行退化" + ) if not answer: img_ = img_result.copy() - placePic1(img_,'待处理图像') - explainPic=cv2.imread(r'.\explain\gaussianBlur.jpg') + placePic1(img_, "待处理图像") + explainPic = cv2.imread(r".\explain\gaussianBlur.jpg") picLists = [["二维高斯核可以根据下面的公式推导为两个一维高斯核的乘积", explainPic]] myWindow.showFig1(picLists) - paraLists=[["高斯模糊核大小",5],["标准方差",0.1]] - paraW=paraWindow(root,"请设置高斯模糊的参数",paraLists) - kernel_size=int(paraW.paraLists[0][1]) - sigma=int(paraW.paraLists[1][1]) + paraLists = [["高斯模糊核大小", 5], ["标准方差", 0.1]] + paraW = paraWindow(root, "请设置高斯模糊的参数", paraLists) + kernel_size = int(paraW.paraLists[0][1]) + sigma = int(paraW.paraLists[1][1]) PSF = Restore.Gaussian_PSF(kernel_size, sigma) # 生成高斯模糊核 - PSF = Restore.extension_PSF(img_, PSF) # 在频域进行运动模糊,需要扩展PSF,使其与图像一样大小 + PSF = Restore.extension_PSF( + img_, PSF + ) # 在频域进行运动模糊,需要扩展PSF,使其与图像一样大小 img_result = Restore.make_blurred(img_, PSF) placePic2(img_result, "高斯模糊后的图像") + def Turbulence_blur(): - ''' 添加大气湍流模糊 ''' + """添加大气湍流模糊""" global picSize, img_gray, img_result, PSF initWindows() if img_gray is None: - showwarning(title='警告', message='请先在文件菜单下打开图片!') + showwarning(title="警告", message="请先在文件菜单下打开图片!") return else: img_ = img_gray.copy() if img_result is not None: - answer = askyesno("选择待退化图像", "Y-对打开图像进行退化,N-继续对已退化图像进行退化") + answer = askyesno( + "选择待退化图像", "Y-对打开图像进行退化,N-继续对已退化图像进行退化" + ) if not answer: img_ = img_result.copy() - placePic1(img_,'待处理图像') - explainPic=cv2.imread(r'.\explain\turbulence.jpg') - picLists = [["大气湍流模型的传递函数如下图所示,其中k值越大,模糊越严重", explainPic]] + placePic1(img_, "待处理图像") + explainPic = cv2.imread(r".\explain\turbulence.jpg") + picLists = [ + ["大气湍流模型的传递函数如下图所示,其中k值越大,模糊越严重", explainPic] + ] myWindow.showFig1(picLists) - k=askfloat("大气湍流模型参数","请输入大气湍流的强度k",initialvalue=0.001) - PSF = Restore.turbulence_PSF(img_,k) # 生成模糊核 - PSF = Restore.extension_PSF(img_, PSF) # 在频域进行运动模糊,需要扩展PSF,使其与图像一样大小 + k = askfloat("大气湍流模型参数", "请输入大气湍流的强度k", initialvalue=0.001) + PSF = Restore.turbulence_PSF(img_, k) # 生成模糊核 + PSF = Restore.extension_PSF( + img_, PSF + ) # 在频域进行运动模糊,需要扩展PSF,使其与图像一样大小 img_result = Restore.make_blurred(img_, PSF) placePic2(img_result, "大气湍流模糊后的图像") -#--------------------------------------------------------- + +# --------------------------------------------------------- def Harmonic_filter(): - ''' 谐波滤波 ''' + """谐波滤波""" global picSize, img_gray, img_result, PSF initWindows() if img_gray is None: - showwarning(title='警告', message='请先在文件菜单下打开图片!') + showwarning(title="警告", message="请先在文件菜单下打开图片!") return if img_result is not None: - answer = askyesno("选择待滤波图像", "Y-对打开图像进行滤波,N-对处理后图像进行滤波") + answer = askyesno( + "选择待滤波图像", "Y-对打开图像进行滤波,N-对处理后图像进行滤波" + ) if not answer: img_ = img_result.copy() else: img_ = img_gray.copy() - placePic1(img_, '待处理图像') + placePic1(img_, "待处理图像") kernelSize = askinteger("滤波核设置", "请输入滤波核大小", initialvalue=3) if kernelSize is None: - showwarning("警告","请先确定滤波核大小!!!") + showwarning("警告", "请先确定滤波核大小!!!") return - img_result = spatialRestore.harmonic_filter(img_,kernelSize) + img_result = spatialRestore.harmonic_filter(img_, kernelSize) placePic2(img_result, "谐波滤波后的图像") - imgExplain = cv2.imread(r'.\explain\harmonic.jpg', 0) - picLists = [["谐波均值(Harmonic Mean)适合去除盐噪声,但不适合胡椒噪声,也适用于其他类型的噪声,比如高斯噪声。", imgExplain]] + imgExplain = cv2.imread(r".\explain\harmonic.jpg", 0) + picLists = [ + [ + "谐波均值(Harmonic Mean)适合去除盐噪声,但不适合胡椒噪声,也适用于其他类型的噪声,比如高斯噪声。", + imgExplain, + ] + ] myWindow.showFig1(picLists) + def Contra_harmonic_filter(): - ''' 逆谐波滤波 ''' + """逆谐波滤波""" global picSize, img_gray, img_result, PSF initWindows() if img_gray is None: - showwarning(title='警告', message='请先在文件菜单下打开图片!') + showwarning(title="警告", message="请先在文件菜单下打开图片!") return if img_result is not None: - answer = askyesno("选择待滤波图像", "Y-对打开图像进行滤波,N-对处理后图像进行滤波") + answer = askyesno( + "选择待滤波图像", "Y-对打开图像进行滤波,N-对处理后图像进行滤波" + ) if not answer: img_ = img_result.copy() else: img_ = img_gray.copy() - placePic1(img_, '待处理图像') + placePic1(img_, "待处理图像") paraLists = [["滤波核大小", 5], ["Q值", -1]] paraW = paraWindow(root, "请设置伪逆滤波的参数", paraLists) kernelSize = int(paraW.paraLists[0][1]) Q = int(paraW.paraLists[1][1]) - img_result = spatialRestore.contra_harmonic_filter(img_,kernelSize,Q) + img_result = spatialRestore.contra_harmonic_filter(img_, kernelSize, Q) placePic2(img_result, "逆谐波滤波后的图像") - imgExplain = cv2.imread(r'.\explain\contraharmonic.jpg', 0) - picLists = [["逆谐波均值,Q是滤波器的阶数。Q为负值时,可消除盐噪声。Q为正值时,可消除胡椒噪声。不能同时消除两者", imgExplain]] + imgExplain = cv2.imread(r".\explain\contraharmonic.jpg", 0) + picLists = [ + [ + "逆谐波均值,Q是滤波器的阶数。Q为负值时,可消除盐噪声。Q为正值时,可消除胡椒噪声。不能同时消除两者", + imgExplain, + ] + ] myWindow.showFig1(picLists) -#------------------------------------------------------------------ + + +# ------------------------------------------------------------------ def Inverse(): - ''' 逆滤波 ''' - global picSize, img_gray, img_result,PSF + """逆滤波""" + global picSize, img_gray, img_result, PSF initWindows() if img_result is None or PSF is None: - showwarning(title='警告', message='请先对图像进行退化!') + showwarning(title="警告", message="请先对图像进行退化!") return - placePic1(img_result,"退化后图像") + placePic1(img_result, "退化后图像") img_restore = Restore.inverse(img_result, PSF) # PSF=None placePic2(img_restore, "逆滤波后的图像") - explainPic = cv2.imread(r'.\explain\nilvbo.jpg') + explainPic = cv2.imread(r".\explain\nilvbo.jpg") picLists = [["有噪声的情况下,逆滤波复原的基本原理可以写成:", explainPic]] myWindow.showFig1(picLists) + def Improved_inverse(): - ''' 伪逆滤波 ''' + """伪逆滤波""" global picSize, img_gray, img_result, PSF initWindows() if img_result is None or PSF is None: - showwarning(title='警告', message='请先对图像进行退化!') + showwarning(title="警告", message="请先对图像进行退化!") return placePic1(img_result, "退化后图像") - explainPic=cv2.imread(r'.\explain\pseudoInverse.jpg') - picLists = [["伪逆滤波的传递函数是:低频部分(小于截止w)频率与逆滤波相同,高频部分采用一个指定的较小的常量k", explainPic]] + explainPic = cv2.imread(r".\explain\pseudoInverse.jpg") + picLists = [ + [ + "伪逆滤波的传递函数是:低频部分(小于截止w)频率与逆滤波相同,高频部分采用一个指定的较小的常量k", + explainPic, + ] + ] myWindow.showFig1(picLists) - paraLists=[["伪逆滤波的截止频率",70],["高频常数",0.01]] - paraW=paraWindow(root,"请设置伪逆滤波的参数",paraLists) - w=int(paraW.paraLists[0][1]) - k=int(paraW.paraLists[1][1]) - img_restore = Restore.improved_inverse(img_result, PSF,w,k) + paraLists = [["伪逆滤波的截止频率", 70], ["高频常数", 0.01]] + paraW = paraWindow(root, "请设置伪逆滤波的参数", paraLists) + w = int(paraW.paraLists[0][1]) + k = int(paraW.paraLists[1][1]) + img_restore = Restore.improved_inverse(img_result, PSF, w, k) # PSF=None placePic2(img_restore, "伪逆滤波后的图像") + + def Wiener(): - ''' 维纳滤波 ''' + """维纳滤波""" global picSize, img_gray, img_result, PSF initWindows() if img_result is None or PSF is None: - showwarning(title='警告', message='请先对图像进行退化!') + showwarning(title="警告", message="请先对图像进行退化!") return placePic1(img_result, "退化后图像") - explainPic = cv2.imread(r'.\explain\wiener.jpg') - picLists = [["维纳滤波采用最小均方误差作为最佳过滤准则,使用时需要估计图像的噪信比,无噪声时,噪信比为0", explainPic]] + explainPic = cv2.imread(r".\explain\wiener.jpg") + picLists = [ + [ + "维纳滤波采用最小均方误差作为最佳过滤准则,使用时需要估计图像的噪信比,无噪声时,噪信比为0", + explainPic, + ] + ] myWindow.showFig1(picLists) - k=askfloat("维纳滤波参数","请估计图像的噪信比",initialvalue=0.01) + k = askfloat("维纳滤波参数", "请估计图像的噪信比", initialvalue=0.01) img_restore = Restore.wiener(img_result, PSF, k) # PSF=None placePic2(img_restore, "维纳滤波后的图像") + def Constrained_least_squares(): - ''' 受限最小二乘滤波 ''' + """受限最小二乘滤波""" global picSize, img_gray, img_result, PSF initWindows() if img_result is None or PSF is None: - showwarning(title='警告', message='请先对图像进行退化!') + showwarning(title="警告", message="请先对图像进行退化!") return placePic1(img_result, "退化后图像") - explainPic = cv2.imread(r'.\explain\constrained_least_squares.jpg') - picLists = [["约束最小二乘滤波通过抑制高频成份,来获得平滑的复原图像,公式中Q是锐化算子,λ是权重参数,其值越大越平滑", explainPic]] + explainPic = cv2.imread(r".\explain\constrained_least_squares.jpg") + picLists = [ + [ + "约束最小二乘滤波通过抑制高频成份,来获得平滑的复原图像,公式中Q是锐化算子,λ是权重参数,其值越大越平滑", + explainPic, + ] + ] myWindow.showFig1(picLists) k = askfloat("约束最小二乘滤波参数", "请输入λ", initialvalue=0.1) img_restore = Restore.constrained_least_squares(img_result, PSF, k) @@ -1510,563 +1918,675 @@ def Constrained_least_squares(): # ------------------------------------------------------------- def Binary(): - ''' 图像二值化 ''' - global img_open,img_binary,img_result + """图像二值化""" + global img_open, img_binary, img_result initWindows() if img_open is None: - showwarning(title='警告', message='请先在文件菜单下打开图片!') + showwarning(title="警告", message="请先在文件菜单下打开图片!") return placePic1(img_open, "原图像") - img=img_open.copy() - if len(img_open.shape)>2: - img = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY) + img = img_open.copy() + if len(img_open.shape) > 2: + img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) _, img_binary = cv2.threshold(img, 128, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU) - img_result=img_binary + img_result = img_binary placePic2(img_result, "二值化图像") - imgExplain = cv2.imread(r'.\explain\Image Binarization.jpg', 0) - picLists = [["图像二值化处理,也就是阈值分割,它的目的就是求一个阈值T,并用T将图像f(x,y)分成对象物和背景两个领域。", imgExplain]] + imgExplain = cv2.imread(r".\explain\Image Binarization.jpg", 0) + picLists = [ + [ + "图像二值化处理,也就是阈值分割,它的目的就是求一个阈值T,并用T将图像f(x,y)分成对象物和背景两个领域。", + imgExplain, + ] + ] myWindow.showFig1(picLists) + + def Dilate(): - ''' 形态学处理-膨胀运算 ''' + """形态学处理-膨胀运算""" global img_open, img_binary, img_result initWindows() if img_open is None: - showwarning(title='警告', message='请先在文件菜单下打开图片!') + showwarning(title="警告", message="请先在文件菜单下打开图片!") return - img=img_open.copy() - if len(img_open.shape)>2: - img = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY) + img = img_open.copy() + if len(img_open.shape) > 2: + img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) _, img_binary = cv2.threshold(img, 128, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU) placePic1(img_binary, "原二值图像") row, col = img_binary.shape minrc = min(row, col) - kernelSize = askinteger("结构算子大小设置", "请输入结构算子大小", initialvalue=3, minvalue=3, maxvalue=minrc) - paraLists = [["请选择结构算子形状", ['矩形','十字形', '椭圆形']]] + kernelSize = askinteger( + "结构算子大小设置", + "请输入结构算子大小", + initialvalue=3, + minvalue=3, + maxvalue=minrc, + ) + paraLists = [["请选择结构算子形状", ["矩形", "十字形", "椭圆形"]]] paraW = listWindow(root, "结构算子选项", paraLists) structType = paraW.returnValue if structType == 1: - kernel = cv2.getStructuringElement(cv2.MORPH_CROSS,(kernelSize,kernelSize)) + kernel = cv2.getStructuringElement(cv2.MORPH_CROSS, (kernelSize, kernelSize)) elif structType == 2: - kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE,(kernelSize,kernelSize)) + kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (kernelSize, kernelSize)) else: - kernel = cv2.getStructuringElement(cv2.MORPH_RECT,(kernelSize,kernelSize)) + kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (kernelSize, kernelSize)) img_result = cv2.dilate(img_binary, kernel, iterations=1) placePic2(img_result, "膨胀后的图像") - myWindow.explainText.set("膨胀运算是对二值化物体边界点扩充,将与物体接触的所以背景点合并到该物体中,使边界向外扩张。") + myWindow.explainText.set( + "膨胀运算是对二值化物体边界点扩充,将与物体接触的所以背景点合并到该物体中,使边界向外扩张。" + ) + def Erode(): - ''' 形态学处理-腐蚀运算 ''' - global img_open,img_binary, img_result + """形态学处理-腐蚀运算""" + global img_open, img_binary, img_result initWindows() if img_open is None: - showwarning(title='警告', message='请先在文件菜单下打开图片!') + showwarning(title="警告", message="请先在文件菜单下打开图片!") return - img=img_open.copy() - if len(img_open.shape)>2: - img = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY) + img = img_open.copy() + if len(img_open.shape) > 2: + img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) _, img_binary = cv2.threshold(img, 128, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU) placePic1(img_binary, "原二值图像") - row,col=img_binary.shape - minrc=min(row,col) - kernelSize=askinteger("结构算子设置","请输入结构算子大小",initialvalue=3,minvalue=3,maxvalue=minrc) - paraLists = [["请选择结构算子形状", ['矩形', '十字形', '椭圆形']]] + row, col = img_binary.shape + minrc = min(row, col) + kernelSize = askinteger( + "结构算子设置", "请输入结构算子大小", initialvalue=3, minvalue=3, maxvalue=minrc + ) + paraLists = [["请选择结构算子形状", ["矩形", "十字形", "椭圆形"]]] paraW = listWindow(root, "结构算子选项", paraLists) structType = paraW.returnValue if structType == 1: - kernel = cv2.getStructuringElement(cv2.MORPH_CROSS,(kernelSize,kernelSize)) + kernel = cv2.getStructuringElement(cv2.MORPH_CROSS, (kernelSize, kernelSize)) elif structType == 2: - kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE,(kernelSize,kernelSize)) + kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (kernelSize, kernelSize)) else: # 矩形 - kernel = cv2.getStructuringElement(cv2.MORPH_RECT,(kernelSize,kernelSize)) - img_result = cv2.erode(img_binary,kernel,iterations = 1) - placePic2(img_result,"腐蚀后的图像") - imgExplain = cv2.imread(r'.\explain\Erosion.jpg',0) - picLists = [["形态学腐蚀的定义:集合A被集合B腐蚀为:A○—B={x:B+x.A}",imgExplain]] + kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (kernelSize, kernelSize)) + img_result = cv2.erode(img_binary, kernel, iterations=1) + placePic2(img_result, "腐蚀后的图像") + imgExplain = cv2.imread(r".\explain\Erosion.jpg", 0) + picLists = [["形态学腐蚀的定义:集合A被集合B腐蚀为:A○—B={x:B+x.A}", imgExplain]] myWindow.showFig1(picLists) + def Open_operation(): - ''' 形态学处理-开运算 ''' + """形态学处理-开运算""" global img_open, img_binary, img_result initWindows() if img_open is None: - showwarning(title='警告', message='请先在文件菜单下打开图片!') + showwarning(title="警告", message="请先在文件菜单下打开图片!") return - img=img_open.copy() - if len(img_open.shape)>2: - img = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY) + img = img_open.copy() + if len(img_open.shape) > 2: + img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) _, img_binary = cv2.threshold(img, 128, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU) placePic1(img_binary, "原二值图像") row, col = img_binary.shape minrc = min(row, col) - kernelSize = askinteger("结构算子设置", "请输入结构算子大小", initialvalue=3, minvalue=3, maxvalue=minrc) - paraLists = [["请选择结构算子形状", ['矩形', '十字形', '椭圆形']]] + kernelSize = askinteger( + "结构算子设置", "请输入结构算子大小", initialvalue=3, minvalue=3, maxvalue=minrc + ) + paraLists = [["请选择结构算子形状", ["矩形", "十字形", "椭圆形"]]] paraW = listWindow(root, "结构算子选项", paraLists) structType = paraW.returnValue if structType == 1: - kernel = cv2.getStructuringElement(cv2.MORPH_CROSS,(kernelSize,kernelSize)) + kernel = cv2.getStructuringElement(cv2.MORPH_CROSS, (kernelSize, kernelSize)) elif structType == 2: - kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE,(kernelSize,kernelSize)) + kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (kernelSize, kernelSize)) else: # 矩形 - kernel = cv2.getStructuringElement(cv2.MORPH_RECT,(kernelSize,kernelSize)) + kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (kernelSize, kernelSize)) img_result = cv2.morphologyEx(img_binary, cv2.MORPH_OPEN, kernel) placePic2(img_result, "开运算后的图像") - myWindow.explainText.set("开运算数学上是先腐蚀后膨胀的结果,物理结果为完全删除了不能包含结构元素的对象区域,平滑了对象的轮廓,断开了狭窄的连接,去掉了细小的突出部分。") + myWindow.explainText.set( + "开运算数学上是先腐蚀后膨胀的结果,物理结果为完全删除了不能包含结构元素的对象区域,平滑了对象的轮廓,断开了狭窄的连接,去掉了细小的突出部分。" + ) + def Close_operation(): - ''' 形态学处理-闭运算 ''' + """形态学处理-闭运算""" global img_open, img_binary, img_result initWindows() if img_open is None: - showwarning(title='警告', message='请先在文件菜单下打开图片!') + showwarning(title="警告", message="请先在文件菜单下打开图片!") return - img=img_open.copy() - if len(img_open.shape)>2: - img = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY) + img = img_open.copy() + if len(img_open.shape) > 2: + img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) _, img_binary = cv2.threshold(img, 128, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU) placePic1(img_binary, "原二值图像") row, col = img_binary.shape minrc = min(row, col) - kernelSize = askinteger("结构算子设置", "请输入结构算子大小", initialvalue=3, minvalue=3, maxvalue=minrc) - paraLists = [["请选择结构算子形状", ['矩形', '十字形', '椭圆形']]] + kernelSize = askinteger( + "结构算子设置", "请输入结构算子大小", initialvalue=3, minvalue=3, maxvalue=minrc + ) + paraLists = [["请选择结构算子形状", ["矩形", "十字形", "椭圆形"]]] paraW = listWindow(root, "结构算子选项", paraLists) structType = paraW.returnValue if structType == 1: - kernel = cv2.getStructuringElement(cv2.MORPH_CROSS,(kernelSize,kernelSize)) + kernel = cv2.getStructuringElement(cv2.MORPH_CROSS, (kernelSize, kernelSize)) elif structType == 2: - kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE,(kernelSize,kernelSize)) + kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (kernelSize, kernelSize)) else: # 矩形 - kernel = cv2.getStructuringElement(cv2.MORPH_RECT,(kernelSize,kernelSize)) + kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (kernelSize, kernelSize)) img_result = cv2.morphologyEx(img_binary, cv2.MORPH_CLOSE, kernel) placePic2(img_result, "闭运算后的图像") - myWindow.explainText.set("闭运算在数学上是先膨胀再腐蚀的结果,物理结果也是会平滑对象的轮廓,但是与开运算不同的是,闭运算一般会将狭窄的缺口连接起来形成细长的弯口,并填充比结构元素小的洞。") + myWindow.explainText.set( + "闭运算在数学上是先膨胀再腐蚀的结果,物理结果也是会平滑对象的轮廓,但是与开运算不同的是,闭运算一般会将狭窄的缺口连接起来形成细长的弯口,并填充比结构元素小的洞。" + ) + def Gradient_morphology(): - ''' 形态学处理-形态学梯度 ''' + """形态学处理-形态学梯度""" global img_open, img_binary, img_result initWindows() if img_open is None: - showwarning(title='警告', message='请先在文件菜单下打开图片!') + showwarning(title="警告", message="请先在文件菜单下打开图片!") return - img=img_open.copy() - if len(img_open.shape)>2: - img = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY) + img = img_open.copy() + if len(img_open.shape) > 2: + img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) _, img_binary = cv2.threshold(img, 128, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU) placePic1(img_binary, "原二值图像") row, col = img_binary.shape minrc = min(row, col) - kernelSize = askinteger("结构算子设置", "请输入结构算子大小", initialvalue=3, minvalue=3, maxvalue=minrc) - paraLists = [["请选择结构算子形状", ['矩形', '十字形', '椭圆形']]] + kernelSize = askinteger( + "结构算子设置", "请输入结构算子大小", initialvalue=3, minvalue=3, maxvalue=minrc + ) + paraLists = [["请选择结构算子形状", ["矩形", "十字形", "椭圆形"]]] paraW = listWindow(root, "结构算子选项", paraLists) structType = paraW.returnValue if structType == 1: - kernel = cv2.getStructuringElement(cv2.MORPH_CROSS,(kernelSize,kernelSize)) + kernel = cv2.getStructuringElement(cv2.MORPH_CROSS, (kernelSize, kernelSize)) elif structType == 2: - kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE,(kernelSize,kernelSize)) + kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (kernelSize, kernelSize)) else: # 矩形 - kernel = cv2.getStructuringElement(cv2.MORPH_RECT,(kernelSize,kernelSize)) + kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (kernelSize, kernelSize)) img_result = cv2.morphologyEx(img_binary, cv2.MORPH_GRADIENT, kernel) placePic2(img_result, "形态学梯度图像") - myWindow.explainText.set("形态学梯度是一幅图像膨胀与腐蚀的差别,结果看上去就像前景物体的轮廓。") + myWindow.explainText.set( + "形态学梯度是一幅图像膨胀与腐蚀的差别,结果看上去就像前景物体的轮廓。" + ) + def Tophat(): - ''' 形态学处理-礼帽运算 ''' + """形态学处理-礼帽运算""" global img_open, img_binary, img_result initWindows() if img_open is None: - showwarning(title='警告', message='请先在文件菜单下打开图片!') + showwarning(title="警告", message="请先在文件菜单下打开图片!") return - img=img_open.copy() - if len(img_open.shape)>2: - img = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY) + img = img_open.copy() + if len(img_open.shape) > 2: + img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) _, img_binary = cv2.threshold(img, 128, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU) placePic1(img_binary, "原二值图像") row, col = img_binary.shape minrc = min(row, col) - kernelSize = askinteger("结构算子设置", "请输入结构算子大小", initialvalue=3, minvalue=3, maxvalue=minrc) - paraLists = [["请选择结构算子形状", ['矩形', '十字形', '椭圆形']]] + kernelSize = askinteger( + "结构算子设置", "请输入结构算子大小", initialvalue=3, minvalue=3, maxvalue=minrc + ) + paraLists = [["请选择结构算子形状", ["矩形", "十字形", "椭圆形"]]] paraW = listWindow(root, "结构算子选项", paraLists) structType = paraW.returnValue if structType == 1: - kernel = cv2.getStructuringElement(cv2.MORPH_CROSS,(kernelSize,kernelSize)) + kernel = cv2.getStructuringElement(cv2.MORPH_CROSS, (kernelSize, kernelSize)) elif structType == 2: - kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE,(kernelSize,kernelSize)) + kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (kernelSize, kernelSize)) else: # 矩形 - kernel = cv2.getStructuringElement(cv2.MORPH_RECT,(kernelSize,kernelSize)) + kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (kernelSize, kernelSize)) img_result = cv2.morphologyEx(img_binary, cv2.MORPH_TOPHAT, kernel) placePic2(img_result, "形态学礼帽图像") myWindow.explainText.set("形态学礼帽是原始图像与进行开运算之后得到的图像的差") + def Blackhat(): - ''' 形态学处理-黑帽运算 ''' + """形态学处理-黑帽运算""" global img_open, img_binary, img_result initWindows() if img_open is None: - showwarning(title='警告', message='请先在文件菜单下打开图片!') + showwarning(title="警告", message="请先在文件菜单下打开图片!") return - img=img_open.copy() - if len(img_open.shape)>2: - img = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY) + img = img_open.copy() + if len(img_open.shape) > 2: + img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) _, img_binary = cv2.threshold(img, 128, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU) placePic1(img_binary, "原二值图像") row, col = img_binary.shape minrc = min(row, col) - kernelSize = askinteger("结构算子设置", "请输入结构算子大小", initialvalue=3, minvalue=3, maxvalue=minrc) - paraLists = [["请选择结构算子形状", ['矩形', '十字形', '椭圆形']]] + kernelSize = askinteger( + "结构算子设置", "请输入结构算子大小", initialvalue=3, minvalue=3, maxvalue=minrc + ) + paraLists = [["请选择结构算子形状", ["矩形", "十字形", "椭圆形"]]] paraW = listWindow(root, "结构算子选项", paraLists) structType = paraW.returnValue if structType == 1: - kernel = cv2.getStructuringElement(cv2.MORPH_CROSS,(kernelSize,kernelSize)) + kernel = cv2.getStructuringElement(cv2.MORPH_CROSS, (kernelSize, kernelSize)) elif structType == 2: - kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE,(kernelSize,kernelSize)) + kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (kernelSize, kernelSize)) else: # 矩形 - kernel = cv2.getStructuringElement(cv2.MORPH_RECT,(kernelSize,kernelSize)) + kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (kernelSize, kernelSize)) img_result = cv2.morphologyEx(img_binary, cv2.MORPH_BLACKHAT, kernel) placePic2(img_result, "形态学黑帽图像") myWindow.explainText.set("形态学黑帽是进行闭运算之后得到的图像与原始图像的差。") + def Fill_holes(): - ''' 形态学处理-孔洞填充运算 ''' + """形态学处理-孔洞填充运算""" global img_open, img_binary, img_result initWindows() if img_open is None: - showwarning(title='警告', message='请先在文件菜单下打开图片!') + showwarning(title="警告", message="请先在文件菜单下打开图片!") return - img=img_open.copy() - if len(img_open.shape)>2: - img = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY) + img = img_open.copy() + if len(img_open.shape) > 2: + img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) _, img_binary = cv2.threshold(img, 128, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU) placePic1(img_binary, "原二值图像") - explainPic = cv2.imread(r'.\explain\fill_holes.jpg') - text="孔洞填充是以带有白色边框的黑色图像Marker为起点,对其进行连续膨胀,并以原图像的补集作为遮罩Mask,来限制膨胀,直至膨胀收敛.最后对Marker取补即得到最终图像,与原图相减可得到填充图像。" + explainPic = cv2.imread(r".\explain\fill_holes.jpg") + text = "孔洞填充是以带有白色边框的黑色图像Marker为起点,对其进行连续膨胀,并以原图像的补集作为遮罩Mask,来限制膨胀,直至膨胀收敛.最后对Marker取补即得到最终图像,与原图相减可得到填充图像。" picLists = [["形态学变换公式", explainPic]] - myWindow.showExplain(text,picLists) + myWindow.showExplain(text, picLists) row, col = img_binary.shape minrc = min(row, col) - kernelSize = askinteger("结构算子设置", "请输入结构算子大小", initialvalue=3, minvalue=3, maxvalue=minrc) - paraLists = [["请选择结构算子形状", ['矩形', '十字形', '椭圆形']]] + kernelSize = askinteger( + "结构算子设置", "请输入结构算子大小", initialvalue=3, minvalue=3, maxvalue=minrc + ) + paraLists = [["请选择结构算子形状", ["矩形", "十字形", "椭圆形"]]] paraW = listWindow(root, "结构算子选项", paraLists) structType = paraW.returnValue if structType == 1: - kernel = cv2.getStructuringElement(cv2.MORPH_CROSS,(kernelSize,kernelSize)) + kernel = cv2.getStructuringElement(cv2.MORPH_CROSS, (kernelSize, kernelSize)) elif structType == 2: - kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE,(kernelSize,kernelSize)) + kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (kernelSize, kernelSize)) else: # 矩形 - kernel = cv2.getStructuringElement(cv2.MORPH_RECT,(kernelSize,kernelSize)) - img_result = morphology.fill_holes(img_binary,kernel) + kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (kernelSize, kernelSize)) + img_result = morphology.fill_holes(img_binary, kernel) placePic2(img_result, "填充孔洞图像") + def Skeleton(): - ''' 形态学处理-骨架提取 ''' + """形态学处理-骨架提取""" global img_open, img_binary, img_result initWindows() if img_open is None: - showwarning(title='警告', message='请先在文件菜单下打开图片!') + showwarning(title="警告", message="请先在文件菜单下打开图片!") return - img=img_open.copy() - if len(img_open.shape)>2: - img = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY) + img = img_open.copy() + if len(img_open.shape) > 2: + img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) _, img_binary = cv2.threshold(img, 128, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU) placePic1(img_binary, "原二值图像") row, col = img_binary.shape minrc = min(row, col) - kernelSize = askinteger("结构算子设置", "请输入结构算子大小", initialvalue=3, minvalue=3, maxvalue=minrc) - paraLists = [["请选择结构算子形状", ['矩形', '十字形', '椭圆形']]] + kernelSize = askinteger( + "结构算子设置", "请输入结构算子大小", initialvalue=3, minvalue=3, maxvalue=minrc + ) + paraLists = [["请选择结构算子形状", ["矩形", "十字形", "椭圆形"]]] paraW = listWindow(root, "结构算子选项", paraLists) structType = paraW.returnValue if structType == 0: # 矩形 - kernel = cv2.getStructuringElement(cv2.MORPH_RECT,(kernelSize,kernelSize)) + kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (kernelSize, kernelSize)) elif structType == 1: - kernel = cv2.getStructuringElement(cv2.MORPH_CROSS,(kernelSize,kernelSize)) + kernel = cv2.getStructuringElement(cv2.MORPH_CROSS, (kernelSize, kernelSize)) elif structType == 2: - kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE,(kernelSize,kernelSize)) + kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (kernelSize, kernelSize)) img_result = cv2.morphologyEx(img_binary, cv2.MORPH_BLACKHAT, kernel) placePic2(img_result, "骨架提取") myWindow.explainText.set("图像骨架提取,实际上就是提取目标在图像上的中心像素轮廓。") + + # 系统默认图片展示形态学效果------------------------------------------------------------ def Binary1(): - ''' 图像二值化 ''' - global img_open,img_binary,img_result + """图像二值化""" + global img_open, img_binary, img_result initWindows() - img_ = cv2.imdecode(np.fromfile(r'.\explain\morphology.png', dtype=np.uint8), 0) + img_ = cv2.imdecode(np.fromfile(r".\explain\morphology.png", dtype=np.uint8), 0) placePic1(img_, "原图像") _, img_binary = cv2.threshold(img_, 128, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU) - img_result=img_binary + img_result = img_binary placePic2(img_result, "二值化图像") - imgExplain = cv2.imread(r'.\explain\Image Binarization.jpg', 0) - picLists = [["图像二值化处理,也就是阈值分割,它的目的就是求一个阈值T,并用T将图像f(x,y)分成对象物和背景两个领域。", imgExplain]] + imgExplain = cv2.imread(r".\explain\Image Binarization.jpg", 0) + picLists = [ + [ + "图像二值化处理,也就是阈值分割,它的目的就是求一个阈值T,并用T将图像f(x,y)分成对象物和背景两个领域。", + imgExplain, + ] + ] myWindow.showFig1(picLists) + + def Dilate1(): - ''' 形态学处理-膨胀运算 ''' + """形态学处理-膨胀运算""" global img_open, img_binary, img_result initWindows() - img_ = cv2.imdecode(np.fromfile(r'.\explain\morphology.png', dtype=np.uint8), 0) + img_ = cv2.imdecode(np.fromfile(r".\explain\morphology.png", dtype=np.uint8), 0) _, img_binary = cv2.threshold(img_, 128, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU) placePic1(img_binary, "原二值图像") row, col = img_binary.shape minrc = min(row, col) - kernelSize = askinteger("结构算子大小设置", "请输入结构算子大小", initialvalue=3, minvalue=3, maxvalue=minrc) - paraLists = [["请选择结构算子形状", ['矩形','十字形', '椭圆形']]] + kernelSize = askinteger( + "结构算子大小设置", + "请输入结构算子大小", + initialvalue=3, + minvalue=3, + maxvalue=minrc, + ) + paraLists = [["请选择结构算子形状", ["矩形", "十字形", "椭圆形"]]] paraW = listWindow(root, "结构算子选项", paraLists) structType = paraW.returnValue if structType == 1: - kernel = cv2.getStructuringElement(cv2.MORPH_CROSS,(kernelSize,kernelSize)) + kernel = cv2.getStructuringElement(cv2.MORPH_CROSS, (kernelSize, kernelSize)) elif structType == 2: - kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE,(kernelSize,kernelSize)) + kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (kernelSize, kernelSize)) else: - kernel = cv2.getStructuringElement(cv2.MORPH_RECT,(kernelSize,kernelSize)) + kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (kernelSize, kernelSize)) img_result = cv2.dilate(img_binary, kernel, iterations=1) placePic2(img_result, "膨胀后的图像") - myWindow.explainText.set("膨胀运算是对二值化物体边界点扩充,将与物体接触的所以背景点合并到该物体中,使边界向外扩张。") + myWindow.explainText.set( + "膨胀运算是对二值化物体边界点扩充,将与物体接触的所以背景点合并到该物体中,使边界向外扩张。" + ) + def Erode1(): - ''' 形态学处理-腐蚀运算 ''' - global img_open,img_binary, img_result + """形态学处理-腐蚀运算""" + global img_open, img_binary, img_result initWindows() - img_ = cv2.imdecode(np.fromfile(r'.\explain\morphology.png', dtype=np.uint8), 0) + img_ = cv2.imdecode(np.fromfile(r".\explain\morphology.png", dtype=np.uint8), 0) _, img_binary = cv2.threshold(img_, 128, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU) placePic1(img_binary, "原二值图像") - row,col=img_binary.shape - minrc=min(row,col) - kernelSize=askinteger("结构算子设置","请输入结构算子大小",initialvalue=3,minvalue=3,maxvalue=minrc) - paraLists = [["请选择结构算子形状", ['矩形', '十字形', '椭圆形']]] + row, col = img_binary.shape + minrc = min(row, col) + kernelSize = askinteger( + "结构算子设置", "请输入结构算子大小", initialvalue=3, minvalue=3, maxvalue=minrc + ) + paraLists = [["请选择结构算子形状", ["矩形", "十字形", "椭圆形"]]] paraW = listWindow(root, "结构算子选项", paraLists) structType = paraW.returnValue if structType == 1: - kernel = cv2.getStructuringElement(cv2.MORPH_CROSS,(kernelSize,kernelSize)) + kernel = cv2.getStructuringElement(cv2.MORPH_CROSS, (kernelSize, kernelSize)) elif structType == 2: - kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE,(kernelSize,kernelSize)) + kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (kernelSize, kernelSize)) else: # 矩形 - kernel = cv2.getStructuringElement(cv2.MORPH_RECT,(kernelSize,kernelSize)) - img_result = cv2.erode(img_binary,kernel,iterations = 1) - placePic2(img_result,"腐蚀后的图像") - imgExplain = cv2.imread(r'.\explain\Erosion.jpg',0) - picLists = [["形态学腐蚀的定义:集合A被集合B腐蚀为:A○—B={x:B+x.A}",imgExplain]] + kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (kernelSize, kernelSize)) + img_result = cv2.erode(img_binary, kernel, iterations=1) + placePic2(img_result, "腐蚀后的图像") + imgExplain = cv2.imread(r".\explain\Erosion.jpg", 0) + picLists = [["形态学腐蚀的定义:集合A被集合B腐蚀为:A○—B={x:B+x.A}", imgExplain]] myWindow.showFig1(picLists) + def Open_operation1(): - ''' 形态学处理-开运算 ''' + """形态学处理-开运算""" global img_open, img_binary, img_result initWindows() - img_ = cv2.imdecode(np.fromfile(r'.\explain\morphology.png', dtype=np.uint8), 0) + img_ = cv2.imdecode(np.fromfile(r".\explain\morphology.png", dtype=np.uint8), 0) _, img_binary = cv2.threshold(img_, 128, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU) placePic1(img_binary, "原二值图像") row, col = img_binary.shape minrc = min(row, col) - kernelSize = askinteger("结构算子设置", "请输入结构算子大小", initialvalue=3, minvalue=3, maxvalue=minrc) - paraLists = [["请选择结构算子形状", ['矩形', '十字形', '椭圆形']]] + kernelSize = askinteger( + "结构算子设置", "请输入结构算子大小", initialvalue=3, minvalue=3, maxvalue=minrc + ) + paraLists = [["请选择结构算子形状", ["矩形", "十字形", "椭圆形"]]] paraW = listWindow(root, "结构算子选项", paraLists) structType = paraW.returnValue if structType == 1: - kernel = cv2.getStructuringElement(cv2.MORPH_CROSS,(kernelSize,kernelSize)) + kernel = cv2.getStructuringElement(cv2.MORPH_CROSS, (kernelSize, kernelSize)) elif structType == 2: - kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE,(kernelSize,kernelSize)) + kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (kernelSize, kernelSize)) else: # 矩形 - kernel = cv2.getStructuringElement(cv2.MORPH_RECT,(kernelSize,kernelSize)) + kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (kernelSize, kernelSize)) img_result = cv2.morphologyEx(img_binary, cv2.MORPH_OPEN, kernel) placePic2(img_result, "开运算后的图像") - myWindow.explainText.set("开运算数学上是先腐蚀后膨胀的结果,物理结果为完全删除了不能包含结构元素的对象区域,平滑了对象的轮廓,断开了狭窄的连接,去掉了细小的突出部分。") + myWindow.explainText.set( + "开运算数学上是先腐蚀后膨胀的结果,物理结果为完全删除了不能包含结构元素的对象区域,平滑了对象的轮廓,断开了狭窄的连接,去掉了细小的突出部分。" + ) + def Close_operation1(): - ''' 形态学处理-闭运算 ''' + """形态学处理-闭运算""" global img_open, img_binary, img_result initWindows() - img_ = cv2.imdecode(np.fromfile(r'.\explain\morphology.png', dtype=np.uint8), 0) + img_ = cv2.imdecode(np.fromfile(r".\explain\morphology.png", dtype=np.uint8), 0) _, img_binary = cv2.threshold(img_, 128, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU) placePic1(img_binary, "原二值图像") row, col = img_binary.shape minrc = min(row, col) - kernelSize = askinteger("结构算子设置", "请输入结构算子大小", initialvalue=3, minvalue=3, maxvalue=minrc) - paraLists = [["请选择结构算子形状", ['矩形', '十字形', '椭圆形']]] + kernelSize = askinteger( + "结构算子设置", "请输入结构算子大小", initialvalue=3, minvalue=3, maxvalue=minrc + ) + paraLists = [["请选择结构算子形状", ["矩形", "十字形", "椭圆形"]]] paraW = listWindow(root, "结构算子选项", paraLists) structType = paraW.returnValue if structType == 1: - kernel = cv2.getStructuringElement(cv2.MORPH_CROSS,(kernelSize,kernelSize)) + kernel = cv2.getStructuringElement(cv2.MORPH_CROSS, (kernelSize, kernelSize)) elif structType == 2: - kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE,(kernelSize,kernelSize)) + kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (kernelSize, kernelSize)) else: # 矩形 - kernel = cv2.getStructuringElement(cv2.MORPH_RECT,(kernelSize,kernelSize)) + kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (kernelSize, kernelSize)) img_result = cv2.morphologyEx(img_binary, cv2.MORPH_CLOSE, kernel) placePic2(img_result, "闭运算后的图像") - myWindow.explainText.set("闭运算在数学上是先膨胀再腐蚀的结果,物理结果也是会平滑对象的轮廓,但是与开运算不同的是,闭运算一般会将狭窄的缺口连接起来形成细长的弯口,并填充比结构元素小的洞。") + myWindow.explainText.set( + "闭运算在数学上是先膨胀再腐蚀的结果,物理结果也是会平滑对象的轮廓,但是与开运算不同的是,闭运算一般会将狭窄的缺口连接起来形成细长的弯口,并填充比结构元素小的洞。" + ) + def Gradient_morphology1(): - ''' 形态学处理-形态学梯度 ''' + """形态学处理-形态学梯度""" global img_open, img_binary, img_result initWindows() - img_ = cv2.imdecode(np.fromfile(r'.\explain\morphology.png', dtype=np.uint8), 0) + img_ = cv2.imdecode(np.fromfile(r".\explain\morphology.png", dtype=np.uint8), 0) _, img_binary = cv2.threshold(img_, 128, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU) placePic1(img_binary, "原二值图像") row, col = img_binary.shape minrc = min(row, col) - kernelSize = askinteger("结构算子设置", "请输入结构算子大小", initialvalue=3, minvalue=3, maxvalue=minrc) - paraLists = [["请选择结构算子形状", ['矩形', '十字形', '椭圆形']]] + kernelSize = askinteger( + "结构算子设置", "请输入结构算子大小", initialvalue=3, minvalue=3, maxvalue=minrc + ) + paraLists = [["请选择结构算子形状", ["矩形", "十字形", "椭圆形"]]] paraW = listWindow(root, "结构算子选项", paraLists) structType = paraW.returnValue if structType == 1: - kernel = cv2.getStructuringElement(cv2.MORPH_CROSS,(kernelSize,kernelSize)) + kernel = cv2.getStructuringElement(cv2.MORPH_CROSS, (kernelSize, kernelSize)) elif structType == 2: - kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE,(kernelSize,kernelSize)) + kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (kernelSize, kernelSize)) else: # 矩形 - kernel = cv2.getStructuringElement(cv2.MORPH_RECT,(kernelSize,kernelSize)) + kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (kernelSize, kernelSize)) img_result = cv2.morphologyEx(img_binary, cv2.MORPH_GRADIENT, kernel) placePic2(img_result, "形态学梯度图像") - myWindow.explainText.set("形态学梯度是一幅图像膨胀与腐蚀的差别,结果看上去就像前景物体的轮廓。") + myWindow.explainText.set( + "形态学梯度是一幅图像膨胀与腐蚀的差别,结果看上去就像前景物体的轮廓。" + ) + def Tophat1(): - ''' 形态学处理-礼帽运算 ''' + """形态学处理-礼帽运算""" global img_open, img_binary, img_result initWindows() - img_ = cv2.imdecode(np.fromfile(r'.\explain\morphology.png', dtype=np.uint8), 0) + img_ = cv2.imdecode(np.fromfile(r".\explain\morphology.png", dtype=np.uint8), 0) _, img_binary = cv2.threshold(img_, 128, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU) placePic1(img_binary, "原二值图像") row, col = img_binary.shape minrc = min(row, col) - kernelSize = askinteger("结构算子设置", "请输入结构算子大小", initialvalue=4, minvalue=3, maxvalue=minrc) - paraLists = [["请选择结构算子形状", ['矩形', '十字形', '椭圆形']]] + kernelSize = askinteger( + "结构算子设置", "请输入结构算子大小", initialvalue=4, minvalue=3, maxvalue=minrc + ) + paraLists = [["请选择结构算子形状", ["矩形", "十字形", "椭圆形"]]] paraW = listWindow(root, "结构算子选项", paraLists) structType = paraW.returnValue if structType == 1: - kernel = cv2.getStructuringElement(cv2.MORPH_CROSS,(kernelSize,kernelSize)) + kernel = cv2.getStructuringElement(cv2.MORPH_CROSS, (kernelSize, kernelSize)) elif structType == 2: - kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE,(kernelSize,kernelSize)) + kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (kernelSize, kernelSize)) else: # 矩形 - kernel = cv2.getStructuringElement(cv2.MORPH_RECT,(kernelSize,kernelSize)) + kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (kernelSize, kernelSize)) img_result = cv2.morphologyEx(img_binary, cv2.MORPH_TOPHAT, kernel) placePic2(img_result, "形态学礼帽图像") myWindow.explainText.set("形态学礼帽是原始图像与进行开运算之后得到的图像的差") + def Blackhat1(): - ''' 形态学处理-黑帽运算 ''' + """形态学处理-黑帽运算""" global img_open, img_binary, img_result initWindows() - img_ = cv2.imdecode(np.fromfile(r'.\explain\morphology.png', dtype=np.uint8), 0) + img_ = cv2.imdecode(np.fromfile(r".\explain\morphology.png", dtype=np.uint8), 0) _, img_binary = cv2.threshold(img_, 128, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU) placePic1(img_binary, "原二值图像") row, col = img_binary.shape minrc = min(row, col) - kernelSize = askinteger("结构算子设置", "请输入结构算子大小", initialvalue=4, minvalue=3, maxvalue=minrc) - paraLists = [["请选择结构算子形状", ['矩形', '十字形', '椭圆形']]] + kernelSize = askinteger( + "结构算子设置", "请输入结构算子大小", initialvalue=4, minvalue=3, maxvalue=minrc + ) + paraLists = [["请选择结构算子形状", ["矩形", "十字形", "椭圆形"]]] paraW = listWindow(root, "结构算子选项", paraLists) structType = paraW.returnValue if structType == 1: - kernel = cv2.getStructuringElement(cv2.MORPH_CROSS,(kernelSize,kernelSize)) + kernel = cv2.getStructuringElement(cv2.MORPH_CROSS, (kernelSize, kernelSize)) elif structType == 2: - kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE,(kernelSize,kernelSize)) + kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (kernelSize, kernelSize)) else: # 矩形 - kernel = cv2.getStructuringElement(cv2.MORPH_RECT,(kernelSize,kernelSize)) + kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (kernelSize, kernelSize)) img_result = cv2.morphologyEx(img_binary, cv2.MORPH_BLACKHAT, kernel) placePic2(img_result, "形态学黑帽图像") myWindow.explainText.set("形态学黑帽是进行闭运算之后得到的图像与原始图像的差。") + def Fill_holes1(): - ''' 形态学处理-孔洞填充运算 ''' + """形态学处理-孔洞填充运算""" global img_open, img_binary, img_result initWindows() - img_ = cv2.imdecode(np.fromfile(r'.\explain\morphology.png', dtype=np.uint8), 0) + img_ = cv2.imdecode(np.fromfile(r".\explain\morphology.png", dtype=np.uint8), 0) _, img_binary = cv2.threshold(img_, 128, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU) placePic1(img_binary, "原二值图像") - explainPic = cv2.imread(r'.\explain\fill_holes.jpg') - text="孔洞填充是以带有白色边框的黑色图像Marker为起点,对其进行连续膨胀,并以原图像的补集作为遮罩Mask,来限制膨胀,直至膨胀收敛.最后对Marker取补即得到最终图像,与原图相减可得到填充图像。" + explainPic = cv2.imread(r".\explain\fill_holes.jpg") + text = "孔洞填充是以带有白色边框的黑色图像Marker为起点,对其进行连续膨胀,并以原图像的补集作为遮罩Mask,来限制膨胀,直至膨胀收敛.最后对Marker取补即得到最终图像,与原图相减可得到填充图像。" picLists = [["形态学变换公式", explainPic]] - myWindow.showExplain(text,picLists) + myWindow.showExplain(text, picLists) row, col = img_binary.shape minrc = min(row, col) - kernelSize = askinteger("结构算子设置", "请输入结构算子大小", initialvalue=3, minvalue=3, maxvalue=minrc) - paraLists = [["请选择结构算子形状", ['矩形', '十字形', '椭圆形']]] + kernelSize = askinteger( + "结构算子设置", "请输入结构算子大小", initialvalue=3, minvalue=3, maxvalue=minrc + ) + paraLists = [["请选择结构算子形状", ["矩形", "十字形", "椭圆形"]]] paraW = listWindow(root, "结构算子选项", paraLists) structType = paraW.returnValue if structType == 1: - kernel = cv2.getStructuringElement(cv2.MORPH_CROSS,(kernelSize,kernelSize)) + kernel = cv2.getStructuringElement(cv2.MORPH_CROSS, (kernelSize, kernelSize)) elif structType == 2: - kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE,(kernelSize,kernelSize)) + kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (kernelSize, kernelSize)) else: # 矩形 - kernel = cv2.getStructuringElement(cv2.MORPH_RECT,(kernelSize,kernelSize)) - img_result = morphology.fill_holes(img_binary,kernel) + kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (kernelSize, kernelSize)) + img_result = morphology.fill_holes(img_binary, kernel) placePic2(img_result, "填充孔洞图像") + def Skeleton1(): - ''' 形态学处理-骨架提取 ''' + """形态学处理-骨架提取""" global img_open, img_binary, img_result initWindows() - img_ = cv2.imdecode(np.fromfile(r'.\explain\morphology.png', dtype=np.uint8), 0) + img_ = cv2.imdecode(np.fromfile(r".\explain\morphology.png", dtype=np.uint8), 0) _, img_binary = cv2.threshold(img_, 128, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU) placePic1(img_binary, "原二值图像") row, col = img_binary.shape minrc = min(row, col) - kernelSize = askinteger("结构算子设置", "请输入结构算子大小", initialvalue=4, minvalue=3, maxvalue=minrc) - paraLists = [["请选择结构算子形状", ['矩形', '十字形', '椭圆形']]] + kernelSize = askinteger( + "结构算子设置", "请输入结构算子大小", initialvalue=4, minvalue=3, maxvalue=minrc + ) + paraLists = [["请选择结构算子形状", ["矩形", "十字形", "椭圆形"]]] paraW = listWindow(root, "结构算子选项", paraLists) structType = paraW.returnValue if structType == 0: # 矩形 - kernel = cv2.getStructuringElement(cv2.MORPH_RECT,(kernelSize,kernelSize)) + kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (kernelSize, kernelSize)) elif structType == 1: - kernel = cv2.getStructuringElement(cv2.MORPH_CROSS,(kernelSize,kernelSize)) + kernel = cv2.getStructuringElement(cv2.MORPH_CROSS, (kernelSize, kernelSize)) elif structType == 2: - kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE,(kernelSize,kernelSize)) + kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (kernelSize, kernelSize)) img_result = cv2.morphologyEx(img_binary, cv2.MORPH_BLACKHAT, kernel) placePic2(img_result, "骨架提取") myWindow.explainText.set("图像骨架提取,实际上就是提取目标在图像上的中心像素轮廓。") -#---------------------------------------- + + +# ---------------------------------------- def Basic_global_thresholding(): - ''' 基本全阈值分割 ''' + """基本全阈值分割""" global picSize, img_gray, img_result initWindows() if img_gray is None: - showwarning(title='警告', message='请先在文件菜单下打开图片!') + showwarning(title="警告", message="请先在文件菜单下打开图片!") return - th=segmentation.basic_global_thresholding(img_gray) - _,img_result=cv2.threshold(img_gray,th,255,cv2.THRESH_BINARY) + th = segmentation.basic_global_thresholding(img_gray) + _, img_result = cv2.threshold(img_gray, th, 255, cv2.THRESH_BINARY) placePic2(img_result, "基本全阈值分割图像") - myWindow.explainText.set('''全局阈值分割就是将整个图像灰度值大于阈值(thresh)的像素调为白色,小于或等于阈值的调整为黑色,也可以反过来。 ''') + myWindow.explainText.set( + """全局阈值分割就是将整个图像灰度值大于阈值(thresh)的像素调为白色,小于或等于阈值的调整为黑色,也可以反过来。 """ + ) + def Otsu_threshold(): - ''' OSTU全阈值分割 ''' + """OSTU全阈值分割""" global picSize, img_gray, img_result initWindows() if img_gray is None: - showwarning(title='警告', message='请先在文件菜单下打开图片!') + showwarning(title="警告", message="请先在文件菜单下打开图片!") return - answer=askyesno("平滑选项","请选择在分割前是否使用高斯平滑") - img=img_gray.copy() + answer = askyesno("平滑选项", "请选择在分割前是否使用高斯平滑") + img = img_gray.copy() if answer: - img = cv2.GaussianBlur(img,(5,5),0) - _, img_result = cv2.threshold(img, 128, 255, cv2.THRESH_BINARY+cv2.THRESH_OTSU) + img = cv2.GaussianBlur(img, (5, 5), 0) + _, img_result = cv2.threshold(img, 128, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU) placePic2(img_result, "单阈值OTSU算法分割图像") - myWindow.explainText.set('''以最佳阈值将图像的灰度直方图分割成两部分,使两类之间的方差取最大值,即分离性最大。 - 此算法利用了最小二乘法原理。''') + myWindow.explainText.set( + """以最佳阈值将图像的灰度直方图分割成两部分,使两类之间的方差取最大值,即分离性最大。 + 此算法利用了最小二乘法原理。""" + ) + def Adaptive_mean_threshold(): - ''' 基于邻域均值的自适应阈值分割 ''' + """基于邻域均值的自适应阈值分割""" global picSize, img_gray, img_result initWindows() if img_gray is None: - showwarning(title='警告', message='请先在文件菜单下打开图片!') + showwarning(title="警告", message="请先在文件菜单下打开图片!") return # img_gray = cv2.GaussianBlur(img_gray, (5, 5), 0) # img_gray=cv2.equalizeHist(img_gray) - maxSize=(min(img_gray.shape[:2]))//2 - kernalSize=askinteger("窗口设置","请输入邻域窗口大小",initialvalue=15,minvalue=3,maxvalue=maxSize) + maxSize = (min(img_gray.shape[:2])) // 2 + kernalSize = askinteger( + "窗口设置", "请输入邻域窗口大小", initialvalue=15, minvalue=3, maxvalue=maxSize + ) # img_gray = cv2.GaussianBlur(img_gray, (5, 5), 0) - img_result = cv2.adaptiveThreshold(img_gray, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, kernalSize, 20) + img_result = cv2.adaptiveThreshold( + img_gray, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, kernalSize, 20 + ) placePic2(img_result, "基于邻域均值的自适应阈值分割图像") myWindow.explainText.set( - "自适应阈值,则是根据像素的邻域块的像素值分布来确定该像素位置上的二值化阈值。") + "自适应阈值,则是根据像素的邻域块的像素值分布来确定该像素位置上的二值化阈值。" + ) + def Adaptive_gaussian_threshold(): - ''' 基于高斯滤波的自适应阈值分割 ''' + """基于高斯滤波的自适应阈值分割""" global picSize, img_gray, img_result initWindows() if img_gray is None: - showwarning(title='警告', message='请先在文件菜单下打开图片!') + showwarning(title="警告", message="请先在文件菜单下打开图片!") return - maxSize=(min(img_gray.shape[:2]))//2 - kernalSize=askinteger("窗口设置","请输入高斯窗口大小",initialvalue=15,minvalue=3,maxvalue=maxSize) + maxSize = (min(img_gray.shape[:2])) // 2 + kernalSize = askinteger( + "窗口设置", "请输入高斯窗口大小", initialvalue=15, minvalue=3, maxvalue=maxSize + ) # img_gray = cv2.GaussianBlur(img_gray, (5, 5), 0) - img_result = cv2.adaptiveThreshold(img_gray, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, kernalSize, 0) + img_result = cv2.adaptiveThreshold( + img_gray, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, kernalSize, 0 + ) placePic2(img_result, "基于高斯滤波的自适应阈值分割图像") myWindow.explainText.set( "基于高斯滤波的自适应阈值分割,则是根据像素的邻域块的像素值分布来确定该像素位置上的二值化阈值。" - "\n计算某个邻域(局部)的高斯加权平均(高斯滤波)来确定阈值。") + "\n计算某个邻域(局部)的高斯加权平均(高斯滤波)来确定阈值。" + ) + # def Moving_threshold(): # ''' 滑动平均阈值分割 ''' @@ -2083,66 +2603,89 @@ def Adaptive_gaussian_threshold(): # placePic2(img_result, "滑动平均阈值分割图像") # myWindow.explainText.set('''滑动平均变阈值法:一般沿行、列或Z字形进行滑动平均,将像素值与滑动平均值进行比较,根据比较情况进行分割。''') -#系统默认图片的分割效果---------------------------------------- + +# 系统默认图片的分割效果---------------------------------------- def Basic_global_thresholding1(): - ''' 基本全阈值分割 ''' + """基本全阈值分割""" global picSize, img_gray, img_result initWindows() - img_ = cv2.imdecode(np.fromfile(r'.\explain\paopao.jpg', dtype=np.uint8), 0) - placePic1(img_,"待分割图像") - th=segmentation.basic_global_thresholding(img_) - _,img_result=cv2.threshold(img_,th,255,cv2.THRESH_BINARY) + img_ = cv2.imdecode(np.fromfile(r".\explain\paopao.jpg", dtype=np.uint8), 0) + placePic1(img_, "待分割图像") + th = segmentation.basic_global_thresholding(img_) + _, img_result = cv2.threshold(img_, th, 255, cv2.THRESH_BINARY) placePic2(img_result, "基本全阈值分割图像") - myWindow.explainText.set('''全局阈值分割就是将整个图像灰度值大于阈值(thresh)的像素调为白色,小于或等于阈值的调整为黑色,也可以反过来。 ''') + myWindow.explainText.set( + """全局阈值分割就是将整个图像灰度值大于阈值(thresh)的像素调为白色,小于或等于阈值的调整为黑色,也可以反过来。 """ + ) + def Otsu_threshold1(): - ''' OSTU全阈值分割 ''' + """OSTU全阈值分割""" global picSize, img_gray, img_result initWindows() - img_ = cv2.imdecode(np.fromfile(r'.\explain\paopao.jpg', dtype=np.uint8), 0) - placePic1(img_,"待分割图像") - answer=askyesno("平滑选项","请选择在分割前是否使用高斯平滑") + img_ = cv2.imdecode(np.fromfile(r".\explain\paopao.jpg", dtype=np.uint8), 0) + placePic1(img_, "待分割图像") + answer = askyesno("平滑选项", "请选择在分割前是否使用高斯平滑") if answer: - img_ = cv2.GaussianBlur(img_,(5,5),0) - _, img_result = cv2.threshold(img_, 128, 255, cv2.THRESH_BINARY+cv2.THRESH_OTSU) + img_ = cv2.GaussianBlur(img_, (5, 5), 0) + _, img_result = cv2.threshold(img_, 128, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU) placePic2(img_result, "单阈值OTSU算法分割图像") - myWindow.explainText.set('''以最佳阈值将图像的灰度直方图分割成两部分,使两类之间的方差取最大值,即分离性最大。 - 此算法利用了最小二乘法原理。''') + myWindow.explainText.set( + """以最佳阈值将图像的灰度直方图分割成两部分,使两类之间的方差取最大值,即分离性最大。 + 此算法利用了最小二乘法原理。""" + ) + def Adaptive_mean_threshold1(): - ''' 基于邻域均值的自适应阈值分割 ''' + """基于邻域均值的自适应阈值分割""" global picSize, img_gray, img_result initWindows() - img_ = cv2.imdecode(np.fromfile(r'.\explain\light_bar.png', dtype=np.uint8), 0) - placePic1(img_,"待分割图像") + img_ = cv2.imdecode(np.fromfile(r".\explain\light_bar.png", dtype=np.uint8), 0) + placePic1(img_, "待分割图像") # img_gray = cv2.GaussianBlur(img_gray, (5, 5), 0) # img_gray=cv2.equalizeHist(img_gray) - maxSize=(min(img_.shape[:2]))//2 - kernalSize=askinteger("窗口设置","请输入邻域窗口大小",initialvalue=15,minvalue=3,maxvalue=maxSize) + maxSize = (min(img_.shape[:2])) // 2 + kernalSize = askinteger( + "窗口设置", "请输入邻域窗口大小", initialvalue=15, minvalue=3, maxvalue=maxSize + ) # img_ = cv2.GaussianBlur(img_, (5, 5), 0) - if kernalSize%2==0: - kernalSize=kernalSize-1 - img_result = cv2.adaptiveThreshold(img_, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, kernalSize, 20) + if kernalSize % 2 == 0: + kernalSize = kernalSize - 1 + img_result = cv2.adaptiveThreshold( + img_, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, kernalSize, 20 + ) placePic2(img_result, "基于邻域均值的自适应阈值分割图像") myWindow.explainText.set( - "自适应阈值,则是根据像素的邻域块的像素值分布来确定该像素位置上的二值化阈值。") + "自适应阈值,则是根据像素的邻域块的像素值分布来确定该像素位置上的二值化阈值。" + ) + def Adaptive_gaussian_threshold1(): - ''' 基于高斯滤波的自适应阈值分割 ''' + """基于高斯滤波的自适应阈值分割""" global picSize, img_gray, img_result initWindows() - img_ = cv2.imdecode(np.fromfile(r'.\explain\light_bar.png', dtype=np.uint8), 0) - placePic1(img_,"待分割图像") - maxSize=(min(img_.shape[:2]))//2 - kernalSize=askinteger("窗口设置","请输入高斯窗口大小,必须为奇数",initialvalue=25,minvalue=3,maxvalue=maxSize) - if kernalSize%2==0: - kernalSize=kernalSize-1 + img_ = cv2.imdecode(np.fromfile(r".\explain\light_bar.png", dtype=np.uint8), 0) + placePic1(img_, "待分割图像") + maxSize = (min(img_.shape[:2])) // 2 + kernalSize = askinteger( + "窗口设置", + "请输入高斯窗口大小,必须为奇数", + initialvalue=25, + minvalue=3, + maxvalue=maxSize, + ) + if kernalSize % 2 == 0: + kernalSize = kernalSize - 1 # img_ = cv2.GaussianBlur(img_, (5, 5), 0) - img_result = cv2.adaptiveThreshold(img_, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, kernalSize, 18) + img_result = cv2.adaptiveThreshold( + img_, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, kernalSize, 18 + ) placePic2(img_result, "基于高斯滤波的自适应阈值分割图像") myWindow.explainText.set( "基于高斯滤波的自适应阈值分割,则是根据像素的邻域块的像素值分布来确定该像素位置上的二值化阈值。" - "\n计算某个邻域(局部)的高斯加权平均(高斯滤波)来确定阈值。") + "\n计算某个邻域(局部)的高斯加权平均(高斯滤波)来确定阈值。" + ) + # def Moving_threshold1(): # ''' 滑动平均阈值分割 ''' @@ -2161,298 +2704,382 @@ def Adaptive_gaussian_threshold1(): # myWindow.explainText.set('''滑动平均变阈值法:一般沿行、列或Z字形进行滑动平均,将像素值与滑动平均值进行比较,根据比较情况进行分割。''') -#---------------------------------------------------- +# ---------------------------------------------------- def Draw_contours(): - ''' 提取目标轮廓 ''' - global img_open, img_binary, img_result,getContours,contoursMinArea + """提取目标轮廓""" + global img_open, img_binary, img_result, getContours, contoursMinArea initWindows() if img_open is None: - showwarning(title='警告', message='请先在文件菜单下打开图片!') + showwarning(title="警告", message="请先在文件菜单下打开图片!") return placePic1(img_open, "原图像") - myWindow.explainText.set("OpenCV的findContours()函数来查找检测物体的轮廓。OpenCV的drawContours在图像上绘制轮廓。区域面积小于设置最小值的区域将会被忽略") - row,col=img_open.shape[:2] - maxV=row*col - minArea=askfloat("最小区域设置","请输入最小区域外接矩形面积值",initialvalue=50,minvalue=1,maxvalue=maxV) + myWindow.explainText.set( + "OpenCV的findContours()函数来查找检测物体的轮廓。OpenCV的drawContours在图像上绘制轮廓。区域面积小于设置最小值的区域将会被忽略" + ) + row, col = img_open.shape[:2] + maxV = row * col + minArea = askfloat( + "最小区域设置", + "请输入最小区域外接矩形面积值", + initialvalue=50, + minvalue=1, + maxvalue=maxV, + ) if not minArea: - showwarning("警告","请输入最小区域外接矩形面积值") + showwarning("警告", "请输入最小区域外接矩形面积值") return - img_result=description.draw_contours(img_open,minArea) - contoursMinArea=minArea + img_result = description.draw_contours(img_open, minArea) + contoursMinArea = minArea placePic2(img_result, "添加了目标轮廓的图像") + def Get_region_description(): - ''' + """ 获得区域描述,包括质心、方向、面积、周长、圆形度、矩形度 - ''' - global img_open, img_binary, img_result,contoursMinArea + """ + global img_open, img_binary, img_result, contoursMinArea initWindows() if contoursMinArea is None: - showwarning(title='警告', message='请重新获得目标轮廓!') + showwarning(title="警告", message="请重新获得目标轮廓!") return placePic1(img_result, "目标轮廓图像") - img_result,contours=description.get_region_description(img_result,contoursMinArea) - contoursMinArea=None + img_result, contours = description.get_region_description( + img_result, contoursMinArea + ) + contoursMinArea = None placePic2(img_result, "添加了区域方向的图像") - i=0 - descipStr='' + i = 0 + descipStr = "" for cnt in contours: - i+=1 - descipStr+="第%d个区域:"%i - descipStr+="质心为(%d,%d),"%(cnt[0][0],cnt[0][1]) - descipStr+="方向:%d度,"%cnt[1] - descipStr+="面积:%d,"%cnt[2] - descipStr+="周长:%d,"%cnt[3] - descipStr+="圆形度:%0.2f,"%cnt[4] - descipStr+= "矩形度:%0.2f"%cnt[5] - descipStr +='\n' + i += 1 + descipStr += "第%d个区域:" % i + descipStr += "质心为(%d,%d)," % (cnt[0][0], cnt[0][1]) + descipStr += "方向:%d度," % cnt[1] + descipStr += "面积:%d," % cnt[2] + descipStr += "周长:%d," % cnt[3] + descipStr += "圆形度:%0.2f," % cnt[4] + descipStr += "矩形度:%0.2f" % cnt[5] + descipStr += "\n" myWindow.explainText.set(descipStr) + def Moment_invariants(): - ''' 图像经过多种几何变换后的不变矩 ''' + """图像经过多种几何变换后的不变矩""" global img_open, img_binary, img_result initWindows() if img_gray is None: - showwarning(title='警告', message='请先在文件菜单下打开图片!') + showwarning(title="警告", message="请先在文件菜单下打开图片!") return placePic1(img_gray, "原图像") - imgLists,textLists,momentLists=description.moment_invariants(img_gray) - i=0 - descipStr='图像几何变换后的7个不变矩\n' + imgLists, textLists, momentLists = description.moment_invariants(img_gray) + i = 0 + descipStr = "图像几何变换后的7个不变矩\n" for moment in momentLists: - descipStr+=textLists[i]+'\t' - i+=1 + descipStr += textLists[i] + "\t" + i += 1 for mm in moment: - descipStr+="%0.2f"%mm+'\t' - descipStr +='\n' + descipStr += "%0.2f" % mm + "\t" + descipStr += "\n" myWindow.explainText.set(descipStr) - #合并几何变换后的图像 + # 合并几何变换后的图像 # picLists=[] # for tt,img in zip(textLists,imgLists): # picLists.append([tt, img]) # combinedImg1 = myWindow.combine_images(picLists[:3], explain=False,fontSize=1)#合并两张图片 # combinedImg2 = myWindow.combine_images(picLists[3:], explain=False,fontSize=1)#合并两张图片 - combinedImg1 = myWindow.combine_images(imgLists[:3])#合并三张图片 - combinedImg2 = myWindow.combine_images(imgLists[3:])#合并后几张图片 - picLists=[combinedImg1,combinedImg2] - img_result=myWindow.combine_images(picLists) + combinedImg1 = myWindow.combine_images(imgLists[:3]) # 合并三张图片 + combinedImg2 = myWindow.combine_images(imgLists[3:]) # 合并后几张图片 + picLists = [combinedImg1, combinedImg2] + img_result = myWindow.combine_images(picLists) placePic2(img_result, "各种几何变换图像") -#系统默认图片展示图像描述效果--------------------------------------- + +# 系统默认图片展示图像描述效果--------------------------------------- def Draw_contours1(): - ''' 提取目标轮廓 ''' - global img_open, img_binary, img_result,getContours,contoursMinArea + """提取目标轮廓""" + global img_open, img_binary, img_result, getContours, contoursMinArea initWindows() - img_ = cv2.imdecode(np.fromfile(r'.\explain\describe.png', dtype=np.uint8), 0) + img_ = cv2.imdecode(np.fromfile(r".\explain\describe.png", dtype=np.uint8), 0) placePic1(img_, "原图像") - myWindow.explainText.set("OpenCV的findContours()函数来查找检测物体的轮廓。OpenCV的drawContours在图像上绘制轮廓。区域面积小于设置最小值的区域将会被忽略") - row,col=img_.shape[:2] - maxV=row*col - minArea=askfloat("最小区域设置","请输入最小区域外接矩形面积值",initialvalue=50,minvalue=1,maxvalue=maxV) + myWindow.explainText.set( + "OpenCV的findContours()函数来查找检测物体的轮廓。OpenCV的drawContours在图像上绘制轮廓。区域面积小于设置最小值的区域将会被忽略" + ) + row, col = img_.shape[:2] + maxV = row * col + minArea = askfloat( + "最小区域设置", + "请输入最小区域外接矩形面积值", + initialvalue=50, + minvalue=1, + maxvalue=maxV, + ) if not minArea: - showwarning("警告","请输入最小区域外接矩形面积值") + showwarning("警告", "请输入最小区域外接矩形面积值") return - img_result=description.draw_contours(img_,minArea) - contoursMinArea=minArea + img_result = description.draw_contours(img_, minArea) + contoursMinArea = minArea placePic2(img_result, "添加了目标轮廓的图像") + def Get_region_description1(): - ''' + """ 获得区域描述,包括质心、方向、面积、周长、圆形度、矩形度 - ''' - global img_open, img_binary, img_result,contoursMinArea + """ + global img_open, img_binary, img_result, contoursMinArea initWindows() if contoursMinArea is None: - showwarning(title='警告', message='请重新获得目标轮廓!') + showwarning(title="警告", message="请重新获得目标轮廓!") return placePic1(img_result, "目标轮廓图像") - img_result,contours=description.get_region_description(img_result,contoursMinArea) - contoursMinArea=None + img_result, contours = description.get_region_description( + img_result, contoursMinArea + ) + contoursMinArea = None placePic2(img_result, "添加了区域方向的图像") - i=0 - descipStr='' + i = 0 + descipStr = "" for cnt in contours: - i+=1 - descipStr+="第%d个区域:"%i - descipStr+="质心为(%d,%d),"%(cnt[0][0],cnt[0][1]) - descipStr+="方向:%d度,"%cnt[1] - descipStr+="面积:%d,"%cnt[2] - descipStr+="周长:%d,"%cnt[3] - descipStr+="圆形度:%0.2f,"%cnt[4] - descipStr+= "矩形度:%0.2f"%cnt[5] - descipStr +='\n' + i += 1 + descipStr += "第%d个区域:" % i + descipStr += "质心为(%d,%d)," % (cnt[0][0], cnt[0][1]) + descipStr += "方向:%d度," % cnt[1] + descipStr += "面积:%d," % cnt[2] + descipStr += "周长:%d," % cnt[3] + descipStr += "圆形度:%0.2f," % cnt[4] + descipStr += "矩形度:%0.2f" % cnt[5] + descipStr += "\n" myWindow.explainText.set(descipStr) + def Moment_invariants1(): - ''' 图像经过多种几何变换后的不变矩 ''' + """图像经过多种几何变换后的不变矩""" global img_open, img_binary, img_result initWindows() - img_ = cv2.imdecode(np.fromfile(r'.\explain\lung.jpg', dtype=np.uint8), 0) + img_ = cv2.imdecode(np.fromfile(r".\explain\lung.jpg", dtype=np.uint8), 0) placePic1(img_, "原图像") - imgLists,textLists,momentLists=description.moment_invariants(img_) - i=0 - descipStr='图像几何变换后的7个不变矩\n' + imgLists, textLists, momentLists = description.moment_invariants(img_) + i = 0 + descipStr = "图像几何变换后的7个不变矩\n" for moment in momentLists: - descipStr+=textLists[i]+'\t' - i+=1 + descipStr += textLists[i] + "\t" + i += 1 for mm in moment: - descipStr+="%0.2f"%mm+'\t' - descipStr +='\n' + descipStr += "%0.2f" % mm + "\t" + descipStr += "\n" myWindow.explainText.set(descipStr) - #合并几何变换后的图像 - picLists=[] - for tt,img in zip(textLists,imgLists): - picLists.append([tt,img]) - combinedImg1 = myWindow.combine_images(picLists[:3], explain=True,fontSize=0.6)#合并两张图片 - combinedImg2 = myWindow.combine_images(picLists[3:], explain=True,fontSize=0.6)#合并两张图片 - picLists=[combinedImg1,combinedImg2] - img_result=myWindow.combine_images(picLists) + # 合并几何变换后的图像 + picLists = [] + for tt, img in zip(textLists, imgLists): + picLists.append([tt, img]) + combinedImg1 = myWindow.combine_images( + picLists[:3], explain=True, fontSize=0.6 + ) # 合并两张图片 + combinedImg2 = myWindow.combine_images( + picLists[3:], explain=True, fontSize=0.6 + ) # 合并两张图片 + picLists = [combinedImg1, combinedImg2] + img_result = myWindow.combine_images(picLists) placePic2(img_result, "各种几何变换图像") -#--------------------------------------------------- +# --------------------------------------------------- def Car_license_train(): - ''' 训练车牌识别模型 ''' + """训练车牌识别模型""" initWindows() - myWindow.explainText.set("车牌识别系统通常包括图像预处理、车牌检测定位、字符分割、特征提取和字符识别等部分。" - "\n1.图像预处理就是去除图像中的噪声、模糊、光照不均匀、遮挡等问题。" - "\n2.车牌检测定位是从复杂背景的汽车图像中检测并定位车牌的位置。" - "\n3.字符分割是将车牌区域进一步分割成若干个单字符图像。" - "\n4.特征提取是提取易于区分字符的特征,用于字符识别。" - "\n5.字符识别包括模型训练和车牌识别两个阶段。") - predictor = predict.CardPredictor(myWindow) #创建识别模型 - flag=True #是否重新训练的标记 - if os.path.exists(r".\carLicense\svm.dat") and os.path.exists(r".\carLicense\svmchinese.dat"): - flag= askyesno("选择是否重新训练模型", "模型已经训练过了,训练模型需要花费一段时间,是否重新训练模型?") - if not flag: + myWindow.explainText.set( + "车牌识别系统通常包括图像预处理、车牌检测定位、字符分割、特征提取和字符识别等部分。" + "\n1.图像预处理就是去除图像中的噪声、模糊、光照不均匀、遮挡等问题。" + "\n2.车牌检测定位是从复杂背景的汽车图像中检测并定位车牌的位置。" + "\n3.字符分割是将车牌区域进一步分割成若干个单字符图像。" + "\n4.特征提取是提取易于区分字符的特征,用于字符识别。" + "\n5.字符识别包括模型训练和车牌识别两个阶段。" + ) + predictor = predict.CardPredictor(myWindow) # 创建识别模型 + flag = True # 是否重新训练的标记 + if os.path.exists(r".\carLicense\svm.dat") and os.path.exists( + r".\carLicense\svmchinese.dat" + ): + flag = askyesno( + "选择是否重新训练模型", + "模型已经训练过了,训练模型需要花费一段时间,是否重新训练模型?", + ) + if not flag: return - predictor.train_svm() #训练模型 + predictor.train_svm() # 训练模型 + def Car_license_recognition(): - ''' 利用训练好的模型,对车牌图像进行识别 ''' + """利用训练好的模型,对车牌图像进行识别""" global picSize, img_gray, img_result initWindows() - myWindow.explainText.set("车牌识别系统通常包括图像预处理、车牌检测定位、字符分割、特征提取和字符识别等部分。" - "\n1.图像预处理就是去除图像中的噪声、模糊、光照不均匀、遮挡等问题。" - "\n2.车牌检测定位是从复杂背景的汽车图像中检测并定位车牌的位置。" - "\n3.字符分割是将车牌区域进一步分割成若干个单字符图像。" - "\n4.特征提取是提取易于区分字符的特征,用于字符识别。" - "\n5.字符识别包括模型训练和车牌识别两个阶段。") + myWindow.explainText.set( + "车牌识别系统通常包括图像预处理、车牌检测定位、字符分割、特征提取和字符识别等部分。" + "\n1.图像预处理就是去除图像中的噪声、模糊、光照不均匀、遮挡等问题。" + "\n2.车牌检测定位是从复杂背景的汽车图像中检测并定位车牌的位置。" + "\n3.字符分割是将车牌区域进一步分割成若干个单字符图像。" + "\n4.特征提取是提取易于区分字符的特征,用于字符识别。" + "\n5.字符识别包括模型训练和车牌识别两个阶段。" + ) if img_open is None: - showwarning(title='警告', message='请先在文件菜单下打开图片!') + showwarning(title="警告", message="请先在文件菜单下打开图片!") return predictor = predict.CardPredictor(myWindow) # 创建识别模型 - predictor.stepFlag = askyesno("选择输出结果方式", "系统提供了分步显示识别数据和显示最终结果两种显示方式,请选择显示方式:是——分步显示,否——仅显示最终结果") + predictor.stepFlag = askyesno( + "选择输出结果方式", + "系统提供了分步显示识别数据和显示最终结果两种显示方式,请选择显示方式:是——分步显示,否——仅显示最终结果", + ) if predictor.stepFlag: myWindow.setVisibleRight0() - r,roi,color = predictor.predict(img_open) #用训练的模型进行识别 + r, roi, color = predictor.predict(img_open) # 用训练的模型进行识别 myWindow.setVisibleRight1() - if r : - roi = cv2.cvtColor(roi,cv2.COLOR_BGR2RGB) + if r: + roi = cv2.cvtColor(roi, cv2.COLOR_BGR2RGB) roi = Image.fromarray(roi) imgtk_roi = ImageTk.PhotoImage(image=roi) - myWindow.roi_ctl.configure(image=imgtk_roi, state='normal') + myWindow.roi_ctl.configure(image=imgtk_roi, state="normal") myWindow.roi_ctl.image = imgtk_roi myWindow.r_ctl.configure(text=str(r)) myWindow.updataTime = time.time() try: c = myWindow.colorTransform[color] - myWindow.color_ctl.configure(text=c[0], background=c[1], state='normal') + myWindow.color_ctl.configure(text=c[0], background=c[1], state="normal") except Exception as e: print(e) - myWindow.color_ctl.configure(state='disabled') - elif myWindow.updataTime + 8 < time.time(): - myWindow.roi_ctl.configure(state='disabled') - myWindow.r_ctl.configure(text="非车牌图片") - myWindow.color_ctl.configure(state='disabled') + myWindow.color_ctl.configure(state="disabled") + elif myWindow.updataTime + 8 < time.time(): + myWindow.roi_ctl.configure(state="disabled") + myWindow.r_ctl.configure(text="非车牌图片") + myWindow.color_ctl.configure(state="disabled") -#------------------------------------------------------------ + +# ------------------------------------------------------------ class MyApp: - ''' 主窗体类,对于显示系统界面 ''' + """主窗体类,对于显示系统界面""" + global picSize - colorTransform = {"green": ("绿", "#55ff55"), "yello": ("黄", "#ffff00"), "blue": ("蓝", "#6666ff")} + colorTransform = { + "green": ("绿", "#55ff55"), + "yello": ("黄", "#ffff00"), + "blue": ("蓝", "#6666ff"), + } updataTime = 0 + def __init__(self, root): - ''' + """ 构造方法 :param root: 主窗体的父类 - ''' + """ self.root = root self.root.title("基于python的图像处理算法演示系统v1.0") # 界面布局 - self.init_menu(self.root) #初始化菜单 - self.init_widget(self.root)#创建显示控件 - self.setVisibleLeft() #显示左窗格 - self.setVisibleRight0() #显示右窗格 - self.hideRight1() #隐藏车牌识别显示 - self.setVisibleBottom() #显示下窗格 - self.hideFig0() #隐藏右窗格的画布myCanvas0 - self.hideFig1() #隐藏下窗格的画布myCanvas1 - self.hideExplain() #隐藏下窗格的画布myCanvas2 + self.init_menu(self.root) # 初始化菜单 + self.init_widget(self.root) # 创建显示控件 + self.setVisibleLeft() # 显示左窗格 + self.setVisibleRight0() # 显示右窗格 + self.hideRight1() # 隐藏车牌识别显示 + self.setVisibleBottom() # 显示下窗格 + self.hideFig0() # 隐藏右窗格的画布myCanvas0 + self.hideFig1() # 隐藏下窗格的画布myCanvas1 + self.hideExplain() # 隐藏下窗格的画布myCanvas2 - def init_widget(self,master): + def init_widget(self, master): self.originalText = tkinter.StringVar() - self.label1 = tkinter.Label(master, textvariable=self.originalText, font=("song", 12)) + self.label1 = tkinter.Label( + master, textvariable=self.originalText, font=("song", 12) + ) self.originalText.set("原始图像") - self.label3 = tkinter.Label(master, bg='gray86') #用于显示原始图像 + self.label3 = tkinter.Label(master, bg="gray86") # 用于显示原始图像 self.resultText = tkinter.StringVar() - self.label2 = tkinter.Label(master, textvariable=self.resultText, font=("song", 12)) + self.label2 = tkinter.Label( + master, textvariable=self.resultText, font=("song", 12) + ) self.resultText.set("处理结果图") - self.label4 = tkinter.Label(master, bg='gray86') #用于显示处理结果 - self.regionLabel = tkinter.Label(master, text='检测的车牌区域:', font=("song", 12)) + self.label4 = tkinter.Label(master, bg="gray86") # 用于显示处理结果 + self.regionLabel = tkinter.Label( + master, text="检测的车牌区域:", font=("song", 12) + ) self.roi_ctl = tkinter.Label(master, font=("song", 12)) - self.recogResult = tkinter.Label(master, text='识别结果:', font=("song", 12)) - self.r_ctl = tkinter.Label(master,font=("song", 12),wraplength=400) + self.recogResult = tkinter.Label(master, text="识别结果:", font=("song", 12)) + self.r_ctl = tkinter.Label(master, font=("song", 12), wraplength=400) self.color_ctl = tkinter.Label(master) - self.from_pic_ctl = tkinter.Button(master, text="识别图片", width=20, command=Car_license_recognition, font=("song", 12)) + self.from_pic_ctl = tkinter.Button( + master, + text="识别图片", + width=20, + command=Car_license_recognition, + font=("song", 12), + ) self.explainText = tkinter.StringVar() - #用于显示算法注释说明 - self.label5 = tkinter.Label(master, textvariable=self.explainText, font=("song", 12),bg='gray86',wraplength=850,justify ='left') - self.explainText.set("图像处理教学演示软件实现了图像处理中的许多经典算法。\n使用此软件需要先点文件-打开命令,打开一个图像," - "原始图片将显示在左边窗格内;\n然后从菜单中调用某算法处理图片,处理的结果将显示在右边窗格内。") - #myfig0在右边结果窗格的画布中显示matplot图片 - self.myFig0 = plt.figure(figsize=(4, 4), facecolor='#dddddd') + # 用于显示算法注释说明 + self.label5 = tkinter.Label( + master, + textvariable=self.explainText, + font=("song", 12), + bg="gray86", + wraplength=850, + justify="left", + ) + self.explainText.set( + "图像处理教学演示软件实现了图像处理中的许多经典算法。\n使用此软件需要先点文件-打开命令,打开一个图像," + "原始图片将显示在左边窗格内;\n然后从菜单中调用某算法处理图片,处理的结果将显示在右边窗格内。" + ) + # myfig0在右边结果窗格的画布中显示matplot图片 + self.myFig0 = plt.figure(figsize=(4, 4), facecolor="#dddddd") self.myCanvas0 = FigureCanvasTkAgg(self.myFig0, master) - #myfig1在下边窗格的画布中显示多张包含图片的注释说明 - self.myFig1 = plt.figure(figsize=(9, 1.8), facecolor='lightgray') - #myfig2在下边窗格中显示说明与单张图片 - self.myFig2 = plt.figure(figsize=(4.5, 1.8), facecolor='lightgray') + # myfig1在下边窗格的画布中显示多张包含图片的注释说明 + self.myFig1 = plt.figure(figsize=(9, 1.8), facecolor="lightgray") + # myfig2在下边窗格中显示说明与单张图片 + self.myFig2 = plt.figure(figsize=(4.5, 1.8), facecolor="lightgray") self.myCanvas1 = FigureCanvasTkAgg(self.myFig1, master) self.myCanvas2 = FigureCanvasTkAgg(self.myFig2, master) + def setVisibleLeft(self): self.label1.place(relx=0.25, rely=0.04, anchor=tkinter.CENTER) # 设置相对座标 - self.label3.place(relx=0.25, rely=0.40, width=picSize, height=picSize,anchor=tkinter.CENTER) # 设置绝对座标 + self.label3.place( + relx=0.25, rely=0.40, width=picSize, height=picSize, anchor=tkinter.CENTER + ) # 设置绝对座标 + def setVisibleRight0(self): - ''' + """ 右边窗格重叠的显示之一(right0) right0一般算法结果显示窗格 right1-用于显示车牌识别结果 myFig0-使用mayplot显示运行结果图片 - ''' + """ self.label2.place(relx=0.75, rely=0.04, anchor=tkinter.CENTER) - self.label4.place(relx=0.75, rely=0.40, width=picSize, height=picSize,anchor=tkinter.CENTER) + self.label4.place( + relx=0.75, rely=0.40, width=picSize, height=picSize, anchor=tkinter.CENTER + ) self.hideRight1() self.hideFig0() + def hideRight0(self): self.label2.place_forget() self.label4.place_forget() def setVisibleRight1(self): - ''' + """ 右边窗格重叠的显示之一(right1) right0一般算法结果显示窗格 right1-用于显示车牌识别结果 myFig0-使用mayplot显示运行结果图片 - ''' + """ self.regionLabel.place(relx=0.75, rely=0.1, anchor=tkinter.CENTER) - self.roi_ctl.place(relx=0.75, rely=0.18, width=picSize, height=50,anchor=tkinter.CENTER) + self.roi_ctl.place( + relx=0.75, rely=0.18, width=picSize, height=50, anchor=tkinter.CENTER + ) self.recogResult.place(relx=0.75, rely=0.26, anchor=tkinter.CENTER) - self.r_ctl.place(relx=0.75, rely=0.34, width=picSize, height=50,anchor=tkinter.CENTER) - self.color_ctl.place(relx=0.75, rely=0.42, width=picSize, height=30,anchor=tkinter.CENTER) - self.from_pic_ctl.place(relx=0.75, rely=0.67,anchor=tkinter.CENTER) + self.r_ctl.place( + relx=0.75, rely=0.34, width=picSize, height=50, anchor=tkinter.CENTER + ) + self.color_ctl.place( + relx=0.75, rely=0.42, width=picSize, height=30, anchor=tkinter.CENTER + ) + self.from_pic_ctl.place(relx=0.75, rely=0.67, anchor=tkinter.CENTER) self.hideRight0() self.hideFig0() + def hideRight1(self): self.regionLabel.place_forget() self.roi_ctl.place_forget() @@ -2460,146 +3087,193 @@ class MyApp: self.r_ctl.place_forget() self.color_ctl.place_forget() self.from_pic_ctl.place_forget() + def setVisibleFig0(self): - ''' + """ 右边窗格重叠的显示之一(myFig0) right0一般算法结果显示窗格 right1-用于显示车牌识别结果 myFig0-使用mayplot显示运行结果图片 - ''' - self.myCanvas0.get_tk_widget().place(relx=0.75, rely=0.40, anchor=tkinter.CENTER) + """ + self.myCanvas0.get_tk_widget().place( + relx=0.75, rely=0.40, anchor=tkinter.CENTER + ) self.myCanvas0.draw() + def hideFig0(self): self.myCanvas0.get_tk_widget().place_forget() # 隐藏画布self def setVisibleBottom(self): - ''' + """ 下边窗格的重叠显示之一(label5) label5-仅显示文字说明 myFig1-用matplot显示多个文字图片 explain-左边是label5,右边是myFig2 - ''' - self.label5.place(relx=0.50, rely=0.86, width=900, height=180,anchor=tkinter.CENTER) + """ + self.label5.place( + relx=0.50, rely=0.86, width=900, height=180, anchor=tkinter.CENTER + ) self.hideFig1() self.hideExplain() def setVisibleFig1(self): - ''' + """ 下边窗格的重叠显示之一(myFig1) label5-仅显示文字说明 myFig1-用matplot显示多个文字图片 explain-左边是label5,右边是myFig2 - ''' + """ self.myCanvas1.get_tk_widget().place(relx=0.5, rely=0.86, anchor=tkinter.CENTER) self.myCanvas1.draw() + def hideFig1(self): self.myCanvas1.get_tk_widget().place_forget() # 隐藏画布self def setVisibleExplain(self): - ''' + """ 下边窗格的重叠显示之一(explain) label5-仅显示文字说明 myFig1-用matplot显示多个文字图片 explain-左边是label5,右边是myFig2 - ''' - self.label5.place(relx=0.275, rely=0.86, width=450, height=180,anchor=tkinter.CENTER) + """ + self.label5.place( + relx=0.275, rely=0.86, width=450, height=180, anchor=tkinter.CENTER + ) self.label5.config(wraplength=430) - self.myCanvas2.get_tk_widget().place(relx=0.725, rely=0.86, anchor=tkinter.CENTER) + self.myCanvas2.get_tk_widget().place( + relx=0.725, rely=0.86, anchor=tkinter.CENTER + ) self.myCanvas2.draw() + def hideExplain(self): self.label5.config(wraplength=850) self.myCanvas2.get_tk_widget().place_forget() # 隐藏画布self - def init_menu(self,master): - '''创建菜单''' - menubar = tkinter.Menu(master)# 创建菜单对象 + def init_menu(self, master): + """创建菜单""" + + menubar = tkinter.Menu(master) # 创建菜单对象 # 创建菜单项 fmenu_file = tkinter.Menu(master) # 文件 - fmenu_file.add_command(label='打开', command=Choosepic) - fmenu_file.add_command(label='重载', command=Reload) - fmenu_file.add_command(label='转换为GRAY', command=Convert_gray) - fmenu_file.add_command(label='转换为HSV', command=Convert_HSV) - fmenu_file.add_command(label='转换为BGR', command=Convert_BGR) - fmenu_file.add_command(label='保存处理结果', command=Savepic) + fmenu_file.add_command(label="打开", command=Choosepic) + fmenu_file.add_command(label="重载", command=Reload) + fmenu_file.add_command(label="转换为GRAY", command=Convert_gray) + fmenu_file.add_command(label="转换为HSV", command=Convert_HSV) + fmenu_file.add_command(label="转换为BGR", command=Convert_BGR) + fmenu_file.add_command(label="保存处理结果", command=Savepic) fmenu_file.add_separator() # 添加一条分隔线 - fmenu_file.add_command(label='退出', command=quit) # 用tkinter里面自带的quit()函数 + fmenu_file.add_command( + label="退出", command=quit + ) # 用tkinter里面自带的quit()函数 fmenu_point = tkinter.Menu(master) # 灰度点运算 - fmenu_point.add_command(label='图像取反', command=Negative) - fmenu_point.add_command(label='全局灰度线性变换', command=Global_linear_transmation) - fmenu_point.add_command(label='分段线性灰度变换', command=Piecewise_linear_transformation) - fmenu_point.add_command(label='对数变换', command=Logarithmic_transformations) - fmenu_point.add_command(label='幂次(伽马)变换', command=Power_law_transformations) + fmenu_point.add_command(label="图像取反", command=Negative) + fmenu_point.add_command( + label="全局灰度线性变换", command=Global_linear_transmation + ) + fmenu_point.add_command( + label="分段线性灰度变换", command=Piecewise_linear_transformation + ) + fmenu_point.add_command(label="对数变换", command=Logarithmic_transformations) + fmenu_point.add_command( + label="幂次(伽马)变换", command=Power_law_transformations + ) submenu_point_1 = tkinter.Menu(fmenu_point) - fmenu_point.add_cascade(label='直方图处理', menu=submenu_point_1) - submenu_point_1.add_command(label='灰度直方图', command=Gray_histogram) - submenu_point_1.add_command(label='掩模直方图', command=Mask_histogram) + fmenu_point.add_cascade(label="直方图处理", menu=submenu_point_1) + submenu_point_1.add_command(label="灰度直方图", command=Gray_histogram) + submenu_point_1.add_command(label="掩模直方图", command=Mask_histogram) # submenu2_2.add_command(label='局部直方图', command=Locality_histogram) - submenu_point_1.add_command(label='直方图均衡化', command=Equalization_histogram) + submenu_point_1.add_command( + label="直方图均衡化", command=Equalization_histogram + ) submenu_point_11 = tkinter.Menu(submenu_point_1) - submenu_point_1.add_cascade(label='直方图规定化', menu=submenu_point_11) - submenu_point_11.add_command(label='SML单映射', command=SML) - submenu_point_11.add_command(label='GML组映射', command=GML) + submenu_point_1.add_cascade(label="直方图规定化", menu=submenu_point_11) + submenu_point_11.add_command(label="SML单映射", command=SML) + submenu_point_11.add_command(label="GML组映射", command=GML) fmenu_spatial = tkinter.Menu(master) # 空域滤波 - submenu_spatial_2 = tkinter.Menu(fmenu_point) #空域平滑 - submenu_spatial_3 = tkinter.Menu(fmenu_point) #边界处理 - submenu_spatial_4 = tkinter.Menu(fmenu_point) #空域锐化 + submenu_spatial_2 = tkinter.Menu(fmenu_point) # 空域平滑 + submenu_spatial_3 = tkinter.Menu(fmenu_point) # 边界处理 + submenu_spatial_4 = tkinter.Menu(fmenu_point) # 空域锐化 - fmenu_spatial.add_cascade(label='空域平滑', menu=submenu_spatial_2) - submenu_spatial_2.add_command(label='高斯平滑滤波', command=Gaussian_filter) - submenu_spatial_2.add_command(label='中值滤波', command=Median_filter) - submenu_spatial_2.add_command(label='最大值滤波', command=Max_filter) - submenu_spatial_2.add_command(label='最小值滤波', command=Min_filter) - submenu_spatial_2.add_command(label='自定义平滑滤波', command=Smooth_filter) - fmenu_spatial.add_cascade(label='扩展边界', menu=submenu_spatial_3) - submenu_spatial_3.add_command(label='常量(补0)法(BORDER_CONSTANT)', command=Constant_border) - submenu_spatial_3.add_command(label='复制法(BORDER_REFLICATE)', command=Replicate_border) - submenu_spatial_3.add_command(label='反射法(BORDER_REFLECT)', command=Reflect_border) - submenu_spatial_3.add_command(label='反射法1(BORDER_REFLECT_101)', command=Reflect_border_101) - submenu_spatial_3.add_command(label='外包装法(BORDER_WRAP)', command=Wrap_border) - fmenu_spatial.add_cascade(label='空域锐化', menu=submenu_spatial_4) - submenu_spatial_4.add_command(label='Roberts算子', command=Roberts_filter) - submenu_spatial_4.add_command(label='Prewitt算子', command=Prewitt_filter) - submenu_spatial_4.add_command(label='Sobel算子', command=Sobel_filter) - submenu_spatial_4.add_command(label='Scharr算子', command=Scharr_filter) - submenu_spatial_4.add_command(label='laplacian算子', command=Laplacian_filter) - submenu_spatial_4.add_command(label='LOG算子', command=Log_filter) - submenu_spatial_4.add_command(label='canny算子', command=Canny_filter) + fmenu_spatial.add_cascade(label="空域平滑", menu=submenu_spatial_2) + submenu_spatial_2.add_command(label="高斯平滑滤波", command=Gaussian_filter) + submenu_spatial_2.add_command(label="中值滤波", command=Median_filter) + submenu_spatial_2.add_command(label="最大值滤波", command=Max_filter) + submenu_spatial_2.add_command(label="最小值滤波", command=Min_filter) + submenu_spatial_2.add_command(label="自定义平滑滤波", command=Smooth_filter) + fmenu_spatial.add_cascade(label="扩展边界", menu=submenu_spatial_3) + submenu_spatial_3.add_command( + label="常量(补0)法(BORDER_CONSTANT)", command=Constant_border + ) + submenu_spatial_3.add_command( + label="复制法(BORDER_REFLICATE)", command=Replicate_border + ) + submenu_spatial_3.add_command( + label="反射法(BORDER_REFLECT)", command=Reflect_border + ) + submenu_spatial_3.add_command( + label="反射法1(BORDER_REFLECT_101)", command=Reflect_border_101 + ) + submenu_spatial_3.add_command( + label="外包装法(BORDER_WRAP)", command=Wrap_border + ) + fmenu_spatial.add_cascade(label="空域锐化", menu=submenu_spatial_4) + submenu_spatial_4.add_command(label="Roberts算子", command=Roberts_filter) + submenu_spatial_4.add_command(label="Prewitt算子", command=Prewitt_filter) + submenu_spatial_4.add_command(label="Sobel算子", command=Sobel_filter) + submenu_spatial_4.add_command(label="Scharr算子", command=Scharr_filter) + submenu_spatial_4.add_command(label="laplacian算子", command=Laplacian_filter) + submenu_spatial_4.add_command(label="LOG算子", command=Log_filter) + submenu_spatial_4.add_command(label="canny算子", command=Canny_filter) fmenu_spectrum = tkinter.Menu(master) # 频域增强 submenu_spectrum_1 = tkinter.Menu(fmenu_spectrum) submenu_spectrum_2 = tkinter.Menu(fmenu_spectrum) submenu_spectrum_3 = tkinter.Menu(fmenu_spectrum) - fmenu_spectrum.add_cascade(label='傅里叶正变换', menu=submenu_spectrum_1) - submenu_spectrum_1.add_command(label='幅值谱', command=Magnitude_spectrum0) - submenu_spectrum_1.add_command(label='低频移中的幅值谱', command=Magnitude_spectrum1) - submenu_spectrum_1.add_command(label='相位谱', command=Phase_spectrum0) - submenu_spectrum_1.add_command(label='低频移中的相位谱', command=Phase_spectrum1) - fmenu_spectrum.add_command(label='傅里叶反变换', command=Inverse_fourier) - fmenu_spectrum.add_cascade(label='低通滤波器', menu=submenu_spectrum_2) - submenu_spectrum_2.add_command(label='理想低通滤波器', command=Idle_low_pass) - submenu_spectrum_2.add_command(label='巴特沃兹低通滤波器', command=Butterworth_low_pass) - submenu_spectrum_2.add_command(label='高斯低通滤波器', command=Gaussian_low_pass) - fmenu_spectrum.add_cascade(label='高通滤波器', menu=submenu_spectrum_3) - submenu_spectrum_3.add_command(label='理想高通滤波器', command=Idle_high_pass) - submenu_spectrum_3.add_command(label='巴特沃斯高通滤波器', command=Butterworth_high_pass) - submenu_spectrum_3.add_command(label='高斯高通滤波器', command=Gaussian_high_pass) - fmenu_spectrum.add_command(label='孤立频点与周期噪声', command=Property_fourier) + fmenu_spectrum.add_cascade(label="傅里叶正变换", menu=submenu_spectrum_1) + submenu_spectrum_1.add_command(label="幅值谱", command=Magnitude_spectrum0) + submenu_spectrum_1.add_command( + label="低频移中的幅值谱", command=Magnitude_spectrum1 + ) + submenu_spectrum_1.add_command(label="相位谱", command=Phase_spectrum0) + submenu_spectrum_1.add_command( + label="低频移中的相位谱", command=Phase_spectrum1 + ) + fmenu_spectrum.add_command(label="傅里叶反变换", command=Inverse_fourier) + fmenu_spectrum.add_cascade(label="低通滤波器", menu=submenu_spectrum_2) + submenu_spectrum_2.add_command(label="理想低通滤波器", command=Idle_low_pass) + submenu_spectrum_2.add_command( + label="巴特沃兹低通滤波器", command=Butterworth_low_pass + ) + submenu_spectrum_2.add_command( + label="高斯低通滤波器", command=Gaussian_low_pass + ) + fmenu_spectrum.add_cascade(label="高通滤波器", menu=submenu_spectrum_3) + submenu_spectrum_3.add_command(label="理想高通滤波器", command=Idle_high_pass) + submenu_spectrum_3.add_command( + label="巴特沃斯高通滤波器", command=Butterworth_high_pass + ) + submenu_spectrum_3.add_command( + label="高斯高通滤波器", command=Gaussian_high_pass + ) + fmenu_spectrum.add_command(label="孤立频点与周期噪声", command=Property_fourier) fmenu_restore = tkinter.Menu(master) - submenu_restore_1 = tkinter.Menu(fmenu_restore) # 图像去噪 - submenu_restore_2 = tkinter.Menu(fmenu_restore) #退化模型 + submenu_restore_1 = tkinter.Menu(fmenu_restore) # 图像去噪 + submenu_restore_2 = tkinter.Menu(fmenu_restore) # 退化模型 submenu_restore_3 = tkinter.Menu(fmenu_restore) # 空域去除特定噪声 # submenu_restore_4 = tkinter.Menu(fmenu_restore) # 频域去除周期噪声 submenu_restore_5 = tkinter.Menu(fmenu_restore) # 无约束图像复原 - submenu_restore_6 = tkinter.Menu(fmenu_restore) #有约束图像复原 - fmenu_restore.add_cascade(label='添加噪声', menu=submenu_restore_1) - submenu_restore_1.add_command(label='添加高斯噪声', command=Add_gaussian_noise) - submenu_restore_1.add_command(label='添加椒盐噪声', command=Add_salt_and_pepper_noise) - submenu_restore_1.add_command(label='添加均值噪声', command=Add_mean_noise) - submenu_restore_1.add_command(label='添加瑞利噪声', command=Add_rayleigh_noise) - submenu_restore_1.add_command(label='添加伽马噪声', command=Add_erlang_noise) + submenu_restore_6 = tkinter.Menu(fmenu_restore) # 有约束图像复原 + fmenu_restore.add_cascade(label="添加噪声", menu=submenu_restore_1) + submenu_restore_1.add_command(label="添加高斯噪声", command=Add_gaussian_noise) + submenu_restore_1.add_command( + label="添加椒盐噪声", command=Add_salt_and_pepper_noise + ) + submenu_restore_1.add_command(label="添加均值噪声", command=Add_mean_noise) + submenu_restore_1.add_command(label="添加瑞利噪声", command=Add_rayleigh_noise) + submenu_restore_1.add_command(label="添加伽马噪声", command=Add_erlang_noise) # fmenu_restore.add_cascade(label='系统默认图片的噪声分布比较', menu=submenu_restore_1) # submenu_restore_1.add_command(label='添加高斯噪声', command=Add_gaussian_noise1) @@ -2608,97 +3282,132 @@ class MyApp: # submenu_restore_1.add_command(label='添加瑞利噪声', command=Add_rayleigh_noise1) # submenu_restore_1.add_command(label='添加伽马噪声', command=Add_erlang_noise1) - fmenu_restore.add_cascade(label='退化模型', menu=submenu_restore_2) - submenu_restore_2.add_command(label='运动模糊', command=Motion_blur) - submenu_restore_2.add_command(label='高斯模糊', command=Gaussian_blur) - submenu_restore_2.add_command(label='大气湍流模糊', command=Turbulence_blur) - fmenu_restore.add_cascade(label='去除噪声', menu=submenu_restore_3) - submenu_restore_3.add_command(label='中值滤波', command=Median_filter) - submenu_restore_3.add_command(label='最大值滤波', command=Max_filter) - submenu_restore_3.add_command(label='最小值滤波', command=Min_filter) - submenu_restore_3.add_command(label='谐波滤波', command=Harmonic_filter) - submenu_restore_3.add_command(label='逆谐波滤波', command=Contra_harmonic_filter) + fmenu_restore.add_cascade(label="退化模型", menu=submenu_restore_2) + submenu_restore_2.add_command(label="运动模糊", command=Motion_blur) + submenu_restore_2.add_command(label="高斯模糊", command=Gaussian_blur) + submenu_restore_2.add_command(label="大气湍流模糊", command=Turbulence_blur) + fmenu_restore.add_cascade(label="去除噪声", menu=submenu_restore_3) + submenu_restore_3.add_command(label="中值滤波", command=Median_filter) + submenu_restore_3.add_command(label="最大值滤波", command=Max_filter) + submenu_restore_3.add_command(label="最小值滤波", command=Min_filter) + submenu_restore_3.add_command(label="谐波滤波", command=Harmonic_filter) + submenu_restore_3.add_command( + label="逆谐波滤波", command=Contra_harmonic_filter + ) # submenu_restore_3.add_command(label='Alpha剪枝滤波', command=Median_filter) # submenu_restore_3.add_command(label='自适应滤波', command=Median_filter) # fmenu_restore.add_cascade(label='频域去除周期噪声', menu=submenu_restore_4) # submenu_restore_4.add_command(label='带阻滤波器', command=quit) # submenu_restore_4.add_command(label='带通滤波器', command=quit) # submenu_restore_4.add_command(label='陷波带阻滤波器', command=quit) - fmenu_restore.add_cascade(label='无约束图像复原', menu=submenu_restore_5) - submenu_restore_5.add_command(label='逆滤波', command=Inverse) - submenu_restore_5.add_command(label='伪逆滤波', command=Improved_inverse) - fmenu_restore.add_cascade(label='有约束图像复原', menu=submenu_restore_6) - submenu_restore_6.add_command(label='维纳滤波器', command=Wiener) - submenu_restore_6.add_command(label='最小二乘滤波', command=Constrained_least_squares) + fmenu_restore.add_cascade(label="无约束图像复原", menu=submenu_restore_5) + submenu_restore_5.add_command(label="逆滤波", command=Inverse) + submenu_restore_5.add_command(label="伪逆滤波", command=Improved_inverse) + fmenu_restore.add_cascade(label="有约束图像复原", menu=submenu_restore_6) + submenu_restore_6.add_command(label="维纳滤波器", command=Wiener) + submenu_restore_6.add_command( + label="最小二乘滤波", command=Constrained_least_squares + ) fmenu_geometric = tkinter.Menu(master) # 几何变换 - fmenu_geometric.add_command(label='平移', command=Move) - fmenu_geometric.add_command(label='旋转', command=Rotate) - fmenu_geometric.add_command(label='垂直镜像', command=Reflect_x) - fmenu_geometric.add_command(label='水平镜像', command=Reflect_y) - fmenu_geometric.add_command(label='缩放', command=Zoom) - fmenu_geometric.add_command(label='仿射变换', command=Affine) - fmenu_geometric.add_command(label='透视变换', command=Perspective) - fmenu_geometric.add_command(label='插值方法', command=Interpolation) + fmenu_geometric.add_command(label="平移", command=Move) + fmenu_geometric.add_command(label="旋转", command=Rotate) + fmenu_geometric.add_command(label="垂直镜像", command=Reflect_x) + fmenu_geometric.add_command(label="水平镜像", command=Reflect_y) + fmenu_geometric.add_command(label="缩放", command=Zoom) + fmenu_geometric.add_command(label="仿射变换", command=Affine) + fmenu_geometric.add_command(label="透视变换", command=Perspective) + fmenu_geometric.add_command(label="插值方法", command=Interpolation) fmenu_morphology = tkinter.Menu(master) # 形态学处理 - submenu_morphology1= tkinter.Menu(fmenu_morphology) - fmenu_morphology.add_cascade(label='系统默认图像的形态学处理', menu=submenu_morphology1) - submenu_morphology1.add_command(label='二值化处理', command=Binary1) - submenu_morphology1.add_command(label='膨胀运算', command=Dilate1) - submenu_morphology1.add_command(label='腐蚀运算', command=Erode1) - submenu_morphology1.add_command(label='开运算', command=Open_operation1) - submenu_morphology1.add_command(label='闭运算', command=Close_operation1) - submenu_morphology1.add_command(label='形态学梯度', command=Gradient_morphology1) - submenu_morphology1.add_command(label='礼帽', command=Tophat1) - submenu_morphology1.add_command(label='黑帽', command=Blackhat1) - submenu_morphology1.add_command(label='孔洞填充', command=Fill_holes1) - submenu_morphology1.add_command(label='骨架提取', command=Skeleton1) - - fmenu_morphology.add_command(label='二值化处理', command=Binary) - fmenu_morphology.add_command(label='膨胀运算', command=Dilate) - fmenu_morphology.add_command(label='腐蚀运算', command=Erode) - fmenu_morphology.add_command(label='开运算', command=Open_operation) - fmenu_morphology.add_command(label='闭运算', command=Close_operation) - fmenu_morphology.add_command(label='形态学梯度', command=Gradient_morphology) - fmenu_morphology.add_command(label='礼帽', command=Tophat) - fmenu_morphology.add_command(label='黑帽', command=Blackhat) - fmenu_morphology.add_command(label='孔洞填充', command=Fill_holes) - fmenu_morphology.add_command(label='骨架提取', command=Skeleton) + submenu_morphology1 = tkinter.Menu(fmenu_morphology) + fmenu_morphology.add_cascade( + label="系统默认图像的形态学处理", menu=submenu_morphology1 + ) + submenu_morphology1.add_command(label="二值化处理", command=Binary1) + submenu_morphology1.add_command(label="膨胀运算", command=Dilate1) + submenu_morphology1.add_command(label="腐蚀运算", command=Erode1) + submenu_morphology1.add_command(label="开运算", command=Open_operation1) + submenu_morphology1.add_command(label="闭运算", command=Close_operation1) + submenu_morphology1.add_command( + label="形态学梯度", command=Gradient_morphology1 + ) + submenu_morphology1.add_command(label="礼帽", command=Tophat1) + submenu_morphology1.add_command(label="黑帽", command=Blackhat1) + submenu_morphology1.add_command(label="孔洞填充", command=Fill_holes1) + submenu_morphology1.add_command(label="骨架提取", command=Skeleton1) + fmenu_morphology.add_command(label="二值化处理", command=Binary) + fmenu_morphology.add_command(label="膨胀运算", command=Dilate) + fmenu_morphology.add_command(label="腐蚀运算", command=Erode) + fmenu_morphology.add_command(label="开运算", command=Open_operation) + fmenu_morphology.add_command(label="闭运算", command=Close_operation) + fmenu_morphology.add_command(label="形态学梯度", command=Gradient_morphology) + fmenu_morphology.add_command(label="礼帽", command=Tophat) + fmenu_morphology.add_command(label="黑帽", command=Blackhat) + fmenu_morphology.add_command(label="孔洞填充", command=Fill_holes) + fmenu_morphology.add_command(label="骨架提取", command=Skeleton) fmenu_segment = tkinter.Menu(master) # 图像分割 submenu_segment_1 = tkinter.Menu(fmenu_segment) - submenu_segment_2=tkinter.Menu(fmenu_segment) - fmenu_segment.add_cascade(label='系统默认图像的阈值分割', menu=submenu_segment_1) - submenu_segment_1.add_command(label='基本全阈值分割', command=Basic_global_thresholding1) - submenu_segment_1.add_command(label='最大类间方差OTSU分割', command=Otsu_threshold1) - submenu_segment_1.add_command(label='邻域均值自适应阈值分割',command=Adaptive_mean_threshold1) - submenu_segment_1.add_command(label='高斯窗口自适应阈值分割',command=Adaptive_gaussian_threshold1) + submenu_segment_2 = tkinter.Menu(fmenu_segment) + fmenu_segment.add_cascade( + label="系统默认图像的阈值分割", menu=submenu_segment_1 + ) + submenu_segment_1.add_command( + label="基本全阈值分割", command=Basic_global_thresholding1 + ) + submenu_segment_1.add_command( + label="最大类间方差OTSU分割", command=Otsu_threshold1 + ) + submenu_segment_1.add_command( + label="邻域均值自适应阈值分割", command=Adaptive_mean_threshold1 + ) + submenu_segment_1.add_command( + label="高斯窗口自适应阈值分割", command=Adaptive_gaussian_threshold1 + ) # submenu_segment_1.add_command(label='局部滑动平均阈值分割', command=Moving_threshold1) # fmenu_segment.add_command(label='阈值估计', command=quit) - fmenu_segment.add_command(label='基本全阈值分割', command=Basic_global_thresholding) - fmenu_segment.add_command(label='最大类间方差OTSU分割', command=Otsu_threshold) - fmenu_segment.add_cascade(label='自适应局部可变阈值分割', menu=submenu_segment_2) - submenu_segment_2.add_command(label='邻域均值自适应阈值分割',command=Adaptive_mean_threshold) - submenu_segment_2.add_command(label='高斯窗口自适应阈值分割',command=Adaptive_gaussian_threshold) + fmenu_segment.add_command( + label="基本全阈值分割", command=Basic_global_thresholding + ) + fmenu_segment.add_command(label="最大类间方差OTSU分割", command=Otsu_threshold) + fmenu_segment.add_cascade( + label="自适应局部可变阈值分割", menu=submenu_segment_2 + ) + submenu_segment_2.add_command( + label="邻域均值自适应阈值分割", command=Adaptive_mean_threshold + ) + submenu_segment_2.add_command( + label="高斯窗口自适应阈值分割", command=Adaptive_gaussian_threshold + ) # fmenu_segment.add_command(label='局部滑动平均阈值分割', command=Moving_threshold) fmenu_description = tkinter.Menu(master) # 图像描述 - submenu_description1= tkinter.Menu(fmenu_description) - fmenu_description.add_cascade(label='系统默认图像的描述', menu=submenu_description1) - submenu_description1.add_command(label='获取目标轮廓', command=Draw_contours1) - submenu_description1.add_command(label='区域描述子', command=Get_region_description1) - submenu_description1.add_command(label='几何变换后的不变矩', command=Moment_invariants1) + submenu_description1 = tkinter.Menu(fmenu_description) + fmenu_description.add_cascade( + label="系统默认图像的描述", menu=submenu_description1 + ) + submenu_description1.add_command(label="获取目标轮廓", command=Draw_contours1) + submenu_description1.add_command( + label="区域描述子", command=Get_region_description1 + ) + submenu_description1.add_command( + label="几何变换后的不变矩", command=Moment_invariants1 + ) - fmenu_description.add_command(label='获取目标轮廓', command=Draw_contours) - fmenu_description.add_command(label='区域描述子', command=Get_region_description) - fmenu_description.add_command(label='几何变换后的不变矩', command=Moment_invariants) + fmenu_description.add_command(label="获取目标轮廓", command=Draw_contours) + fmenu_description.add_command( + label="区域描述子", command=Get_region_description + ) + fmenu_description.add_command( + label="几何变换后的不变矩", command=Moment_invariants + ) fmenu_license = tkinter.Menu(master) # 车牌识别系统 - fmenu_license.add_command(label='训练识别模型', command=Car_license_train) - fmenu_license.add_command(label='车牌识别', command=Car_license_recognition) + fmenu_license.add_command(label="训练识别模型", command=Car_license_train) + fmenu_license.add_command(label="车牌识别", command=Car_license_recognition) menubar.add_cascade(label="文件", menu=fmenu_file) menubar.add_cascade(label="灰度点运算", menu=fmenu_point) @@ -2713,8 +3422,8 @@ class MyApp: master.config(menu=menubar) - def combine_images(self,picLists,axis=None,explain=False,fontSize=1): - ''' + def combine_images(self, picLists, axis=None, explain=False, fontSize=1): + """ 将图像合并为一个图片,并可以为图像添加文字说明 :param picLists:[图像说明,图像](图像成员的维数必须相同) :param axis:合并方向。 @@ -2724,64 +3433,84 @@ class MyApp: :param explain:是否添加文字说明的标记 :param fontSize:文字大小 :return:合并后的图像 - ''' + """ if explain: - images=[] + images = [] # font = cv2.FONT_HERSHEY_SIMPLEX # 使用默认字体 - font=cv2.QT_FONT_NORMAL + font = cv2.QT_FONT_NORMAL # fontSize = 12 for i in range(len(picLists)): # 各参数依次是:图片,添加的文字,左上角坐标,字体,字体大小,颜色,字体粗细 - images.append(cv2.putText(picLists[i][1], picLists[i][0], (5, 25), font, fontSize, (255,255, 0),2)) - else: #无文字说明 - images=picLists + images.append( + cv2.putText( + picLists[i][1], + picLists[i][0], + (5, 25), + font, + fontSize, + (255, 255, 0), + 2, + ) + ) + else: # 无文字说明 + images = picLists # ndim = images[0].shape shapes = np.array([mat.shape for mat in images]) # assert np.all(map(lambda e: len(e) == ndim, shapes)), 'all picLists should be same ndim.' if axis is None: - if shapes[0, 0] < shapes[0, 1]: # 根据图像的长宽比决定是合并方向,让合并后图像尽量是方形 + if ( + shapes[0, 0] < shapes[0, 1] + ): # 根据图像的长宽比决定是合并方向,让合并后图像尽量是方形 axis = 0 else: axis = 1 if axis == 0: # 垂直方向合并图像 cols = np.max(shapes[:, 1]) # 扩展各图像 cols大小,使得 cols一致 - copy_imgs = [cv2.copyMakeBorder(img, 0, 0, 0, cols - img.shape[1], - cv2.BORDER_CONSTANT, (0, 0, 0)) for img in images] + copy_imgs = [ + cv2.copyMakeBorder( + img, 0, 0, 0, cols - img.shape[1], cv2.BORDER_CONSTANT, (0, 0, 0) + ) + for img in images + ] # 垂直方向合并 return np.vstack(copy_imgs) else: # 水平方向合并图像 rows = np.max(shapes[:, 0]) # 扩展各图像rows大小,使得 rows一致 - copy_imgs = [cv2.copyMakeBorder(img, 0, rows - img.shape[0], 0, 0, - cv2.BORDER_CONSTANT, (0, 0, 0)) for img in images] + copy_imgs = [ + cv2.copyMakeBorder( + img, 0, rows - img.shape[0], 0, 0, cv2.BORDER_CONSTANT, (0, 0, 0) + ) + for img in images + ] # 水平方向合并 return np.hstack(copy_imgs) - def showFig0(self,picLists,type="show"): - ''' + def showFig0(self, picLists, type="show"): + """ 在右边窗格的画布上用matplot显示多张结果图片 :param picLists: 需要显示的图片及说明 :param type: 显示的类型可以是imshow或bar - ''' + """ self.myFig0.clear() plt.subplots_adjust(top=0.7) - num=len(picLists) + num = len(picLists) for i in range(num): f_plot = self.myFig1.add_subplot(1, num, i + 1) - if type == 'show': - f_plot.imshow(picLists[i][1], cmap='gray') - elif type == 'bar': + if type == "show": + f_plot.imshow(picLists[i][1], cmap="gray") + elif type == "bar": f_plot.bar(np.arange(256), picLists[i][1]) - plt.title(picLists[i][0],wrap=True) + plt.title(picLists[i][0], wrap=True) self.setVisibleFig0() - def showAndBarFig0(self,pic,histr): - ''' + def showAndBarFig0(self, pic, histr): + """ 在右边窗格用matplotlib显示图像及其直方图 :param pic: 需要显示的图片 :param histr: 需要显示的直方图 - ''' + """ self.myFig0.clear() height, width = pic.shape if width >= height: @@ -2790,48 +3519,48 @@ class MyApp: else: f_plot1 = self.myFig0.add_subplot(121) f_plot2 = self.myFig0.add_subplot(122) - f_plot1.imshow(pic, cmap='gray') - f_plot2.bar(np.arange(256), histr.flatten(), color='blue') + f_plot1.imshow(pic, cmap="gray") + f_plot2.bar(np.arange(256), histr.flatten(), color="blue") self.setVisibleFig0() - def showFig1(self,picLists,type='show'): - ''' + def showFig1(self, picLists, type="show"): + """ 此函数用于显示算法说明,包括多个文字和图片说明 picLists[i][0]存放的是文字说明,picLists[i][1]存放的是图片 type:是图片显示方式, type='show'是通过imshow显示,type='bar'是通过plt.bar显示, - ''' + """ self.myFig1.clear() self.setVisibleFig1() # plt.subplots_adjust(top=0.7) - num=len(picLists) + num = len(picLists) for i in range(num): - f_plot = self.myFig1.add_subplot(1, num, i+1) - if type=='show': - f_plot.imshow(picLists[i][1], cmap='gray') - f_plot.xaxis.set_major_locator(plt.NullLocator()) #不显示坐标轴 + f_plot = self.myFig1.add_subplot(1, num, i + 1) + if type == "show": + f_plot.imshow(picLists[i][1], cmap="gray") + f_plot.xaxis.set_major_locator(plt.NullLocator()) # 不显示坐标轴 f_plot.yaxis.set_major_locator(plt.NullLocator()) - f_plot.spines['top'].set_visible(False) #不显示边框 - f_plot.spines['right'].set_visible(False) - f_plot.spines['bottom'].set_visible(False) - f_plot.spines['left'].set_visible(False) - elif type=='bar': + f_plot.spines["top"].set_visible(False) # 不显示边框 + f_plot.spines["right"].set_visible(False) + f_plot.spines["bottom"].set_visible(False) + f_plot.spines["left"].set_visible(False) + elif type == "bar": f_plot.bar(np.arange(256), picLists[i][1]) f_plot.xaxis.set_major_locator(plt.NullLocator()) # 不显示坐标轴 f_plot.yaxis.set_major_locator(plt.NullLocator()) - f_plot.spines['top'].set_visible(False) # 不显示边框 - f_plot.spines['right'].set_visible(False) - f_plot.spines['bottom'].set_visible(False) - f_plot.spines['left'].set_visible(False) - f_plot.set_title(picLists[i][0],verticalalignment='center') + f_plot.spines["top"].set_visible(False) # 不显示边框 + f_plot.spines["right"].set_visible(False) + f_plot.spines["bottom"].set_visible(False) + f_plot.spines["left"].set_visible(False) + f_plot.set_title(picLists[i][0], verticalalignment="center") self.setVisibleFig1() - def showExplain(self, explain,picLists,type='show'): - ''' + def showExplain(self, explain, picLists, type="show"): + """ 在下边窗格显示文字和图片说明 ,文字和图片各占一半 :param explain:需要显示的文字 :param picLists: 文字和图片 :param type: 图片显示方式imshow和bar - ''' + """ self.setVisibleExplain() self.explainText.set(explain) self.myFig2.clear() @@ -2839,48 +3568,50 @@ class MyApp: num = len(picLists) for i in range(num): f_plot = self.myFig2.add_subplot(1, num, i + 1) - if type == 'show': - if len(picLists[i][1].shape)==2: + if type == "show": + if len(picLists[i][1].shape) == 2: f_plot.imshow(picLists[i][1], cmap=plt.cm.gray) else: - f_plot.imshow(picLists[i][1], cmap= plt.cm.jet) + f_plot.imshow(picLists[i][1], cmap=plt.cm.jet) f_plot.xaxis.set_major_locator(plt.NullLocator()) # 不显示坐标轴 f_plot.yaxis.set_major_locator(plt.NullLocator()) - f_plot.spines['top'].set_visible(False) # 不显示边框 - f_plot.spines['right'].set_visible(False) - f_plot.spines['bottom'].set_visible(False) - f_plot.spines['left'].set_visible(False) - elif type == 'bar': + f_plot.spines["top"].set_visible(False) # 不显示边框 + f_plot.spines["right"].set_visible(False) + f_plot.spines["bottom"].set_visible(False) + f_plot.spines["left"].set_visible(False) + elif type == "bar": f_plot.bar(np.arange(256), picLists[i][1]) f_plot.xaxis.set_major_locator(plt.NullLocator()) # 不显示坐标轴 f_plot.yaxis.set_major_locator(plt.NullLocator()) - f_plot.spines['top'].set_visible(False) # 不显示边框 - f_plot.spines['right'].set_visible(False) - f_plot.spines['bottom'].set_visible(False) - f_plot.spines['left'].set_visible(False) - f_plot.set_title(picLists[i][0],verticalalignment='center') + f_plot.spines["top"].set_visible(False) # 不显示边框 + f_plot.spines["right"].set_visible(False) + f_plot.spines["bottom"].set_visible(False) + f_plot.spines["left"].set_visible(False) + f_plot.set_title(picLists[i][0], verticalalignment="center") self.setVisibleExplain() def hide(self): - '''隐藏窗体''' + """隐藏窗体""" self.root.withdraw() def show(self): - '''显示窗体''' + """显示窗体""" self.root.update() self.root.deiconify() + # -------------------------------------- class paraWindow(tkinter.Toplevel): - ''' + """ 多行一列的参数设置窗口 - ''' - def __init__(self, root, title = None, paraLists=[]): + """ + + def __init__(self, root, title=None, paraLists=[]): """Constructor""" self.root = root - self.paraLists=paraLists - self.names = locals() #动态组件 - tkinter.Toplevel.__init__(self,root) + self.paraLists = paraLists + self.names = locals() # 动态组件 + tkinter.Toplevel.__init__(self, root) if title: self.title(title) # 创建对话框的主体内容 @@ -2889,7 +3620,7 @@ class paraWindow(tkinter.Toplevel): self.initial_focus = self.init_widgets(frame) frame.pack(padx=5, pady=5) # 根据modal选项设置是否为模式对话框 - self.grab_set() #重要,必须是模式对话框 + self.grab_set() # 重要,必须是模式对话框 # 为"WM_DELETE_WINDOW"协议使用self.cancel_click事件处理方法 self.protocol("WM_DELETE_WINDOW", self.cancel_click) # 根据父窗口来设置对话框的位置 @@ -2898,50 +3629,57 @@ class paraWindow(tkinter.Toplevel): self.initial_focus.focus_set() self.wait_window(self) - def init_widgets(self,master): - '''创建自定义对话框的内容''' - nrow=0 + def init_widgets(self, master): + """创建自定义对话框的内容""" + nrow = 0 for i in range(len(self.paraLists)): str0 = self.paraLists[i][0] nrow = i + 1 labelMessage = tkinter.Label(master, text=str0, font=("song", 12)) labelMessage.grid(row=nrow, column=0) - self.names['paraV' + str(i)] = tkinter.StringVar() - self.names['paraE' + str(i)] = tkinter.Entry(master, textvariable=self.names['paraV' + str(i)], width=20) - self.names['paraV' + str(i)].set(self.paraLists[i][1]) - self.names['paraE' + str(i)].grid(row=nrow, column=1)#控件按列排列 - b1 = tkinter.Button(master, text='确定退出', command=self.setPara) + self.names["paraV" + str(i)] = tkinter.StringVar() + self.names["paraE" + str(i)] = tkinter.Entry( + master, textvariable=self.names["paraV" + str(i)], width=20 + ) + self.names["paraV" + str(i)].set(self.paraLists[i][1]) + self.names["paraE" + str(i)].grid(row=nrow, column=1) # 控件按列排列 + b1 = tkinter.Button(master, text="确定退出", command=self.setPara) b1.grid(row=nrow + 1, column=1) self.bind("", self.setPara) self.bind("", self.cancel_click) - return self.names['paraE0'] + return self.names["paraE0"] def cancel_click(self, event=None): - showwarning(title='警告', message='必须先设置参数') + showwarning(title="警告", message="必须先设置参数") self.initial_focus.focus_set() def setPara(self): - '''通过对话框设置参数''' + """通过对话框设置参数""" for i in range(len(self.paraLists)): - text0 = self.names['paraV' + str(i)].get() - if not self.on_validate(text0): # 如果不能通过校验,让用户重新输入 - showwarning(title='警告', message='必须输入数字') - self.names['paraV' + str(i)].set(self.paraLists[i][1]) - self.names['paraE'+ str(i)].focus_set() + text0 = self.names["paraV" + str(i)].get() + if not self.on_validate(text0): # 如果不能通过校验,让用户重新输入 + showwarning(title="警告", message="必须输入数字") + self.names["paraV" + str(i)].set(self.paraLists[i][1]) + self.names["paraE" + str(i)].focus_set() return else: self.paraLists[i][1] = float(text0) self.hide() - def on_validate(self,content): - '''该方法可对用户输入的数据进行校验,保证输入的是数字''' + def on_validate(self, content): + """该方法可对用户输入的数据进行校验,保证输入的是数字""" for i in range(len(content) - 1, -1, -1): - if not (48 <= ord(content[i]) <= 57 or content[i] == "." or content[i] =="+" or content[i] =="-" ): + if not ( + 48 <= ord(content[i]) <= 57 + or content[i] == "." + or content[i] == "+" + or content[i] == "-" + ): return False return True def hide(self): - '''销毁对话框''' + """销毁对话框""" self.withdraw() self.update_idletasks() # 将焦点返回给父窗口 @@ -2951,16 +3689,19 @@ class paraWindow(tkinter.Toplevel): self.root.update() self.root.deiconify() self.root.focus_set() + + # ---------------------------------------------------------------------- class paraWindow2(tkinter.Toplevel): - '''多行多列参数设置窗口 ''' - def __init__(self, root, paraLists,title,explain): + """多行多列参数设置窗口""" + + def __init__(self, root, paraLists, title, explain): """Constructor""" self.root = root - self.names=locals() - self.paraLists=paraLists - self.explain=explain - tkinter.Toplevel.__init__(self,root) + self.names = locals() + self.paraLists = paraLists + self.explain = explain + tkinter.Toplevel.__init__(self, root) # self.geometry("400x300") self.title(title) # 调用init_widgets方法来初始化对话框界面 @@ -2982,42 +3723,51 @@ class paraWindow2(tkinter.Toplevel): explainLabel.pack(padx=50, pady=5) frame1.pack(padx=5, pady=5) frame2 = tkinter.Frame(self) - nrow=1 + nrow = 1 for i in range(len(self.paraLists)): nrow = i + 3 for j in range(len(self.paraLists[i])): - self.names['ev' + str(i) + str(j)] = tkinter.StringVar() - self.names['e' + str(i) + str(j)] = tkinter.Entry(frame2, textvariable=self.names['ev' + str(i) + str(j)],width=10) - self.names['ev' + str(i) + str(j)].set(self.paraLists[i][j]) - self.names['e' + str(i) + str(j)].grid(row=nrow, column=j) - b1 = tkinter.Button(frame2, text='确定退出', command=self.setPara) + self.names["ev" + str(i) + str(j)] = tkinter.StringVar() + self.names["e" + str(i) + str(j)] = tkinter.Entry( + frame2, textvariable=self.names["ev" + str(i) + str(j)], width=10 + ) + self.names["ev" + str(i) + str(j)].set(self.paraLists[i][j]) + self.names["e" + str(i) + str(j)].grid(row=nrow, column=j) + b1 = tkinter.Button(frame2, text="确定退出", command=self.setPara) b1.grid(row=nrow + 1, column=1) frame2.pack(padx=5, pady=6) return b1 def setPara(self): - '''通过对话框给参数赋值''' + """通过对话框给参数赋值""" for i in range(len(self.paraLists)): for j in range(len(self.paraLists[i])): - text0 = self.names['ev' + str(i) + str(j)].get() + text0 = self.names["ev" + str(i) + str(j)].get() if not self.on_validate(text0): # 如果不能通过校验,让用户重新输入 - showwarning(title='警告', message='必须输入数字') - self.names['ev' + str(i) + str(j)].set(self.paraLists[i][j]) - self.names['e' + str(i) + str(j)].focus_set() + showwarning(title="警告", message="必须输入数字") + self.names["ev" + str(i) + str(j)].set(self.paraLists[i][j]) + self.names["e" + str(i) + str(j)].focus_set() return else: self.paraLists[i][j] = float(text0) self.hide() - def on_validate(self,content): - '''该方法可对用户输入的数据进行校验,保证输入的是数字''' + def on_validate(self, content): + """该方法可对用户输入的数据进行校验,保证输入的是数字""" for i in range(len(content) - 1, -1, -1): - if not (48 <= ord(content[i]) <= 57 or content[i] == "." or content[i] == "+" or content[i] == "-"): + if not ( + 48 <= ord(content[i]) <= 57 + or content[i] == "." + or content[i] == "+" + or content[i] == "-" + ): return False return True + def cancel_click(self, event=None): - showwarning(title='警告', message='必须先设置参数') + showwarning(title="警告", message="必须先设置参数") self.initial_focus.focus_set() + def hide(self): """""" self.withdraw() @@ -3029,20 +3779,23 @@ class paraWindow2(tkinter.Toplevel): self.root.update() self.root.deiconify() self.root.focus_set() + + # ---------------------------------------------------------------------- class listWindow(tkinter.Toplevel): - '''下拉列表窗口,用对参数设置''' - def __init__(self, root, title = None, paraLists=[]): + """下拉列表窗口,用对参数设置""" + + def __init__(self, root, title=None, paraLists=[]): """Constructor""" self.root = root - self.paraLists=paraLists - tkinter.Toplevel.__init__(self,root) + self.paraLists = paraLists + tkinter.Toplevel.__init__(self, root) if title: self.title(title) # 创建对话框的主体内容 # 调用init_widgets方法来初始化对话框界面 self.initial_focus = self.init_widgets(self) - # 根据modal选项设置是否为模式对话框 + # 根据modal选项设置是否为模式对话框 self.grab_set() # 为"WM_DELETE_WINDOW"协议使用self.cancel_click事件处理方法 self.protocol("WM_DELETE_WINDOW", self.cancel_click) @@ -3053,27 +3806,30 @@ class listWindow(tkinter.Toplevel): self.initial_focus.focus_set() self.wait_window(self) - def init_widgets(self,master): + def init_widgets(self, master): str0 = self.paraLists[0][0] - labelMessage = tkinter.Label(master, text=self.paraLists[0][0], font=("song", 12)) + labelMessage = tkinter.Label( + master, text=self.paraLists[0][0], font=("song", 12) + ) labelMessage.grid(row=0, column=0) - self.listBox = tkinter.Listbox(master,selectmode=tkinter.SINGLE) + self.listBox = tkinter.Listbox(master, selectmode=tkinter.SINGLE) self.listBox.grid(row=1, column=0) - lists=self.paraLists[0][1] + lists = self.paraLists[0][1] for item in lists: self.listBox.insert(tkinter.END, item) self.listBox.select_set(0) - b1 = tkinter.Button(master, text='确定退出', command=self.setPara) + b1 = tkinter.Button(master, text="确定退出", command=self.setPara) b1.grid(row=2, column=0) self.bind("", self.setPara) self.bind("", self.cancel_click) return self.listBox def cancel_click(self, event=None): - showwarning(title='警告', message='必须先设置参数') + showwarning(title="警告", message="必须先设置参数") self.initial_focus.focus_set() + def setPara(self): - self.returnValue=self.listBox.curselection()[0] + self.returnValue = self.listBox.curselection()[0] self.hide() def hide(self): @@ -3088,10 +3844,11 @@ class listWindow(tkinter.Toplevel): self.root.deiconify() self.root.focus_set() -#------------------------------------------- + +# ------------------------------------------- if __name__ == "__main__": root = tkinter.Tk() root.geometry("1000x640+150+5") # 界面大小,以相对屏幕的坐标 root.resizable(0, 0) myWindow = MyApp(root) - root.mainloop() \ No newline at end of file + root.mainloop()