ArrcttacsrjksX commited on
Commit
73b24eb
·
verified ·
1 Parent(s): acca59f

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +246 -114
app.py CHANGED
@@ -3,135 +3,267 @@ import subprocess
3
  import os
4
  import tempfile
5
  import datetime
 
6
  from huggingface_hub import upload_file
 
 
 
 
7
 
8
- def convert_image_to_dxf(image_file, output_folder=None, use_lines=False):
9
- try:
10
- # Validate input image
11
- if image_file is None:
12
- return "No image provided", None, None
13
-
14
- # Define output folder, using a temp directory if none is specified
15
- if not output_folder:
16
- output_folder = tempfile.mkdtemp()
17
- else:
18
- os.makedirs(output_folder, exist_ok=True)
19
-
20
- # Generate the date-based output file name
21
- current_date = datetime.datetime.now().strftime("%Y-%m-%d_%H-%M-%S")
22
- image_path = image_file
23
- output_path = os.path.join(output_folder, f"{current_date}_output.dxf")
24
- debug_output_path = os.path.join(output_folder, f"{current_date}_debug.png")
25
- output_pathsimple = os.path.join(output_folder, "_output.dxf")
26
- debug_output_pathsimple = os.path.join(output_folder, "_debug.png")
27
- # Prepare the command arguments
28
- command_args = ["./SimpleImageToDxfHavePass"]
29
-
30
- # Add --use-lines if the checkbox is checked
31
- if use_lines:
32
- command_args.append("--use-lines")
33
-
34
- # Add other arguments
35
- command_args.extend([f"--imagePath={image_path}", f"--outputPath={output_pathsimple}", f"--debug-output={debug_output_pathsimple}"])
36
-
37
- # Execute conversion command
38
- result = subprocess.run(
39
- command_args,
40
- check=True,
41
- capture_output=True
42
- )
43
-
44
- # Log stdout for debugging
45
- print(result.stdout.decode('utf-8'))
46
-
47
- # Check if conversion was successful
48
- if not os.path.exists(output_path):
49
- return "Conversion failed: DXF file was not created.", None, None
50
-
51
- # Prepare folder structure for upload
52
- date_folder = f"{current_date}"
53
-
54
- # Hugging Face token
55
- hf_token = os.getenv("HF_TOKEN")
56
- if not hf_token:
57
- return "Hugging Face token not found", None, None
58
 
59
- # Upload input image, output DXF, and optionally debug image to Hugging Face in a date-based folder
60
- uploaded_files = []
 
61
 
62
- # Upload input image
63
- uploaded_input = upload_file(
64
- path_or_fileobj=image_path,
65
- path_in_repo=f"datasets/ArrcttacsrjksX/ImageToAutocadData/{date_folder}/{os.path.basename(image_path)}",
66
- repo_id="ArrcttacsrjksX/ImageToAutocadData",
67
- token=hf_token
68
- )
69
- uploaded_files.append(uploaded_input)
70
-
71
- # Upload DXF output
72
- uploaded_dxf = upload_file(
73
- path_or_fileobj=output_path,
74
- path_in_repo=f"datasets/ArrcttacsrjksX/ImageToAutocadData/{date_folder}/{os.path.basename(output_path)}",
75
- repo_id="ArrcttacsrjksX/ImageToAutocadData",
76
- token=hf_token
77
- )
78
- uploaded_files.append(uploaded_dxf)
79
-
80
- # If the checkbox is ticked, upload debug image
81
- if use_lines:
82
- uploaded_debug = upload_file(
83
- path_or_fileobj=debug_output_path,
84
- path_in_repo=f"datasets/ArrcttacsrjksX/ImageToAutocadData/{date_folder}/{os.path.basename(debug_output_path)}",
85
- repo_id="ArrcttacsrjksX/ImageToAutocadData",
86
- token=hf_token
 
 
87
  )
88
- uploaded_files.append(uploaded_debug)
 
 
 
 
 
89
 
90
- # Return files directly for download in Gradio interface
91
- return output_path, debug_output_path if use_lines else None, uploaded_files
92
-
93
- except subprocess.CalledProcessError as e:
94
- error_msg = f"Error converting image to DXF: {e.stderr.decode('utf-8') if e.stderr else e}"
95
- return error_msg, None, None
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
96
 
97
- def main():
98
- with gr.Blocks() as demo:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
99
  with gr.Tabs():
100
- # Tab for conversion
101
  with gr.TabItem("Image to DXF"):
102
- gr.Markdown("# SimpleImageToDxfHavePass")
 
 
 
103
 
104
- # Input row for image and optional output folder
105
  with gr.Row():
106
- image_input = gr.Image(type="filepath", label="Input Image (PNG/JPG)")
107
- output_folder = gr.Textbox(label="Output Folder (optional)", placeholder="Leave blank to use a temporary folder")
108
-
109
- # Checkbox to decide whether to use --use-lines
110
- use_lines_checkbox = gr.Checkbox(label="Use --use-lines for conversion", value=False) # Default is False
111
-
112
- # Outputs: Debug image and DXF file download link
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
113
  with gr.Row():
114
- debug_output = gr.Image(type="filepath", label="Debug Output Preview")
115
- dxf_output = gr.File(label="DXF File Download")
 
 
 
 
 
 
 
 
 
116
 
117
- # Conversion button with event binding
118
- convert_btn = gr.Button("Convert to DXF")
119
- convert_btn.click(
120
- convert_image_to_dxf,
121
- inputs=[image_input, output_folder, use_lines_checkbox],
122
- outputs=[dxf_output, debug_output, dxf_output]
123
  )
124
-
125
- # About tab
126
  with gr.TabItem("About"):
127
- gr.Markdown("This Gradio app allows users to convert an image to a DXF file using the SimpleImageToDxfHavePass command-line tool.")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
128
 
129
- demo.launch(share=True)
 
 
 
 
 
 
130
 
131
- if __name__ == "__main__":
 
 
 
132
  try:
133
- subprocess.run(['chmod', '+x', './SimpleImageToDxfHavePass'], check=True)
134
- except subprocess.CalledProcessError as e:
135
- print(f"Error setting permissions: {e}")
136
- exit(1)
137
- main()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3
  import os
4
  import tempfile
5
  import datetime
6
+ from pathlib import Path
7
  from huggingface_hub import upload_file
8
+ from typing import Optional, Tuple, List, Union
9
+ import logging
10
+ from functools import partial
11
+ import shutil
12
 
13
+ # Configure logging
14
+ logging.basicConfig(
15
+ level=logging.INFO,
16
+ format='%(asctime)s - %(levelname)s - %(message)s'
17
+ )
18
+ logger = logging.getLogger(__name__)
19
+
20
+ class ImageToDxfConverter:
21
+ def __init__(self, executable_path: str = "./SimpleImageToDxfHavePass"):
22
+ """Initialize the converter with configuration."""
23
+ self.executable_path = Path(executable_path)
24
+ self.hf_token = os.getenv("HF_TOKEN")
25
+ self.repo_id = "ArrcttacsrjksX/ImageToAutocadData"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
26
 
27
+ # Validate executable exists
28
+ if not self.executable_path.exists():
29
+ raise FileNotFoundError(f"Executable not found: {executable_path}")
30
 
31
+ # Validate HF token
32
+ if not self.hf_token:
33
+ logger.warning("HF_TOKEN environment variable not set")
34
+
35
+ def _ensure_directory(self, path: Union[str, Path]) -> Path:
36
+ """Ensure directory exists and return Path object."""
37
+ path = Path(path)
38
+ path.mkdir(parents=True, exist_ok=True)
39
+ return path
40
+
41
+ def _generate_output_paths(self, output_folder: Path, timestamp: str) -> dict:
42
+ """Generate all required output paths."""
43
+ return {
44
+ 'output_dxf': output_folder / f"{timestamp}_output.dxf",
45
+ 'debug_png': output_folder / f"{timestamp}_debug.png",
46
+ 'temp_dxf': output_folder / "_output.dxf",
47
+ 'temp_debug': output_folder / "_debug.png"
48
+ }
49
+
50
+ def _execute_conversion(self, command_args: List[str]) -> subprocess.CompletedProcess:
51
+ """Execute the conversion command with proper error handling."""
52
+ try:
53
+ result = subprocess.run(
54
+ command_args,
55
+ check=True,
56
+ capture_output=True,
57
+ text=True
58
  )
59
+ logger.info(f"Conversion output: {result.stdout}")
60
+ return result
61
+ except subprocess.CalledProcessError as e:
62
+ error_msg = f"Conversion failed: {e.stderr}"
63
+ logger.error(error_msg)
64
+ raise RuntimeError(error_msg)
65
 
66
+ def _upload_to_huggingface(self,
67
+ file_path: Path,
68
+ date_folder: str,
69
+ filename: str) -> Optional[str]:
70
+ """Upload a file to Hugging Face with error handling."""
71
+ if not self.hf_token:
72
+ return None
73
+
74
+ try:
75
+ repo_path = f"datasets/{self.repo_id}/{date_folder}/{filename}"
76
+ uploaded_file = upload_file(
77
+ path_or_fileobj=str(file_path),
78
+ path_in_repo=repo_path,
79
+ repo_id=self.repo_id,
80
+ token=self.hf_token
81
+ )
82
+ logger.info(f"Successfully uploaded {filename}")
83
+ return uploaded_file
84
+ except Exception as e:
85
+ logger.error(f"Failed to upload {filename}: {str(e)}")
86
+ return None
87
 
88
+ def convert_image(self,
89
+ image_path: Optional[str],
90
+ output_folder: Optional[str] = None,
91
+ use_lines: bool = False) -> Tuple[Optional[str], Optional[str], List[str]]:
92
+ """
93
+ Convert image to DXF format with improved error handling and logging.
94
+ """
95
+ try:
96
+ # Input validation
97
+ if not image_path:
98
+ raise ValueError("No image provided")
99
+
100
+ # Setup output directory
101
+ output_dir = self._ensure_directory(output_folder if output_folder else tempfile.mkdtemp())
102
+ timestamp = datetime.datetime.now().strftime("%Y-%m-%d_%H-%M-%S")
103
+ paths = self._generate_output_paths(output_dir, timestamp)
104
+
105
+ # Prepare conversion command
106
+ command_args = [
107
+ str(self.executable_path),
108
+ f"--imagePath={image_path}",
109
+ f"--outputPath={paths['temp_dxf']}",
110
+ f"--debug-output={paths['temp_debug']}"
111
+ ]
112
+ if use_lines:
113
+ command_args.append("--use-lines")
114
+
115
+ # Execute conversion
116
+ self._execute_conversion(command_args)
117
+
118
+ # Move temporary files to final locations
119
+ shutil.move(paths['temp_dxf'], paths['output_dxf'])
120
+ if use_lines:
121
+ shutil.move(paths['temp_debug'], paths['debug_png'])
122
+
123
+ # Upload files to Hugging Face
124
+ uploaded_files = []
125
+ date_folder = timestamp
126
+
127
+ # Upload input image
128
+ if uploaded_input := self._upload_to_huggingface(
129
+ Path(image_path),
130
+ date_folder,
131
+ Path(image_path).name
132
+ ):
133
+ uploaded_files.append(uploaded_input)
134
+
135
+ # Upload DXF output
136
+ if uploaded_dxf := self._upload_to_huggingface(
137
+ paths['output_dxf'],
138
+ date_folder,
139
+ paths['output_dxf'].name
140
+ ):
141
+ uploaded_files.append(uploaded_dxf)
142
+
143
+ # Upload debug image if available
144
+ if use_lines and (uploaded_debug := self._upload_to_huggingface(
145
+ paths['debug_png'],
146
+ date_folder,
147
+ paths['debug_png'].name
148
+ )):
149
+ uploaded_files.append(uploaded_debug)
150
+
151
+ return (
152
+ str(paths['output_dxf']),
153
+ str(paths['debug_png']) if use_lines else None,
154
+ uploaded_files
155
+ )
156
+
157
+ except Exception as e:
158
+ logger.error(f"Conversion failed: {str(e)}")
159
+ return None, None, []
160
+
161
+ def create_gradio_interface():
162
+ """Create and configure the Gradio interface."""
163
+ converter = ImageToDxfConverter()
164
+
165
+ with gr.Blocks(theme=gr.themes.Soft()) as demo:
166
  with gr.Tabs():
 
167
  with gr.TabItem("Image to DXF"):
168
+ gr.Markdown("""
169
+ # Image to DXF Converter
170
+ Convert your images to DXF format for CAD software.
171
+ """)
172
 
 
173
  with gr.Row():
174
+ with gr.Column(scale=2):
175
+ image_input = gr.Image(
176
+ type="filepath",
177
+ label="Input Image",
178
+ elem_id="image_input"
179
+ )
180
+ with gr.Column(scale=1):
181
+ output_folder = gr.Textbox(
182
+ label="Output Folder (optional)",
183
+ placeholder="Leave blank for temporary folder",
184
+ elem_id="output_folder"
185
+ )
186
+ use_lines_checkbox = gr.Checkbox(
187
+ label="Enable line detection",
188
+ value=False,
189
+ elem_id="use_lines"
190
+ )
191
+ convert_btn = gr.Button(
192
+ "Convert to DXF",
193
+ variant="primary",
194
+ elem_id="convert_btn"
195
+ )
196
+
197
  with gr.Row():
198
+ with gr.Column():
199
+ dxf_output = gr.File(
200
+ label="DXF Output",
201
+ elem_id="dxf_output"
202
+ )
203
+ with gr.Column():
204
+ debug_output = gr.Image(
205
+ type="filepath",
206
+ label="Debug Preview",
207
+ elem_id="debug_output"
208
+ )
209
 
210
+ # Add error message display
211
+ error_output = gr.Textbox(
212
+ label="Status",
213
+ interactive=False,
214
+ visible=False,
215
+ elem_id="error_output"
216
  )
217
+
 
218
  with gr.TabItem("About"):
219
+ gr.Markdown("""
220
+ ## Image to DXF Converter
221
+
222
+ This application converts images to DXF format using the SimpleImageToDxfHavePass tool.
223
+
224
+ ### Features:
225
+ - Supports PNG and JPG input formats
226
+ - Optional line detection mode
227
+ - Debug preview output
228
+ - Automatic file organization
229
+ - Hugging Face integration for result sharing
230
+
231
+ ### Usage:
232
+ 1. Upload an image
233
+ 2. (Optional) Specify output folder
234
+ 3. Toggle line detection if needed
235
+ 4. Click Convert
236
+ """)
237
 
238
+ # Setup event handlers
239
+ convert_btn.click(
240
+ fn=converter.convert_image,
241
+ inputs=[image_input, output_folder, use_lines_checkbox],
242
+ outputs=[dxf_output, debug_output, error_output],
243
+ api_name="convert"
244
+ )
245
 
246
+ return demo
247
+
248
+ def main():
249
+ """Main entry point with proper error handling."""
250
  try:
251
+ # Ensure executable permissions
252
+ executable = Path("./SimpleImageToDxfHavePass")
253
+ if executable.exists():
254
+ executable.chmod(0o755)
255
+
256
+ # Create and launch the interface
257
+ demo = create_gradio_interface()
258
+ demo.launch(
259
+ share=True,
260
+ server_name="0.0.0.0",
261
+ server_port=7860,
262
+ show_error=True
263
+ )
264
+ except Exception as e:
265
+ logger.critical(f"Application failed to start: {str(e)}")
266
+ raise
267
+
268
+ if __name__ == "__main__":
269
+ main()