unlimited-storage

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README

commit ab1c5d9128c9745332474b519c914fe1c0accf1d
parent ee3fc9cf5720fe687fc13c8331c768d6444d7741
Author: andrew.laack <andrew.laack@imbue.com>
Date:   Sat, 13 Sep 2025 23:21:26 -0700

Added unlimited for python.

Diffstat:
Apython/decode.py | 76++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Apython/encode.py | 138+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 214 insertions(+), 0 deletions(-)

diff --git a/python/decode.py b/python/decode.py @@ -0,0 +1,76 @@ +import numpy as np +import os +from PIL import Image + +class Header: + def __init__(self, name, chunk, bytes_enc): + self.name = name + self.chunk = chunk + self.bytes_enc = bytes_enc + def __str__(self): + return f"Name: {self.name} Chunk Number: {self.chunk} Bytes Encoded: {self.bytes_enc}" + def __lt__(self, other): + if self.name != other.name: + return self.name < other.name + else: + return self.chunk < other.chunk + +def bits_to_byte(bits): + return sum(bit << (7 - i) for i, bit in enumerate(bits)) + +def get_header(image_path): + img = Image.open(image_path).convert('L') + pixels = list(img.getdata()) + + bits = [1 if px == 255 else 0 for px in pixels[:864]] + header_bytes = bytearray() + for i in range(0, 864, 8): + header_bytes.append(bits_to_byte(bits[i:i+8])) + + file_name_bytes = header_bytes[0:100] + chunk_number_bytes = header_bytes[100:104] + bytes_encoded_bytes = header_bytes[104:108] + + file_name = file_name_bytes.rstrip(b'\x00').decode('utf-8', errors='replace').replace('\x00', '') + chunk_number = int(np.frombuffer(chunk_number_bytes, dtype=np.int32)[0]) + bytes_encoded = int(np.frombuffer(bytes_encoded_bytes, dtype=np.int32)[0]) + + return Header(file_name, chunk_number, bytes_encoded) + +def extract_data_from_image(image_path, bytes_to_read): + img = Image.open(image_path).convert('L') + pixels = list(img.getdata()) + + data_pixels = pixels[864:] + + bits = [1 if px > 127 else 0 for px in data_pixels] + + data_bytes = bytearray() + total_bits_needed = bytes_to_read * 8 + + for i in range(0, total_bits_needed, 8): + byte_bits = bits[i:i+8] + if len(byte_bits) < 8: + byte_bits += [0] * (8 - len(byte_bits)) + data_bytes.append(bits_to_byte(byte_bits)) + + return data_bytes + +files = os.listdir() +headers = [] + +for file in files: + if file.endswith(".png"): + header = get_header(file) + headers.append((header, file)) + +headers.sort() + +for header, file in headers: + print(f"Decoding: {header}") + + data = extract_data_from_image(file, header.bytes_enc) + + mode = 'wb' if header.chunk == 1 else 'ab' + with open(header.name, mode) as f: + f.write(data) diff --git a/python/encode.py b/python/encode.py @@ -0,0 +1,138 @@ +import numpy as np +import math +import os +import sys +from PIL import Image +import random +import string + +def get_rnd_str(length): + random_string = ''.join(random.choices(string.ascii_letters + string.digits, k=length)) + return(random_string) + +# idea: +# encode text into XxX matrix for white/black bits +# use image magick or smt +# ffmpeg -> vid + + +def byte_to_bits(byte): + return [(byte >> i) & 1 for i in reversed(range(8))] + + +# assume 1920x1080 +# how many bits? +# 2073600 bits! +# 259,200 bytes + +# this should be a deterministic size for ease of encoding/decoding + +# reserved header: +# file name - 100 bytes - 800 bits / pixels +# chunk number - 4 bytes - 32 pixels +# bytes encoded - 4 bytes - 32 pixels + +# 108 * 8 = 864 + +def make_header(file_name, chunk_number, bytes_encoded): + file_name_bytes = file_name.encode("utf-8") + if len(file_name_bytes) > 100: + file_name_bytes = file_name_bytes[:100] + else: + file_name_bytes = file_name_bytes.ljust(100, b'\x00') + + chunk_num = np.int32(chunk_number).tobytes() + num_bytes = np.int32(bytes_encoded).tobytes() + + header = file_name_bytes + chunk_num + num_bytes + assert len(header) == 108 + return header + +# reserved header is 108 bytes + +# 259,200 - 108 = 259,092 + +x = int(float(sys.argv[1])) +y = int(float(sys.argv[2])) +total_bits_available = x * y +bits_for_data = total_bits_available - 864 +bytes_per_frame = bits_for_data // 8 + +print("Max bytes per frame: " + str(bytes_per_frame)) + +filename = sys.argv[3] +destination = sys.argv[4] +destination.removesuffix("/") + +if not os.path.exists(destination): + os.mkdir(destination) + +print("x: " + str(x)) +print("y: " + str(y)) +print("filename: " + filename) + +file_size_bytes = os.path.getsize(filename) +print("File size in bytes: " + str(file_size_bytes)) + + +chunks_required = math.ceil(file_size_bytes / bytes_per_frame) +print("Chunks required: " + str(chunks_required)) + +if chunks_required > 1: + bytes_per_chunk = [bytes_per_frame] * (chunks_required - 1) + bytes_per_chunk.append(file_size_bytes % bytes_per_frame) +else: + bytes_per_chunk = [file_size_bytes] + +assert sum(bytes_per_chunk) == file_size_bytes + +print("Bytes per chunk: " + str(bytes_per_chunk)) + + +with open(filename, "rb") as file: + file_bytes = file.read() + +# ENSURE HEADER IS 108 BYTES +# SIDE EFFECT FOR ARR +# BYTES TO WRITE IS NUM +# BYTES STR IS DATA +def create_image(arr, header, byte_str, start_idx, bytes_to_write, x): + + written = 0 + + offset = 0 + for byte in header: + for bit in byte_to_bits(byte): + x_idx = offset % x + y_idx = offset // x + arr[y_idx][x_idx] = 255 if bit else 0 + offset += 1 + written += 1 + + assert offset == 108 * 8 + + for i in range(bytes_to_write): + byte = byte_str[start_idx + i] + for bit in byte_to_bits(byte): + x_idx = offset % x + y_idx = offset // x + arr[y_idx][x_idx] = 255 if bit else 0 + written += 1 + offset += 1 + return written + + +index = 0 +chunk_num = 1 +for chunk_size in bytes_per_chunk: + arr = np.zeros((y,x)) + written = create_image(arr, make_header(filename, chunk_num, chunk_size), file_bytes, index, chunk_size, x) + img = Image.fromarray(arr) + img = img.convert('L') + fname = destination + "/" + str(get_rnd_str(16)) + ".png" + print("Writing to: " + fname) + img.save(fname) + index += chunk_size + chunk_num += 1 + +assert index == file_size_bytes