openpose_editor.py 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122
  1. import base64
  2. import gradio as gr
  3. import requests
  4. from typing import List, Dict, Any, Tuple
  5. from annotator.openpose import decode_json_as_poses, draw_poses
  6. from scripts.controlnet_ui.modal import ModalInterface
  7. from modules import shared
  8. from scripts.logging import logger
  9. def parse_data_url(data_url: str):
  10. # Split the URL at the comma
  11. media_type, data = data_url.split(",", 1)
  12. # Check if the data is base64-encoded
  13. assert ";base64" in media_type
  14. # Decode the base64 data
  15. return base64.b64decode(data)
  16. def encode_data_url(json_string: str) -> str:
  17. base64_encoded_json = base64.b64encode(json_string.encode("utf-8")).decode("utf-8")
  18. return f"data:application/json;base64,{base64_encoded_json}"
  19. class OpenposeEditor(object):
  20. # Filename used when user click the download link.
  21. download_file = "pose.json"
  22. # URL the openpose editor is mounted on.
  23. editor_url = "/openpose_editor_index"
  24. def __init__(self) -> None:
  25. self.render_button = None
  26. self.download_link = None
  27. self.modal = None
  28. self.render()
  29. def render(self):
  30. # The hidden button to trigger a re-render of generated image.
  31. self.render_button = gr.Button(visible=False, elem_classes=["cnet-render-pose"])
  32. # The hidden element that stores the pose json for backend retrieval.
  33. # The front-end javascript will write the edited JSON data to the element.
  34. self.pose_input = gr.Textbox(visible=False, elem_classes=["cnet-pose-json"])
  35. self.modal = ModalInterface(
  36. # Use about:blank here as placeholder so that the iframe does not
  37. # immediately navigate. Most of controlnet units do not need
  38. # openpose editor active. Only navigate when the user first click
  39. # 'Edit'. The navigation logic is in `openpose_editor.js`.
  40. f'<iframe src="about:blank"></iframe>',
  41. open_button_text="Edit",
  42. open_button_classes=["cnet-edit-pose"],
  43. open_button_extra_attrs=f'title="Send pose to {OpenposeEditor.editor_url} for edit."',
  44. ).create_modal(visible=False)
  45. self.download_link = gr.HTML(
  46. value="", visible=False, elem_classes=["cnet-download-pose"]
  47. )
  48. def register_callbacks(
  49. self, generated_image: gr.Image, use_preview_as_input: gr.Checkbox
  50. ):
  51. def render_pose(pose_url: str) -> Tuple[Dict, Dict]:
  52. json_string = parse_data_url(pose_url)
  53. poses, height, weight = decode_json_as_poses(json_string)
  54. logger.info('Preview as input is enabled.')
  55. return (
  56. # Generated image.
  57. gr.update(
  58. value=draw_poses(
  59. poses,
  60. height,
  61. weight,
  62. draw_body=True,
  63. draw_hand=True,
  64. draw_face=True,
  65. ),
  66. visible=True,
  67. ),
  68. # Use preview as input.
  69. gr.update(value=True),
  70. )
  71. self.render_button.click(
  72. fn=render_pose,
  73. inputs=[self.pose_input],
  74. outputs=[generated_image, use_preview_as_input],
  75. )
  76. def outputs(self) -> List[Any]:
  77. return [
  78. self.download_link,
  79. self.modal,
  80. ]
  81. def update(self, json_string: str) -> List[Dict]:
  82. """
  83. Called when there is a new JSON pose value generated by running
  84. preprocessor.
  85. Args:
  86. json_string: The new JSON string generated by preprocessor.
  87. Returns:
  88. An gr.update event.
  89. """
  90. hint = "Download the pose as .json file"
  91. html = f"""<a href='{encode_data_url(json_string)}'
  92. download='{OpenposeEditor.download_file}' title="{hint}">
  93. JSON</a>"""
  94. visible = json_string != ""
  95. return [
  96. # Download link update.
  97. gr.update(value=html, visible=visible),
  98. # Modal update.
  99. gr.update(
  100. visible=visible
  101. and not shared.opts.data.get("controlnet_disable_openpose_edit", False)
  102. ),
  103. ]