├── preview-mac.png
├── preview-windows.png
├── README.md
├── propertyWindow.py
├── propertyWindow.ui
├── mainWindow.ui
├── mainWindow.py
└── main.py
/preview-mac.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/crazy-zxx/ImageProcessor/HEAD/preview-mac.png
--------------------------------------------------------------------------------
/preview-windows.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/crazy-zxx/ImageProcessor/HEAD/preview-windows.png
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # ImageProcessor
2 | 基于PyQt+OpenCV的图像处理软件
3 | ### 效果预览
4 | 
5 | 
6 | ### 环境搭建
7 | Python3.8 + PyQt5 + opencv-python + matplotlib
8 | #### 安装Python
9 | Linux:
10 | ```
11 | apt install python3.8
12 | ```
13 | 或者
14 | ```
15 | yum install python3.8
16 | ```
17 | Windows:直接拿exe安装,记得加系统环境path
18 | #### 安装依赖库
19 | ```
20 | pip install PyQt5
21 | pip install opencv-python
22 | pip install matplotlib
23 | ```
24 |
--------------------------------------------------------------------------------
/propertyWindow.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | # Form implementation generated from reading ui file 'propertyWindow.ui'
4 | #
5 | # Created by: PyQt5 UI code generator 5.15.4
6 | #
7 | # WARNING: Any manual changes made to this file will be lost when pyuic5 is
8 | # run again. Do not edit this file unless you know what you are doing.
9 |
10 |
11 | from PyQt5 import QtCore, QtGui, QtWidgets
12 |
13 |
14 | class Ui_Form(object):
15 | def setupUi(self, Form):
16 | Form.setObjectName("Form")
17 | Form.resize(300, 200)
18 | Form.setMinimumSize(QtCore.QSize(300, 200))
19 | Form.setMaximumSize(QtCore.QSize(300, 200))
20 | Form.setWindowTitle("")
21 | self.gridLayout = QtWidgets.QGridLayout(Form)
22 | self.gridLayout.setObjectName("gridLayout")
23 | self.submitButton = QtWidgets.QPushButton(Form)
24 | self.submitButton.setObjectName("submitButton")
25 | self.gridLayout.addWidget(self.submitButton, 1, 1, 1, 1)
26 | self.horizontalLayout = QtWidgets.QHBoxLayout()
27 | self.horizontalLayout.setObjectName("horizontalLayout")
28 | self.propertyLabel = QtWidgets.QLabel(Form)
29 | self.propertyLabel.setObjectName("propertyLabel")
30 | self.horizontalLayout.addWidget(self.propertyLabel)
31 | self.spinBox = QtWidgets.QSpinBox(Form)
32 | self.spinBox.setMinimum(-100)
33 | self.spinBox.setMaximum(100)
34 | self.spinBox.setSingleStep(1)
35 | self.spinBox.setProperty("value", 0)
36 | self.spinBox.setObjectName("spinBox")
37 | self.horizontalLayout.addWidget(self.spinBox)
38 | self.slider = QtWidgets.QSlider(Form)
39 | self.slider.setEnabled(True)
40 | font = QtGui.QFont()
41 | font.setKerning(True)
42 | self.slider.setFont(font)
43 | self.slider.setCursor(QtGui.QCursor(QtCore.Qt.SizeHorCursor))
44 | self.slider.setAcceptDrops(False)
45 | self.slider.setMinimum(-100)
46 | self.slider.setMaximum(100)
47 | self.slider.setSingleStep(1)
48 | self.slider.setPageStep(1)
49 | self.slider.setTracking(True)
50 | self.slider.setOrientation(QtCore.Qt.Horizontal)
51 | self.slider.setInvertedAppearance(False)
52 | self.slider.setInvertedControls(False)
53 | self.slider.setObjectName("slider")
54 | self.horizontalLayout.addWidget(self.slider)
55 | self.gridLayout.addLayout(self.horizontalLayout, 0, 0, 1, 3)
56 |
57 | self.retranslateUi(Form)
58 | QtCore.QMetaObject.connectSlotsByName(Form)
59 |
60 | def retranslateUi(self, Form):
61 | _translate = QtCore.QCoreApplication.translate
62 | self.submitButton.setText(_translate("Form", "确定"))
63 | self.propertyLabel.setText(_translate("Form", "亮度"))
64 |
--------------------------------------------------------------------------------
/propertyWindow.ui:
--------------------------------------------------------------------------------
1 |
2 |
3 | Form
4 |
5 |
6 |
7 | 0
8 | 0
9 | 300
10 | 200
11 |
12 |
13 |
14 |
15 | 300
16 | 200
17 |
18 |
19 |
20 |
21 | 300
22 | 200
23 |
24 |
25 |
26 |
27 |
28 |
29 | -
30 |
31 |
32 | 确定
33 |
34 |
35 |
36 | -
37 |
38 |
-
39 |
40 |
41 | 亮度
42 |
43 |
44 |
45 | -
46 |
47 |
48 | -100
49 |
50 |
51 | 100
52 |
53 |
54 | 1
55 |
56 |
57 | 0
58 |
59 |
60 |
61 | -
62 |
63 |
64 | true
65 |
66 |
67 |
68 | true
69 |
70 |
71 |
72 | SizeHorCursor
73 |
74 |
75 | false
76 |
77 |
78 | -100
79 |
80 |
81 | 100
82 |
83 |
84 | 1
85 |
86 |
87 | 1
88 |
89 |
90 | true
91 |
92 |
93 | Qt::Horizontal
94 |
95 |
96 | false
97 |
98 |
99 | false
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
--------------------------------------------------------------------------------
/mainWindow.ui:
--------------------------------------------------------------------------------
1 |
2 |
3 | MainWindow
4 |
5 |
6 |
7 | 0
8 | 0
9 | 896
10 | 577
11 |
12 |
13 |
14 | 图像处理软件
15 |
16 |
17 |
18 | -
19 |
20 |
21 | Qt::ScrollBarAlwaysOn
22 |
23 |
24 | Qt::ScrollBarAlwaysOn
25 |
26 |
27 |
28 | -
29 |
30 |
31 |
32 | 0
33 | 0
34 |
35 |
36 |
37 | Qt::ScrollBarAlwaysOn
38 |
39 |
40 | Qt::ScrollBarAlwaysOn
41 |
42 |
43 |
44 | -
45 |
46 |
47 | 原图预览
48 |
49 |
50 | Qt::AlignCenter
51 |
52 |
53 |
54 | -
55 |
56 |
57 | 处理后的图片预览
58 |
59 |
60 | Qt::AlignCenter
61 |
62 |
63 |
64 |
65 |
66 |
168 |
169 |
170 |
171 | 打开
172 |
173 |
174 |
175 |
176 | 保存
177 |
178 |
179 |
180 |
181 | 另存为
182 |
183 |
184 |
185 |
186 | 退出
187 |
188 |
189 |
190 |
191 | 恢复到原始图片
192 |
193 |
194 |
195 |
196 | 关于作者
197 |
198 |
199 |
200 |
201 | gg
202 |
203 |
204 |
205 |
206 | 灰度化
207 |
208 |
209 |
210 |
211 | 二值化
212 |
213 |
214 |
215 |
216 | 颜色反转
217 |
218 |
219 |
220 |
221 | 加
222 |
223 |
224 |
225 |
226 | 减
227 |
228 |
229 |
230 |
231 | 乘
232 |
233 |
234 |
235 |
236 | 归一化直方图
237 |
238 |
239 |
240 |
241 | 直方图均衡化
242 |
243 |
244 |
245 |
246 | 亮度
247 |
248 |
249 |
250 |
251 | 对比度
252 |
253 |
254 |
255 |
256 | 锐度
257 |
258 |
259 |
260 |
261 | 缩放
262 |
263 |
264 |
265 |
266 | 旋转
267 |
268 |
269 |
270 |
271 | gg
272 |
273 |
274 |
275 |
276 | 饱和度
277 |
278 |
279 |
280 |
281 | 色调
282 |
283 |
284 |
285 |
286 | 重新着色
287 |
288 |
289 |
290 |
291 | 加高斯噪声
292 |
293 |
294 |
295 |
296 | gg
297 |
298 |
299 |
300 |
301 | gg
302 |
303 |
304 |
305 |
306 | 均值滤波器
307 |
308 |
309 |
310 |
311 | 中值滤波器
312 |
313 |
314 |
315 |
316 | Sobel算子
317 |
318 |
319 |
320 |
321 | Prewitt算子
322 |
323 |
324 |
325 |
326 | 拉普拉斯算子
327 |
328 |
329 |
330 |
331 | 加均匀噪声
332 |
333 |
334 |
335 |
336 | 加脉冲噪声
337 |
338 |
339 |
340 |
341 |
342 |
343 |
--------------------------------------------------------------------------------
/mainWindow.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | # Form implementation generated from reading ui file 'mainWindow.ui'
4 | #
5 | # Created by: PyQt5 UI code generator 5.15.9
6 | #
7 | # WARNING: Any manual changes made to this file will be lost when pyuic5 is
8 | # run again. Do not edit this file unless you know what you are doing.
9 |
10 |
11 | from PyQt5 import QtCore, QtGui, QtWidgets
12 |
13 |
14 | class Ui_MainWindow(object):
15 | def setupUi(self, MainWindow):
16 | MainWindow.setObjectName("MainWindow")
17 | MainWindow.resize(896, 577)
18 | self.centralwidget = QtWidgets.QWidget(MainWindow)
19 | self.centralwidget.setObjectName("centralwidget")
20 | self.gridLayout = QtWidgets.QGridLayout(self.centralwidget)
21 | self.gridLayout.setObjectName("gridLayout")
22 | self.outImageView = QtWidgets.QGraphicsView(self.centralwidget)
23 | self.outImageView.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOn)
24 | self.outImageView.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOn)
25 | self.outImageView.setObjectName("outImageView")
26 | self.gridLayout.addWidget(self.outImageView, 1, 1, 1, 1)
27 | self.srcImageView = QtWidgets.QGraphicsView(self.centralwidget)
28 | sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding)
29 | sizePolicy.setHorizontalStretch(0)
30 | sizePolicy.setVerticalStretch(0)
31 | sizePolicy.setHeightForWidth(self.srcImageView.sizePolicy().hasHeightForWidth())
32 | self.srcImageView.setSizePolicy(sizePolicy)
33 | self.srcImageView.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOn)
34 | self.srcImageView.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOn)
35 | self.srcImageView.setObjectName("srcImageView")
36 | self.gridLayout.addWidget(self.srcImageView, 1, 0, 1, 1)
37 | self.srcImageLabel = QtWidgets.QLabel(self.centralwidget)
38 | self.srcImageLabel.setAlignment(QtCore.Qt.AlignCenter)
39 | self.srcImageLabel.setObjectName("srcImageLabel")
40 | self.gridLayout.addWidget(self.srcImageLabel, 0, 0, 1, 1)
41 | self.outImageLabel = QtWidgets.QLabel(self.centralwidget)
42 | self.outImageLabel.setAlignment(QtCore.Qt.AlignCenter)
43 | self.outImageLabel.setObjectName("outImageLabel")
44 | self.gridLayout.addWidget(self.outImageLabel, 0, 1, 1, 1)
45 | MainWindow.setCentralWidget(self.centralwidget)
46 | self.menubar = QtWidgets.QMenuBar(MainWindow)
47 | self.menubar.setGeometry(QtCore.QRect(0, 0, 896, 24))
48 | self.menubar.setObjectName("menubar")
49 | self.fileMenu = QtWidgets.QMenu(self.menubar)
50 | self.fileMenu.setObjectName("fileMenu")
51 | self.resetImageMenu = QtWidgets.QMenu(self.menubar)
52 | self.resetImageMenu.setObjectName("resetImageMenu")
53 | self.aboutMenu = QtWidgets.QMenu(self.menubar)
54 | self.aboutMenu.setObjectName("aboutMenu")
55 | self.grayMappingMenu = QtWidgets.QMenu(self.menubar)
56 | self.grayMappingMenu.setObjectName("grayMappingMenu")
57 | self.operateImageMenu = QtWidgets.QMenu(self.menubar)
58 | self.operateImageMenu.setObjectName("operateImageMenu")
59 | self.histogramMenu = QtWidgets.QMenu(self.menubar)
60 | self.histogramMenu.setObjectName("histogramMenu")
61 | self.noiseMenu = QtWidgets.QMenu(self.menubar)
62 | self.noiseMenu.setObjectName("noiseMenu")
63 | self.filterMenu = QtWidgets.QMenu(self.menubar)
64 | self.filterMenu.setObjectName("filterMenu")
65 | self.smoothMenu = QtWidgets.QMenu(self.filterMenu)
66 | self.smoothMenu.setObjectName("smoothMenu")
67 | self.sharpMenu = QtWidgets.QMenu(self.filterMenu)
68 | self.sharpMenu.setObjectName("sharpMenu")
69 | MainWindow.setMenuBar(self.menubar)
70 | self.statusbar = QtWidgets.QStatusBar(MainWindow)
71 | self.statusbar.setObjectName("statusbar")
72 | MainWindow.setStatusBar(self.statusbar)
73 | self.openFileAction = QtWidgets.QAction(MainWindow)
74 | self.openFileAction.setObjectName("openFileAction")
75 | self.saveFileAction = QtWidgets.QAction(MainWindow)
76 | self.saveFileAction.setObjectName("saveFileAction")
77 | self.saveFileAsAction = QtWidgets.QAction(MainWindow)
78 | self.saveFileAsAction.setObjectName("saveFileAsAction")
79 | self.exitAppAction = QtWidgets.QAction(MainWindow)
80 | self.exitAppAction.setObjectName("exitAppAction")
81 | self.resetImageAction = QtWidgets.QAction(MainWindow)
82 | self.resetImageAction.setObjectName("resetImageAction")
83 | self.aboutAction = QtWidgets.QAction(MainWindow)
84 | self.aboutAction.setObjectName("aboutAction")
85 | self.actiongg_2 = QtWidgets.QAction(MainWindow)
86 | self.actiongg_2.setObjectName("actiongg_2")
87 | self.grayAction = QtWidgets.QAction(MainWindow)
88 | self.grayAction.setObjectName("grayAction")
89 | self.binaryAction = QtWidgets.QAction(MainWindow)
90 | self.binaryAction.setObjectName("binaryAction")
91 | self.reverseAction = QtWidgets.QAction(MainWindow)
92 | self.reverseAction.setObjectName("reverseAction")
93 | self.imageAddAction = QtWidgets.QAction(MainWindow)
94 | self.imageAddAction.setObjectName("imageAddAction")
95 | self.imageSubtractAction = QtWidgets.QAction(MainWindow)
96 | self.imageSubtractAction.setObjectName("imageSubtractAction")
97 | self.imageMultiplyAction = QtWidgets.QAction(MainWindow)
98 | self.imageMultiplyAction.setObjectName("imageMultiplyAction")
99 | self.histogramAction = QtWidgets.QAction(MainWindow)
100 | self.histogramAction.setObjectName("histogramAction")
101 | self.histogramEqAction = QtWidgets.QAction(MainWindow)
102 | self.histogramEqAction.setObjectName("histogramEqAction")
103 | self.lightAction = QtWidgets.QAction(MainWindow)
104 | self.lightAction.setObjectName("lightAction")
105 | self.contrastAction = QtWidgets.QAction(MainWindow)
106 | self.contrastAction.setObjectName("contrastAction")
107 | self.sharpAction = QtWidgets.QAction(MainWindow)
108 | self.sharpAction.setObjectName("sharpAction")
109 | self.zoomAction = QtWidgets.QAction(MainWindow)
110 | self.zoomAction.setObjectName("zoomAction")
111 | self.rotateAction = QtWidgets.QAction(MainWindow)
112 | self.rotateAction.setObjectName("rotateAction")
113 | self.actiongg = QtWidgets.QAction(MainWindow)
114 | self.actiongg.setObjectName("actiongg")
115 | self.saturationAction = QtWidgets.QAction(MainWindow)
116 | self.saturationAction.setObjectName("saturationAction")
117 | self.hueAction = QtWidgets.QAction(MainWindow)
118 | self.hueAction.setObjectName("hueAction")
119 | self.reColorAction = QtWidgets.QAction(MainWindow)
120 | self.reColorAction.setObjectName("reColorAction")
121 | self.addGaussianNoiseAction = QtWidgets.QAction(MainWindow)
122 | self.addGaussianNoiseAction.setObjectName("addGaussianNoiseAction")
123 | self.actiongg_3 = QtWidgets.QAction(MainWindow)
124 | self.actiongg_3.setObjectName("actiongg_3")
125 | self.actiongg_4 = QtWidgets.QAction(MainWindow)
126 | self.actiongg_4.setObjectName("actiongg_4")
127 | self.meanValueAction = QtWidgets.QAction(MainWindow)
128 | self.meanValueAction.setObjectName("meanValueAction")
129 | self.medianValueAction = QtWidgets.QAction(MainWindow)
130 | self.medianValueAction.setObjectName("medianValueAction")
131 | self.sobelAction = QtWidgets.QAction(MainWindow)
132 | self.sobelAction.setObjectName("sobelAction")
133 | self.prewittAction = QtWidgets.QAction(MainWindow)
134 | self.prewittAction.setObjectName("prewittAction")
135 | self.laplacianAction = QtWidgets.QAction(MainWindow)
136 | self.laplacianAction.setObjectName("laplacianAction")
137 | self.addUiformNoiseAction = QtWidgets.QAction(MainWindow)
138 | self.addUiformNoiseAction.setObjectName("addUiformNoiseAction")
139 | self.addImpulseNoiseAction = QtWidgets.QAction(MainWindow)
140 | self.addImpulseNoiseAction.setObjectName("addImpulseNoiseAction")
141 | self.fileMenu.addAction(self.openFileAction)
142 | self.fileMenu.addAction(self.saveFileAction)
143 | self.fileMenu.addAction(self.saveFileAsAction)
144 | self.fileMenu.addSeparator()
145 | self.fileMenu.addAction(self.exitAppAction)
146 | self.resetImageMenu.addAction(self.resetImageAction)
147 | self.aboutMenu.addAction(self.aboutAction)
148 | self.grayMappingMenu.addAction(self.grayAction)
149 | self.grayMappingMenu.addAction(self.binaryAction)
150 | self.grayMappingMenu.addAction(self.reverseAction)
151 | self.grayMappingMenu.addSeparator()
152 | self.grayMappingMenu.addAction(self.lightAction)
153 | self.grayMappingMenu.addAction(self.contrastAction)
154 | self.grayMappingMenu.addAction(self.sharpAction)
155 | self.grayMappingMenu.addAction(self.saturationAction)
156 | self.grayMappingMenu.addAction(self.hueAction)
157 | self.operateImageMenu.addAction(self.imageAddAction)
158 | self.operateImageMenu.addAction(self.imageSubtractAction)
159 | self.operateImageMenu.addAction(self.imageMultiplyAction)
160 | self.operateImageMenu.addSeparator()
161 | self.operateImageMenu.addAction(self.zoomAction)
162 | self.operateImageMenu.addAction(self.rotateAction)
163 | self.histogramMenu.addAction(self.histogramAction)
164 | self.histogramMenu.addAction(self.histogramEqAction)
165 | self.noiseMenu.addAction(self.addGaussianNoiseAction)
166 | self.noiseMenu.addAction(self.addUiformNoiseAction)
167 | self.noiseMenu.addAction(self.addImpulseNoiseAction)
168 | self.smoothMenu.addAction(self.meanValueAction)
169 | self.smoothMenu.addAction(self.medianValueAction)
170 | self.sharpMenu.addAction(self.sobelAction)
171 | self.sharpMenu.addAction(self.prewittAction)
172 | self.sharpMenu.addAction(self.laplacianAction)
173 | self.filterMenu.addAction(self.smoothMenu.menuAction())
174 | self.filterMenu.addAction(self.sharpMenu.menuAction())
175 | self.menubar.addAction(self.fileMenu.menuAction())
176 | self.menubar.addAction(self.resetImageMenu.menuAction())
177 | self.menubar.addAction(self.grayMappingMenu.menuAction())
178 | self.menubar.addAction(self.operateImageMenu.menuAction())
179 | self.menubar.addAction(self.histogramMenu.menuAction())
180 | self.menubar.addAction(self.noiseMenu.menuAction())
181 | self.menubar.addAction(self.filterMenu.menuAction())
182 | self.menubar.addAction(self.aboutMenu.menuAction())
183 |
184 | self.retranslateUi(MainWindow)
185 | QtCore.QMetaObject.connectSlotsByName(MainWindow)
186 |
187 | def retranslateUi(self, MainWindow):
188 | _translate = QtCore.QCoreApplication.translate
189 | MainWindow.setWindowTitle(_translate("MainWindow", "图像处理软件"))
190 | self.srcImageLabel.setText(_translate("MainWindow", "原图预览"))
191 | self.outImageLabel.setText(_translate("MainWindow", "处理后的图片预览"))
192 | self.fileMenu.setTitle(_translate("MainWindow", "文件"))
193 | self.resetImageMenu.setTitle(_translate("MainWindow", "重置图片"))
194 | self.aboutMenu.setTitle(_translate("MainWindow", "关于"))
195 | self.grayMappingMenu.setTitle(_translate("MainWindow", "直接灰度映射"))
196 | self.operateImageMenu.setTitle(_translate("MainWindow", "图像运算"))
197 | self.histogramMenu.setTitle(_translate("MainWindow", "直方图均衡"))
198 | self.noiseMenu.setTitle(_translate("MainWindow", "噪声"))
199 | self.filterMenu.setTitle(_translate("MainWindow", "空域滤波"))
200 | self.smoothMenu.setTitle(_translate("MainWindow", "平滑滤波器"))
201 | self.sharpMenu.setTitle(_translate("MainWindow", "锐化滤波器"))
202 | self.openFileAction.setText(_translate("MainWindow", "打开"))
203 | self.saveFileAction.setText(_translate("MainWindow", "保存"))
204 | self.saveFileAsAction.setText(_translate("MainWindow", "另存为"))
205 | self.exitAppAction.setText(_translate("MainWindow", "退出"))
206 | self.resetImageAction.setText(_translate("MainWindow", "恢复到原始图片"))
207 | self.aboutAction.setText(_translate("MainWindow", "关于作者"))
208 | self.actiongg_2.setText(_translate("MainWindow", "gg"))
209 | self.grayAction.setText(_translate("MainWindow", "灰度化"))
210 | self.binaryAction.setText(_translate("MainWindow", "二值化"))
211 | self.reverseAction.setText(_translate("MainWindow", "颜色反转"))
212 | self.imageAddAction.setText(_translate("MainWindow", "加"))
213 | self.imageSubtractAction.setText(_translate("MainWindow", "减"))
214 | self.imageMultiplyAction.setText(_translate("MainWindow", "乘"))
215 | self.histogramAction.setText(_translate("MainWindow", "归一化直方图"))
216 | self.histogramEqAction.setText(_translate("MainWindow", "直方图均衡化"))
217 | self.lightAction.setText(_translate("MainWindow", "亮度"))
218 | self.contrastAction.setText(_translate("MainWindow", "对比度"))
219 | self.sharpAction.setText(_translate("MainWindow", "锐度"))
220 | self.zoomAction.setText(_translate("MainWindow", "缩放"))
221 | self.rotateAction.setText(_translate("MainWindow", "旋转"))
222 | self.actiongg.setText(_translate("MainWindow", "gg"))
223 | self.saturationAction.setText(_translate("MainWindow", "饱和度"))
224 | self.hueAction.setText(_translate("MainWindow", "色调"))
225 | self.reColorAction.setText(_translate("MainWindow", "重新着色"))
226 | self.addGaussianNoiseAction.setText(_translate("MainWindow", "加高斯噪声"))
227 | self.actiongg_3.setText(_translate("MainWindow", "gg"))
228 | self.actiongg_4.setText(_translate("MainWindow", "gg"))
229 | self.meanValueAction.setText(_translate("MainWindow", "均值滤波器"))
230 | self.medianValueAction.setText(_translate("MainWindow", "中值滤波器"))
231 | self.sobelAction.setText(_translate("MainWindow", "Sobel算子"))
232 | self.prewittAction.setText(_translate("MainWindow", "Prewitt算子"))
233 | self.laplacianAction.setText(_translate("MainWindow", "拉普拉斯算子"))
234 | self.addUiformNoiseAction.setText(_translate("MainWindow", "加均匀噪声"))
235 | self.addImpulseNoiseAction.setText(_translate("MainWindow", "加脉冲噪声"))
236 |
--------------------------------------------------------------------------------
/main.py:
--------------------------------------------------------------------------------
1 | import os
2 | import random
3 | import sys
4 |
5 | import cv2
6 | import numpy
7 | import numpy as np
8 | from PyQt5 import QtGui, QtCore
9 | from PyQt5.QtGui import QImage, QPixmap
10 | from PyQt5.QtWidgets import QMainWindow, QWidget, QApplication, QGraphicsScene, QFileDialog, QMessageBox
11 | from matplotlib import pyplot as plt
12 |
13 | from mainWindow import Ui_MainWindow
14 | from propertyWindow import Ui_Form
15 |
16 |
17 | # 预处理窗口类
18 | # 就是弹出来的调整各种属性值的小窗口
19 | class PropertyWindow(QWidget, Ui_Form):
20 | # 信号只能在Object的子类中创建,并且只能在创建类的时候的时候添加,而不能等类已经定义完再作为动态属性添加进去。
21 | # 自定义的信号在__init__()函数之前定义。
22 | # 自定义一个信号signal,有一个object类型的参数
23 | # **** 鬼知道咋回事,想用str类型但会出bug,只好用object了 ****
24 | signal = QtCore.pyqtSignal(object)
25 |
26 | # 类初始化
27 | def __init__(self):
28 | # 调用父类的初始化
29 | super(PropertyWindow, self).__init__()
30 | # 窗口界面初始化
31 | self.setupUi(self)
32 |
33 | # 绑定窗口组件响应事件的处理函数(将窗口中的组件被用户触发的点击、值变化等事件绑定到处理函数)
34 | # 数值框的值改变
35 | self.spinBox.valueChanged.connect(self.__spinBoxChange)
36 | # 滑动条的值改变
37 | self.slider.valueChanged.connect(self.__sliderChange)
38 | # 点击确认按钮
39 | self.submitButton.clicked.connect(self.__valueConfirm)
40 |
41 | # 数值框值改变的处理函数
42 | def __spinBoxChange(self):
43 | # 获取数值框的当前值
44 | value = self.spinBox.value()
45 | # 与滑动条进行数值同步
46 | self.slider.setValue(value)
47 | # 发送信号到主窗口,参数是当前数值(主窗口有自定义的接收并处理该信号的函数)
48 | self.signal.emit(value)
49 |
50 | # 滑动条值改变的处理函数
51 | def __sliderChange(self):
52 | # 获取滑动条的当前值
53 | value = self.slider.value()
54 | # 与数值框进行数值同步
55 | # 注意:该操作也会触发数值框的数值改变,即会触发调用__spinBoxChange(),所以不要需要在此处重复发信号到主窗口
56 | self.spinBox.setValue(value)
57 |
58 | # 确认按钮按下的处理函数
59 | def __valueConfirm(self):
60 | # 发送确认修改信号
61 | self.signal.emit('ok')
62 | # 关闭窗口
63 | self.close()
64 |
65 | # 重写窗口关闭处理
66 | def closeEvent(self, a0: QtGui.QCloseEvent) -> None:
67 | # 发送取消修改信号,关闭窗口时触发
68 | self.signal.emit('close')
69 |
70 |
71 | # 主窗口类
72 | class MainWindow(QMainWindow, Ui_MainWindow):
73 | # 类初始化
74 | def __init__(self):
75 | # 调用父类的初始化
76 | super(MainWindow, self).__init__()
77 | # 窗口界面初始化
78 | self.setupUi(self)
79 |
80 | # 图像属性调整的子窗口,初始化默认空(亮度、对比度、锐度、饱和度、色调、旋转、缩放调节窗口)
81 | self.__propertyWindow = None
82 |
83 | # 当前打开的图片文件名,初始化默认空
84 | self.__fileName = None
85 |
86 | # 保存图片原始数据,初始化默认空
87 | self.__srcImageRGB = None
88 | # 保存图片最终处理结果的数据,初始化默认空
89 | self.__outImageRGB = None
90 | # 保存图片暂时修改的数据,初始化默认空
91 | # (在修改图像属性未点击确认时,需要暂存修改数据,如果确认后将临时数据同步为最终结果数据,如果未确认将复原数据)
92 | self.__tempImageRGB = None
93 |
94 | # 绑定窗口事件的响应函数
95 | # 文件菜单
96 | # 打开文件
97 | self.openFileAction.triggered.connect(self.__openFileAndShowImage)
98 | # 保存文件
99 | self.saveFileAction.triggered.connect(self.saveFile)
100 | # 另存为文件
101 | self.saveFileAsAction.triggered.connect(self.saveFileAs)
102 | # 退出程序
103 | self.exitAppAction.triggered.connect(self.close)
104 |
105 | # 重置图像菜单
106 | # 重置图像
107 | self.resetImageAction.triggered.connect(self.__resetImage)
108 |
109 | # 直接灰度映射菜单
110 | # 灰度化
111 | self.grayAction.triggered.connect(self.__toGrayImage)
112 | # 二值化
113 | self.binaryAction.triggered.connect(self.__toBinaryImage)
114 | # 颜色反转
115 | self.reverseAction.triggered.connect(self.__reverseImage)
116 | # 亮度调整
117 | self.lightAction.triggered.connect(self.__openLightWindow)
118 | # 对比度调整
119 | self.contrastAction.triggered.connect(self.__openContrastWindow)
120 | # 锐度调整
121 | self.sharpAction.triggered.connect(self.__openSharpWindow)
122 | # 饱和度调整
123 | self.saturationAction.triggered.connect(self.__openSaturationWindow)
124 | # 色度调整
125 | self.hueAction.triggered.connect(self.__openHueWindow)
126 |
127 | # 图像运算菜单
128 | # 加
129 | self.imageAddAction.triggered.connect(self.__addImage)
130 | # 减
131 | self.imageSubtractAction.triggered.connect(self.__subtractImage)
132 | # 乘
133 | self.imageMultiplyAction.triggered.connect(self.__multiplyImage)
134 | # 缩放
135 | self.zoomAction.triggered.connect(self.__openZoomWindow)
136 | # 旋转
137 | self.rotateAction.triggered.connect(self.__openRotateWindow)
138 |
139 | # 直方图均衡菜单
140 | # 归一化直方图
141 | self.histogramAction.triggered.connect(self.__histogram)
142 | # 直方图均衡化
143 | self.histogramEqAction.triggered.connect(self.__histogramEqualization)
144 |
145 | # 噪声菜单
146 | # 加高斯噪声
147 | self.addGaussianNoiseAction.triggered.connect(self.__addGasussNoise)
148 | # 加均匀噪声
149 | self.addUiformNoiseAction.triggered.connect(self.__addUniformNoise)
150 | # 加脉冲(椒盐)噪声
151 | self.addImpulseNoiseAction.triggered.connect(self.__addImpulseNoise)
152 |
153 | # 空域滤波菜单
154 | # 均值滤波
155 | self.meanValueAction.triggered.connect(self.__meanValueFilter)
156 | # 中值滤波
157 | self.medianValueAction.triggered.connect(self.__medianValueFilter)
158 | # Sobel算子锐化
159 | self.sobelAction.triggered.connect(self.__sobel)
160 | # Prewitt算子锐化
161 | self.prewittAction.triggered.connect(self.__prewitt)
162 | # 拉普拉斯算子锐化
163 | self.laplacianAction.triggered.connect(self.__laplacian)
164 |
165 | # 关于菜单
166 | # 关于作者
167 | self.aboutAction.triggered.connect(self.__aboutAuthor)
168 |
169 | # -----------------------------------文件-----------------------------------
170 | # 打开文件并在主窗口中显示打开的图像
171 | def __openFileAndShowImage(self):
172 | # 打开文件选择窗口
173 | __fileName, _ = QFileDialog.getOpenFileName(self, '选择图片', '.', 'Image Files(*.png *.jpeg *.jpg *.bmp)')
174 | # 文件存在
175 | if __fileName and os.path.exists(__fileName):
176 | # 设置打开的文件名属性
177 | self.__fileName = __fileName
178 | # 转换颜色空间,cv2默认打开BGR空间,Qt界面显示需要RGB空间,所以就统一到RGB吧
179 | # __bgrImg = cv2.imread(self.__fileName)
180 | # cv2 读取不了有中文名的图像文件 !
181 | # 所以用numpy读取数据,再用cv2.imdecode解码数据来解决。
182 | __bgrImg = cv2.imdecode(np.fromfile(self.__fileName, dtype=np.uint8), -1)
183 | # 设置初始化数据
184 | self.__srcImageRGB = cv2.cvtColor(__bgrImg, cv2.COLOR_BGR2RGB)
185 | self.__outImageRGB = self.__srcImageRGB.copy()
186 | self.__tempImageRGB = self.__srcImageRGB.copy()
187 | # 在窗口中左侧QGraphicsView区域显示图片
188 | self.__drawImage(self.srcImageView, self.__srcImageRGB)
189 | # 在窗口中右侧QGraphicsView区域显示图片
190 | self.__drawImage(self.outImageView, self.__srcImageRGB)
191 |
192 | # 在窗口中指定的QGraphicsView区域(左或右)显示指定类型(rgb、灰度、二值)的图像
193 | def __drawImage(self, location, img):
194 | # RBG图
195 | if len(img.shape) > 2:
196 | # 获取行(高度)、列(宽度)、通道数
197 | __height, __width, __channel = img.shape
198 | # 转换为QImage对象,注意第四、五个参数
199 | __qImg = QImage(img, __width, __height, __width * __channel, QImage.Format_RGB888)
200 | # 灰度图、二值图
201 | else:
202 | # 获取行(高度)、列(宽度)、通道数
203 | __height, __width = img.shape
204 | # 转换为QImage对象,注意第四、五个参数
205 | __qImg = QImage(img, __width, __height, __width, QImage.Format_Indexed8)
206 |
207 | # 创建QPixmap对象
208 | __qPixmap = QPixmap.fromImage(__qImg)
209 | # 创建显示容器QGraphicsScene对象
210 | __scene = QGraphicsScene()
211 | # 填充QGraphicsScene对象
212 | __scene.addPixmap(__qPixmap)
213 | # 将QGraphicsScene对象设置到QGraphicsView区域实现图片显示
214 | location.setScene(__scene)
215 |
216 | # 执行保存图片文件的操作
217 | def __saveImg(self, fileName):
218 | # 已经打开了文件才能保存
219 | if fileName:
220 | # RGB转BRG空间后才能通过opencv正确保存
221 | __bgrImg = cv2.cvtColor(self.__outImageRGB, cv2.COLOR_RGB2BGR)
222 | # 保存
223 | cv2.imwrite(fileName, __bgrImg)
224 | # 消息提示窗口
225 | QMessageBox.information(self, '提示', '文件保存成功!')
226 | else:
227 | # 消息提示窗口
228 | QMessageBox.information(self, '提示', '文件保存失败!')
229 |
230 | # 保存文件,覆盖原始文件
231 | def saveFile(self):
232 | self.__saveImg(self.__fileName)
233 |
234 | # 文件另存
235 | def saveFileAs(self):
236 | # 已经打开了文件才能保存
237 | if self.__fileName:
238 | # 打开文件保存的选择窗口
239 | __fileName, _ = QFileDialog.getSaveFileName(self, '保存图片', 'Image',
240 | 'Image Files(*.png *.jpeg *.jpg *.bmp)')
241 | self.__saveImg(__fileName)
242 | else:
243 | # 消息提示窗口
244 | QMessageBox.information(self, '提示', '文件保存失败!')
245 |
246 | # 重写窗口关闭事件函数,来关闭所有窗口。因为默认关闭主窗口子窗口依然存在。
247 | def closeEvent(self, a0: QtGui.QCloseEvent) -> None:
248 | sys.exit(0)
249 |
250 | # -----------------------------------重置图片-----------------------------------
251 | # 重置图片到初始状态
252 | def __resetImage(self):
253 | if self.__fileName:
254 | # 还原文件打开时的初始化图片数据
255 | self.__outImageRGB = self.__srcImageRGB.copy()
256 | # 窗口显示图片
257 | self.__drawImage(self.outImageView, self.__outImageRGB)
258 |
259 | # -----------------------------------图像预处理-----------------------------------
260 | # 灰度化
261 | def __toGrayImage(self):
262 | # 只有RGB图才能灰度化
263 | if self.__fileName and len(self.__outImageRGB.shape) > 2:
264 | # 灰度化使得三通道RGB图变成单通道灰度图
265 | self.__outImageRGB = cv2.cvtColor(self.__outImageRGB, cv2.COLOR_RGB2GRAY)
266 | self.__drawImage(self.outImageView, self.__outImageRGB)
267 |
268 | # 二值化
269 | def __toBinaryImage(self):
270 | # 先灰度化
271 | self.__toGrayImage()
272 | if self.__fileName:
273 | # 后阈值化为二值图
274 | _, self.__outImageRGB = cv2.threshold(self.__outImageRGB, 127, 255, cv2.THRESH_BINARY)
275 | self.__drawImage(self.outImageView, self.__outImageRGB)
276 |
277 | # 反转图片颜色
278 | def __reverseImage(self):
279 | if self.__fileName:
280 | self.__outImageRGB = cv2.bitwise_not(self.__outImageRGB)
281 | self.__drawImage(self.outImageView, self.__outImageRGB)
282 |
283 | # 执行打开属性调节子窗口(亮度、对比度、锐度、饱和度、色调、缩放、旋转)
284 | def __openPropertyWindow(self, propertyName, func):
285 | if self.__fileName:
286 | if self.__propertyWindow:
287 | self.__propertyWindow.close()
288 | self.__propertyWindow = PropertyWindow()
289 | # 设置窗口内容
290 | self.__propertyWindow.setWindowTitle(propertyName)
291 | self.__propertyWindow.propertyLabel.setText(propertyName)
292 | # 接收信号
293 | # 设置主窗口接收子窗口发送的信号的处理函数
294 | self.__propertyWindow.signal.connect(func)
295 | # 禁用主窗口菜单栏,子窗口置顶,且无法切换到主窗口
296 | self.__propertyWindow.setWindowFlags(QtCore.Qt.WindowStaysOnTopHint)
297 | self.__propertyWindow.setWindowModality(QtCore.Qt.ApplicationModal)
298 | # 显示子窗口
299 | self.__propertyWindow.show()
300 |
301 | # 亮度调节子窗口
302 | def __openLightWindow(self):
303 | self.__openPropertyWindow('亮度', self.__changeLight)
304 |
305 | # 对比度调节子窗口
306 | def __openContrastWindow(self):
307 | self.__openPropertyWindow('对比度', self.__changeContrast)
308 |
309 | # 锐度调节子窗口
310 | def __openSharpWindow(self):
311 | self.__openPropertyWindow('锐度', self.__changeSharp)
312 |
313 | # 饱和度调节子窗口
314 | def __openSaturationWindow(self):
315 | self.__openPropertyWindow('饱和度', self.__changeSaturation)
316 |
317 | # 色调调节子窗口
318 | def __openHueWindow(self):
319 | self.__openPropertyWindow('色调', self.__changeHue)
320 |
321 | # 预处理信号
322 | def __dealSignal(self, val):
323 | # 拷贝后修改副本
324 | __img = self.__outImageRGB.copy()
325 | # 如果是灰度图要转为RGB图
326 | if len(__img.shape) < 3:
327 | __img = cv2.cvtColor(__img, cv2.COLOR_GRAY2RGB)
328 |
329 | value = str(val)
330 | # 确认修改
331 | if value == 'ok':
332 | # 将暂存的修改保存为结果
333 | self.__outImageRGB = self.__tempImageRGB.copy()
334 | return None
335 | # 修改完成(确认已经做的修改或取消了修改)
336 | elif value == 'close':
337 | # 重绘修改预览
338 | self.__drawImage(self.outImageView, self.__outImageRGB)
339 | return None
340 | # 暂时修改
341 | else:
342 | return __img
343 |
344 | # 执行改变亮度或对比度
345 | # g(i,j)=αf(i,j)+(1-α)black+β,α用来调节对比度, β用来调节亮度
346 | def __lightAndContrast(self, img, alpha, beta):
347 | if len(img.shape) < 3:
348 | img = cv2.cvtColor(img, cv2.COLOR_GRAY2RGB)
349 | rows, cols, channels = img.shape
350 | # 新建全零(黑色)图片数组
351 | blank = numpy.zeros([rows, cols, channels], img.dtype)
352 | # 计算两个图像阵列的加权和
353 | img = cv2.addWeighted(img, alpha, blank, 1 - alpha, beta)
354 | # 显示修改数据
355 | self.__drawImage(self.outImageView, img)
356 | return img
357 |
358 | # 修改亮度
359 | def __changeLight(self, val):
360 | # 预处理接收到的信号
361 | __img = self.__dealSignal(val)
362 | # 如果修改了属性值
363 | # None的size是1 !!!
364 | if numpy.size(__img) > 1:
365 | beta = int(val) * (255 / 100)
366 | # 暂存本次修改
367 | self.__tempImageRGB = self.__lightAndContrast(__img, 1, beta)
368 |
369 | # 修改对比度
370 | def __changeContrast(self, val):
371 | # 预处理接收到的信号
372 | __img = self.__dealSignal(val)
373 | # 如果修改了属性值
374 | if numpy.size(__img) > 1:
375 | k = int(val)
376 | if k != -100:
377 | alpha = (k + 100) / 100
378 | else:
379 | alpha = 0.01
380 | # 暂存本次修改
381 | self.__tempImageRGB = self.__lightAndContrast(__img, alpha, 0)
382 |
383 | # 修改锐度
384 | def __changeSharp(self, val):
385 | # 预处理接收到的信号
386 | __img = self.__dealSignal(val)
387 | # 如果修改了属性值
388 | if numpy.size(__img) > 1:
389 | # 比例
390 | k = int(val) * 0.01
391 | if k != 0:
392 | # 卷积核(拉普拉斯算子)
393 | kernel = numpy.array([[-1, -1, -1], [-1, 9 + k, -1], [-1, -1, -1]])
394 | # 通过卷积实现锐化,暂存修改数据
395 | self.__tempImageRGB = cv2.filter2D(__img, -1, kernel)
396 | else:
397 | self.__tempImageRGB = self.__outImageRGB.copy()
398 | # 显示修改数据
399 | self.__drawImage(self.outImageView, self.__tempImageRGB)
400 |
401 | # 修改饱和度
402 | def __changeSaturation(self, val):
403 | # 预处理接收到的信号
404 | __img = self.__dealSignal(val)
405 | # 如果修改了属性值
406 | if numpy.size(__img) > 1:
407 | # 转换颜色空间到HLS
408 | __img = cv2.cvtColor(__img, cv2.COLOR_RGB2HLS)
409 | # 比例
410 | k = int(val) * (255 / 100)
411 | # 切片修改S分量,并限制色彩数值在0-255之间
412 | __img[:, :, 2] = numpy.clip(__img[:, :, 2] + k, 0, 255)
413 | # 暂存修改数据
414 | self.__tempImageRGB = cv2.cvtColor(__img, cv2.COLOR_HLS2RGB)
415 | # 显示修改数据
416 | self.__drawImage(self.outImageView, self.__tempImageRGB)
417 |
418 | # 修改色调
419 | # OpenCV中hue通道的取值范围是0 - 180
420 | def __changeHue(self, val):
421 | # 预处理接收到的信号
422 | __img = self.__dealSignal(val)
423 | # 如果修改了属性值
424 | if numpy.size(__img) > 1:
425 | # 转换颜色空间到HLS
426 | __img = cv2.cvtColor(__img, cv2.COLOR_RGB2HLS)
427 | # 比例
428 | k = int(val) * (90 / 100)
429 | # 切片修改H分量,并限制色彩数值在0-180之间
430 | __img[:, :, 0] = (__img[:, :, 0] + k) % 180
431 | # 暂存修改数据
432 | self.__tempImageRGB = cv2.cvtColor(__img, cv2.COLOR_HLS2RGB)
433 | # 显示修改数据
434 | self.__drawImage(self.outImageView, self.__tempImageRGB)
435 |
436 | # -----------------------------------图像运算-----------------------------------
437 | # 加、减、乘操作
438 | def __operation(self, func):
439 | if self.__fileName:
440 | __fileName, _ = QFileDialog.getOpenFileName(self, '选择图片', '.', 'Image Files(*.png *.jpeg *.jpg *.bmp)')
441 | if __fileName and os.path.exists(__fileName):
442 | # __bgrImg = cv2.imread(__fileName)
443 | __bgrImg = cv2.imdecode(np.fromfile(__fileName, dtype=np.uint8), -1)
444 | # 图片尺寸相同才能进行运算
445 | if self.__outImageRGB.shape == __bgrImg.shape:
446 | __rgbImg = cv2.cvtColor(__bgrImg, cv2.COLOR_BGR2RGB)
447 | self.__outImageRGB = func(self.__outImageRGB, __rgbImg)
448 | self.__drawImage(self.outImageView, self.__outImageRGB)
449 | else:
450 | QMessageBox.information(None, '提示', '图像尺寸不一致,无法进行操作!')
451 |
452 | # 加
453 | def __addImage(self):
454 | self.__operation(cv2.add)
455 |
456 | # 减
457 | def __subtractImage(self):
458 | self.__operation(cv2.subtract)
459 |
460 | # 乘
461 | def __multiplyImage(self):
462 | self.__operation(cv2.multiply)
463 |
464 | # 缩放调节子窗口
465 | def __openZoomWindow(self):
466 | self.__openPropertyWindow('缩放', self.__changeZoom)
467 |
468 | # 缩放
469 | def __changeZoom(self, val):
470 | # 预处理接收到的信号
471 | __img = self.__dealSignal(val)
472 | # 如果修改了属性值
473 | # None的size是1 !!! why???
474 | if numpy.size(__img) > 1:
475 | # 计算比例
476 | i = int(val)
477 | if i == -100:
478 | k = 0.01
479 | elif i >= 0:
480 | k = (i + 10) / 10
481 | else:
482 | k = (i + 100) / 100
483 | # 直接cv2.resize()缩放
484 | self.__tempImageRGB = cv2.resize(__img, None, fx=k, fy=k, interpolation=cv2.INTER_LINEAR)
485 | # 显示修改数据
486 | self.__drawImage(self.outImageView, self.__tempImageRGB)
487 |
488 | # 旋转调节子窗口
489 | def __openRotateWindow(self):
490 | self.__openPropertyWindow('旋转', self.__changeRotate)
491 | if self.__fileName:
492 | # 重设属性值取值范围
493 | self.__propertyWindow.slider.setMaximum(360)
494 | self.__propertyWindow.slider.setMinimum(-360)
495 | self.__propertyWindow.spinBox.setMaximum(360)
496 | self.__propertyWindow.spinBox.setMinimum(-360)
497 |
498 | # 旋转
499 | def __changeRotate(self, val):
500 | # 预处理接收到的信号
501 | __img = self.__dealSignal(val)
502 | # 如果修改了属性值
503 | # None的size是1 !!! why???
504 | if numpy.size(__img) > 1:
505 | # 比例
506 | k = int(val)
507 | (h, w) = __img.shape[:2]
508 | (cX, cY) = (w // 2, h // 2)
509 | # 绕图片中心旋转
510 | m = cv2.getRotationMatrix2D((cX, cY), k, 1.0)
511 | # 计算调整后的图片显示大小,使得图片不会被切掉边缘
512 | cos = numpy.abs(m[0, 0])
513 | sin = numpy.abs(m[0, 1])
514 | nW = int((h * sin) + (w * cos))
515 | nH = int((h * cos) + (w * sin))
516 | m[0, 2] += (nW / 2) - cX
517 | m[1, 2] += (nH / 2) - cY
518 | # 变换,并设置旋转调整后产生的无效区域为白色
519 | self.__tempImageRGB = __img = cv2.warpAffine(__img, m, (nW, nH), borderValue=(255, 255, 255))
520 | # 显示修改数据
521 | self.__drawImage(self.outImageView, self.__tempImageRGB)
522 |
523 | # -----------------------------------直方图均衡-----------------------------------
524 | # 归一化直方图
525 | def __histogram(self):
526 | if self.__fileName:
527 | # 如果是灰度图
528 | if len(self.__outImageRGB.shape) < 3:
529 | # __hist = cv2.calcHist([self.__outImageRGB], [0], None, [256], [0, 256])
530 | # __hist /= self.__outImageRGB.shape[0] * self.__outImageRGB.shape[1]
531 | # plt.plot(__hist)
532 | # 使用 matplotlib 的绘图功能同时绘制单通道的直方图
533 | # density的类型是 bool型,指定为True,则为频率直方图,反之为频数直方图
534 | plt.hist(self.__outImageRGB.ravel(), bins=255, rwidth=0.8, range=(0, 256), density=True)
535 | # 如果是RGB图
536 | else:
537 | color = {'r', 'g', 'b'}
538 | # 使用 matplotlib 的绘图功能同时绘制多通道 RGB 的直方图
539 | for i, col in enumerate(color):
540 | __hist = cv2.calcHist([self.__outImageRGB], [i], None, [256], [0, 256])
541 | __hist /= self.__outImageRGB.shape[0] * self.__outImageRGB.shape[1]
542 | plt.plot(__hist, color=col)
543 | # x轴长度区间
544 | plt.xlim([0, 256])
545 | # 显示直方图
546 | plt.show()
547 |
548 | # 直方图均衡化
549 | def __histogramEqualization(self):
550 | if self.__fileName:
551 | # 如果是灰度图
552 | if len(self.__outImageRGB.shape) < 3:
553 | self.__outImageRGB = cv2.equalizeHist(self.__outImageRGB)
554 | # 如果是RGB图
555 | else:
556 | # 分解通道,各自均衡化,再合并通道
557 | (r, g, b) = cv2.split(self.__outImageRGB)
558 | rh = cv2.equalizeHist(r)
559 | gh = cv2.equalizeHist(g)
560 | bh = cv2.equalizeHist(b)
561 | self.__outImageRGB = cv2.merge((rh, gh, bh))
562 | self.__drawImage(self.outImageView, self.__outImageRGB)
563 |
564 | # -----------------------------------噪声-----------------------------------
565 | # 加高斯噪声
566 | def __addGasussNoise(self):
567 | if self.__fileName:
568 | # 图片灰度标准化
569 | self.__outImageRGB = numpy.array(self.__outImageRGB / 255, dtype=float)
570 | # 产生高斯噪声
571 | noise = numpy.random.normal(0, 0.001 ** 0.5, self.__outImageRGB.shape)
572 | # 叠加图片和噪声
573 | out = cv2.add(self.__outImageRGB, noise)
574 | # 还原灰度并截取灰度区间
575 | self.__outImageRGB = numpy.clip(numpy.uint8(out * 255), 0, 255)
576 | self.__drawImage(self.outImageView, self.__outImageRGB)
577 |
578 | # 加均匀噪声
579 | def __addUniformNoise(self):
580 | if self.__fileName:
581 | # 起始范围
582 | low = 100
583 | # 终止范围
584 | height = 150
585 | # 搞一个与图片同规模数组
586 | out = numpy.zeros(self.__outImageRGB.shape, numpy.uint8)
587 | # 噪声生成比率
588 | ratio = 0.05
589 | # 遍历图片
590 | for i in range(self.__outImageRGB.shape[0]):
591 | for j in range(self.__outImageRGB.shape[1]):
592 | # 随机数[0.0,1.0)
593 | r = random.random()
594 | # 填充黑点
595 | if r < ratio:
596 | # 生成[low,height]的随机值
597 | out[i][j] = random.randint(low, height)
598 | # 填充白点
599 | elif r > 1 - ratio:
600 | out[i][j] = random.randint(low, height)
601 | # 填充原图
602 | else:
603 | out[i][j] = self.__outImageRGB[i][j]
604 | self.__outImageRGB = out.copy()
605 | self.__drawImage(self.outImageView, self.__outImageRGB)
606 |
607 | # 加脉冲噪声
608 | def __addImpulseNoise(self):
609 | if self.__fileName:
610 | # 搞一个与图片同规模数组
611 | out = numpy.zeros(self.__outImageRGB.shape, numpy.uint8)
612 | # 椒盐噪声生成比率
613 | ratio = 0.05
614 | # 遍历图片
615 | for i in range(self.__outImageRGB.shape[0]):
616 | for j in range(self.__outImageRGB.shape[1]):
617 | # 随机数[0.0,1.0)
618 | r = random.random()
619 | # 填充黑点
620 | if r < ratio:
621 | out[i][j] = 0
622 | # 填充白点
623 | elif r > 1 - ratio:
624 | out[i][j] = 255
625 | # 填充原图
626 | else:
627 | out[i][j] = self.__outImageRGB[i][j]
628 | self.__outImageRGB = out.copy()
629 | self.__drawImage(self.outImageView, self.__outImageRGB)
630 |
631 | # -----------------------------------空域滤波-----------------------------------
632 | # 均值滤波
633 | def __meanValueFilter(self):
634 | if self.__fileName:
635 | # 直接调库
636 | self.__outImageRGB = cv2.blur(self.__outImageRGB, (5, 5))
637 | self.__drawImage(self.outImageView, self.__outImageRGB)
638 |
639 | # 中值滤波
640 | def __medianValueFilter(self):
641 | if self.__fileName:
642 | # 直接调库
643 | self.__outImageRGB = cv2.medianBlur(self.__outImageRGB, 5)
644 | self.__drawImage(self.outImageView, self.__outImageRGB)
645 |
646 | # Sobel算子锐化
647 | def __sobel(self):
648 | if self.__fileName:
649 | # 直接调库
650 | self.__outImageRGB = cv2.Sobel(self.__outImageRGB, -1, 1, 1, 3)
651 | self.__drawImage(self.outImageView, self.__outImageRGB)
652 |
653 | # Prewitt算子锐化
654 | def __prewitt(self):
655 | if self.__fileName:
656 | # Prewitt 算子
657 | kernelx = numpy.array([[1, 1, 1], [0, 0, 0], [-1, -1, -1]], dtype=int)
658 | kernely = numpy.array([[-1, 0, 1], [-1, 0, 1], [-1, 0, 1]], dtype=int)
659 | # 通过自定义卷积核实现卷积
660 | imgx = cv2.filter2D(self.__outImageRGB, -1, kernelx)
661 | imgy = cv2.filter2D(self.__outImageRGB, -1, kernely)
662 | # 合并
663 | self.__outImageRGB = cv2.add(imgx, imgy)
664 | self.__drawImage(self.outImageView, self.__outImageRGB)
665 |
666 | # 拉普拉斯算子锐化
667 | def __laplacian(self):
668 | if self.__fileName:
669 | # 直接调库
670 | self.__outImageRGB = cv2.Laplacian(self.__outImageRGB, -1, ksize=3)
671 | self.__drawImage(self.outImageView, self.__outImageRGB)
672 |
673 | # -----------------------------------关于-----------------------------------
674 | # 关于作者
675 | def __aboutAuthor(self):
676 | QMessageBox.information(None, '关于作者', '图像处理软件2.1\n\nCopyright © 2021–2099 赵相欣\n\n保留一切权利')
677 |
678 |
679 | if __name__ == '__main__':
680 | app = QApplication(sys.argv)
681 | mainWindow = MainWindow()
682 | mainWindow.show()
683 | sys.exit(app.exec())
684 |
--------------------------------------------------------------------------------