CoolFace
Apppublic

hololens/stable-diffusion-webui-depthmap-script

sourceHugging Faceupdated 2y agoView on Hugging Face
1likes
common_ui.py596 linesDownload Raw Back to src
1import traceback
2from pathlib import Path
3import gradio as gr
4from PIL import Image
5
6from src import backbone, video_mode
7from src.core import core_generation_funnel, unload_models, run_makevideo
8from src.depthmap_generation import ModelHolder
9from src.gradio_args_transport import GradioComponentBundle
10from src.misc import *
11from src.common_constants import GenerationOptions as go
12
13# Ugly workaround to fix gradio tempfile issue
14def ensure_gradio_temp_directory():
15    try:
16        import tempfile
17        path = os.path.join(tempfile.gettempdir(), 'gradio')
18        if not (os.path.exists(path)):
19            os.mkdir(path)
20    except Exception as e:
21        traceback.print_exc()
22
23
24ensure_gradio_temp_directory()
25
26
27def main_ui_panel(is_depth_tab):
28    inp = GradioComponentBundle()
29    # TODO: Greater visual separation
30    with gr.Blocks():
31        with gr.Row() as cur_option_root:
32            inp -= 'depthmap_gen_row_0', cur_option_root
33            inp += go.COMPUTE_DEVICE, gr.Radio(label="Compute on", choices=['GPU', 'CPU'], value='GPU')
34            # TODO: Should return value instead of index. Maybe Enum should be used?
35            inp += go.MODEL_TYPE, gr.Dropdown(label="Model",
36                                             choices=['res101', 'dpt_beit_large_512 (midas 3.1)',
37                                                      'dpt_beit_large_384 (midas 3.1)', 'dpt_large_384 (midas 3.0)',
38                                                      'dpt_hybrid_384 (midas 3.0)',
39                                                      'midas_v21', 'midas_v21_small',
40                                                      'zoedepth_n (indoor)', 'zoedepth_k (outdoor)', 'zoedepth_nk',
41                                                      'Marigold v1', 'Depth Anything', 'Depth Anything v2 Small',
42                                                      'Depth Anything v2 Base', 'Depth Anything v2 Large'],
43                                              value='Depth Anything v2 Base', type="index")
44        with gr.Box() as cur_option_root:
45            inp -= 'depthmap_gen_row_1', cur_option_root
46            with gr.Row():
47                inp += go.BOOST, gr.Checkbox(label="BOOST",
48                                             info="Generate depth map parts in a mosaic fashion - very slow",
49                                             value=False)
50                inp += go.NET_SIZE_MATCH, gr.Checkbox(label="Match net size to input size",
51                                                      info="Net size affects quality, performance and VRAM usage")
52            with gr.Row() as options_depend_on_match_size:
53                inp += go.NET_WIDTH, gr.Slider(minimum=64, maximum=2048, step=64, label='Net width')
54                inp += go.NET_HEIGHT, gr.Slider(minimum=64, maximum=2048, step=64, label='Net height')
55            with gr.Row():
56                inp += go.TILING_MODE, gr.Checkbox(
57                    label='Tiling mode', info='Reduces seams that appear if the depthmap is tiled into a grid'
58                )
59
60        with gr.Box() as cur_option_root:
61            inp -= 'depthmap_gen_row_2', cur_option_root
62            with gr.Row():
63                with gr.Group():  # 50% of width
64                    inp += "save_outputs", gr.Checkbox(label="Save Outputs", value=True)
65                with gr.Group():  # 50% of width
66                    inp += go.DO_OUTPUT_DEPTH, gr.Checkbox(label="Output DepthMap")
67                    inp += go.OUTPUT_DEPTH_INVERT, gr.Checkbox(label="Invert (black=near, white=far)")
68            with gr.Row() as options_depend_on_output_depth_1:
69                inp += go.OUTPUT_DEPTH_COMBINE, gr.Checkbox(
70                    label="Combine input and depthmap into one image")
71                inp += go.OUTPUT_DEPTH_COMBINE_AXIS, gr.Radio(
72                    label="Combine axis", choices=['Vertical', 'Horizontal'], type="value", visible=False)
73
74        with gr.Box() as cur_option_root:
75            inp -= 'depthmap_gen_row_3', cur_option_root
76            with gr.Row():
77                inp += go.CLIPDEPTH, gr.Checkbox(label="Clip and renormalize DepthMap")
78                inp += go.CLIPDEPTH_MODE,\
79                    gr.Dropdown(label="Mode", choices=['Range', 'Outliers'], type="value", visible=False)
80            with gr.Row(visible=False) as clip_options_row_1:
81                inp += go.CLIPDEPTH_FAR, gr.Slider(minimum=0, maximum=1, step=0.001, label='Far clip')
82                inp += go.CLIPDEPTH_NEAR, gr.Slider(minimum=0, maximum=1, step=0.001, label='Near clip')
83
84        with gr.Box():
85            with gr.Row():
86                inp += go.GEN_STEREO, gr.Checkbox(label="Generate stereoscopic (3D) image(s)")
87            with gr.Column(visible=False) as stereo_options:
88                with gr.Row():
89                    inp += go.STEREO_MODES, gr.CheckboxGroup(
90                        ["left-right", "right-left", "top-bottom", "bottom-top", "red-cyan-anaglyph",
91                         "left-only", "only-right", "cyan-red-reverseanaglyph"
92                         ][0:8 if backbone.get_opt('depthmap_script_extra_stereomodes', False) else 5], label="Output")
93                with gr.Row():
94                    inp += go.STEREO_DIVERGENCE, gr.Slider(minimum=0.05, maximum=15.005, step=0.01,
95                                                          label='Divergence (3D effect)')
96                    inp += go.STEREO_SEPARATION, gr.Slider(minimum=-5.0, maximum=5.0, step=0.01,
97                                                          label='Separation (moves images apart)')
98                with gr.Row():
99                    inp += go.STEREO_FILL_ALGO, gr.Dropdown(label="Gap fill technique",
100                                                      choices=['none', 'naive', 'naive_interpolating', 'polylines_soft',
101                                                               'polylines_sharp'],
102                                                      type="value")
103                    inp += go.STEREO_OFFSET_EXPONENT, gr.Slider(label="Magic exponent", minimum=1, maximum=2, step=1)
104                    inp += go.STEREO_BALANCE, gr.Slider(minimum=-1.0, maximum=1.0, step=0.05,
105                                                       label='Balance between eyes')
106
107        with gr.Box():
108            with gr.Row():
109                inp += go.GEN_NORMALMAP, gr.Checkbox(label="Generate NormalMap")
110            with gr.Column(visible=False) as normalmap_options:
111                with gr.Row():
112                    inp += go.NORMALMAP_PRE_BLUR, gr.Checkbox(label="Smooth before calculating normals")
113                    inp += go.NORMALMAP_PRE_BLUR_KERNEL, gr.Slider(minimum=1, maximum=31, step=2, label='Pre-smooth kernel size', visible=False)
114                    inp.add_rule(go.NORMALMAP_PRE_BLUR_KERNEL, 'visible-if', go.NORMALMAP_PRE_BLUR)
115                with gr.Row():
116                    inp += go.NORMALMAP_SOBEL, gr.Checkbox(label="Sobel gradient")
117                    inp += go.NORMALMAP_SOBEL_KERNEL, gr.Slider(minimum=1, maximum=31, step=2, label='Sobel kernel size')
118                    inp.add_rule(go.NORMALMAP_SOBEL_KERNEL, 'visible-if', go.NORMALMAP_SOBEL)
119                with gr.Row():
120                    inp += go.NORMALMAP_POST_BLUR, gr.Checkbox(label="Smooth after calculating normals")
121                    inp += go.NORMALMAP_POST_BLUR_KERNEL, gr.Slider(minimum=1, maximum=31, step=2, label='Post-smooth kernel size', visible=False)
122                    inp.add_rule(go.NORMALMAP_POST_BLUR_KERNEL, 'visible-if', go.NORMALMAP_POST_BLUR)
123                with gr.Row():
124                    inp += go.NORMALMAP_INVERT, gr.Checkbox(label="Invert")
125
126        if backbone.get_opt('depthmap_script_gen_heatmap_from_ui', False):
127            with gr.Box():
128                with gr.Row():
129                    inp += go.GEN_HEATMAP, gr.Checkbox(label="Generate HeatMap")
130
131        with gr.Box():
132            with gr.Column():
133                inp += go.GEN_SIMPLE_MESH, gr.Checkbox(label="Generate simple 3D mesh")
134            with gr.Column(visible=False) as mesh_options:
135                with gr.Row():
136                    gr.HTML(value="Generates fast, accurate only with ZoeDepth models and no boost, no custom maps.")
137                with gr.Row():
138                    inp += go.SIMPLE_MESH_OCCLUDE, gr.Checkbox(label="Remove occluded edges")
139                    inp += go.SIMPLE_MESH_SPHERICAL, gr.Checkbox(label="Equirectangular projection")
140
141        if is_depth_tab:
142            with gr.Box():
143                with gr.Column():
144                    inp += go.GEN_INPAINTED_MESH, gr.Checkbox(
145                        label="Generate 3D inpainted mesh")
146                with gr.Column(visible=False) as inpaint_options_row_0:
147                    gr.HTML("Generation is sloooow. Required for generating videos from mesh.")
148                    inp += go.GEN_INPAINTED_MESH_DEMOS, gr.Checkbox(
149                        label="Generate 4 demo videos with 3D inpainted mesh.")
150                    gr.HTML("More options for generating video can be found in the Generate video tab.")
151
152        with gr.Box():
153            # TODO: it should be clear from the UI that there is an option of the background removal
154            #  that does not use the model selected above
155            with gr.Row():
156                inp += go.GEN_REMBG, gr.Checkbox(label="Remove background")
157            with gr.Column(visible=False) as bgrem_options:
158                with gr.Row():
159                    inp += go.SAVE_BACKGROUND_REMOVAL_MASKS, gr.Checkbox(label="Save the foreground masks")
160                    inp += go.PRE_DEPTH_BACKGROUND_REMOVAL, gr.Checkbox(label="Pre-depth background removal")
161                with gr.Row():
162                    inp += go.REMBG_MODEL, gr.Dropdown(
163                        label="Rembg Model", type="value",
164                        choices=['u2net', 'u2netp', 'u2net_human_seg', 'silueta', "isnet-general-use", "isnet-anime"])
165
166        with gr.Box():
167            gr.HTML(f"{SCRIPT_FULL_NAME}<br/>")
168            gr.HTML("Information, comment and share @ <a "
169                    "href='https://github.com/thygate/stable-diffusion-webui-depthmap-script'>"
170                    "https://github.com/thygate/stable-diffusion-webui-depthmap-script</a>")
171
172        def update_default_net_size(model_type):
173            w, h = ModelHolder.get_default_net_size(model_type)
174            return inp[go.NET_WIDTH].update(value=w), inp[go.NET_HEIGHT].update(value=h)
175
176        inp[go.MODEL_TYPE].change(
177            fn=update_default_net_size,
178            inputs=inp[go.MODEL_TYPE],
179            outputs=[inp[go.NET_WIDTH], inp[go.NET_HEIGHT]]
180        )
181
182        inp[go.BOOST].change(  # Go boost! Wroom!..
183            fn=lambda a, b: (inp[go.NET_SIZE_MATCH].update(visible=not a),
184                             options_depend_on_match_size.update(visible=not a and not b)),
185            inputs=[inp[go.BOOST], inp[go.NET_SIZE_MATCH]],
186            outputs=[inp[go.NET_SIZE_MATCH], options_depend_on_match_size]
187        )
188        inp.add_rule(options_depend_on_match_size, 'visible-if-not', go.NET_SIZE_MATCH)
189        inp[go.TILING_MODE].change(  # Go boost! Wroom!..
190            fn=lambda a: (
191                inp[go.BOOST].update(value=False), inp[go.NET_SIZE_MATCH].update(value=True)
192            ) if a else (inp[go.BOOST].update(), inp[go.NET_SIZE_MATCH].update()),
193            inputs=[inp[go.TILING_MODE]],
194            outputs=[inp[go.BOOST], inp[go.NET_SIZE_MATCH]]
195        )
196
197        inp.add_rule(options_depend_on_output_depth_1, 'visible-if', go.DO_OUTPUT_DEPTH)
198        inp.add_rule(go.OUTPUT_DEPTH_INVERT, 'visible-if', go.DO_OUTPUT_DEPTH)
199        inp.add_rule(go.OUTPUT_DEPTH_COMBINE_AXIS, 'visible-if', go.OUTPUT_DEPTH_COMBINE)
200        inp.add_rule(go.CLIPDEPTH_MODE, 'visible-if', go.CLIPDEPTH)
201        inp.add_rule(clip_options_row_1, 'visible-if', go.CLIPDEPTH)
202
203        inp[go.CLIPDEPTH_FAR].change(
204            fn=lambda a, b: a if b < a else b,
205            inputs=[inp[go.CLIPDEPTH_FAR], inp[go.CLIPDEPTH_NEAR]],
206            outputs=[inp[go.CLIPDEPTH_NEAR]],
207            show_progress=False
208        )
209        inp[go.CLIPDEPTH_NEAR].change(
210            fn=lambda a, b: a if b > a else b,
211            inputs=[inp[go.CLIPDEPTH_NEAR], inp[go.CLIPDEPTH_FAR]],
212            outputs=[inp[go.CLIPDEPTH_FAR]],
213            show_progress=False
214        )
215
216        inp.add_rule(stereo_options, 'visible-if', go.GEN_STEREO)
217        inp.add_rule(normalmap_options, 'visible-if', go.GEN_NORMALMAP)
218        inp.add_rule(mesh_options, 'visible-if', go.GEN_SIMPLE_MESH)
219        if is_depth_tab:
220            inp.add_rule(inpaint_options_row_0, 'visible-if', go.GEN_INPAINTED_MESH)
221        inp.add_rule(bgrem_options, 'visible-if', go.GEN_REMBG)
222
223    return inp
224
225def open_folder_action():
226    # Adapted from stable-diffusion-webui
227    f = backbone.get_outpath()
228    if backbone.get_cmd_opt('hide_ui_dir_config', False):
229        return
230    if not os.path.exists(f) or not os.path.isdir(f):
231        raise Exception("Couldn't open output folder")  # .isdir is security-related, do not remove!
232    import platform
233    import subprocess as sp
234    path = os.path.normpath(f)
235    if platform.system() == "Windows":
236        os.startfile(path)
237    elif platform.system() == "Darwin":
238        sp.Popen(["open", path])
239    elif "microsoft-standard-WSL2" in platform.uname().release:
240        sp.Popen(["wsl-open", path])
241    else:
242        sp.Popen(["xdg-open", path])
243
244
245def depthmap_mode_video(inp):
246    gr.HTML(value="Single video mode allows generating videos from videos. Please "
247                  "keep in mind that all the frames of the video need to be processed - therefore it is important to "
248                  "pick settings so that the generation is not too slow. For the best results, "
249                  "use a zoedepth model, since they provide the highest level of coherency between frames.")
250    inp += gr.File(elem_id='depthmap_vm_input', label="Video or animated file",
251                   file_count="single", interactive=True, type="file")
252    inp += gr.Checkbox(elem_id="depthmap_vm_custom_checkbox",
253                       label="Use custom/pregenerated DepthMap video", value=False)
254    inp += gr.Dropdown(elem_id="depthmap_vm_smoothening_mode", label="Smoothening",
255                       type="value", choices=['none', 'experimental'], value='experimental')
256    inp += gr.File(elem_id='depthmap_vm_custom', file_count="single",
257                   interactive=True, type="file", visible=False)
258    with gr.Row():
259        inp += gr.Checkbox(elem_id='depthmap_vm_compress_checkbox', label="Compress colorvideos?", value=False)
260        inp += gr.Slider(elem_id='depthmap_vm_compress_bitrate', label="Bitrate (kbit)", visible=False,
261                         minimum=1000, value=15000, maximum=50000, step=250)
262
263    inp.add_rule('depthmap_vm_custom', 'visible-if', 'depthmap_vm_custom_checkbox')
264    inp.add_rule('depthmap_vm_smoothening_mode', 'visible-if-not', 'depthmap_vm_custom_checkbox')
265    inp.add_rule('depthmap_vm_compress_bitrate', 'visible-if', 'depthmap_vm_compress_checkbox')
266
267    return inp
268
269
270custom_css = """
271#depthmap_vm_input {height: 75px}
272#depthmap_vm_custom {height: 75px}
273"""
274
275
276def on_ui_tabs():
277    inp = GradioComponentBundle()
278    with gr.Blocks(analytics_enabled=False, title="DepthMap", css=custom_css) as depthmap_interface:
279        with gr.Row(equal_height=False):
280            with gr.Column(variant='panel'):
281                inp += 'depthmap_mode', gr.HTML(visible=False, value='0')
282                with gr.Tabs():
283                    with gr.TabItem('Single Image') as depthmap_mode_0:
284                        with gr.Group():
285                            with gr.Row():
286                                inp += gr.Image(label="Source", source="upload", interactive=True, type="pil",
287                                                elem_id="depthmap_input_image")
288                                # TODO: depthmap generation settings should disappear when using this
289                                inp += gr.File(label="Custom DepthMap", file_count="single", interactive=True,
290                                               type="file", elem_id='custom_depthmap_img', visible=False)
291                        inp += gr.Checkbox(elem_id="custom_depthmap", label="Use custom DepthMap", value=False)
292                    with gr.TabItem('Batch Process') as depthmap_mode_1:
293                        inp += gr.File(elem_id='image_batch', label="Batch Process", file_count="multiple",
294                                       interactive=True, type="file")
295                    with gr.TabItem('Batch from Directory') as depthmap_mode_2:
296                        inp += gr.Textbox(elem_id="depthmap_batch_input_dir", label="Input directory",
297                                          **backbone.get_hide_dirs(),
298                                          placeholder="A directory on the same machine where the server is running.")
299                        inp += gr.Textbox(elem_id="depthmap_batch_output_dir", label="Output directory",
300                                          **backbone.get_hide_dirs(),
301                                          placeholder="Leave blank to save images to the default path.")
302                        gr.HTML("Files in the output directory may be overwritten.")
303                        inp += gr.Checkbox(elem_id="depthmap_batch_reuse",
304                                           label="Skip generation and use (edited/custom) depthmaps "
305                                                 "in output directory when a file already exists.",
306                                           value=True)
307                    with gr.TabItem('Single Video') as depthmap_mode_3:
308                        inp = depthmap_mode_video(inp)
309                submit = gr.Button('Generate', elem_id="depthmap_generate", variant='primary')
310                inp |= main_ui_panel(True)  # Main panel is inserted here
311                unloadmodels = gr.Button('Unload models', elem_id="depthmap_unloadmodels")
312
313            with gr.Column(variant='panel'):
314                with gr.Tabs(elem_id="mode_depthmap_output"):
315                    with gr.TabItem('Depth Output'):
316                        with gr.Group():
317                            result_images = gr.Gallery(label='Output', show_label=False,
318                                                       elem_id=f"depthmap_gallery", columns=4)
319                        with gr.Column():
320                            html_info = gr.HTML()
321                        folder_symbol = '\U0001f4c2'  # 📂
322                        gr.Button(folder_symbol, visible=not backbone.get_cmd_opt('hide_ui_dir_config', False)).click(
323                            fn=lambda: open_folder_action(), inputs=[], outputs=[],
324                        )
325
326                    with gr.TabItem('3D Mesh'):
327                        with gr.Group():
328                            result_depthmesh = gr.Model3D(label="3d Mesh", clear_color=[1.0, 1.0, 1.0, 1.0])
329                            with gr.Row():
330                                # loadmesh = gr.Button('Load')
331                                clearmesh = gr.Button('Clear')
332
333                    with gr.TabItem('Generate video'):
334                        # generate video
335                        with gr.Group():
336                            with gr.Row():
337                                gr.Markdown("Generate video from inpainted(!) mesh.")
338                            with gr.Row():
339                                depth_vid = gr.Video(interactive=False)
340                            with gr.Column():
341                                vid_html_info_x = gr.HTML()
342                                vid_html_info = gr.HTML()
343                                fn_mesh = gr.Textbox(label="Input Mesh (.ply | .obj)", **backbone.get_hide_dirs(),
344                                                     placeholder="A file on the same machine where "
345                                                                 "the server is running.")
346                            with gr.Row():
347                                vid_numframes = gr.Textbox(label="Number of frames", value="300")
348                                vid_fps = gr.Textbox(label="Framerate", value="40")
349                                vid_format = gr.Dropdown(label="Format", choices=['mp4', 'webm'], value='mp4',
350                                                         type="value", elem_id="video_format")
351                                vid_ssaa = gr.Dropdown(label="SSAA", choices=['1', '2', '3', '4'], value='3',
352                                                       type="value", elem_id="video_ssaa")
353                            with gr.Row():
354                                vid_traj = gr.Dropdown(label="Trajectory",
355                                                       choices=['straight-line', 'double-straight-line', 'circle'],
356                                                       value='double-straight-line', type="index",
357                                                       elem_id="video_trajectory")
358                                vid_shift = gr.Textbox(label="Translate: x, y, z", value="-0.015, 0.0, -0.05")
359                                vid_border = gr.Textbox(label="Crop: top, left, bottom, right",
360                                                        value="0.03, 0.03, 0.05, 0.03")
361                                vid_dolly = gr.Checkbox(label="Dolly", value=False, elem_classes="smalltxt")
362                            with gr.Row():
363                                submit_vid = gr.Button('Generate Video', elem_id="depthmap_generatevideo",
364                                                       variant='primary')
365
366        inp += inp.enkey_tail()
367
368        depthmap_mode_0.select(lambda: '0', None, inp['depthmap_mode'])
369        depthmap_mode_1.select(lambda: '1', None, inp['depthmap_mode'])
370        depthmap_mode_2.select(lambda: '2', None, inp['depthmap_mode'])
371        depthmap_mode_3.select(lambda: '3', None, inp['depthmap_mode'])
372
373        def custom_depthmap_change_fn(mode, zero_on, three_on):
374            hide = mode == '0' and zero_on or mode == '3' and three_on
375            return inp['custom_depthmap_img'].update(visible=hide), \
376                inp['depthmap_gen_row_0'].update(visible=not hide), \
377                inp['depthmap_gen_row_1'].update(visible=not hide), \
378                inp['depthmap_gen_row_3'].update(visible=not hide), not hide
379        custom_depthmap_change_els = ['depthmap_mode', 'custom_depthmap', 'depthmap_vm_custom_checkbox']
380        for el in custom_depthmap_change_els:
381            inp[el].change(
382            fn=custom_depthmap_change_fn,
383            inputs=[inp[el] for el in custom_depthmap_change_els],
384            outputs=[inp[st] for st in [
385                'custom_depthmap_img', 'depthmap_gen_row_0', 'depthmap_gen_row_1', 'depthmap_gen_row_3',
386                go.DO_OUTPUT_DEPTH]])
387
388        unloadmodels.click(
389            fn=unload_models,
390            inputs=[],
391            outputs=[]
392        )
393
394        clearmesh.click(
395            fn=lambda: None,
396            inputs=[],
397            outputs=[result_depthmesh]
398        )
399
400        submit.click(
401            fn=backbone.wrap_gradio_gpu_call(run_generate),
402            inputs=inp.enkey_body(),
403            outputs=[
404                result_images,
405                fn_mesh,
406                result_depthmesh,
407                html_info
408            ]
409        )
410
411        submit_vid.click(
412            fn=backbone.wrap_gradio_gpu_call(run_makevideo),
413            inputs=[
414                fn_mesh,
415                vid_numframes,
416                vid_fps,
417                vid_traj,
418                vid_shift,
419                vid_border,
420                vid_dolly,
421                vid_format,
422                vid_ssaa
423            ],
424            outputs=[
425                depth_vid,
426                vid_html_info_x,
427                vid_html_info
428            ]
429        )
430
431    return depthmap_interface
432
433
434def format_exception(e: Exception):
435    traceback.print_exc()
436    msg = '<h3>' + 'ERROR: ' + str(e) + '</h3>' + '\n'
437    if 'out of GPU memory' in msg:
438        pass
439    elif "torch.hub.load('facebookresearch/dinov2'," in traceback.format_exc():
440        msg += ('<h4>To use Depth Anything integration in WebUI mode, please add "--disable-safe-unpickle" to the command line flags. '
441                'Alternatively, use Standalone mode. This is a known issue.')
442    elif "Error(s) in loading state_dict " in traceback.format_exc():
443        msg += ('<h4>There was issue during loading the model.'
444                'Please add "--disable-safe-unpickle" to the command line flags. This is a known issue.')
445    elif 'out of GPU memory' not in msg:
446        msg += \
447            'Please report this issue ' \
448            f'<a href="https://github.com/thygate/{REPOSITORY_NAME}/issues">here</a>. ' \
449            'Make sure to provide the full stacktrace: \n'
450        msg += '<code style="white-space: pre;">' + traceback.format_exc() + '</code>'
451    return msg
452
453
454def run_generate(*inputs):
455    inputs = GradioComponentBundle.enkey_to_dict(inputs)
456    depthmap_mode = inputs['depthmap_mode']
457    depthmap_batch_input_dir = inputs['depthmap_batch_input_dir']
458    image_batch = inputs['image_batch']
459    depthmap_input_image = inputs['depthmap_input_image']
460    depthmap_batch_output_dir = inputs['depthmap_batch_output_dir']
461    depthmap_batch_reuse = inputs['depthmap_batch_reuse']
462    custom_depthmap = inputs['custom_depthmap']
463    custom_depthmap_img = inputs['custom_depthmap_img']
464
465    inputimages = []
466    inputdepthmaps = []  # Allow supplying custom depthmaps
467    inputnames = []  # Also keep track of original file names
468
469    if depthmap_mode == '3':
470        try:
471            custom_depthmap = inputs['depthmap_vm_custom'] \
472                if inputs['depthmap_vm_custom_checkbox'] else None
473            colorvids_bitrate = inputs['depthmap_vm_compress_bitrate'] \
474                if inputs['depthmap_vm_compress_checkbox'] else None
475            ret = video_mode.gen_video(
476                inputs['depthmap_vm_input'], backbone.get_outpath(), inputs, custom_depthmap, colorvids_bitrate,
477                inputs['depthmap_vm_smoothening_mode'])
478            return [], None, None, ret
479        except Exception as e:
480            ret = format_exception(e)
481        return [], None, None, ret
482
483    if depthmap_mode == '2' and depthmap_batch_output_dir != '':
484        outpath = depthmap_batch_output_dir
485    else:
486        outpath = backbone.get_outpath()
487
488    if depthmap_mode == '0':  # Single image
489        if depthmap_input_image is None:
490            return [], None, None, "Please select an input image"
491        inputimages.append(depthmap_input_image)
492        inputnames.append(None)
493        if custom_depthmap:
494            if custom_depthmap_img is None:
495                return [], None, None, \
496                    "Custom depthmap is not specified. Please either supply it or disable this option."
497            inputdepthmaps.append(Image.open(os.path.abspath(custom_depthmap_img.name)))
498        else:
499            inputdepthmaps.append(None)
500    if depthmap_mode == '1':  # Batch Process
501        if image_batch is None:
502            return [], None, None, "Please select input images", ""
503        for img in image_batch:
504            image = Image.open(os.path.abspath(img.name))
505            inputimages.append(image)
506            inputnames.append(os.path.splitext(img.orig_name)[0])
507        print(f'{len(inputimages)} images will be processed')
508    elif depthmap_mode == '2':  # Batch from Directory
509        # TODO: There is a RAM leak when we process batches, I can smell it! Or maybe it is gone.
510        assert not backbone.get_cmd_opt('hide_ui_dir_config', False), '--hide-ui-dir-config option must be disabled'
511        if depthmap_batch_input_dir == '':
512            return [], None, None, "Please select an input directory."
513        if depthmap_batch_input_dir == depthmap_batch_output_dir:
514            return [], None, None, "Please pick different directories for batch processing."
515        image_list = backbone.listfiles(depthmap_batch_input_dir)
516        for path in image_list:
517            try:
518                inputimages.append(Image.open(path))
519                inputnames.append(path)
520
521                custom_depthmap = None
522                if depthmap_batch_reuse:
523                    basename = Path(path).stem
524                    # Custom names are not used in samples directory
525                    if outpath != backbone.get_opt('outdir_extras_samples', None):
526                        # Possible filenames that the custom depthmaps may have
527                        name_candidates = [f'{basename}-0000.{backbone.get_opt("samples_format", "png")}',  # current format
528                                           f'{basename}.png',  # human-intuitive format
529                                           f'{Path(path).name}']  # human-intuitive format (worse)
530                        for fn_cand in name_candidates:
531                            path_cand = os.path.join(outpath, fn_cand)
532                            if os.path.isfile(path_cand):
533                                custom_depthmap = Image.open(os.path.abspath(path_cand))
534                                break
535                inputdepthmaps.append(custom_depthmap)
536            except Exception as e:
537                print(f'Failed to load {path}, ignoring. Exception: {str(e)}')
538        inputdepthmaps_n = len([1 for x in inputdepthmaps if x is not None])
539        print(f'{len(inputimages)} images will be processed, {inputdepthmaps_n} existing depthmaps will be reused')
540
541    gen_obj = core_generation_funnel(outpath, inputimages, inputdepthmaps, inputnames, inputs, backbone.gather_ops())
542
543    # Saving images
544    img_results = []
545    results_total = 0
546    inpainted_mesh_fi = mesh_simple_fi = None
547    msg = ""  # Empty string is never returned
548    while True:
549        try:
550            input_i, type, result = next(gen_obj)
551            results_total += 1
552        except StopIteration:
553            # TODO: return more info
554            msg = '<h3>Successfully generated</h3>' if results_total > 0 else \
555                '<h3>Successfully generated nothing - please check the settings and try again</h3>'
556            break
557        except Exception as e:
558            msg = format_exception(e)
559            break
560        if type == 'simple_mesh':
561            mesh_simple_fi = result
562            continue
563        if type == 'inpainted_mesh':
564            inpainted_mesh_fi = result
565            continue
566        if not isinstance(result, Image.Image):
567            print(f'This is not supposed to happen! Somehow output type {type} is not supported! Input_i: {input_i}.')
568            continue
569        img_results += [(input_i, type, result)]
570
571        if inputs["save_outputs"]:
572            try:
573                basename = 'depthmap'
574                if depthmap_mode == '2' and inputnames[input_i] is not None:
575                    if outpath != backbone.get_opt('outdir_extras_samples', None):
576                        basename = Path(inputnames[input_i]).stem
577                suffix = "" if type == "depth" else f"{type}"
578                backbone.save_image(result, path=outpath, basename=basename, seed=None,
579                           prompt=None, extension=backbone.get_opt('samples_format', 'png'), short_filename=True,
580                           no_prompt=True, grid=False, pnginfo_section_name="extras",
581                           suffix=suffix)
582            except Exception as e:
583                if not ('image has wrong mode' in str(e) or 'I;16' in str(e)):
584                    raise e
585                print('Catched exception: image has wrong mode!')
586                traceback.print_exc()
587
588    # Deciding what mesh to display (and if)
589    display_mesh_fi = None
590    if backbone.get_opt('depthmap_script_show_3d', True):
591        display_mesh_fi = mesh_simple_fi
592        if backbone.get_opt('depthmap_script_show_3d_inpaint', True):
593            if inpainted_mesh_fi is not None and len(inpainted_mesh_fi) > 0:
594                display_mesh_fi = inpainted_mesh_fi
595    return map(lambda x: x[2], img_results), inpainted_mesh_fi, display_mesh_fi, msg.replace('\n', '<br>')
596