Tk 8.5 and Python nf.py 中的应用

本文详细介绍了Python如何通过Tkinter模块与Tk进行交互,包括如何注册Python函数为Tk命令并从Python环境中调用这些命令。此外,还提供了一个具体示例来说明这一过程。

import Tkinterroot = Tkinter.Tk()
print Tkinter.TkVersion
def test():
   return (1, 2, 3)
root.tk.createcommand("test", test)
x = root.tk.call("test")
print x, type(x)
#python 和 tk 的交互
root.tk.createcommand("test", test): tk.createcommand 把python test 函数注册为tk命令test,
#在python 中执行tk 命令
x = root.tk.call("test")


#nf.py 中的makecommad1
def makecommand1(master, name, func, subst=None, needcleanup=0):
    f = Tkinter.CallWrapper(func, subst, master).__call__
 master.tk.createcommand(name, f)
 return name
#将 func ,subst,master 包装在一起 
f=Tkinter.CallWrapper(func, subst, master).__call__
#再将 python 中的f 函数注册tk 的name 命令
master.tk.createcommand(name, f)
#当然这段代码可以不使用包装函数
def makecommand1(master, name, func, subst=None, needcleanup=0):
   master.tk.createcommand(name, func)
   return name
#也可以运行成功.......

 

import cv2 import numpy as np import os import tkinter as tk from tkinter import filedialog, ttk, messagebox from PIL import Image, ImageTk import re from collections import deque class FastImageBrowser: def __init__(self, root): self.root = root self.root.title("去噪图") self.root.geometry("1400x700") # 初始化变量 self.image_folder = "" self.image_files = [] self.current_index = 0 self.current_image = None self.processed_image = None self.is_playing = False self.play_delay = 100 # 默认轮播速度 self.play_timer = None self.scale_factor = 0.1 self.min_clip = tk.DoubleVar(value=0.0) self.max_clip = tk.DoubleVar(value=1.0) self.buffer_size = 3 self.frame_buffer = deque(maxlen=self.buffer_size) # 多帧缓冲 # 统一尺寸(默认为None,首次加载时确定) self.fixed_size = None # 参数 self.nlm_h = tk.IntVar(value=10) self.min_clip = tk.DoubleVar(value=0.0) self.max_clip = tk.DoubleVar(value=0.99) self.gamma_value = tk.DoubleVar(value=1.0) # 固定gamma值,不自动调节 self.setup_ui() def setup_ui(self): controls_frame = tk.Frame(self.root, padx=10, pady=5) controls_frame.pack(fill=tk.X) tk.Button(controls_frame, text="选择文件夹", command=self.select_folder).grid(row=0, column=0, padx=5) tk.Label(controls_frame, text="选择图片:").grid(row=0, column=1, padx=5) self.image_combobox = ttk.Combobox(controls_frame, state="readonly", width=30) self.image_combobox.grid(row=0, column=2, padx=5) self.image_combobox.bind("<<ComboboxSelected>>", self.on_image_selected) # 播放控制按钮 button_frame = tk.Frame(controls_frame) button_frame.grid(row=0, column=3, padx=5) self.play_button = tk.Button(button_frame, text="开始轮播", command=self.toggle_play) self.play_button.pack(side=tk.LEFT, padx=2) tk.Button(button_frame, text="上一张", command=self.prev_image).pack(side=tk.LEFT, padx=2) tk.Button(button_frame, text="下一张", command=self.next_image).pack(side=tk.LEFT, padx=2) # 轮播速度选择 tk.Label(controls_frame, text="轮播速度(ms):").grid(row=0, column=4, padx=5) self.speed_var = tk.IntVar(value=1000) speed_combobox = ttk.Combobox(controls_frame, textvariable=self.speed_var, width=8) speed_combobox.grid(row=0, column=5, padx=5) speed_combobox['values'] = (50,100,200,500,1000) speed_combobox.state(["readonly"]) speed_combobox.bind("<<ComboboxSelected>>", self.update_play_speed) # 图像缩放控制 tk.Label(controls_frame, text="图像缩放(%):").grid(row=0, column=6, padx=5) self.scale_var = tk.DoubleVar(value=100) scale_combo = ttk.Combobox(controls_frame, textvariable=self.scale_var, width=8) scale_combo.grid(row=0, column=7, padx=5) scale_combo['values'] = ("1", "5", "10", "20", "40", "60","80","100") scale_combo.state(["readonly"]) scale_combo.bind("<<ComboboxSelected>>", self.update_scale_factor) # 去噪参数控制 tk.Label(controls_frame, text="帧间去噪强度:").grid(row=0, column=8, padx=5) tk.Scale(controls_frame, from_=0, to=30, orient=tk.HORIZONTAL, variable=self.nlm_h, length=150, command=lambda e: self.process_image()).grid(row=0, column=9, padx=5) # 对比度拉伸阈值控制 threshold_frame = tk.Frame(self.root, padx=10, pady=5) threshold_frame.pack(fill=tk.X) # 最小值阈值滑块 (0%-50%) tk.Label(threshold_frame, text="最小值阈值(0-0.5):").pack(side=tk.LEFT, padx=5) min_scale = tk.Scale(threshold_frame, from_=0, to=0.5, resolution=0.01, orient=tk.HORIZONTAL, variable=self.min_clip, length=200, command=lambda e: self.process_image()) min_scale.pack(side=tk.LEFT, padx=5) # 最大值阈值滑块 (50%-100%) tk.Label(threshold_frame, text="最大值阈值(0.5-1):").pack(side=tk.LEFT, padx=5) max_scale = tk.Scale(threshold_frame, from_=0.5, to=1.0, resolution=0.01, orient=tk.HORIZONTAL, variable=self.max_clip, length=200, command=lambda e: self.process_image()) max_scale.pack(side=tk.LEFT, padx=5) # Gamma控制 gamma_frame = tk.Frame(self.root, padx=10, pady=5) gamma_frame.pack(fill=tk.X) tk.Label(gamma_frame, text="Gamma校正:").pack(side=tk.LEFT, padx=5) tk.Scale(gamma_frame, from_=0.1, to=3.0, resolution=0.1, orient=tk.HORIZONTAL, variable=self.gamma_value, length=300, command=lambda e: self.process_image()).pack(side=tk.LEFT) # 图像显示区域 images_frame = tk.Frame(self.root, padx=10, pady=5) images_frame.pack(fill=tk.BOTH, expand=True) for title, attr in [("原始图像", "canvas_original"), ("处理结果", "canvas_processed")]: frame = tk.Frame(images_frame) frame.pack(side=tk.LEFT, fill=tk.BOTH, expand=True) tk.Label(frame, text=title).pack() canvas = tk.Canvas(frame, bg="lightgray", width=600, height=600) canvas.pack(fill=tk.BOTH, expand=True) setattr(self, attr, canvas) def update_play_speed(self, event=None): """更新轮播速度""" self.play_delay = self.speed_var.get() if self.is_playing: self.stop_play() self.toggle_play() def update_scale_factor(self, event=None): new_scale = float(self.scale_var.get()) / 100 if abs(self.scale_factor - new_scale) < 1e-5: return self.scale_factor = new_scale if self.image_files: filename = self.image_files[self.current_index] self.load_and_buffer_image(filename) # 重新处理和显示当前图像 self.process_image() self.display_image(self.current_image, self.canvas_original) def select_folder(self): folder = filedialog.askdirectory(title="选择图片文件夹") if not folder: return self.stop_play() self.image_folder = folder exts = ('.tif', '.jpg', '.jpeg', '.png', '.bmp') self.image_files = sorted([f for f in os.listdir(folder) if f.lower().endswith(exts)]) if not self.image_files: messagebox.showwarning("警告", "文件夹中没有支持格式的图片") return # 按数字自然排序(有数字的文件名) def natural_key(filename): match = re.search(r'(\d+)', filename) if match: return int(match.group(1)) return filename self.image_files.sort(key=natural_key) self.image_combobox['values'] = self.image_files self.image_combobox.current(0) self.current_index = 0 self.frame_buffer.clear() self.fixed_size = None # 重置尺寸 self.load_and_buffer_image(self.image_files[0]) self.display_image(self.current_image, self.canvas_original) def on_image_selected(self, event): selected = self.image_combobox.get() if selected: self.current_index = self.image_files.index(selected) self.load_and_buffer_image(selected) self.display_image(self.current_image, self.canvas_original) self.process_image() def load_and_buffer_image(self, filename): path = os.path.join(self.image_folder, filename) try: img_data = np.fromfile(path, dtype=np.uint8) img = cv2.imdecode(img_data, cv2.IMREAD_UNCHANGED) if img is None: raise ValueError("图像解码失败") except Exception as e: messagebox.showerror("错误", f"无法加载图像 {filename}:\n{e}") return # 应用缩放 if self.scale_factor != 1.0: h, w = img.shape[:2] new_w = max(10, int(w * self.scale_factor)) new_h = max(10, int(h * self.scale_factor)) img = cv2.resize(img, (new_w, new_h), interpolation=cv2.INTER_AREA) # 转灰度并归一化成uint8 gray = self.to_grayscale(img) if gray.dtype != np.uint8: gray = cv2.normalize(gray, None, 0, 255, cv2.NORM_MINMAX) gray = gray.astype(np.uint8) # 首张图确定统一尺寸 if self.fixed_size is None: self.fixed_size = gray.shape[::-1] # 统一尺寸,resize成fixed_size if (gray.shape[1], gray.shape[0]) != self.fixed_size: gray = cv2.resize(gray, self.fixed_size, interpolation=cv2.INTER_AREA) self.current_image = gray # 更新缓冲 self.frame_buffer.append(gray) # 缓冲不够补齐 while len(self.frame_buffer) < self.buffer_size: self.frame_buffer.append(gray) self.process_image() def to_grayscale(self, img): if len(img.shape) == 3: return cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) return img def gamma_correction(self, image, gamma=1.0): if gamma <= 0: gamma = 1.0 inv_gamma = 1.0 / gamma table = np.array([((i / 255.0) ** inv_gamma) * 255 for i in range(256)]).astype(np.uint8) if image.dtype != np.uint8: image = cv2.normalize(image, None, 0, 255, cv2.NORM_MINMAX) image = image.astype(np.uint8) return cv2.LUT(image, table) def contrast_stretch(self, image, min_clip, max_clip): max_range = 255 min_val = np.percentile(image, min_clip * 100) max_val = np.percentile(image, max_clip * 100) result = np.zeros_like(image, dtype=float) mask_low = image < min_val mask_high = image > max_val mask_mid = (image >= min_val) & (image <= max_val) result[mask_low] = 0 result[mask_high] = max_range if max_val > min_val: normalized = (image[mask_mid] - min_val) / (max_val - min_val) result[mask_mid] = normalized * max_range else: result[mask_mid] = max_range / 2 return result.clip(0, 255).astype(np.uint8) def process_image(self): if len(self.frame_buffer) < self.buffer_size: return frames = list(self.frame_buffer) frames_uint8 = [] for f in frames: if f.dtype != np.uint8: nf = cv2.normalize(f, None, 0, 255, cv2.NORM_MINMAX).astype(np.uint8) frames_uint8.append(nf) else: frames_uint8.append(f) # 多帧去噪 denoised = cv2.fastNlMeansDenoisingMulti( frames_uint8, imgToDenoiseIndex=self.buffer_size // 2, temporalWindowSize=self.buffer_size, h=self.nlm_h.get(), templateWindowSize=3, searchWindowSize=11 ) # Gamma校正 gamma_corrected = self.gamma_correction(denoised, self.gamma_value.get()) # 亮度裁剪 + 对比度拉伸 stretched = self.contrast_stretch( gamma_corrected, self.min_clip.get(), self.max_clip.get() ) # 中值滤波,去噪平滑 filtered = cv2.medianBlur(stretched, 3) # CLAHE局部对比度增强 clahe = cv2.createCLAHE(clipLimit=4.0, tileGridSize=(4, 4)) enhanced = clahe.apply(filtered) # Otsu二值化,用增强后的图像 _, binary = cv2.threshold(enhanced, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU) # 开运算去噪 kernel_open = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (7, 7)) opened = cv2.morphologyEx(binary, cv2.MORPH_OPEN, kernel_open) # 闭运算补洞 kernel_close = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (3, 3)) closed = cv2.morphologyEx(opened, cv2.MORPH_CLOSE, kernel_close) # 找最大轮廓 contours, _ = cv2.findContours(closed, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) if not contours: messagebox.showwarning("警告", "未检测到任何轮廓") return largest_contour = max(contours, key=cv2.contourArea) # 生成掩膜 mask = np.zeros_like(stretched, dtype=np.uint8) cv2.drawContours(mask, [largest_contour], -1, 255, thickness=-1) # 连通域面积筛选 contours_mask, _ = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) mask_filtered = np.zeros_like(mask) area_threshold = 5000 # 可根据目标大小调整 for cnt in contours_mask: if cv2.contourArea(cnt) > area_threshold: cv2.drawContours(mask_filtered, [cnt], -1, 255, thickness=-1) mask = mask_filtered kernel_dilate = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5, 5)) mask = cv2.dilate(mask, kernel_dilate, iterations=1) # 合成结果,掩膜区域用对比度拉伸后的图像,掩膜外设为0 result = denoised.copy() result[mask == 255] = stretched[mask == 255] result[mask == 0] = 0 # 彩色显示,绘制白色轮廓线 result_color = cv2.cvtColor(result, cv2.COLOR_GRAY2BGR) cv2.drawContours(result_color, [largest_contour], -1, (255, 255, 255), 2) self.processed_image = result_color self.display_image(self.processed_image, self.canvas_processed) def display_image(self, image, canvas): img = image # 16位转换8位 if img.dtype == np.uint16: img = (img / 256).astype(np.uint8) if len(img.shape) == 2: display_img = cv2.cvtColor(img, cv2.COLOR_GRAY2RGB) elif len(img.shape) == 3 and img.shape[2] == 3: display_img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) else: gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) display_img = cv2.cvtColor(gray, cv2.COLOR_GRAY2RGB) img_pil = Image.fromarray(display_img) canvas_width = canvas.winfo_width() or 600 canvas_height = canvas.winfo_height() or 600 img_width, img_height = img_pil.size ratio = min(canvas_width / img_width, canvas_height / img_height) if ratio < 1: new_size = (int(img_width * ratio), int(img_height * ratio)) img_pil = img_pil.resize(new_size, Image.LANCZOS) img_tk = ImageTk.PhotoImage(img_pil) canvas.delete("all") canvas.create_image(canvas_width // 2, canvas_height // 2, anchor=tk.CENTER, image=img_tk) canvas.image = img_tk def toggle_play(self): if not self.image_files: messagebox.showwarning("警告", "请先选择包含图片的文件夹") return if self.is_playing: self.stop_play() self.play_button.config(text="开始轮播") else: self.play_delay = self.speed_var.get() self.is_playing = True self.play_button.config(text="停止轮播") self.play_next() def stop_play(self): self.is_playing = False if self.play_timer: self.root.after_cancel(self.play_timer) self.play_timer = None def play_next(self): if not self.is_playing or not self.image_files: return self.current_index = (self.current_index + 1) % len(self.image_files) filename = self.image_files[self.current_index] self.image_combobox.set(filename) self.load_and_buffer_image(filename) self.display_image(self.current_image, self.canvas_original) self.process_image() self.play_timer = self.root.after(self.play_delay, self.play_next) def next_image(self): if not self.image_files: return self.current_index = (self.current_index + 1) % len(self.image_files) filename = self.image_files[self.current_index] self.image_combobox.set(filename) self.load_and_buffer_image(filename) self.display_image(self.current_image, self.canvas_original) self.process_image() def prev_image(self): if not self.image_files: return self.current_index = (self.current_index - 1) % len(self.image_files) filename = self.image_files[self.current_index] self.image_combobox.set(filename) self.load_and_buffer_image(filename) self.display_image(self.current_image, self.canvas_original) self.process_image() if __name__ == "__main__": root = tk.Tk() app = FastImageBrowser(root) root.mainloop() 怎么改
07-09
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值