--- /dev/null
+# This application is based on:\r
+# https://github.com/foobar167/junkyard/blob/master/zoom_advanced.py\r
+\r
+# -*- coding: utf-8 -*-\r
+# Advanced zoom example. Like in Google Maps.\r
+# It zooms only a tile, but not the whole image. So the zoomed tile occupies\r
+# constant memory and not cram it with a huge resized image for the large zooms.\r
+\r
+import tkinter as tk\r
+from tkinter import ttk\r
+from PIL import Image, ImageTk\r
+import os\r
+import argparse\r
+# sudo apt-get install python3-pil.imagetk\r
+\r
+in_default = r'D:\ML\hentai_ai\dataset_thighs'\r
+out_default = r'D:\ML\hentai_ai\dataset_thighs_cropped'\r
+\r
+parser = argparse.ArgumentParser()\r
+parser.add_argument('-i', '--input', type=str, default=in_default, help="input directory")\r
+parser.add_argument('-o', '--output', type=str, default=out_default, help="output directory")\r
+\r
+\r
+class AutoScrollbar(ttk.Scrollbar):\r
+ def set(self, lo, hi):\r
+ self.grid_remove()\r
+\r
+ def pack(self, **kw):\r
+ raise tk.TclError('Cannot use pack with this widget')\r
+\r
+ def place(self, **kw):\r
+ raise tk.TclError('Cannot use place with this widget')\r
+\r
+\r
+def load_paths(in_path, out_path):\r
+ in_list = []\r
+ out_list = []\r
+ for fname in os.listdir(in_path):\r
+ in_list.append(os.path.join(in_path, fname))\r
+ out_list.append(os.path.join(out_path, fname.split('.')[0] + "-crop.png"))\r
+ return in_list, out_list\r
+\r
+\r
+class ZoomAdvanced(ttk.Frame):\r
+ # Advanced zoom of the image\r
+ def __init__(self, mainframe, input, output):\r
+ # Initialize the main Frame\r
+ ttk.Frame.__init__(self, master=mainframe)\r
+ # Vertical and horizontal scrollbars for canvas\r
+ vbar = AutoScrollbar(self.master, orient='vertical')\r
+ hbar = AutoScrollbar(self.master, orient='horizontal')\r
+ vbar.grid(row=0, column=1, sticky='ns')\r
+ hbar.grid(row=1, column=0, sticky='we')\r
+ # Create canvas and put image on it\r
+ self.canvas = tk.Canvas(self.master, highlightthickness=0,\r
+ xscrollcommand=hbar.set, yscrollcommand=vbar.set)\r
+ self.canvas.grid(row=0, column=0, sticky='nswe')\r
+ self.canvas.update() # wait till canvas is created\r
+ vbar.configure(command=self.scroll_y) # bind scrollbars to the canvas\r
+ hbar.configure(command=self.scroll_x)\r
+ # Make the canvas expandable\r
+ self.master.rowconfigure(0, weight=1)\r
+ self.master.columnconfigure(0, weight=1)\r
+ # Bind events to the Canvas\r
+ self.canvas.bind('<Configure>', self.show_image) # canvas is resized\r
+ self.canvas.bind('<ButtonPress-1>', self.move_from)\r
+ self.canvas.bind('<B1-Motion>', self.move_to)\r
+ self.canvas.bind('<MouseWheel>', self.wheel) # with Windows and MacOS, but not Linux\r
+ self.canvas.bind('<Button-5>', self.wheel) # only with Linux, wheel scroll down\r
+ self.canvas.bind('<Button-4>', self.wheel) # only with Linux, wheel scroll up\r
+ self.canvas.bind_all('<Return>', self.save_img)\r
+\r
+ self.in_paths, self.out_paths = load_paths(input, output)\r
+ self.counter = 0\r
+ self.image = None\r
+ self.destination = None\r
+ self.width = 0\r
+ self.height = 0\r
+ self.imscale = 1.0\r
+ self.delta = 1.1\r
+ self.container = None\r
+ self.img = None\r
+ # open first image\r
+ self.load_img()\r
+ self.show_image()\r
+\r
+ def load_img(self):\r
+ while os.path.exists(self.out_paths[self.counter]):\r
+ self.counter += 1\r
+ self.image = Image.open(self.in_paths[self.counter])\r
+ self.destination = self.out_paths[self.counter]\r
+ print(str(os.path.basename(self.in_paths[self.counter])))\r
+ self.master.title(os.path.basename(self.in_paths[self.counter]))\r
+ self.width, self.height = self.image.size\r
+ self.imscale = 1.0 # scale for the canvas image\r
+ # Put image into container rectangle and use it to set proper coordinates to the image\r
+ self.container = self.canvas.create_rectangle(0, 0, self.width, self.height, width=0)\r
+ self.img = None\r
+\r
+ def save_img(self, event):\r
+ print("Saving cropped image...")\r
+ self.img.save(self.destination)\r
+ self.counter += 1\r
+ if self.counter < len(self.in_paths):\r
+ print("Loading new image...")\r
+ self.load_img()\r
+ self.show_image()\r
+ else:\r
+ print("Exiting...")\r
+ self.master.destroy()\r
+\r
+ def scroll_y(self, *args, **kwargs):\r
+ # Scroll canvas vertically and redraw the image\r
+ self.canvas.yview(*args, **kwargs) # scroll vertically\r
+ self.show_image() # redraw the image\r
+\r
+ def scroll_x(self, *args, **kwargs):\r
+ # Scroll canvas horizontally and redraw the image\r
+ self.canvas.xview(*args, **kwargs) # scroll horizontally\r
+ self.show_image() # redraw the image\r
+\r
+ def move_from(self, event):\r
+ # Remember previous coordinates for scrolling with the mouse\r
+ self.canvas.scan_mark(event.x, event.y)\r
+\r
+ def move_to(self, event):\r
+ # Drag (move) canvas to the new position\r
+ self.canvas.scan_dragto(event.x, event.y, gain=1)\r
+ self.show_image() # redraw the image\r
+\r
+ def wheel(self, event):\r
+ # Zoom with mouse wheel\r
+ x = self.canvas.canvasx(event.x)\r
+ y = self.canvas.canvasy(event.y)\r
+ bbox = self.canvas.bbox(self.container) # get image area\r
+ if bbox[0] < x < bbox[2] and bbox[1] < y < bbox[3]:\r
+ pass # Ok! Inside the image\r
+ else:\r
+ return # zoom only inside image area\r
+ scale = 1.0\r
+ # Respond to Linux (event.num) or Windows (event.delta) wheel event\r
+ if event.num == 5 or event.delta == -120: # scroll down\r
+ i = min(self.width, self.height)\r
+ if int(i * self.imscale) < 30:\r
+ return # image is less than 30 pixels\r
+ self.imscale /= self.delta\r
+ scale /= self.delta\r
+ if event.num == 4 or event.delta == 120: # scroll up\r
+ i = min(self.canvas.winfo_width(), self.canvas.winfo_height())\r
+ if i < self.imscale:\r
+ return # 1 pixel is bigger than the visible area\r
+ self.imscale *= self.delta\r
+ scale *= self.delta\r
+ self.canvas.scale('all', x, y, scale, scale) # rescale all canvas objects\r
+ self.show_image()\r
+\r
+ def show_image(self, event=None):\r
+ # Show image on the Canvas\r
+ bbox1 = self.canvas.bbox(self.container) # get image area\r
+ # Remove 1 pixel shift at the sides of the bbox1\r
+ bbox1 = (bbox1[0] + 1, bbox1[1] + 1, bbox1[2] - 1, bbox1[3] - 1)\r
+ bbox2 = (self.canvas.canvasx(0), # get visible area of the canvas\r
+ self.canvas.canvasy(0),\r
+ self.canvas.canvasx(self.canvas.winfo_width()),\r
+ self.canvas.canvasy(self.canvas.winfo_height()))\r
+ bbox = [min(bbox1[0], bbox2[0]), min(bbox1[1], bbox2[1]), # get scroll region box\r
+ max(bbox1[2], bbox2[2]), max(bbox1[3], bbox2[3])]\r
+ if bbox[0] == bbox2[0] and bbox[2] == bbox2[2]: # whole image in the visible area\r
+ bbox[0] = bbox1[0]\r
+ bbox[2] = bbox1[2]\r
+ if bbox[1] == bbox2[1] and bbox[3] == bbox2[3]: # whole image in the visible area\r
+ bbox[1] = bbox1[1]\r
+ bbox[3] = bbox1[3]\r
+ self.canvas.configure(scrollregion=bbox) # set scroll region\r
+ x1 = max(bbox2[0] - bbox1[0], 0) # get coordinates (x1,y1,x2,y2) of the image tile\r
+ y1 = max(bbox2[1] - bbox1[1], 0)\r
+ x2 = min(bbox2[2], bbox1[2]) - bbox1[0]\r
+ y2 = min(bbox2[3], bbox1[3]) - bbox1[1]\r
+ if int(x2 - x1) > 0 and int(y2 - y1) > 0: # show image if it in the visible area\r
+ x = min(int(x2 / self.imscale), self.width) # sometimes it is larger on 1 pixel...\r
+ y = min(int(y2 / self.imscale), self.height) # ...and sometimes not\r
+ image = self.image.crop((int(x1 / self.imscale), int(y1 / self.imscale), x, y))\r
+ imagetk = ImageTk.PhotoImage(image.resize((int(x2 - x1), int(y2 - y1))))\r
+ imageid = self.canvas.create_image(max(bbox2[0], bbox1[0]), max(bbox2[1], bbox1[1]),\r
+ anchor='nw', image=imagetk)\r
+ self.canvas.lower(imageid) # set image into background\r
+ self.canvas.imagetk = imagetk # keep an extra reference to prevent garbage-collection\r
+ self.img = image.resize((int(x2 - x1), int(y2 - y1)))\r
+\r
+\r
+root = tk.Tk()\r
+root.geometry("500x500")\r
+root.resizable(width=False, height=False)\r
+args = parser.parse_args()\r
+app = ZoomAdvanced(root, args.input, args.output)\r
+root.mainloop()\r