diff --git a/mainwindow(1).py b/mainwindow(1).py new file mode 100644 index 0000000..2073040 --- /dev/null +++ b/mainwindow(1).py @@ -0,0 +1,956 @@ +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_())