From 29d0ff082c2f8a6239e71a9ba1641ae744ddb776 Mon Sep 17 00:00:00 2001 From: Alex DeLorenzo Date: Sun, 8 Sep 2019 21:09:31 -0400 Subject: [PATCH 1/8] Paralellize tile creation & parameterize options behind a CLI --- gen_tiles.py | 123 ++++++++++++++++++++++++++++++++++------------- requirements.txt | 3 +- 2 files changed, 92 insertions(+), 34 deletions(-) diff --git a/gen_tiles.py b/gen_tiles.py index 87487a7..0e7b1ce 100644 --- a/gen_tiles.py +++ b/gen_tiles.py @@ -1,45 +1,102 @@ -import cv2 +import cv2 import numpy as np import os import sys from tqdm import tqdm import math import conf +from pathlib import Path +from typing import Tuple, List +import multiprocessing +from concurrent.futures import ThreadPoolExecutor +import click + # DEPTH = 4 -> 4 * 4 * 4 = 64 colors DEPTH = conf.DEPTH # list of rotations, in degrees, to apply over the original image ROTATIONS = conf.ROTATIONS +THREADS = multiprocessing.cpu_count() + +def get_tile_dir(img_dir: Path, img_name: str) -> Path: + tile_dir = img_dir / Path(f"gen_{img_name}") + + if not tile_dir.exists(): + tile_dir.mkdir() + + return tile_dir + + +def get_img(img: Path) -> np.array: + img = cv2.imread(str(img), cv2.IMREAD_UNCHANGED) + return img.astype("float") + + +def get_dimensions(img: np.array) -> Tuple[int, int, Tuple[float, float]]: + height, width = img.shape[:2] + center = (width / 2, height / 2) + + return height, width, center + + +def make_rotation( + img_name: str, + ext: str, + tile_dir: Path, + new_img: np.array, + height: int, + width: int, + center: Tuple[float, float], + rotation: float, + colors: Tuple[int, int, int], + bar: tqdm +): + r, g, b = colors + rotation_matrix = cv2.getRotationMatrix2D(center, rotation, 1) + abs_cos = abs(rotation_matrix[0, 0]) + abs_sin = abs(rotation_matrix[0, 1]) + new_w = int(height * abs_sin + width * abs_cos) + new_h = int(height * abs_cos + width * abs_sin) + rotation_matrix[0, 2] += new_w / 2 - center[0] + rotation_matrix[1, 2] += new_h / 2 - center[1] + cv2.imwrite( + f"{tile_dir}/{img_name}_{round(r,1)}_{round(g,1)}_{round(b,1)}_r{rotation}.{ext}", + cv2.warpAffine(new_img, rotation_matrix, (new_w, new_h)), + # compress image + [cv2.IMWRITE_PNG_COMPRESSION, 9], + ) + bar.update() + + +def generate_tiles(img: np.array, img_name: str, ext: str, tile_dir: Path, depth: int, rotations: List[int], pool: ThreadPoolExecutor): + height, width, center = get_dimensions(img) + b_range = np.arange(0, 1.01, 1 / depth) + progress_bar = tqdm(total=len(b_range) ** 3) + + for b in b_range: + for g in np.arange(0, 1.01, 1 / depth): + for r in np.arange(0, 1.01, 1 / depth): + colors = r, g, b + new_img = img * [b, g, r, 1] + new_img = new_img.astype("uint8") + for rotation in rotations: + pool.submit(make_rotation, img_name, ext, tile_dir, new_img, height, width, center, rotation, colors, progress_bar) + + +@click.command() +@click.option('-d','--depth', default=DEPTH, help="Color depth.", show_default=True, type=click.INT) +@click.option('-r','--rotations', default=ROTATIONS, help="Rotations.", multiple=True, show_default=True, type=click.INT) +@click.argument('img', type=click.Path(exists=True)) +def cmd(img: str, depth: int, rotations: List[int]): + img_path = Path(img) + img_dir = img_path.parent + img_name, ext = img_path.name.split(".") + tile_dir = get_tile_dir(img_dir, img_name) + img = get_img(img_path) + + with ThreadPoolExecutor(max_workers=THREADS) as pool: + generate_tiles(img, img_name, ext, tile_dir, depth, rotations, pool) + -img_path = sys.argv[1] -img_dir = os.path.dirname(img_path) -img_name, ext = os.path.basename(img_path).rsplit('.', 1) -out_folder = img_dir + '/gen_' + img_name - -if not os.path.exists(out_folder): - os.mkdir(out_folder) - -img = cv2.imread(img_path, cv2.IMREAD_UNCHANGED) -img = img.astype('float') - -height, width = img.shape[:2] -center = (width/2, height/2) - -for b in tqdm(np.arange(0, 1.01, 1 / DEPTH)): - for g in np.arange(0, 1.01, 1 / DEPTH): - for r in np.arange(0, 1.01, 1 / DEPTH): - new_img = img * [b, g, r, 1] - new_img = new_img.astype('uint8') - for rotation in ROTATIONS: - rotation_matrix = cv2.getRotationMatrix2D(center, rotation, 1) - abs_cos = abs(rotation_matrix[0,0]) - abs_sin = abs(rotation_matrix[0,1]) - new_w = int(height * abs_sin + width * abs_cos) - new_h = int(height * abs_cos + width * abs_sin) - rotation_matrix[0, 2] += new_w/2 - center[0] - rotation_matrix[1, 2] += new_h/2 - center[1] - cv2.imwrite( - f'{out_folder}/{img_name}_{round(r,1)}_{round(g,1)}_{round(b,1)}_r{rotation}.{ext}', - cv2.warpAffine(new_img, rotation_matrix, (new_w, new_h)), - # compress image - [cv2.IMWRITE_PNG_COMPRESSION, 9]) +if __name__ == "__main__": + cmd() diff --git a/requirements.txt b/requirements.txt index 9c0b5b3..13d2c43 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,4 @@ opencv-python numpy -tqdm \ No newline at end of file +tqdm +click \ No newline at end of file From ffc7e0f1690dfb2604640b7c21b0bd2d3826355f Mon Sep 17 00:00:00 2001 From: Alex DeLorenzo Date: Sun, 8 Sep 2019 21:10:46 -0400 Subject: [PATCH 2/8] Format with black --- gen_tiles.py | 48 ++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 42 insertions(+), 6 deletions(-) diff --git a/gen_tiles.py b/gen_tiles.py index 0e7b1ce..ce80bcf 100644 --- a/gen_tiles.py +++ b/gen_tiles.py @@ -18,6 +18,7 @@ ROTATIONS = conf.ROTATIONS THREADS = multiprocessing.cpu_count() + def get_tile_dir(img_dir: Path, img_name: str) -> Path: tile_dir = img_dir / Path(f"gen_{img_name}") @@ -49,7 +50,7 @@ def make_rotation( center: Tuple[float, float], rotation: float, colors: Tuple[int, int, int], - bar: tqdm + bar: tqdm, ): r, g, b = colors rotation_matrix = cv2.getRotationMatrix2D(center, rotation, 1) @@ -68,7 +69,15 @@ def make_rotation( bar.update() -def generate_tiles(img: np.array, img_name: str, ext: str, tile_dir: Path, depth: int, rotations: List[int], pool: ThreadPoolExecutor): +def generate_tiles( + img: np.array, + img_name: str, + ext: str, + tile_dir: Path, + depth: int, + rotations: List[int], + pool: ThreadPoolExecutor, +): height, width, center = get_dimensions(img) b_range = np.arange(0, 1.01, 1 / depth) progress_bar = tqdm(total=len(b_range) ** 3) @@ -80,13 +89,40 @@ def generate_tiles(img: np.array, img_name: str, ext: str, tile_dir: Path, depth new_img = img * [b, g, r, 1] new_img = new_img.astype("uint8") for rotation in rotations: - pool.submit(make_rotation, img_name, ext, tile_dir, new_img, height, width, center, rotation, colors, progress_bar) + pool.submit( + make_rotation, + img_name, + ext, + tile_dir, + new_img, + height, + width, + center, + rotation, + colors, + progress_bar, + ) @click.command() -@click.option('-d','--depth', default=DEPTH, help="Color depth.", show_default=True, type=click.INT) -@click.option('-r','--rotations', default=ROTATIONS, help="Rotations.", multiple=True, show_default=True, type=click.INT) -@click.argument('img', type=click.Path(exists=True)) +@click.option( + "-d", + "--depth", + default=DEPTH, + help="Color depth.", + show_default=True, + type=click.INT, +) +@click.option( + "-r", + "--rotations", + default=ROTATIONS, + help="Rotations.", + multiple=True, + show_default=True, + type=click.INT, +) +@click.argument("img", type=click.Path(exists=True)) def cmd(img: str, depth: int, rotations: List[int]): img_path = Path(img) img_dir = img_path.parent From 7f67929ebfa7d6680942d997c1fbda8f829e02fe Mon Sep 17 00:00:00 2001 From: Alex DeLorenzo Date: Sun, 8 Sep 2019 21:25:33 -0400 Subject: [PATCH 3/8] Fix progress bar --- __pycache__/conf.cpython-36.pyc | Bin 0 -> 412 bytes gen_tiles.py | 3 ++- 2 files changed, 2 insertions(+), 1 deletion(-) create mode 100644 __pycache__/conf.cpython-36.pyc diff --git a/__pycache__/conf.cpython-36.pyc b/__pycache__/conf.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b26cb2baa6ee6aaf8451ec69eb85323df300f08f GIT binary patch literal 412 zcmYjMO;5r=5MBBO1&SAcfQctA^uU#f4J@!p={9XOW)jmFrZyU&q?CjH6TN%jC-@UQ zdyA?bEXO{^#dNqHtDtsEiW!}Rr~!q+hEOMD(X zA4l^(EUP;vWkcw5J=+z%%{ukEN?xs$(rq2|G4-R_!3=ohM2veOv_{3G6j5#E!$x=+gQxxHE!c35QjU7FZ_o;@auVrv%<%OtlK+4{b>c{(%(s| Date: Sun, 8 Sep 2019 21:25:57 -0400 Subject: [PATCH 4/8] remove cached files --- __pycache__/conf.cpython-36.pyc | Bin 412 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 __pycache__/conf.cpython-36.pyc diff --git a/__pycache__/conf.cpython-36.pyc b/__pycache__/conf.cpython-36.pyc deleted file mode 100644 index b26cb2baa6ee6aaf8451ec69eb85323df300f08f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 412 zcmYjMO;5r=5MBBO1&SAcfQctA^uU#f4J@!p={9XOW)jmFrZyU&q?CjH6TN%jC-@UQ zdyA?bEXO{^#dNqHtDtsEiW!}Rr~!q+hEOMD(X zA4l^(EUP;vWkcw5J=+z%%{ukEN?xs$(rq2|G4-R_!3=ohM2veOv_{3G6j5#E!$x=+gQxxHE!c35QjU7FZ_o;@auVrv%<%OtlK+4{b>c{(%(s| Date: Mon, 9 Sep 2019 04:30:29 -0400 Subject: [PATCH 5/8] condense params --- gen_tiles.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/gen_tiles.py b/gen_tiles.py index a3fed12..0c3184e 100644 --- a/gen_tiles.py +++ b/gen_tiles.py @@ -45,14 +45,14 @@ def make_rotation( ext: str, tile_dir: Path, new_img: np.array, - height: int, - width: int, - center: Tuple[float, float], + dimensions: Tuple[int, int, Tuple[float, float]], rotation: float, colors: Tuple[int, int, int], bar: tqdm, ): + height, width, center = dimensions r, g, b = colors + rotation_matrix = cv2.getRotationMatrix2D(center, rotation, 1) abs_cos = abs(rotation_matrix[0, 0]) abs_sin = abs(rotation_matrix[0, 1]) @@ -78,7 +78,7 @@ def generate_tiles( rotations: List[int], pool: ThreadPoolExecutor, ): - height, width, center = get_dimensions(img) + dimensions = get_dimensions(img) b_range = np.arange(0, 1.01, 1 / depth) operations = len(b_range) ** 3 * len(rotations) progress_bar = tqdm(total=operations) From 5d8e8c1ead3251656725d4c0976f43ec9eed2d43 Mon Sep 17 00:00:00 2001 From: Alex DeLorenzo Date: Mon, 9 Sep 2019 05:07:40 -0400 Subject: [PATCH 6/8] Refactor --- gen_tiles.py | 44 ++++++++++++++++++++++---------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/gen_tiles.py b/gen_tiles.py index 0c3184e..6bb5e35 100644 --- a/gen_tiles.py +++ b/gen_tiles.py @@ -47,11 +47,11 @@ def make_rotation( new_img: np.array, dimensions: Tuple[int, int, Tuple[float, float]], rotation: float, - colors: Tuple[int, int, int], + colors: Tuple[float, float, float], bar: tqdm, ): height, width, center = dimensions - r, g, b = colors + b, g, r = colors rotation_matrix = cv2.getRotationMatrix2D(center, rotation, 1) abs_cos = abs(rotation_matrix[0, 0]) @@ -80,29 +80,29 @@ def generate_tiles( ): dimensions = get_dimensions(img) b_range = np.arange(0, 1.01, 1 / depth) + g_range = np.arange(0, 1.01, 1 / depth) + r_range = np.arange(0, 1.01, 1 / depth) operations = len(b_range) ** 3 * len(rotations) progress_bar = tqdm(total=operations) - for b in b_range: - for g in np.arange(0, 1.01, 1 / depth): - for r in np.arange(0, 1.01, 1 / depth): - colors = r, g, b - new_img = img * [b, g, r, 1] - new_img = new_img.astype("uint8") - for rotation in rotations: - pool.submit( - make_rotation, - img_name, - ext, - tile_dir, - new_img, - height, - width, - center, - rotation, - colors, - progress_bar, - ) + for b, g, r in product(b_range, g_range, r_range): + colors = b, g, r + new_img = img * [b, g, r, 1] + new_img = new_img.astype("uint8") + for rotation in rotations: + pool.submit( + make_rotation, + img_name, + ext, + tile_dir, + new_img, + height, + width, + center, + rotation, + colors, + progress_bar, + ) @click.command() From aa3fcf127bcba4610f51246e74f6179c46928bca Mon Sep 17 00:00:00 2001 From: Alex DeLorenzo Date: Mon, 9 Sep 2019 05:09:47 -0400 Subject: [PATCH 7/8] Refactor --- gen_tiles.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/gen_tiles.py b/gen_tiles.py index 6bb5e35..a904097 100644 --- a/gen_tiles.py +++ b/gen_tiles.py @@ -1,3 +1,7 @@ +from concurrent.futures import ThreadPoolExecutor +from itertools import product +from pathlib import Path +from typing import Tuple, List import cv2 import numpy as np import os @@ -5,10 +9,8 @@ from tqdm import tqdm import math import conf -from pathlib import Path -from typing import Tuple, List import multiprocessing -from concurrent.futures import ThreadPoolExecutor + import click @@ -52,7 +54,7 @@ def make_rotation( ): height, width, center = dimensions b, g, r = colors - + rotation_matrix = cv2.getRotationMatrix2D(center, rotation, 1) abs_cos = abs(rotation_matrix[0, 0]) abs_sin = abs(rotation_matrix[0, 1]) From af99f73ecde385b097495f2903841e5792c27188 Mon Sep 17 00:00:00 2001 From: Alex DeLorenzo Date: Mon, 9 Sep 2019 13:16:28 -0400 Subject: [PATCH 8/8] fix parameters --- gen_tiles.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/gen_tiles.py b/gen_tiles.py index a904097..e27151f 100644 --- a/gen_tiles.py +++ b/gen_tiles.py @@ -98,9 +98,7 @@ def generate_tiles( ext, tile_dir, new_img, - height, - width, - center, + dimensions, rotation, colors, progress_bar,