浏览代码

working 3d api

Sergiu 9 月之前
父节点
当前提交
eb2ee657f3

+ 1 - 1
03_blender/sd_blender/__init__.py

@@ -93,7 +93,7 @@ def load_scene():
 
     set_output_paths(
         "D://Git//ap-canvas-creation-module//03_blender//sd_blender//sample_scene//Renders",
-        "TestProject",
+        scene_data["project_id"],
     )
 
     # setup compositing

二进制
03_blender/sd_blender/__pycache__/__init__.cpython-310.pyc


二进制
03_blender/sd_blender/__pycache__/__init__.cpython-311.pyc


+ 1 - 1
03_blender/sd_blender/mklink_40.bat

@@ -1,4 +1,4 @@
-mklink /J "C:\Users\hulpe\AppData\Roaming\Blender Foundation\Blender\4.0\scripts\addons\zs_sd_blender" "D:\Git\ap-canvas-creation-module\03_blender\sd_blender"
+mklink /J "C:\Users\hulpe\AppData\Roaming\Blender Foundation\Blender\4.2\scripts\addons\zs_sd_blender" "D:\Git\ap-canvas-creation-module\03_blender\sd_blender"
 
 ECHO\
 ECHO Enter your input string and press ENTER when done.

二进制
03_blender/sd_blender/sample_scene/Canvas_Render_Scene.blend


+ 6 - 1
03_blender/sd_blender/sample_scene/lighting_database.json

@@ -8,5 +8,10 @@
     "id": "15a314a1-8ba1-4e0e-ad0c-f605b06f89e8",
     "name": "GSG_PRO_STUDIOS_METAL_016_sm",
     "web_path": "https://www.laneige.com/LNG_PerfectRenew_Serum_MY2023.glb"
+  },
+  {
+    "id": "15a314a1-8ba1-4e0e-ad0c-f605b06f89e9",
+    "name": "i_Phone_Boxes_Reflection_sm",
+    "web_path": "https://www.laneige.com/LNG_PerfectRenew_Serum_MY2023.glb"
   }
-]
+]

+ 5 - 4
03_blender/sd_blender/sample_scene/scene_info.json

@@ -1,6 +1,5 @@
 {
   "scene": {
-    "project_id": "15a314a1-8ba1-4e0e-ad0c-f605b06f89f8",
     "objects": [
       {
         "name": "LNG Serum",
@@ -160,13 +159,15 @@
       },
       "lighting": {
         "type": "image",
-        "id": "15a314a1-8ba1-4e0e-ad0c-f605b06f89e8",
+        "id": "15a314a1-8ba1-4e0e-ad0c-f605b06f89e9",
         "intensity": 1,
-        "rotation": 25,
+        "rotation": 0,
         "flip_horizontal": false,
         "flip_vertical": false,
         "visible": false
       }
     }
-  }
+  },
+  "user_id": "test_user",
+  "project_id": "15a314a1-8ba1-4e0e-ad0c-f605b06f89f8"
 }

+ 8 - 7
03_blender/sd_blender/zs_ai_ddl.py

@@ -6,7 +6,7 @@ import base64
 DDL_SERVER = "http://69.230.251.234:8081/api"
 
 script_path_ai = (
-    "D://Git//ap-canvas-creation-module//04_stable_diffusion//sd_comfy_api.py"
+    "D://Git//ap-canvas-creation-module//04_stable_diffusion//sd_comfy_api_v2.py"
 )
 
 # script_path = Path(__file__).resolve()
@@ -17,6 +17,7 @@ scene_path = "D://Git//ap-canvas-creation-module//03_blender//sd_blender//sample
 render_script_path = (
     "D://Git//ap-canvas-creation-module//03_blender//sd_blender//zs_ai_render_script.py"
 )
+post_job_script_path = "D:/Git/ap-canvas-creation-module/04_stable_diffusion/zs_ai_post_ai_render_script.py"
 
 
 def convert_object_to_base64_string(obj):
@@ -100,7 +101,7 @@ def submit_3d_job(username, scene_data):
 
     ddl_3d_job = send_3d_ddl_job(
         job_info={
-            "Name": scene_data["scene"]["project_id"],
+            "Name": scene_data["project_id"],
             "UserName": username,
             "Frames": 1,
             "Priority": 49,
@@ -130,7 +131,7 @@ def submit_ai_image_job(username, ai_scene_data, dependency_job_id=""):
             "Priority": 49,
             "Name": "ComfyUI Job",
             "JobDependencies": dependency_job_id,
-            # "PostJobScript": "C:/WORK/2022.DDL_Script/postJobTest.py",
+            "PostJobScript": post_job_script_path,
         },
         plugin_info={
             "ScriptFile": script_path_ai,
@@ -152,10 +153,10 @@ def submit_test_job():
     scene_data = get_scene_data()
     ai_scene_data = get_ai_scene_data()
     # ddl_3d_job = submit_3d_job("test_user", scene_data)
-    # ddl_ai_job = submit_ai_image_job("test_user", ai_scene_data)
-    ddl_3d_job, ddl_ai_job = submit_3d_and_ai_image_job(
-        "test_user", scene_data, scene_data
-    )
+    ddl_ai_job = submit_ai_image_job("test_user", ai_scene_data)
+    # ddl_3d_job, ddl_ai_job = submit_3d_and_ai_image_job(
+    #     "test_user", scene_data, scene_data
+    # )
 
 
 submit_test_job()

+ 98 - 0
03_blender/sd_blender/zs_ai_post_3d_render_script.py

@@ -0,0 +1,98 @@
+from __future__ import (
+    print_function,
+)  # Ensure print() function is available in Python 2
+import sys
+import json
+import requests
+import base64
+
+from Deadline.Scripting import *  # Import Deadline script utilities
+
+api_path = "https://canvas-api-test.anvil.app/_/api"
+image_path = (
+    "D:/Git/ap-canvas-creation-module/03_blender/sd_blender/sample_scene/Renders/"
+)
+
+
+def update_3d_image_task_status(row_id, new_status):
+    # Define the URL for the API endpoint
+    url = "{}/tasks/3d-image/update-status".format(api_path)
+
+    # Create a JSON payload
+    payload = {"row_id": row_id, "new_status": new_status}
+
+    # Make a POST request to the API endpoint with the JSON payload
+    response = requests.post(url, json=payload)
+
+    # Handle the response
+    if response.status_code == 200:
+        print("Status update was successful")
+        return response.json()
+    else:
+        print("Status update failed")
+        print("Status code:", response.status_code)
+        print("Response:", response.text)
+        return None
+
+
+def get_3d_image_task(row_id):
+    # Define the URL for the API endpoint
+    url = "{}/tasks/3d-image/{}".format(api_path, row_id)
+    print("Constructed URL:", url)  # Print the URL for debugging
+
+    # Make a GET request to the API endpoint
+    response = requests.get(url)
+
+    # Handle the response
+    if response.status_code == 200:
+        print("Request was successful")
+        return response.json()
+    else:
+        print("Request failed")
+        print("Status code:", response.status_code)
+        print("Response:", response.text)
+        return None
+
+
+def find_image_and_convert_to_base64(image_path):
+    with open(image_path, "rb") as image_file:
+        image_data = image_file.read()
+        image_base64 = base64.b64encode(image_data).decode("utf-8")
+        return image_base64
+
+
+def upload_image_to_anvil(row_id, image_base64):
+    url = "{}/test-2".format(api_path)
+    payload = {"row_id": row_id, "image_base64": image_base64}
+    response = requests.post(url, json=payload)
+    if response.status_code == 200:
+        print("Image uploaded successfully")
+        update_3d_image_task_status(row_id=row_id, new_status=3)
+        return response.json()
+
+    else:
+        print("Image upload failed")
+        print("Status code:", response.status_code)
+        print("Response:", response.text)
+        return None
+
+
+def __main__(*args):
+    deadlinePlugin = args[0]
+    job = deadlinePlugin.GetJob()
+    row_id = job.JobExtraInfo0
+
+    response = get_3d_image_task(row_id)
+    data = json.loads(response["data"])
+
+    project_id = data["project_id"]
+
+    image_base64 = find_image_and_convert_to_base64(
+        image_path + "{}/base0001.jpg".format(project_id)
+    )
+
+    upload_image_to_anvil(row_id, image_base64)
+
+
+if __name__ == "__main__":
+    __main__(sys.argv)

+ 38 - 0
03_blender/sd_blender/zs_ai_pre_3d_render_script.py

@@ -0,0 +1,38 @@
+from __future__ import (
+    print_function,
+)  # Ensure print() function is available in Python 2
+import sys
+import json
+import requests
+from Deadline.Scripting import *  # Import Deadline script utilities
+
+api_path = "https://canvas-api-test.anvil.app/_/api"
+
+
+def update_3d_image_task_status(row_id, new_status):
+    # Define the URL for the API endpoint
+    url = "{}/tasks/3d-image/update-status".format(api_path)
+
+    # Create a JSON payload
+    payload = {"row_id": row_id, "new_status": new_status}
+
+    # Make a POST request to the API endpoint with the JSON payload
+    response = requests.post(url, json=payload)
+
+    # Handle the response
+    if response.status_code == 200:
+        print("Status update was successful")
+        return response.json()
+    else:
+        print("Status update failed")
+        print("Status code:", response.status_code)
+        print("Response:", response.text)
+        return None
+
+
+def __main__(*args):
+    deadlinePlugin = args[0]
+    job = deadlinePlugin.GetJob()
+    row_id = job.JobExtraInfo0
+
+    update_3d_image_task_status(row_id=row_id, new_status=2)

+ 1 - 1
03_blender/sd_blender/zs_ai_render_script.py

@@ -4,7 +4,6 @@ import sys
 import json
 import base64
 
-
 argv = sys.argv
 try:
 
@@ -21,5 +20,6 @@ try:
 
     bpy.ops.zs_sd_loader.load_scene()
 
+
 except Exception as e:
     print("Error:", e)

+ 45 - 0
03_blender/sd_blender/zs_ai_utilities.py

@@ -0,0 +1,45 @@
+import requests
+
+
+def update_3d_image_task_status(row_id, new_status):
+    # Define the URL for the API endpoint
+    url = "https://your-anvil-app-url/_/api/update_3d_image_task_status"
+
+    # Create a JSON payload
+    payload = {"row_id": row_id, "new_status": new_status}
+
+    # Make a POST request to the API endpoint with the JSON payload
+    response = requests.post(url, json=payload)
+
+    # Handle the response
+    if response.status_code == 200:
+        print("Status update was successful")
+        return response.json()
+    else:
+        print("Status update failed")
+        print("Status code:", response.status_code)
+        print("Response:", response.text)
+        return None
+
+
+def get_3d_image_task(row_id):
+    # Define the URL for the API endpoint
+    url = f"https://your-anvil-app-url/_/api/get_3d_image_task/{row_id}"
+
+    # Make a GET request to the API endpoint
+    response = requests.get(url)
+
+    # Handle the response
+    if response.status_code == 200:
+        print("Request was successful")
+        return response.json()
+    else:
+        print("Request failed")
+        print("Status code:", response.status_code)
+        print("Response:", response.text)
+        return None
+
+
+# Example usage
+result = get_3d_image_task("example_row_id")
+print(result)

+ 13 - 6
04_stable_diffusion/sd_comfy_api.py

@@ -4,6 +4,7 @@ import random
 import sys
 import ast
 import base64
+import uuid
 
 # This is the ComfyUI api prompt format.
 
@@ -25,8 +26,11 @@ def convert_base64_string_to_object(base64_string):
     return json.loads(string)
 
 
+server_address = "127.0.0.1:8188"
+client_id = str(uuid.uuid4())
+
 with open(
-    "D://Git//ap-canvas-creation-module//04_stable_diffusion//workflows//canvas_3d_to_img_standard.json",
+    "D://Git//ap-canvas-creation-module//04_stable_diffusion//workflows//canvas_3d_to_img_standard_V1.json",
     "r",
 ) as f:
     prompt_text_json = f.read()
@@ -83,11 +87,11 @@ def main():
     positive_text = ai_scene_info["ai_scene"]["settings"]["positive_prompt"]
     negative_text = ai_scene_info["ai_scene"]["settings"]["negative_prompt"]
 
-    image_path = "D://Git//ap-canvas-creation-module//03_blender//sd_blender//sample_scene//Renders//TestProject//"
+    image_path = "D://Git//ap-canvas-creation-module//03_blender//sd_blender//sample_scene//Renders//15a314a1-8ba1-4e0e-ad0c-f605b06f89f8//"
 
-    image_base_path = image_path + "base0001.png"
-    image_alpha_products_path = image_path + "alpha_products0001.png"
-    image_depth_path = image_path + "depth0001.png"
+    image_base_path = image_path + "base0001.jpg"
+    image_alpha_products_path = image_path + "alpha_products0001.jpg"
+    # image_depth_path = image_path + "depth0001.png"
 
     prompt = json.loads(prompt_text_json)
     set_filename(prompt, "Save Image", "custom/basic_api_example")
@@ -95,6 +99,9 @@ def main():
     ksampler_main = find_node(prompt, "KSampler")
     ksampler_main["inputs"]["noise_seed"] = random.randint(0, 1000000)
 
+    ksampler_main = find_node(prompt, "KSampler")
+    ksampler_main["inputs"]["steps"] = 30
+
     prompt_positive = find_node(prompt, "positive_CLIPTextEncodeSDXL")
     prompt_positive["inputs"]["text_g"] = positive_text
 
@@ -111,7 +118,7 @@ def main():
     image_base["inputs"]["image"] = image_alpha_products_path
 
     image_base = find_node(prompt, "image_depth")
-    image_base["inputs"]["image"] = image_depth_path
+    # image_base["inputs"]["image"] = image_depth_path
 
     queue_prompt(prompt)
 

+ 171 - 0
04_stable_diffusion/sd_comfy_api_v2.py

@@ -0,0 +1,171 @@
+#This is an example that uses the websockets api to know when a prompt execution is done
+#Once the prompt execution is done it downloads the images using the /history endpoint
+
+import websocket #NOTE: websocket-client (https://github.com/websocket-client/websocket-client)
+import uuid
+import json
+import urllib.request
+import urllib.parse
+from PIL import Image
+import io
+import random
+import sys
+import base64
+
+server_address = "127.0.0.1:8188"
+client_id = str(uuid.uuid4())
+
+def convert_base64_string_to_object(base64_string):
+    bytes = base64.b64decode(base64_string)
+    string = bytes.decode("ascii")
+
+    return json.loads(string)
+
+def set_filename(json_obj, title, new_prefix):
+    for key, value in json_obj.items():
+        if isinstance(value, dict):
+            if value.get("_meta", {}).get("title") == title:
+                if "inputs" in value and "filename_prefix" in value["inputs"]:
+                    value["inputs"]["filename_prefix"] = new_prefix
+                else:
+                    result = set_filename(value, title, new_prefix)
+                    if result:
+                        return result
+    return None
+
+def find_node(json_obj, title):
+    for key, value in json_obj.items():
+        if isinstance(value, dict):
+            if value.get("_meta", {}).get("title") == title:
+                return value
+            else:
+                result = find_node(value, title)
+                if result:
+                    return result
+    return None
+
+
+def queue_prompt(prompt):
+    p = {"prompt": prompt, "client_id": client_id}
+    data = json.dumps(p).encode('utf-8')
+    req =  urllib.request.Request("http://{}/prompt".format(server_address), data=data)
+    return json.loads(urllib.request.urlopen(req).read())
+
+def get_prompt(ai_scene_info):
+    with open(
+    "D://Git//ap-canvas-creation-module//04_stable_diffusion//workflows//canvas_3d_to_img_standard_V1.json",
+    "r",
+    ) as f:
+        prompt_text_json = f.read()
+
+    prompt = json.loads(prompt_text_json)
+    #set the text prompt for our positive CLIPTextEncode
+    positive_text = ai_scene_info["ai_scene"]["settings"]["positive_prompt"]
+    negative_text = ai_scene_info["ai_scene"]["settings"]["negative_prompt"]
+
+    image_path = "D://Git//ap-canvas-creation-module//03_blender//sd_blender//sample_scene//Renders//15a314a1-8ba1-4e0e-ad0c-f605b06f89f8//"
+
+    image_base_path = image_path + "base0001.jpg"
+    image_alpha_products_path = image_path + "alpha_products0001.jpg"
+    # image_depth_path = image_path + "depth0001.png"
+
+    prompt = json.loads(prompt_text_json)
+    set_filename(prompt, "Save Image", "custom/basic_api_example")
+
+    ksampler_main = find_node(prompt, "KSampler")
+    ksampler_main["inputs"]["noise_seed"] = random.randint(0, 1000000)
+
+    ksampler_main = find_node(prompt, "KSampler")
+    ksampler_main["inputs"]["steps"] = 30
+
+    prompt_positive = find_node(prompt, "positive_CLIPTextEncodeSDXL")
+    prompt_positive["inputs"]["text_g"] = positive_text
+
+    prompt_positive["inputs"]["text_l"] = positive_text
+
+    prompt_negative = find_node(prompt, "negative_CLIPTextEncodeSDXL")
+    prompt_negative["inputs"]["text_g"] = negative_text
+    prompt_negative["inputs"]["text_l"] = negative_text
+
+    image_base = find_node(prompt, "image_base")
+    image_base["inputs"]["image"] = image_base_path
+
+    image_base = find_node(prompt, "image_product_mask")
+    image_base["inputs"]["image"] = image_alpha_products_path
+
+    image_base = find_node(prompt, "image_depth")
+    # image_base["inputs"]["image"] = image_depth_path
+
+    return prompt
+
+def get_image(filename, subfolder, folder_type):
+    data = {"filename": filename, "subfolder": subfolder, "type": folder_type}
+    url_values = urllib.parse.urlencode(data)
+    with urllib.request.urlopen("http://{}/view?{}".format(server_address, url_values)) as response:
+        return response.read()
+
+def get_history(prompt_id):
+    with urllib.request.urlopen("http://{}/history/{}".format(server_address, prompt_id)) as response:
+        return json.loads(response.read())
+
+def get_images(ws, prompt):
+    prompt_id = queue_prompt(prompt)['prompt_id']
+    output_images = {}
+    while True:
+        out = ws.recv()
+        if isinstance(out, str):
+            message = json.loads(out)
+            if message['type'] == 'executing':
+                data = message['data']
+                if data['node'] is None and data['prompt_id'] == prompt_id:
+                    break #Execution is done
+        else:
+            continue #previews are binary data
+
+    history = get_history(prompt_id)[prompt_id]
+    for node_id in history['outputs']:
+        node_output = history['outputs'][node_id]
+        images_output = []
+        if 'images' in node_output:
+            for image in node_output['images']:
+                image_data = get_image(image['filename'], image['subfolder'], image['type'])
+                images_output.append(image_data)
+        output_images[node_id] = images_output
+
+    return output_images
+
+def main():
+
+    argv = sys.argv
+
+    try:
+
+        argv = argv[argv.index("--") + 1 :]
+
+        ai_scene_info = convert_base64_string_to_object(argv[0])
+
+        print("loading scene data", ai_scene_info)
+
+    except Exception as e:
+        print("Error:", e)
+
+    prompt = get_prompt(ai_scene_info)    
+
+    ws = websocket.WebSocket()
+    ws.connect("ws://{}/ws?clientId={}".format(server_address, client_id))
+    images = get_images(ws, prompt)
+
+    print("Workflow completed, images are stored in the images variable")
+
+    #Commented out code to display the output images:
+
+    # for node_id in images:
+    #     for image_data in images[node_id]:
+            
+    #         image = Image.open(io.BytesIO(image_data))
+    #         image.show()
+
+if __name__ == "__main__":
+    main()
+
+

+ 35 - 19
04_stable_diffusion/workflows/canvas_3d_to_img_standard.json → 04_stable_diffusion/workflows/canvas_3d_to_img_standard_V1.json

@@ -1,7 +1,7 @@
 {
   "1": {
     "inputs": {
-      "ckpt_name": "epicrealismXL_v8Kiss.safetensors"
+      "ckpt_name": "juggernautXL_juggXIByRundiffusion.safetensors"
     },
     "class_type": "CheckpointLoaderSimple",
     "_meta": {
@@ -51,7 +51,7 @@
   "8": {
     "inputs": {
       "add_noise": "enable",
-      "noise_seed": 106,
+      "noise_seed": 114,
       "steps": 12,
       "cfg": 5,
       "sampler_name": "dpmpp_sde_gpu",
@@ -95,7 +95,7 @@
   "17": {
     "inputs": {
       "images": [
-        "55",
+        "38",
         0
       ]
     },
@@ -106,7 +106,7 @@
   },
   "20": {
     "inputs": {
-      "image": "010_KER_AlwaysOn_Beauty (1).png",
+      "image": "base0001.jpg",
       "upload": "image"
     },
     "class_type": "LoadImage",
@@ -116,7 +116,7 @@
   },
   "25": {
     "inputs": {
-      "strength": 0.7000000000000001,
+      "strength": 1,
       "start_percent": 0,
       "end_percent": 1,
       "positive": [
@@ -132,7 +132,7 @@
         0
       ],
       "image": [
-        "30",
+        "61",
         0
       ]
     },
@@ -143,26 +143,16 @@
   },
   "26": {
     "inputs": {
-      "control_net_name": "control-lora-depth-rank256.safetensors"
+      "control_net_name": "diffusion_pytorch_model_promax.safetensors"
     },
     "class_type": "ControlNetLoader",
     "_meta": {
       "title": "Load ControlNet Model"
     }
   },
-  "30": {
-    "inputs": {
-      "image": "010_KER_AlwaysOn_Depth_2.png",
-      "upload": "image"
-    },
-    "class_type": "LoadImage",
-    "_meta": {
-      "title": "image_depth"
-    }
-  },
   "33": {
     "inputs": {
-      "image": "010_KER_AlwaysOn_Alpha (1).png",
+      "image": "alpha_products0001.jpg",
       "channel": "red",
       "upload": "image"
     },
@@ -214,7 +204,7 @@
     "inputs": {
       "filename_prefix": "ComfyUI",
       "images": [
-        "55",
+        "38",
         0
       ]
     },
@@ -247,5 +237,31 @@
     "_meta": {
       "title": "Upscale Image (using Model)"
     }
+  },
+  "61": {
+    "inputs": {
+      "preprocessor": "DepthAnythingV2Preprocessor",
+      "resolution": 1024,
+      "image": [
+        "20",
+        0
+      ]
+    },
+    "class_type": "AIO_Preprocessor",
+    "_meta": {
+      "title": "AIO Aux Preprocessor"
+    }
+  },
+  "62": {
+    "inputs": {
+      "images": [
+        "61",
+        0
+      ]
+    },
+    "class_type": "PreviewImage",
+    "_meta": {
+      "title": "Preview Image"
+    }
   }
 }

+ 261 - 0
04_stable_diffusion/workflows/canvas_3d_to_img_standard_V2.json

@@ -0,0 +1,261 @@
+{
+  "1": {
+    "inputs": {
+      "image": "base_0001 (1).png",
+      "upload": "image"
+    },
+    "class_type": "LoadImage",
+    "_meta": {
+      "title": "image_base"
+    }
+  },
+  "2": {
+    "inputs": {
+      "preprocessor": "CannyEdgePreprocessor",
+      "resolution": 512,
+      "image": [
+        "1",
+        0
+      ]
+    },
+    "class_type": "AIO_Preprocessor",
+    "_meta": {
+      "title": "AIO Aux Preprocessor"
+    }
+  },
+  "3": {
+    "inputs": {
+      "images": [
+        "2",
+        0
+      ]
+    },
+    "class_type": "PreviewImage",
+    "_meta": {
+      "title": "Preview Image"
+    }
+  },
+  "4": {
+    "inputs": {
+      "ckpt_name": "juggernautXL_juggXIByRundiffusion.safetensors"
+    },
+    "class_type": "CheckpointLoaderSimple",
+    "_meta": {
+      "title": "Load Checkpoint"
+    }
+  },
+  "5": {
+    "inputs": {
+      "seed": 982662750540146,
+      "steps": 25,
+      "cfg": 6,
+      "sampler_name": "dpmpp_2m",
+      "scheduler": "karras",
+      "denoise": 1,
+      "model": [
+        "4",
+        0
+      ],
+      "positive": [
+        "18",
+        0
+      ],
+      "negative": [
+        "18",
+        1
+      ],
+      "latent_image": [
+        "8",
+        0
+      ]
+    },
+    "class_type": "KSampler",
+    "_meta": {
+      "title": "KSampler"
+    }
+  },
+  "7": {
+    "inputs": {
+      "text": "",
+      "clip": [
+        "4",
+        1
+      ]
+    },
+    "class_type": "CLIPTextEncode",
+    "_meta": {
+      "title": "negative_CLIPTextEncodeSDXL"
+    }
+  },
+  "8": {
+    "inputs": {
+      "width": 1024,
+      "height": 1024,
+      "batch_size": 1
+    },
+    "class_type": "EmptyLatentImage",
+    "_meta": {
+      "title": "Empty Latent Image"
+    }
+  },
+  "9": {
+    "inputs": {
+      "samples": [
+        "5",
+        0
+      ],
+      "vae": [
+        "4",
+        2
+      ]
+    },
+    "class_type": "VAEDecode",
+    "_meta": {
+      "title": "VAE Decode"
+    }
+  },
+  "10": {
+    "inputs": {
+      "images": [
+        "22",
+        0
+      ]
+    },
+    "class_type": "PreviewImage",
+    "_meta": {
+      "title": "Preview Image"
+    }
+  },
+  "15": {
+    "inputs": {
+      "switch_1": "On",
+      "controlnet_1": "diffusion_pytorch_model_promax.safetensors",
+      "controlnet_strength_1": 1,
+      "start_percent_1": 0,
+      "end_percent_1": 1,
+      "switch_2": "On",
+      "controlnet_2": "diffusion_pytorch_model_promax.safetensors",
+      "controlnet_strength_2": 1,
+      "start_percent_2": 0,
+      "end_percent_2": 1,
+      "switch_3": "Off",
+      "controlnet_3": "None",
+      "controlnet_strength_3": 1,
+      "start_percent_3": 0,
+      "end_percent_3": 1,
+      "image_1": [
+        "16",
+        0
+      ],
+      "image_2": [
+        "2",
+        0
+      ]
+    },
+    "class_type": "CR Multi-ControlNet Stack",
+    "_meta": {
+      "title": "🕹️ CR Multi-ControlNet Stack"
+    }
+  },
+  "16": {
+    "inputs": {
+      "preprocessor": "DepthAnythingV2Preprocessor",
+      "resolution": 512,
+      "image": [
+        "1",
+        0
+      ]
+    },
+    "class_type": "AIO_Preprocessor",
+    "_meta": {
+      "title": "AIO Aux Preprocessor"
+    }
+  },
+  "17": {
+    "inputs": {
+      "images": [
+        "16",
+        0
+      ]
+    },
+    "class_type": "PreviewImage",
+    "_meta": {
+      "title": "Preview Image"
+    }
+  },
+  "18": {
+    "inputs": {
+      "switch": "On",
+      "base_positive": [
+        "19",
+        0
+      ],
+      "base_negative": [
+        "7",
+        0
+      ],
+      "controlnet_stack": [
+        "15",
+        0
+      ]
+    },
+    "class_type": "CR Apply Multi-ControlNet",
+    "_meta": {
+      "title": "🕹️ CR Apply Multi-ControlNet"
+    }
+  },
+  "19": {
+    "inputs": {
+      "width": 1024,
+      "height": 1024,
+      "crop_w": 0,
+      "crop_h": 0,
+      "target_width": 1024,
+      "target_height": 1024,
+      "text_g": "A sleek, luxury sedan parked in front of a traditional chinese yet modern building with large windows, casting a clean reflection into a calm, mirror-like water surface. The scene is captured in a professional, high-quality photograph with realistic shadows, crisp details, and a clear blue sky overhead. The entire setting exudes an elegant fusion of modern and traditional architecture, with a focus on photorealism, showcasing the car in a high-end, luxurious environment, automotive photography",
+      "text_l": "A sleek, luxury sedan parked in front of a traditional chinese yet modern building with large windows, casting a clean reflection into a calm, mirror-like water surface. The scene is captured in a professional, high-quality photograph with realistic shadows, crisp details, and a clear blue sky overhead. The entire setting exudes an elegant fusion of modern and traditional architecture, with a focus on photorealism, showcasing the car in a high-end, luxurious environment, automotive photography",
+      "clip": [
+        "4",
+        1
+      ]
+    },
+    "class_type": "CLIPTextEncodeSDXL",
+    "_meta": {
+      "title": "positive_CLIPTextEncodeSDXL"
+    }
+  },
+  "21": {
+    "inputs": {
+      "image": "alpha_0001.png",
+      "channel": "red",
+      "upload": "image"
+    },
+    "class_type": "LoadImageMask",
+    "_meta": {
+      "title": "image_product_mask"
+    }
+  },
+  "22": {
+    "inputs": {
+      "x": 0,
+      "y": 0,
+      "resize_source": true,
+      "destination": [
+        "9",
+        0
+      ],
+      "source": [
+        "1",
+        0
+      ],
+      "mask": [
+        "21",
+        0
+      ]
+    },
+    "class_type": "ImageCompositeMasked",
+    "_meta": {
+      "title": "ImageCompositeMasked"
+    }
+  }
+}

+ 99 - 0
04_stable_diffusion/zs_ai_post_ai_render_script.py

@@ -0,0 +1,99 @@
+from __future__ import (
+    print_function,
+)  # Ensure print() function is available in Python 2
+import sys
+import json
+import requests
+import base64
+
+from Deadline.Scripting import *  # Import Deadline script utilities
+
+api_path = "https://canvas-api-test.anvil.app/_/api"
+image_path = (
+    "D:/Git/ap-canvas-creation-module/03_blender/sd_blender/sample_scene/Renders/"
+)
+
+
+# def update_3d_image_task_status(row_id, new_status):
+#     # Define the URL for the API endpoint
+#     url = "{}/tasks/3d-image/update-status".format(api_path)
+
+#     # Create a JSON payload
+#     payload = {"row_id": row_id, "new_status": new_status}
+
+#     # Make a POST request to the API endpoint with the JSON payload
+#     response = requests.post(url, json=payload)
+
+#     # Handle the response
+#     if response.status_code == 200:
+#         print("Status update was successful")
+#         return response.json()
+#     else:
+#         print("Status update failed")
+#         print("Status code:", response.status_code)
+#         print("Response:", response.text)
+#         return None
+
+
+# def get_3d_image_task(row_id):
+#     # Define the URL for the API endpoint
+#     url = "{}/tasks/3d-image/{}".format(api_path, row_id)
+#     print("Constructed URL:", url)  # Print the URL for debugging
+
+#     # Make a GET request to the API endpoint
+#     response = requests.get(url)
+
+#     # Handle the response
+#     if response.status_code == 200:
+#         print("Request was successful")
+#         return response.json()
+#     else:
+#         print("Request failed")
+#         print("Status code:", response.status_code)
+#         print("Response:", response.text)
+#         return None
+
+
+# def find_image_and_convert_to_base64(image_path):
+#     with open(image_path, "rb") as image_file:
+#         image_data = image_file.read()
+#         image_base64 = base64.b64encode(image_data).decode("utf-8")
+#         return image_base64
+
+
+# def upload_image_to_anvil(row_id, image_base64):
+#     url = "{}/test-2".format(api_path)
+#     payload = {"row_id": row_id, "image_base64": image_base64}
+#     response = requests.post(url, json=payload)
+#     if response.status_code == 200:
+#         print("Image uploaded successfully")
+#         update_3d_image_task_status(row_id=row_id, new_status=3)
+#         return response.json()
+
+#     else:
+#         print("Image upload failed")
+#         print("Status code:", response.status_code)
+#         print("Response:", response.text)
+#         return None
+
+
+def __main__(*args):
+    deadlinePlugin = args[0]
+    job = deadlinePlugin.GetJob()
+    print("AI image completed")
+    # row_id = job.JobExtraInfo0
+
+    # response = get_3d_image_task(row_id)
+    # data = json.loads(response["data"])
+
+    # project_id = data["project_id"]
+
+    # image_base64 = find_image_and_convert_to_base64(
+    #     image_path + "{}/base0001.jpg".format(project_id)
+    # )
+
+    # upload_image_to_anvil(row_id, image_base64)
+
+
+if __name__ == "__main__":
+    __main__(sys.argv)