| import html | |
| import json | |
| import os.path | |
| import pathlib | |
| import typing | |
| import urllib.parse | |
| import gradio as gr | |
| try: | |
| root_path = pathlib.Path(__file__).resolve().parents[1] | |
| except NameError: | |
| import inspect | |
| root_path = pathlib.Path(inspect.getfile(lambda: None)).resolve().parents[1] | |
| def get_asset_url( | |
| file_path: pathlib.Path, append: typing.Optional[dict[str, str]] = None | |
| ) -> str: | |
| if append is None: | |
| append = {"v": str(os.path.getmtime(file_path))} | |
| else: | |
| append = append.copy() | |
| append["v"] = str(os.path.getmtime(file_path)) | |
| return f"/file={file_path.absolute()}?{urllib.parse.urlencode(append)}" | |
| def write_config_file() -> pathlib.Path: | |
| assets = { | |
| "models/hand.fbx": get_asset_url(root_path / "models" / "hand.fbx"), | |
| "models/foot.fbx": get_asset_url(root_path / "models" / "foot.fbx"), | |
| "src/poses/data.bin": get_asset_url(root_path / "src" / "poses" / "data.bin"), | |
| } | |
| MEDIAPIPE_POSE_VERSION = "0.5.1675469404" | |
| mediapipe_dir = root_path / "downloads" / "pose" / MEDIAPIPE_POSE_VERSION | |
| for file_name in [ | |
| "pose_landmark_full.tflite", | |
| "pose_web.binarypb", | |
| "pose_solution_packed_assets.data", | |
| "pose_solution_simd_wasm_bin.wasm", | |
| "pose_solution_packed_assets_loader.js", | |
| "pose_solution_simd_wasm_bin.js", | |
| ]: | |
| file_path = mediapipe_dir / file_name | |
| if not file_path.exists(): | |
| continue | |
| assets[file_name] = get_asset_url(file_path.absolute()) | |
| consts = {"assets": assets} | |
| config_dir = root_path / "downloads" | |
| config_dir.mkdir(mode=0o755, parents=True, exist_ok=True) | |
| config_path = config_dir / "config.json" | |
| config_path.write_text(json.dumps(consts)) | |
| return config_path | |
| def on_ui_tabs(): | |
| with gr.Blocks(analytics_enabled=False) as blocks: | |
| create_ui() | |
| return [(blocks, "3D Openpose", "threedopenpose")] | |
| def create_ui(): | |
| try: | |
| from modules.shared import opts | |
| cn_max: int = opts.control_net_max_models_num | |
| use_online: bool = opts.openpose3d_use_online_version | |
| except (ImportError, AttributeError): | |
| cn_max = 0 | |
| use_online = False | |
| if use_online: | |
| html_url = "https://zhuyu1997.github.io/open-pose-editor/" | |
| else: | |
| config = {"config": get_asset_url(write_config_file()) or ""} | |
| html_url = get_asset_url(root_path / "pages" / "index.html", config) | |
| with gr.Tabs(elem_id="openpose3d_main"): | |
| with gr.Tab(label="Edit Openpose"): | |
| gr.HTML( | |
| f""" | |
| <iframe id="openpose3d_iframe" src="{html.escape(html_url)}"></iframe> | |
| """ | |
| ) | |
| gr.Markdown( | |
| "Original: [Online 3D Openpose Editor](https://zhuyu1997.github.io/open-pose-editor/)" | |
| ) | |
| with gr.Tab(label="Send to ControlNet"): | |
| with gr.Row(): | |
| send_t2i = gr.Button(value="Send to txt2img", variant="primary") | |
| send_i2i = gr.Button(value="Send to img2img", variant="primary") | |
| with gr.Row(): | |
| cn_dropdown_list = [str(i) for i in range(cn_max)] | |
| cn_dropdown_list.insert(0, "-") | |
| with gr.Column(variant="panel"): | |
| pose_image = gr.Image( | |
| label="Pose", | |
| elem_id="openpose3d_pose_image", | |
| ) | |
| with gr.Row(): | |
| pose_target = gr.Dropdown( | |
| label="Control Model number", | |
| choices=cn_dropdown_list, | |
| value="0" if cn_max >= 1 else "-", | |
| ) | |
| pose_download = gr.Button(value="Download") | |
| with gr.Column(variant="panel"): | |
| depth_image = gr.Image( | |
| label="Depth", | |
| elem_id="openpose3d_depth_image", | |
| ) | |
| with gr.Row(): | |
| depth_target = gr.Dropdown( | |
| label="Control Model number", | |
| choices=cn_dropdown_list, | |
| value="1" if cn_max >= 2 else "-", | |
| ) | |
| depth_download = gr.Button(value="Download") | |
| with gr.Column(variant="panel"): | |
| normal_image = gr.Image( | |
| label="Normal", | |
| elem_id="openpose3d_normal_image", | |
| ) | |
| with gr.Row(): | |
| normal_target = gr.Dropdown( | |
| label="Control Model number", | |
| choices=cn_dropdown_list, | |
| value="2" if cn_max >= 3 else "-", | |
| ) | |
| normal_download = gr.Button(value="Download") | |
| with gr.Column(variant="panel"): | |
| canny_image = gr.Image( | |
| label="Canny", | |
| elem_id="openpose3d_canny_image", | |
| ) | |
| with gr.Row(): | |
| canny_target = gr.Dropdown( | |
| label="Control Model number", | |
| choices=cn_dropdown_list, | |
| value="3" if cn_max >= 4 else "-", | |
| ) | |
| canny_download = gr.Button(value="Download") | |
| send_cn_inputs = [ | |
| pose_image, | |
| pose_target, | |
| depth_image, | |
| depth_target, | |
| normal_image, | |
| normal_target, | |
| canny_image, | |
| canny_target, | |
| ] | |
| send_t2i.click( | |
| None, | |
| send_cn_inputs, | |
| None, | |
| _js="window.openpose3d.sendTxt2img", | |
| ) | |
| send_i2i.click( | |
| None, | |
| send_cn_inputs, | |
| None, | |
| _js="window.openpose3d.sendImg2img", | |
| ) | |
| pose_download.click( | |
| None, | |
| pose_image, | |
| None, | |
| _js="(v) => window.openpose3d.downloadImage(v, 'pose')", | |
| ) | |
| depth_download.click( | |
| None, | |
| depth_image, | |
| None, | |
| _js="(v) => window.openpose3d.downloadImage(v, 'depth')", | |
| ) | |
| normal_download.click( | |
| None, | |
| normal_image, | |
| None, | |
| _js="(v) => window.openpose3d.downloadImage(v, 'normal')", | |
| ) | |
| canny_download.click( | |
| None, | |
| canny_image, | |
| None, | |
| _js="(v) => window.openpose3d.downloadImage(v, 'canny')", | |
| ) | |
| def on_ui_settings(): | |
| from modules.shared import OptionInfo, opts | |
| section = ("openpose3d", "3D Openpose Editor") | |
| opts.add_option( | |
| "openpose3d_use_online_version", | |
| OptionInfo(False, "Use online version", section=section), | |
| ) | |
| def main(): | |
| js_path = root_path / "javascript" / "index.js" | |
| css_path = root_path / "style.css" | |
| original_template_response = gr.routes.templates.TemplateResponse | |
| head = """ | |
| <script> | |
| function waitForElement(parent, selector) { | |
| return new Promise((resolve) => { | |
| const observer = new MutationObserver(() => { | |
| if (!parent.querySelector(selector)) { | |
| return | |
| } | |
| observer.disconnect() | |
| resolve(undefined) | |
| }) | |
| observer.observe(parent, { | |
| childList: true, | |
| subtree: true, | |
| }) | |
| if (parent.querySelector(selector)) { | |
| resolve(undefined) | |
| } | |
| }) | |
| } | |
| let onTabChangedCallback | |
| function gradioApp() { | |
| const elems = document.getElementsByTagName('gradio-app') | |
| const gradioShadowRoot = elems.length == 0 ? null : elems[0].shadowRoot | |
| return gradioShadowRoot ? gradioShadowRoot : document | |
| } | |
| async function onUiLoaded(callback){ | |
| await waitForElement(gradioApp(), '#openpose3d_main') | |
| await callback() | |
| await onTabChangedCallback?.() | |
| } | |
| function onUiUpdate(callback){ | |
| onTabChangedCallback = callback | |
| } | |
| </script> | |
| """ | |
| head += f""" | |
| <script type="module"> | |
| document.addEventListener("DOMContentLoaded", function() {{import("{get_asset_url(js_path)}")}}) | |
| </script> | |
| """ | |
| def template_response(*args, **kwargs): | |
| res = original_template_response(*args, **kwargs) | |
| res.body = res.body.replace(b"</head>", f"{head}</head>".encode("utf8")) | |
| res.init_headers() | |
| return res | |
| gr.routes.templates.TemplateResponse = template_response | |
| with gr.Blocks(analytics_enabled=False, css=css_path.read_text()) as blocks: | |
| with gr.Tab(label="3D Openpose", elem_id="tab_threedopenpose"): | |
| create_ui() | |
| blocks.launch() | |
| try: | |
| from modules import script_callbacks | |
| script_callbacks.on_ui_tabs(on_ui_tabs) | |
| script_callbacks.on_ui_settings(on_ui_settings) | |
| except ImportError: | |
| main() | |