3 from collections
import Counter
10 def __init__(self
, text
):
11 self
._text
= text
.split()
12 self
.magic_number
= self
._text
[0]
13 self
.image_width
= self
._text
[1]
14 self
.image_height
= self
._text
[2]
15 self
.image_array
= self
._text
[3:]
18 self
.size
= int(self
.image_width
) * int(self
.image_height
)
20 for _
in range(len(self
.image_array
)):
21 if self
.image_array
[_
] == "0":
23 elif self
.image_array
[_
] == "1":
32 def get_file(file_path
):
33 with open(file_path
, 'r', encoding
='utf-8') as file:
34 _file_content
= file.read()
38 To evaluate comprimento médio do código - l(c)
39 l(all_pixels) = ceil(log2P(all_pixels))+1
41 def arithmetic_cod_preview(_pbm
):
42 print("\n*** Arithmetic Preview ***")
43 pixels
= [int(pixel
) for pixel
in _pbm
.image_array
]
44 _P0
= _pbm
.zeros
/_pbm
.size
45 _P1
= _pbm
.ones
/_pbm
.size
54 _arithmetic_encode_lenght
= np
.ceil(-1*np
.log2(_PT01_int
)) + 1
55 print(f
"L(image) --> {_arithmetic_encode_lenght} bits")
56 _entropy
= calcular_entropia(_pbm
.image_array
)
57 print(f
"entropy --> {_entropy} bits")
58 _datastream
= clean_image_data(_pbm
.image_array
)
59 _entropia_conditional
= calcular_entropia_condicional(_datastream
)
60 print(f
"entropy condicional --> {_entropia_conditional} bits")
61 return int(_arithmetic_encode_lenght
)
64 Arithmetic Encoding approach
66 it uses static probabilities provided on output file
68 c(x)=|interval_low_val +(interval_high_val).F(x)_low
69 =|interval_low_val +(interval_high_val).F(x)_high
70 _to_encode_val = avg(C(all)_low,C(all)_high)
71 _output = bin(_to_encode_val))
73 def arithmetic_encode_image(_pbm
):
74 pixels
= [int(pixel
) for pixel
in _pbm
.image_array
]
75 _P0
= np
.float64(_pbm
.zeros
/_pbm
.size
)
76 _P1
= np
.float64(_pbm
.ones
/_pbm
.size
)
78 # probabilidades acomuladas orderdanas por ordem crescente
81 _output_opt_1
= f
"{_pbm.zeros} {_pbm.ones} {_pbm.image_width} {_pbm.image_height} "
85 _low_val
= np
.float64(_interval
[0] + (_interval
[1]-_interval
[0]) * _Fx
.get(0)[0])
86 _high_val
= np
.float64(_interval
[0] + (_interval
[1]-_interval
[0])*_Fx
.get(0)[1])
88 _low_val
= np
.float64(_interval
[0] + (_interval
[1] - _interval
[0]) * _Fx
.get(1)[0])
89 _high_val
= np
.float64(_interval
[0] + (_interval
[1] - _interval
[0]) * _Fx
.get(1)[1])
90 _interval
[0] = _low_val
91 _interval
[1] = _high_val
93 _to_encode_value
= np
.float64((_interval
[0]+_interval
[1])/2)
94 print(f
"to encode --> {_to_encode_value}")
95 _output_opt_2
= _output_opt_1
+ f
"{_to_encode_value}"
97 _size
= arithmetic_cod_preview(_pbm
)
98 _res
= _to_encode_value
99 for _
in range(_size
):
108 return _output_opt_1
,_output_opt_2
111 To decode encoded pbm files with arithmetic_encode_image()
112 amount_of_zeros amount_of_ones image_width image_height encoded_image
114 def arithmetic_decode_file(_txt
):
115 _encoded_pbm_file
= get_file(_txt
).split()
117 amount_of_zeros
= int(_encoded_pbm_file
[0])
118 amount_of_ones
= int(_encoded_pbm_file
[1])
119 image_width
= int(_encoded_pbm_file
[2])
120 image_height
= int(_encoded_pbm_file
[3])
121 encoded_image
= np
.float64(_encoded_pbm_file
[4])
123 _output
= f
"P1\n{image_width} {image_height}\n"
124 _size
= int(image_width
) * int(image_height
)
125 _P0
= np
.float64(amount_of_zeros
/_size
)
126 _P1
= np
.float64(amount_of_ones
/_size
)
130 _constructed_image
= ""
131 for h
in range(image_height
):
132 for w
in range(image_width
):
133 if _P1
< encoded_image
< 1:
134 _constructed_image
+= "0 "
135 encoded_image
= (encoded_image
-_P1
)/_P0
136 elif 0 < encoded_image
< _P1
:
137 _constructed_image
+= "1 "
138 encoded_image
= (encoded_image
- 0) / _P1
140 if w
== image_width
-1:
141 _constructed_image
+= "\n"
143 _output
+= _constructed_image
147 def calcular_entropia(bitstream
):
151 p0
= bitstream
.count('0') / n
152 p1
= bitstream
.count('1') / n
157 entropia
-= p
* math
.log2(p
)
161 def calcular_entropia_condicional(bitstream
):
162 transicoes
= {'00': 0, '01': 0, '10': 0, '11': 0}
163 contagem_base
= {'0': 0, '1': 0}
165 for i
in range(len(bitstream
) - 1):
166 par
= bitstream
[i
:i
+ 2]
168 contagem_base
[bitstream
[i
]] += 1
171 for base
in ['0', '1']:
172 p_base
= contagem_base
[base
] / (len(bitstream
) - 1)
175 for prox
in ['0', '1']:
176 p_transicao
= transicoes
[base
+ prox
] / contagem_base
[base
]
178 h_local
-= p_transicao
* math
.log2(p_transicao
)
179 h_condicional
+= p_base
* h_local
184 def clean_image_data(image_data
):
187 for line
in image_data
:
189 line
= line
.replace(" ", "")
190 if not line
or line
.startswith('#'):
197 def calcular_taxa_compressao(caminho_original
, caminho_comprimido
, total_pixeis
):
198 tamanho_orig
= os
.path
.getsize(caminho_original
)
199 tamanho_comp
= os
.path
.getsize(caminho_comprimido
)
201 poupanca
= (1 - (tamanho_comp
/ tamanho_orig
)) * 100
203 bpp
= (tamanho_comp
* 8) / total_pixeis
205 return poupanca
, bpp
, tamanho_orig
, tamanho_comp
208 def xor(bitstream
, largura
, altura
):
210 for i
in range(altura
):
211 linha
= [int(b
) for b
in bitstream
[i
* largura
: (i
+ 1) * largura
]]
215 # A primeira linha mantém-se igual (não tem linha anterior)
216 nova_imagem
+= "".join(map(str, matriz
[0]))
218 for i
in range(1, altura
):
220 for j
in range(largura
):
221 res
= matriz
[i
][j
] ^ matriz
[i
- 1][j
]
222 nova_linha
.append(str(res
))
223 nova_imagem
+= "".join(nova_linha
)
228 def descodificar_xor(bitstream_transformado
, largura
, altura
):
230 for i
in range(altura
):
231 linha
= [int(b
) for b
in bitstream_transformado
[i
* largura
: (i
+ 1) * largura
]]
232 matriz_temp
.append(linha
)
236 matriz_original
.append(matriz_temp
[0])
238 for i
in range(1, altura
):
239 linha_recuperada
= []
240 for j
in range(largura
):
241 pixel_original
= matriz_temp
[i
][j
] ^ matriz_original
[i
- 1][j
]
242 linha_recuperada
.append(pixel_original
)
243 matriz_original
.append(linha_recuperada
)
246 for linha
in matriz_original
:
247 bitstream_final
+= "".join(map(str, linha
))
249 return bitstream_final
252 # -------------------------------------------------------------------------
253 # HUFFMAN IMPLEMENTATION
254 # -------------------------------------------------------------------------
257 def __init__(self
, char
, freq
):
258 self
.char
= char
# 0 ou 1 que está no pixel
259 self
.freq
= freq
# Quantas vezes aparece
260 self
.left
= None # Filho à esquerda
261 self
.right
= None # Filho à direita
263 # Nó com menor frequencia
264 def __lt__(self
, other
):
265 return self
.freq
< other
.freq
269 def build_huffman_tree(pixels
):
270 frequency
= Counter(pixels
)
271 heap
= [HuffmanNode(char
, freq
) for char
, freq
in frequency
.items()]
274 # Cria a árvore de baixo para cima / frequencia menor para maior
276 node1
= heapq
.heappop(heap
)
277 node2
= heapq
.heappop(heap
)
278 merged
= HuffmanNode(None, node1
.freq
+ node2
.freq
)
281 heapq
.heappush(heap
, merged
)
283 return heap
[0] if heap
else None
286 # Cria os códigos (0 para a esquerda, 1 para a direira)
287 def make_codes(node
, current_code
="", codes
=None):
292 if node
.char
is not None:
293 codes
[node
.char
] = current_code
296 make_codes(node
.left
, current_code
+ "0", codes
)
297 make_codes(node
.right
, current_code
+ "1", codes
)
301 # Faz encoding do ficheiro
302 def huffman_encode_image(_pbm
):
303 print("\n*** Huffman Encoding ***")
304 pixels
= _pbm
.image_array
306 # Erro caso não encontre a imagem
308 print("Empty image.")
311 # Constroi a árvore e os códigos
312 root
= build_huffman_tree(pixels
)
313 codes
= make_codes(root
)
315 print(f
"Codes: {codes}")
317 # Faz encode dos pixeis
318 encoded_str
= "".join([codes
[p
] for p
in pixels
])
320 # Garante que o total é multiplo de 8
321 extra_padding
= 8 - len(encoded_str
) % 8
322 encoded_str
= encoded_str
+ "0" * extra_padding
324 # Reduz o tamanho convertendo de byte para bit
326 for i
in range(0, len(encoded_str
), 8):
327 byte
= encoded_str
[i
:i
+ 8]
328 b
.append(int(byte
, 2))
331 width
= int(_pbm
.image_width
)
332 height
= int(_pbm
.image_height
)
335 header
= struct
.pack('>IIIIB', width
, height
, freq0
, freq1
, extra_padding
)
340 # Faz decode do ficheiro
341 def huffman_decode_file(filename
):
342 print(f
"\n*** Huffman Decoding from {filename} ***")
345 with open(filename
, 'rb') as f
:
346 file_content
= f
.read()
349 header_size
= struct
.calcsize('>IIIIB')
350 width
, height
, freq0
, freq1
, extra_padding
= struct
.unpack('>IIIIB', file_content
[:header_size
])
351 encoded_data
= file_content
[header_size
:]
353 print(f
"Header Info -> W:{width}, H:{height}, Zeros:{freq0}, Ones:{freq1}")
356 fake_pixels
= ['0'] * freq0
+ ['1'] * freq1
357 root
= build_huffman_tree(fake_pixels
)
358 codes
= make_codes(root
)
360 # Faz decode dos pixeis
361 reverse_codes
= {v: k for k, v in codes.items()}
363 # Converte os bytes em string
365 for byte
in encoded_data
:
366 bit_string
+= f
"{byte:08b}"
371 for bit
in bit_string
:
373 if current_code
in reverse_codes
:
374 decoded_pixels
.append(reverse_codes
[current_code
])
377 # Limita o tamanho width * height
378 expected_pixels
= width
* height
379 decoded_pixels
= decoded_pixels
[:expected_pixels
]
381 pbm_content
= f
"P1\n{width} {height}\n"
383 # Formata as linhas para não ficar sequencial
385 for i
, p
in enumerate(decoded_pixels
):
387 if (i
+ 1) % width
== 0:
388 pbm_content
+= row_str
.strip() + "\n"
391 print("Decoding finished.")