Commit
·
8e3405b
1
Parent(s):
b492be8
Enhance app.py with improved error handling, compatibility fixes, and streamlined image processing functions. Update Gradio interface for better user experience, including clearer background choice options and refined button functionality.
Browse files
app.py
CHANGED
@@ -1,4 +1,4 @@
|
|
1 |
-
#
|
2 |
import spaces
|
3 |
import argparse
|
4 |
import numpy as np
|
@@ -36,7 +36,7 @@ def init_model():
|
|
36 |
help="config for stage2",
|
37 |
)
|
38 |
parser.add_argument("--device", type=str, default="cuda")
|
39 |
-
args = parser.parse_args()
|
40 |
|
41 |
# Download model files
|
42 |
crm_path = hf_hub_download(repo_id="Zhengyi/CRM", filename="CRM.pth")
|
@@ -73,6 +73,7 @@ def init_model():
|
|
73 |
# Global variables to store model and pipeline
|
74 |
model = None
|
75 |
pipeline = None
|
|
|
76 |
|
77 |
@spaces.GPU
|
78 |
def get_model():
|
@@ -84,7 +85,6 @@ def get_model():
|
|
84 |
|
85 |
rembg_session = rembg.new_session()
|
86 |
|
87 |
-
|
88 |
def expand_to_square(image, bg_color=(0, 0, 0, 0)):
|
89 |
# expand image to 1:1
|
90 |
width, height = image.size
|
@@ -111,7 +111,7 @@ def remove_background(
|
|
111 |
do_remove = True
|
112 |
if image.mode == "RGBA" and image.getextrema()[3][0] < 255:
|
113 |
# explain why current do not rm bg
|
114 |
-
print("
|
115 |
background = Image.new("RGBA", image.size, (0, 0, 0, 0))
|
116 |
image = Image.alpha_composite(background, image)
|
117 |
do_remove = False
|
@@ -121,7 +121,7 @@ def remove_background(
|
|
121 |
return image
|
122 |
|
123 |
def do_resize_content(original_image: Image, scale_rate):
|
124 |
-
# resize image content
|
125 |
if scale_rate != 1:
|
126 |
# Calculate the new size after rescaling
|
127 |
new_size = tuple(int(dim * scale_rate) for dim in original_image.size)
|
@@ -147,30 +147,35 @@ def add_random_background(image, color):
|
|
147 |
return Image.alpha_composite(background, image)
|
148 |
|
149 |
@spaces.GPU
|
150 |
-
def preprocess_image(input_image, background_choice, foreground_ratio,
|
151 |
"""Preprocess the input image"""
|
152 |
try:
|
153 |
-
#
|
154 |
-
|
|
|
|
|
|
|
|
|
|
|
155 |
|
156 |
-
#
|
157 |
-
|
|
|
158 |
|
159 |
# Process background
|
160 |
-
if background_choice == "Remove
|
161 |
-
|
162 |
elif background_choice == "Custom Background":
|
163 |
-
|
164 |
|
165 |
# Resize content if needed
|
166 |
if foreground_ratio != 1.0:
|
167 |
-
|
168 |
-
np_image = np.array(np_image)
|
169 |
|
170 |
-
return
|
171 |
except Exception as e:
|
172 |
print(f"Error in preprocess_image: {str(e)}")
|
173 |
-
raise e
|
174 |
|
175 |
@spaces.GPU
|
176 |
def gen_image(processed_image, seed, scale, step):
|
@@ -179,26 +184,51 @@ def gen_image(processed_image, seed, scale, step):
|
|
179 |
# Get model and pipeline when needed
|
180 |
model, pipeline = get_model()
|
181 |
|
|
|
|
|
|
|
|
|
182 |
# Convert to numpy array
|
183 |
-
|
|
|
|
|
|
|
184 |
|
185 |
# Set random seed
|
186 |
-
torch.manual_seed(seed)
|
187 |
-
np.random.seed(seed)
|
188 |
|
189 |
# Generate images
|
190 |
np_imgs, np_xyzs = pipeline.generate(
|
191 |
np_image,
|
192 |
-
guidance_scale=scale,
|
193 |
-
num_inference_steps=step
|
194 |
)
|
195 |
|
196 |
# Generate 3D model
|
197 |
glb_path = generate3d(model, np_imgs, np_xyzs, args.device)
|
|
|
198 |
return Image.fromarray(np_imgs), Image.fromarray(np_xyzs), glb_path
|
199 |
except Exception as e:
|
200 |
print(f"Error in gen_image: {str(e)}")
|
201 |
-
raise e
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
202 |
|
203 |
_DESCRIPTION = '''
|
204 |
* Our [official implementation](https://github.com/thu-ml/CRM) uses UV texture instead of vertex color. It has better texture than this online demo.
|
@@ -206,7 +236,8 @@ _DESCRIPTION = '''
|
|
206 |
* If you find the output unsatisfying, try using different seeds:)
|
207 |
'''
|
208 |
|
209 |
-
|
|
|
210 |
gr.Markdown("# CRM: Single Image to 3D Textured Mesh with Convolutional Reconstruction Model")
|
211 |
gr.Markdown(_DESCRIPTION)
|
212 |
|
@@ -215,21 +246,23 @@ with gr.Blocks() as demo:
|
|
215 |
with gr.Row():
|
216 |
image_input = gr.Image(
|
217 |
label="Image input",
|
218 |
-
type="pil"
|
|
|
219 |
)
|
220 |
processed_image = gr.Image(
|
221 |
label="Processed Image",
|
222 |
-
type="pil"
|
|
|
223 |
)
|
224 |
|
225 |
with gr.Row():
|
226 |
with gr.Column():
|
227 |
background_choice = gr.Radio(
|
228 |
-
choices=["Alpha as mask", "Auto Remove background"],
|
229 |
value="Auto Remove background",
|
230 |
label="Background choice"
|
231 |
)
|
232 |
-
|
233 |
label="Background Color",
|
234 |
value="#7F7F7F"
|
235 |
)
|
@@ -261,12 +294,14 @@ with gr.Blocks() as demo:
|
|
261 |
precision=0
|
262 |
)
|
263 |
|
264 |
-
text_button = gr.Button("Generate 3D shape")
|
265 |
|
266 |
-
|
267 |
-
|
268 |
-
|
269 |
-
|
|
|
|
|
270 |
|
271 |
with gr.Column():
|
272 |
image_output = gr.Image(label="Output RGB image")
|
@@ -274,24 +309,20 @@ with gr.Blocks() as demo:
|
|
274 |
output_model = gr.Model3D(label="Output GLB")
|
275 |
gr.Markdown("Note: Ensure that the input image is correctly pre-processed into a grey background, otherwise the results will be unpredictable.")
|
276 |
|
277 |
-
|
278 |
-
if image is None:
|
279 |
-
raise gr.Error("No image uploaded!")
|
280 |
-
processed = preprocess_image(image, bg_choice, fg_ratio, bg_color)
|
281 |
-
return gen_image(processed, seed_val, guidance, steps)
|
282 |
-
|
283 |
text_button.click(
|
284 |
fn=process_and_generate,
|
285 |
inputs=[
|
286 |
image_input,
|
287 |
background_choice,
|
288 |
foreground_ratio,
|
289 |
-
|
290 |
seed,
|
291 |
guidance_scale,
|
292 |
step
|
293 |
],
|
294 |
outputs=[
|
|
|
295 |
image_output,
|
296 |
xyz_output,
|
297 |
output_model
|
@@ -300,4 +331,10 @@ with gr.Blocks() as demo:
|
|
300 |
)
|
301 |
|
302 |
if __name__ == "__main__":
|
303 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Fixed version with proper error handling and compatibility
|
2 |
import spaces
|
3 |
import argparse
|
4 |
import numpy as np
|
|
|
36 |
help="config for stage2",
|
37 |
)
|
38 |
parser.add_argument("--device", type=str, default="cuda")
|
39 |
+
args = parser.parse_args(args=[]) # Fix: provide empty args list
|
40 |
|
41 |
# Download model files
|
42 |
crm_path = hf_hub_download(repo_id="Zhengyi/CRM", filename="CRM.pth")
|
|
|
73 |
# Global variables to store model and pipeline
|
74 |
model = None
|
75 |
pipeline = None
|
76 |
+
args = None
|
77 |
|
78 |
@spaces.GPU
|
79 |
def get_model():
|
|
|
85 |
|
86 |
rembg_session = rembg.new_session()
|
87 |
|
|
|
88 |
def expand_to_square(image, bg_color=(0, 0, 0, 0)):
|
89 |
# expand image to 1:1
|
90 |
width, height = image.size
|
|
|
111 |
do_remove = True
|
112 |
if image.mode == "RGBA" and image.getextrema()[3][0] < 255:
|
113 |
# explain why current do not rm bg
|
114 |
+
print("alpha channel not empty, skip remove background, using alpha channel as mask")
|
115 |
background = Image.new("RGBA", image.size, (0, 0, 0, 0))
|
116 |
image = Image.alpha_composite(background, image)
|
117 |
do_remove = False
|
|
|
121 |
return image
|
122 |
|
123 |
def do_resize_content(original_image: Image, scale_rate):
|
124 |
+
# resize image content while retaining the original image size
|
125 |
if scale_rate != 1:
|
126 |
# Calculate the new size after rescaling
|
127 |
new_size = tuple(int(dim * scale_rate) for dim in original_image.size)
|
|
|
147 |
return Image.alpha_composite(background, image)
|
148 |
|
149 |
@spaces.GPU
|
150 |
+
def preprocess_image(input_image, background_choice, foreground_ratio, back_ground_color):
|
151 |
"""Preprocess the input image"""
|
152 |
try:
|
153 |
+
# Check if image is provided
|
154 |
+
if input_image is None:
|
155 |
+
raise gr.Error("No image uploaded!")
|
156 |
+
|
157 |
+
# Convert to PIL Image if needed
|
158 |
+
if isinstance(input_image, np.ndarray):
|
159 |
+
input_image = Image.fromarray(input_image)
|
160 |
|
161 |
+
# Ensure RGBA mode
|
162 |
+
if input_image.mode != "RGBA":
|
163 |
+
input_image = input_image.convert("RGBA")
|
164 |
|
165 |
# Process background
|
166 |
+
if background_choice == "Auto Remove background":
|
167 |
+
input_image = remove_background(input_image, rembg_session)
|
168 |
elif background_choice == "Custom Background":
|
169 |
+
input_image = add_random_background(input_image, back_ground_color)
|
170 |
|
171 |
# Resize content if needed
|
172 |
if foreground_ratio != 1.0:
|
173 |
+
input_image = do_resize_content(input_image, foreground_ratio)
|
|
|
174 |
|
175 |
+
return input_image
|
176 |
except Exception as e:
|
177 |
print(f"Error in preprocess_image: {str(e)}")
|
178 |
+
raise gr.Error(f"Preprocessing failed: {str(e)}")
|
179 |
|
180 |
@spaces.GPU
|
181 |
def gen_image(processed_image, seed, scale, step):
|
|
|
184 |
# Get model and pipeline when needed
|
185 |
model, pipeline = get_model()
|
186 |
|
187 |
+
# Check if image is provided
|
188 |
+
if processed_image is None:
|
189 |
+
raise gr.Error("No processed image provided!")
|
190 |
+
|
191 |
# Convert to numpy array
|
192 |
+
if isinstance(processed_image, Image.Image):
|
193 |
+
np_image = np.array(processed_image)
|
194 |
+
else:
|
195 |
+
np_image = processed_image
|
196 |
|
197 |
# Set random seed
|
198 |
+
torch.manual_seed(int(seed))
|
199 |
+
np.random.seed(int(seed))
|
200 |
|
201 |
# Generate images
|
202 |
np_imgs, np_xyzs = pipeline.generate(
|
203 |
np_image,
|
204 |
+
guidance_scale=float(scale),
|
205 |
+
num_inference_steps=int(step)
|
206 |
)
|
207 |
|
208 |
# Generate 3D model
|
209 |
glb_path = generate3d(model, np_imgs, np_xyzs, args.device)
|
210 |
+
|
211 |
return Image.fromarray(np_imgs), Image.fromarray(np_xyzs), glb_path
|
212 |
except Exception as e:
|
213 |
print(f"Error in gen_image: {str(e)}")
|
214 |
+
raise gr.Error(f"Generation failed: {str(e)}")
|
215 |
+
|
216 |
+
def process_and_generate(image, bg_choice, fg_ratio, bg_color, seed_val, guidance, steps):
|
217 |
+
"""Combined function to process image and generate 3D model"""
|
218 |
+
try:
|
219 |
+
if image is None:
|
220 |
+
raise gr.Error("No image uploaded!")
|
221 |
+
|
222 |
+
# Preprocess the image
|
223 |
+
processed = preprocess_image(image, bg_choice, fg_ratio, bg_color)
|
224 |
+
|
225 |
+
# Generate 3D model
|
226 |
+
rgb_img, ccm_img, glb_file = gen_image(processed, seed_val, guidance, steps)
|
227 |
+
|
228 |
+
return processed, rgb_img, ccm_img, glb_file
|
229 |
+
except Exception as e:
|
230 |
+
print(f"Error in process_and_generate: {str(e)}")
|
231 |
+
raise gr.Error(f"Process failed: {str(e)}")
|
232 |
|
233 |
_DESCRIPTION = '''
|
234 |
* Our [official implementation](https://github.com/thu-ml/CRM) uses UV texture instead of vertex color. It has better texture than this online demo.
|
|
|
236 |
* If you find the output unsatisfying, try using different seeds:)
|
237 |
'''
|
238 |
|
239 |
+
# Create the Gradio interface
|
240 |
+
with gr.Blocks(title="CRM: Single Image to 3D") as demo:
|
241 |
gr.Markdown("# CRM: Single Image to 3D Textured Mesh with Convolutional Reconstruction Model")
|
242 |
gr.Markdown(_DESCRIPTION)
|
243 |
|
|
|
246 |
with gr.Row():
|
247 |
image_input = gr.Image(
|
248 |
label="Image input",
|
249 |
+
type="pil",
|
250 |
+
height=300
|
251 |
)
|
252 |
processed_image = gr.Image(
|
253 |
label="Processed Image",
|
254 |
+
type="pil",
|
255 |
+
height=300
|
256 |
)
|
257 |
|
258 |
with gr.Row():
|
259 |
with gr.Column():
|
260 |
background_choice = gr.Radio(
|
261 |
+
choices=["Alpha as mask", "Auto Remove background", "Custom Background"],
|
262 |
value="Auto Remove background",
|
263 |
label="Background choice"
|
264 |
)
|
265 |
+
back_ground_color = gr.ColorPicker(
|
266 |
label="Background Color",
|
267 |
value="#7F7F7F"
|
268 |
)
|
|
|
294 |
precision=0
|
295 |
)
|
296 |
|
297 |
+
text_button = gr.Button("Generate 3D shape", variant="primary")
|
298 |
|
299 |
+
# Only add examples if the directory exists
|
300 |
+
if os.path.exists("examples") and os.listdir("examples"):
|
301 |
+
gr.Examples(
|
302 |
+
examples=[[os.path.join("examples", i)] for i in os.listdir("examples") if i.lower().endswith(('.png', '.jpg', '.jpeg'))],
|
303 |
+
inputs=[image_input]
|
304 |
+
)
|
305 |
|
306 |
with gr.Column():
|
307 |
image_output = gr.Image(label="Output RGB image")
|
|
|
309 |
output_model = gr.Model3D(label="Output GLB")
|
310 |
gr.Markdown("Note: Ensure that the input image is correctly pre-processed into a grey background, otherwise the results will be unpredictable.")
|
311 |
|
312 |
+
# Connect the button to the processing function
|
|
|
|
|
|
|
|
|
|
|
313 |
text_button.click(
|
314 |
fn=process_and_generate,
|
315 |
inputs=[
|
316 |
image_input,
|
317 |
background_choice,
|
318 |
foreground_ratio,
|
319 |
+
back_ground_color,
|
320 |
seed,
|
321 |
guidance_scale,
|
322 |
step
|
323 |
],
|
324 |
outputs=[
|
325 |
+
processed_image,
|
326 |
image_output,
|
327 |
xyz_output,
|
328 |
output_model
|
|
|
331 |
)
|
332 |
|
333 |
if __name__ == "__main__":
|
334 |
+
# Launch with proper settings for HF Spaces
|
335 |
+
demo.queue().launch(
|
336 |
+
server_name="0.0.0.0",
|
337 |
+
server_port=7860,
|
338 |
+
share=False, # Don't need share=True on HF Spaces
|
339 |
+
show_api=False # Disable API docs to avoid the error
|
340 |
+
)
|