以鼠标位置进行图像非中心zoom
#coding=utf-8 # Reference # tkinter的Label控件以及三种布局管理方法 # https://www.cnblogs.com/jackie-lee/p/16191662.html # python 对话框图形界面显示图片 # https://blog.csdn.net/SAPmatinal/article/details/131818285 # 菜单设置 # https://blog.csdn.net/weixin_42272768/article/details/100808828 # tk布局设置 # https://www.cnblogs.com/peachh/p/16776734.html #opencv PIL格式互相转换 # https://blog.csdn.net/qq_19707521/article/details/78367617 #Pyinstaller 打包exe后,打开出现can‘t find package tkdnd 报错 #https://blog.csdn.net/qq_43079386/article/details/139725149 #十六进制颜色取色 google #https://www.sioe.cn/yingyong/yanse-rgb-16/ color convert #1 BUILD # pyinstaller -F --windowed oplus_imageview.py --paths "C:\Program Files\Python311\lib\site-packages" --add-data "C:\Program Files\Python311\lib\site-packages\tkinterdnd2;tkinterdnd2" --hidden-import=tkinterdnd2 --hidden-import=tkdnd --clean # 2 DEBUG # 如果在打包过程中遇到问题,可以将输出重定向到一个日志文件中,以便详细检查: # pyinstaller -F ccc.py --paths C:\Program Files\Python311\lib\site-packages --add-data "C:\Program Files\Python311\lib\site-packages\tkinterdnd2;tkinterdnd2" --hidden-import=tkinterdnd2 --hidden-import=tkdnd --clean > build_log.txt 2>&1 # 这样所有输出(包括错误信息)都会保存到 build_log.txt 文件中,你可以查看该文件以获取详细的调试信息。 import tkinter as tk import tkinter.filedialog from tkinter import messagebox from tkinter import font from tkinterdnd2 import DND_FILES, TkinterDnD from PIL import Image, ImageTk import cv2 import numpy as np import os import re import time import shutil def timebegin(): # 记录开始时间 # global start_time start_time = time.time() return start_time def timeend(code_blokc, start_time): # 记录结束时间 # global end_time end_time = time.time() # 计算耗时(单位:秒) elapsed_time = end_time - start_time # print(code_blokc + f" 代码块耗时: {elapsed_time} 秒") class Rect: def __init__(self, x = 0, y = 0, width = 0, height = 0): self.x = x self.y = y self.width = width self.height = height class Point: def __init__(self, x = 0, y = 0): self.x = x self.y = y SDK_VERSION = "1.0.0" image_show_width = 1024 image_show_height = 768 window_width = image_show_width window_height = image_show_height+130 check10bit_width_thresh = 5800 suport_image_format = ('.yuv', '.gray16', 'gray','.png', '.jpg', '.jpeg', '.gif', '.bmp') #打开二进制buffer gray8 gray16, 需要输入buffer 宽度高度 def cv_imread_gray(file_path, image_bit, width8, height8): if(image_bit == "16bit"): databuf16 = np.fromfile(file_path, dtype=np.uint16) databuf8 = (databuf16 / 4).astype(np.uint8)#取低8bit数据 cv_img = databuf8.reshape(height8, width8) elif(image_bit == "8bit"): databuf8 = np.fromfile(file_path, dtype=np.uint8) cv_img = databuf8.reshape((height8, width8)) else: cv_img = None return cv_img #打开普通图像,如png jpeg 自动解码 def cv_imread(file_path, image_bit): if(image_bit == "8bit"): cv_img = cv2.imdecode(np.fromfile(file_path,dtype=np.uint8), -1) else: cv_img = None return cv_img #NV21 8bit转为BGR def NV21_P8_CONVERT_BGR(yuv_path,width,height): with open(yuv_path, 'rb') as f: yuvdata = np.fromfile(f, dtype=np.uint8) cv_format=cv2.COLOR_YUV2BGR_NV21 bgr_img = cv2.cvtColor(yuvdata.reshape(((int)(height*3/2), width)), cv_format) return bgr_img #NV12 10bit转为BGR def NV12_P010_CONVERT_BGR(file_path, width10, height10): width8 = int(width10 / 2) height8 = height10 databuf10 = np.fromfile(file_path, dtype=np.uint16) databuf8 = (databuf10 / 4).astype(np.uint8)#取低8bit数据 cv_format=cv2.COLOR_YUV2BGR_NV12 bgr_img = cv2.cvtColor(databuf8.reshape(((int)(height8*3/2), width8)), cv_format) return bgr_img def find_number_x_number(s): # 定义正则表达式模式 # pattern = r'\b(\d+)x(\d+)\b' # input_string = "IMG20240926193220_wh=4096x3072_ss=4096x3072_idx0_beforebeauty_doBokehEffect" pattern = r'ss\=(\d+)x(\d+)' #find _ss=4096x3072 # 使用 re.findall() 查找所有匹配的内容 matches = re.findall(pattern, s) if len(matches) == 0: # input_string = "IMG20240926193220_4096x3072_idx0_beforebeauty_doBokehEffect" pattern = r'\_(\d+)x(\d+)' #find _4096x3072 # 使用 re.findall() 查找所有匹配的内容 matches = re.findall(pattern, s) print(matches) return matches def find_yuvimage_size(input_string): results = find_number_x_number(input_string) width = -1 height = -1 for match in results: # print(f"{match[0]}x{match[1]}") width = int(match[0]) height = int(match[1]) return width, height def findAllfile_slow(path, allfile): filelist = os.listdir(path) for filename in filelist: filepath = os.path.join(path, filename) # print(filepath) if os.path.isdir(filepath): # print(filepath) findAllfile(filepath, allfile) else: # allfile.append(filepath) # allfile = allfile + "/" + filepath allfile.append(filename) return allfile def findAllfile_signlefolder(path, allfile): filelist = os.listdir(path) for filename in filelist: allfile.append(filename) return allfile def GetStringFileFormat(image_path): format = "." + image_path.split('.')[-1] return format def GetStringFileImageId(image_path): ImageId = image_path.split('_')[0] return ImageId def close_main_if_no_child(root, child): def on_child_close(): if not child.winfo_exists(): root.destroy() return on_child_close class ImageDropApp: def __init__(self, root): self.root = root self.root.title("图片查看器--IMAGE_VIEW_" + SDK_VERSION) self.root.geometry(f"{window_width}x{window_height}+800+50") self.image_has_open = False self.show_image_PIL_SRC = None self.show_image_PIL_small = None self.last_save_imgpath = None # self.save_batchfile_choosefolder() # #创建一个frame窗体对象,用来包裹标签 # self.frame = tk.Frame(self.root, relief=tk.SUNKEN, borderwidth=2, width=450, height=250) # self.frame.place(x=0, y=0, relheight=1.0, relwidth=1.0, bordermode = 'outside') # 创建标签用于显示图片像素坐标和像素值 self.label_imgpx = tk.Label(self.root, text="图像坐标与像素值:", width=image_show_width, height=20, bg="lightgrey", anchor = "nw") self.label_imgpx.place(x=0, y=0, width=image_show_width, height=20, bordermode = 'outside') # 创建标签用于显示图片 self.label_imgshow = tk.Label(self.root, text="拖拽图片到这里", width=image_show_width, height=image_show_height, bg="lightgrey") self.label_imgshow.place(relx = 0.0, rely=1.0, in_ = self.label_imgpx,\ width=image_show_width, height=image_show_height, bordermode = 'outside') # 注册拖放功能 self.label_imgshow.drop_target_register(DND_FILES) self.label_imgshow.dnd_bind('<<Drop>>', self.on_drop) self.text_box = tk.Text(self.root, height=8, width=200) # 设置文本框的高度和宽度 self.text_box.place(relx = 0.0, rely=1.0, in_ = self.label_imgshow, width=image_show_width, height=100, bordermode = 'outside') # 将 Text 控件设置为只读 # self.text_box.config(state=tk.DISABLED) #image zoom self.imgAdapting_display = self.show_image_PIL_SRC self.imgZoom = self.imgAdapting_display self.zoom_value = 1.0 self.zoom_value_before = self.zoom_value self.showRect = Rect(0,0,image_show_width,image_show_height) self.showRect_xshift = 0 self.showRect_yshift = 0 self.Mouse_leftClik_pix = Point(0,0) self.Mouse_leftRelease_pix = Point(image_show_width-1,image_show_height-1) # 绑定鼠标左键单击事件到按钮控件上 self.label_imgshow.bind("<Button-1>", self.on_mouse_left_click) self.label_imgshow.bind("<ButtonRelease-1>", self.on_mouse_left_Release) self.label_imgshow.bind("<MouseWheel>", self.image_show_zoom) # self.label_imgshow.bind("<Motion>", self.show_coordinates_zoom) self.label_imgshow.bind("<Motion>", self.show_coordinates) #设置打开关闭菜单 menubar = tk.Menu(root) filemenu=tk.Menu(menubar,activebackground='blue',tearoff=False) filemenu.add_command(label='打开文件',command=self.open_file) filemenu.add_command(label='另存文件',command=self.save_file_choosefolder) filemenu.add_command(label='批量另存YUV文件',command=self.save_batchfile_choosefolder) menubar.add_cascade(label='文件', menu=filemenu) menubar.add_command(label='退出',command=root.destroy) menubar.add_command(label='使用说明',command=self.show_guide) root.bind('<Control-o>',self.open_file) root.config(menu=menubar) def show_guide(self): dialog = tk.Toplevel() dialog.geometry(f"{500}x{350}+810+55") dialog.title("使用说明") def open_link(event): import webbrowser webbrowser.open("https://odocs.myoas.com/docs/1lq7Mgb0XgS5O6Ae") texts = [ "version " + SDK_VERSION, "please reference " ] text_box = tk.Text(dialog) text_box.place(x=0, y=0, relwidth=1.0, relheight=0.12, bordermode = 'outside') for text in texts: text_box.insert(tk.END, text + '\n') text_box.see(tk.END) # dialog.update() text_widget = tk.Text(dialog) text_widget.place(relx = 0.0, rely=1.0, in_ = text_box, relwidth=1.0, relheight=1.0,bordermode = 'outside') # 设置超链接文本 hyperlink_text = "https://odocs.myoas.com/docs/1lq7Mgb0XgS5O6Ae" text_widget.insert(tk.END, hyperlink_text) # 设置超链接的字体样式和颜色 hyperlink_font = font.Font(text_widget, text_widget.cget("font")) hyperlink_font.configure(underline=True) text_widget.tag_configure("hyperlink", font=hyperlink_font, foreground="blue") text_widget.tag_add("hyperlink", "1.0", "1.end") # 绑定超链接的点击事件 text_widget.tag_bind("hyperlink", "<Button-1>", open_link) # 设置鼠标停留显示手势 def show_hand_cursor(event): text_widget.config(cursor="hand2") def hide_hand_cursor(event): text_widget.config(cursor="") text_widget.tag_bind("hyperlink", "<Enter>", show_hand_cursor) text_widget.tag_bind("hyperlink", "<Leave>", hide_hand_cursor) dialog.update() #image zoom BEGIN.......................... def on_mouse_left_click(self, event): # print("on_mouse_left_click at:", event.x, event.y) self.Mouse_leftClik_pix.x = event.x self.Mouse_leftClik_pix.y = event.y # self.label_imgpx.config(text=f"on_mouse_left_click: ({self.zoom_value}, {self.Mouse_leftClik_pix.x}, {self.Mouse_leftClik_pix.y})") def on_mouse_left_Release(self, event): if self.imgZoom is not None: # print("on_mouse_left_Release at:", event.x, event.y) self.Mouse_leftRelease_pix.x = event.x self.Mouse_leftRelease_pix.y = event.y # self.label_imgpx.config(text=f"on_mouse_left_Release: ({self.zoom_value}, {self.Mouse_leftRelease_pix.x}, {self.Mouse_leftRelease_pix.y})") #imgZoom的图像比原始图像大,从imgZoom的图像中crop一块区域进行显示,crop的大小为显示控件的size self.showRect_xshift = self.Mouse_leftRelease_pix.x - self.Mouse_leftClik_pix.x self.showRect_yshift = self.Mouse_leftRelease_pix.y - self.Mouse_leftClik_pix.y print("on_mouse_left_Release showRect before ", self.showRect.x, self.showRect.y) if(self.showRect.x - self.showRect_xshift < 0): self.showRect.x = 0 else: self.showRect.x = self.showRect.x - self.showRect_xshift if(self.showRect.y - self.showRect_yshift < 0): self.showRect.y = 0 else: self.showRect.y = self.showRect.y - self.showRect_yshift if(self.showRect.x + self.showRect.width - 1 > self.imgZoom.size[0] - 1 ): self.showRect.x = self.imgZoom.size[0] - self.showRect.width if(self.showRect.y + self.showRect.height - 1 > self.imgZoom.size[1] - 1 ): self.showRect.y = self.imgZoom.size[1] - self.showRect.height print("on_mouse_left_Release showRect after ", self.showRect.x, self.showRect.y) left = self.showRect.x top = self.showRect.y width = self.showRect.width height = self.showRect.height crop_rect = (left, top, left + width, top + height) self.cropped_show_image = self.imgZoom.crop(crop_rect) self.show_lable_img() def image_show_zoom(self, event): # 1 imgAdapting_display用于控件显示的1x倍率图像,FOV为原始FOV, 大小与控件大小一致 # 2 imgZoom是根据当前zoom_value,缩放的图像,FOV为原始FOV # 3 cropped_show_image 图像大小控件大小一致, FOV不确定 start_time = timebegin() # 获取鼠标位置或其他指定的缩放触发点位置 ptMouse = Point(event.x, event.y) print("ptMouse ", ptMouse.x, ptMouse.y) scalestep = 1.21 self.zoom_value_before = self.zoom_value if (event.delta > 0):#放大图片 if (self.zoom_value > 10): return else: self.zoom_value = self.zoom_value * scalestep; elif (event.delta < 0): #缩小图片 self.zoom_value /= scalestep if(self.zoom_value < 1.0): self.zoom_value = 1.0 # print("zoom_value ", self.zoom_value) zoom_width = int(image_show_width * self.zoom_value) zoom_height = int(image_show_height * self.zoom_value) timeend("mgAdapting_display.resize pre", start_time) start_time = timebegin() self.imgZoom = self.imgAdapting_display.resize((zoom_width, zoom_height), Image.LANCZOS) timeend("mgAdapting_display.resize", start_time) start_time = timebegin() print("zoomfun showRect before ", (int)(self.showRect.x), (int)(self.showRect.y)) print("zoomfun self.zoom_value_before ", self.zoom_value_before) print("zoomfun self.zoom_value ", self.zoom_value ) #保持图像放大后,鼠标所指向的像素位置与原始像素位置一致 self.showRect.x = (self.zoom_value / self.zoom_value_before)*(self.showRect.x + ptMouse.x) - ptMouse.x self.showRect.y = (self.zoom_value / self.zoom_value_before)*(self.showRect.y + ptMouse.y) - ptMouse.y print("zoomfun showRect after ", (int)(self.showRect.x), (int)(self.showRect.y)) self.zoom_value_before = self.zoom_value self.showRect.width = image_show_width self.showRect.height = image_show_height if self.showRect.x < 0: self.showRect.x = 0 if self.showRect.y < 0: self.showRect.y = 0 if(self.showRect.x + self.showRect.width - 1 > self.imgZoom.size[0] - 1 ): self.showRect.x = self.imgZoom.size[0] - self.showRect.width if(self.showRect.y + self.showRect.height - 1 > self.imgZoom.size[1] - 1 ): self.showRect.y = self.imgZoom.size[1] - self.showRect.height self.showRect.x = int(self.showRect.x) self.showRect.y = int(self.showRect.y) left = self.showRect.x top = self.showRect.y width = self.showRect.width height = self.showRect.height crop_rect = (left, top, left + width, top + height) self.cropped_show_image = self.imgZoom.crop(crop_rect) timeend("cropped_show_image ", start_time) self.show_coordinates(event) self.show_lable_img() def show_lable_img(self): start_time = timebegin() # 更新Label中的图像 self.photo_image = ImageTk.PhotoImage(self.cropped_show_image) self.label_imgshow.config(image = self.photo_image) self.label_imgshow.image = self.photo_image # self.label_imgshow.bind("<Motion>", self.show_coordinates_zoom) timeend("更新Label中的图像 ", start_time) def show_coordinates(self, event): if self.show_image_PIL_SRC is not None: #get mouse pix ptMouse = Point(event.x, event.y) #convert mouse pix To self.imgZoom pix ptZoom = Point(self.showRect.x + ptMouse.x, self.showRect.y + ptMouse.y) #convert self.imgZoom pix to SRC img scalex = self.show_image_PIL_SRC.width / self.imgZoom.width scaley = self.show_image_PIL_SRC.height / self.imgZoom.height x = int(ptZoom.x * scalex) y = int(ptZoom.y * scaley) zoomvalue = (int) (self.zoom_value * 100) if x < self.show_image_PIL_SRC.width and y < self.show_image_PIL_SRC.height: # 获取像素值 pixel_value = self.show_image_PIL_SRC.getpixel((x, y)) self.label_imgpx.config(text=f"Pixel zoom: ({zoomvalue}%)- Pixel Coordinates: ({x}, {y}) - Pixel RGB_value: {pixel_value}") #image zoom END.......................... def winshow_image_PIL_SRC(self): self.show_image_PIL_small = self.show_image_PIL_SRC.resize((image_show_width, image_show_height))#, Image.ANTIALIAS) # 将图片转换为Tkinter格式 photo = ImageTk.PhotoImage(self.show_image_PIL_small) # 更新标签内容以显示图片 self.label_imgshow.config(image=photo) self.label_imgshow.image = photo # 保持引用以避免被垃圾回收 self.image_has_open = True self.imgAdapting_display = self.show_image_PIL_small self.imgZoom = self.imgAdapting_display def winshow_image(self, image_path): self.current_image_path = image_path self.image_format = "." + image_path.split('.')[-1] if self.image_format == ".yuv": #fine image size self.image_srcwidth, self.image_srcheight = find_yuvimage_size(image_path) print("image src size " + str(self.image_srcwidth) + "x" + str(self.image_srcheight)) self.image_channels = 3 print("image src channels " + str(self.image_channels)) if self.image_srcwidth == -1: messagebox.showinfo("prompt","not find yuv image size") return None #存储opencv原图 if self.image_srcwidth > check10bit_width_thresh: self.image_bit = "10bit" print("input p010 yuv ") self.save_image_cv = NV12_P010_CONVERT_BGR(self.current_image_path, self.image_srcwidth, self.image_srcheight) self.image_srcwidth = int(self.image_srcwidth / 2) print("image src size p010_TO_p8 " + str(self.image_srcwidth) + "x" + str(self.image_srcheight)) else: self.image_bit = "8bit" self.save_image_cv = NV21_P8_CONVERT_BGR(self.current_image_path, self.image_srcwidth, self.image_srcheight) #opencv图像转换为PTL图像进行显示 self.show_image_PIL_SRC = Image.fromarray(cv2.cvtColor(self.save_image_cv,cv2.COLOR_BGR2RGB)) self.winshow_image_PIL_SRC() elif self.image_format == ".gray16": self.image_bit = "16bit" #fine image size self.image_srcwidth, self.image_srcheight = find_yuvimage_size(image_path) if self.image_srcwidth == -1: messagebox.showinfo("prompt","not find yuv image size") return None self.image_srcwidth = int(self.image_srcwidth / 2) #存储opencv原图 self.save_image_cv = cv_imread_gray(image_path, self.image_bit, self.image_srcwidth, self.image_srcheight ) if self.save_image_cv is not None: shape_tuple = self.save_image_cv.shape self.image_channels = 1 self.image_srcheight = shape_tuple[0] self.image_srcwidth = shape_tuple[1] print("image src size " + str(self.image_srcwidth) + "x" + str(self.image_srcheight)) print("image src channels " + str(self.image_channels)) else: messagebox.showinfo("prompt","open image failed") return #opencv图像转换为PTL图像进行显示 self.show_image_PIL_SRC = Image.fromarray(self.save_image_cv) self.winshow_image_PIL_SRC() elif self.image_format == ".gray": self.image_bit = "8bit" #fine image size self.image_srcwidth, self.image_srcheight = find_yuvimage_size(image_path) if self.image_srcwidth == -1: messagebox.showinfo("prompt","not find yuv image size") return None #存储opencv原图 self.save_image_cv = cv_imread_gray(image_path, self.image_bit, self.image_srcwidth, self.image_srcheight ) if self.save_image_cv is not None: shape_tuple = self.save_image_cv.shape self.image_channels = 1 self.image_srcheight = shape_tuple[0] self.image_srcwidth = shape_tuple[1] print("image src size " + str(self.image_srcwidth) + "x" + str(self.image_srcheight)) print("image src channels " + str(self.image_channels)) else: messagebox.showinfo("prompt","open image failed") return #opencv图像转换为PTL图像进行显示 self.show_image_PIL_SRC = Image.fromarray(self.save_image_cv) self.winshow_image_PIL_SRC() else: self.image_bit = "8bit" #存储opencv原图 self.save_image_cv = cv_imread(image_path, self.image_bit) if self.save_image_cv is not None: shape_tuple = self.save_image_cv.shape if(len(shape_tuple) == 2): self.image_channels = 1 else: self.image_channels = 3 self.image_srcheight = shape_tuple[0] self.image_srcwidth = shape_tuple[1] print("image src size " + str(self.image_srcwidth) + "x" + str(self.image_srcheight)) print("image src channels " + str(self.image_channels)) else: messagebox.showinfo("prompt","open image failed") return #opencv图像转换为PTL图像进行显示 if self.image_channels == 3: print("image debug") self.show_image_PIL_SRC = Image.fromarray(cv2.cvtColor(self.save_image_cv,cv2.COLOR_BGR2RGB)) else: self.show_image_PIL_SRC = Image.fromarray(self.save_image_cv) self.winshow_image_PIL_SRC() showinfo_text_box = "image_path: " + self.current_image_path + "\n" \ +"image_srcwidth: " + str(self.image_srcwidth) + "\n" \ +"image_srcheight: " + str(self.image_srcheight) + "\n" \ +"image_channels: " + str(self.image_channels) + "\n" \ +"image_format: " + self.image_format + "\n" \ +"image_bit: " + self.image_bit + "\n" \ self.text_box.delete("1.0", tk.END) # 从第一行第一列开始删除到最后 self.text_box.insert(tk.END, showinfo_text_box) def on_drop(self, event): # 获取文件路径 file_path = event.data #如果文件路径出现中文"新建文件夹",file_path会增加{file_path} if(len(file_path)> 2): if file_path[0] == "{" and file_path[-1] == "}": print("chinese path " + file_path) file_path = file_path[1:-1] print("remove {} in path " + file_path) if file_path.endswith(suport_image_format): print("find " + file_path) self.winshow_image(file_path) else: print(file_path + " format not support") messagebox.showinfo("prompt","this file format not support, please choose format like these---" \ + str(suport_image_format)) def open_file(self): file_path = tkinter.filedialog.askopenfilename() if file_path.endswith(suport_image_format): print("find " + file_path) self.winshow_image(file_path) else: print(file_path + " format not support") messagebox.showinfo("prompt","this file format not support, please choose format like these---" \ + str(suport_image_format)) def save_file_choosefolder(self): if self.image_has_open: defaultextension = ".png" init_current_image_name = self.current_image_path.replace(self.image_format, defaultextension) # print("src file " + init_current_image_name) if self.last_save_imgpath == None: print("first save") initialdir = os.path.dirname(self.current_image_path) else: print("not first save") print("last_save_imgpath " + self.last_save_imgpath) initialdir = os.path.dirname(self.last_save_imgpath) print("initialdir " + initialdir) savefolderpath = tkinter.filedialog.asksaveasfilename( title="保存图像", defaultextension=".png", initialfile=os.path.basename(init_current_image_name), initialdir = initialdir, filetypes=(("PNG files", "*.png"), ("JPEG files", "*.jpg"), ("All files", "*.*"))) image_save_format = "." + savefolderpath.split('.')[-1] if savefolderpath: print("save file " + savefolderpath) self.last_save_imgpath = savefolderpath showinfo_text_box = "save image_path: " + savefolderpath + " begin\n" self.text_box.insert(tk.END, showinfo_text_box) # image = cv_imread(self.current_image_path) cv2.imencode(image_save_format, self.save_image_cv)[1].tofile(savefolderpath) showinfo_text_box = "save image_path: " + savefolderpath + " end\n" self.text_box.insert(tk.END, showinfo_text_box) else: print("please open image file") messagebox.showinfo("prompt","please open image file") def save_batchfile_choosefolder(self): dialog = tk.Toplevel(self.root) dialog.geometry(f"{1000}x{800}+810+55") dialog.title("批量转换yuv文件-支持文件格式: 1. 8bit NV21, 2.10bit NV12") filepath_textwidth = 800 filepath_textheigth = 20 imageid_textwidth = 200 imageid_textheight = 20 OBJECT_DIST_SCALE_X = 0.1 OBJECT_DIST_SCALE_Y = 0.2 #创建一个frame窗体对象,用来包裹标签 frame = tk.Frame(dialog, relief=tk.SUNKEN, borderwidth=2, width=450, height=250) # 在水平、垂直方向上填充窗体 # frame.pack(side=tk.TOP, fill=tk.BOTH, expand = True) frame.place(x=0, y=0, relheight=1.0, relwidth=1.0, bordermode = 'outside') # frame.bind("<Configure>", lambda event: update_coordinates(frame, "frame")) label_inputpath = tk.Label(frame, text="input path:",bg='#a85632',fg='black', anchor = 'center') label_inputpath.place(x=0, y=0, width=100, height=20, bordermode = 'outside') entry_inputpath = tk.Entry(frame, width = 150) # entry_inputpath.insert(0, r"D:\Users\Desktop\bug\bokehdump") entry_inputpath.place(relx = 1.0, rely=0, in_ = label_inputpath, width=filepath_textwidth, height=filepath_textheigth, bordermode = 'outside') label_outputpath = tk.Label(frame, text="output path:",bg='#a8a632',fg='black', anchor = 'center') label_outputpath.place(in_ = label_inputpath, relx = 0.0, rely=1.0 + OBJECT_DIST_SCALE_Y, width=100, height=20, bordermode = 'outside') entry_outputpath = tk.Entry(frame, width = 150) entry_outputpath.place(in_ = label_outputpath, relx = 1, rely=0, width=filepath_textwidth, height=filepath_textheigth, bordermode = 'outside') # entry_outputpath.insert(0, r"D:\Users\Desktop\bug\bokehdump_png") label_imageid = tk.Label(frame, text="imageid:",bg='#6da832',fg='black', anchor = 'center') label_imageid.place(relx = 0, rely=1 + OBJECT_DIST_SCALE_Y, in_ = label_outputpath, width=100, height=20, bordermode = 'outside') entry_imageid = tk.Entry(frame, width = 150) entry_imageid.place(relx = 1, rely=0, in_ = label_imageid, width=imageid_textwidth, height=imageid_textheight, bordermode = 'outside') entry_imageid.insert(0, r"default_all_id") label_output_format = tk.Label(frame, text="outputformat:",bg='#326fa8',fg='black', anchor = 'center') label_output_format.place(relx = 0, rely= 1.0 + OBJECT_DIST_SCALE_Y, in_ = label_imageid, width=100, height=20, bordermode = 'outside') var_output_format = tk.StringVar() var_output_format.set(".png") # 默认选中选项png radio1 = tk.Radiobutton(frame, text = "png", variable = var_output_format, value = ".png") radio2 = tk.Radiobutton(frame, text = "jpg", variable = var_output_format, value = ".jpg") radio1.place(relx = 1, rely=0, in_ = label_output_format, bordermode = 'outside') radio2.place(relx = 1, rely=0, in_ = radio1, bordermode = 'outside') def batch_yuv_convetimg(inputpath, outputpath, output_format): image_srcwidth, image_srcheight = find_yuvimage_size(inputpath) print(inputpath + "-image src size " + str(image_srcwidth) + "x" + str(image_srcheight)) if image_srcwidth == -1: messagebox.showinfo("prompt","not find yuv image size") return None save_image_cv = None if image_srcwidth > check10bit_width_thresh: save_image_cv = NV12_P010_CONVERT_BGR(inputpath, image_srcwidth, image_srcheight) image_srcwidth = int(image_srcwidth / 2) print("image src size p010_TO_p8 " + str(image_srcwidth) + "x" + str(image_srcheight)) else: save_image_cv = NV21_P8_CONVERT_BGR(inputpath, image_srcwidth, image_srcheight) print(outputpath) cv2.imencode(output_format, save_image_cv)[1].tofile(outputpath) def convert_yuv_fun(): inputpath = entry_inputpath.get() print(f"inputpath: {inputpath}") outputpath = entry_outputpath.get() print(f"outputpath: {outputpath}") imageid = entry_imageid.get() print(f"图像ID: {imageid}") output_format = var_output_format.get() print(f"图像format: {output_format}") if not os.path.exists(inputpath): messagebox.showinfo("prompt", "inputpath " + inputpath + " not exist!!!") return None if imageid != "default_all_id": outputpath = outputpath + '/' + imageid if not os.path.exists(outputpath): # messagebox.showinfo("prompt", "outputpath " + outputpath + " not exist will mkdir!!!") showinfo_text_box = "outputpath " + outputpath + " not exist will mkdir!!!" text_box.insert(tk.END, showinfo_text_box) text_box.see(tk.END) dialog.update() print(outputpath) os.makedirs(outputpath) # return None text_box.delete("1.0", tk.END) # 从第一行第一列开始删除到最后 filelist = [] filelist = findAllfile_signlefolder(inputpath, filelist)#find all file #find dist imageid filename_list=[] if imageid == "default_all_id": for filesrcname in filelist: file_format = GetStringFileFormat(filesrcname) if file_format.find(".yuv") != -1: filename_list.append(filesrcname) else: for filesrcname in filelist: file_format = GetStringFileFormat(filesrcname) if file_format.find(".yuv") != -1 and GetStringFileImageId(filesrcname) == imageid: filename_list.append(filesrcname) process_num = len(filename_list) index = 1 for filename in filename_list: # time.sleep(0.1) showinfo_text_box = "process: " + str((int)(index / process_num * 100 ))+ "% " + filename + " beign \n" # print(showinfo_text_box) text_box.insert(tk.END, showinfo_text_box) text_box.see(tk.END) dialog.update() file_save = filename.replace(".yuv", output_format) batch_yuv_convetimg(inputpath + "/" + filename, outputpath + "/" +file_save, output_format) showinfo_text_box = "process: " + str((int)(index / process_num * 100 )) + "% " + file_save + " end \n" # print(showinfo_text_box) text_box.insert(tk.END, showinfo_text_box) text_box.see(tk.END) dialog.update() index = index+1 def copydumpfile_fun(): inputpath = entry_inputpath.get() print(f"inputpath: {inputpath}") outputpath = entry_outputpath.get() print(f"outputpath: {outputpath}") imageid = entry_imageid.get() print(f"图像ID: {imageid}") output_format = var_output_format.get() print(f"图像format: {output_format}") if not os.path.exists(inputpath): messagebox.showinfo("prompt", "inputpath " + inputpath + " not exist!!!") return None if imageid != "default_all_id": outputpath = outputpath + '/' + imageid if not os.path.exists(outputpath): # messagebox.showinfo("prompt", "outputpath " + outputpath + " not exist will mkdir!!!") showinfo_text_box = "outputpath " + outputpath + " not exist will mkdir!!!" text_box.insert(tk.END, showinfo_text_box) text_box.see(tk.END) dialog.update() print(outputpath) os.makedirs(outputpath) # return None text_box.delete("1.0", tk.END) # 从第一行第一列开始删除到最后 filelist = [] filelist = findAllfile_signlefolder(inputpath, filelist)#find all file #find dist imageid filename_list=[] if imageid == "default_all_id": for filesrcname in filelist: filename_list.append(filesrcname) else: for filesrcname in filelist: if GetStringFileImageId(filesrcname) == imageid: filename_list.append(filesrcname) process_num = len(filename_list) index = 1 for filename in filename_list: # time.sleep(0.1) showinfo_text_box = "process: " + str((int)(index / process_num * 100 ))+ "% " + filename + " beign \n" # print(showinfo_text_box) text_box.insert(tk.END, showinfo_text_box) text_box.see(tk.END) dialog.update() # file_save = filename.replace(".yuv", output_format) file_save = filename # batch_yuv_convetimg(inputpath + "/" + filename, outputpath + "/" +file_save, output_format) shutil.copyfile(inputpath + "/" + filename, outputpath + "/" +file_save) showinfo_text_box = "process: " + str((int)(index / process_num * 100 )) + "% " + file_save + " end \n" # print(showinfo_text_box) text_box.insert(tk.END, showinfo_text_box) text_box.see(tk.END) dialog.update() index = index+1 # 创建一个 Text 控件 text_box = tk.Text(frame, height=50, width=150) text_box.place(relx=0, rely=1.0 + OBJECT_DIST_SCALE_Y, in_ = label_output_format, bordermode = 'outside') text_box['fg'] = '#D8DEE9' text_box['bg'] = '#303841' convert_yuv_button = tk.Button(frame, text="批量另存yuv", bg='#326fa8',fg='black', command=convert_yuv_fun) convert_yuv_button.place(relx=0, rely=1.0+0.01, in_ = text_box, width=100, height=40, bordermode = 'outside') copydumpfile_button = tk.Button(frame, text="批量拷贝文件", bg='#326fa8',fg='black', command=copydumpfile_fun) copydumpfile_button.place(relx=1, rely=0, in_ = convert_yuv_button, width=100, height=40, bordermode = 'outside') cancel_button = tk.Button(frame, text="关闭",bg='#a8a632',fg='black', command=dialog.destroy) cancel_button.place(relx = 1, rely=0, in_ = copydumpfile_button, width=100, height=40, bordermode = 'outside') if __name__ == "__main__": root = TkinterDnD.Tk() app = ImageDropApp(root) root.mainloop()