|
|
|
|
import sys
|
|
|
|
|
from PyQt5 import QtCore, QtGui, QtWidgets
|
|
|
|
|
from PyQt5.QtWidgets import QFileDialog, QGraphicsScene, QGraphicsPixmapItem
|
|
|
|
|
import cv2
|
|
|
|
|
import numpy as np
|
|
|
|
|
from PyQt5.QtGui import QImage, QPixmap
|
|
|
|
|
from mainwindow_ui import Ui_Image_processing
|
|
|
|
|
import vessel_detection
|
|
|
|
|
import os
|
|
|
|
|
import io
|
|
|
|
|
from PIL import Image
|
|
|
|
|
from PIL import ImageFile
|
|
|
|
|
|
|
|
|
|
class MainWindow(QtWidgets.QMainWindow, Ui_Image_processing):
|
|
|
|
|
def __init__(self, parent=None):
|
|
|
|
|
super(MainWindow, self).__init__(parent)
|
|
|
|
|
self.setupUi(self)
|
|
|
|
|
self.original_image = None
|
|
|
|
|
self.modified_image = None
|
|
|
|
|
self.image_history = []
|
|
|
|
|
|
|
|
|
|
# Connect buttons to functions
|
|
|
|
|
#图片存取
|
|
|
|
|
self.pushButton_open_image.clicked.connect(self.open_image)
|
|
|
|
|
self.pushButton_undo_image.clicked.connect(self.undo_last_action)
|
|
|
|
|
self.pushButton_close_image.clicked.connect(self.close_image)
|
|
|
|
|
self.pushButton_save_image.clicked.connect(self.save_image)
|
|
|
|
|
self.pushButton_reset_image.clicked.connect(self.reset_image)
|
|
|
|
|
# 基础功能
|
|
|
|
|
self.pushButton_symmetry.clicked.connect(self.symmetry)
|
|
|
|
|
self.pushButton_rotate.clicked.connect(self.rotate)
|
|
|
|
|
self.pushButton_to_grayscale.clicked.connect(self.to_grayscale)
|
|
|
|
|
self.pushButton_invert_colors.clicked.connect(self.invert_colors)
|
|
|
|
|
self.size_controller.valueChanged.connect(self.change_size)
|
|
|
|
|
#算术运算
|
|
|
|
|
self.pushButton_add.clicked.connect(self.image_add)
|
|
|
|
|
self.pushButton_minus.clicked.connect(self.image_subtract)
|
|
|
|
|
self.pushButton_multi.clicked.connect(self.image_multiply)
|
|
|
|
|
self.pushButton_complex_change.clicked.connect(self.complex_change)
|
|
|
|
|
#二值化
|
|
|
|
|
self.pushButton_to_binary.clicked.connect(self.to_binary)
|
|
|
|
|
#hough线条预测
|
|
|
|
|
self.pushButton_hough.clicked.connect(self.hough)
|
|
|
|
|
self.pushButton_hist_equ.clicked.connect(self.equalize_histogram)
|
|
|
|
|
self.pushButton_brightness_change.clicked.connect(self.brightness_change)
|
|
|
|
|
self.pushButton_hist_equ_2.clicked.connect(self.histogram_normalization)
|
|
|
|
|
#空间滤波器
|
|
|
|
|
self.pushButton_mean_filter.clicked.connect(self.mean_filter)
|
|
|
|
|
self.pushButton_gaussian_filter.clicked.connect(self.gaussian_filter)
|
|
|
|
|
self.pushButton_median_filter.clicked.connect(self.median_filter)
|
|
|
|
|
#频域滤波
|
|
|
|
|
self.pushButton_band_stop_filter.clicked.connect(self.band_stop_filter)
|
|
|
|
|
self.pushButton_band_pass_filter.clicked.connect(self.band_pass_filter)
|
|
|
|
|
self.pushButton_ideal_high_pass_filter.clicked.connect(self.ideal_high_pass_filter)
|
|
|
|
|
self.pushButton_ideal_low_pass_filter.clicked.connect(self.ideal_low_pass_filter)
|
|
|
|
|
self.pushButton_butterworth_high_pass_filter.clicked.connect(self.butterworth_high_pass_filter)
|
|
|
|
|
self.pushButton_butterworth_low_pass_filter.clicked.connect(self.butterworth_low_pass_filter)
|
|
|
|
|
self.pushButton_gaussian_high_pass_filter.clicked.connect(self.gaussian_high_pass_filter)
|
|
|
|
|
self.pushButton_gaussian_low_pass_filter.clicked.connect(self.gaussian_low_pass_filter)
|
|
|
|
|
#添加噪声
|
|
|
|
|
self.pushButton_add_gaussian_noise.clicked.connect(self.add_gaussian_noise)
|
|
|
|
|
self.pushButton_add_poisson_noise.clicked.connect(self.add_poisson_noise)
|
|
|
|
|
self.pushButton_add_salt_and_pepper_noise.clicked.connect(self.add_salt_and_pepper_noise)
|
|
|
|
|
#形态学操作
|
|
|
|
|
self.pushButton_apply_erosion.clicked.connect(self.apply_erosion)
|
|
|
|
|
self.pushButton_apply_dilation.clicked.connect(self.apply_dilation)
|
|
|
|
|
#边缘检测
|
|
|
|
|
self.pushButton_log_edge_detection.clicked.connect(self.log_edge_detection)
|
|
|
|
|
self.pushButton_roberts_edge_detection.clicked.connect(self.roberts_edge_detection)
|
|
|
|
|
self.pushButton_sobel_edge_detection.clicked.connect(self.sobel_edge_detection)
|
|
|
|
|
self.pushButton_laplacian_edge_detection.clicked.connect(self.laplacian_edge_detection)
|
|
|
|
|
self.pushButton_canny_edge_detection.clicked.connect(self.canny_edge_detection)
|
|
|
|
|
#锐化
|
|
|
|
|
self.pushButton_apply_roberts.clicked.connect(self.apply_roberts)
|
|
|
|
|
self.pushButton_apply_sobel.clicked.connect(self.apply_sobel)
|
|
|
|
|
self.pushButton_apply_prewitt.clicked.connect(self.apply_prewitt)
|
|
|
|
|
self.pushButton_sobel_edge_detection_5.clicked.connect(self.apply_laplacian)
|
|
|
|
|
#实际应用
|
|
|
|
|
self.pushButton_vessel_division.clicked.connect(self.vessel_division)
|
|
|
|
|
self.pushButton_repair_image.clicked.connect(self.repair_image)
|
|
|
|
|
self.pushButton_compress_image.clicked.connect(self.compress_image)
|
|
|
|
|
#测试专用
|
|
|
|
|
self.pushButton_test.clicked.connect(self.repair_image)
|
|
|
|
|
|
|
|
|
|
def open_image(self):
|
|
|
|
|
options = QFileDialog.Options()
|
|
|
|
|
fileName, _ = QFileDialog.getOpenFileName(self, "打开图片", "", "All Files (*);;Image Files (*.jpg *.png)", options=options)
|
|
|
|
|
if fileName:
|
|
|
|
|
self.original_image = cv2.imread(fileName)
|
|
|
|
|
self.modified_image = self.original_image.copy()
|
|
|
|
|
self.image_history.clear() # Clear history when a new image is opened
|
|
|
|
|
self.display_image(self.original_image, self.graphicsView)
|
|
|
|
|
self.display_image(self.modified_image, self.graphicsView_2)
|
|
|
|
|
|
|
|
|
|
def close_image(self):
|
|
|
|
|
self.original_image = None
|
|
|
|
|
self.modified_image = None
|
|
|
|
|
self.image_history.clear() # Clear history when image is closed
|
|
|
|
|
self.graphicsView.scene().clear()
|
|
|
|
|
self.graphicsView_2.scene().clear()
|
|
|
|
|
|
|
|
|
|
def save_image(self):
|
|
|
|
|
if self.modified_image is not None:
|
|
|
|
|
options = QFileDialog.Options()
|
|
|
|
|
fileName, _ = QFileDialog.getSaveFileName(self, "保存图片", "", "Image Files (*.jpg *.png)", options=options)
|
|
|
|
|
if fileName:
|
|
|
|
|
cv2.imwrite(fileName, self.modified_image)
|
|
|
|
|
|
|
|
|
|
def reset_image(self):
|
|
|
|
|
if self.original_image is not None:
|
|
|
|
|
self.modified_image = self.original_image.copy()
|
|
|
|
|
self.image_history.clear() # Clear history when image is reset
|
|
|
|
|
self.display_image(self.modified_image, self.graphicsView_2)
|
|
|
|
|
|
|
|
|
|
def add_to_history(self):
|
|
|
|
|
if self.modified_image is not None:
|
|
|
|
|
self.image_history.append(self.modified_image.copy())
|
|
|
|
|
|
|
|
|
|
def undo_last_action(self):
|
|
|
|
|
if self.image_history:
|
|
|
|
|
self.modified_image = self.image_history.pop()
|
|
|
|
|
self.display_image(self.modified_image, self.graphicsView_2)
|
|
|
|
|
|
|
|
|
|
def symmetry(self):
|
|
|
|
|
if self.modified_image is not None:
|
|
|
|
|
self.add_to_history()
|
|
|
|
|
self.modified_image = cv2.flip(self.modified_image, 1) # Horizontal flip
|
|
|
|
|
self.display_image(self.modified_image, self.graphicsView_2)
|
|
|
|
|
|
|
|
|
|
def rotate(self):
|
|
|
|
|
if self.modified_image is not None:
|
|
|
|
|
self.add_to_history()
|
|
|
|
|
self.modified_image = cv2.rotate(self.modified_image, cv2.ROTATE_90_CLOCKWISE) # Rotate 90 degrees clockwise
|
|
|
|
|
self.display_image(self.modified_image, self.graphicsView_2)
|
|
|
|
|
|
|
|
|
|
def to_grayscale(self):
|
|
|
|
|
if self.modified_image is not None:
|
|
|
|
|
self.add_to_history()
|
|
|
|
|
if len(self.modified_image.shape) == 3: # Image has 3 channels (BGR)
|
|
|
|
|
self.modified_image = cv2.cvtColor(self.modified_image, cv2.COLOR_BGR2GRAY)
|
|
|
|
|
self.display_image(self.modified_image, self.graphicsView_2)
|
|
|
|
|
|
|
|
|
|
def to_binary(self):
|
|
|
|
|
if self.modified_image is not None:
|
|
|
|
|
self.add_to_history()
|
|
|
|
|
if len(self.original_image.shape) == 3: # Image has 3 channels (BGR)
|
|
|
|
|
gray_image = cv2.cvtColor(self.modified_image, cv2.COLOR_BGR2GRAY)
|
|
|
|
|
else:
|
|
|
|
|
gray_image = self.modified_image
|
|
|
|
|
|
|
|
|
|
unique_values = set(gray_image.flatten())
|
|
|
|
|
if unique_values.issubset({0, 255}): # Image is already binary
|
|
|
|
|
self.modified_image = gray_image
|
|
|
|
|
else:
|
|
|
|
|
_, self.modified_image = cv2.threshold(gray_image, self.spinBox_threshold_for_to_binary.value(), 255, cv2.THRESH_BINARY)
|
|
|
|
|
|
|
|
|
|
self.display_image(self.modified_image, self.graphicsView_2)
|
|
|
|
|
|
|
|
|
|
def image_add(self):
|
|
|
|
|
if self.modified_image is not None:
|
|
|
|
|
self.add_to_history()
|
|
|
|
|
options = QFileDialog.Options()
|
|
|
|
|
fileName, _ = QFileDialog.getOpenFileName(self, "打开图片", "", "All Files (*);;Image Files (*.jpg *.png)", options=options)
|
|
|
|
|
if fileName:
|
|
|
|
|
obj_image = cv2.imread(fileName)
|
|
|
|
|
|
|
|
|
|
# 获取原始图像的尺寸
|
|
|
|
|
original_shape = self.original_image.shape
|
|
|
|
|
original_height, original_width = original_shape[:2]
|
|
|
|
|
|
|
|
|
|
# 调整新图像的尺寸与原始图像相同
|
|
|
|
|
resized_obj_image = cv2.resize(obj_image, (original_width, original_height))
|
|
|
|
|
|
|
|
|
|
# 确保通道数匹配,如果不匹配,进行相应调整
|
|
|
|
|
if len(original_shape) == 2 and len(resized_obj_image.shape) == 3:
|
|
|
|
|
resized_obj_image = cv2.cvtColor(resized_obj_image, cv2.COLOR_BGR2GRAY)
|
|
|
|
|
elif len(original_shape) == 3 and len(resized_obj_image.shape) == 2:
|
|
|
|
|
resized_obj_image = cv2.cvtColor(resized_obj_image, cv2.COLOR_GRAY2BGR)
|
|
|
|
|
|
|
|
|
|
# 执行图像加法操作
|
|
|
|
|
self.modified_image = cv2.add(self.original_image, resized_obj_image)
|
|
|
|
|
|
|
|
|
|
# 显示结果图像
|
|
|
|
|
self.display_image(self.modified_image, self.graphicsView_2)
|
|
|
|
|
|
|
|
|
|
def image_subtract(self):
|
|
|
|
|
if self.modified_image is not None:
|
|
|
|
|
self.add_to_history()
|
|
|
|
|
options = QFileDialog.Options()
|
|
|
|
|
fileName, _ = QFileDialog.getOpenFileName(self, "打开图片", "", "All Files (*);;Image Files (*.jpg *.png)", options=options)
|
|
|
|
|
if fileName:
|
|
|
|
|
obj_image = cv2.imread(fileName)
|
|
|
|
|
|
|
|
|
|
# 获取原始图像的尺寸
|
|
|
|
|
original_shape = self.original_image.shape
|
|
|
|
|
original_height, original_width = original_shape[:2]
|
|
|
|
|
|
|
|
|
|
# 调整新图像的尺寸与原始图像相同
|
|
|
|
|
resized_obj_image = cv2.resize(obj_image, (original_width, original_height))
|
|
|
|
|
|
|
|
|
|
# 确保通道数匹配,如果不匹配,进行相应调整
|
|
|
|
|
if len(original_shape) == 2 and len(resized_obj_image.shape) == 3:
|
|
|
|
|
resized_obj_image = cv2.cvtColor(resized_obj_image, cv2.COLOR_BGR2GRAY)
|
|
|
|
|
elif len(original_shape) == 3 and len(resized_obj_image.shape) == 2:
|
|
|
|
|
resized_obj_image = cv2.cvtColor(resized_obj_image, cv2.COLOR_GRAY2BGR)
|
|
|
|
|
|
|
|
|
|
# 执行图像加法操作
|
|
|
|
|
self.modified_image = cv2.subtract(self.original_image, resized_obj_image)
|
|
|
|
|
|
|
|
|
|
# 显示结果图像
|
|
|
|
|
self.display_image(self.modified_image, self.graphicsView_2)
|
|
|
|
|
|
|
|
|
|
def image_multiply(self):
|
|
|
|
|
if self.modified_image is not None:
|
|
|
|
|
self.add_to_history()
|
|
|
|
|
options = QFileDialog.Options()
|
|
|
|
|
fileName, _ = QFileDialog.getOpenFileName(self, "打开图片", "", "All Files (*);;Image Files (*.jpg *.png)", options=options)
|
|
|
|
|
if fileName:
|
|
|
|
|
obj_image = cv2.imread(fileName)
|
|
|
|
|
|
|
|
|
|
# 获取原始图像的尺寸
|
|
|
|
|
original_shape = self.original_image.shape
|
|
|
|
|
original_height, original_width = original_shape[:2]
|
|
|
|
|
|
|
|
|
|
# 调整新图像的尺寸与原始图像相同
|
|
|
|
|
resized_obj_image = cv2.resize(obj_image, (original_width, original_height))
|
|
|
|
|
|
|
|
|
|
# 确保通道数匹配,如果不匹配,进行相应调整
|
|
|
|
|
if len(original_shape) == 2 and len(resized_obj_image.shape) == 3:
|
|
|
|
|
resized_obj_image = cv2.cvtColor(resized_obj_image, cv2.COLOR_BGR2GRAY)
|
|
|
|
|
elif len(original_shape) == 3 and len(resized_obj_image.shape) == 2:
|
|
|
|
|
resized_obj_image = cv2.cvtColor(resized_obj_image, cv2.COLOR_GRAY2BGR)
|
|
|
|
|
|
|
|
|
|
# 执行图像加法操作
|
|
|
|
|
self.modified_image = cv2.multiply(self.original_image, resized_obj_image)
|
|
|
|
|
|
|
|
|
|
# 显示结果图像
|
|
|
|
|
self.display_image(self.modified_image, self.graphicsView_2)
|
|
|
|
|
|
|
|
|
|
def complex_change(self):
|
|
|
|
|
if self.modified_image is not None:
|
|
|
|
|
self.add_to_history()
|
|
|
|
|
|
|
|
|
|
# 图像放缩
|
|
|
|
|
self.modified_image = cv2.resize(self.original_image, (256, 256))
|
|
|
|
|
# 获取图像shape
|
|
|
|
|
rows, cols = self.modified_image.shape[: 2]
|
|
|
|
|
|
|
|
|
|
########Begin########
|
|
|
|
|
# 设置图像仿射变化矩阵
|
|
|
|
|
|
|
|
|
|
post1 = np.float32([[50, 50], [200, 50], [50, 200]])
|
|
|
|
|
post2 = np.float32([[10, 100], [200, 50], [100,250]])
|
|
|
|
|
M = cv2.getAffineTransform(post1, post2)
|
|
|
|
|
|
|
|
|
|
# 图像仿射变换,及保存
|
|
|
|
|
self.modified_image = cv2.warpAffine(self.modified_image, M, (rows, cols))
|
|
|
|
|
self.display_image(self.modified_image, self.graphicsView_2)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def change_size(self):
|
|
|
|
|
def resize_image(image, scale):
|
|
|
|
|
original_height, original_width = image.shape[:2]
|
|
|
|
|
|
|
|
|
|
if scale > 1:
|
|
|
|
|
# 截取中心区域
|
|
|
|
|
new_width = int(original_width / scale)
|
|
|
|
|
new_height = int(original_height / scale)
|
|
|
|
|
start_x = (original_width - new_width) // 2
|
|
|
|
|
start_y = (original_height - new_height) // 2
|
|
|
|
|
cropped_image = image[start_y:start_y + new_height, start_x:start_x + new_width]
|
|
|
|
|
resized_image = cv2.resize(cropped_image, (original_width, original_height), interpolation=cv2.INTER_LINEAR)
|
|
|
|
|
elif scale < 1:
|
|
|
|
|
# 填充白边
|
|
|
|
|
new_width = int(original_width * scale)
|
|
|
|
|
new_height = int(original_height * scale)
|
|
|
|
|
resized_image = cv2.resize(image, (new_width, new_height), interpolation=cv2.INTER_LINEAR)
|
|
|
|
|
|
|
|
|
|
# 创建带有白边的新图像
|
|
|
|
|
padded_image = np.ones((original_height, original_width, 3), dtype=np.uint8) * 255
|
|
|
|
|
start_x = (original_width - new_width) // 2
|
|
|
|
|
start_y = (original_height - new_height) // 2
|
|
|
|
|
padded_image[start_y:start_y + new_height, start_x:start_x + new_width] = resized_image
|
|
|
|
|
resized_image = padded_image
|
|
|
|
|
else:
|
|
|
|
|
resized_image = image.copy()
|
|
|
|
|
|
|
|
|
|
return resized_image
|
|
|
|
|
|
|
|
|
|
if self.modified_image is not None:
|
|
|
|
|
self.add_to_history()
|
|
|
|
|
|
|
|
|
|
# Get the factor from the slider value
|
|
|
|
|
factor = self.size_controller.value() / 10.0
|
|
|
|
|
|
|
|
|
|
if self.radioButton_bigger.isChecked():
|
|
|
|
|
changed=resize_image(self.modified_image,factor)
|
|
|
|
|
else:
|
|
|
|
|
changed=resize_image(self.modified_image,1/factor)
|
|
|
|
|
|
|
|
|
|
self.display_image(changed, self.graphicsView_2)
|
|
|
|
|
|
|
|
|
|
def hough(self):
|
|
|
|
|
if self.modified_image is not None:
|
|
|
|
|
self.add_to_history()
|
|
|
|
|
|
|
|
|
|
self.modified_image = cv2.GaussianBlur(self.modified_image, (3, 3), 0)
|
|
|
|
|
edges = cv2.Canny(self.modified_image, 50, 150, apertureSize=3)
|
|
|
|
|
|
|
|
|
|
minLineLength = 200
|
|
|
|
|
maxLineGap = 15
|
|
|
|
|
|
|
|
|
|
linesP = cv2.HoughLinesP(edges, 1, np.pi/180, 80, minLineLength, maxLineGap)
|
|
|
|
|
self.modified_image = self.modified_image.copy()
|
|
|
|
|
for i_P in linesP:
|
|
|
|
|
for x1, y1, x2, y2 in i_P:
|
|
|
|
|
cv2.line(self.modified_image, (x1, y1), (x2, y2), (0, 255, 0), 3)
|
|
|
|
|
self.display_image(self.modified_image, self.graphicsView_2)
|
|
|
|
|
|
|
|
|
|
def equalize_histogram(self):
|
|
|
|
|
if self.modified_image is not None:
|
|
|
|
|
self.add_to_history()
|
|
|
|
|
#判断是不是灰度图
|
|
|
|
|
if len(self.modified_image.shape) == 3:
|
|
|
|
|
ycrcb_img = cv2.cvtColor(self.modified_image, cv2.COLOR_BGR2YCrCb)
|
|
|
|
|
|
|
|
|
|
# 对Y通道进行直方图均衡化
|
|
|
|
|
ycrcb_img[:, :, 0] = cv2.equalizeHist(ycrcb_img[:, :, 0])
|
|
|
|
|
|
|
|
|
|
# 将图像转换回BGR颜色空间
|
|
|
|
|
self.modified_image = cv2.cvtColor(ycrcb_img, cv2.COLOR_YCrCb2BGR)
|
|
|
|
|
else: # 是灰度图
|
|
|
|
|
self.modified_image = cv2.equalizeHist(self.modified_image)
|
|
|
|
|
self.display_image(self.modified_image, self.graphicsView_2)
|
|
|
|
|
|
|
|
|
|
def brightness_change(self):
|
|
|
|
|
if self.modified_image is not None:
|
|
|
|
|
self.add_to_history()
|
|
|
|
|
self.modified_image=cv2.convertScaleAbs(self.modified_image, alpha=1, beta=self.spinBox_threshold_for_brightness_change.value())
|
|
|
|
|
self.display_image(self.modified_image, self.graphicsView_2)
|
|
|
|
|
|
|
|
|
|
def histogram_normalization(self):
|
|
|
|
|
if self.modified_image is not None:
|
|
|
|
|
self.add_to_history()
|
|
|
|
|
|
|
|
|
|
options = QFileDialog.Options()
|
|
|
|
|
fileName, _ = QFileDialog.getOpenFileName(self, "打开图片", "", "All Files (*);;Image Files (*.jpg *.png)", options=options)
|
|
|
|
|
if fileName:
|
|
|
|
|
self.modified_image=cv2.imread(fileName)
|
|
|
|
|
img=self.original_image.copy()
|
|
|
|
|
scr=self.modified_image.copy()
|
|
|
|
|
mHist1=[]
|
|
|
|
|
mNum1=[]
|
|
|
|
|
inhist1=[]
|
|
|
|
|
mHist2=[]
|
|
|
|
|
mNum2=[]
|
|
|
|
|
inhist2=[]
|
|
|
|
|
########Begin########
|
|
|
|
|
# 对原图像进行均衡化
|
|
|
|
|
for i in range(256):
|
|
|
|
|
mHist1.append(0)
|
|
|
|
|
# 获取原图像像素点的宽度和高度
|
|
|
|
|
row, col = img.shape
|
|
|
|
|
for i in range(row):
|
|
|
|
|
for j in range(col):
|
|
|
|
|
mHist1[img[i, j]] = mHist1[img[i, j]] + 1 # 统计灰度值的个数
|
|
|
|
|
mNum1.append(mHist1[0] / img.size)
|
|
|
|
|
for i in range(0, 255):
|
|
|
|
|
mNum1.append(mNum1[i] + mHist1[i + 1] / img.size)
|
|
|
|
|
for i in range(256):
|
|
|
|
|
inhist1.append(round(255 * mNum1[i]))
|
|
|
|
|
# 对目标图像进行均衡化
|
|
|
|
|
for i in range(256):
|
|
|
|
|
mHist2.append(0)
|
|
|
|
|
rows, cols = scr.shape # 获取目标图像像素点的宽度和高度
|
|
|
|
|
for i in range(rows):
|
|
|
|
|
for j in range(cols):
|
|
|
|
|
mHist2[scr[i, j]] = mHist2[scr[i, j]] + 1 # 统计灰度值的个数
|
|
|
|
|
mNum2.append(mHist2[0] / scr.size)
|
|
|
|
|
for i in range(0, 255):
|
|
|
|
|
mNum2.append(mNum2[i] + mHist2[i + 1] / scr.size)
|
|
|
|
|
for i in range(256):
|
|
|
|
|
inhist2.append(round(255 * mNum2[i]))
|
|
|
|
|
# 进行规定化
|
|
|
|
|
# 用于放入规定化后的图片像素
|
|
|
|
|
g = []
|
|
|
|
|
for i in range(256):
|
|
|
|
|
a = inhist1[i]
|
|
|
|
|
flag = True
|
|
|
|
|
for j in range(256):
|
|
|
|
|
if inhist2[j] == a:
|
|
|
|
|
g.append(j)
|
|
|
|
|
flag = False
|
|
|
|
|
break
|
|
|
|
|
if flag == True:
|
|
|
|
|
minp = 255
|
|
|
|
|
for j in range(256):
|
|
|
|
|
b = abs(inhist2[j] - a)
|
|
|
|
|
if b < minp:
|
|
|
|
|
minp = b
|
|
|
|
|
jmin = j
|
|
|
|
|
g.append(jmin)
|
|
|
|
|
for i in range(row):
|
|
|
|
|
for j in range(col):
|
|
|
|
|
img[i, j] = g[img[i, j]]
|
|
|
|
|
self.modified_image=img
|
|
|
|
|
self.display_image(self.modified_image, self.graphicsView_2)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def invert_colors(self):
|
|
|
|
|
if self.modified_image is not None:
|
|
|
|
|
self.add_to_history()
|
|
|
|
|
self.modified_image = cv2.bitwise_not(self.modified_image)
|
|
|
|
|
self.display_image(self.modified_image, self.graphicsView_2)
|
|
|
|
|
|
|
|
|
|
def display_image(self, image, graphicsView):
|
|
|
|
|
if len(image.shape) == 2:
|
|
|
|
|
height, width = image.shape
|
|
|
|
|
bytesPerLine = width
|
|
|
|
|
qImg = QImage(image.data.tobytes(), width, height, bytesPerLine, QImage.Format_Grayscale8)
|
|
|
|
|
else:
|
|
|
|
|
height, width, channel = image.shape
|
|
|
|
|
bytesPerLine = 3 * width
|
|
|
|
|
qImg = QImage(image.data.tobytes(), width, height, bytesPerLine, QImage.Format_RGB888).rgbSwapped()
|
|
|
|
|
pixmap = QPixmap.fromImage(qImg)
|
|
|
|
|
scene = QGraphicsScene()
|
|
|
|
|
scene.addItem(QGraphicsPixmapItem(pixmap))
|
|
|
|
|
graphicsView.setScene(scene)
|
|
|
|
|
graphicsView.fitInView(scene.itemsBoundingRect(), QtCore.Qt.KeepAspectRatio)
|
|
|
|
|
|
|
|
|
|
def mean_filter(self):
|
|
|
|
|
if self.modified_image is not None:
|
|
|
|
|
self.add_to_history()
|
|
|
|
|
kernel=self.spinBox_kernel_for_mean_filter.value()
|
|
|
|
|
self.modified_image = cv2.blur(self.modified_image, (kernel, kernel))
|
|
|
|
|
self.display_image(self.modified_image, self.graphicsView_2)
|
|
|
|
|
|
|
|
|
|
def gaussian_filter(self):
|
|
|
|
|
if self.modified_image is not None:
|
|
|
|
|
self.add_to_history()
|
|
|
|
|
kernel=self.spinBox_kernel_for_gaussian_filter.value()
|
|
|
|
|
self.modified_image = cv2.GaussianBlur(self.modified_image, (kernel,kernel), 0)
|
|
|
|
|
self.display_image(self.modified_image, self.graphicsView_2)
|
|
|
|
|
|
|
|
|
|
def median_filter(self):
|
|
|
|
|
if self.modified_image is not None:
|
|
|
|
|
self.add_to_history()
|
|
|
|
|
kernel=self.spinBox_kernel_for_median_filter.value()
|
|
|
|
|
self.modified_image = cv2.medianBlur(self.modified_image, kernel)
|
|
|
|
|
self.display_image(self.modified_image, self.graphicsView_2)
|
|
|
|
|
|
|
|
|
|
def low_pass_filter(self):
|
|
|
|
|
if self.modified_image is not None:
|
|
|
|
|
self.add_to_history()
|
|
|
|
|
kernel = np.array([[-1, -1, -1],
|
|
|
|
|
[-1, 8, -1],
|
|
|
|
|
[-1, -1, -1]])
|
|
|
|
|
|
|
|
|
|
# 使用 cv2.filter2D() 进行高通滤波
|
|
|
|
|
self.modified_image = cv2.filter2D(self.modified_image, -1, kernel)
|
|
|
|
|
self.display_image(self.modified_image, self.graphicsView_2)
|
|
|
|
|
|
|
|
|
|
def high_pass_filter(self):
|
|
|
|
|
if self.modified_image is not None:
|
|
|
|
|
self.add_to_history()
|
|
|
|
|
self.modified_image= cv2.blur(self.modified_image, (5, 5))
|
|
|
|
|
self.display_image(self.modified_image, self.graphicsView_2)
|
|
|
|
|
|
|
|
|
|
def band_pass_filter(self):
|
|
|
|
|
if self.modified_image is not None:
|
|
|
|
|
self.add_to_history()
|
|
|
|
|
# 将图像转换为灰度图像
|
|
|
|
|
gray_image = cv2.cvtColor(self.original_image, cv2.COLOR_BGR2GRAY) if len(self.original_image.shape) == 3 else self.original_image
|
|
|
|
|
rows, cols = gray_image.shape
|
|
|
|
|
crow, ccol = rows // 2, cols // 2
|
|
|
|
|
|
|
|
|
|
dft = cv2.dft(np.float32(gray_image), flags=cv2.DFT_COMPLEX_OUTPUT)
|
|
|
|
|
dft_shift = np.fft.fftshift(dft)
|
|
|
|
|
|
|
|
|
|
low_thresh=self.spinBox_min_for_band_pass_filter.value()
|
|
|
|
|
high_thresh=self.spinBox_max_for_band_pass_filter.value()
|
|
|
|
|
|
|
|
|
|
mask = np.zeros((rows, cols, 2), np.float32)
|
|
|
|
|
for u in range(rows):
|
|
|
|
|
for v in range(cols):
|
|
|
|
|
d = np.sqrt((u - crow)**2 + (v - ccol)**2)
|
|
|
|
|
if low_thresh < d < high_thresh:
|
|
|
|
|
mask[u, v] = 1
|
|
|
|
|
|
|
|
|
|
fshift = dft_shift * mask
|
|
|
|
|
f_ishift = np.fft.ifftshift(fshift)
|
|
|
|
|
img_back = cv2.idft(f_ishift)
|
|
|
|
|
self.modified_image = cv2.magnitude(img_back[:, :, 0], img_back[:, :, 1])
|
|
|
|
|
|
|
|
|
|
self.display_image(self.modified_image, self.graphicsView_2)
|
|
|
|
|
|
|
|
|
|
def band_stop_filter(self):
|
|
|
|
|
if self.modified_image is not None:
|
|
|
|
|
self.add_to_history()
|
|
|
|
|
# 将图像转换为灰度图像
|
|
|
|
|
gray_image = cv2.cvtColor(self.original_image, cv2.COLOR_BGR2GRAY) if len(self.original_image.shape) == 3 else self.original_image
|
|
|
|
|
rows, cols = gray_image.shape
|
|
|
|
|
crow, ccol = rows // 2, cols // 2
|
|
|
|
|
|
|
|
|
|
dft = cv2.dft(np.float32(gray_image), flags=cv2.DFT_COMPLEX_OUTPUT)
|
|
|
|
|
dft_shift = np.fft.fftshift(dft)
|
|
|
|
|
|
|
|
|
|
low_thresh=self.spinBox_min_for_band_stop_filter.value()
|
|
|
|
|
high_thresh=self.spinBox_max_for_band_stop_filter.value()
|
|
|
|
|
|
|
|
|
|
mask = np.ones((rows, cols, 2), np.float32)
|
|
|
|
|
for u in range(rows):
|
|
|
|
|
for v in range(cols):
|
|
|
|
|
d = np.sqrt((u - crow)**2 + (v - ccol)**2)
|
|
|
|
|
if low_thresh < d < high_thresh:
|
|
|
|
|
mask[u, v] = 0
|
|
|
|
|
|
|
|
|
|
fshift = dft_shift * mask
|
|
|
|
|
f_ishift = np.fft.ifftshift(fshift)
|
|
|
|
|
img_back = cv2.idft(f_ishift)
|
|
|
|
|
self.modified_image = cv2.magnitude(img_back[:, :, 0], img_back[:, :, 1])
|
|
|
|
|
|
|
|
|
|
self.display_image(self.modified_image, self.graphicsView_2)
|
|
|
|
|
|
|
|
|
|
def add_gaussian_noise(self):
|
|
|
|
|
if self.modified_image is not None:
|
|
|
|
|
self.add_to_history()
|
|
|
|
|
row, col, ch = self.modified_image.shape
|
|
|
|
|
mean = self.doubleSpinBox_mean_for_add_gaussian_noise.value()
|
|
|
|
|
sigma = self.doubleSpinBox_sigma_for_add_gaussian_noise.value()
|
|
|
|
|
gauss = np.random.normal(mean, sigma, (row, col, ch))
|
|
|
|
|
gauss = gauss.reshape(row, col, ch)
|
|
|
|
|
noisy = self.modified_image + gauss * 255
|
|
|
|
|
self.modified_image = np.clip(noisy, 0, 255).astype(np.uint8)
|
|
|
|
|
self.display_image(self.modified_image, self.graphicsView_2)
|
|
|
|
|
|
|
|
|
|
def add_poisson_noise(self):
|
|
|
|
|
if self.modified_image is not None:
|
|
|
|
|
self.add_to_history()
|
|
|
|
|
vals = len(np.unique(self.modified_image))
|
|
|
|
|
vals = 2 ** np.ceil(np.log2(vals))
|
|
|
|
|
noisy = np.random.poisson(self.modified_image * vals) / float(vals)
|
|
|
|
|
self.modified_image = np.clip(noisy, 0, 255).astype(np.uint8)
|
|
|
|
|
self.display_image(self.modified_image, self.graphicsView_2)
|
|
|
|
|
|
|
|
|
|
def add_salt_and_pepper_noise(self):
|
|
|
|
|
if self.modified_image is not None:
|
|
|
|
|
self.add_to_history()
|
|
|
|
|
noisy_img = self.modified_image.copy()
|
|
|
|
|
total_pixels = self.modified_image.size // self.modified_image.shape[2] # 计算总像素数
|
|
|
|
|
num_noise_pixels = int(total_pixels * self.doubleSpinBox_occupation_for_add_salt_and_pepper_noise.value()) # 计算噪声像素数
|
|
|
|
|
|
|
|
|
|
if self.radioButton_salt_and_pepper_for_add_salt_and_pepper_noise.isChecked():
|
|
|
|
|
# 随机分布椒盐噪声
|
|
|
|
|
for _ in range(num_noise_pixels):
|
|
|
|
|
x = np.random.randint(0, self.modified_image.shape[0])
|
|
|
|
|
y = np.random.randint(0, self.modified_image.shape[1])
|
|
|
|
|
if np.random.randint(2) == 0:
|
|
|
|
|
noisy_img[x, y] = [0, 0, 0] # 椒噪声
|
|
|
|
|
else:
|
|
|
|
|
noisy_img[x, y] = [255, 255, 255] # 盐噪声
|
|
|
|
|
elif self.radioButton_salt_for_add_salt_and_pepper_noise.isChecked():
|
|
|
|
|
# 全盐噪声
|
|
|
|
|
for _ in range(num_noise_pixels):
|
|
|
|
|
x = np.random.randint(0, self.modified_image.shape[0])
|
|
|
|
|
y = np.random.randint(0, self.modified_image.shape[1])
|
|
|
|
|
noisy_img[x, y] = [255, 255, 255] # 盐噪声
|
|
|
|
|
elif self.radioButton_pepper_for_add_salt_and_pepper_noise.isChecked():
|
|
|
|
|
# 全椒噪声
|
|
|
|
|
for _ in range(num_noise_pixels):
|
|
|
|
|
x = np.random.randint(0, self.modified_image.shape[0])
|
|
|
|
|
y = np.random.randint(0, self.modified_image.shape[1])
|
|
|
|
|
noisy_img[x, y] = [0, 0, 0] # 椒噪声
|
|
|
|
|
self.modified_image=noisy_img
|
|
|
|
|
self.display_image(self.modified_image, self.graphicsView_2)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def apply_erosion(self):
|
|
|
|
|
if self.modified_image is not None:
|
|
|
|
|
self.add_to_history()
|
|
|
|
|
kernel = np.ones((5,5), np.uint8)
|
|
|
|
|
self.modified_image = cv2.erode(self.modified_image, kernel, iterations = 1)
|
|
|
|
|
self.display_image(self.modified_image, self.graphicsView_2)
|
|
|
|
|
|
|
|
|
|
def apply_dilation(self):
|
|
|
|
|
if self.modified_image is not None:
|
|
|
|
|
self.add_to_history()
|
|
|
|
|
kernel = np.ones((5,5), np.uint8)
|
|
|
|
|
self.modified_image = cv2.dilate(self.modified_image, kernel, iterations = 1)
|
|
|
|
|
self.display_image(self.modified_image, self.graphicsView_2)
|
|
|
|
|
|
|
|
|
|
def log_edge_detection(self):
|
|
|
|
|
if self.modified_image is not None:
|
|
|
|
|
self.add_to_history()
|
|
|
|
|
gray = cv2.cvtColor(self.original_image, cv2.COLOR_BGR2GRAY)
|
|
|
|
|
blurred = cv2.GaussianBlur(gray, (3, 3), 0)
|
|
|
|
|
log = cv2.Laplacian(blurred, cv2.CV_64F)
|
|
|
|
|
self.modified_image = cv2.convertScaleAbs(log)
|
|
|
|
|
self.display_image(self.modified_image, self.graphicsView_2)
|
|
|
|
|
|
|
|
|
|
def roberts_edge_detection(self):
|
|
|
|
|
if self.modified_image is not None:
|
|
|
|
|
self.add_to_history()
|
|
|
|
|
gray = cv2.cvtColor(self.original_image, cv2.COLOR_BGR2GRAY)
|
|
|
|
|
kernelx = np.array([[1, 0], [0, -1]], dtype=int)
|
|
|
|
|
kernely = np.array([[0, 1], [-1, 0]], dtype=int)
|
|
|
|
|
x = cv2.filter2D(gray, cv2.CV_16S, kernelx)
|
|
|
|
|
y = cv2.filter2D(gray, cv2.CV_16S, kernely)
|
|
|
|
|
absX = cv2.convertScaleAbs(x)
|
|
|
|
|
absY = cv2.convertScaleAbs(y)
|
|
|
|
|
self.modified_image = cv2.addWeighted(absX, 0.5, absY, 0.5, 0)
|
|
|
|
|
self.display_image(self.modified_image, self.graphicsView_2)
|
|
|
|
|
|
|
|
|
|
def sobel_edge_detection(self):
|
|
|
|
|
if self.modified_image is not None:
|
|
|
|
|
self.add_to_history()
|
|
|
|
|
gray = cv2.cvtColor(self.original_image, cv2.COLOR_BGR2GRAY)
|
|
|
|
|
grad_x = cv2.Sobel(gray, cv2.CV_16S, 1, 0)
|
|
|
|
|
grad_y = cv2.Sobel(gray, cv2.CV_16S, 0, 1)
|
|
|
|
|
abs_grad_x = cv2.convertScaleAbs(grad_x)
|
|
|
|
|
abs_grad_y = cv2.convertScaleAbs(grad_y)
|
|
|
|
|
self.modified_image = cv2.addWeighted(abs_grad_x, 0.5, abs_grad_y, 0.5, 0)
|
|
|
|
|
self.display_image(self.modified_image, self.graphicsView_2)
|
|
|
|
|
|
|
|
|
|
def laplacian_edge_detection(self):
|
|
|
|
|
if self.modified_image is not None:
|
|
|
|
|
self.add_to_history()
|
|
|
|
|
gray = cv2.cvtColor(self.original_image, cv2.COLOR_BGR2GRAY)
|
|
|
|
|
laplacian = cv2.Laplacian(gray, cv2.CV_16S)
|
|
|
|
|
self.modified_image = cv2.convertScaleAbs(laplacian)
|
|
|
|
|
self.display_image(self.modified_image, self.graphicsView_2)
|
|
|
|
|
|
|
|
|
|
def canny_edge_detection(self):
|
|
|
|
|
if self.modified_image is not None:
|
|
|
|
|
self.add_to_history()
|
|
|
|
|
gray = cv2.cvtColor(self.original_image, cv2.COLOR_BGR2GRAY)
|
|
|
|
|
self.modified_image = cv2.Canny(gray, 100, 200)
|
|
|
|
|
self.display_image(self.modified_image, self.graphicsView_2)
|
|
|
|
|
|
|
|
|
|
def ideal_high_pass_filter(self):
|
|
|
|
|
if self.modified_image is not None:
|
|
|
|
|
self.add_to_history()
|
|
|
|
|
|
|
|
|
|
gray = cv2.cvtColor(self.original_image, cv2.COLOR_BGR2GRAY)
|
|
|
|
|
rows, cols = gray.shape
|
|
|
|
|
crow, ccol = rows // 2, cols // 2
|
|
|
|
|
|
|
|
|
|
threshold=self.spinBox_threshold_for_ideal_high_pass_filter.value()
|
|
|
|
|
|
|
|
|
|
if self.radioButton_circle_for_ideal_high_pass_filter.isChecked():
|
|
|
|
|
shape = "圆形"
|
|
|
|
|
elif self.radioButton_rectangle_for_ideal_high_pass_filter.isChecked():
|
|
|
|
|
shape = "矩形"
|
|
|
|
|
else:
|
|
|
|
|
shape = "圆形"
|
|
|
|
|
|
|
|
|
|
mask = np.ones((rows, cols), np.uint8)
|
|
|
|
|
if shape == "圆形":
|
|
|
|
|
for i in range(rows):
|
|
|
|
|
for j in range(cols):
|
|
|
|
|
dist = np.sqrt((i - crow) ** 2 + (j - ccol) ** 2)
|
|
|
|
|
if dist <= threshold:
|
|
|
|
|
mask[i, j] = 0
|
|
|
|
|
elif shape == "矩形":
|
|
|
|
|
mask[int(crow - threshold):int(crow + threshold), int(ccol - threshold):int(ccol + threshold)] = 0
|
|
|
|
|
|
|
|
|
|
fshift = np.fft.fftshift(np.fft.fft2(gray))
|
|
|
|
|
fshift = fshift * mask
|
|
|
|
|
|
|
|
|
|
img_back = np.fft.ifft2(np.fft.ifftshift(fshift))
|
|
|
|
|
img_back = np.abs(img_back)
|
|
|
|
|
|
|
|
|
|
self.modified_image = np.uint8(img_back)
|
|
|
|
|
self.display_image(self.modified_image, self.graphicsView_2)
|
|
|
|
|
|
|
|
|
|
def butterworth_high_pass_filter(self):
|
|
|
|
|
if self.modified_image is not None:
|
|
|
|
|
self.add_to_history()
|
|
|
|
|
|
|
|
|
|
gray = cv2.cvtColor(self.original_image, cv2.COLOR_BGR2GRAY)
|
|
|
|
|
rows, cols = gray.shape
|
|
|
|
|
crow, ccol = rows // 2, cols // 2
|
|
|
|
|
|
|
|
|
|
# 设置阈值和阶数
|
|
|
|
|
threshold=self.spinBox_threshold_for_butterworth_high_pass_filter.value()
|
|
|
|
|
|
|
|
|
|
order=self.spinBox_order_for_butterworth_high_pass_filter.value()
|
|
|
|
|
|
|
|
|
|
mask = np.zeros((rows, cols), np.float32)
|
|
|
|
|
for i in range(rows):
|
|
|
|
|
for j in range(cols):
|
|
|
|
|
dist = np.sqrt((i - crow) ** 2 + (j - ccol) ** 2)
|
|
|
|
|
mask[i, j] = 1 / (1 + (threshold / dist) ** (2 * order))
|
|
|
|
|
|
|
|
|
|
fshift = np.fft.fftshift(np.fft.fft2(gray))
|
|
|
|
|
fshift = fshift * mask
|
|
|
|
|
|
|
|
|
|
img_back = np.fft.ifft2(np.fft.ifftshift(fshift))
|
|
|
|
|
img_back = np.abs(img_back)
|
|
|
|
|
|
|
|
|
|
self.modified_image = np.uint8(img_back)
|
|
|
|
|
self.display_image(self.modified_image, self.graphicsView_2)
|
|
|
|
|
|
|
|
|
|
def gaussian_high_pass_filter(self):
|
|
|
|
|
if self.modified_image is not None:
|
|
|
|
|
self.add_to_history()
|
|
|
|
|
|
|
|
|
|
gray = cv2.cvtColor(self.original_image, cv2.COLOR_BGR2GRAY)
|
|
|
|
|
rows, cols = gray.shape
|
|
|
|
|
crow, ccol = rows // 2, cols // 2
|
|
|
|
|
|
|
|
|
|
# 设置标准差
|
|
|
|
|
threshold=self.spinBox_sigma_for_gaussian_high_pass_filter.value()
|
|
|
|
|
|
|
|
|
|
mask = np.zeros((rows, cols), np.float32)
|
|
|
|
|
for i in range(rows):
|
|
|
|
|
for j in range(cols):
|
|
|
|
|
dist = np.sqrt((i - crow) ** 2 + (j - ccol) ** 2)
|
|
|
|
|
mask[i, j] = 1 - np.exp(-dist ** 2 / (2 * (threshold ** 2)))
|
|
|
|
|
|
|
|
|
|
fshift = np.fft.fftshift(np.fft.fft2(gray))
|
|
|
|
|
fshift = fshift * mask
|
|
|
|
|
|
|
|
|
|
img_back = np.fft.ifft2(np.fft.ifftshift(fshift))
|
|
|
|
|
img_back = np.abs(img_back)
|
|
|
|
|
|
|
|
|
|
self.modified_image = np.uint8(img_back)
|
|
|
|
|
self.display_image(self.modified_image, self.graphicsView_2)
|
|
|
|
|
|
|
|
|
|
def ideal_low_pass_filter(self):
|
|
|
|
|
if self.modified_image is not None:
|
|
|
|
|
self.add_to_history()
|
|
|
|
|
|
|
|
|
|
gray = cv2.cvtColor(self.original_image, cv2.COLOR_BGR2GRAY)
|
|
|
|
|
rows, cols = gray.shape
|
|
|
|
|
crow, ccol = rows // 2, cols // 2
|
|
|
|
|
|
|
|
|
|
threshold=self.spinBox_threshold_for_ideal_low_pass_filter.value()
|
|
|
|
|
|
|
|
|
|
if self.radioButton_circle_for_ideal_low_pass_filter.isChecked():
|
|
|
|
|
shape = "圆形"
|
|
|
|
|
|
|
|
|
|
elif self.radioButton_rectangle_for_ideal_low_pass_filter.isChecked():
|
|
|
|
|
shape = "矩形"
|
|
|
|
|
else:
|
|
|
|
|
shape = "圆形"
|
|
|
|
|
|
|
|
|
|
mask = np.zeros((rows, cols), np.uint8)
|
|
|
|
|
if shape == "圆形":
|
|
|
|
|
for i in range(rows):
|
|
|
|
|
for j in range(cols):
|
|
|
|
|
dist = np.sqrt((i - crow) ** 2 + (j - ccol) ** 2)
|
|
|
|
|
if dist <= threshold:
|
|
|
|
|
mask[i, j] = 1
|
|
|
|
|
elif shape == "矩形":
|
|
|
|
|
mask[int(crow - threshold):int(crow + threshold), int(ccol - threshold):int(ccol + threshold)] = 1
|
|
|
|
|
|
|
|
|
|
fshift = np.fft.fftshift(np.fft.fft2(gray))
|
|
|
|
|
fshift = fshift * mask
|
|
|
|
|
|
|
|
|
|
img_back = np.fft.ifft2(np.fft.ifftshift(fshift))
|
|
|
|
|
img_back = np.abs(img_back)
|
|
|
|
|
|
|
|
|
|
self.modified_image = np.uint8(img_back)
|
|
|
|
|
self.display_image(self.modified_image, self.graphicsView_2)
|
|
|
|
|
|
|
|
|
|
def butterworth_low_pass_filter(self):
|
|
|
|
|
if self.modified_image is not None:
|
|
|
|
|
self.add_to_history()
|
|
|
|
|
|
|
|
|
|
gray = cv2.cvtColor(self.original_image, cv2.COLOR_BGR2GRAY)
|
|
|
|
|
rows, cols = gray.shape
|
|
|
|
|
crow, ccol = rows // 2, cols // 2
|
|
|
|
|
|
|
|
|
|
threshold=self.spinBox_threshold_for_butterworth_low_pass_filter.value()
|
|
|
|
|
|
|
|
|
|
order=self.spinBox_order_for_butterworth_low_pass_filter.value()
|
|
|
|
|
|
|
|
|
|
mask = np.zeros((rows, cols))
|
|
|
|
|
for i in range(rows):
|
|
|
|
|
for j in range(cols):
|
|
|
|
|
distance = np.sqrt((i - crow) ** 2 + (j - ccol) ** 2)
|
|
|
|
|
mask[i, j] = 1 / (1 + (distance / threshold) ** (2 * order))
|
|
|
|
|
|
|
|
|
|
fshift = np.fft.fftshift(np.fft.fft2(gray))
|
|
|
|
|
fshift = fshift * mask
|
|
|
|
|
|
|
|
|
|
img_back = np.fft.ifft2(np.fft.ifftshift(fshift))
|
|
|
|
|
img_back = np.abs(img_back)
|
|
|
|
|
|
|
|
|
|
self.modified_image = np.uint8(img_back)
|
|
|
|
|
self.display_image(self.modified_image, self.graphicsView_2)
|
|
|
|
|
|
|
|
|
|
def gaussian_low_pass_filter(self):
|
|
|
|
|
if self.modified_image is not None:
|
|
|
|
|
self.add_to_history()
|
|
|
|
|
|
|
|
|
|
gray = cv2.cvtColor(self.original_image, cv2.COLOR_BGR2GRAY)
|
|
|
|
|
rows, cols = gray.shape
|
|
|
|
|
crow, ccol = rows // 2, cols // 2
|
|
|
|
|
|
|
|
|
|
sigma=self.spinBox_sigma_for_gaussian_low_pass_filter.value()
|
|
|
|
|
|
|
|
|
|
mask = np.zeros((rows, cols))
|
|
|
|
|
for i in range(rows):
|
|
|
|
|
for j in range(cols):
|
|
|
|
|
distance = np.sqrt((i - crow) ** 2 + (j - ccol) ** 2)
|
|
|
|
|
mask[i, j] = np.exp(-(distance ** 2) / (2 * (sigma ** 2)))
|
|
|
|
|
|
|
|
|
|
fshift = np.fft.fftshift(np.fft.fft2(gray))
|
|
|
|
|
fshift = fshift * mask
|
|
|
|
|
|
|
|
|
|
img_back = np.fft.ifft2(np.fft.ifftshift(fshift))
|
|
|
|
|
img_back = np.abs(img_back)
|
|
|
|
|
|
|
|
|
|
self.modified_image = np.uint8(img_back)
|
|
|
|
|
self.display_image(self.modified_image, self.graphicsView_2)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def apply_roberts(self):
|
|
|
|
|
if self.modified_image is not None:
|
|
|
|
|
self.add_to_history()
|
|
|
|
|
|
|
|
|
|
gray = cv2.cvtColor(self.modified_image, cv2.COLOR_BGR2GRAY)
|
|
|
|
|
kernelx = np.array([[1, 0], [0, -1]], dtype=int)
|
|
|
|
|
kernely = np.array([[0, 1], [-1, 0]], dtype=int)
|
|
|
|
|
x = cv2.filter2D(gray, cv2.CV_16S, kernelx)
|
|
|
|
|
y = cv2.filter2D(gray, cv2.CV_16S, kernely)
|
|
|
|
|
absX = cv2.convertScaleAbs(x)
|
|
|
|
|
absY = cv2.convertScaleAbs(y)
|
|
|
|
|
roberts = cv2.addWeighted(absX, 0.5, absY, 0.5, 0)
|
|
|
|
|
self.modified_image = cv2.cvtColor(roberts, cv2.COLOR_GRAY2BGR)
|
|
|
|
|
self.display_image(self.modified_image, self.graphicsView_2)
|
|
|
|
|
|
|
|
|
|
def apply_sobel(self):
|
|
|
|
|
if self.modified_image is not None:
|
|
|
|
|
self.add_to_history()
|
|
|
|
|
|
|
|
|
|
gray = cv2.cvtColor(self.modified_image, cv2.COLOR_BGR2GRAY)
|
|
|
|
|
x = cv2.Sobel(gray, cv2.CV_16S, 1, 0)
|
|
|
|
|
y = cv2.Sobel(gray, cv2.CV_16S, 0, 1)
|
|
|
|
|
absX = cv2.convertScaleAbs(x)
|
|
|
|
|
absY = cv2.convertScaleAbs(y)
|
|
|
|
|
sobel = cv2.addWeighted(absX, 0.5, absY, 0.5, 0)
|
|
|
|
|
self.modified_image = cv2.cvtColor(sobel, cv2.COLOR_GRAY2BGR)
|
|
|
|
|
self.display_image(self.modified_image, self.graphicsView_2)
|
|
|
|
|
|
|
|
|
|
def apply_prewitt(self):
|
|
|
|
|
if self.modified_image is not None:
|
|
|
|
|
self.add_to_history()
|
|
|
|
|
|
|
|
|
|
gray = cv2.cvtColor(self.modified_image, cv2.COLOR_BGR2GRAY)
|
|
|
|
|
kernelx = np.array([[1, 0, -1], [1, 0, -1], [1, 0, -1]], dtype=int)
|
|
|
|
|
kernely = np.array([[1, 1, 1], [0, 0, 0], [-1, -1, -1]], dtype=int)
|
|
|
|
|
x = cv2.filter2D(gray, cv2.CV_16S, kernelx)
|
|
|
|
|
y = cv2.filter2D(gray, cv2.CV_16S, kernely)
|
|
|
|
|
absX = cv2.convertScaleAbs(x)
|
|
|
|
|
absY = cv2.convertScaleAbs(y)
|
|
|
|
|
prewitt = cv2.addWeighted(absX, 0.5, absY, 0.5, 0)
|
|
|
|
|
self.modified_image = cv2.cvtColor(prewitt, cv2.COLOR_GRAY2BGR)
|
|
|
|
|
self.display_image(self.modified_image, self.graphicsView_2)
|
|
|
|
|
|
|
|
|
|
def apply_laplacian(self):
|
|
|
|
|
if self.modified_image is not None:
|
|
|
|
|
self.add_to_history()
|
|
|
|
|
|
|
|
|
|
gray = cv2.cvtColor(self.modified_image, cv2.COLOR_BGR2GRAY)
|
|
|
|
|
laplacian = cv2.Laplacian(gray, cv2.CV_16S)
|
|
|
|
|
laplacian = cv2.convertScaleAbs(laplacian)
|
|
|
|
|
self.modified_image = cv2.cvtColor(laplacian, cv2.COLOR_GRAY2BGR)
|
|
|
|
|
self.display_image(self.modified_image, self.graphicsView_2)
|
|
|
|
|
|
|
|
|
|
def vessel_division(self):
|
|
|
|
|
if self.modified_image is not None:
|
|
|
|
|
self.add_to_history()
|
|
|
|
|
|
|
|
|
|
self.modified_image=vessel_detection.vessel_division(self.original_image)
|
|
|
|
|
self.display_image(self.modified_image, self.graphicsView_2)
|
|
|
|
|
|
|
|
|
|
def repair_image(self):
|
|
|
|
|
if self.modified_image is not None:
|
|
|
|
|
self.add_to_history()
|
|
|
|
|
|
|
|
|
|
# 读取图片
|
|
|
|
|
img = self.original_image
|
|
|
|
|
# 对图片进行噪声去除
|
|
|
|
|
blurred = cv2.medianBlur(img, 5)
|
|
|
|
|
noise = cv2.fastNlMeansDenoisingColored(blurred, None, 10, 10, 7, 21)
|
|
|
|
|
# 对图片进行细节修复
|
|
|
|
|
detail_enhancer = cv2.detailEnhance(noise)
|
|
|
|
|
detail_enhancer = detail_enhancer.astype(np.uint8)
|
|
|
|
|
self.modified_image=detail_enhancer
|
|
|
|
|
self.display_image(self.modified_image, self.graphicsView_2)
|
|
|
|
|
|
|
|
|
|
def compress_image(self): # 通常你只需要修改mb大小
|
|
|
|
|
if self.modified_image is not None:
|
|
|
|
|
self.add_to_history()
|
|
|
|
|
|
|
|
|
|
# 如果输入图像是 numpy 数组,将其转换为 PIL 图像
|
|
|
|
|
if isinstance(self.original_image, np.ndarray):
|
|
|
|
|
self.modified_image = Image.fromarray(self.original_image)
|
|
|
|
|
# 创建一个BytesIO对象,用于存储压缩后的图像数据
|
|
|
|
|
img_bytes = io.BytesIO()
|
|
|
|
|
|
|
|
|
|
# 初始压缩质量
|
|
|
|
|
quality = 95
|
|
|
|
|
target_size_kb=self.spinBox_size_to_transform.value()
|
|
|
|
|
quality_step=5
|
|
|
|
|
# 获取当前文件大小
|
|
|
|
|
def get_size_kb(byte_data):
|
|
|
|
|
return len(byte_data) / 1024
|
|
|
|
|
|
|
|
|
|
while quality > 0:
|
|
|
|
|
# 清空BytesIO对象
|
|
|
|
|
img_bytes.seek(0)
|
|
|
|
|
img_bytes.truncate()
|
|
|
|
|
|
|
|
|
|
# 压缩图像
|
|
|
|
|
self.modified_image.save(img_bytes, format='JPEG', quality=quality)
|
|
|
|
|
|
|
|
|
|
# 获取压缩后图像的大小
|
|
|
|
|
current_size_kb = get_size_kb(img_bytes.getvalue())
|
|
|
|
|
|
|
|
|
|
# 如果压缩后的图像大小在目标范围内,则返回图像对象
|
|
|
|
|
if current_size_kb <= target_size_kb:
|
|
|
|
|
img_bytes.seek(0)
|
|
|
|
|
self.modified_image = Image.open(img_bytes)
|
|
|
|
|
self.modified_image =np.array(self.modified_image)
|
|
|
|
|
self.display_image(self.modified_image, self.graphicsView_2)
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
# 否则,降低质量并继续压缩
|
|
|
|
|
quality -= quality_step
|
|
|
|
|
|
|
|
|
|
# 如果无法压缩到目标大小,返回None并提示
|
|
|
|
|
print("无法压缩到目标大小,请检查目标大小是否合理。")
|
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
if __name__ == '__main__':
|
|
|
|
|
def scale_ui(widget, scale_factor):
|
|
|
|
|
widget.resize(widget.size() * scale_factor)
|
|
|
|
|
for child in widget.findChildren(QtWidgets.QWidget):
|
|
|
|
|
child.resize(child.size() * scale_factor)
|
|
|
|
|
child.move(child.pos() * scale_factor)
|
|
|
|
|
|
|
|
|
|
app = QtWidgets.QApplication(sys.argv)
|
|
|
|
|
window=MainWindow()
|
|
|
|
|
|
|
|
|
|
scale_factor = 1.8
|
|
|
|
|
scale_ui(window, scale_factor)
|
|
|
|
|
|
|
|
|
|
window.show()
|
|
|
|
|
sys.exit(app.exec_())
|