Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Badger2040: Build .py conversion into convert.py #288

Merged
merged 1 commit into from
Mar 10, 2022
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
90 changes: 77 additions & 13 deletions examples/badger2040/image_converter/convert.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,32 @@
#!/usr/bin/env python3
"""
Converts images into a format suitable for display on Badger 2040.

# converts images into a format suitable for display on badger2040. this
# includes scaling the image fit the longest edge, cropping down to 296x128
# and reducing to black and white with dither. the data is then output as an
# array that can be embedded directly into your c++ code
Optionally resizes images to 296x128 to fit the display.

Crunches images down to dithered, 1bit colour depth.

Outputs either in raw binary format or as a .py file for embedding into MicroPython.

Output to py functionality is borrwed from data_to_py.py, Copyright (c) 2016 Peter Hinch
"""

import io
import argparse
from PIL import Image, ImageEnhance
from pathlib import Path
import data_to_py


PY_HEADER = """# Code generated by convert.py.
"""

PY_FOOTER = """_mvdata = memoryview(_data)

def data():
return _mvdata

"""


parser = argparse.ArgumentParser(description='Converts images into the format used by Badger2040.')
parser.add_argument('file', nargs="+", help='input files to convert')
Expand All @@ -21,18 +38,67 @@
options = parser.parse_args()


class ByteWriter(object):
bytes_per_line = 16

def __init__(self, stream, varname):
self.stream = stream
self.stream.write('{} =\\\n'.format(varname))
self.bytecount = 0 # For line breaks

def _eol(self):
self.stream.write("'\\\n")

def _eot(self):
self.stream.write("'\n")

def _bol(self):
self.stream.write("b'")

# Output a single byte
def obyte(self, data):
if not self.bytecount:
self._bol()
self.stream.write('\\x{:02x}'.format(data))
self.bytecount += 1
self.bytecount %= self.bytes_per_line
if not self.bytecount:
self._eol()

# Output from a sequence
def odata(self, bytelist):
for byt in bytelist:
self.obyte(byt)

# ensure a correct final line
def eot(self): # User force EOL if one hasn't occurred
if self.bytecount:
self._eot()
self.stream.write('\n')


def convert_image(img):
if options.resize:
img = img.resize((296, 128)) # resize
img = img.resize((296, 128)) # resize
try:
enhancer = ImageEnhance.Contrast(img)
img = enhancer.enhance(2.0)
enhancer = ImageEnhance.Contrast(img)
img = enhancer.enhance(2.0)
except ValueError:
pass
img = img.convert("1") # convert to black and white
pass
img = img.convert("1") # convert to black and white
return img


def write_stream(header, footer, ip_stream, op_stream):
op_stream.write(header)
op_stream.write('\n')
data = ip_stream.read()
bw_data = ByteWriter(op_stream, '_data')
bw_data.odata(data)
bw_data.eot()
op_stream.write(footer)


# create map of images based on input filenames
for input_filename in options.file:
with Image.open(input_filename) as img:
Expand All @@ -59,7 +125,7 @@ def convert_image(img):
output_filename = Path(input_filename).with_suffix(".py")
print(f"Saving to {output_filename}, {w}x{h}")
with open(output_filename, "w") as out:
data_to_py.write_stream(io.BytesIO(bytes(output_data)), out)
write_stream(PY_HEADER, PY_FOOTER, io.BytesIO(bytes(output_data)), out)
else:
image_code = '''\
static const uint8_t {image_name}[{count}] = {{
Expand All @@ -68,5 +134,3 @@ def convert_image(img):
'''.format(image_name=image_name, count=len(output_data), byte_data=", ".join(str(b) for b in output_data))

print(image_code)