Skip to content

Commit 45e1c4b

Browse files
authored
added rle and lzw
1 parent 381caa2 commit 45e1c4b

2 files changed

Lines changed: 390 additions & 0 deletions

File tree

lossless/lzw.ipynb

Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
{
2+
"cells": [
3+
{
4+
"cell_type": "code",
5+
"execution_count": 4,
6+
"id": "a686a0ee",
7+
"metadata": {},
8+
"outputs": [
9+
{
10+
"ename": "KeyboardInterrupt",
11+
"evalue": "",
12+
"output_type": "error",
13+
"traceback": [
14+
"\u001b[31m---------------------------------------------------------------------------\u001b[39m",
15+
"\u001b[31mKeyboardInterrupt\u001b[39m Traceback (most recent call last)",
16+
"\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[4]\u001b[39m\u001b[32m, line 119\u001b[39m\n\u001b[32m 116\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m img.dtype != np.uint8:\n\u001b[32m 117\u001b[39m img = (img * \u001b[32m255\u001b[39m).astype(np.uint8)\n\u001b[32m--> \u001b[39m\u001b[32m119\u001b[39m \u001b[43mvisualize_lzw_compression\u001b[49m\u001b[43m(\u001b[49m\u001b[43mimg\u001b[49m\u001b[43m)\u001b[49m\n",
17+
"\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[4]\u001b[39m\u001b[32m, line 91\u001b[39m, in \u001b[36mvisualize_lzw_compression\u001b[39m\u001b[34m(image, max_dict_size)\u001b[39m\n\u001b[32m 89\u001b[39m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34mvisualize_lzw_compression\u001b[39m(image, max_dict_size=\u001b[32m256\u001b[39m):\n\u001b[32m 90\u001b[39m ratio, compressed, dictionary, unique_pixels, total_bits, orig_bits = \\\n\u001b[32m---> \u001b[39m\u001b[32m91\u001b[39m \u001b[43mcalculate_lzw_compression_ratio\u001b[49m\u001b[43m(\u001b[49m\u001b[43mimage\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mmax_dict_size\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 92\u001b[39m decoded_img = lzw_decompress(compressed, dictionary, image.shape, unique_pixels, max_dict_size)\n\u001b[32m 94\u001b[39m \u001b[38;5;28mprint\u001b[39m(\u001b[33mf\u001b[39m\u001b[33m\"\u001b[39m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[33mLZW Compression (max dict size=\u001b[39m\u001b[38;5;132;01m{\u001b[39;00mmax_dict_size\u001b[38;5;132;01m}\u001b[39;00m\u001b[33m)\u001b[39m\u001b[33m\"\u001b[39m)\n",
18+
"\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[4]\u001b[39m\u001b[32m, line 78\u001b[39m, in \u001b[36mcalculate_lzw_compression_ratio\u001b[39m\u001b[34m(image, max_dict_size)\u001b[39m\n\u001b[32m 77\u001b[39m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34mcalculate_lzw_compression_ratio\u001b[39m(image, max_dict_size=\u001b[32m4096\u001b[39m):\n\u001b[32m---> \u001b[39m\u001b[32m78\u001b[39m compressed, dictionary, unique_pixels = \u001b[43mlzw_compress\u001b[49m\u001b[43m(\u001b[49m\u001b[43mimage\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mmax_dict_size\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 80\u001b[39m orig_bits = image.nbytes * \u001b[32m8\u001b[39m\n\u001b[32m 81\u001b[39m compressed_bits = \u001b[38;5;28mlen\u001b[39m(compressed) * \u001b[32m32\u001b[39m\n",
19+
"\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[4]\u001b[39m\u001b[32m, line 32\u001b[39m, in \u001b[36mlzw_compress\u001b[39m\u001b[34m(image, max_dict_size)\u001b[39m\n\u001b[32m 29\u001b[39m dict_size += \u001b[32m1\u001b[39m\n\u001b[32m 30\u001b[39m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[32m 31\u001b[39m \u001b[38;5;66;03m# Reset dictionary when limit reached\u001b[39;00m\n\u001b[32m---> \u001b[39m\u001b[32m32\u001b[39m dictionary = {\u001b[43mp\u001b[49m\u001b[43m.\u001b[49m\u001b[43mtobytes\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m: i \u001b[38;5;28;01mfor\u001b[39;00m i, p \u001b[38;5;129;01min\u001b[39;00m \u001b[38;5;28menumerate\u001b[39m(unique_pixels)}\n\u001b[32m 33\u001b[39m dict_size = \u001b[38;5;28mlen\u001b[39m(dictionary)\n\u001b[32m 34\u001b[39m w = pixel\n",
20+
"\u001b[31mKeyboardInterrupt\u001b[39m: "
21+
]
22+
}
23+
],
24+
"source": [
25+
"import numpy as np\n",
26+
"import matplotlib.pyplot as plt\n",
27+
"\n",
28+
"def lzw_compress(image, max_dict_size=4096):\n",
29+
" \"\"\"\n",
30+
" Faster LZW compression for RGB NumPy images.\n",
31+
" Uses byte strings as keys for faster hashing.\n",
32+
" \"\"\"\n",
33+
" # Flatten to bytes for faster operations\n",
34+
" pixels = image.reshape(-1, image.shape[-1])\n",
35+
" pixel_bytes = [px.tobytes() for px in pixels]\n",
36+
" \n",
37+
" # Initialize dictionary with unique pixel values\n",
38+
" unique_pixels = [pixels[i] for i in np.unique(pixels, axis=0, return_index=True)[1]]\n",
39+
" dictionary = {p.tobytes(): i for i, p in enumerate(unique_pixels)}\n",
40+
" dict_size = len(dictionary)\n",
41+
" \n",
42+
" w = b''\n",
43+
" compressed = []\n",
44+
" \n",
45+
" for pixel in pixel_bytes:\n",
46+
" wc = w + pixel\n",
47+
" if wc in dictionary:\n",
48+
" w = wc\n",
49+
" else:\n",
50+
" compressed.append(dictionary[w])\n",
51+
" if dict_size < max_dict_size:\n",
52+
" dictionary[wc] = dict_size\n",
53+
" dict_size += 1\n",
54+
" else:\n",
55+
" # Reset dictionary when limit reached\n",
56+
" dictionary = {p.tobytes(): i for i, p in enumerate(unique_pixels)}\n",
57+
" dict_size = len(dictionary)\n",
58+
" w = pixel\n",
59+
" \n",
60+
" if w:\n",
61+
" compressed.append(dictionary[w])\n",
62+
" \n",
63+
" return compressed, dictionary, unique_pixels\n",
64+
"\n",
65+
"\n",
66+
"def lzw_decompress(compressed, dictionary, shape, unique_pixels, max_dict_size=4096):\n",
67+
" \"\"\"\n",
68+
" Faster LZW decompression using byte string dictionary.\n",
69+
" \"\"\"\n",
70+
" rev_dict = {v: k for k, v in dictionary.items()}\n",
71+
" dict_size = len(rev_dict)\n",
72+
" \n",
73+
" w = rev_dict[compressed[0]]\n",
74+
" result = [w]\n",
75+
" \n",
76+
" for k in compressed[1:]:\n",
77+
" if k in rev_dict:\n",
78+
" entry = rev_dict[k]\n",
79+
" elif k == dict_size:\n",
80+
" entry = w + w[:shape[-1]]\n",
81+
" else:\n",
82+
" raise ValueError(\"Invalid compressed code encountered\")\n",
83+
" \n",
84+
" result.append(entry)\n",
85+
" \n",
86+
" if dict_size < max_dict_size:\n",
87+
" rev_dict[dict_size] = w + entry[:shape[-1]]\n",
88+
" dict_size += 1\n",
89+
" else:\n",
90+
" rev_dict = {v: k for k, v in dictionary.items()}\n",
91+
" dict_size = len(rev_dict)\n",
92+
" \n",
93+
" w = entry\n",
94+
" \n",
95+
" # Convert byte string back to array\n",
96+
" pixel_size = shape[-1]\n",
97+
" decoded = np.frombuffer(b''.join(result), dtype=np.uint8)\n",
98+
" return decoded.reshape(shape)\n",
99+
"\n",
100+
"\n",
101+
"def calculate_lzw_compression_ratio(image, max_dict_size=4096):\n",
102+
" compressed, dictionary, unique_pixels = lzw_compress(image, max_dict_size)\n",
103+
" \n",
104+
" orig_bits = image.nbytes * 8\n",
105+
" compressed_bits = len(compressed) * 32\n",
106+
" dict_bits = len(dictionary) * 32\n",
107+
" total_bits = compressed_bits + dict_bits\n",
108+
" ratio = orig_bits / total_bits\n",
109+
" \n",
110+
" return ratio, compressed, dictionary, unique_pixels, total_bits, orig_bits\n",
111+
"\n",
112+
"\n",
113+
"def visualize_lzw_compression(image, max_dict_size=256):\n",
114+
" ratio, compressed, dictionary, unique_pixels, total_bits, orig_bits = \\\n",
115+
" calculate_lzw_compression_ratio(image, max_dict_size)\n",
116+
" decoded_img = lzw_decompress(compressed, dictionary, image.shape, unique_pixels, max_dict_size)\n",
117+
" \n",
118+
" print(f\"\\nLZW Compression (max dict size={max_dict_size})\")\n",
119+
" print(f\"Original bits: {orig_bits:,}\")\n",
120+
" print(f\"Compressed + dictionary bits: {total_bits:,}\")\n",
121+
" print(f\"Compression ratio: {ratio:.3f}\")\n",
122+
" \n",
123+
" plt.figure(figsize=(10, 4))\n",
124+
" plt.subplot(1, 2, 1)\n",
125+
" plt.imshow(image)\n",
126+
" plt.title(\"Original\")\n",
127+
" plt.axis(\"off\")\n",
128+
" \n",
129+
" plt.subplot(1, 2, 2)\n",
130+
" plt.imshow(decoded_img)\n",
131+
" plt.title(f\"LZW (dict={max_dict_size})\")\n",
132+
" plt.axis(\"off\")\n",
133+
" \n",
134+
" plt.tight_layout()\n",
135+
" plt.show()\n",
136+
"\n",
137+
"import matplotlib.image as mpimg\n",
138+
"\n",
139+
"img = mpimg.imread(\"/mnt/769EC2439EC1FB9D/vsc_projs/DIP/kodim01.png\")\n",
140+
"if img.dtype != np.uint8:\n",
141+
" img = (img * 255).astype(np.uint8)\n",
142+
"\n",
143+
"visualize_lzw_compression(img)"
144+
]
145+
},
146+
{
147+
"cell_type": "code",
148+
"execution_count": null,
149+
"id": "5042407f",
150+
"metadata": {},
151+
"outputs": [],
152+
"source": []
153+
}
154+
],
155+
"metadata": {
156+
"kernelspec": {
157+
"display_name": "dip_proj",
158+
"language": "python",
159+
"name": "python3"
160+
},
161+
"language_info": {
162+
"codemirror_mode": {
163+
"name": "ipython",
164+
"version": 3
165+
},
166+
"file_extension": ".py",
167+
"mimetype": "text/x-python",
168+
"name": "python",
169+
"nbconvert_exporter": "python",
170+
"pygments_lexer": "ipython3",
171+
"version": "3.13.7"
172+
}
173+
},
174+
"nbformat": 4,
175+
"nbformat_minor": 5
176+
}

0 commit comments

Comments
 (0)