roblem is that when we compress a 508×371 BMP color image using LZW (by splitting it into R, G, and B channels), the resulting compressed file is larger than the original. This happens because: We use a fixed 32-bit code for every LZW code, which adds overhead. The color image lacks enough repetitive patterns for LZW to achieve significant compression. Although decompression is lossless (restored image matches the original), the compression ratio is unfavorable (expansion rather than reduction). from PIL import Image import numpy as np import os import math import struct from collections import Counter # --- LZW Sıkıştırma ve Açma Fonksiyonları (Gray-Scale için) --- def lzw_compress_gray(data): dictionary = {bytes(): i for i in range(256)} dict_size = 256 max_dict_size = 65536 # 16-bit sınırı w = bytes() compressed = [] for pixel in data: wc = w + bytes([pixel]) if wc in dictionary: w = wc else: compressed.append(dictionary[w]) # Sözlüğün boyutunu sınırla: if dict_size < max_dict_size: dictionary[wc] = dict_size dict_size += 1 w = bytes([pixel]) if w: compressed.append(dictionary[w]) return compressed def lzw_decompress_gray(codes): """ LZW algoritmasıyla sıkıştırılmış kod listesini açar. Return: Açılmış veriyi (piksel değerleri listesi) elde eder. """ dictionary = {i: bytes() for i in range(256)} dict_size = 256 # İlk kodu alıp sözlüğe bakıyoruz. w = bytes([codes.pop(0)]) result = [w] for code in codes: if code in dictionary: entry = dictionary Código: elif code == dict_size: entry = w + w[:1] else: raise ValueError("Geçersiz sıkıştırılmış kod: %s" % code) result.append(entry) dictionary[dict_size] = w + entry[:1] dict_size += 1 w = entry # Sonuçları tek bir bytes dizisine dönüştürüp liste haline getiriyoruz. return list(b''.join(result)) # --- Renkli Görüntü için Sıkıştırma ve Açma Fonksiyonları --- def compress_color_image(image_path, compressed_file): """ 1. Renkli resmi açar ve RGB bileşenlerine ayırır (her biri bir gri seviye görüntüsüdür). 2. Her kanal için LZW ile sıkıştırma uygular. 3. Sıkıştırılmış kodları ve görüntü boyut bilgisini tek bir binary dosyada saklar. 4. Ayrıca her kanal için metrikler hesaplanabilir. """ # Resmi aç ve RGB'ye çevir img = Image.open(image_path).convert("RGB") # Görüntü boyutları (tüm kanallar aynı boyutta) rows, cols = img.size[1], img.size[0] # Pillow'da (width, height) olduğundan dikkat! # Kanal ayrımı (R, G, B) r, g, b = img.split() channels = {'R': np.array(r, dtype=np.uint8), 'G': np.array(g, dtype=np.uint8), 'B': np.array(b, dtype=np.uint8)} # Her kanal için sıkıştırılmış kodları saklamak üzere bir yapı oluşturuyoruz compressed_channels = {} for key in channels: # Düzleştir: 2D görüntüyü 1D listeye çeviriyoruz. data = channels[key].flatten().tolist() codes = lzw_compress_gray(data) compressed_channels[key] = codes # Sıkıştırılmış veriyi dosyaya yazıyoruz. # Dosya formatı: # 1) 4 byte: rows (unsigned int, big endian) # 2) 4 byte: cols (unsigned int) # Sonra her kanal için sırasıyla: # 3) 4 byte: Kanalın kod sayısı (N) # 4) N adet 2 byte: Her kod (unsigned short, big endian) with open(compressed_file, "wb") as f: f.write(struct.pack(">I", rows)) f.write(struct.pack(">I", cols)) # Kanallar R, G, B sırasıyla yazılıyor. for key in ['R', 'G', 'B']: codes = compressed_channels[key] f.write(struct.pack(">I", len(codes))) # Kanal kod sayısı for code in codes: f.write(struct.pack(">I", code)) # Her kodu 2 bayt olarak yazıyoruz. print("Sıkıştırma tamamlandı. Sıkıştırılmış dosya:", compressed_file) def decompress_color_image(compressed_file, restored_image_path): """ Sıkıştırılmış dosyayı açar, içindeki bilgiden RGB kanallarını LZW açma yöntemiyle geri getirir, orijinal boyutlara yeniden şekillendirir ve restore edilmiş renkli resmi oluşturur. """ with open(compressed_file, "rb") as f: # Görüntü boyutlarını oku. rows = struct.unpack(">I", f.read(4))[0] cols = struct.unpack(">I", f.read(4))[0] channels_restored = {} for key in ['R', 'G', 'B']: # Her kanal için kod sayısını oku. num_codes = struct.unpack(">I", f.read(4))[0] codes = [struct.unpack(">I", f.read(4))[0] for _ in range(num_codes)] # LZW açma işlemi decompressed = lzw_decompress_gray(codes) # Yeniden 2D'ye dönüştür (uint8 formatına çeviriyoruz) channel_array = np.array(decompressed, dtype=np.uint8).reshape((rows, cols)) channels_restored[key] = channel_array # Kanalları birleştirip renkli görüntü oluşturuyoruz. restored_img = Image.merge("RGB", (Image.fromarray(channels_restored['R']), Image.fromarray(channels_restored['G']), Image.fromarray(channels_restored['B']))) restored_img.save(restored_image_path) print("Açma tamamlandı. Restore edilmiş resim:", restored_image_path) return restored_img def calculate_color_metrics(image_path, compressed_file): """ Orijinal resim ve sıkıştırılmış dosya üzerinden metrikleri hesaplar: - Entropi (her kanal için ayrı ayrı hesaplanıp ortalaması alınabilir) - Ortalama kod uzunluğu (tüm kanalların toplam kod sayısı üzerinden) - Sıkıştırılmış dosya boyutu - Sıkıştırma oranı """ # Orijinal resmin dosya boyutunu alıyoruz original_size = os.path.getsize(image_path) compressed_size = os.path.getsize(compressed_file) # Açık resmi numpy array'e çevir (RGB) img = np.array(Image.open(image_path).convert("RGB")) # Entropi: Her kanalın histogramı hesaplanıp ortalaması alınabilir. entropies = [] for i in range(3): channel = img[:, :, i].flatten() hist = Counter(channel) total = len(channel) entropy = -sum((count / total) * math.log2(count / total) for count in hist.values()) entropies.append(entropy) avg_entropy = sum(entropies) / 3 # Ortalama kod uzunluğunu hesaplamak için, # dosyadaki toplam bit sayısı bölünmüş toplam sembol sayısı (R+G+B) total_codes = 0 with open(compressed_file, "rb") as f: f.read(8) # rows, cols for _ in range(3): num_codes = struct.unpack(">I", f.read(4))[0] total_codes += num_codes f.read(num_codes * 4) # Her kodu 2 bayt = 16 bit olarak sakladığımızı varsayarsak: avg_code_length = (total_codes * 16) / (img.shape[0] * img.shape[1] * 3) # bit/piksel compression_ratio = compressed_size / original_size print(f"Orijinal Dosya Boyutu: {original_size} byte") print(f"Sıkıştırılmış Dosya Boyutu: {compressed_size} byte") print(f"Compression Ratio: {compression_ratio:.4f}") print(f"Ortalama Kod Uzunluğu: {avg_code_length:.4f} bit/piksel") print(f"Ortalama Entropi: {avg_entropy:.4f} bit/piksel") return avg_entropy, avg_code_length, compressed_size, compression_ratio # --- Ana Program Bloğu --- if __name__ == "__main__": image_path = "color_image.bmp" # Renkli görüntü dosyanızın yolu compressed_file = "compressed_level4.bin" restored_image_path = "restored_level4.bmp" # Sıkıştırma aşaması compress_color_image(image_path, compressed_file) calculate_color_metrics(image_path, compressed_file) # Açma (Decompression) aşaması restored_img = decompress_color_image(compressed_file, restored_image_path) # Karşılaştırma: Orijinal ve açılmış görüntüleri piksel bazında kontrol edelim. original_img = Image.open(image_path).convert("RGB") if np.array_equal(np.array(original_img), np.array(restored_img)): print("Başarılı: Orijinal ve restore edilmiş görüntüler aynı!") else: print("Hata: Görüntüler arasında fark var.") I'm excepting .bin file should be have KB less than .bmp file [url="https://stackoverflow.com/questions/79495991/implementing-variable-length-bit-packing-for-lzw"]Continue reading...[/url]