Spaces:
Build error
Build error
File size: 11,110 Bytes
8a50c3f 13b9afe de8ea7d 8a50c3f 8219ccf 13b9afe 8a50c3f de8ea7d 8a50c3f 13b9afe 8a50c3f 13b9afe 8a50c3f 13b9afe de8ea7d 13b9afe 8a50c3f 13b9afe de8ea7d 13b9afe de8ea7d 8a50c3f de8ea7d 8219ccf de8ea7d 8219ccf de8ea7d 13b9afe 8a50c3f de8ea7d 8a50c3f de8ea7d 8219ccf 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 |
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
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
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 (cement, bricks, steel) 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).
Raises:
ValueError: If areas are negative or invalid.
"""
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 and foundation_area must be numeric")
raise ValueError("Wall and foundation areas must be numeric")
if wall_area < 0 or foundation_area < 0:
logger.error("Negative areas provided: 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² for walls
materials['bricks'] += wall_area * 500 # 500 bricks per m² for walls
materials['steel'] += wall_area * 2 # 2 kg steel per m² for walls
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² for foundation
materials['bricks'] += foundation_area * 750 # 750 bricks per m² for foundation
materials['steel'] += foundation_area * 5 # 5 kg steel per m² for foundation
logger.debug("Foundation materials added: 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 PDF
def process_blueprint(pdf_path, blueprint_width_m=27, blueprint_height_m=9.78, poppler_path=None):
"""
Process a PDF blueprint to estimate construction materials.
Args:
pdf_path (str): Path to the PDF file.
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 inputs
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}"}
if not isinstance(blueprint_width_m, (int, float)) or not isinstance(blueprint_height_m, (int, float)):
logger.error("Invalid blueprint 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 blueprint 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 PDF to images
logger.debug("Converting PDF to image 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 images extracted from PDF")
return {"error": "No images extracted from the PDF"}
logger.info("Successfully extracted %d image(s) from PDF", len(images))
# Save the first page as a temporary image
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 exists
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"}
# Open the image with OpenCV
image = cv2.imread(temp_image_path)
if image is None:
logger.error("Failed to load image from: %s", temp_image_path)
return {"error": "Could not load the 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 file: %s", temp_image_path)
# Convert to grayscale for easier processing
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
logger.debug("Converted image to grayscale")
# Apply edge detection to find lines (walls)
edges = cv2.Canny(gray, 50, 150, apertureSize=3)
logger.debug("Applied Canny edge detection")
# Use Hough Transform to detect lines (walls)
lines = cv2.HoughLinesP(edges, 1, np.pi / 180, threshold=100, minLineLength=50, maxLineGap=10)
logger.debug("Detected %s lines with Hough Transform", 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 the blueprint")
logger.info("Total wall length in pixels: %f", 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 in meters: %f", total_wall_length_m)
# Estimate wall area (assume wall height of 3 m)
wall_height_m = 3 # Hardcoded assumption; may need adjustment
wall_area = total_wall_length_m * wall_height_m
logger.info("Wall area: %f m² (wall length=%f m, height=%f m)",
wall_area, total_wall_length_m, wall_height_m)
# Estimate foundation area (10% of total blueprint area)
total_area = blueprint_width_m * blueprint_height_m
foundation_area = total_area * 0.1 # Hardcoded assumption
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 material estimates: %s", materials)
# Format the output
formatted_materials = {
"cement": f"{materials['cement']:.2f} kg",
"bricks": f"{materials['bricks']:.0f} units",
"steel": f"{materials['steel']:.2f} kg"
}
logger.info("Formatted material estimates: %s", formatted_materials)
return formatted_materials
except Exception as e:
# Customize error message for poppler-related issues
error_msg = str(e)
if "Unable to get page count" in error_msg:
error_msg = (
"Poppler is not installed or not in PATH. "
"Please install poppler-utils:\n"
"- Windows: Download from https://github.com/oschwartz10612/poppler-windows and add to PATH.\n"
"- Linux: Run 'sudo apt-get install poppler-utils'.\n"
"- Mac: Run 'brew install poppler'.\n"
"Alternatively, specify a valid poppler_path in the input (e.g., C:/poppler/bin)."
)
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 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 PDF containing a blueprint to estimate construction materials. "
"Specify blueprint dimensions and provide the path to poppler binaries if not in PATH."
)
)
# Launch the interface
if __name__ == "__main__":
logger.info("Launching Gradio interface")
interface.launch(share=False) |