|
| 1 | +#TODO: Dividere in moduli, testare scaling algoritmo |
| 2 | + |
| 3 | +import tkinter as tk |
| 4 | +from tkinter import ttk, filedialog, messagebox |
| 5 | +from PIL import Image, ImageOps, ImageTk |
| 6 | +import numpy as np |
| 7 | +import time |
| 8 | +import matplotlib.pyplot as plt |
| 9 | +import os |
| 10 | +from scipy.fftpack import dct, idct |
| 11 | +import threading |
| 12 | +import queue |
| 13 | + |
| 14 | +normalize_coefficients = False |
| 15 | + |
| 16 | +def dct2_manual(matrix): |
| 17 | + N = matrix.shape[0] |
| 18 | + result = np.zeros_like(matrix, dtype=np.float32) |
| 19 | + for u in range(N): |
| 20 | + for v in range(N): |
| 21 | + sum_val = 0 |
| 22 | + for x in range(N): |
| 23 | + for y in range(N): |
| 24 | + sum_val += matrix[x, y] * np.cos(np.pi * u * (2 * x + 1) / (2 * N)) * np.cos(np.pi * v * (2 * y + 1) / (2 * N)) |
| 25 | + alpha_u = np.sqrt(1/N) if u == 0 else np.sqrt(2/N) |
| 26 | + alpha_v = np.sqrt(1/N) if v == 0 else np.sqrt(2/N) |
| 27 | + result[u, v] = alpha_u * alpha_v * sum_val |
| 28 | + return result |
| 29 | + |
| 30 | + |
| 31 | + |
| 32 | +def idct2_manual(matrix): |
| 33 | + N = matrix.shape[0] |
| 34 | + result = np.zeros_like(matrix, dtype=np.float32) |
| 35 | + for x in range(N): |
| 36 | + for y in range(N): |
| 37 | + sum_val = 0 |
| 38 | + for u in range(N): |
| 39 | + for v in range(N): |
| 40 | + alpha_u = np.sqrt(1/N) if u == 0 else np.sqrt(2/N) |
| 41 | + alpha_v = np.sqrt(1/N) if v == 0 else np.sqrt(2/N) |
| 42 | + sum_val += alpha_u * alpha_v * matrix[u, v] * np.cos(np.pi * u * (2 * x + 1) / (2 * N)) * np.cos(np.pi * v * (2 * y + 1) / (2 * N)) |
| 43 | + result[x, y] = sum_val |
| 44 | + return result |
| 45 | + |
| 46 | +def dct2(matrix): |
| 47 | + return dct(dct(matrix.T, norm='ortho').T, norm='ortho') |
| 48 | + |
| 49 | +def idct2(matrix): |
| 50 | + return idct(idct(matrix.T, norm='ortho').T, norm='ortho') |
| 51 | + |
| 52 | +def compare_dct2_algorithms(progress_queue, plot_queue): |
| 53 | + sizes = [8, 16, 32, 64] |
| 54 | + manual_times = [] |
| 55 | + library_times = [] |
| 56 | + |
| 57 | + total_steps = len(sizes) * 2 |
| 58 | + step = 0 |
| 59 | + |
| 60 | + for size in sizes: |
| 61 | + print("size: ", size) |
| 62 | + matrix = np.random.rand(size, size).astype(np.float32) |
| 63 | + |
| 64 | + start_time = time.time() |
| 65 | + dct2_manual(matrix) |
| 66 | + manual_times.append(time.time() - start_time) |
| 67 | + step += 1 |
| 68 | + progress_queue.put((step / total_steps) * 100) |
| 69 | + |
| 70 | + start_time = time.time() |
| 71 | + dct2(matrix) |
| 72 | + library_times.append(time.time() - start_time) |
| 73 | + step += 1 |
| 74 | + progress_queue.put((step / total_steps) * 100) |
| 75 | + |
| 76 | + progress_queue.put("done") |
| 77 | + plot_queue.put((sizes, manual_times, library_times)) |
| 78 | + |
| 79 | +def compare_dct2_algorithms_thread(progress_var, progress_bar): |
| 80 | + progress_queue = queue.Queue() |
| 81 | + plot_queue = queue.Queue() |
| 82 | + threading.Thread(target=compare_dct2_algorithms, args=(progress_queue, plot_queue)).start() |
| 83 | + root.after(100, check_progress, progress_queue, progress_var, progress_bar) |
| 84 | + root.after(100, check_plot, plot_queue) |
| 85 | + |
| 86 | +def check_progress(progress_queue, progress_var, progress_bar): |
| 87 | + try: |
| 88 | + progress = progress_queue.get_nowait() |
| 89 | + if progress == "done": |
| 90 | + return |
| 91 | + progress_var.set(progress) |
| 92 | + progress_bar.update_idletasks() |
| 93 | + except queue.Empty: |
| 94 | + pass |
| 95 | + root.after(100, check_progress, progress_queue, progress_var, progress_bar) |
| 96 | + |
| 97 | +def check_plot(plot_queue): |
| 98 | + try: |
| 99 | + plot_data = plot_queue.get_nowait() |
| 100 | + sizes, manual_times, library_times = plot_data |
| 101 | + |
| 102 | + plt.figure() |
| 103 | + plt.plot(sizes, manual_times, label='Manual DCT2 ', marker='o') |
| 104 | + plt.plot(sizes, library_times, label='Library DCT2', marker='o') |
| 105 | + plt.xlabel('Matrix size (N)') |
| 106 | + plt.ylabel('Time (s)') |
| 107 | + plt.yscale('log') |
| 108 | + plt.legend() |
| 109 | + plt.title('Comparison of DCT2 Algorithms') |
| 110 | + plt.show() |
| 111 | + except queue.Empty: |
| 112 | + root.after(100, check_plot, plot_queue) |
| 113 | + |
| 114 | +def process_image(file_path, F, d): |
| 115 | + try: |
| 116 | + img = Image.open(file_path).convert('L') |
| 117 | + except Exception as e: |
| 118 | + print(f"Error in opening image: {e}") |
| 119 | + return "" |
| 120 | + |
| 121 | + gray_img = ImageOps.grayscale(img) |
| 122 | + matrix = image_to_matrix(gray_img) |
| 123 | + |
| 124 | + compressed_matrix = apply_dct_and_idct(matrix, F, d) |
| 125 | + |
| 126 | + compressed_img = matrix_to_image(compressed_matrix) |
| 127 | + compressed_file_path = "compressed_image.png" |
| 128 | + compressed_img.save(compressed_file_path) |
| 129 | + |
| 130 | + return compressed_file_path |
| 131 | + |
| 132 | +def image_to_matrix(img): |
| 133 | + return np.array(img, dtype=np.float32) |
| 134 | + |
| 135 | +def matrix_to_image(matrix): |
| 136 | + min_val, max_val = matrix.min(), matrix.max() |
| 137 | + normalized_matrix = 255 * (matrix - min_val) / (max_val - min_val) |
| 138 | + return Image.fromarray(np.uint8(normalized_matrix)) |
| 139 | + |
| 140 | +def apply_dct_and_idct(matrix, F, d): |
| 141 | + height, width = matrix.shape |
| 142 | + compressed_matrix = np.zeros_like(matrix) |
| 143 | + |
| 144 | + for y in range(0, height, F): |
| 145 | + for x in range(0, width, F): |
| 146 | + if y + F <= height and x + F <= width: |
| 147 | + block = matrix[y:y+F, x:x+F] |
| 148 | + dct_block = dct2(block) |
| 149 | + filtered_dct_block = filter_frequencies(dct_block, d) |
| 150 | + idct_block = idct2(filtered_dct_block) |
| 151 | + compressed_matrix[y:y+F, x:x+F] = idct_block |
| 152 | + |
| 153 | + return compressed_matrix |
| 154 | + |
| 155 | +def filter_frequencies(matrix, d): |
| 156 | + size = matrix.shape[0] |
| 157 | + for k in range(size): |
| 158 | + for l in range(size): |
| 159 | + if k + l >= d: |
| 160 | + matrix[k, l] = 0 |
| 161 | + return matrix |
| 162 | + |
| 163 | +def load_image(): |
| 164 | + file_path = filedialog.askopenfilename(filetypes=[("BMP files", "*.bmp")]) |
| 165 | + if not file_path: |
| 166 | + return |
| 167 | + |
| 168 | + file_label.config(text=file_path) |
| 169 | + original_image = Image.open(file_path) |
| 170 | + resized_original_image = resize_image(original_image, 400, 400) |
| 171 | + original_photo = ImageTk.PhotoImage(resized_original_image) |
| 172 | + img_label.config(image=original_photo) |
| 173 | + img_label.image = original_photo |
| 174 | + |
| 175 | + try: |
| 176 | + F = int(f_entry.get() or 10) |
| 177 | + d = int(d_entry.get() or 7) |
| 178 | + except ValueError: |
| 179 | + messagebox.showerror("Error", "Invalid F or d value") |
| 180 | + return |
| 181 | + |
| 182 | + original_size = os.path.getsize(file_path) |
| 183 | + compressed_file_path = process_image(file_path, F, d) |
| 184 | + if compressed_file_path: |
| 185 | + compressed_size = os.path.getsize(compressed_file_path) |
| 186 | + compression_ratio = 100 * (original_size - compressed_size) / original_size |
| 187 | + compression_label.config(text=f"Dimensione originale: {original_size} bytes\n" |
| 188 | + f"Dimensione compressa: {compressed_size} bytes\n" |
| 189 | + f"Compressione: {compression_ratio:.2f}%") |
| 190 | + |
| 191 | + compressed_image = Image.open(compressed_file_path) |
| 192 | + resized_compressed_image = resize_image(compressed_image, 400, 400) |
| 193 | + compressed_photo = ImageTk.PhotoImage(resized_compressed_image) |
| 194 | + compressed_img_label.config(image=compressed_photo) |
| 195 | + compressed_img_label.image = compressed_photo |
| 196 | + |
| 197 | +def resize_image(image, max_width, max_height): |
| 198 | + width, height = image.size |
| 199 | + if width > max_width or height > max_height: |
| 200 | + ratio = min(max_width / width, max_height / height) |
| 201 | + new_size = (int(width * ratio), int(height * ratio)) |
| 202 | + image = image.resize(new_size) |
| 203 | + return image |
| 204 | + |
| 205 | +def toggle_normalize(): |
| 206 | + global normalize_coefficients |
| 207 | + normalize_coefficients = not normalize_coefficients |
| 208 | + print("normalize_coefficients set to", normalize_coefficients) |
| 209 | + |
| 210 | +root = tk.Tk() |
| 211 | +root.title("Compressione di immagini tramite la DCT") |
| 212 | + |
| 213 | +root.geometry("800x600") |
| 214 | + |
| 215 | +# Menu bar |
| 216 | +menu_bar = tk.Menu(root) |
| 217 | +root.config(menu=menu_bar) |
| 218 | + |
| 219 | +# Create a Notebook |
| 220 | +notebook = ttk.Notebook(root) |
| 221 | +notebook.pack(expand=True, fill='both') |
| 222 | + |
| 223 | +# Create frames for each tab |
| 224 | +dct_frame = ttk.Frame(notebook) |
| 225 | +hello_frame = ttk.Frame(notebook) |
| 226 | + |
| 227 | +notebook.add(dct_frame, text="DCT") |
| 228 | +notebook.add(hello_frame, text=("Comp")) |
| 229 | + |
| 230 | +f_entry = tk.Entry(dct_frame) |
| 231 | +f_entry.insert(0, "10") |
| 232 | +d_entry = tk.Entry(dct_frame) |
| 233 | +d_entry.insert(0, "7") |
| 234 | + |
| 235 | +normalize_button = tk.Checkbutton(dct_frame, text="“Normalizzare coefficenti DTC”", command=toggle_normalize) |
| 236 | +load_button = tk.Button(dct_frame, text="“Load .bmp image”", command=load_image) |
| 237 | +file_label = tk.Label(dct_frame, text="“Nessun file selezionato”") |
| 238 | +compression_label = tk.Label(dct_frame, text="””") |
| 239 | + |
| 240 | +image_frame = tk.Frame(dct_frame) |
| 241 | +image_frame.pack() |
| 242 | + |
| 243 | +img_label = tk.Label(image_frame) |
| 244 | +compressed_img_label = tk.Label(image_frame) |
| 245 | +f_entry.pack() |
| 246 | +d_entry.pack() |
| 247 | +normalize_button.pack() |
| 248 | +load_button.pack() |
| 249 | +file_label.pack() |
| 250 | +compression_label.pack() |
| 251 | +img_label.pack(side=tk.LEFT) |
| 252 | +compressed_img_label.pack(side=tk.LEFT) |
| 253 | + |
| 254 | +progress_var = tk.DoubleVar() |
| 255 | +progress_bar = ttk.Progressbar(hello_frame, variable=progress_var, maximum=100) |
| 256 | +progress_bar.pack(pady=10) |
| 257 | + |
| 258 | +compare_button = tk.Button(hello_frame, text="“Compare DCT2 Algorithms”", command=lambda: compare_dct2_algorithms_thread(progress_var, progress_bar)) |
| 259 | +compare_button.pack() |
| 260 | + |
| 261 | +root.mainloop() |
0 commit comments