From d9c073dc847f101f7188b697cd0b8e0a78d684c9 Mon Sep 17 00:00:00 2001 From: py7rxwkbq <10225101493@stu.ecnu.edu.cn> Date: Wed, 3 Jul 2024 21:23:34 +0800 Subject: [PATCH] ADD file via upload --- gui4.py | 512 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 512 insertions(+) create mode 100644 gui4.py diff --git a/gui4.py b/gui4.py new file mode 100644 index 0000000..19ce4ea --- /dev/null +++ b/gui4.py @@ -0,0 +1,512 @@ +import tkinter as tk +from tkinter import ttk +from PIL import Image, ImageTk +import ctypes +import cv2 +import numpy as np +from ttkbootstrap import Style, Button +from tkinter import filedialog, Scale +import filter_methods +import math +"""部分全局变量""" +#当下图像/需要替换图像 +current_img=None +display_img=None + +#裁剪参数 +crop_start_x, crop_start_y = None, None +crop_end_x, crop_end_y = None, None +cropping = False + + +# 获取屏幕的高度和宽度 +user32 = ctypes.windll.user32 +screen_width, screen_height = user32.GetSystemMetrics(0), user32.GetSystemMetrics(1) + +# 目标高度为窗口高度的2/5 +fixed_height = int(screen_height * 0.6) + +# 初始化ttkbootstrap样式 +style = Style(theme='litera') + +# 创建主窗口 +root = style.master +# 背景 + # 上背景 + # 加载初始图片 +bg_image = Image.open("D:\VScode_Python\digital_image_processing\images\p5.jpg") +bg_photo = ImageTk.PhotoImage(bg_image) + # 创建一个label并将其作为背景 +background_label = tk.Label(root, image=bg_photo) +# 加载底部图片 +bottom_image = Image.open("D:\VScode_Python\digital_image_processing\images\p6.jpg") +# 初始窗口高度的1/3 +initial_bottom_height = int(screen_height * 1/3) +bottom_image = bottom_image.resize((screen_width, initial_bottom_height), Image.LANCZOS) +bottom_photo = ImageTk.PhotoImage(bottom_image) + +# 创建底部图片的label +bottom_label = tk.Label(root, image=bottom_photo) +# 创建用于显示图片的label +image_label = tk.Label(root) +# 设置窗口大小为屏幕高度的正方形 +window_size = (screen_height, screen_height) +root.geometry(f"{window_size[0]}x{window_size[1]}") + +# 设置窗口位置居中 +root.geometry("+%d+%d" % ((screen_width / 2) - (window_size[0] / 2), 0)) + +"""背景设置""" +def resize_bg(event): + # 加载图片 + new_width = event.width + new_height = event.height + + # 背景图片 + bg_image = Image.open("D:\VScode_Python\digital_image_processing\images\p5.jpg") + bg_image = bg_image.resize((new_width, new_height), Image.LANCZOS) + global bg_photo + bg_photo = ImageTk.PhotoImage(bg_image) + + background_label.config(image=bg_photo) + background_label.place(x=0, y=0, relwidth=1, relheight=1) + + # 底部图片 + bottom_image = Image.open("D:\VScode_Python\digital_image_processing\images\p6.jpg") + bottom_height = int(new_height * 1/3) + bottom_image = bottom_image.resize((new_width, bottom_height), Image.LANCZOS) + global bottom_photo + bottom_photo = ImageTk.PhotoImage(bottom_image) + bottom_label.config(image=bottom_photo) + bottom_label.place(x=0, y=int(screen_height * 3/5)) + +"""设置拉条""" +# 计算滑动条之间的间隔,确保它们均匀分布在整个窗口宽度上 +slider_height = screen_height // 5 +gap = screen_height // 5 # 空隙包括滑动条两侧的边界和滑动条间的四个空隙 +start_y = screen_height * 2 // 3 +# 创建四个垂直滑动条并放置 +sliders = [] +labels_text = ["阴影", "锐化", "褪色", "对比"] +value_labels = [] +for i in range(4): + slider = ttk.Scale(root, style='TScale', from_=0, to=100, orient=tk.VERTICAL, length=slider_height, + command=lambda val, idx=i: update_value_label(val, idx)) + sliders.append(slider) + # 设置滑动条的位置,从左边开始,每次加上滑动条的宽度和一个间隙 + slider.place(x=(i+1) * gap, y=start_y) + text_label = ttk.Label(root, text=labels_text[i], style='TLabel') + text_label.place(x=(i+1) * gap, y=start_y + slider_height + 10) + # 添加显示数值的标签,并放置在滑动条右侧 + value_label = ttk.Label(root,text="0",bootstyle='info') + value_label.place(x=(i+1) * gap + slider.winfo_reqwidth() + 5, y=start_y + slider_height // 2 - 10) + value_labels.append(value_label) + style.configure('TLabel', background='white', foreground='black', font=('Arial', 12)) + +"""设置标签和拉条数值""" +# 更新对应滑动条右侧的数值标签 +def update_value_label(val, idx): + # 将val转换为整数,然后更新对应滑动条右侧的数值标签 + value_labels[idx].config(text=str(int(float(val)))) +#设置样式 +style.configure('TLabel', + background='black', + foreground='white', + font=('SimSun', 16, 'bold'), # 更改字体大小和样式 + padding=10, # 增加内部填充 + relief="ridge") # 更改边框样式 + +"""拉条button确定""" +#点击确认函数 +def on_confirm_button_click(): + # 获取阴影拉条的值 + global current_img,display_img + shadow_value = sliders[0].get() + sharpen_slider_value = sliders[1].get() + fade_slider_value = sliders[2].get() + con_slider_value = sliders[3].get() + # 假设current_img是当前要处理的图像 + if current_img is not None: + # 调用阴影调整函数 + adjusted_image = adjust_shadow(current_img, shadow_value) + sharp_image = sharpen_image(adjusted_image, sharpen_slider_value) + fade = fade_image(sharp_image, fade_slider_value) + display_img = adjust_contrast(fade, con_slider_value) + # 在界面上更新图像 + update_display() + +#确定参数按钮摆放 +button = Button(root, text="确定参数", bootstyle="success-outline-toolbutton", command= on_confirm_button_click) +button.place(x=gap // 3, y=725, width=screen_height // 10, height=screen_height // 20) + + + +"""拉条函数实现""" +#阴影实现 +def adjust_shadow(image, shadow_slider_value): + # 将图像转换为HSV颜色空间 + hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV) + # 提取V(明度)通道 + v_channel = hsv[:,:,2] + # 使用直方图均衡化增强阴影细节 + v_channel_eq = cv2.equalizeHist(v_channel) + # 将增强后的V通道放回HSV图像中 + hsv[:,:,2] = v_channel_eq + # 将HSV图像转换回BGR颜色空间 + adjusted_image = cv2.cvtColor(hsv, cv2.COLOR_HSV2BGR) + # 50是亮度增加的最大值,可以根据需要调整 + brightness_increase = (shadow_slider_value / 100) * 50 + v_channel_eq = np.clip(v_channel_eq + brightness_increase, 0, 255).astype(np.uint8) + + # 将增强后的V通道放回HSV图像中 + hsv[:,:,2] = v_channel_eq + + # 将HSV图像转换回BGR颜色空间 + adjusted_image = cv2.cvtColor(hsv, cv2.COLOR_HSV2BGR) + return adjusted_image + +#锐化实现 +def unsharp_mask(image, kernel_size=(5, 5), sigma=1.0, amount=1.0, threshold=0): + blurred = cv2.GaussianBlur(image, kernel_size, sigma) + sharpened = float(amount + 1) * image - float(amount) * blurred + sharpened = np.maximum(sharpened, np.zeros(sharpened.shape)) + sharpened = np.minimum(sharpened, 255 * np.ones(sharpened.shape)) + sharpened = sharpened.round().astype(np.uint8) + if threshold > 0: + low_contrast_mask = np.absolute(image - blurred) < threshold + np.copyto(sharpened, image, where=low_contrast_mask) + return sharpened +def sharpen_image(image, sharpen_slider_value): + # 将滑块值映射到Unsharp Mask的amount参数上 + amount = sharpen_slider_value / 100.0 + # 使用Unsharp Mask锐化图像 + sharpened_image = unsharp_mask(image, amount=amount) + return sharpened_image + +#褪色 +def fade_image(image, fade_slider_value): + factor = fade_slider_value // 100 + # 将BGR图像转换为HSV颜色空间 + hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV) + + # 提取饱和度通道 + h, s, v = cv2.split(hsv) + + # 调整饱和度 + s = (s * factor).astype(np.uint8) + + # 重新组合HSV图像 + hsv_faded = cv2.merge([h, s, v]) + + # 将HSV图像转换回BGR格式 + faded_image = cv2.cvtColor(hsv_faded, cv2.COLOR_HSV2BGR) + + return faded_image + +#对比度 +def adjust_contrast(image, con_slider_value): + # 将滑动条值转换为对比度因子 + # 这里我们设定对比度因子的范围为0.5到1.5 + # 当con_slider_value为0时,对比度最低;为100时,对比度最高 + alpha = 0.5 + (con_slider_value / 100) # 映射到0.5到1.5的范围 + # 线性变换调整对比度 + adjusted_image = np.clip(alpha * image, 0, 255).astype(np.uint8) + return adjusted_image + + +"""设置button""" +function_button_names = ["打开图片", "添加滤镜", "顺时针旋转", "逆时针旋转", "水平镜像", "垂直镜像", "添加边框", "裁剪图像", "手势识别", "保存图像"] +# 创建10个按钮并放置在窗口的下三分之一处 +buttons = [] +for i, button_name in enumerate(function_button_names): + if function_button_names == "裁剪图像": + button = tk.Button(root, text="开始裁剪", command=lambda: image_label.bind("", start_crop)) + button = Button(root, text=button_name, bootstyle="success-outline-toolbutton") + button.config(command=lambda btn_text=button_name: button_event(btn_text)) + buttons.append(button) + # 计算每个按钮的位置 + button_start_y = int(screen_height * 3 / 5) + button_x = i * (screen_height // 10) + button.place(x=button_x, y=button_start_y, width=screen_height // 10, height=screen_height // 20) + +def button_event(button_text): + print(f"Button '{button_text}' was clicked.") + if button_text == "打开图片": + read_image_from_file() + elif button_text == "添加滤镜": + add_fiLter() + elif button_text == "顺时针旋转": + rotate(1) + elif button_text == "逆时针旋转": + rotate(0) + elif button_text == "水平镜像": + mirror_horizontal() + elif button_text == "垂直镜像": + mirror_vertical() + elif button_text == "添加边框": + add_border(20, border_color=(0, 0, 0)) + elif button_text == "手势识别": + detect_hand_gesture() + elif button_text == "裁剪图像": + global display_img + start_crop() + display_img = perform_crop() + end_crop() + + elif button_text == "保存图像": + save_image_dialog() + + +result_label = tk.Label(root, text="识别的数字:无", font=("Helvetica", 20)) +result_label.pack(pady=20) + +#button1:打开图片 + #读取图片 +def read_image_from_file(): + global current_img,display_img,flag + flag=0 + # 打开文件选择对话框 + filepath = filedialog.askopenfilename( + title="Select an image", + filetypes=(("JPEG files", "*.jpg"), ("PNG files", "*.png"), ("All files", "*.*")) + ) + if not filepath: + return + current_img = cv2.imread(filepath) + display_img = cv2.imread(filepath) # 初始显示原图 + update_display() + return + +#用display替换current更新图片 +def update_display(): + global display_img,current_img,flag + if display_img is not None: + # 缩放到适合的大小 + display_img = resize_image_to_fixed_height() + # 将OpenCV的BGR图像转换为PIL的RGB图像 + img_pil = Image.fromarray(cv2.cvtColor(display_img, cv2.COLOR_BGR2RGB)) + img_tk = ImageTk.PhotoImage(image=img_pil) + + # 更新界面显示 + image_label.config(image=img_tk) + image_label.image = img_tk + image_label.place(relx=0.5,rely=0.3,anchor=tk.CENTER) + #替换 + current_img = display_img.copy() + +#处理图像位置 +def resize_image_to_fixed_height(): + global display_img + if display_img is not None: + image=display_img + # 获取图像的原始高度和宽度 + orig_height, orig_width = image.shape[:2] + # 计算高度缩放比例 + ratio = fixed_height / orig_height + # 根据比例计算新的宽度 + new_width = int(orig_width * ratio) + # 使用cv2.resize调整图像大小 + resized_image = cv2.resize(image, (new_width, fixed_height), interpolation=cv2.INTER_LINEAR) + return resized_image + + +#button旋转画布 +def rotate(number): + global display_img + if number==1: + display_img = rotate_shun() + else: + display_img = rotate_ni() + update_display() +#顺时针 +def rotate_shun(): + global current_img,flag + flag+=1 + rows, cols = current_img.shape[:2] + M = cv2.getRotationMatrix2D((cols / 2, rows / 2), -90, 1.0) + rotate_image=cv2.warpAffine(current_img, M,(rows, cols)) + abs_cos = abs(M[0, 0]) + abs_sin = abs(M[0, 1]) + bound_w = int(rows * abs_sin + cols * abs_cos) + bound_h = int(rows * abs_cos + cols * abs_sin) + # 调整旋转矩阵的平移部分,以适应新的尺寸 + center = (cols / 2, rows / 2) + M[0, 2] += bound_w / 2 - center[0] + M[1, 2] += bound_h / 2 - center[1] + # 应用旋转和尺寸调整 + rotate_image = cv2.warpAffine(current_img, M, (bound_w, bound_h)) + return rotate_image +#逆时针 +def rotate_ni(): + global current_img,flag + flag+=1 + rows, cols = current_img.shape[:2] + M = cv2.getRotationMatrix2D((cols / 2, rows / 2), 90, 1.0) + rotate_image=cv2.warpAffine(current_img, M,(rows, cols)) + abs_cos = abs(M[0, 0]) + abs_sin = abs(M[0, 1]) + bound_w = int(rows * abs_sin + cols * abs_cos) + bound_h = int(rows * abs_cos + cols * abs_sin) + # 调整旋转矩阵的平移部分,以适应新的尺寸 + center = (cols / 2, rows / 2) + M[0, 2] += bound_w / 2 - center[0] + M[1, 2] += bound_h / 2 - center[1] + + # 应用旋转和尺寸调整 + rotate_image = cv2.warpAffine(current_img, M, (bound_w, bound_h)) + + return rotate_image + + +#button水平镜像 +def mirror_horizontal(): + global display_img,current_img + display_img = cv2.flip(current_img,1,dst=None) + update_display() + +#button垂直镜像 +def mirror_vertical(): + global current_img,display_img + display_img = cv2.flip(current_img,0,dst=None) + update_display() + +#button保存图片 +def save_image_dialog(): + global current_img + # 创建Tkinter的根窗口,但不会显示出来 + root = tk.Tk() + root.withdraw() + # 打开文件保存对话框 + file_path = filedialog.asksaveasfilename( + defaultextension=".jpg", # 默认文件扩展名 + filetypes=[("JPEG files", "*.jpg"), ("PNG files", "*.png"), ("All files", "*.*")], # 文件过滤器 + title="Save Image As..." # 对话框标题 + ) + # 如果用户选择了文件路径,则保存图像 + if file_path: + cv2.imwrite(file_path, current_img) + print(f"Image saved to {file_path}") + +#button添加滤镜 +def add_fiLter(): + global current_img,display_img + display_img =filter_methods.Nostalgia(current_img) + display_img =filter_methods.relief(current_img,60) + display_img =filter_methods.stylization(current_img) + display_img =filter_methods.beauty_filter(current_img) + update_display() + +#button添加边框 +def add_border(border_size, border_color=(0, 0, 0)):#边框大小和颜色 + # 使用cv2.copyMakeBorder()函数添加边框 + global current_img,display_img + display_img = cv2.copyMakeBorder( + current_img, + top=border_size, + bottom=border_size, + left=border_size, + right=border_size, + borderType=cv2.BORDER_CONSTANT, + value=border_color + ) + update_display() + +#button裁剪图像 + #开始监听 +def start_crop(event): + global crop_start_x, crop_start_y, cropping + crop_start_x, crop_start_y = event.x, event.y + cropping = True + #结束监听 +def end_crop(event): + global crop_start_x, crop_start_y, crop_end_x, crop_end_y, cropping + crop_end_x, crop_end_y = event.x, event.y + cropping = False + perform_crop() + #裁剪 +def perform_crop(): + global current_img, display_img, crop_start_x, crop_start_y, crop_end_x, crop_end_y + if current_img is None or not cropping: + return + # 获取裁剪区域的边界 + x1, y1 = min(crop_start_x, crop_end_x), min(crop_start_y, crop_end_y) + x2, y2 = max(crop_start_x, crop_end_x), max(crop_start_y, crop_end_y) + + # 裁剪图像 + cropped_img = current_img[y1:y2, x1:x2] + + # 更新显示图像 + display_img = cropped_img + update_display() + + # 清除裁剪状态 + crop_start_x, crop_start_y = None, None + crop_end_x, crop_end_y = None, None + + # 返回裁剪后的图像 + return cropped_img + +#button 手势识别 +def detect_hand_gesture(): + global current_img + image = current_img.copy() + roi = image[10:210, 400:600] + cv2.rectangle(image, (400, 10), (600, 210), (0, 0, 255), 0) + hsv = cv2.cvtColor(roi, cv2.COLOR_BGR2HSV) + lower_skin = np.array([0, 28, 70], dtype=np.uint8) + upper_skin = np.array([20, 255, 255], dtype=np.uint8) + + mask = cv2.inRange(hsv, lower_skin, upper_skin) + + kernel = np.ones((2, 2), np.uint8) + mask = cv2.dilate(mask, kernel, iterations=4) + mask = cv2.GaussianBlur(mask, (5, 5), 100) + + contours, _ = cv2.findContours(mask, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE) + + cnt = max(contours, key=lambda x: cv2.contourArea(x)) + areacnt = cv2.contourArea(cnt) + + + hull = cv2.convexHull(cnt) + areahull = cv2.contourArea(hull) + arearatio = areacnt / areahull + + hull = cv2.convexHull(cnt, returnPoints=False) + defects = cv2.convexityDefects(cnt, hull) + n = 0 + + for i in range(defects.shape[0]): + s, e, f, d = defects[i, 0] + start = tuple(cnt[s][0]) + end = tuple(cnt[e][0]) + far = tuple(cnt[f][0]) + a = math.sqrt((end[0] - start[0]) ** 2 + (end[1] - start[1]) ** 2) + b = math.sqrt((far[0] - start[0]) ** 2 + (far[1] - start[1]) ** 2) + c = math.sqrt((end[0] - far[0]) ** 2 + (end[1] - far[1]) ** 2) + angle = math.acos((b ** 2 + c ** 2 - a ** 2) / (2 * b * c)) * 57 + + if angle <= 90 and d > 20: + n += 1 + cv2.circle(roi, far, 3, [255, 0, 0], -1) + cv2.line(roi, start, end, [0, 255, 0], 2) + if n == 0: + if arearatio > 0.9: + result = '0' + else: + result = '1' + elif n == 1: + result = '2' + elif n == 2: + result = '3' + elif n == 3: + result = '4' + elif n == 4: + result = '5' + result_label.config(text=f"识别出的手势是:{result}") + + +# 绑定事件处理器以在窗口调整大小时更新背景图片和底部图片 +root.bind("", resize_bg) +# 运行主循环 +root.mainloop() \ No newline at end of file