Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
I
image-compression
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Elphel
image-compression
Commits
e4fa4e0f
Commit
e4fa4e0f
authored
May 26, 2022
by
Bryce Hepner
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
just had to walk back one thing
parent
a51a255b
Changes
1
Hide whitespace changes
Inline
Side-by-side
Showing
1 changed file
with
464 additions
and
0 deletions
+464
-0
BryceCheckpoint.py
BryceCheckpoint.py
+464
-0
No files found.
BryceCheckpoint.py
0 → 100644
View file @
e4fa4e0f
# %%
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
import
numpy.linalg
as
la
# %%
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
[
-
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
# %%
def
predict_pix
(
tiff_image_path
,
difference
=
True
):
"""
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_path (string): path to the tiff file
Return:
image ndarray(512 X 640): original image
predict ndarray(325380,): predicted image excluding the boundary
diff. ndarray(325380,): IF difference = TRUE, difference between the min and max of four neighbors exclude the boundary
ELSE: the residuals of the four nearest pixels to a fitted hyperplane
error ndarray(325380,): difference between the original image and predicted image
A ndarray(3 X 3): system of equation
"""
image_obj
=
Image
.
open
(
tiff_image_path
)
#Open the image and read it as an Image object
image_array
=
np
.
array
(
image_obj
)[
1
:,:]
.
astype
(
int
)
#Convert to an array, leaving out the first row because the first row is just housekeeping data
# image_array = image_array.astype(int)
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_array
[
0
:
-
2
,
0
:
-
2
]
# get all the first pixel for the entire image
z1
=
image_array
[
0
:
-
2
,
1
:
-
1
]
# get all the second pixel for the entire image
z2
=
image_array
[
0
:
-
2
,
2
::]
# get all the third pixel for the entire image
z3
=
image_array
[
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
))
#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
]])
# 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
if
difference
:
# calculate the difference
diff
=
np
.
max
(
neighbor
,
axis
=
1
)
-
np
.
min
(
neighbor
,
axis
=
1
)
else
:
#Compute the best fitting hyperplane using least squares
#The res is the residuals of the four points used to fit the hyperplane (summed distance of each of the
#points to the hyperplane), it is a measure of gradient
f
,
diff
,
rank
,
s
=
la
.
lstsq
(
points
,
neighbor
.
T
,
rcond
=
None
)
diff
=
diff
.
astype
(
int
)
# calculate the error
error
=
np
.
ravel
(
image_array
[
1
:
-
1
,
1
:
-
1
])
-
predict
return
image_array
,
predict
,
diff
,
error
,
A
# %%
"""
this huffman encoding 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
))
#reverse True, decending order
sorted_nodes
=
sorted
(
nodes
,
key
=
lambda
x
:
x
[
1
],
reverse
=
True
)
return
sorted_nodes
[
0
][
0
]
# %%
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:
list_dic list (num_bins + 1): a list of dictionary
image_array array (512, 640): original image
new_error array (512, 640): error that includes the boundary
diff array (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 - 1,): a list of threshold to cut the bins
A (3 X 3): system of equation
"""
# get the image_array, etc
image_array
,
predict
,
diff
,
error
,
A
=
predict_pix
(
tiff_image_path
,
difference
)
# calculate the number of points that will go in each bin
data_points_per_bin
=
diff
.
size
//
num_bins
# sort the difference and create the bins
sorted_diff
=
np
.
sort
(
diff
.
copy
())
bins
=
[
sorted_diff
[
i
*
data_points_per_bin
]
for
i
in
range
(
1
,
num_bins
)]
# 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
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_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
diff
=
np
.
reshape
(
diff
,(
510
,
638
))
# return the huffman dictionary
return
huffman_encoding_list
,
image_array
,
new_error
,
diff
,
boundary
,
predict
,
bins
,
A
# %%
def
encoder
(
error
,
list_dic
,
diff
,
bound
,
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
bound (2300,): the boundary values after subtracting the very first pixel value
bins (num_bins - 1,): a list of threshold to cut the bins
Return:
encoded (512, 640): encoded matrix
"""
# 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
# %%
def
decoder
(
A
,
encoded_matrix
,
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
"""
# 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
())
#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
:
decode_matrix
[
i
][
j
]
=
int
(
the_keys0
[
the_values0
.
index
(
encoded_matrix
[
i
,
j
])])
# if it's on the boundary
elif
i
==
0
or
i
==
decode_matrix
.
shape
[
0
]
-
1
or
j
==
0
or
j
==
decode_matrix
.
shape
[
1
]
-
1
:
decode_matrix
[
i
][
j
]
=
int
(
the_keys0
[
the_values0
.
index
(
encoded_matrix
[
i
,
j
])])
+
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
]:
decode_matrix
[
i
][
j
]
=
int
(
the_keys1
[
the_values1
.
index
(
encoded_matrix
[
i
,
j
])])
+
int
(
predict
)
elif
difference
<=
bins
[
1
]
and
difference
>
bins
[
0
]:
decode_matrix
[
i
][
j
]
=
int
(
the_keys2
[
the_values2
.
index
(
encoded_matrix
[
i
,
j
])])
+
int
(
predict
)
elif
difference
<=
bins
[
2
]
and
difference
>
bins
[
1
]:
decode_matrix
[
i
][
j
]
=
int
(
the_keys3
[
the_values3
.
index
(
encoded_matrix
[
i
,
j
])])
+
int
(
predict
)
else
:
decode_matrix
[
i
][
j
]
=
int
(
the_keys4
[
the_values4
.
index
(
encoded_matrix
[
i
,
j
])])
+
int
(
predict
)
return
decode_matrix
.
astype
(
int
)
# %%
def
compress_rate
(
image
,
new_error
,
diff
,
bound
,
list_dic
,
bins
):
'''
This function is used to calculate the compression rate.
Input:
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
bound (2300,): the boundary values after subtracting the very first pixel value
list_dic (num_dic + 1,): a list of huffman coding table
bins (num_bins - 1,): a list of threshold to cut the bins
Return:
compression rate
'''
# 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
((
image
[
0
,:],
image
[
-
1
,:],
image
[
1
:
-
1
,
0
],
image
[
1
:
-
1
,
-
1
]))
original
=
image
[
1
:
-
1
,
1
:
-
1
]
.
reshape
(
-
1
)
diff
=
diff
.
reshape
(
-
1
)
error
=
new_error
[
1
:
-
1
,
1
:
-
1
]
.
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
[
4
][
str
(
int
(
error
[
i
]))])
return
c_len
/
o_len
# %%
scenes
=
file_extractor
()
images
=
image_extractor
(
scenes
)
list_dic
,
image
,
new_error
,
diff
,
bound
,
predict
,
bins
,
A
=
huffman
(
images
[
0
],
4
,
False
)
encoded_matrix
=
encoder
(
new_error
,
list_dic
,
diff
,
bound
,
bins
)
reconstruct_image
=
decoder
(
A
,
encoded_matrix
,
list_dic
,
bins
,
False
)
print
(
np
.
allclose
(
image
,
reconstruct_image
))
print
(
len
(
list_dic
))
# %%
compress_rate
(
image
,
new_error
,
diff
,
bound
,
list_dic
,
bins
)
# %%
print
(
sys
.
getsizeof
(
encoded_matrix
))
print
(
sys
.
getsizeof
(
reconstruct_image
))
# %%
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment