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