Skip to content

Instantly share code, notes, and snippets.

@eros18123
Last active February 23, 2026 20:55
Show Gist options
  • Select an option

  • Save eros18123/3e31601f3632f3ba294498d9a73b9062 to your computer and use it in GitHub Desktop.

Select an option

Save eros18123/3e31601f3632f3ba294498d9a73b9062 to your computer and use it in GitHub Desktop.
kanji pdf 2136 + 1 extra
import fitz # PyMuPDF
import os
import tkinter as tk
from tkinter import messagebox
from PIL import Image, ImageTk
from tqdm import tqdm
import numpy as np
PDF_PATH = "kanji_poster.pdf"
OUTPUT_FOLDER = "kanji_images"
TOTAL_KANJI = 2136
class CalibrationGUI:
def __init__(self, pil_image):
self.root = tk.Tk()
self.root.title("CALIBRAÇÃO KANJI - PRECISÃO TOTAL")
self.points = []
self.pil_image = pil_image
# Lista de Kanjis para clicar (Sem repetir o 1)
self.anchors_to_click = [101, 201, 301, 401, 501, 601, 701, 801, 901, 1001,
1101, 1201, 1301, 1401, 1501, 1601, 1701, 1801, 1901, 2001, 2101]
self.instructions = [
"1. CLIQUE NO CANTO SUPERIOR ESQUERDO DO KANJI 1 (Início de tudo)",
"2. CLIQUE NO CANTO INFERIOR DIREITO DO KANJI 1 (Define o tamanho do card)",
"3. CLIQUE NO CANTO SUPERIOR ESQUERDO DO KANJI 10 (Define largura das colunas)",
"4. CLIQUE NO CANTO SUPERIOR ESQUERDO DO KANJI 91 (Define altura das linhas - Fim da 1ª coluna)"
]
for num in self.anchors_to_click:
self.instructions.append(f"CLIQUE NO CANTO SUPERIOR ESQUERDO DO KANJI {num}")
# Label de Instrução
self.label_inst = tk.Label(self.root, text=self.instructions[0], font=("Arial", 12, "bold"), fg="red", bg="yellow")
self.label_inst.pack(fill=tk.X)
# Frame Principal
frame = tk.Frame(self.root)
frame.pack(fill=tk.BOTH, expand=True)
# Canvas
self.canvas = tk.Canvas(frame, cursor="cross", bg="gray")
self.canvas.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
# Scrollbars
vbar = tk.Scrollbar(frame, orient=tk.VERTICAL, command=self.canvas.yview)
vbar.pack(side=tk.RIGHT, fill=tk.Y)
hbar = tk.Scrollbar(self.root, orient=tk.HORIZONTAL, command=self.canvas.xview)
hbar.pack(side=tk.BOTTOM, fill=tk.X)
self.canvas.config(xscrollcommand=hbar.set, yscrollcommand=vbar.set)
# Imagem
self.tk_img = ImageTk.PhotoImage(pil_image)
self.canvas.create_image(0, 0, image=self.tk_img, anchor="nw")
self.canvas.config(scrollregion=self.canvas.bbox(tk.ALL))
# Lupa (Janela flutuante)
self.lupa_size = 200
self.lupa_window = tk.Toplevel(self.root)
self.lupa_window.overrideredirect(True) # Sem bordas
self.lupa_canvas = tk.Canvas(self.lupa_window, width=self.lupa_size, height=self.lupa_size, highlightthickness=2, highlightbackground="green")
self.lupa_canvas.pack()
# Eventos
self.canvas.bind("<Button-1>", self.on_click)
self.canvas.bind("<Motion>", self.update_lupa)
self.root.bind("<Control-z>", lambda e: self.undo())
def update_lupa(self, event):
x = self.canvas.canvasx(event.x)
y = self.canvas.canvasy(event.y)
# Posiciona a janela da lupa perto do mouse
self.lupa_window.geometry(f"+{self.root.winfo_pointerx() + 20}+{self.root.winfo_pointery() + 20}")
# Recorte para a lupa (Zoom 2x do que já está em 4x)
crop_x = x - 50
crop_y = y - 50
crop = self.pil_image.crop((crop_x, crop_y, crop_x + 100, crop_y + 100))
crop = crop.resize((self.lupa_size, self.lupa_size), Image.NEAREST)
self.tk_lupa = ImageTk.PhotoImage(crop)
self.lupa_canvas.create_image(0, 0, image=self.tk_lupa, anchor="nw")
# Cruz central da lupa
self.lupa_canvas.create_line(100, 0, 100, 200, fill="green")
self.lupa_canvas.create_line(0, 100, 200, 100, fill="green")
def on_click(self, event):
x = self.canvas.canvasx(event.x)
y = self.canvas.canvasy(event.y)
self.points.append((x, y))
idx = len(self.points)
tag = f"pt_{idx}"
self.canvas.create_oval(x-3, y-3, x+3, y+3, fill="red", tags=tag)
self.canvas.create_text(x+10, y+10, text=str(idx), fill="red", font=("Arial", 10, "bold"), tags=tag)
if idx < len(self.instructions):
self.label_inst.config(text=self.instructions[idx])
else:
self.lupa_window.destroy()
self.root.destroy()
def undo(self):
if self.points:
idx = len(self.points)
self.points.pop()
self.canvas.delete(f"pt_{idx}")
self.label_inst.config(text=self.instructions[len(self.points)])
def run(self):
self.root.mainloop()
return self.points
def processar():
if not os.path.exists(OUTPUT_FOLDER): os.makedirs(OUTPUT_FOLDER)
print("Renderizando PDF...")
doc = fitz.open(PDF_PATH)
page = doc[0]
pix = page.get_pixmap(matrix=fitz.Matrix(4, 4))
img_pil = Image.frombytes("RGB", [pix.width, pix.height], pix.samples)
gui = CalibrationGUI(img_pil)
points = gui.run()
if len(points) >= 4:
# 1. Tamanho do card (Baseado no Kanji 1)
x1, y1 = points[0]
x_br, y_br = points[1]
w, h = int(abs(x_br - x1)), int(abs(y_br - y1))
# 2. Passos de grade
step_x = (points[2][0] - points[0][0]) / 9.0
step_y = (points[3][1] - points[0][1]) / 9.0 # Do 1 ao 91 são 9 pulos de linha
# 3. Âncoras (O primeiro ponto é o Kanji 1)
anchors = [points[0]] + points[4:]
print(f"\nIniciando corte. Tamanho: {w}x{h}, StepY: {step_y}")
count = 0
for i in tqdm(range(TOTAL_KANJI), desc="Cortando"):
anchor_idx = i // 100
if anchor_idx >= len(anchors): anchor_idx = len(anchors) - 1
ax, ay = anchors[anchor_idx]
rel_i = i % 100
row = rel_i // 10
col = rel_i % 10
# Cálculo final
cur_x = int(ax + col * step_x)
cur_y = int(ay + row * step_y)
crop = img_pil.crop((cur_x, cur_y, cur_x + w, cur_y + h))
crop.save(os.path.join(OUTPUT_FOLDER, f"{i + 1}.jpg"), quality=95)
count += 1
print(f"\nSucesso! {count} imagens salvas.")
if __name__ == "__main__":
processar()
@eros18123
Copy link
Author

eros18123 commented Jan 23, 2026

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment