Skip to content
Open
Show file tree
Hide file tree
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
Binary file added __pycache__/imageDataTools.cpython-38.pyc
Binary file not shown.
Binary file added __pycache__/imageTools.cpython-38.pyc
Binary file not shown.
165 changes: 165 additions & 0 deletions imageDataTools.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
"""

python2 or python3.

"""

import itertools
from collections import deque



ALPHABET = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
#case is ignored by builtin int(), but int() can't go above base 36 anyway, so it probably doesn't matter.

def assert_equals(thing0, thing1):
assert thing0 == thing1, "{} does not equal {}.".format(thing0, thing1)



def gen_chunks(input_seq, length):
if length < 1:
raise ValueError("chunk length must be at least 1.")
labeling_function = (lambda toLabel: toLabel[0]//length)
labled_group_gen = itertools.groupby(enumerate(input_seq), labeling_function)
unlabled_group_gen = (labeledGroup[1] for labeledGroup in labled_group_gen)
#remove enumeration:
result = ((itemB[1] for itemB in unlabledGroup) for unlabledGroup in unlabled_group_gen)
return result



def int_to_base(value, base):
if not value >= 0:
raise ValueError("negative value: {}. Value must be positive.".format(value))
if not base >= 2:
raise ValueError("invalid base: {}.".format(base))
result = deque([])
while value > 0:
current_digit = value % base
result.appendleft(current_digit)
value //= base
if len(result) == 0:
result.appendleft(0)
assert len(result) >= 1
return list(result)


def int_seq_to_str(int_seq):
return "".join(ALPHABET[item] for item in int_seq)


def int_to_base_str(value, base):
return int_seq_to_str(int_to_base(value, base))



def int8rgb_color_is_valid(color):
return all((
len(color) == 3,
min(color) >= 0,
max(color) < 256,
all(isinstance(componentValue, int) for componentValue in color)
))


def int8rgb_color_component_to_hex(component, length=2):

if not 0 < length <= 2:
raise ValueError("invalid length {}.".format(length))
if not 0 <= component < 256:
raise ValueError("invalid component {}.".format(component))

result = int_to_base_str(component, 16)
result = result.rjust(2, ALPHABET[0])
assert len(result) == 2
result = result[:length]
return result


def int8rgb_color_to_hex(
color, length=2,
component_header="", component_delimiter="",
):

if not 0 < length <= 2:
raise ValueError("invalid length {}.".format(length))
if not int8rgb_color_is_valid(color):
raise ValueError("invalid color {}.".format(color))

component_text_gen = (component_header + int8rgb_color_component_to_hex(value, length=length) for value in color)

return component_delimiter.join(component_text_gen)


def int8rgb_pixels_to_hex(
pixels, chars_per_channel=2,
pixel_header="", pixel_delimiter="",
component_header="", component_delimiter="",
):

pixel_text_gen = (pixel_header + int8rgb_color_to_hex(
color,
length=chars_per_channel,
component_header=component_header, component_delimiter=component_delimiter,
) for color in pixels)

return pixel_delimiter.join(pixel_text_gen)


def int8rgb_pixel_rows_to_hex(
pixels,
auto_row_length=None,
row_header="", row_delimiter="",
**other_format_kwargs,
):

if auto_row_length is not None:
input_row_generator = gen_chunks(pixels, length=auto_row_length)
else:
input_row_generator = pixels
output_row_generator = (row_header + int8rgb_pixels_to_hex(pixel_row, **other_format_kwargs) for pixel_row in input_row_generator)
return row_delimiter.join(output_row_generator)



def gen_triplet_tuples_from_int_seq(int_seq):
current_triplet = []
for i, item in enumerate(int_seq):
current_triplet.append(item)
if len(current_triplet) == 3:
yield tuple(current_triplet)
current_triplet = []
if not len(current_triplet) == 0:
raise ValueError("int seq had invalid length {} (not divisible by 3).".format(i))


def int_lists_to_triplet_tuple_lists(int_list_seq):
result = []
for int_list in int_list_seq:
tripletTupleList = list(gen_triplet_tuples_from_int_seq(int_list))
result.append(tripletTupleList)
return result





def test():
assert_equals(int8rgb_color_to_hex([255,255,255], component_delimiter="!"), "ff!ff!ff")

for test_int in [0,1,2,3,5,8,13,21,34,55,127,128,129,255]:
assert_equals(int(int8rgb_color_component_to_hex(test_int), 16), test_int)

assert_equals(int8rgb_color_to_hex([14,15,16], length=2, component_header="?", component_delimiter="!"), "?0e!?0f!?10")
assert_equals(int8rgb_color_to_hex([14,15,16], length=1, component_header="?", component_delimiter="!"), "?0!?0!?1")

assert_equals(int8rgb_pixels_to_hex([[14,15,16], [30,31,32]], chars_per_channel=2, pixel_header="#", component_delimiter=","), "#0e,0f,10#1e,1f,20")

assert_equals(int8rgb_pixel_rows_to_hex([[14,15,16], [30,31,32]], auto_row_length=2), int8rgb_pixels_to_hex([[14,15,16], [30,31,32]]))

assert_equals(int_lists_to_triplet_tuple_lists([[1,2,3,4,5,6],[7,8,9,10,11,12]]), [[(1,2,3),(4,5,6)],[(7,8,9),(10,11,12)]])

test()


203 changes: 203 additions & 0 deletions imageTools.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,203 @@
"""

Python3 recommended.

"""

import os
import fnmatch
import warnings

from PIL import Image

import imageDataTools
from imageDataTools import assert_equals



def get_image_int8rgb_pixels(filename):
try:
image = Image.open(filename) #image is now of type PngImageFile, not Image.
imageData = image.getdata() #imageData is now of type ImagingCore, not list.
ARGBPixels = [item for item in imageData] #these may be rgb already, depending on the input image.
try:
RGBPixels = [item[:3] for item in ARGBPixels]
except TypeError:
#this is because Pillow returns data in different formats depending on which file it is reading.
raise IOError("File could not be properly read by Pillow.")
finally:
image.close()
return RGBPixels


def save_int8rgb_tuples_as_half_hex(
data,
output_filename=None, mode=None,
decoration="{output_filename} = \"{result}\"",
**format_kwargs,
):
#does not save when output_filename is None.
warnings.warn("save_int8rgb_tuples_as_half_hex is deprecated. Use imageDataTools.int8rgb_pixels_to_hex and/or save_text_to_file instead.")

if decoration is None:
decoration = "{result}"
if output_filename is not None:
if mode is None:
raise ValueError("mode not specified")

result = imageDataTools.int8rgb_pixels_to_hex(data, chars_per_channel=1, **format_kwargs)
if output_filename is None:
return result
else:
with open(output_filename, mode) as output_file:
#assert "{output_filename}" in decoration
assert "{result}" in decoration
decorated_result = decoration.replace("{output_filename}", output_filename).replace("{result}", result)
output_file.write(decorated_result)
return result


def save_text_to_file(text, output_filename, mode):

with open(output_filename, mode) as output_file:
output_file.write(text)


def gen_matching_file_names(folder_name, file_name_pattern):

for testFileName in os.listdir(folder_name):
if fnmatch.fnmatch(testFileName, file_name_pattern):
yield testFileName


def gen_matching_png_file_names_and_pixels(
folder_name, file_name_pattern,
skip_errors=True, supress_warnings=False,
):

for scanFileName in gen_matching_file_names(folder_name, file_name_pattern):
scanFullFileName = folder_name + "/" + scanFileName
try:
scanFilePixels = get_image_int8rgb_pixels(scanFullFileName)
except IOError as ioe:
if skip_errors:
if not supress_warnings:
print("Skipping file {} because it couldn't be read using the current Pillow settings.".format(scanFullFileName))
else:
raise ioe
continue
yield scanFileName, scanFilePixels


def process_png_files(
folder_name,
file_name_pattern="*.png",
do_console_output=True, do_file_output=False,
common_output_filename=None, mode="w",
output_filename_prefix="outputs/", output_filename_suffix=".txt",
console_line_length=48, file_line_length=None,
console_output_delimiter="\n---\n", console_output_decoration="{name}:\n{content}",
file_output_delimiter="", file_output_decoration="\n{name} = \"{content}\"\n",
skip_errors=True, supress_warnings=False,
):
"""
folder_name - folder to search for input files, without trailing slash.
file_name_pattern - bash-like, e.g. "*.png" or "*x*.png".
do_console_output - controls whether all processed files will be printed.
do_file_output - controls whether any files will be created or modified.
common_output_filename - when not None, this option forces all file\
outputs to be added to the same output file in append mode.
output_filename_prefix, output_filename_suffix - prefix and suffix to be\
added to the base name, which is either the name of the input image\
file or the provided common output filename.
console_line_length - custom line wrapping length for console output, or\
None for unmodified output.
file_line_length - custom line wrapping length for writing to files, or\
None for unmodified output.
"""

useSingleOutputFile = (common_output_filename is not None)

fileNamesAndPixelsGen = gen_matching_png_file_names_and_pixels(
folder_name, file_name_pattern,
skip_errors=skip_errors, supress_warnings=supress_warnings
)

for scanFileIndex, scanFileNameAndPixels in enumerate(fileNamesAndPixelsGen):
scanFileName, scanFilePixels = scanFileNameAndPixels
scanFilePixelsAsHalfHex = imageDataTools.int8rgb_pixels_to_hex(scanFilePixels, chars_per_channel=1)

if do_file_output:
outputFileNameBase = common_output_filename if useSingleOutputFile else scanFileName
outputFileName = output_filename_prefix + outputFileNameBase + output_filename_suffix

stringToSave = file_output_decoration.replace("{name}", scanFileName).replace("{content}", scanFilePixelsAsHalfHex)

if scanFileIndex != 0 and useSingleOutputFile:
stringToSave = file_output_delimiter + stringToSave
if file_line_length is not None:
stringToSave = wrap_text(stringToSave, line_length=file_line_length)

modeForCurrentWrite = ("a" if (useSingleOutputFile and scanFileIndex > 0) else mode)
save_text_to_file(stringToSave, output_filename=outputFileName, mode=modeForCurrentWrite)

if do_console_output:
stringToPrint = console_output_decoration.replace("{name}", scanFileName).replace("{content}", scanFilePixelsAsHalfHex)

if scanFileIndex != 0:
stringToPrint = console_output_delimiter + stringToPrint
if console_line_length is not None:
stringToPrint = wrap_text(stringToPrint, line_length=console_line_length)

print(stringToPrint)


def gen_short_lines(text, line_length):
if line_length < 1:
raise ValueError("line length must be at least 1.")
for line in text.split("\n"):
i = 0
while i < len(line):
yield line[i:i+line_length]
i += line_length


def wrap_text(text, line_length=80):
return "\n".join(gen_short_lines(text, line_length))


def print_wrapped(text, line_length=80):
for line in gen_short_lines(text, line_length):
print(line)



SAMPLE_IMAGE_HALF_HEX="\
390390390390390390390000000390390390390390390390\
390390390390390390000ff0ff0000390390390390390390\
390390390390390390000ff0ff0000390390390390390390\
390390390390390000ff0ff0ff0ff0000390390390390390\
000000000000000000ff0ff0ff0ff0000000000000000000\
000ff0ff0ff0ff0ff0ff0ff0ff0ff0ff0ff0ff0ff0ff0000\
390000ff0ff0ff0ff0000ff0ff0000ff0ff0ff0ff0000390\
390390000ff0ff0ff0000ff0ff0000ff0ff0ff0000390390\
390390390000ff0ff0000ff0ff0000ff0ff0000390390390\
390390390000ff0ff0ff0ff0ff0ff0ff0ff0000390390390\
390390000ff0ff0ff0ff0ff0ff0ff0ff0ff0ff0000390390\
390390000ff0ff0ff0ff0ff0ff0ff0ff0ff0ff0000390390\
390000ff0ff0ff0ff0ff0000000ff0ff0ff0ff0ff0000390\
390000ff0ff0ff0000000390390000000ff0ff0ff0000390\
000ff0ff0000000390390390390390390000000ff0ff0000\
000000000390390390390390390390390390390000000000"

def test():
assert_equals(save_int8rgb_tuples_as_half_hex(get_image_int8rgb_pixels("images/SAMPLE.png"), pixel_header=""), SAMPLE_IMAGE_HALF_HEX)
assert_equals(wrap_text("abcdefg\n1234567", line_length=3), "abc\ndef\ng\n123\n456\n7")


test()





Binary file added images/SAMPLE.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.