├── .gitignore ├── README.md ├── gaugan2_renderer.py ├── main.py └── requirements.txt /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | share/python-wheels/ 24 | *.egg-info/ 25 | .installed.cfg 26 | *.egg 27 | MANIFEST 28 | 29 | # PyInstaller 30 | # Usually these files are written by a python script from a template 31 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 32 | *.manifest 33 | *.spec 34 | 35 | # Installer logs 36 | pip-log.txt 37 | pip-delete-this-directory.txt 38 | 39 | # Unit test / coverage reports 40 | htmlcov/ 41 | .tox/ 42 | .nox/ 43 | .coverage 44 | .coverage.* 45 | .cache 46 | nosetests.xml 47 | coverage.xml 48 | *.cover 49 | *.py,cover 50 | .hypothesis/ 51 | .pytest_cache/ 52 | cover/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | .pybuilder/ 76 | target/ 77 | 78 | # Jupyter Notebook 79 | .ipynb_checkpoints 80 | 81 | # IPython 82 | profile_default/ 83 | ipython_config.py 84 | 85 | # pyenv 86 | # For a library or package, you might want to ignore these files since the code is 87 | # intended to run in multiple environments; otherwise, check them in: 88 | # .python-version 89 | 90 | # pipenv 91 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 92 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 93 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 94 | # install all needed dependencies. 95 | #Pipfile.lock 96 | 97 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 98 | __pypackages__/ 99 | 100 | # Celery stuff 101 | celerybeat-schedule 102 | celerybeat.pid 103 | 104 | # SageMath parsed files 105 | *.sage.py 106 | 107 | # Environments 108 | .env 109 | .venv 110 | env/ 111 | venv/ 112 | ENV/ 113 | env.bak/ 114 | venv.bak/ 115 | 116 | # Spyder project settings 117 | .spyderproject 118 | .spyproject 119 | 120 | # Rope project settings 121 | .ropeproject 122 | 123 | # mkdocs documentation 124 | /site 125 | 126 | # mypy 127 | .mypy_cache/ 128 | .dmypy.json 129 | dmypy.json 130 | 131 | # Pyre type checker 132 | .pyre/ 133 | 134 | # pytype static type analyzer 135 | .pytype/ 136 | 137 | # Cython debug symbols 138 | cython_debug/ 139 | 140 | # PyCharm 141 | # JetBrains specific template is maintainted in a separate JetBrains.gitignore that can 142 | # be found at https://github.com/github/gitignore/blob/master/Global/JetBrains.gitignore 143 | # and can be added to the global gitignore or merged into this file. For a more nuclear 144 | # option (not recommended) you can uncomment the following to ignore the entire idea folder. 145 | #.idea/ 146 | 147 | input_*/ 148 | output_* 149 | assets 150 | comparison.mp4 151 | example.mp4 152 | rendering_1.c4d 153 | input_afetr-effects.aep 154 | .DS_Store 155 | output.mp4 156 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # gaugan2-renderer 2 | 3 | ## Create videos with gaugan2 4 | 5 | The gaugan2-renderer is a Python script that automatically uploads an image sequence to http://gaugan.org/gaugan2/ as a segmentation map, generates the output and creates a video from it. 6 | 7 | https://user-images.githubusercontent.com/53308156/151351425-7dfdedfd-5fd4-4880-ab85-82983100e213.mp4 8 | 9 | A example video created by Gaugan2 Renderer. On the left the input video on the right the output video. 10 | 11 | ### Usage 12 | 13 | 1. clone this repository and import the Gaugan2Renderer from gaugan2_renderer or use the example in `main.py` 14 | 2. install the dependencies via `pip install -r requirements.txt` 15 | 3. create a folder named `input_folder` and add there the segmentation maps as PNGs you want to render 16 | 17 | ```python 18 | from gaugan2_renderer import Gaugan2Renderer # import the gaugan2_renderer class from gaugan2_renderer.py 19 | 20 | renderer = Gaugan2Renderer() # create an instance of the gaugan2_renderer class 21 | renderer.run("./input_folder", "./output_folder") # run the renderer 22 | renderer.create_video("./output.mp4") # create a video of the created images 23 | 24 | ``` 25 | 26 | # Api 27 | 28 | `gaugan2_renderer.run(input_folder, output_folder)` 29 | 30 | - **input_folder** the folder with the segmentation maps that should be rendered, PNGs required 31 | - **output_folder** the folder with the rendered images - if it does not exist, it will be created automatically 32 | 33 | `gaugan2_renderer.create_video(output_path)` 34 | 35 | - **output_path** the path to the rendered video 36 | 37 | # For best results, use: 38 | - PNG format - required 39 | - input images with the size of 1024 px x 1024 px 40 | - the exact segmentation map colors 41 | - no anti aliasing (every pixel should have a color value specified in **Semgmentation Map Colors**) 42 | 43 | # Segmentation Map Colors 44 | 45 | - bridge: #5e5bc5 46 | - bush: #606e32 47 | - clouds: #696969 48 | - dirt: #6e6e28 49 | - fence: #706419 50 | - flower: #760000 51 | - fog: #77ba1d 52 | - grass: #7bc800 53 | - gravel: #7c32c8 54 | - ground-other: #7d3054 55 | - hill: #7ec864 56 | - house: #7f4502 57 | - mountain: #869664 58 | - mud: #87716f 59 | - pavement: #8b3027 60 | - platform: #8f2a91 61 | - river: #9364c8 62 | - road: #946e28 63 | - rock: #956432 64 | - roof: #9600b1 65 | - sand: #999900 66 | - sea: #9ac6da 67 | - sky: #9ceedd 68 | - snow: #9e9eaa 69 | - stone: #a1a164 70 | - straw: #a2a3eb 71 | - tree: #a8c832 72 | - wall-brick: #aad16a 73 | - wall-stone: #ae2974 74 | - wall-wood: #b0c1c3 75 | - water: #b1c8ff 76 | - wood: #b57b00 77 | -------------------------------------------------------------------------------- /gaugan2_renderer.py: -------------------------------------------------------------------------------- 1 | import base64 2 | import os 3 | import time 4 | from glob import glob 5 | from tqdm import tqdm 6 | 7 | import imageio 8 | from selenium import webdriver 9 | from selenium.webdriver.common.by import By 10 | from selenium.webdriver.support import expected_conditions as EC 11 | from selenium.webdriver.support.ui import WebDriverWait 12 | from webdriver_manager.chrome import ChromeDriverManager 13 | from selenium.webdriver.chrome.options import Options 14 | 15 | 16 | class Gaugan2Renderer: 17 | def __init__(self, waiting_time=5): 18 | self.waiting_time = waiting_time 19 | self.output_images = [] 20 | chrome_options = Options() 21 | chrome_options.add_argument("--headless") 22 | 23 | self.driver = webdriver.Chrome( 24 | ChromeDriverManager().install(), options=chrome_options) 25 | 26 | def open(self): 27 | self.driver.get("http://gaugan.org/gaugan2/") 28 | WebDriverWait(self.driver, 10).until( 29 | EC.presence_of_element_located((By.ID, "viewport")) 30 | ) 31 | self.close_popups() 32 | 33 | def close_popups(self): 34 | close_button = self.driver.find_element(By.XPATH, 35 | "/html/body/div[2]/div/header/button") 36 | if close_button: 37 | close_button.click() 38 | 39 | terms_and_conditions = self.driver.find_element( 40 | By.XPATH, '//*[@id="myCheck"]') 41 | 42 | if terms_and_conditions: 43 | terms_and_conditions.click() 44 | 45 | def download_image(self, file_path): 46 | output_canvas = self.driver.find_element( 47 | By.ID, 'output') 48 | canvas_base64 = self.driver.execute_script( 49 | "return arguments[0].toDataURL('image/png').substring(21);", output_canvas) 50 | canvas_png = base64.b64decode(canvas_base64) 51 | 52 | with open(file_path, 'wb') as f: 53 | f.write(canvas_png) 54 | 55 | def create_output_dir(self): 56 | os.makedirs(self.output_path, exist_ok=True) 57 | 58 | def render_image(self, file_path): 59 | self.driver.find_element( 60 | By.XPATH, '//*[@id="segmapfile"]').send_keys(file_path) 61 | self.driver.find_element( 62 | By.XPATH, '//*[@id="btnSegmapLoad"]').click() 63 | self.driver.find_element( 64 | By.XPATH, '//*[@id="render"]').click() 65 | 66 | def run(self, input_folder, output_path): 67 | self.image_paths = glob(input_folder + "/*.png") 68 | self.output_path = output_path 69 | 70 | self.open() 71 | self.create_output_dir() 72 | 73 | for file_path in tqdm(self.image_paths): 74 | file_path = os.path.abspath(file_path) 75 | basename = os.path.basename(file_path) 76 | output_image = os.path.join(self.output_path, 77 | basename) 78 | 79 | self.render_image(file_path) 80 | time.sleep(self.waiting_time) 81 | self.download_image(output_image) 82 | self.output_images.append(output_image) 83 | 84 | self.driver.close() 85 | 86 | def create_video(self, output_video): 87 | images = [imageio.imread(image) for image in self.output_images] 88 | imageio.mimsave(output_video, images, fps=10) 89 | -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | from gaugan2_renderer import Gaugan2Renderer 2 | 3 | renderer = Gaugan2Renderer(waiting_time=10) 4 | renderer.run("./input_folder", "./output_folder") 5 | renderer.create_video("./output.mp4") 6 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | imageio==2.13.4 2 | selenium==4.1.0 3 | webdriver_manager==3.5.2 4 | imageio-ffmpeg==0.4.5 5 | tqdm==4.62.3 --------------------------------------------------------------------------------