Commit ef51cf73 authored by Nathaniel Callens's avatar Nathaniel Callens
Browse files

predic_pix_lstsq added

parent 817a3ee5
Loading
Loading
Loading
Loading
+49 −0
Original line number Diff line number Diff line
%% Cell type:code id:14f74f21 tags:

``` python
import numpy as np
from matplotlib import pyplot as plt
from itertools import product
import os
import sys
from PIL import Image
from scipy.optimize import minimize,linprog
import time
import seaborn as sns
from sklearn.neighbors import KernelDensity
import pandas as pd
from collections import Counter
import time
```

%% Cell type:code id:c16af61f tags:

``` python
def file_extractor(dirname="images"):
    files = os.listdir(dirname)
    scenes = []
    for file in files:
        if file == '.DS_Store':
            continue
        else:
            scenes.append(os.path.join(dirname, file))
    return scenes

def image_extractor(scenes):
    image_folder = []
    for scene in scenes:
        files = os.listdir(scene)
        for file in files:
            #if file[-4:] == ".jp4" or file[-7:] == "_6.tiff":
            if file[-5:] != ".tiff" or file[-7:] == "_6.tiff":
                continue
            else:
                image_folder.append(os.path.join(scene, file))
    return image_folder #returns a list of file paths to .tiff files in the specified directory given in file_extractor

def im_distribution(images, num):
    """
    Function that extracts tiff files from specific cameras and returns a list of all
    the tiff files corresponding to that camera. i.e. all pictures labeled "_7.tiff" or otherwise
    specified camera numbers.

    Parameters:
        images (list): list of all tiff files, regardless of classification. This is NOT a list of directories but
        of specific tiff files that can be opened right away. This is the list that we iterate through and
        divide.

        num (str): a string designation for the camera number that we want to extract i.e. "14" for double digits
        of "_1" for single digits.

    Returns:
        tiff (list): A list of tiff files that have the specified designation from num. They are the files extracted
        from the 'images' list that correspond to the given num.
    """
    tiff = []
    for im in images:
        if im[-7:-5] == num:
            tiff.append(im)
    return tiff
```

%% Cell type:code id:aceba613 tags:

``` python
def predict_pix(tiff_image):
    """
    This function predict the pixel values excluding the boundary.
    Using the 4 neighbor pixel values and MSE to predict the next pixel value
    (-1,1) (0,1) (1,1)  => relative position of the 4 other given values
    (-1,0) (0,0)        => (0,0) is the one we want to predict
    take the derivative of mean square error to solve for the system of equation
    A = np.array([[3,0,-1],[0,3,3],[1,-3,-4]])
    A @ [a, b, c] = [-z0+z2-z3, z0+z1+z2, -z0-z1-z2-z3] where z0 = (-1,1), z1 = (0,1), z2 = (1,1), z3 = (-1,0)
    and the predicted pixel value is c.

    Input:
    tiff_image (string): path to the tiff file

    Return:
    image   (512 X 640): original image
    predict (325380,): predicted image exclude the boundary
    diff.   (325380,): difference between the min and max of four neighbors exclude the boundary
    error   (325380,): difference between the original image and predicted image
    A       (3 X 3): system of equation
    """
    image = Image.open(tiff_image)    #Open the image and read it as an Image object
    image = np.array(image)[1:,:]    #Convert to an array, leaving out the first row because the first row is just housekeeping data
    image = image.astype(int)
    print(image.shape)
    # use
    A = np.array([[3,0,-1],[0,3,3],[1,-3,-4]]) # the matrix for system of equation
    # where z0 = (-1,1), z1 = (0,1), z2 = (1,1), z3 = (-1,0)
    z0 = image[0:-2,0:-2]   # get all the first pixel for the entire image
    z1 = image[0:-2,1:-1]   # get all the second pixel for the entire image
    z2 = image[0:-2,2::]    # get all the third pixel for the entire image
    z3 = image[1:-1,0:-2]   # get all the forth pixel for the entire image
    # calculate the out put of the system of equation
    y0 = np.ravel(-z0+z2-z3)
    y1 = np.ravel(z0+z1+z2)
    y2 = np.ravel(-z0-z1-z2-z3)
    y = np.vstack((y0,y1,y2))
    # use numpy solver to solve the system of equations all at once
    #predict = np.floor(np.linalg.solve(A,y)[-1])
    predict = np.round(np.round((np.linalg.solve(A,y)[-1]),1))
    # flatten the neighbor pixlels and stack them together
    z0 = np.ravel(z0)
    z1 = np.ravel(z1)
    z2 = np.ravel(z2)
    z3 = np.ravel(z3)
    neighbor = np.vstack((z0,z1,z2,z3)).T
    # calculate the difference
    diff = np.max(neighbor,axis = 1) - np.min(neighbor, axis=1)
    # calculate the error
    error = np.ravel(image[1:-1,1:-1])-predict

    return image, predict, diff, error, A
```

%% Cell type:code id:6b965751 tags:

``` python
"""
this huffman coding code is found online
https://favtutor.com/blogs/huffman-coding
"""

class NodeTree(object):
    def __init__(self, left=None, right=None):
        self.left = left
        self.right = right

    def children(self):
        return self.left, self.right

    def __str__(self):
        return self.left, self.right


def huffman_code_tree(node, binString=''):
    '''
    Function to find Huffman Code
    '''
    if type(node) is str:
        return {node: binString}
    (l, r) = node.children()
    d = dict()
    d.update(huffman_code_tree(l, binString + '0'))
    d.update(huffman_code_tree(r, binString + '1'))
    return d


def make_tree(nodes):
    '''
    Function to make tree
    :param nodes: Nodes
    :return: Root of the tree
    '''
    while len(nodes) > 1:
        (key1, c1) = nodes[-1]
        (key2, c2) = nodes[-2]
        nodes = nodes[:-2]
        node = NodeTree(key1, key2)
        nodes.append((node, c1 + c2))
        nodes = sorted(nodes, key=lambda x: x[1], reverse=True)
    return nodes[0][0]
```

%% Cell type:code id:b7561883 tags:

``` python
def huffman(image, num_bins=4):
    """
    This function is used to encode the error based on the difference
    and split the difference into different bins

    Input:
    image    (string): path to the tiff file
    num_bins (int): number of bins

    Return:
    list_dic   (num_bins + 1): a list of dictionary
    image      (512, 640): original image
    new_error  (512, 640): error that includes the boundary
    diff       (510, 638): difference of min and max of the 4 neighbors
    boundary   (2300,): the boundary values after subtracting the very first pixel value
    predict    (325380,): the list of predicted values
    bins       (num_bins)
    A       (3 X 3): system of equation

    """
    # get the prediction error and difference
    image, predict, diff, error, A = predict_pix(image)

    # get the number of points in each bins
    data_points_per_bin = len(diff) // num_bins

    # sort the difference and create the bins
    sorted_diff = diff.copy()
    sorted_diff.sort()
    bins = [sorted_diff[i*data_points_per_bin] for i in range(1,num_bins)]

    # get the boundary
    boundary = np.hstack((image[0,:],image[-1,:],image[1:-1,0],image[1:-1,-1]))

    # take the difference of the boundary with the very first pixel
    boundary = boundary - image[0,0]
    boundary[0] = image[0,0]

    # huffman encode the boundary
    string = [str(i) for i in boundary]
    freq = dict(Counter(string))
    freq = sorted(freq.items(), key=lambda x: x[1], reverse=True)
    node = make_tree(freq)
    encode = huffman_code_tree(node)

    # create a list of huffman table
    list_dic = [encode]
    n = len(bins)

    # loop through different bins
    for i in range (0,n):
        # the fisrt bin
        if i == 0 :
            # get the point within the bin and huffman encode
            mask = diff <= bins[i]
            string = [str(i) for i in error[mask].astype(int)]
            freq = dict(Counter(string))
            freq = sorted(freq.items(), key=lambda x: x[1], reverse=True)
            node = make_tree(freq)
            encode = huffman_code_tree(node)
            list_dic.append(encode)

        # the middle bins
        else:
            # get the point within the bin and huffman encode
            mask = diff > bins[i-1]
            new_error = error[mask]
            mask2 = diff[mask] <= bins[i]
            string = [str(i) for i in new_error[mask2].astype(int)]
            freq = dict(Counter(string))
            freq = sorted(freq.items(), key=lambda x: x[1], reverse=True)
            node = make_tree(freq)
            encode = huffman_code_tree(node)
            list_dic.append(encode)

    # the last bin
    # get the point within the bin and huffman encode
    mask = diff > bins[-1]
    string = [str(i) for i in error[mask].astype(int)]
    freq = dict(Counter(string))
    freq = sorted(freq.items(), key=lambda x: x[1], reverse=True)
    node = make_tree(freq)
    encode = huffman_code_tree(node)
    list_dic.append(encode)

    # create a error matrix that includes the boundary (used in encoding matrix)
    new_error = np.copy(image)
    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

    diff = np.reshape(diff,(510,638))
    # return the huffman dictionary
    return list_dic, image, new_error, diff, boundary, predict, bins, A

```

%% Cell type:code id:2eb774d2 tags:

``` python
def encoder(error, list_dic, diff, bound, bins):
    # 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:
                encoded[i][j] = list_dic[0][encoded[i][j]]
            elif diff[i-1][j-1] <= bins[0]:
                encoded[i][j] = list_dic[1][encoded[i][j]]
            elif diff[i-1][j-1] <= bins[1] and diff[i-1][j-1] > bins[0]:
                encoded[i][j] = list_dic[2][encoded[i][j]]
            elif diff[i-1][j-1] <= bins[2] and diff[i-1][j-1] > bins[1]:
                encoded[i][j] = list_dic[3][encoded[i][j]]
            else:
                encoded[i][j] = list_dic[4][encoded[i][j]]

    return encoded
```

%% Cell type:code id:8eeb40d0 tags:

``` python
def decoder(A, encoded_matrix, list_dic, bins):
    """
    Function that accecpts the prediction matrix A for the linear system,
    the encoded matrix of error values, and the encoding dicitonary.
    """
    # change the dictionary back to list
    # !!!!!WARNING!!!! has to change this part, eveytime 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())

    error_matrix = np.zeros((512,640))
    # loop through all the element in the matrix
    for i in range(error_matrix.shape[0]):
        for j in range(error_matrix.shape[1]):
            # if it's the very first pixel on the image
            if i == 0 and j == 0:
                error_matrix[i][j] = int(the_keys0[the_values0.index(encoded_matrix[i,j])])
            # if it's on the boundary
            elif i == 0 or i == error_matrix.shape[0]-1 or j == 0 or j == error_matrix.shape[1]-1:
                error_matrix[i][j] = int(the_keys0[the_values0.index(encoded_matrix[i,j])]) + error_matrix[0][0]
            # if not the boundary
            else:
                # predict the image with the known pixel value
                z0 = error_matrix[i-1][j-1]
                z1 = error_matrix[i-1][j]
                z2 = error_matrix[i-1][j+1]
                z3 = error_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))
                difference = max(z0,z1,z2,z3) - min(z0,z1,z2,z3)
                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]:
                    error_matrix[i][j] = int(the_keys1[the_values1.index(encoded_matrix[i,j])]) + int(predict)
                elif difference <= bins[1] and difference > bins[0]:
                    error_matrix[i][j] = int(the_keys2[the_values2.index(encoded_matrix[i,j])]) + int(predict)
                elif difference <= bins[2] and difference > bins[1]:
                    error_matrix[i][j] = int(the_keys3[the_values3.index(encoded_matrix[i,j])]) + int(predict)
                else:
                    error_matrix[i][j] = int(the_keys4[the_values4.index(encoded_matrix[i,j])]) + int(predict)


    return error_matrix.astype(int)
```

%% Cell type:code id:f959fe93 tags:

``` python
def compress_rate(image, error, diff, bound, list_dic, bins):
    # the bits for the original image
    o_len = 0
    # the bits for the compressed image
    c_len = 0
    # initializing the varible
    im = np.reshape(image,(512, 640))
    real_b = np.hstack((im[0,:],im[-1,:],im[1:-1,0],im[1:-1,-1]))
    original = im[1:-1,1:-1].reshape(-1)
    diff = diff.reshape(-1)

    # calculate the bit for boundary
    for i in range(0,len(bound)):
        o_len += len(bin(real_b[i])[2:])
        c_len += len(list_dic[0][str(bound[i])])

    # calculate the bit for the pixels inside the boundary
    for i in range(0,len(original)):
        # for the original image
        o_len += len(bin(original[i])[2:])

        # check the difference and find the coresponding huffman table
        # !!!!!WARNING!!!! has to change this part, eveytime you change the number of bins
        if diff[i] <= bins[0]:
            c_len += len(list_dic[1][str(int(error[i]))])

        elif diff[i] <= bins[1] and diff[i] > bins[0]:
            c_len += len(list_dic[2][str(int(error[i]))])

        elif diff[i] <= bins[2] and diff[i] > bins[1]:
            c_len += len(list_dic[3][str(int(error[i]))])

        else:
            c_len += len(list_dic[5][str(int(error[i]))])

    return c_len/o_len
```

%% Cell type:code id:3e0e9742 tags:

``` python
scenes = file_extractor()
images = image_extractor(scenes)
list_dic, image, new_error, diff, bound, predict, bins, A = huffman(images[0], 4)
encoded_matrix = encoder(new_error, list_dic, diff, bound, bins)
reconstruct_image = decoder(A, encoded_matrix, list_dic, bins)
print(np.allclose(image, reconstruct_image))
print(len(list_dic))
```

%% Output

    (512, 640)
    True
    5

%% Cell type:code id:004e8ba8 tags:

``` python

print(bins)
```

%% Output

    [26, 40, 62]

%% Cell type:code id:a282f9e6 tags:

``` python
def predict_pix_lstsq(tiff_list):
    """
    Predict the next pixel using a fit hyperplane of the four closest pixels.
    The gradient measure in this function is the summed distance to the fitted hyperplane
    of each of the four points, aka the residual from the least squares function. The previous
    predict_pix function uses the difference between the minimal and maximal pixels of the surrounding
    four.
    """

    image = tiff_list
    image = Image.open(image)    #Open the image and read it as an Image object
    image = np.array(image)[1:,:]    #Convert to an array, leaving out the first row because the first row is just housekeeping data
    image = image.astype(int)
    A = np.array([[3,0,-1],[0,3,3],[1,-3,-4]]) # the matrix for system of equation
    z0 = image[0:-2,0:-2]   # get all the first pixel for the entire image
    z1 = image[0:-2,1:-1]   # get all the second pixel for the entire image
    z2 = image[0:-2,2::]    # get all the third pixel for the entire image
    z3 = image[1:-1,0:-2]   # get all the forth pixel for the entire image
    # calculate the out put of the system of equation
    y0 = np.ravel(-z0+z2-z3)
    y1 = np.ravel(z0+z1+z2)
    y2 = np.ravel(-z0-z1-z2-z3)
    y = np.vstack((y0,y1,y2))

    # use numpy solver to solve the system of equations all at once
    predict = np.round(np.round((np.linalg.solve(A,y)[-1]),1)) #round the solution to the nearest integer so that encoding/decoding is easier

    points = np.array([[-1,-1,1], [-1,0,1], [-1,1,1], [0,-1,1]]) #Matrix system of points that will be used to solve the least squares fitting hyperplane



    # flatten the neighbor pixels and stack them together
    z0 = np.ravel(z0)
    z1 = np.ravel(z1)
    z2 = np.ravel(z2)
    z3 = np.ravel(z3)
    neighbor = np.vstack((z0,z1,z2,z3)).T

    f, res, rank, s = la.lstsq(points, neighbor.T, rcond=None)


    # calculate the difference
    diff = np.max(neighbor,axis = 1) - np.min(neighbor, axis=1)

    # flatten the image to a vector
    image = np.ravel(image[1:-1,1:-1])
    error = image-predict

    return image, predict, res, error, A, diff
```
+89 −42

File changed.

Preview size limit exceeded, changes collapsed.

+49 −0
Original line number Diff line number Diff line
%% Cell type:code id:14f74f21 tags:

``` python
import numpy as np
from matplotlib import pyplot as plt
from itertools import product
import os
import sys
from PIL import Image
from scipy.optimize import minimize,linprog
import time
import seaborn as sns
from sklearn.neighbors import KernelDensity
import pandas as pd
from collections import Counter
import time
```

%% Cell type:code id:c16af61f tags:

``` python
def file_extractor(dirname="images"):
    files = os.listdir(dirname)
    scenes = []
    for file in files:
        if file == '.DS_Store':
            continue
        else:
            scenes.append(os.path.join(dirname, file))
    return scenes

def image_extractor(scenes):
    image_folder = []
    for scene in scenes:
        files = os.listdir(scene)
        for file in files:
            #if file[-4:] == ".jp4" or file[-7:] == "_6.tiff":
            if file[-5:] != ".tiff" or file[-7:] == "_6.tiff":
                continue
            else:
                image_folder.append(os.path.join(scene, file))
    return image_folder #returns a list of file paths to .tiff files in the specified directory given in file_extractor

def im_distribution(images, num):
    """
    Function that extracts tiff files from specific cameras and returns a list of all
    the tiff files corresponding to that camera. i.e. all pictures labeled "_7.tiff" or otherwise
    specified camera numbers.

    Parameters:
        images (list): list of all tiff files, regardless of classification. This is NOT a list of directories but
        of specific tiff files that can be opened right away. This is the list that we iterate through and
        divide.

        num (str): a string designation for the camera number that we want to extract i.e. "14" for double digits
        of "_1" for single digits.

    Returns:
        tiff (list): A list of tiff files that have the specified designation from num. They are the files extracted
        from the 'images' list that correspond to the given num.
    """
    tiff = []
    for im in images:
        if im[-7:-5] == num:
            tiff.append(im)
    return tiff
```

%% Cell type:code id:aceba613 tags:

``` python
def predict_pix(tiff_image):
    """
    This function predict the pixel values excluding the boundary.
    Using the 4 neighbor pixel values and MSE to predict the next pixel value
    (-1,1) (0,1) (1,1)  => relative position of the 4 other given values
    (-1,0) (0,0)        => (0,0) is the one we want to predict
    take the derivative of mean square error to solve for the system of equation
    A = np.array([[3,0,-1],[0,3,3],[1,-3,-4]])
    A @ [a, b, c] = [-z0+z2-z3, z0+z1+z2, -z0-z1-z2-z3] where z0 = (-1,1), z1 = (0,1), z2 = (1,1), z3 = (-1,0)
    and the predicted pixel value is c.

    Input:
    tiff_image (string): path to the tiff file

    Return:
    image   (512 X 640): original image
    predict (325380,): predicted image exclude the boundary
    diff.   (325380,): difference between the min and max of four neighbors exclude the boundary
    error   (325380,): difference between the original image and predicted image
    A       (3 X 3): system of equation
    """
    image = Image.open(tiff_image)    #Open the image and read it as an Image object
    image = np.array(image)[1:,:]    #Convert to an array, leaving out the first row because the first row is just housekeeping data
    image = image.astype(int)
    print(image.shape)
    # use
    A = np.array([[3,0,-1],[0,3,3],[1,-3,-4]]) # the matrix for system of equation
    # where z0 = (-1,1), z1 = (0,1), z2 = (1,1), z3 = (-1,0)
    z0 = image[0:-2,0:-2]   # get all the first pixel for the entire image
    z1 = image[0:-2,1:-1]   # get all the second pixel for the entire image
    z2 = image[0:-2,2::]    # get all the third pixel for the entire image
    z3 = image[1:-1,0:-2]   # get all the forth pixel for the entire image
    # calculate the out put of the system of equation
    y0 = np.ravel(-z0+z2-z3)
    y1 = np.ravel(z0+z1+z2)
    y2 = np.ravel(-z0-z1-z2-z3)
    y = np.vstack((y0,y1,y2))
    # use numpy solver to solve the system of equations all at once
    #predict = np.floor(np.linalg.solve(A,y)[-1])
    predict = np.round(np.round((np.linalg.solve(A,y)[-1]),1))
    # flatten the neighbor pixlels and stack them together
    z0 = np.ravel(z0)
    z1 = np.ravel(z1)
    z2 = np.ravel(z2)
    z3 = np.ravel(z3)
    neighbor = np.vstack((z0,z1,z2,z3)).T
    # calculate the difference
    diff = np.max(neighbor,axis = 1) - np.min(neighbor, axis=1)
    # calculate the error
    error = np.ravel(image[1:-1,1:-1])-predict

    return image, predict, diff, error, A
```

%% Cell type:code id:6b965751 tags:

``` python
"""
this huffman coding code is found online
https://favtutor.com/blogs/huffman-coding
"""

class NodeTree(object):
    def __init__(self, left=None, right=None):
        self.left = left
        self.right = right

    def children(self):
        return self.left, self.right

    def __str__(self):
        return self.left, self.right


def huffman_code_tree(node, binString=''):
    '''
    Function to find Huffman Code
    '''
    if type(node) is str:
        return {node: binString}
    (l, r) = node.children()
    d = dict()
    d.update(huffman_code_tree(l, binString + '0'))
    d.update(huffman_code_tree(r, binString + '1'))
    return d


def make_tree(nodes):
    '''
    Function to make tree
    :param nodes: Nodes
    :return: Root of the tree
    '''
    while len(nodes) > 1:
        (key1, c1) = nodes[-1]
        (key2, c2) = nodes[-2]
        nodes = nodes[:-2]
        node = NodeTree(key1, key2)
        nodes.append((node, c1 + c2))
        nodes = sorted(nodes, key=lambda x: x[1], reverse=True)
    return nodes[0][0]
```

%% Cell type:code id:b7561883 tags:

``` python
def huffman(image, num_bins=4):
    """
    This function is used to encode the error based on the difference
    and split the difference into different bins

    Input:
    image    (string): path to the tiff file
    num_bins (int): number of bins

    Return:
    list_dic   (num_bins + 1): a list of dictionary
    image      (512, 640): original image
    new_error  (512, 640): error that includes the boundary
    diff       (510, 638): difference of min and max of the 4 neighbors
    boundary   (2300,): the boundary values after subtracting the very first pixel value
    predict    (325380,): the list of predicted values
    bins       (num_bins)
    A       (3 X 3): system of equation

    """
    # get the prediction error and difference
    image, predict, diff, error, A = predict_pix(image)

    # get the number of points in each bins
    data_points_per_bin = len(diff) // num_bins

    # sort the difference and create the bins
    sorted_diff = diff.copy()
    sorted_diff.sort()
    bins = [sorted_diff[i*data_points_per_bin] for i in range(1,num_bins)]

    # get the boundary
    boundary = np.hstack((image[0,:],image[-1,:],image[1:-1,0],image[1:-1,-1]))

    # take the difference of the boundary with the very first pixel
    boundary = boundary - image[0,0]
    boundary[0] = image[0,0]

    # huffman encode the boundary
    string = [str(i) for i in boundary]
    freq = dict(Counter(string))
    freq = sorted(freq.items(), key=lambda x: x[1], reverse=True)
    node = make_tree(freq)
    encode = huffman_code_tree(node)

    # create a list of huffman table
    list_dic = [encode]
    n = len(bins)

    # loop through different bins
    for i in range (0,n):
        # the fisrt bin
        if i == 0 :
            # get the point within the bin and huffman encode
            mask = diff <= bins[i]
            string = [str(i) for i in error[mask].astype(int)]
            freq = dict(Counter(string))
            freq = sorted(freq.items(), key=lambda x: x[1], reverse=True)
            node = make_tree(freq)
            encode = huffman_code_tree(node)
            list_dic.append(encode)

        # the middle bins
        else:
            # get the point within the bin and huffman encode
            mask = diff > bins[i-1]
            new_error = error[mask]
            mask2 = diff[mask] <= bins[i]
            string = [str(i) for i in new_error[mask2].astype(int)]
            freq = dict(Counter(string))
            freq = sorted(freq.items(), key=lambda x: x[1], reverse=True)
            node = make_tree(freq)
            encode = huffman_code_tree(node)
            list_dic.append(encode)

    # the last bin
    # get the point within the bin and huffman encode
    mask = diff > bins[-1]
    string = [str(i) for i in error[mask].astype(int)]
    freq = dict(Counter(string))
    freq = sorted(freq.items(), key=lambda x: x[1], reverse=True)
    node = make_tree(freq)
    encode = huffman_code_tree(node)
    list_dic.append(encode)

    # create a error matrix that includes the boundary (used in encoding matrix)
    new_error = np.copy(image)
    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

    diff = np.reshape(diff,(510,638))
    # return the huffman dictionary
    return list_dic, image, new_error, diff, boundary, predict, bins, A

```

%% Cell type:code id:2eb774d2 tags:

``` python
def encoder(error, list_dic, diff, bound, bins):
    # 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:
                encoded[i][j] = list_dic[0][encoded[i][j]]
            elif diff[i-1][j-1] <= bins[0]:
                encoded[i][j] = list_dic[1][encoded[i][j]]
            elif diff[i-1][j-1] <= bins[1] and diff[i-1][j-1] > bins[0]:
                encoded[i][j] = list_dic[2][encoded[i][j]]
            elif diff[i-1][j-1] <= bins[2] and diff[i-1][j-1] > bins[1]:
                encoded[i][j] = list_dic[3][encoded[i][j]]
            else:
                encoded[i][j] = list_dic[4][encoded[i][j]]

    return encoded
```

%% Cell type:code id:8eeb40d0 tags:

``` python
def decoder(A, encoded_matrix, list_dic, bins):
    """
    Function that accecpts the prediction matrix A for the linear system,
    the encoded matrix of error values, and the encoding dicitonary.
    """
    # change the dictionary back to list
    # !!!!!WARNING!!!! has to change this part, eveytime 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())

    error_matrix = np.zeros((512,640))
    # loop through all the element in the matrix
    for i in range(error_matrix.shape[0]):
        for j in range(error_matrix.shape[1]):
            # if it's the very first pixel on the image
            if i == 0 and j == 0:
                error_matrix[i][j] = int(the_keys0[the_values0.index(encoded_matrix[i,j])])
            # if it's on the boundary
            elif i == 0 or i == error_matrix.shape[0]-1 or j == 0 or j == error_matrix.shape[1]-1:
                error_matrix[i][j] = int(the_keys0[the_values0.index(encoded_matrix[i,j])]) + error_matrix[0][0]
            # if not the boundary
            else:
                # predict the image with the known pixel value
                z0 = error_matrix[i-1][j-1]
                z1 = error_matrix[i-1][j]
                z2 = error_matrix[i-1][j+1]
                z3 = error_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))
                difference = max(z0,z1,z2,z3) - min(z0,z1,z2,z3)
                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]:
                    error_matrix[i][j] = int(the_keys1[the_values1.index(encoded_matrix[i,j])]) + int(predict)
                elif difference <= bins[1] and difference > bins[0]:
                    error_matrix[i][j] = int(the_keys2[the_values2.index(encoded_matrix[i,j])]) + int(predict)
                elif difference <= bins[2] and difference > bins[1]:
                    error_matrix[i][j] = int(the_keys3[the_values3.index(encoded_matrix[i,j])]) + int(predict)
                else:
                    error_matrix[i][j] = int(the_keys4[the_values4.index(encoded_matrix[i,j])]) + int(predict)


    return error_matrix.astype(int)
```

%% Cell type:code id:f959fe93 tags:

``` python
def compress_rate(image, error, diff, bound, list_dic, bins):
    # the bits for the original image
    o_len = 0
    # the bits for the compressed image
    c_len = 0
    # initializing the varible
    im = np.reshape(image,(512, 640))
    real_b = np.hstack((im[0,:],im[-1,:],im[1:-1,0],im[1:-1,-1]))
    original = im[1:-1,1:-1].reshape(-1)
    diff = diff.reshape(-1)

    # calculate the bit for boundary
    for i in range(0,len(bound)):
        o_len += len(bin(real_b[i])[2:])
        c_len += len(list_dic[0][str(bound[i])])

    # calculate the bit for the pixels inside the boundary
    for i in range(0,len(original)):
        # for the original image
        o_len += len(bin(original[i])[2:])

        # check the difference and find the coresponding huffman table
        # !!!!!WARNING!!!! has to change this part, eveytime you change the number of bins
        if diff[i] <= bins[0]:
            c_len += len(list_dic[1][str(int(error[i]))])

        elif diff[i] <= bins[1] and diff[i] > bins[0]:
            c_len += len(list_dic[2][str(int(error[i]))])

        elif diff[i] <= bins[2] and diff[i] > bins[1]:
            c_len += len(list_dic[3][str(int(error[i]))])

        else:
            c_len += len(list_dic[5][str(int(error[i]))])

    return c_len/o_len
```

%% Cell type:code id:3e0e9742 tags:

``` python
scenes = file_extractor()
images = image_extractor(scenes)
list_dic, image, new_error, diff, bound, predict, bins, A = huffman(images[0], 4)
encoded_matrix = encoder(new_error, list_dic, diff, bound, bins)
reconstruct_image = decoder(A, encoded_matrix, list_dic, bins)
print(np.allclose(image, reconstruct_image))
print(len(list_dic))
```

%% Output

    (512, 640)
    True
    5

%% Cell type:code id:004e8ba8 tags:

``` python

print(bins)
```

%% Output

    [26, 40, 62]

%% Cell type:code id:a282f9e6 tags:

``` python
def predict_pix_lstsq(tiff_list):
    """
    Predict the next pixel using a fit hyperplane of the four closest pixels.
    The gradient measure in this function is the summed distance to the fitted hyperplane
    of each of the four points, aka the residual from the least squares function. The previous
    predict_pix function uses the difference between the minimal and maximal pixels of the surrounding
    four.
    """

    image = tiff_list
    image = Image.open(image)    #Open the image and read it as an Image object
    image = np.array(image)[1:,:]    #Convert to an array, leaving out the first row because the first row is just housekeeping data
    image = image.astype(int)
    A = np.array([[3,0,-1],[0,3,3],[1,-3,-4]]) # the matrix for system of equation
    z0 = image[0:-2,0:-2]   # get all the first pixel for the entire image
    z1 = image[0:-2,1:-1]   # get all the second pixel for the entire image
    z2 = image[0:-2,2::]    # get all the third pixel for the entire image
    z3 = image[1:-1,0:-2]   # get all the forth pixel for the entire image
    # calculate the out put of the system of equation
    y0 = np.ravel(-z0+z2-z3)
    y1 = np.ravel(z0+z1+z2)
    y2 = np.ravel(-z0-z1-z2-z3)
    y = np.vstack((y0,y1,y2))

    # use numpy solver to solve the system of equations all at once
    predict = np.round(np.round((np.linalg.solve(A,y)[-1]),1)) #round the solution to the nearest integer so that encoding/decoding is easier

    points = np.array([[-1,-1,1], [-1,0,1], [-1,1,1], [0,-1,1]]) #Matrix system of points that will be used to solve the least squares fitting hyperplane



    # flatten the neighbor pixels and stack them together
    z0 = np.ravel(z0)
    z1 = np.ravel(z1)
    z2 = np.ravel(z2)
    z3 = np.ravel(z3)
    neighbor = np.vstack((z0,z1,z2,z3)).T

    f, res, rank, s = la.lstsq(points, neighbor.T, rcond=None)


    # calculate the difference
    diff = np.max(neighbor,axis = 1) - np.min(neighbor, axis=1)

    # flatten the image to a vector
    image = np.ravel(image[1:-1,1:-1])
    error = image-predict

    return image, predict, res, error, A, diff
```
+89 −42

File changed.

Preview size limit exceeded, changes collapsed.