Daryl Fung
added imgur bucket
932db78
raw
history blame
2.98 kB
import io
import math
import zlib
import base64
import pickle
import numpy as np
from PIL import Image
from os import PathLike
from typing import Any
HEADER_SIZE = 4 # bytes
def object_to_image(obj: Any) -> Image.Image:
"""Take a object and convert it to an image.
The object is first pickled to bytes, then the array is padded and reshaped
into a NxNx4 array and converted to an RGBA image
Example of 1d pickle array to image array:
[1,2,3,4,5,6,7] ->
[
[[0,0,0,7], [1,2,3,4]],
[[5,6,7,0], [0,0,0,0]]
]
The array begins with a 4 byte header representing the length of the data.
Zeroes are added to the end the ensure that the number pixels is a suare number
"""
data = compress(pickle.dumps(obj))
header = len(data).to_bytes(length=HEADER_SIZE, byteorder='big')
data = header + data
# divide the data into pixels, add an extra if data doesn't perfectly fit
whole_pixels, remainder = divmod(len(data), 4)
n_pixels = whole_pixels + 1 * (remainder != 0)
# ensure n_pixels is a square number
side_length = math.ceil(math.sqrt(n_pixels))
n_pixels = side_length ** 2
n_bytes = n_pixels * 4
# right pad the data with zeros so it can be shaped to (n,n,4)
data += b'\x00' * (n_bytes - len(data))
# create (n,n,4) array from pickle data
data_arr = np.frombuffer(data, dtype=np.uint8)
img_arr = np.reshape(data_arr, (side_length, side_length, 4))
return Image.fromarray(img_arr)
def image_to_object(image: Image.Image) -> Any:
"""Take a PIL Image and unpickle it's data to an object
Convert the image to an array, flatten to obtain serial bytes, then unpickle
these bytes.
"""
data_arr = np.array(image).flatten()
data = data_arr.tobytes()
# number of bytes containing meaningful data
length = int.from_bytes(data[:HEADER_SIZE], 'big')
# slice off header
data = data[HEADER_SIZE:]
# slice off zero padding if any
data = data[:length]
return pickle.loads(decompress(data))
def image_to_b64_string(img: Image.Image) -> str:
"""Return a str representing the image as b64 encoded bytes"""
# save the image as PNG to a buffer
buffer = io.BytesIO()
img.save(buffer, 'png')
buffer.seek(0)
return base64.b64encode(buffer.read()).decode()
def bytes_to_image(b: bytes) -> Image.Image:
"""Create an Image using raw bytes"""
buffer = io.BytesIO(b)
buffer.seek(0)
return Image.open(buffer)
def compress(b: bytes, level=9) -> bytes:
"""Compress the bytes using zlib"""
return zlib.compress(b, level)
def decompress(b: bytes) -> bytes:
"""Decompress the bytes using zlib"""
return zlib.decompress(b)
def write_compressed(data: bytes, fp: PathLike) -> None:
with open(fp, 'wb') as f:
f.write(compress(data))
def read_compressed(fp: PathLike) -> bytes:
with open(fp, 'rb') as f:
return decompress(f.read())