SaiChamakura commited on
Commit
092b806
·
verified ·
1 Parent(s): cf753b4

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +211 -0
app.py ADDED
@@ -0,0 +1,211 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import os
3
+ import torch
4
+ import cadquery as cq
5
+ from transformers import AutoModelForCausalLM, AutoProcessor, AutoConfig
6
+ from PIL import Image
7
+ import ast # For safe evaluation of string-formatted lists
8
+ from io import BytesIO
9
+
10
+ # --- CONFIGURATION (Keep as constants) ---
11
+ MODEL_PATH = "/raid/home/posahemanth/miniconda3/Sai/FinalYearProject/1000_gpusoutput"
12
+ OUTPUT_DIRECTORY = "/raid/home/posahemanth/miniconda3/Sai/FinalYearProject/Gradio_Output" # Separate output
13
+ USE_FLASH_ATTENTION = True
14
+ PRE_TRAINED_MODEL_NAME = "microsoft/Phi-4-multimodal-instruct"
15
+ os.makedirs(OUTPUT_DIRECTORY, exist_ok=True) # Ensure the output directory exists
16
+
17
+
18
+ # --- MODEL LOADING (Global Scope) ---
19
+ # Load only once, outside the functions, to improve performance
20
+ try:
21
+ config = AutoConfig.from_pretrained(MODEL_PATH, trust_remote_code=True, local_files_only=True)
22
+ config.attn_implementation = "flash_attention_2" if USE_FLASH_ATTENTION else "sdpa"
23
+ config.num_logits_to_keep = 20
24
+
25
+ model = AutoModelForCausalLM.from_pretrained(
26
+ MODEL_PATH,
27
+ config=config,
28
+ trust_remote_code=True,
29
+ torch_dtype=torch.bfloat16 if USE_FLASH_ATTENTION else torch.float32,
30
+ local_files_only=True
31
+ ).to("cuda").eval() # .eval() is crucial for inference
32
+
33
+ processor = AutoProcessor.from_pretrained(
34
+ PRE_TRAINED_MODEL_NAME,
35
+ trust_remote_code=True,
36
+ local_files_only=False,
37
+ config=config,
38
+ )
39
+ except Exception as e:
40
+ print(f"Error loading model/processor: {e}")
41
+ raise # Re-raise to halt execution
42
+
43
+
44
+ # --- CAPTION GENERATION ---
45
+ def generate_caption(image):
46
+ """Generates a caption for the given image."""
47
+ if image is None:
48
+ return "Please upload an image."
49
+
50
+ try:
51
+ # Convert numpy array to PIL Image
52
+ image = Image.fromarray(image).convert("RGB")
53
+ except Exception as e:
54
+ print(f"Error converting image: {e}")
55
+ return "Error processing image."
56
+
57
+ prompt = "Describe this image."
58
+ user_message = {'role': 'user', 'content': f'<|image_1|>{prompt}'}
59
+ prompt_tokenized = processor.tokenizer.apply_chat_template([user_message], tokenize=False, add_generation_prompt=True)
60
+ inputs = processor(prompt_tokenized, images=[image], return_tensors='pt').to("cuda")
61
+
62
+ try:
63
+ with torch.no_grad(): # Ensure no gradients are calculated
64
+ generated_ids = model.generate(
65
+ **inputs,
66
+ eos_token_id=processor.tokenizer.eos_token_id,
67
+ max_new_tokens=512,
68
+ num_logits_to_keep=20,
69
+ )
70
+
71
+ input_len = inputs.input_ids.size(1)
72
+ generated_text = processor.decode(
73
+ generated_ids[0, input_len:],
74
+ skip_special_tokens=True,
75
+ clean_up_tokenization_spaces=False,
76
+ ).strip()
77
+ except Exception as e:
78
+ print(f"Error during generation: {e}")
79
+ return "Error during caption generation."
80
+
81
+ return generated_text
82
+
83
+ # --- CAD MODEL BUILDING ---
84
+ def build_model(sequence):
85
+ """Builds a CAD model from the sequence and returns the STEP file path."""
86
+ workplane = cq.Workplane("XY")
87
+ model = None
88
+ primitive = None
89
+
90
+ if isinstance(sequence, str):
91
+ try:
92
+ sequence = ast.literal_eval(sequence)
93
+ except (ValueError, SyntaxError):
94
+ return "Invalid sequence format. Could not convert to list."
95
+ if not isinstance(sequence, list):
96
+ return "Invalid sequence format. Expected a list."
97
+ elif not isinstance(sequence, list):
98
+ return "Invalid sequence format. Expected a list."
99
+
100
+
101
+ for step in sequence:
102
+ index = step[0]
103
+ if index == 0: # Cube
104
+ _, length, width, height, loc_x, loc_y, loc_z, axis = step
105
+ primitive = workplane.box(length, width, height).translate((loc_x, loc_y, loc_z))
106
+ elif index == 1: # Cylinder
107
+ _, height, radius, loc_x, loc_y, loc_z, axis = step
108
+ primitive = workplane.cylinder(radius, height).translate((loc_x, loc_y, loc_z))
109
+ elif index == 2: # Sphere
110
+ _, radius, loc_x, loc_y, loc_z, axis = step
111
+ primitive = workplane.sphere(radius).translate((loc_x, loc_y, loc_z))
112
+
113
+ if primitive is None:
114
+ print(f"Skipping step {step} because primitive was not initialized.")
115
+ continue
116
+
117
+ if index in [3, 4, 5]: # Operations
118
+ if model is None:
119
+ model = primitive
120
+ _, loc_x, loc_y, loc_z = step
121
+ if index == 3:
122
+ model = model.union(primitive.translate((loc_x, loc_y, loc_z)))
123
+ elif index == 4:
124
+ model = model.cut(primitive.translate((loc_x, loc_y, loc_z)))
125
+ elif index == 5:
126
+ model = model.intersect(primitive.translate((loc_x, loc_y, loc_z)))
127
+
128
+ if model is None:
129
+ model = primitive
130
+
131
+ if model is None:
132
+ return "Error: No valid CAD model was created."
133
+
134
+ # Create a unique filename using a timestamp (more robust)
135
+ import datetime
136
+ timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
137
+ model_name = f"generated_model_{timestamp}"
138
+ step_file_path = os.path.join(OUTPUT_DIRECTORY, f"{model_name}.step")
139
+ cq.exporters.export(model, step_file_path)
140
+ return step_file_path
141
+
142
+
143
+
144
+
145
+ def process_image(image):
146
+ """Combines caption generation and model building."""
147
+ if image is None:
148
+ return "Please upload an image first.", None
149
+
150
+ caption = generate_caption(image)
151
+ if not caption or caption.startswith("Error"):
152
+ return caption, None
153
+
154
+ step_file_path = build_model(caption)
155
+ if step_file_path.startswith("Error"):
156
+ return step_file_path, None
157
+
158
+ return "CAD model generated successfully!", step_file_path
159
+
160
+
161
+
162
+ # --- GRADIO INTERFACE ---
163
+
164
+ css = """
165
+ .container {
166
+ max-width: 800px;
167
+ margin: auto;
168
+ padding: 20px;
169
+ border: 2px solid #ddd;
170
+ border-radius: 10px;
171
+ }
172
+ h1 {
173
+ text-align: center;
174
+ color: #333;
175
+ }
176
+ .description {
177
+ text-align: center;
178
+ margin-bottom: 20px;
179
+ }
180
+ .input-section, .output-section {
181
+ margin-bottom: 20px;
182
+ padding: 10px;
183
+ border: 1px solid #ccc;
184
+ border-radius: 5px;
185
+ }
186
+ .input-section h2, .output-section h2 {
187
+ margin-top: 0;
188
+ color: #555;
189
+ }
190
+ .output-section p {
191
+ font-weight: bold;
192
+ }
193
+
194
+ """
195
+
196
+ iface = gr.Interface(
197
+ fn=process_image,
198
+ inputs=gr.Image(label="Upload Image", type="numpy"),
199
+ outputs=[
200
+ gr.Textbox(label="Status"), # Show status messages
201
+ gr.File(label="Download STEP File") # Download link for the file
202
+ ],
203
+ title="Image to CAD Converter",
204
+ description="Upload an image of a mechanical drawing, and this app will attempt to generate a corresponding STEP CAD file.",
205
+ css=css, # Apply the CSS
206
+ allow_flagging="never", # Disable flagging
207
+ theme=gr.themes.Soft()
208
+
209
+ )
210
+
211
+ iface.launch(share=True)