From 6f279a846ca21624df3e995857ffc4f8907748d2 Mon Sep 17 00:00:00 2001 From: Carson Date: Tue, 14 Jan 2020 10:16:28 +0800 Subject: [PATCH 1/7] (Make it conform to the naming convention of PEP8.) --- asciify.py | 49 ++++++++++++++++++++++++++++++++----------------- 1 file changed, 32 insertions(+), 17 deletions(-) diff --git a/asciify.py b/asciify.py index 2463731..a0f431d 100644 --- a/asciify.py +++ b/asciify.py @@ -1,6 +1,6 @@ from PIL import Image -ASCII_CHARS = ['.',',',':',';','+','*','?','%','S','#','@'] +ASCII_CHARS = ['.', ',', ':', ';', '+', '*', '?', '%', 'S', '#', '@'] ASCII_CHARS = ASCII_CHARS[::-1] ''' @@ -8,59 +8,72 @@ - takes as parameters the image, and the final width - resizes the image into the final width while maintaining aspect ratio ''' + + def resize(image, new_width=100): - (old_width, old_height) = image.size - aspect_ratio = float(old_height)/float(old_width) + old_width, old_height = image.size + aspect_ratio = float(old_height) / float(old_width) new_height = int(aspect_ratio * new_width) - new_dim = (new_width, new_height) + new_dim = new_width, new_height new_image = image.resize(new_dim) return new_image + + ''' -method grayscalify(): +method gray_scalify(): - takes an image as a parameter - returns the grayscale version of image ''' -def grayscalify(image): + + +def gray_scarify(image): return image.convert('L') + ''' method modify(): - replaces every pixel with a character whose intensity is similar ''' + + def modify(image, buckets=25): initial_pixels = list(image.getdata()) - new_pixels = [ASCII_CHARS[pixel_value//buckets] for pixel_value in initial_pixels] + new_pixels = [ASCII_CHARS[pixel_value // buckets] for pixel_value in initial_pixels] return ''.join(new_pixels) + ''' method do(): - does all the work by calling all the above functions ''' + + def do(image, new_width=100): image = resize(image) - image = grayscalify(image) + image = gray_scarify(image) pixels = modify(image) len_pixels = len(pixels) # Construct the image from the character list - new_image = [pixels[index:index+new_width] for index in range(0, len_pixels, new_width)] + new_image = [pixels[index:index + new_width] for index in range(0, len_pixels, new_width)] return '\n'.join(new_image) + ''' method runner(): - takes as parameter the image path and runs the above code - handles exceptions as well - provides alternative output options ''' + + def runner(path): - image = None try: image = Image.open(path) - except Exception: - print("Unable to find image in",path) - #print(e) + except FileNotFoundError as e: + print(f"Unable to find image in. {str(e)}") return image = do(image) @@ -71,10 +84,11 @@ def runner(path): # Note: This text file will be created by default under # the same directory as this python file, # NOT in the directory from where the image is pulled. - f = open('img.txt','w') + f = open('img.txt', 'w') f.write(image) f.close() + ''' method main(): - reads input from console @@ -83,9 +97,10 @@ def runner(path): if __name__ == '__main__': import sys import urllib.request + if sys.argv[1].startswith('http://') or sys.argv[1].startswith('https://'): urllib.request.urlretrieve(sys.argv[1], "asciify.jpg") - path = "asciify.jpg" + input_image_path = "asciify.jpg" else: - path = sys.argv[1] - runner(path) + input_image_path = sys.argv[1] + runner(input_image_path) From 8be8a170ab3bdd552c704c633eb0dac6704070cc Mon Sep 17 00:00:00 2001 From: Carson Date: Tue, 14 Jan 2020 17:55:05 +0800 Subject: [PATCH 2/7] use config.yaml to control the script --- asciify.py | 50 ++++++++++++++++++++++++++++++---------------- config/config.yaml | 4 ++++ 2 files changed, 37 insertions(+), 17 deletions(-) create mode 100644 config/config.yaml diff --git a/asciify.py b/asciify.py index a0f431d..22ac40e 100644 --- a/asciify.py +++ b/asciify.py @@ -1,4 +1,7 @@ from PIL import Image +import os +from pathlib import Path +import yaml # pip install pyyaml ASCII_CHARS = ['.', ',', ':', ';', '+', '*', '?', '%', 'S', '#', '@'] ASCII_CHARS = ASCII_CHARS[::-1] @@ -10,7 +13,8 @@ ''' -def resize(image, new_width=100): +def resize(image, new_width): + old_width, old_height = image.size aspect_ratio = float(old_height) / float(old_width) new_height = int(aspect_ratio * new_width) @@ -48,8 +52,8 @@ def modify(image, buckets=25): ''' -def do(image, new_width=100): - image = resize(image) +def do(image, new_width): + image = resize(image, new_width) image = gray_scarify(image) pixels = modify(image) @@ -69,13 +73,13 @@ def do(image, new_width=100): ''' -def runner(path): +def runner(src_img_path: Path, output_path: Path, new_width: int): try: - image = Image.open(path) + image = Image.open(src_img_path) except FileNotFoundError as e: print(f"Unable to find image in. {str(e)}") return - image = do(image) + image = do(image, new_width) # To print on console print(image) @@ -84,9 +88,9 @@ def runner(path): # Note: This text file will be created by default under # the same directory as this python file, # NOT in the directory from where the image is pulled. - f = open('img.txt', 'w') - f.write(image) - f.close() + with open(str(output_path), 'w') as f: + f.write(image) + os.startfile(output_path) ''' @@ -94,13 +98,25 @@ def runner(path): - reads input from console - profit ''' -if __name__ == '__main__': - import sys - import urllib.request - if sys.argv[1].startswith('http://') or sys.argv[1].startswith('https://'): - urllib.request.urlretrieve(sys.argv[1], "asciify.jpg") - input_image_path = "asciify.jpg" + +def load_setting_from_config() -> tuple: + with open('config/config.yaml', 'r', encoding='utf-8') as f: + config = yaml.load(f.read(), Loader=yaml.SafeLoader) + input_dict = config.get(Path(__file__).stem.upper()) + src_img_path = input_dict['SRC_IMG_PATH'] + download_dir = Path('./temp') + os.makedirs(download_dir, exist_ok=True) + if src_img_path.startswith('http://') or src_img_path.startswith('https://'): + img_path = download_dir.joinpath(Path(src_img_path).name) + urllib.request.urlretrieve(src_img_path, img_path) + input_dict['SRC_IMG_PATH'] = img_path else: - input_image_path = sys.argv[1] - runner(input_image_path) + input_dict['SRC_IMG_PATH'] = Path(input_dict['SRC_IMG_PATH']) + return input_dict['SRC_IMG_PATH'], Path(input_dict['OUTPUT_PATH']), input_dict.get('NEW_WIDTH', 100) + + +if __name__ == '__main__': + import urllib.request + _src_img_path, _output_path, _new_width = load_setting_from_config() + runner(_src_img_path, _output_path, _new_width) diff --git a/config/config.yaml b/config/config.yaml new file mode 100644 index 0000000..a3936b6 --- /dev/null +++ b/config/config.yaml @@ -0,0 +1,4 @@ +ASCIIFY: + SRC_IMG_PATH: octocat.png # it can from the Online, for example: http://p1.pstatp.com/large/pgc-image/c3b90be2c32749d3b744f1e322da8738 + OUTPUT_PATH: temp/img.txt + NEW_WIDTH: 200 # Height will be determined by width (equivalent scaling) From eb2c24fa329d8f09b74d9f1328a558149b3ed373 Mon Sep 17 00:00:00 2001 From: Carson Date: Tue, 14 Jan 2020 18:00:03 +0800 Subject: [PATCH 3/7] Can now specify which config.yaml to use - add USAGE - can now decide whether to print on the terminal --- asciify.py | 31 ++++++++++++++++++++++++------- config/config.yaml | 2 ++ 2 files changed, 26 insertions(+), 7 deletions(-) diff --git a/asciify.py b/asciify.py index 22ac40e..1ba54c7 100644 --- a/asciify.py +++ b/asciify.py @@ -2,10 +2,13 @@ import os from pathlib import Path import yaml # pip install pyyaml +import urllib.request ASCII_CHARS = ['.', ',', ':', ';', '+', '*', '?', '%', 'S', '#', '@'] ASCII_CHARS = ASCII_CHARS[::-1] +PRINT_CONSOLE_FLAG: bool + ''' method resize(): - takes as parameters the image, and the final width @@ -14,7 +17,6 @@ def resize(image, new_width): - old_width, old_height = image.size aspect_ratio = float(old_height) / float(old_width) new_height = int(aspect_ratio * new_width) @@ -81,8 +83,8 @@ def runner(src_img_path: Path, output_path: Path, new_width: int): return image = do(image, new_width) - # To print on console - print(image) + if PRINT_CONSOLE_FLAG: + print(image) # Else, to write into a file # Note: This text file will be created by default under @@ -100,8 +102,8 @@ def runner(src_img_path: Path, output_path: Path, new_width: int): ''' -def load_setting_from_config() -> tuple: - with open('config/config.yaml', 'r', encoding='utf-8') as f: +def load_setting_from_config(config_path: str) -> tuple: + with open(config_path, 'r', encoding='utf-8') as f: config = yaml.load(f.read(), Loader=yaml.SafeLoader) input_dict = config.get(Path(__file__).stem.upper()) src_img_path = input_dict['SRC_IMG_PATH'] @@ -113,10 +115,25 @@ def load_setting_from_config() -> tuple: input_dict['SRC_IMG_PATH'] = img_path else: input_dict['SRC_IMG_PATH'] = Path(input_dict['SRC_IMG_PATH']) + global PRINT_CONSOLE_FLAG + PRINT_CONSOLE_FLAG = input_dict['PRINT_CONSOLE'] return input_dict['SRC_IMG_PATH'], Path(input_dict['OUTPUT_PATH']), input_dict.get('NEW_WIDTH', 100) if __name__ == '__main__': - import urllib.request - _src_img_path, _output_path, _new_width = load_setting_from_config() + from argparse import ArgumentParser, RawTextHelpFormatter + + script_description = '\n'.join([desc for desc in + ['\n', + f'python asciify.py --help', + f'python asciify.py', + f'python asciify.py [CONFIG.YAML]', + f'\tpython asciify.py -c config/config.yaml', + ]]) + arg_parser = ArgumentParser(description='Convert Images into ASCII Art', + usage=script_description, formatter_class=RawTextHelpFormatter) + + arg_parser.add_argument('-c', '--config', dest='config', default='config/config.yaml', help="input the path of ``config.yaml``") + g_args = arg_parser.parse_args() + _src_img_path, _output_path, _new_width = load_setting_from_config(g_args.config) runner(_src_img_path, _output_path, _new_width) diff --git a/config/config.yaml b/config/config.yaml index a3936b6..fc5b3e0 100644 --- a/config/config.yaml +++ b/config/config.yaml @@ -2,3 +2,5 @@ ASCIIFY: SRC_IMG_PATH: octocat.png # it can from the Online, for example: http://p1.pstatp.com/large/pgc-image/c3b90be2c32749d3b744f1e322da8738 OUTPUT_PATH: temp/img.txt NEW_WIDTH: 200 # Height will be determined by width (equivalent scaling) + + PRINT_CONSOLE: False # print console or not \ No newline at end of file From dd4fb0ec87da559c6114e00b565dabe3e5acb54c Mon Sep 17 00:00:00 2001 From: Carson Date: Tue, 14 Jan 2020 18:11:14 +0800 Subject: [PATCH 4/7] (Add config/README.rst) --- config/README.rst | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 config/README.rst diff --git a/config/README.rst b/config/README.rst new file mode 100644 index 0000000..d593276 --- /dev/null +++ b/config/README.rst @@ -0,0 +1,3 @@ +You can decide which ``config.yaml`` to use through the CLI(Command-Line Interface). + +.. note:: see more: ``python asciify.py --help`` \ No newline at end of file From 8d114066870f76158221eb4165140293ce61880c Mon Sep 17 00:00:00 2001 From: Carson Date: Tue, 14 Jan 2020 18:42:35 +0800 Subject: [PATCH 5/7] can add more ASCII_CHARS --- asciify.py | 13 ++++++++----- config/config.yaml | 2 ++ 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/asciify.py b/asciify.py index 1ba54c7..a09b696 100644 --- a/asciify.py +++ b/asciify.py @@ -4,10 +4,12 @@ import yaml # pip install pyyaml import urllib.request -ASCII_CHARS = ['.', ',', ':', ';', '+', '*', '?', '%', 'S', '#', '@'] +ASCII_CHARS = ['.', ',', ':', ';', '+', '*', '?', '%', 'S', '#', '@', + 'A', 'B', 'Z', 'R'] ASCII_CHARS = ASCII_CHARS[::-1] PRINT_CONSOLE_FLAG: bool +BUCKETS: int ''' method resize(): @@ -42,9 +44,9 @@ def gray_scarify(image): ''' -def modify(image, buckets=25): +def modify(image, buckets): initial_pixels = list(image.getdata()) - new_pixels = [ASCII_CHARS[pixel_value // buckets] for pixel_value in initial_pixels] + new_pixels = [ASCII_CHARS[(pixel_value // buckets) % len(ASCII_CHARS)] for pixel_value in initial_pixels] return ''.join(new_pixels) @@ -58,7 +60,7 @@ def do(image, new_width): image = resize(image, new_width) image = gray_scarify(image) - pixels = modify(image) + pixels = modify(image, BUCKETS) len_pixels = len(pixels) # Construct the image from the character list @@ -115,8 +117,9 @@ def load_setting_from_config(config_path: str) -> tuple: input_dict['SRC_IMG_PATH'] = img_path else: input_dict['SRC_IMG_PATH'] = Path(input_dict['SRC_IMG_PATH']) - global PRINT_CONSOLE_FLAG + global PRINT_CONSOLE_FLAG, BUCKETS PRINT_CONSOLE_FLAG = input_dict['PRINT_CONSOLE'] + BUCKETS = input_dict['BUCKETS'] return input_dict['SRC_IMG_PATH'], Path(input_dict['OUTPUT_PATH']), input_dict.get('NEW_WIDTH', 100) diff --git a/config/config.yaml b/config/config.yaml index fc5b3e0..c14ba33 100644 --- a/config/config.yaml +++ b/config/config.yaml @@ -3,4 +3,6 @@ ASCIIFY: OUTPUT_PATH: temp/img.txt NEW_WIDTH: 200 # Height will be determined by width (equivalent scaling) + BUCKETS: 25 + PRINT_CONSOLE: False # print console or not \ No newline at end of file From fd8f04aaf10dc7d6d45ead1eb32746b120af8243 Mon Sep 17 00:00:00 2001 From: Carson Date: Tue, 14 Jan 2020 19:00:22 +0800 Subject: [PATCH 6/7] You can specify ASCII CHARS through config.yaml --- asciify.py | 17 ++++++++--------- config/config.yaml | 2 +- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/asciify.py b/asciify.py index a09b696..3d3e4b0 100644 --- a/asciify.py +++ b/asciify.py @@ -1,15 +1,13 @@ from PIL import Image import os +from math import ceil from pathlib import Path import yaml # pip install pyyaml import urllib.request -ASCII_CHARS = ['.', ',', ':', ';', '+', '*', '?', '%', 'S', '#', '@', - 'A', 'B', 'Z', 'R'] -ASCII_CHARS = ASCII_CHARS[::-1] +ASCII_CHARS: list PRINT_CONSOLE_FLAG: bool -BUCKETS: int ''' method resize(): @@ -44,9 +42,10 @@ def gray_scarify(image): ''' -def modify(image, buckets): +def modify(image): initial_pixels = list(image.getdata()) - new_pixels = [ASCII_CHARS[(pixel_value // buckets) % len(ASCII_CHARS)] for pixel_value in initial_pixels] + buckets = ceil(255 / len(ASCII_CHARS)) + new_pixels = [ASCII_CHARS[(pixel_value // buckets)] for pixel_value in initial_pixels] return ''.join(new_pixels) @@ -60,7 +59,7 @@ def do(image, new_width): image = resize(image, new_width) image = gray_scarify(image) - pixels = modify(image, BUCKETS) + pixels = modify(image) len_pixels = len(pixels) # Construct the image from the character list @@ -117,9 +116,9 @@ def load_setting_from_config(config_path: str) -> tuple: input_dict['SRC_IMG_PATH'] = img_path else: input_dict['SRC_IMG_PATH'] = Path(input_dict['SRC_IMG_PATH']) - global PRINT_CONSOLE_FLAG, BUCKETS + global PRINT_CONSOLE_FLAG, ASCII_CHARS PRINT_CONSOLE_FLAG = input_dict['PRINT_CONSOLE'] - BUCKETS = input_dict['BUCKETS'] + ASCII_CHARS = eval(input_dict['ASCII_CHARS']) return input_dict['SRC_IMG_PATH'], Path(input_dict['OUTPUT_PATH']), input_dict.get('NEW_WIDTH', 100) diff --git a/config/config.yaml b/config/config.yaml index c14ba33..e062915 100644 --- a/config/config.yaml +++ b/config/config.yaml @@ -3,6 +3,6 @@ ASCIIFY: OUTPUT_PATH: temp/img.txt NEW_WIDTH: 200 # Height will be determined by width (equivalent scaling) - BUCKETS: 25 + ASCII_CHARS: "['.', ',', ':', ';', '+', '*', '?', '%', 'S', '#', '@', '[', ']', '|', 'A', 'B', 'C', 'D']" PRINT_CONSOLE: False # print console or not \ No newline at end of file From 558d25874a55dfb79be0c07185e06bdf18ad1cd3 Mon Sep 17 00:00:00 2001 From: Carson Date: Tue, 14 Jan 2020 19:10:58 +0800 Subject: [PATCH 7/7] merge build_config.yaml BEFORE --- about/Authors/carson.rst | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 about/Authors/carson.rst diff --git a/about/Authors/carson.rst b/about/Authors/carson.rst new file mode 100644 index 0000000..c93d487 --- /dev/null +++ b/about/Authors/carson.rst @@ -0,0 +1,5 @@ +.. csv-table:: Merge-History + :header: Date, Type ,Description + :widths: 10, 10, 100 + + 2020-01-14, config, "use config.yaml to control the script"