Commit 6cb62a30 authored by Bryce Hepner's avatar Bryce Hepner

more compressed files

parent 96e7a95d
...@@ -8,6 +8,7 @@ from scipy.optimize import minimize,linprog ...@@ -8,6 +8,7 @@ from scipy.optimize import minimize,linprog
from sklearn.neighbors import KernelDensity from sklearn.neighbors import KernelDensity
from collections import Counter from collections import Counter
import numpy.linalg as la import numpy.linalg as la
from time import time
def file_extractor(dirname="images"): def file_extractor(dirname="images"):
files = os.listdir(dirname) files = os.listdir(dirname)
...@@ -120,7 +121,7 @@ def predict_pix(tiff_image_path, difference = True): ...@@ -120,7 +121,7 @@ def predict_pix(tiff_image_path, difference = True):
# calculate the error # calculate the error
error = np.ravel(image_array[1:-1,1:-1])-predict error = np.ravel(image_array[1:-1,1:-1])-predict
return image_array, predict, diff, error return image_array, diff, error
""" """
this huffman encoding code is found online this huffman encoding code is found online
...@@ -173,3 +174,362 @@ def decode_string(huffman_string, the_keys, the_values): ...@@ -173,3 +174,362 @@ def decode_string(huffman_string, the_keys, the_values):
return (int(the_keys[the_values.index(huffman_string[:i+1])]),huffman_string[i+1:]) return (int(the_keys[the_values.index(huffman_string[:i+1])]),huffman_string[i+1:])
except: except:
pass pass
def make_dictionary(tiff_image_path_list, num_bins=4, difference = True):
"""
This function is used to encode the error based on the difference
and split the difference into different bins
Input:
tiff_image_path (string): path to the tiff file
num_bins (int): number of bins
Return:
huffman_encoding_list list (num_bins + 1): a list of dictionary
image_array ndarray (512, 640): original image
new_error ndarray (512, 640): error that includes the boundary
diff ndarray (510, 638): difference of min and max of the 4 neighbors
boundary ndarray (2300,): the boundary values after subtracting the very first pixel value
predict ndarray (325380,): the list of predicted values
bins list (num_bins - 1,): a list of threshold to cut the bins
A ndarray (3 X 3): system of equation
"""
list_of_all_vals = []
huffman_encoding_list = []
for _ in range(num_bins+1):
list_of_all_vals.append([])
for _, tiff_image_path in enumerate(tiff_image_path_list):
# get the image_array, etc
image_array, diff, error= predict_pix(tiff_image_path, difference)
bins = [21,32,48]
# get the boundary
boundary = np.hstack((image_array[0,:],image_array[-1,:],image_array[1:-1,0],image_array[1:-1,-1]))
# take the difference of the boundary with the very first pixel
boundary = boundary - image_array[0,0]
#boundary is 1dim, so boundary[0] is just the first element
boundary[0] = image_array[0,0]
# huffman encode the boundary
for j in boundary:
list_of_all_vals[0].append(str(j))
# create a list of huffman table
n = len(bins)
# loop through different bins
for k in range (0,n):
# the first bin
if k == 0 :
# get the point within the bin and huffman huffman_encoding_dict
mask = diff <= bins[k]
for j in error[mask].astype(int):
list_of_all_vals[k+1].append(str(j))
# the middle bins
else:
# get the point within the bin and huffman huffman_encoding_dict
mask = diff > bins[k-1]
new_error = error[mask]
mask2 = diff[mask] <= bins[k]
for j in new_error[mask2].astype(int):
list_of_all_vals[k+1].append(str(j))
# the last bin
# get the point within the bin and huffman huffman_encoding_dict
mask = diff > bins[-1]
for j in error[mask].astype(int):
list_of_all_vals[-1].append(str(j))
for item in list_of_all_vals:
freq = dict(Counter(item))
freq = sorted(freq.items(), key=lambda x: x[1], reverse=True)
node = make_tree(freq)
huffman_encoding_list.append(huffman_code_tree(node))
# create a error matrix that includes the boundary (used in encoding matrix)
new_error = np.copy(image_array)
new_error[1:-1,1:-1] = np.reshape(error,(510, 638))
keep = new_error[0,0]
new_error[0,:] = new_error[0,:] - keep
new_error[-1,:] = new_error[-1,:] - keep
new_error[1:-1,0] = new_error[1:-1,0] - keep
new_error[1:-1,-1] = new_error[1:-1,-1] - keep
new_error[0,0] = keep
# huffman_encoding_list = list(set(huffman_encoding_list))
diff = np.reshape(diff,(510,638))
# return the huffman dictionary
return huffman_encoding_list,bins
def huffman(tiff_image_path, num_bins=4, difference = True):
"""
This function is used to encode the error based on the difference
and split the difference into different bins
Input:
tiff_image_path (string): path to the tiff file
num_bins (int): number of bins
Return:
huffman_encoding_list list (num_bins + 1): a list of dictionary
image_as_array ndarray (512, 640): original image
new_error ndarray (512, 640): error that includes the boundary
diff ndarray (510, 638): difference of min and max of the 4 neighbors
boundary ndarray (2300,): the boundary values after subtracting the very first pixel value
predict ndarray (325380,): the list of predicted values
bins list (num_bins - 1,): a list of threshold to cut the bins
A ndarray (3 X 3): system of equation
"""
# get the image_as_array, etc
image_as_array, diff, error= predict_pix(tiff_image_path, difference)
# calculate the number of points that will go in each bin
# sort the difference and create the bins
bins = [21,32,48]
# get the boundary
boundary = np.hstack((image_as_array[0,:],image_as_array[-1,:],image_as_array[1:-1,0],image_as_array[1:-1,-1]))
# take the difference of the boundary with the very first pixel
boundary = boundary - image_as_array[0,0]
#boundary is 1dim, so boundary[0] is just the first element
boundary[0] = image_as_array[0,0]
# huffman encode the boundary
bound_vals_as_string = [str(i) for i in boundary]
freq = dict(Counter(bound_vals_as_string))
freq = sorted(freq.items(), key=lambda x: x[1], reverse=True)
node = make_tree(freq)
huffman_encoding_dict = huffman_code_tree(node)
# create a list of huffman table
huffman_encoding_list = [huffman_encoding_dict]
n = len(bins)
# loop through different bins
for i in range (0,n):
# the first bin
if i == 0 :
# get the point within the bin and huffman huffman_encoding_dict
mask = diff <= bins[i]
line_as_string = [str(i) for i in error[mask].astype(int)]
freq = dict(Counter(line_as_string))
freq = sorted(freq.items(), key=lambda x: x[1], reverse=True)
node = make_tree(freq)
huffman_encoding_dict = huffman_code_tree(node)
huffman_encoding_list.append(huffman_encoding_dict)
# the middle bins
else:
# get the point within the bin and huffman huffman_encoding_dict
mask = diff > bins[i-1]
new_error = error[mask]
mask2 = diff[mask] <= bins[i]
line_as_string = [str(i) for i in new_error[mask2].astype(int)]
freq = dict(Counter(line_as_string))
freq = sorted(freq.items(), key=lambda x: x[1], reverse=True)
node = make_tree(freq)
huffman_encoding_dict = huffman_code_tree(node)
huffman_encoding_list.append(huffman_encoding_dict)
# the last bin
# get the point within the bin and huffman huffman_encoding_dict
mask = diff > bins[-1]
line_as_string = [str(i) for i in error[mask].astype(int)]
freq = dict(Counter(line_as_string))
freq = sorted(freq.items(), key=lambda x: x[1], reverse=True)
node = make_tree(freq)
huffman_encoding_dict = huffman_code_tree(node)
huffman_encoding_list.append(huffman_encoding_dict)
# create a error matrix that includes the boundary (used in encoding matrix)
new_error = np.copy(image_as_array)
new_error[1:-1,1:-1] = np.reshape(error,(510, 638))
keep = new_error[0,0]
new_error[0,:] = new_error[0,:] - keep
new_error[-1,:] = new_error[-1,:] - keep
new_error[1:-1,0] = new_error[1:-1,0] - keep
new_error[1:-1,-1] = new_error[1:-1,-1] - keep
new_error[0,0] = keep
# huffman_encoding_list = list(set(huffman_encoding_list))
diff = np.reshape(diff,(510,638))
# return the huffman dictionary
return image_as_array, new_error, diff
def encoder(error, list_dic, diff, bins):
"""
This function encode the matrix with huffman coding tables
Input:
error (512, 640): a matrix with all the errors
list_dic (num_dic + 1,): a list of huffman coding table
bins (num_bins - 1,): a list of threshold to cut the bins
Return:
encoded (512, 640): encoded matrix
"""
returnable_encode = ""
# copy the error matrix (including the boundary)
encoded = np.copy(error).astype(int).astype(str).astype(object)
#diff = np.reshape(diff,(510,638))
# loop through all the pixel to encode
for i in range(encoded.shape[0]):
for j in range(encoded.shape[1]):
if i == 0 or i == encoded.shape[0]-1 or j == 0 or j == encoded.shape[1]-1:
returnable_encode += list_dic[0][encoded[i][j]]
elif diff[i-1][j-1] <= bins[0]:
returnable_encode += list_dic[1][encoded[i][j]]
elif diff[i-1][j-1] <= bins[1] and diff[i-1][j-1] > bins[0]:
returnable_encode +=list_dic[2][encoded[i][j]]
elif diff[i-1][j-1] <= bins[2] and diff[i-1][j-1] > bins[1]:
returnable_encode +=list_dic[3][encoded[i][j]]
else:
returnable_encode += list_dic[4][encoded[i][j]]
return returnable_encode
def decoder(encoded_string, list_dic, bins, use_diff):
"""
This function decodes the encoded_matrix.
Input:
A (3 X 3): system of equation
list_dic (num_dic + 1,): a list of huffman coding table
encoded_matrix (512, 640): encoded matrix
bins (num_bins - 1,): a list of threshold to cut the bins
Return:
decode_matrix (512, 640): decoded matrix
"""
A = np.array([[3,0,-1],[0,3,3],[1,-3,-4]]) # the matrix for system of equation
# change the dictionary back to list
# !!!!!WARNING!!!! has to change this part, everytime you change the number of bins
the_keys0 = list(list_dic[0].keys())
the_values0 = list(list_dic[0].values())
the_keys1 = list(list_dic[1].keys())
the_values1 = list(list_dic[1].values())
the_keys2 = list(list_dic[2].keys())
the_values2 = list(list_dic[2].values())
the_keys3 = list(list_dic[3].keys())
the_values3 = list(list_dic[3].values())
the_keys4 = list(list_dic[4].keys())
the_values4 = list(list_dic[4].values())
#Matrix system of points that will be used to solve the least squares fitting hyperplane
points = np.array([[-1,-1,1], [-1,0,1], [-1,1,1], [0,-1,1]])
decode_matrix = np.zeros((512,640))
# loop through all the element in the matrix
for i in range(decode_matrix.shape[0]):
for j in range(decode_matrix.shape[1]):
# if it's the very first pixel on the image
if i == 0 and j == 0:
colorvalue, encoded_string = decode_string(encoded_string,the_keys=the_keys0, the_values=the_values0)
decode_matrix[i][j] = colorvalue
# if it's on the boundary (any of the 4 edges)
elif i == 0 or i == decode_matrix.shape[0]-1 or j == 0 or j == decode_matrix.shape[1]-1:
colorvalue, encoded_string = decode_string(encoded_string,the_keys=the_keys0, the_values=the_values0)
decode_matrix[i][j] = colorvalue + decode_matrix[0][0]
# if not the boundary
else:
# predict the image with the known pixel value
z0 = decode_matrix[i-1][j-1]
z1 = decode_matrix[i-1][j]
z2 = decode_matrix[i-1][j+1]
z3 = decode_matrix[i][j-1]
y0 = int(-z0+z2-z3)
y1 = int(z0+z1+z2)
y2 = int(-z0-z1-z2-z3)
y = np.vstack((y0,y1,y2))
if use_diff:
difference = max(z0,z1,z2,z3) - min(z0,z1,z2,z3)
else:
f, difference, rank, s = la.lstsq(points, [z0,z1,z2,z3], rcond=None)
difference = difference.astype(int)
predict = np.round(np.round(np.linalg.solve(A,y)[-1][0],1))
# add on the difference by searching the dictionary
# !!!!!WARNING!!!! has to change this part, eveytime you change the number of bins
if difference <= bins[0]:
colorvalue, encoded_string = decode_string(encoded_string,the_keys=the_keys1, the_values=the_values1)
decode_matrix[i][j] = colorvalue + int(predict)
elif difference <= bins[1] and difference > bins[0]:
colorvalue, encoded_string = decode_string(encoded_string,the_keys=the_keys2, the_values=the_values2)
decode_matrix[i][j] = colorvalue + int(predict)
elif difference <= bins[2] and difference > bins[1]:
colorvalue, encoded_string = decode_string(encoded_string,the_keys=the_keys3, the_values=the_values3)
decode_matrix[i][j] = colorvalue + int(predict)
else:
colorvalue, encoded_string = decode_string(encoded_string,the_keys=the_keys4, the_values=the_values4)
decode_matrix[i][j] = colorvalue + int(predict)
return decode_matrix.astype(int)
def read_from_file(filename):
with open(filename, 'rb') as file:
return file.read()
def bitstring_to_bytes(input_string):
int_array = []
length_of_string = len(input_string)
while length_of_string >= 8:
int_array.append(int(input_string[:8],2))
input_string = input_string[8:]
length_of_string = len(input_string)
if length_of_string > 0:
zerobuffer = ""
for _ in range(8-length_of_string):
zerobuffer += "0"
int_array.append(int(input_string+zerobuffer,2))
return bytes(int_array)
def bytes_to_bitstring(input_bytearray):
end_string = ""
int_array = [i for i in input_bytearray]
for i, item in enumerate(int_array):
end_string += (bin(item)[2:].zfill(8))
return end_string
starttime = time()
scenes = file_extractor()
newnamesforlater = []
images = image_extractor(scenes)
list_dic, ogbins = make_dictionary(images[0:10], 4, False)
file_size_ratios = []
np.save("first_dic.npy", list_dic)
for i in range(10):
image, new_error, diff = huffman(images[i], 4, False)
encoded_string1 = encoder(new_error, list_dic, diff, ogbins)
# reconstruct_image = decoder(A, encoded_string, list_dic, bins, False)
# print(np.allclose(image, reconstruct_image))
inletters = bitstring_to_bytes(encoded_string1)
if images[i][:-5] == ".tiff":
newname = images[i][:-5]
else:
newname = images[i][:-4]
newnamesforlater.append(newname + "_Compressed.txt")
with open(newname + "_Compressed.txt", 'wb') as f:
f.write(inletters)
list_dic = np.load("first_dic.npy", allow_pickle="TRUE")
ogbins = [21,32,48]
for i,item in enumerate(newnamesforlater[0:10]):
image, new_error, diff = huffman(images[i], 4, False)
encoded_string2 = bytes_to_bitstring(read_from_file(item))
reconstruct_image = decoder(encoded_string2, list_dic, ogbins, False)
print(np.allclose(image, reconstruct_image))
print(time() - starttime)
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment