skkk commited on
Commit
ff1e8e9
1 Parent(s): 5b24095
.DS_Store ADDED
Binary file (6.15 kB). View file
 
README.md CHANGED
@@ -1,10 +1,10 @@
1
  ---
2
- title: Rodin
3
- emoji: 🐠
4
- colorFrom: yellow
5
- colorTo: indigo
6
  sdk: gradio
7
- sdk_version: 4.31.5
8
  app_file: app.py
9
  pinned: false
10
  ---
 
1
  ---
2
+ title: RodinTest
3
+ emoji: 🌖
4
+ colorFrom: green
5
+ colorTo: green
6
  sdk: gradio
7
+ sdk_version: 4.31.2
8
  app_file: app.py
9
  pinned: false
10
  ---
Rodin.py ADDED
@@ -0,0 +1,193 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import socketio
2
+ import requests
3
+ import json
4
+ import random
5
+ import base64
6
+ import io
7
+ from PIL import Image
8
+ from requests_toolbelt.multipart.encoder import MultipartEncoder
9
+ from constant import *
10
+
11
+
12
+ def login(email, password):
13
+ payload = {'password': password}
14
+ if email:
15
+ payload['email'] = email
16
+
17
+ response = requests.post(f"{BASE_URL}/user/login", json=payload)
18
+ response_data = response.json()
19
+
20
+ if 'error' in response_data and response_data['error']:
21
+ raise Exception(response_data['error'])
22
+ print("Login successful")
23
+ user_uuid = response_data['user_uuid']
24
+ token = response_data['token']
25
+
26
+ return user_uuid, token
27
+
28
+ def rodin_history(task_uuid, token):
29
+ headers = {
30
+ 'Authorization': f'Bearer {token}'
31
+ }
32
+ response = requests.post(f"{BASE_URL}/task/rodin_history", data={"uuid": task_uuid}, headers=headers)
33
+ return response.json()
34
+
35
+ def rodin_preprocess_image(generate_prompt, image, name, token):
36
+ m = MultipartEncoder(
37
+ fields={
38
+ 'generate_prompt': "true" if generate_prompt else "false",
39
+ 'images': (name, image, 'image/jpeg')
40
+ }
41
+ )
42
+ headers = {
43
+ 'Content-Type': m.content_type,
44
+ 'Authorization': f'Bearer {token}'
45
+ }
46
+ response = requests.post(f"{BASE_URL}/task/rodin_mesh_image_process", data=m, headers=headers)
47
+ return response.json()
48
+
49
+ def crop_image(image, type):
50
+ new_image_width = 360 * (11520 // 720) # 每隔720像素裁切一次,每次裁切宽度为360
51
+ new_image_height = 360 # 新图片的高度
52
+ new_image = Image.new('RGB', (new_image_width, new_image_height))
53
+
54
+ for i in range(11520 // 720):
55
+ left = i * 720 + type[1]
56
+ upper = type[0]
57
+ right = left + 360
58
+ lower = upper + 360
59
+ cropped_image = image.crop((left, upper, right, lower))
60
+ new_image.paste(cropped_image, (i * 360, 0))
61
+ return new_image
62
+
63
+ # Perform Rodin mesh operation
64
+ def rodin_mesh(prompt, group_uuid, settings, images, name, token):
65
+ images = [convert_base64_to_binary(img) for img in images]
66
+
67
+ m = MultipartEncoder(
68
+ fields={
69
+ 'prompt': prompt,
70
+ 'group_uuid': group_uuid,
71
+ 'settings': json.dumps(settings), # Convert settings dictionary to JSON string
72
+ **{f'images': (name, image, 'image/jpeg') for i, image in enumerate(images)}
73
+ }
74
+ )
75
+
76
+ headers = {
77
+ 'Content-Type': m.content_type,
78
+ 'Authorization': f'Bearer {token}'
79
+ }
80
+ response = requests.post(f"{BASE_URL}/task/rodin_mesh", data=m, headers=headers)
81
+ return response.json()
82
+
83
+ # Convert base64 to binary since the result from `rodin_preprocess_image` is encoded with base64
84
+ def convert_base64_to_binary(base64_string):
85
+ if ',' in base64_string:
86
+ base64_string = base64_string.split(',')[1]
87
+
88
+ image_data = base64.b64decode(base64_string)
89
+ image_buffer = io.BytesIO(image_data)
90
+
91
+ return image_buffer
92
+
93
+ def rodin_update(prompt, task_uuid, token, settings):
94
+ headers = {
95
+ 'Authorization': f'Bearer {token}'
96
+ }
97
+ response = requests.post(f"{BASE_URL}/task/rodin_update", data={"uuid": task_uuid, "prompt": prompt, "settings": settings}, headers=headers)
98
+ return response.json()
99
+
100
+
101
+ class Generator:
102
+ def __init__(self, user_id, password) -> None:
103
+ _, self.token = login(user_id, password)
104
+ self.task_uuid = None
105
+
106
+ def preprocess(self, prompt, image_path):
107
+ image_file = open(image_path, 'rb')
108
+
109
+ if image_file == None:
110
+ print("Invalid image file.")
111
+
112
+ try:
113
+ if not prompt:
114
+ preprocess_response = rodin_preprocess_image(generate_prompt=True, image=image_file, name="images.jpeg", token=self.token)
115
+ else:
116
+ preprocess_response = rodin_preprocess_image(generate_prompt=False, image=image_file, name="images.jpeg", token=self.token)
117
+ if 'error' in preprocess_response:
118
+ print("Error in image preprocessing:", preprocess_response['error'])
119
+ else:
120
+ if not prompt:
121
+ prompt = preprocess_response.get('prompt', 'Default prompt if none returned')
122
+ processed_image = "data:image/png;base64," + preprocess_response.get('processed_image', None)
123
+ finally:
124
+ image_file.close()
125
+
126
+ return prompt, processed_image
127
+
128
+ def generate_mesh(self, prompt, processed_image, task_uuid=""):
129
+ if task_uuid == "":
130
+ settings = {'view_weights': [1]} # Define weights as per your requirements, for multiple images, use multiple values, e,g [0.5, 0.5]
131
+ images = [processed_image] # List of images, all the images should be processed first
132
+
133
+ mesh_response = rodin_mesh(prompt=prompt, group_uuid=None, settings=settings, images=images, name="images.jpeg", token=self.token)
134
+ progress_checker = JobStatusChecker(BASE_URL, mesh_response['job']['subscription_key'])
135
+ progress_checker.start()
136
+
137
+ task_uuid = mesh_response['uuid'] # The task_uuid should be same during whole generation process
138
+ else:
139
+ new_prompt = prompt
140
+ settings = {
141
+ "view_weights": [1],
142
+ "seed": random.randint(0, 10000), # Customize your seed here
143
+ "escore": 5.5, # Temprature
144
+ }
145
+
146
+ update_response = rodin_update(new_prompt, task_uuid, self.token, settings)
147
+
148
+ # Check progress
149
+ subscription_key = update_response['job']['subscription_key']
150
+ checker = JobStatusChecker(BASE_URL, subscription_key)
151
+ checker.start()
152
+
153
+ preview_image = rodin_history(task_uuid, self.token)["v1"]["preview_image"]
154
+ response = requests.get(preview_image, stream=True)
155
+ if response.status_code == 200:
156
+ # 创建一个PIL Image对象
157
+ image = Image.open(response.raw)
158
+ # 在这里对image对象进行处理,如显示、保存等
159
+ else:
160
+ print(f"Can't get the preview image. Status code:{response.status_code}")
161
+ raise RuntimeError
162
+ response.close()
163
+ return image, task_uuid, crop_image(image, DEFAULT)
164
+
165
+
166
+ class JobStatusChecker:
167
+ def __init__(self, base_url, subscription_key):
168
+ self.base_url = base_url
169
+ self.subscription_key = subscription_key
170
+ self.sio = socketio.Client(logger=True, engineio_logger=True)
171
+
172
+ @self.sio.event
173
+ def connect():
174
+ print("Connected to the server.")
175
+
176
+ @self.sio.event
177
+ def disconnect():
178
+ print("Disconnected from server.")
179
+
180
+ @self.sio.on('message', namespace='*')
181
+ def message(*args, **kwargs):
182
+ if len(args) > 2:
183
+ data = args[2]
184
+ if data.get('jobStatus') == 'Succeeded':
185
+ print("Job Succeeded! Please find the SDF image in history")
186
+ self.sio.disconnect()
187
+ else:
188
+ print("Received event with insufficient arguments.")
189
+
190
+ def start(self):
191
+ self.sio.connect(f"{self.base_url}/scheduler_socket?subscription={self.subscription_key}",
192
+ namespaces=['/api/scheduler_socket'], transports='websocket')
193
+ self.sio.wait()
__pycache__/Rodin.cpython-310.pyc ADDED
Binary file (6.32 kB). View file
 
__pycache__/app.cpython-310.pyc ADDED
Binary file (5.55 kB). View file
 
__pycache__/constant.cpython-310.pyc ADDED
Binary file (739 Bytes). View file
 
app.py ADDED
@@ -0,0 +1,205 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ # os.system('pip uninstall -y gradio_fake3d')
3
+ # os.system('pip install gradio_fake3d-0.0.2-py3-none-any.whl')
4
+
5
+ import gradio as gr
6
+ import re
7
+ from gradio_fake3d import Fake3D
8
+ from PIL import Image
9
+ from Rodin import Generator, crop_image
10
+ from constant import *
11
+
12
+ generator = Generator(USER, PASSWORD)
13
+
14
+ change_button_name = """
15
+ function updateButton(input) {
16
+ var buttonGenerate = document.getElementById('button_generate');
17
+ buttonGenerate.innerText = 'Redo';
18
+ return '';
19
+ }
20
+ """
21
+
22
+ reset_button_name = """
23
+ function updateButton(input) {
24
+ var buttonGenerate = document.getElementById('button_generate');
25
+ buttonGenerate.innerText = 'Generate';
26
+ return '';
27
+ }
28
+ """
29
+
30
+ jump_to_rodin = """
31
+ function redirectToGithub(input) {
32
+ if (input.includes('OpenClay')) {
33
+ window.open("https://github.com/CLAY-3D/OpenCLAY", "_blank");
34
+ }
35
+ return "Rodin Gen-1(0525)";
36
+ }
37
+ """
38
+
39
+ html_content = """
40
+ <div style="text-align: center;">
41
+ <h1>Rodin Gen-1</h1>
42
+ <div style="display: flex; justify-content: space-around;">
43
+ <p><strong>Paper:</strong> <a href="https://sites.google.com/view/clay-3dlm" target="_blank"> https://sites.google.com/view/clay-3dlm</a></p>
44
+ <p><strong>Rodin Gen-1:</strong> <a href="https://hyperhuman.top/rodin" target="_blank">https://hyperhuman.top/rodin</a></p>
45
+ <p><strong>Github:</strong> <a href="https://github.com/CLAY-3D/OpenCLAY" target="_blank">https://github.com/CLAY-3D/OpenCLAY</a></p>
46
+ </div>
47
+ </div>
48
+ """
49
+
50
+ options = [
51
+ "Rodin Gen-1(0525)",
52
+ "OpenClay(600M) - Coming soon",
53
+ "OpenClay(200M) - Coming soon"
54
+ ]
55
+
56
+ def do_nothing(text):
57
+ return ""
58
+
59
+ def handle_selection(selection):
60
+ return "Rodin Gen-1(0525)"
61
+ # if selection in ["OpenClay(600M)", "OpenClay(200M)"]:
62
+ # # 返回一个 HTML 字符串,用于在新窗口中打开指定的 URL
63
+ # return f"<script>window.open('https://github.com/CLAY-3D/OpenCLAY', '_blank');</script>"
64
+ # else:
65
+ # return "You selected Rodin Gen-1(0525)."
66
+
67
+ def hint_in_prompt(hint, prompt):
68
+ return re.search(fr"{hint[:-1]}", prompt) is not None
69
+
70
+ def prompt_remove_hint(prompt, hint):
71
+ return re.sub(fr"\s*{hint[:-1]}[\.,]*", "", prompt)
72
+
73
+ def handle_hint_change(prompt: str, prompt_hint):
74
+ prompt = prompt.strip()
75
+ if prompt != "" and not prompt.endswith("."):
76
+ prompt = prompt + "."
77
+ for _, hint in PROMPT_HINT_LIST:
78
+ if hint in prompt_hint:
79
+ if not hint_in_prompt(hint, prompt):
80
+ prompt = prompt + " " + hint
81
+ else:
82
+ prompt = prompt_remove_hint(prompt, hint)
83
+ prompt = prompt.strip()
84
+ return prompt
85
+
86
+ def handle_prompt_change(prompt):
87
+ hint_list = []
88
+ for _, hint in PROMPT_HINT_LIST:
89
+ if hint_in_prompt(hint, prompt):
90
+ hint_list.append(hint)
91
+
92
+ return hint_list
93
+
94
+ def clear_task_uuid():
95
+ return ""
96
+
97
+ def return_render(image):
98
+ image = Image.fromarray(image)
99
+ return image, crop_image(image, DEFAULT)
100
+
101
+ def crop_image_default(image):
102
+ return crop_image(image, DEFAULT)
103
+
104
+ def crop_image_metal(image):
105
+ return crop_image(image, METAL)
106
+
107
+ def crop_image_contrast(image):
108
+ return crop_image(image, CONTRAST)
109
+
110
+ def crop_image_normal(image):
111
+ return crop_image(image, NORMAL)
112
+
113
+ with gr.Blocks() as demo:
114
+ gr.HTML(html_content)
115
+ with gr.Row():
116
+ with gr.Column():
117
+ block_image = gr.Image(height=256, image_mode="RGB", sources="upload", elem_classes="elem_imageupload", type="filepath")
118
+ block_model_card = gr.Dropdown(choices=options, label="Model Card", value="Rodin Gen-1(0525)", interactive=True)
119
+ # block_image_scale = gr.Slider(minimum=0, maximum=1, value=1, label="scale", interactive=True)
120
+ # block_model_card = gr.Radio(
121
+ # choices=["Rodin Gen-1(0525)", "OpenClay(600M)", "OpenClay(200M)"],
122
+ # label="Model Selection",
123
+ # value="Rodin Gen-1(0525)" # 默认选中第一个选项
124
+ # )
125
+ with gr.Group():
126
+ # with gr.Row(equal_height=True):
127
+ block_prompt = gr.Textbox(
128
+ value="",
129
+ placeholder="Auto generated description of 3d geometry",
130
+ lines=1,
131
+ show_label=True,
132
+ label="Prompt",
133
+ )
134
+ block_prompt_hint = gr.CheckboxGroup(value="Labels", choices=PROMPT_HINT_LIST)
135
+
136
+ with gr.Column():
137
+ with gr.Group():
138
+ fake3d = Fake3D(interactive=False, label="3D Preview")
139
+ with gr.Row():
140
+ button_generate = gr.Button(value="Generate", variant="primary", elem_id="button_generate")
141
+
142
+ with gr.Column(min_width=200, scale=20):
143
+ with gr.Row():
144
+ block_default = gr.Button("Default", min_width=0)
145
+ block_metal = gr.Button("Metal", min_width=0)
146
+ with gr.Row():
147
+ block_contrast = gr.Button("Contrast", min_width=0)
148
+ block_normal = gr.Button("Normal", min_width=0)
149
+
150
+ button_more = gr.Button(value="Download", variant="primary", link=rodin_url)
151
+
152
+ cache_raw_image = gr.Image(visible=False, type="pil")
153
+ cache_image_base64 = gr.Text(visible=False)
154
+ cacha_empty = gr.Text(visible=False)
155
+ cache_task_uuid = gr.Text(value="", visible=False)
156
+
157
+ # button_generate.click(fn=return_render, inputs=[block_image], outputs=[raw_image, fake3d])
158
+ block_image.change(
159
+ fn=do_nothing,
160
+ js=reset_button_name,
161
+ inputs=[cacha_empty],
162
+ outputs=[cacha_empty]
163
+ ).then(fn=clear_task_uuid, outputs=[cache_task_uuid], show_progress="hidden")
164
+
165
+ button_generate.click(
166
+ fn=generator.preprocess,
167
+ inputs=[block_prompt, block_image],
168
+ outputs=[block_prompt, cache_image_base64],
169
+ show_progress="minimal"
170
+ ).success(
171
+ fn=generator.generate_mesh,
172
+ inputs=[block_prompt, cache_image_base64, cache_task_uuid],
173
+ outputs=[cache_raw_image, cache_task_uuid, fake3d],
174
+ ).success(
175
+ fn=do_nothing,
176
+ js=change_button_name,
177
+ inputs=[cacha_empty],
178
+ outputs=[cacha_empty]
179
+ )
180
+ block_default.click(fn=crop_image_default, inputs=[cache_raw_image], outputs=fake3d, show_progress="minimal")
181
+ block_metal.click(fn=crop_image_metal, inputs=[cache_raw_image], outputs=fake3d, show_progress="minimal")
182
+ block_contrast.click(fn=crop_image_contrast, inputs=[cache_raw_image], outputs=fake3d, show_progress="minimal")
183
+ block_normal.click(fn=crop_image_normal, inputs=[cache_raw_image], outputs=fake3d, show_progress="minimal")
184
+
185
+ button_more.click()
186
+
187
+ block_prompt_hint.input(
188
+ fn=handle_hint_change, inputs=[block_prompt, block_prompt_hint], outputs=[block_prompt],
189
+ show_progress="hidden",
190
+ queue=False,
191
+ )
192
+
193
+ block_prompt.change(
194
+ fn=handle_prompt_change,
195
+ inputs=[block_prompt],
196
+ outputs=[block_prompt_hint],
197
+ trigger_mode="always_last",
198
+ show_progress="hidden",
199
+ )
200
+
201
+ block_model_card.change(fn=handle_selection, inputs=[block_model_card], outputs=[block_model_card], show_progress="hidden", js=jump_to_rodin)
202
+
203
+
204
+ if __name__ == "__main__":
205
+ demo.launch(show_api=False)
change_button_text.js ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ console.log('DOMContentLoaded');
2
+ document.addEventListener('DOMContentLoaded', function() {
3
+ var buttonGenerate = document.getElementById('button_generate');
4
+ var clickedOnce = false; // 用来追踪按钮是否被点击过一次
5
+
6
+ buttonGenerate.addEventListener('click', function() {
7
+ if (!clickedOnce) {
8
+ this.innerText = 'Redo';
9
+ clickedOnce = true; // 更新状态为已点击
10
+ }
11
+ });
12
+ });
constant.py ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Replace with your own backend
2
+ BASE_URL = "https://hyperhuman.deemos.com/api"
3
+
4
+ # Creds
5
+ USER = "merife6322@duiter.com"
6
+ PASSWORD = "lrhswjj1314"
7
+
8
+ DEFAULT = [0, 0]
9
+ CONTRAST = [360, 0]
10
+ METAL = [0, 360]
11
+ NORMAL = [360, 360]
12
+ rodin_url = "https://hyperhuman.deemos.com/rodin"
13
+
14
+ PROMPT_HINT_LIST = [
15
+ ["symmetric", "symmetric geometry."],
16
+ ["character", "character."],
17
+ ["low-poly", "low-poly."],
18
+ ["high-poly", "high-poly."],
19
+ ["sharp", "sharp edges."],
20
+ ["smooth", "smooth edges."],
21
+ ["simple", "simple geometry."],
22
+ ["complex", "complex geometry."],
23
+ ["single", "single asset."],
24
+ ["game-ready", "game-ready."],
25
+ ]
gradio_fake3d-0.0.2-py3-none-any.whl ADDED
Binary file (46.7 kB). View file
 
requirements.txt ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ python-socketio
2
+ requests
3
+ pillow
4
+ gradio==4.31.2
5
+ requests-toolbelt
6
+ websocket-client