dschandra commited on
Commit
65ba25a
·
verified ·
1 Parent(s): 8219ccf

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +56 -54
app.py CHANGED
@@ -6,7 +6,7 @@ import os
6
  import tempfile
7
  import logging
8
 
9
- # Set up logging to debug.log
10
  logging.basicConfig(
11
  filename='debug.log',
12
  level=logging.DEBUG,
@@ -25,6 +25,7 @@ def validate_poppler_path(poppler_path):
25
  """
26
  if not poppler_path:
27
  return None
 
28
  if not os.path.isdir(poppler_path):
29
  logger.error("Invalid poppler_path: %s is not a directory", poppler_path)
30
  return None
@@ -38,23 +39,21 @@ def validate_poppler_path(poppler_path):
38
  # Function to calculate materials based on blueprint dimensions
39
  def calculate_materials_from_dimensions(wall_area, foundation_area):
40
  """
41
- Calculate required materials (cement, bricks, steel) based on wall and foundation areas.
42
  Args:
43
  wall_area (float): Wall area in square meters.
44
  foundation_area (float): Foundation area in square meters.
45
  Returns:
46
  dict: Material quantities (cement in kg, bricks in units, steel in kg).
47
- Raises:
48
- ValueError: If areas are negative or invalid.
49
  """
50
  logger.debug(f"Calculating materials for wall_area={wall_area}, foundation_area={foundation_area}")
51
 
52
  # Validate inputs
53
  if not isinstance(wall_area, (int, float)) or not isinstance(foundation_area, (int, float)):
54
- logger.error("Invalid area type: wall_area and foundation_area must be numeric")
55
  raise ValueError("Wall and foundation areas must be numeric")
56
  if wall_area < 0 or foundation_area < 0:
57
- logger.error("Negative areas provided: wall_area=%s, foundation_area=%s", wall_area, foundation_area)
58
  raise ValueError("Wall and foundation areas must be non-negative")
59
 
60
  materials = {
@@ -65,29 +64,29 @@ def calculate_materials_from_dimensions(wall_area, foundation_area):
65
 
66
  # Wall calculations (in m²)
67
  if wall_area > 0:
68
- materials['cement'] += wall_area * 10 # 10 kg cement per m² for walls
69
- materials['bricks'] += wall_area * 500 # 500 bricks per m² for walls
70
- materials['steel'] += wall_area * 2 # 2 kg steel per m² for walls
71
  logger.debug("Wall materials: cement=%s kg, bricks=%s, steel=%s kg",
72
  materials['cement'], materials['bricks'], materials['steel'])
73
 
74
  # Foundation calculations (in m²)
75
  if foundation_area > 0:
76
- materials['cement'] += foundation_area * 20 # 20 kg cement per m² for foundation
77
- materials['bricks'] += foundation_area * 750 # 750 bricks per m² for foundation
78
- materials['steel'] += foundation_area * 5 # 5 kg steel per m² for foundation
79
- logger.debug("Foundation materials added: cement=%s kg, bricks=%s, steel=%s kg",
80
  materials['cement'], materials['bricks'], materials['steel'])
81
 
82
  logger.info("Material calculation complete: %s", materials)
83
  return materials
84
 
85
- # Function to process the blueprint from a PDF
86
  def process_blueprint(pdf_path, blueprint_width_m=27, blueprint_height_m=9.78, poppler_path=None):
87
  """
88
- Process a PDF blueprint to estimate construction materials.
89
  Args:
90
- pdf_path (str): Path to the PDF file.
91
  blueprint_width_m (float): Blueprint width in meters (default: 27).
92
  blueprint_height_m (float): Blueprint height in meters (default: 9.78).
93
  poppler_path (str, optional): Path to poppler binaries.
@@ -96,15 +95,17 @@ def process_blueprint(pdf_path, blueprint_width_m=27, blueprint_height_m=9.78, p
96
  """
97
  logger.info("Starting blueprint processing for PDF: %s", pdf_path)
98
 
99
- # Validate inputs
100
  if not os.path.exists(pdf_path):
101
  logger.error("PDF file does not exist: %s", pdf_path)
102
  return {"error": f"PDF file not found: {pdf_path}"}
 
 
103
  if not isinstance(blueprint_width_m, (int, float)) or not isinstance(blueprint_height_m, (int, float)):
104
- logger.error("Invalid blueprint dimensions: width=%s, height=%s", blueprint_width_m, blueprint_height_m)
105
  return {"error": "Blueprint width and height must be numeric"}
106
  if blueprint_width_m <= 0 or blueprint_height_m <= 0:
107
- logger.error("Invalid blueprint dimensions: width=%s, height=%s", blueprint_width_m, blueprint_height_m)
108
  return {"error": "Blueprint width and height must be positive"}
109
 
110
  # Validate poppler_path
@@ -112,8 +113,8 @@ def process_blueprint(pdf_path, blueprint_width_m=27, blueprint_height_m=9.78, p
112
  logger.debug("Using poppler_path: %s", poppler_path)
113
 
114
  try:
115
- # Convert PDF to images
116
- logger.debug("Converting PDF to image with poppler_path=%s", poppler_path)
117
  images = convert_from_path(
118
  pdf_path,
119
  first_page=1,
@@ -121,45 +122,45 @@ def process_blueprint(pdf_path, blueprint_width_m=27, blueprint_height_m=9.78, p
121
  poppler_path=poppler_path
122
  )
123
  if not images:
124
- logger.error("No images extracted from PDF")
125
- return {"error": "No images extracted from the PDF"}
126
 
127
- logger.info("Successfully extracted %d image(s) from PDF", len(images))
128
 
129
- # Save the first page as a temporary image
130
  with tempfile.NamedTemporaryFile(suffix=".png", delete=False) as temp_file:
131
  images[0].save(temp_file.name, 'PNG')
132
  temp_image_path = temp_file.name
133
  logger.debug("Saved temporary image to: %s", temp_image_path)
134
 
135
- # Verify temporary file exists
136
  if not os.path.exists(temp_image_path):
137
  logger.error("Temporary image file not created: %s", temp_image_path)
138
  return {"error": "Failed to create temporary image file"}
139
 
140
- # Open the image with OpenCV
141
  image = cv2.imread(temp_image_path)
142
  if image is None:
143
- logger.error("Failed to load image from: %s", temp_image_path)
144
- return {"error": "Could not load the image from PDF"}
145
 
146
  logger.info("Loaded image with dimensions: %s", image.shape)
147
 
148
  # Clean up temporary file
149
  os.unlink(temp_image_path)
150
- logger.debug("Deleted temporary image file: %s", temp_image_path)
151
 
152
- # Convert to grayscale for easier processing
153
  gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
154
  logger.debug("Converted image to grayscale")
155
 
156
- # Apply edge detection to find lines (walls)
157
  edges = cv2.Canny(gray, 50, 150, apertureSize=3)
158
  logger.debug("Applied Canny edge detection")
159
 
160
- # Use Hough Transform to detect lines (walls)
161
  lines = cv2.HoughLinesP(edges, 1, np.pi / 180, threshold=100, minLineLength=50, maxLineGap=10)
162
- logger.debug("Detected %s lines with Hough Transform", len(lines) if lines is not None else 0)
163
 
164
  # Calculate total wall length (in pixels)
165
  total_wall_length_pixels = 0
@@ -170,9 +171,9 @@ def process_blueprint(pdf_path, blueprint_width_m=27, blueprint_height_m=9.78, p
170
  total_wall_length_pixels += length
171
  logger.debug("Line from (%d,%d) to (%d,%d), length=%f pixels", x1, y1, x2, y2, length)
172
  else:
173
- logger.warning("No lines detected in the blueprint")
174
 
175
- logger.info("Total wall length in pixels: %f", total_wall_length_pixels)
176
 
177
  # Get image dimensions
178
  image_height, image_width = image.shape[:2]
@@ -187,44 +188,45 @@ def process_blueprint(pdf_path, blueprint_width_m=27, blueprint_height_m=9.78, p
187
 
188
  # Convert wall length to meters
189
  total_wall_length_m = total_wall_length_pixels * pixel_to_meter
190
- logger.info("Total wall length in meters: %f", total_wall_length_m)
191
 
192
- # Estimate wall area (assume wall height of 3 m)
193
- wall_height_m = 3 # Hardcoded assumption; may need adjustment
194
  wall_area = total_wall_length_m * wall_height_m
195
- logger.info("Wall area: %f m² (wall length=%f m, height=%f m)",
196
  wall_area, total_wall_length_m, wall_height_m)
197
 
198
- # Estimate foundation area (10% of total blueprint area)
199
  total_area = blueprint_width_m * blueprint_height_m
200
- foundation_area = total_area * 0.1 # Hardcoded assumption
201
  logger.info("Total area: %f m², Foundation area: %f m²", total_area, foundation_area)
202
 
203
  # Calculate materials
204
  materials = calculate_materials_from_dimensions(wall_area, foundation_area)
205
- logger.info("Raw material estimates: %s", materials)
206
 
207
- # Format the output
208
  formatted_materials = {
209
  "cement": f"{materials['cement']:.2f} kg",
210
  "bricks": f"{materials['bricks']:.0f} units",
211
  "steel": f"{materials['steel']:.2f} kg"
212
  }
213
- logger.info("Formatted material estimates: %s", formatted_materials)
214
 
215
  return formatted_materials
216
 
217
  except Exception as e:
218
- # Customize error message for poppler-related issues
219
  error_msg = str(e)
220
- if "Unable to get page count" in error_msg:
221
  error_msg = (
222
- "Poppler is not installed or not in PATH. "
223
- "Please install poppler-utils:\n"
224
- "- Windows: Download from https://github.com/oschwartz10612/poppler-windows and add to PATH.\n"
225
  "- Linux: Run 'sudo apt-get install poppler-utils'.\n"
226
  "- Mac: Run 'brew install poppler'.\n"
227
- "Alternatively, specify a valid poppler_path in the input (e.g., C:/poppler/bin)."
 
228
  )
229
  logger.error("Processing failed: %s", error_msg)
230
  return {"error": f"Failed to process PDF: {error_msg}"}
@@ -233,7 +235,7 @@ def process_blueprint(pdf_path, blueprint_width_m=27, blueprint_height_m=9.78, p
233
  interface = gr.Interface(
234
  fn=process_blueprint,
235
  inputs=[
236
- gr.File(file_types=[".pdf"], label="Upload Blueprint PDF"),
237
  gr.Number(label="Blueprint Width (meters)", value=27),
238
  gr.Number(label="Blueprint Height (meters)", value=9.78),
239
  gr.Textbox(label="Poppler Path (optional)", placeholder="e.g., C:/poppler/bin")
@@ -241,8 +243,8 @@ interface = gr.Interface(
241
  outputs=gr.JSON(label="Material Estimates"),
242
  title="Blueprint Material Estimator",
243
  description=(
244
- "Upload a PDF containing a blueprint to estimate construction materials. "
245
- "Specify blueprint dimensions and provide the path to poppler binaries if not in PATH."
246
  )
247
  )
248
 
 
6
  import tempfile
7
  import logging
8
 
9
+ # Set up logging to debug.log for debugging without affecting output
10
  logging.basicConfig(
11
  filename='debug.log',
12
  level=logging.DEBUG,
 
25
  """
26
  if not poppler_path:
27
  return None
28
+ poppler_path = poppler_path.replace('\\', '/') # Normalize path
29
  if not os.path.isdir(poppler_path):
30
  logger.error("Invalid poppler_path: %s is not a directory", poppler_path)
31
  return None
 
39
  # Function to calculate materials based on blueprint dimensions
40
  def calculate_materials_from_dimensions(wall_area, foundation_area):
41
  """
42
+ Calculate required materials based on wall and foundation areas.
43
  Args:
44
  wall_area (float): Wall area in square meters.
45
  foundation_area (float): Foundation area in square meters.
46
  Returns:
47
  dict: Material quantities (cement in kg, bricks in units, steel in kg).
 
 
48
  """
49
  logger.debug(f"Calculating materials for wall_area={wall_area}, foundation_area={foundation_area}")
50
 
51
  # Validate inputs
52
  if not isinstance(wall_area, (int, float)) or not isinstance(foundation_area, (int, float)):
53
+ logger.error("Invalid area type: wall_area=%s, foundation_area=%s", wall_area, foundation_area)
54
  raise ValueError("Wall and foundation areas must be numeric")
55
  if wall_area < 0 or foundation_area < 0:
56
+ logger.error("Negative areas: wall_area=%s, foundation_area=%s", wall_area, foundation_area)
57
  raise ValueError("Wall and foundation areas must be non-negative")
58
 
59
  materials = {
 
64
 
65
  # Wall calculations (in m²)
66
  if wall_area > 0:
67
+ materials['cement'] += wall_area * 10 # 10 kg cement per m²
68
+ materials['bricks'] += wall_area * 500 # 500 bricks per m²
69
+ materials['steel'] += wall_area * 2 # 2 kg steel per m²
70
  logger.debug("Wall materials: cement=%s kg, bricks=%s, steel=%s kg",
71
  materials['cement'], materials['bricks'], materials['steel'])
72
 
73
  # Foundation calculations (in m²)
74
  if foundation_area > 0:
75
+ materials['cement'] += foundation_area * 20 # 20 kg cement per m²
76
+ materials['bricks'] += foundation_area * 750 # 750 bricks per m²
77
+ materials['steel'] += foundation_area * 5 # 5 kg steel per m²
78
+ logger.debug("Foundation materials: cement=%s kg, bricks=%s, steel=%s kg",
79
  materials['cement'], materials['bricks'], materials['steel'])
80
 
81
  logger.info("Material calculation complete: %s", materials)
82
  return materials
83
 
84
+ # Function to process the blueprint from a single-page PDF
85
  def process_blueprint(pdf_path, blueprint_width_m=27, blueprint_height_m=9.78, poppler_path=None):
86
  """
87
+ Process a single-page PDF blueprint to estimate construction materials.
88
  Args:
89
+ pdf_path (str): Path to the PDF file (single-page).
90
  blueprint_width_m (float): Blueprint width in meters (default: 27).
91
  blueprint_height_m (float): Blueprint height in meters (default: 9.78).
92
  poppler_path (str, optional): Path to poppler binaries.
 
95
  """
96
  logger.info("Starting blueprint processing for PDF: %s", pdf_path)
97
 
98
+ # Validate PDF file
99
  if not os.path.exists(pdf_path):
100
  logger.error("PDF file does not exist: %s", pdf_path)
101
  return {"error": f"PDF file not found: {pdf_path}"}
102
+
103
+ # Validate blueprint dimensions
104
  if not isinstance(blueprint_width_m, (int, float)) or not isinstance(blueprint_height_m, (int, float)):
105
+ logger.error("Invalid dimensions: width=%s, height=%s", blueprint_width_m, blueprint_height_m)
106
  return {"error": "Blueprint width and height must be numeric"}
107
  if blueprint_width_m <= 0 or blueprint_height_m <= 0:
108
+ logger.error("Invalid dimensions: width=%s, height=%s", blueprint_width_m, blueprint_height_m)
109
  return {"error": "Blueprint width and height must be positive"}
110
 
111
  # Validate poppler_path
 
113
  logger.debug("Using poppler_path: %s", poppler_path)
114
 
115
  try:
116
+ # Convert single-page PDF to image
117
+ logger.debug("Converting single-page PDF with poppler_path=%s", poppler_path)
118
  images = convert_from_path(
119
  pdf_path,
120
  first_page=1,
 
122
  poppler_path=poppler_path
123
  )
124
  if not images:
125
+ logger.error("No image extracted from PDF")
126
+ return {"error": "Failed to extract image from PDF (empty or invalid PDF)"}
127
 
128
+ logger.info("Extracted 1 image from PDF")
129
 
130
+ # Save the image to a temporary file
131
  with tempfile.NamedTemporaryFile(suffix=".png", delete=False) as temp_file:
132
  images[0].save(temp_file.name, 'PNG')
133
  temp_image_path = temp_file.name
134
  logger.debug("Saved temporary image to: %s", temp_image_path)
135
 
136
+ # Verify temporary file
137
  if not os.path.exists(temp_image_path):
138
  logger.error("Temporary image file not created: %s", temp_image_path)
139
  return {"error": "Failed to create temporary image file"}
140
 
141
+ # Load image with OpenCV
142
  image = cv2.imread(temp_image_path)
143
  if image is None:
144
+ logger.error("Failed to load image: %s", temp_image_path)
145
+ return {"error": "Could not load image from PDF"}
146
 
147
  logger.info("Loaded image with dimensions: %s", image.shape)
148
 
149
  # Clean up temporary file
150
  os.unlink(temp_image_path)
151
+ logger.debug("Deleted temporary image: %s", temp_image_path)
152
 
153
+ # Convert to grayscale
154
  gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
155
  logger.debug("Converted image to grayscale")
156
 
157
+ # Apply edge detection
158
  edges = cv2.Canny(gray, 50, 150, apertureSize=3)
159
  logger.debug("Applied Canny edge detection")
160
 
161
+ # Detect lines (walls) with Hough Transform
162
  lines = cv2.HoughLinesP(edges, 1, np.pi / 180, threshold=100, minLineLength=50, maxLineGap=10)
163
+ logger.debug("Detected %s lines", len(lines) if lines is not None else 0)
164
 
165
  # Calculate total wall length (in pixels)
166
  total_wall_length_pixels = 0
 
171
  total_wall_length_pixels += length
172
  logger.debug("Line from (%d,%d) to (%d,%d), length=%f pixels", x1, y1, x2, y2, length)
173
  else:
174
+ logger.warning("No lines detected in blueprint")
175
 
176
+ logger.info("Total wall length: %f pixels", total_wall_length_pixels)
177
 
178
  # Get image dimensions
179
  image_height, image_width = image.shape[:2]
 
188
 
189
  # Convert wall length to meters
190
  total_wall_length_m = total_wall_length_pixels * pixel_to_meter
191
+ logger.info("Total wall length: %f meters", total_wall_length_m)
192
 
193
+ # Estimate wall area (wall height = 3 m)
194
+ wall_height_m = 3
195
  wall_area = total_wall_length_m * wall_height_m
196
+ logger.info("Wall area: %f m² (length=%f m, height=%f m)",
197
  wall_area, total_wall_length_m, wall_height_m)
198
 
199
+ # Estimate foundation area (10% of total area)
200
  total_area = blueprint_width_m * blueprint_height_m
201
+ foundation_area = total_area * 0.1
202
  logger.info("Total area: %f m², Foundation area: %f m²", total_area, foundation_area)
203
 
204
  # Calculate materials
205
  materials = calculate_materials_from_dimensions(wall_area, foundation_area)
206
+ logger.info("Raw materials: %s", materials)
207
 
208
+ # Format output (same as image-based app)
209
  formatted_materials = {
210
  "cement": f"{materials['cement']:.2f} kg",
211
  "bricks": f"{materials['bricks']:.0f} units",
212
  "steel": f"{materials['steel']:.2f} kg"
213
  }
214
+ logger.info("Formatted materials: %s", formatted_materials)
215
 
216
  return formatted_materials
217
 
218
  except Exception as e:
219
+ # Handle poppler and other errors
220
  error_msg = str(e)
221
+ if "Unable to get page count" in error_msg or "poppler" in error_msg.lower():
222
  error_msg = (
223
+ "Cannot convert PDF to image. Ensure poppler-utils is installed:\n"
224
+ "- Windows: Download from https://github.com/oschwartz10612/poppler-windows, extract, "
225
+ "and add the 'bin' folder (e.g., C:/poppler/bin) to PATH.\n"
226
  "- Linux: Run 'sudo apt-get install poppler-utils'.\n"
227
  "- Mac: Run 'brew install poppler'.\n"
228
+ "Alternatively, enter the poppler 'bin' folder path in the Poppler Path field "
229
+ "(e.g., C:/poppler/bin). Current poppler_path: %s" % poppler_path
230
  )
231
  logger.error("Processing failed: %s", error_msg)
232
  return {"error": f"Failed to process PDF: {error_msg}"}
 
235
  interface = gr.Interface(
236
  fn=process_blueprint,
237
  inputs=[
238
+ gr.File(file_types=[".pdf"], label="Upload Single-Page Blueprint PDF"),
239
  gr.Number(label="Blueprint Width (meters)", value=27),
240
  gr.Number(label="Blueprint Height (meters)", value=9.78),
241
  gr.Textbox(label="Poppler Path (optional)", placeholder="e.g., C:/poppler/bin")
 
243
  outputs=gr.JSON(label="Material Estimates"),
244
  title="Blueprint Material Estimator",
245
  description=(
246
+ "Upload a single-page PDF blueprint to estimate construction materials. "
247
+ "Enter blueprint dimensions and, if poppler-utils is not in PATH, provide the path to poppler's 'bin' folder."
248
  )
249
  )
250