-
-
Save bczhc/ab52f4f33ccdb6cd579f00fb2e1b0566 to your computer and use it in GitHub Desktop.
用ChatGPT写的垃圾性能图片查看器,在标题栏显示ev100值 #vibecoding #photography #image
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import os | |
| import sys | |
| import tkinter as tk | |
| from tkinter import Label, messagebox | |
| from PIL import Image, ImageTk | |
| import piexif | |
| class ImageViewer: | |
| def _on_mousewheel(self, event): | |
| # Windows/macOS: event.delta positive = up, negative = down | |
| # Linux: Button-4 = up, Button-5 = down | |
| delta = 0 | |
| if hasattr(event, 'delta') and event.delta != 0: | |
| delta = event.delta | |
| else: | |
| if event.num == 4: | |
| delta = 1 | |
| elif event.num == 5: | |
| delta = -1 | |
| if delta > 0: | |
| self.prev_image() | |
| else: | |
| self.next_image() | |
| def __init__(self, root, target_path): | |
| self.root = root | |
| self.target_path = target_path | |
| # Gather files (non-recursive), sort them, then decide which one to show | |
| if os.path.isdir(target_path): | |
| folder = target_path | |
| files = [f for f in os.listdir(folder) if f.lower().endswith('.jpg')] | |
| files.sort() | |
| if not files: | |
| messagebox.showerror("错误", f"目录 {folder} 中没有找到 jpg 文件") | |
| root.destroy() | |
| return | |
| self.files = files | |
| self.folder = folder | |
| self.index = 0 # 目录输入时显示排序后的第一个 | |
| else: | |
| folder = os.path.dirname(target_path) or '.' | |
| files = [f for f in os.listdir(folder) if f.lower().endswith('.jpg')] | |
| files.sort() | |
| if not files: | |
| messagebox.showerror("错误", f"{folder} 中没有找到 jpg 文件") | |
| root.destroy() | |
| return | |
| basename = os.path.basename(target_path) | |
| if basename not in files: | |
| # 传入的文件不是 jpg 或不在同一目录的文件列表内 | |
| messagebox.showwarning("警告", f"指定文件 {basename} 未在目录 {folder} 的 jpg 列表中,默认使用第一个文件") | |
| self.index = 0 | |
| else: | |
| self.index = files.index(basename) | |
| self.files = files | |
| self.folder = folder | |
| # 窗口尺寸为屏幕大小的一半 | |
| screen_w = root.winfo_screenwidth() | |
| screen_h = root.winfo_screenheight() | |
| root.geometry(f"{screen_w//2}x{screen_h//2}") | |
| self.label = Label(root) | |
| self.label.pack(fill=tk.BOTH, expand=True) | |
| root.bind('<Left>', lambda e: self.prev_image()) | |
| root.bind('<Right>', lambda e: self.next_image()) | |
| root.bind('<Configure>', lambda e: self._on_configure()) | |
| root.bind('q', lambda e: root.destroy()) | |
| root.bind('<MouseWheel>', lambda e: self._on_mousewheel(e)) # Windows/macOS | |
| root.bind('<Button-4>', lambda e: self._on_mousewheel(e)) # Linux scroll up | |
| root.bind('<Button-5>', lambda e: self._on_mousewheel(e)) # Linux scroll down | |
| self.original_img = None | |
| self.exif = {} | |
| self.tk_img = None | |
| self.show_image() | |
| def load_image(self): | |
| path = os.path.join(self.folder, self.files[self.index]) | |
| try: | |
| self.original_img = Image.open(path) | |
| try: | |
| self.exif = piexif.load(path) | |
| except Exception: | |
| self.exif = {} | |
| except Exception as e: | |
| messagebox.showerror("错误", f"不能打开图片: {path} {e}") | |
| self.original_img = None | |
| self.exif = {} | |
| except Exception as e: | |
| messagebox.showerror("错误", f"不能打开图片: {path}\n{e}") | |
| self.original_img = None | |
| self.exif = {} | |
| def compute_ev100(self): | |
| try: | |
| if not self.exif or "Exif" not in self.exif: | |
| return "N/A" | |
| ex = self.exif["Exif"] | |
| def to_float(v): | |
| try: | |
| if isinstance(v, tuple) and len(v) == 2: | |
| num, den = v | |
| return num / den if den else None | |
| return float(v) | |
| except: | |
| return None | |
| t = to_float(ex.get(piexif.ExifIFD.ExposureTime)) | |
| n = to_float(ex.get(piexif.ExifIFD.FNumber)) | |
| iso = ex.get(piexif.ExifIFD.ISOSpeedRatings) | |
| if isinstance(iso, (list, tuple)): | |
| iso = iso[0] | |
| try: | |
| iso = float(iso) | |
| except: | |
| iso = None | |
| if not t or not n or not iso or t <= 0 or iso <= 0: | |
| return "N/A" | |
| import math | |
| ev100 = math.log2((n * n) / t) - math.log2(iso / 100.0) | |
| return f"{ev100:.2f}" | |
| except Exception: | |
| return "N/A" | |
| # EXIF tags (numeric): 33434 ExposureTime, 33437 FNumber, 34855 ISOSpeedRatings | |
| t = exif.get(33434) | |
| n = exif.get(33437) | |
| iso = exif.get(34855) | |
| # EXIF values sometimes are rational tuples | |
| def to_float(v): | |
| if v is None: | |
| return None | |
| if isinstance(v, tuple) and len(v) == 2 and v[1] != 0: | |
| return float(v[0]) / float(v[1]) | |
| try: | |
| return float(v) | |
| except Exception: | |
| return None | |
| t = to_float(t) | |
| n = to_float(n) | |
| iso = to_float(iso) | |
| if t is None or n is None or iso is None or t <= 0 or iso <= 0: | |
| return "N/A" | |
| import math | |
| ev100 = math.log2((n * n) / t) - math.log2(iso / 100.0) | |
| return f"{ev100:.2f}" | |
| except Exception: | |
| return "N/A" | |
| def update_title(self): | |
| name = self.files[self.index] | |
| ev = self.compute_ev100() | |
| self.root.title(f"{self.index+1}/{len(self.files)} {name} EV100={ev}") | |
| def show_image(self): | |
| self.load_image() | |
| self.update_title() | |
| self.resize_image() | |
| def resize_image(self): | |
| if self.original_img is None: | |
| return | |
| # 获取当前可用显示区域 | |
| w = self.label.winfo_width() | |
| h = self.label.winfo_height() | |
| if w < 10 or h < 10: | |
| return | |
| # 按比例缩放,使图片完全适应区域(letterbox),保持长宽比 | |
| img = self.original_img.copy() | |
| img_ratio = img.width / img.height | |
| area_ratio = w / h | |
| if img_ratio > area_ratio: | |
| # 以宽度为准 | |
| new_w = w | |
| new_h = int(w / img_ratio) | |
| else: | |
| # 以高度为准 | |
| new_h = h | |
| new_w = int(h * img_ratio) | |
| img = img.resize((max(1, new_w), max(1, new_h)), Image.LANCZOS) | |
| self.tk_img = ImageTk.PhotoImage(img) | |
| self.label.config(image=self.tk_img) | |
| def _on_configure(self): | |
| # 窗口大小改变时更新图片显示和标题(标题里也显示当前图片索引) | |
| self.resize_image() | |
| self.update_title() | |
| def prev_image(self): | |
| if not self.files: | |
| return | |
| self.index = (self.index - 1) % len(self.files) | |
| self.show_image() | |
| def next_image(self): | |
| if not self.files: | |
| return | |
| self.index = (self.index + 1) % len(self.files) | |
| self.show_image() | |
| if __name__ == '__main__': | |
| root = tk.Tk() | |
| target_path = sys.argv[1] if len(sys.argv) > 1 else '.' | |
| viewer = ImageViewer(root, target_path) | |
| # 如果初始化失败(例如没有图片),ImageViewer 会调用 root.destroy() | |
| try: | |
| root.mainloop() | |
| except Exception: | |
| pass |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment