From a90c3c9fe9d24d3810baa741dc377aee02a6bbbd Mon Sep 17 00:00:00 2001 From: Vaishnavi Kulkarni <2020BTEIT00031@walchandsangli.ac.in> Date: Tue, 9 Aug 2022 12:04:32 +0530 Subject: [PATCH] Added Assignment 2 --- .../compressed_image.jpeg | Bin 0 -> 10964 bytes 2020BTEIT00031_Assignment 2/compression.py | 219 ++++++++++++++++++ 2020BTEIT00031_Assignment 2/encoding.py | 135 +++++++++++ 2020BTEIT00031_Assignment 2/observation.txt | 16 ++ .../original_image.jpeg | Bin 0 -> 18809 bytes 5 files changed, 370 insertions(+) create mode 100644 2020BTEIT00031_Assignment 2/compressed_image.jpeg create mode 100644 2020BTEIT00031_Assignment 2/compression.py create mode 100644 2020BTEIT00031_Assignment 2/encoding.py create mode 100644 2020BTEIT00031_Assignment 2/observation.txt create mode 100644 2020BTEIT00031_Assignment 2/original_image.jpeg diff --git a/2020BTEIT00031_Assignment 2/compressed_image.jpeg b/2020BTEIT00031_Assignment 2/compressed_image.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..b67c1a57a43b78196cb369ddeb27a50d21977c06 GIT binary patch literal 10964 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 E0ivhj9RL6T literal 0 HcmV?d00001 diff --git a/2020BTEIT00031_Assignment 2/compression.py b/2020BTEIT00031_Assignment 2/compression.py new file mode 100644 index 0000000..2566d1a --- /dev/null +++ b/2020BTEIT00031_Assignment 2/compression.py @@ -0,0 +1,219 @@ +# Name:Vaishnvi Vivekanand Kulkarni +# PRN:2020BTEIT00031 +# Program for image compression using 3-Dimensional Codebook + + + +import cv2 +import lbg +import math +import numpy as np +import matplotlib.pyplot as plt +import pandas as pd + +def generate_training(img, block): + """ + This function will generate the training codevector from the image via non-overlapped patch. + """ + train_vec = [] + x = block[0] + y = block[1] + for i in range(0, img.shape[0], x): + for j in range(0, img.shape[1], y): + train_vec.append(img[i:i + x, j:j + y].reshape((x * y))) + return (np.array(train_vec)) + +def generate_multi_training(path_list, block): + """ + This function will generate the training codevector from the multi-image via non-overlapped patch. + """ + img_list = [] + for path in path_list: + img_list.append(cv2.imread(path, cv2.IMREAD_GRAYSCALE)) + train_vec = [] + x = block[0] + y = block[1] + for img in img_list: + for i in range(0, img.shape[0], x): + for j in range(0, img.shape[1], y): + train_vec.append(img[i:i + x, j:j + y].reshape((x * y))) + return (np.array(train_vec)) + + +def distance(a, b): + """ + This function will calculate the distance (MSE) of two vectors. + """ + return np.mean((np.subtract(a, b) ** 2)) + + +def closest_match(src, cb): + """ + This function will get the closest distance (nearest) of the compared vectors. + """ + c = np.zeros((cb.shape[0],)) + for i in range(0, cb.shape[0]): + c[i] = distance(src, cb[i]) + minimum = np.argmin(c, axis=0) + return minimum + + +def encode_image(img, cb, block): + """ + This function will encode (compress) the image by sending the image block, vectorize it then get the index of the + closest vector to form the compressed data. + """ + x = block[0] + y = block[1] + compressed = np.zeros((img.shape[0] // y, img.shape[1] // x)) + ix = 0 + for i in range(0, img.shape[0], x): + iy = 0 + for j in range(0, img.shape[1], y): + src = img[i:i + x, j:j + y].reshape((x * y)).copy() + k = closest_match(src, cb) + compressed[ix, iy] = k + iy += 1 + ix += 1 + return compressed + + +def decode_image(cb, compressed, block): + """ + This function will decode the compressed data beck to the image by taking the index of the codebook then copy the associate vector to the image block. + """ + x = block[0] + y = block[1] + original = np.zeros((compressed.shape[0] * y, compressed.shape[1] * x)) + ix = 0 + for i in range(0, compressed.shape[0]): + iy = 0 + for j in range(0, compressed.shape[1]): + original[ix:ix + x, iy:iy + y] = cb[int(compressed[i, j])].reshape(block) + iy += y + ix += x + return original + + +def save_weight(filename, cb): + """ + This function will save the absolute and relative weight as CSV file. + """ + fd = open(filename, 'a') + for i in range(0, cb.shape[0]): + linecsv = str(cb[i]) + '\n' + fd.write(linecsv) + fd.close() + + +def save_codebook(filename, cb): + """ + This function will save the codebook as CSV file. + """ + fd = open(filename, 'a') + for i in range(0, cb.shape[0]): + linecsv = '' + for j in range(0, cb.shape[1]): + linecsv = linecsv + str(cb[i, j]) + ',' + linecsv = linecsv + '\n' + fd.write(linecsv) + fd.close() + + +def save_csv(root, csv, cb, cb_abs_w, cb_rel_w): + """ + This function will save the codebook and weight as CSV file given the associate name. + """ + numpy_cb = np.array(cb) + numpy_abs_w = np.array(cb_abs_w) + numpy_rel_w = np.array(cb_rel_w) + save_codebook(root + 'CB_' + csv + '.csv', numpy_cb) + save_weight(root + '3CB_abs_' + csv + '.csv', numpy_abs_w) + save_weight(root + '3CB_rel_' + csv + '.csv', numpy_rel_w) + + +def sim_protocol(img, cb_size, epsilon, block, root, outpng): + """ + This function needod for doing simulation for different scenario. + """ + train_X = generate_training(img, block) + cb, cb_abs_w, cb_rel_w = lbg.generate_codebook(train_X, cb_size, epsilon) + cb_n = np.array(cb) + cb_abs_w_n = np.array(cb_abs_w) + cb_rel_w_n = np.array(cb_rel_w) + result = encode_image(img, cb_n, block) + final_result = decode_image(cb_n, result, block) + fig = plt.gcf() + fig.set_figheight(6) + fig.set_figwidth(6) + plt.imshow(final_result, cmap='gray') + cv2.imwrite(root + outpng + '.png', final_result) + save_csv(root, outpng, cb_n, cb_abs_w_n, cb_rel_w_n) + +def sim_multi_protocol(path_list, cb_size, epsilon, block, root, outpng): + """ + This function needod for doing simulation for different scenario. + """ + train_X = generate_multi_training(path_list, block) + cb, cb_abs_w, cb_rel_w = lbg.generate_codebook(train_X, cb_size, epsilon) + cb_n = np.array(cb) + cb_abs_w_n = np.array(cb_abs_w) + cb_rel_w_n = np.array(cb_rel_w) + save_csv(root, outpng, cb_n, cb_abs_w_n, cb_rel_w_n) + print('Weight Saved as: '+outpng) + +def sim_testing_protocol(inpath_list, weight, block, outpng): + """ + This function needod for doing simulation for different scenario. + """ + fig, ax = plt.subplots(nrows=1, ncols=4) + idx = 1 + for inpath in inpath_list: + img = cv2.imread(inpath, cv2.IMREAD_GRAYSCALE) + cb = pd.read_csv(weight, header=None).as_matrix().astype('int') + cb = cb[:,0:cb.shape[1]-1] + result = encode_image(img, cb, block) + final_result = decode_image(cb, result, block) + rem = inpath.replace('./images/', '') + cv2.imwrite(outpng + rem.replace('.csv',''), final_result) + psnr_value = psnr(img, final_result) + ax = plt.subplot(1, 4, idx) + ax.set_title('PSNR = {}'.format(psnr_value)) + ax.imshow(final_result, cmap='gray') + idx+=1 + fig.set_figheight(6) + fig.set_figwidth(24) + plt.show() + + +def psnr(img1, img2): + """ + This function will calculate the PSNR of two images. + """ + mse = np.mean( (img1 - img2) ** 2 ) + if mse == 0: + return 100 + PIXEL_MAX = 255.0 + return 20 * math.log10(PIXEL_MAX / math.sqrt(mse)) + + +def measure_psnr(apath, bpath): + """ + This function will doing PSNR comparison of two images. + """ + img1 = cv2.imread(apath, cv2.IMREAD_GRAYSCALE) + img2 = cv2.imread(bpath, cv2.IMREAD_GRAYSCALE) + print('PSNR: {}'.format(psnr(img1, img2))) + + fig, ax = plt.subplots(nrows=1, ncols=2) + ax1 = plt.subplot(1, 2, 1) + ax1.set_title("Original") + ax1.imshow(img1, cmap='gray') + + ax2 = plt.subplot(1, 2, 2) + ax2.set_title("Result") + ax2.imshow(img2, cmap='gray') + + fig.set_figheight(7) + fig.set_figwidth(14) + plt.show() \ No newline at end of file diff --git a/2020BTEIT00031_Assignment 2/encoding.py b/2020BTEIT00031_Assignment 2/encoding.py new file mode 100644 index 0000000..baa8ab2 --- /dev/null +++ b/2020BTEIT00031_Assignment 2/encoding.py @@ -0,0 +1,135 @@ +# Name:Vaishnvi Vivekanand Kulkarni +# PRN:2020BTEIT00031 +# Program using huffman coding to compress the image + +import re +import numpy as np +from PIL import Image +print("Huffman Compression Program") + +file = "output.png" +my_string = np.asarray(Image.open(file), np.uint8) +shape = my_string.shape +a = my_string + +my_string = str(my_string.tolist()) + + +letters = [] +only_letters = [] +for letter in my_string: + if letter not in letters: + frequency = my_string.count(letter) + letters.append(frequency) + letters.append(letter) + only_letters.append(letter) + +nodes = [] +while len(letters) > 0: + nodes.append(letters[0:2]) + letters = letters[2:] +nodes.sort() +huffman_tree = [] +huffman_tree.append(nodes) + + +def combine_nodes(nodes): + pos = 0 + newnode = [] + if len(nodes) > 1: + nodes.sort() + nodes[pos].append("1") + nodes[pos+1].append("0") + combined_node1 = (nodes[pos][0] + nodes[pos+1][0]) + combined_node2 = (nodes[pos][1] + nodes[pos+1][1]) + newnode.append(combined_node1) + newnode.append(combined_node2) + newnodes = [] + newnodes.append(newnode) + newnodes = newnodes + nodes[2:] + nodes = newnodes + huffman_tree.append(nodes) + combine_nodes(nodes) + return huffman_tree + + +newnodes = combine_nodes(nodes) + +huffman_tree.sort(reverse=True) +print("Huffman tree with merged pathways:") + +checklist = [] +for level in huffman_tree: + for node in level: + if node not in checklist: + checklist.append(node) + else: + level.remove(node) +count = 0 +for level in huffman_tree: + print("Level", count, ":", level) + count += 1 +print() + +letter_binary = [] +if len(only_letters) == 1: + lettercode = [only_letters[0], "0"] + letter_binary.append(letter_code*len(my_string)) +else: + for letter in only_letters: + code = "" + for node in checklist: + if len(node) > 2 and letter in node[1]: + code = code + node[2] + lettercode = [letter, code] + letter_binary.append(lettercode) +print(letter_binary) +print("Binary code generated:") +for letter in letter_binary: + print(letter[0], letter[1]) + +bitstring = "" +for character in my_string: + for item in letter_binary: + if character in item: + bitstring = bitstring + item[1] +binary = "0b"+bitstring +print("Your message as binary is:") + + +uncompressed_file_size = len(my_string)*7 +compressed_file_size = len(binary)-2 +print("Your original file size was", uncompressed_file_size, + "bits. The compressed size is:", compressed_file_size) +print("This is a saving of ", uncompressed_file_size-compressed_file_size, "bits") +output = open("compressed.txt", "w+") +print("Compressed file generated as compressed.txt") +output = open("compressed.txt", "w+") +print("Decoding.......") +output.write(bitstring) + +bitstring = str(binary[2:]) +uncompressed_string = "" +code = "" +for digit in bitstring: + code = code+digit + pos = 0 + for letter in letter_binary: + if code == letter[1]: + uncompressed_string = uncompressed_string+letter_binary[pos][0] + code = "" + pos += 1 + +temp = re.findall(r'\d+', uncompressed_string) +res = list(map(int, temp)) +res = np.array(res) +res = res.astype(np.uint8) +res = np.reshape(res, shape) +print(res) +print("Observe the shapes and input and output arrays are matching or not") +print("Input image dimensions:", shape) +print("Output image dimensions:", res.shape) +data = Image.fromarray(res) +data.save('previous.png') +if a.all() == res.all(): + print("Success") \ No newline at end of file diff --git a/2020BTEIT00031_Assignment 2/observation.txt b/2020BTEIT00031_Assignment 2/observation.txt new file mode 100644 index 0000000..6712a76 --- /dev/null +++ b/2020BTEIT00031_Assignment 2/observation.txt @@ -0,0 +1,16 @@ +Name:Vaishnavi Vivekanand Kulkarni +PRN:2020BTEIT00031 + + +Original image size:19kb +compressed image size:11kb + +Observation:By using Vector quantization algorithm: +Output image is 40% smaller than input image +So, compressed ratio is:0.4 + + +Huffman Encoding Time Complexity: + +Time complexity: O(nlogn) + \ No newline at end of file diff --git a/2020BTEIT00031_Assignment 2/original_image.jpeg b/2020BTEIT00031_Assignment 2/original_image.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..c8ca61fd06cd16ddd8682571c9bd430bf8ad1ff5 GIT binary patch literal 18809 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 YY^akk literal 0 HcmV?d00001