iplotnor commited on
Commit
ab4918d
Β·
verified Β·
1 Parent(s): 9e83591

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +72 -70
app.py CHANGED
@@ -126,44 +126,7 @@ class FloorPlanProcessor:
126
  logger.error(f"Image error: {str(e)}")
127
  pdf.error = str(e)
128
  return False
129
-
130
- def _preprocess_floor_plan(self, image):
131
- """
132
- Enhance floor plan image for better analysis
133
- """
134
- try:
135
- logger.info(f"Preprocessing image: {image.size}")
136
-
137
- # Convert to grayscale
138
- if image.mode != 'L':
139
- gray = image.convert('L')
140
- else:
141
- gray = image
142
-
143
- # Enhance contrast
144
- enhancer = ImageEnhance.Contrast(gray)
145
- gray = enhancer.enhance(1.5)
146
- logger.info("βœ“ Contrast enhanced")
147
-
148
- # Sharpen
149
- gray = gray.filter(ImageFilter.SHARPEN)
150
- logger.info("βœ“ Sharpened")
151
-
152
- # Remove noise
153
- gray = gray.filter(ImageFilter.MedianFilter(size=3))
154
- logger.info("βœ“ Noise removed")
155
-
156
- # Convert back to RGB
157
- result = gray.convert('RGB')
158
- logger.info(f"βœ“ Preprocessing complete: {result.size}")
159
-
160
- return result
161
-
162
- except Exception as e:
163
- logger.error(f"Preprocessing error: {str(e)}")
164
- return image
165
-
166
-
167
  async def extract_images_from_pdf(self, pdf, file_content):
168
  try:
169
  pdf_document = fitz.open(stream=file_content, filetype="pdf")
@@ -195,6 +158,74 @@ class FloorPlanProcessor:
195
  pdf.error = str(e)
196
  return False
197
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
198
  async def analyze_floor_plan(self, pdf_id, description=None):
199
  pdf = self.pdfs.get(pdf_id)
200
  if not pdf:
@@ -211,7 +242,6 @@ class FloorPlanProcessor:
211
  # Use ONLY the first/best image for single file analysis
212
  best_image = self._select_single_best_image(pdf.images)
213
  best_image = self._preprocess_floor_plan(best_image)
214
-
215
  optimized_image = self._optimize_image(best_image, target_size=2048)
216
 
217
  logger.info(f"Using single image: {optimized_image.size[0]}x{optimized_image.size[1]}px")
@@ -259,35 +289,6 @@ class FloorPlanProcessor:
259
  logger.warning("All attempts failed, using fallback")
260
  return self._generate_fallback(pdf.measurement_info)
261
 
262
- def _select_single_best_image(self, images):
263
- """Select the single best image"""
264
- if len(images) == 1:
265
- return images[0]
266
-
267
- # Score by area (largest = best for floor plans)
268
- scored = [(img.size[0] * img.size[1], img) for img in images]
269
- scored.sort(reverse=True, key=lambda x: x[0])
270
-
271
- best = scored[0][1]
272
- logger.info(f"Selected best from {len(images)} images")
273
- return best
274
-
275
- def _optimize_image(self, image, target_size=2048):
276
- """Optimize image for analysis"""
277
- if image.mode not in ('RGB', 'L'):
278
- image = image.convert('RGB')
279
-
280
- width, height = image.size
281
-
282
- if width > target_size or height > target_size:
283
- ratio = target_size / max(width, height)
284
- new_width = int(width * ratio)
285
- new_height = int(height * ratio)
286
- image = image.resize((new_width, new_height), Image.Resampling.LANCZOS)
287
- logger.info(f"Resized: {width}x{height} β†’ {new_width}x{new_height}")
288
-
289
- return image
290
-
291
  async def _analyze_with_gemini(self, image, measurement_info, description, timeout, attempt=0):
292
  """Analyze with Gemini API"""
293
  prompt = self._create_detailed_prompt(description, measurement_info)
@@ -516,7 +517,7 @@ Standard takhΓΈyde: {measurement_info['ceiling_height']}m
516
 
517
  app = FastAPI(
518
  title="Floor Plan API",
519
- version="1.0.6",
520
  docs_url="/"
521
  )
522
 
@@ -673,8 +674,9 @@ async def startup_event():
673
  os.makedirs("logs", exist_ok=True)
674
 
675
  logger.info("\n" + "="*60)
676
- logger.info("Floor Plan API - Single File Mode")
677
  logger.info(f"Model: gemini-2.5-pro")
 
678
  logger.info(f"API Key: {'SET' if GOOGLE_API_KEY else 'NOT SET'}")
679
  logger.info(f"Port: 7860")
680
  logger.info("="*60 + "\n")
 
126
  logger.error(f"Image error: {str(e)}")
127
  pdf.error = str(e)
128
  return False
129
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
130
  async def extract_images_from_pdf(self, pdf, file_content):
131
  try:
132
  pdf_document = fitz.open(stream=file_content, filetype="pdf")
 
158
  pdf.error = str(e)
159
  return False
160
 
161
+ def _select_single_best_image(self, images):
162
+ """Select the single best image"""
163
+ if len(images) == 1:
164
+ return images[0]
165
+
166
+ # Score by area (largest = best for floor plans)
167
+ scored = [(img.size[0] * img.size[1], img) for img in images]
168
+ scored.sort(reverse=True, key=lambda x: x[0])
169
+
170
+ best = scored[0][1]
171
+ logger.info(f"Selected best from {len(images)} images")
172
+ return best
173
+
174
+ def _preprocess_floor_plan(self, image):
175
+ """
176
+ Enhance floor plan image for better analysis
177
+ - Improves contrast (helps model see room boundaries better)
178
+ - Sharpens edges (makes text/dimensions clearer)
179
+ - Removes noise (reduces confusion from scan artifacts)
180
+ """
181
+ try:
182
+ logger.info(f"Preprocessing image: {image.size}")
183
+
184
+ # Step 1: Convert to grayscale for processing
185
+ if image.mode != 'L':
186
+ gray = image.convert('L')
187
+ else:
188
+ gray = image
189
+
190
+ # Step 2: Enhance contrast (1.5x = moderate boost)
191
+ enhancer = ImageEnhance.Contrast(gray)
192
+ gray = enhancer.enhance(1.5)
193
+ logger.info("βœ“ Contrast enhanced")
194
+
195
+ # Step 3: Sharpen to make text/lines clearer
196
+ gray = gray.filter(ImageFilter.SHARPEN)
197
+ logger.info("βœ“ Sharpened")
198
+
199
+ # Step 4: Remove noise with median filter
200
+ gray = gray.filter(ImageFilter.MedianFilter(size=3))
201
+ logger.info("βœ“ Noise removed")
202
+
203
+ # Step 5: Convert back to RGB for Gemini
204
+ result = gray.convert('RGB')
205
+ logger.info(f"βœ“ Preprocessing complete: {result.size}")
206
+
207
+ return result
208
+
209
+ except Exception as e:
210
+ logger.error(f"Preprocessing error: {str(e)}")
211
+ return image # Return original if preprocessing fails
212
+
213
+ def _optimize_image(self, image, target_size=2048):
214
+ """Optimize image for analysis"""
215
+ if image.mode not in ('RGB', 'L'):
216
+ image = image.convert('RGB')
217
+
218
+ width, height = image.size
219
+
220
+ if width > target_size or height > target_size:
221
+ ratio = target_size / max(width, height)
222
+ new_width = int(width * ratio)
223
+ new_height = int(height * ratio)
224
+ image = image.resize((new_width, new_height), Image.Resampling.LANCZOS)
225
+ logger.info(f"Resized: {width}x{height} β†’ {new_width}x{new_height}")
226
+
227
+ return image
228
+
229
  async def analyze_floor_plan(self, pdf_id, description=None):
230
  pdf = self.pdfs.get(pdf_id)
231
  if not pdf:
 
242
  # Use ONLY the first/best image for single file analysis
243
  best_image = self._select_single_best_image(pdf.images)
244
  best_image = self._preprocess_floor_plan(best_image)
 
245
  optimized_image = self._optimize_image(best_image, target_size=2048)
246
 
247
  logger.info(f"Using single image: {optimized_image.size[0]}x{optimized_image.size[1]}px")
 
289
  logger.warning("All attempts failed, using fallback")
290
  return self._generate_fallback(pdf.measurement_info)
291
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
292
  async def _analyze_with_gemini(self, image, measurement_info, description, timeout, attempt=0):
293
  """Analyze with Gemini API"""
294
  prompt = self._create_detailed_prompt(description, measurement_info)
 
517
 
518
  app = FastAPI(
519
  title="Floor Plan API",
520
+ version="1.0.7",
521
  docs_url="/"
522
  )
523
 
 
674
  os.makedirs("logs", exist_ok=True)
675
 
676
  logger.info("\n" + "="*60)
677
+ logger.info("Floor Plan API - Optimized Version")
678
  logger.info(f"Model: gemini-2.5-pro")
679
+ logger.info(f"With Image Preprocessing: YES")
680
  logger.info(f"API Key: {'SET' if GOOGLE_API_KEY else 'NOT SET'}")
681
  logger.info(f"Port: 7860")
682
  logger.info("="*60 + "\n")