Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- # tk_FFT_RGB_Image_Compressor.py
- from tkinter import *
- from tkinter import filedialog
- from PIL import Image, ImageTk
- import cmath, struct, os, base64
- def fft(x):
- n = len(x)
- if n <= 1: return x
- even = fft(x[0::2]); odd = fft(x[1::2])
- t = [cmath.exp(-2j*cmath.pi*k/n) * odd[k] for k in range(n//2)]
- return [even[k] + t[k] for k in range(n//2)] + [even[k] - t[k] for k in range(n//2)]
- def ifft(x):
- n = len(x)
- conj = [v.conjugate() for v in x]
- y = fft(conj)
- return [v.conjugate()/n for v in y]
- def dft2d(m):
- t = [fft(row) for row in m]
- t2 = list(zip(*t))
- t3 = [fft(list(col)) for col in t2]
- return [list(row) for row in zip(*t3)]
- def idft2d(m):
- t = [ifft(row) for row in m]
- t2 = list(zip(*t))
- t3 = [ifft(list(col)) for col in t2]
- return [list(row) for row in zip(*t3)]
- def compress_fft_sorted(m,f):
- vals = [abs(v) for r in m for v in r]
- vals.sort(reverse=True)
- k = int(len(vals) * f)
- th = vals[k] if k < len(vals) else 0
- return [[v if abs(v) >= th else 0 for v in r] for r in m]
- def compress_image(img):
- w,h = img.size
- r = [[img.getpixel((x,y))[0] for x in range(w)] for y in range(h)]
- g = [[img.getpixel((x,y))[1] for x in range(w)] for y in range(h)]
- b = [[img.getpixel((x,y))[2] for x in range(w)] for y in range(h)]
- return dft2d(r), dft2d(g), dft2d(b)
- def reconstruct(fr,fg,fb,w,h,f):
- fr = compress_fft_sorted(fr,f)
- fg = compress_fft_sorted(fg,f)
- fb = compress_fft_sorted(fb,f)
- rr = idft2d(fr); rg = idft2d(fg); rb = idft2d(fb)
- out = Image.new("RGB",(w,h))
- for y in range(h):
- for x in range(w):
- rv = int(max(0,min(255,abs(rr[y][x]))))
- gv = int(max(0,min(255,abs(rg[y][x]))))
- bv = int(max(0,min(255,abs(rb[y][x]))))
- out.putpixel((x,y),(rv,gv,bv))
- return out
- def save_binary(ms,w,h,p):
- with open(p,"wb") as f:
- f.write(struct.pack("II",w,h))
- for mtx in ms:
- for row in mtx:
- for v in row:
- f.write(struct.pack("ff",v.real,v.imag))
- def load_binary(p):
- with open(p,"rb") as f:
- w,h = struct.unpack("II",f.read(8))
- data = []
- for _ in range(3):
- m = []
- for _ in range(h):
- row = []
- for _ in range(w):
- r,i = struct.unpack("ff",f.read(8))
- row.append(complex(r,i))
- m.append(row)
- data.append(m)
- return w,h,data
- def load_image():
- global fft_data,w,h,folder
- p = filedialog.askopenfilename()
- if not p: return
- folder = os.path.dirname(p)
- img = Image.open(p).convert("RGB")
- fft_data = compress_image(img)
- w,h = img.size
- savepath = os.path.splitext(p)[0] + ".fftbin"
- save_binary(fft_data,w,h,savepath)
- generate_random_filename()
- show_image()
- def load_compression():
- global fft_data,w,h,folder
- p = filedialog.askopenfilename(filetypes=[("FFT Binary","*.fftbin")])
- if not p: return
- folder = os.path.dirname(p)
- w,h,fft_data = load_binary(p)
- generate_random_filename()
- show_image()
- def update_compression():
- global ratio
- if not fft_data: return
- try: ratio = float(entry.get())
- except: ratio = 0.1
- show_image()
- def save_png():
- if not fft_data: return
- out = reconstruct(fft_data[0],fft_data[1],fft_data[2],w,h,ratio)
- p = filedialog.asksaveasfilename(defaultextension=".png")
- if p: out.save(p)
- def generate_random_filename():
- global preview_name
- rand = base64.b64encode(os.urandom(12)).decode("ascii")[:16]
- preview_name = str(h).zfill(4) + str(w).zfill(4) + "_" + rand + ".png"
- filename_label.config(text=preview_name)
- def auto_rename_save():
- if not fft_data or not folder: return
- out = reconstruct(fft_data[0],fft_data[1],fft_data[2],w,h,ratio)
- while True:
- rand = base64.b64encode(os.urandom(12)).decode("ascii")[:16]
- name = str(h).zfill(4) + str(w).zfill(4) + "_" + rand + ".png"
- path = os.path.join(folder,name)
- if not os.path.exists(path):
- out.save(path)
- generate_random_filename()
- break
- def show_image():
- if not fft_data: return
- out = reconstruct(fft_data[0],fft_data[1],fft_data[2],w,h,ratio)
- tkimg = ImageTk.PhotoImage(out)
- canvas.delete("all")
- canvas.image = tkimg
- canvas.create_image(0,0,image=tkimg,anchor=NW)
- canvas.config(scrollregion=(0,0,w,h))
- def start_pan(e): canvas.scan_mark(e.x,e.y)
- def drag_pan(e): canvas.scan_dragto(e.x,e.y,1)
- fft_data = None
- ratio = 0.1
- w = h = 0
- folder = ""
- preview_name = ""
- root = Tk()
- root.title("FFT RGB Image Compressor")
- root.geometry("+0+0")
- main_frame = Frame(root)
- main_frame.pack(fill=BOTH, expand=True)
- control_panel = Frame(main_frame, width=200)
- control_panel.pack(side=LEFT, fill=Y, padx=5, pady=5)
- canvas_frame = Frame(main_frame)
- canvas_frame.pack(side=RIGHT, fill=BOTH, expand=True)
- canvas = Canvas(canvas_frame, bg="black", width=1000, height=600)
- canvas.pack(side=LEFT, fill=BOTH, expand=True)
- scroll_y = Scrollbar(canvas_frame, orient=VERTICAL, command=canvas.yview)
- scroll_y.pack(side=RIGHT, fill=Y)
- canvas.configure(yscrollcommand=scroll_y.set)
- canvas.bind("<ButtonPress-1>", start_pan)
- canvas.bind("<B1-Motion>", drag_pan)
- Button(control_panel,text="Load Image (auto save FFT)",command=load_image).pack(pady=5,fill=X)
- Button(control_panel,text="Load FFT Binary",command=load_compression).pack(pady=5,fill=X)
- Label(control_panel,text="Compression Ratio (0-1):").pack(pady=5)
- entry = Entry(control_panel); entry.insert(0,"0.1"); entry.pack(pady=5,fill=X)
- Button(control_panel,text="Update",command=update_compression).pack(pady=5,fill=X)
- Button(control_panel,text="Save Reconstructed PNG",command=save_png).pack(pady=5,fill=X)
- Button(control_panel,text="Auto Rename + Save",command=auto_rename_save).pack(pady=5,fill=X)
- filename_label = Label(control_panel, text="No File Yet", bg="green", fg="white")
- filename_label.pack(pady=5, fill=X)
- Button(control_panel, text="Rerandomize Filename", command=generate_random_filename).pack(pady=5, fill=X)
- root.mainloop()
Add Comment
Please, Sign In to add comment