LogicGoInfotechSpaces commited on
Commit
3105993
·
1 Parent(s): 738e35e

Add compressed image support with 2-3MB target file size - Add Compressed_Image_URL field to colorize endpoint response - Implement compress_image_to_target_size function with iterative quality/dimension adjustment - Original download_url returns uncompressed PNG (model output) - Compressed_Image_URL returns JPEG compressed to 2-3MB file size

Browse files
Files changed (1) hide show
  1. app/main.py +170 -2
app/main.py CHANGED
@@ -1,5 +1,5 @@
1
  from fastapi import FastAPI, File, UploadFile, HTTPException, Header, Request, Form, Depends, Body
2
- from typing import Optional
3
  from fastapi.responses import FileResponse
4
  from huggingface_hub import hf_hub_download
5
  import uuid
@@ -81,8 +81,10 @@ else:
81
  # -------------------------------------------------
82
  UPLOAD_DIR = "/tmp/uploads"
83
  RESULTS_DIR = "/tmp/results"
 
84
  os.makedirs(UPLOAD_DIR, exist_ok=True)
85
  os.makedirs(RESULTS_DIR, exist_ok=True)
 
86
 
87
  MEDIA_CLICK_DEFAULT_CATEGORY = os.getenv("DEFAULT_CATEGORY_FALLBACK", "69368fcd2e46bd68ae1889b2")
88
 
@@ -182,6 +184,105 @@ def colorize_image(img: Image.Image, model_type: str = "gan", cco_model: str = "
182
  else:
183
  return colorize_image_gan(img)
184
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
185
  # -------------------------------------------------
186
  # 🗄️ MongoDB Initialization
187
  # -------------------------------------------------
@@ -737,8 +838,35 @@ async def colorize(
737
 
738
  result_id = f"{uuid.uuid4()}.png"
739
  output_path = os.path.join(RESULTS_DIR, result_id)
 
740
  output_img.save(output_path, "PNG")
741
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
742
  base_url = "https://logicgoinfotechspaces-text-guided-image-colorization.hf.space"
743
 
744
  result_id_clean = result_id.replace(".png", "")
@@ -752,7 +880,8 @@ async def colorize(
752
  "download_url": f"{base_url}/results/{result_id}",
753
  "api_download_url": f"{base_url}/download/{result_id_clean}",
754
  "filename": result_id,
755
- "caption": caption
 
756
  }
757
 
758
  # Log to MongoDB (colorization_db -> colorizations)
@@ -915,6 +1044,14 @@ def get_upload(request: Request, filename: str):
915
  )
916
  raise HTTPException(status_code=404, detail="File not found")
917
 
 
 
 
 
 
 
 
 
918
  log_api_call(
919
  endpoint=f"/uploads/{filename}",
920
  method="GET",
@@ -924,3 +1061,34 @@ def get_upload(request: Request, filename: str):
924
  )
925
 
926
  return FileResponse(path, media_type=media_type)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  from fastapi import FastAPI, File, UploadFile, HTTPException, Header, Request, Form, Depends, Body
2
+ from typing import Optional, Tuple
3
  from fastapi.responses import FileResponse
4
  from huggingface_hub import hf_hub_download
5
  import uuid
 
81
  # -------------------------------------------------
82
  UPLOAD_DIR = "/tmp/uploads"
83
  RESULTS_DIR = "/tmp/results"
84
+ COMPRESSED_DIR = "/tmp/compressed"
85
  os.makedirs(UPLOAD_DIR, exist_ok=True)
86
  os.makedirs(RESULTS_DIR, exist_ok=True)
87
+ os.makedirs(COMPRESSED_DIR, exist_ok=True)
88
 
89
  MEDIA_CLICK_DEFAULT_CATEGORY = os.getenv("DEFAULT_CATEGORY_FALLBACK", "69368fcd2e46bd68ae1889b2")
90
 
 
184
  else:
185
  return colorize_image_gan(img)
186
 
187
+ def compress_image_to_target_size(img: Image.Image, target_size_mb: float = 2.5, min_size_mb: float = 2.0, max_size_mb: float = 3.0) -> Tuple[Image.Image, str]:
188
+ """
189
+ Compress image to target file size (2-3MB) by adjusting quality and dimensions
190
+
191
+ Args:
192
+ img: PIL Image to compress
193
+ target_size_mb: Target file size in MB (default: 2.5MB)
194
+ min_size_mb: Minimum acceptable file size in MB (default: 2.0MB)
195
+ max_size_mb: Maximum acceptable file size in MB (default: 3.0MB)
196
+
197
+ Returns:
198
+ Tuple of (compressed PIL Image, path to saved compressed image)
199
+ """
200
+ import tempfile
201
+
202
+ # Ensure image is RGB
203
+ if img.mode != "RGB":
204
+ img = img.convert("RGB")
205
+
206
+ # Get original dimensions
207
+ original_size = img.size
208
+ current_img = img.copy()
209
+
210
+ # Start with high quality and reduce if needed
211
+ quality = 85
212
+ scale_factor = 1.0
213
+
214
+ # Try different quality levels and scales to achieve target size
215
+ for attempt in range(20): # Max 20 attempts
216
+ # Create temporary file to check size
217
+ with tempfile.NamedTemporaryFile(suffix='.jpg', delete=False) as tmp_file:
218
+ temp_path = tmp_file.name
219
+
220
+ try:
221
+ # Resize if needed
222
+ if scale_factor < 1.0:
223
+ new_width = int(original_size[0] * scale_factor)
224
+ new_height = int(original_size[1] * scale_factor)
225
+ resized_img = current_img.resize((new_width, new_height), Image.Resampling.LANCZOS)
226
+ else:
227
+ resized_img = current_img
228
+
229
+ # Save with current quality
230
+ resized_img.save(temp_path, "JPEG", quality=quality, optimize=True)
231
+
232
+ # Check file size
233
+ file_size_bytes = os.path.getsize(temp_path)
234
+ file_size_mb = file_size_bytes / (1024 * 1024)
235
+
236
+ logger.info("Compression attempt %d: quality=%d, scale=%.2f, size=%.2f MB",
237
+ attempt + 1, quality, scale_factor, file_size_mb)
238
+
239
+ # If size is within target range, we're done
240
+ if min_size_mb <= file_size_mb <= max_size_mb:
241
+ logger.info("Target size achieved: %.2f MB", file_size_mb)
242
+ return resized_img, temp_path
243
+
244
+ # If too large, reduce quality or scale
245
+ if file_size_mb > max_size_mb:
246
+ if quality > 30:
247
+ quality -= 5 # Reduce quality
248
+ elif scale_factor > 0.5:
249
+ scale_factor -= 0.05 # Reduce dimensions
250
+ quality = 75 # Reset quality when scaling
251
+ else:
252
+ # Already at minimum, accept current result
253
+ logger.warning("Reached minimum compression, file size: %.2f MB", file_size_mb)
254
+ return resized_img, temp_path
255
+
256
+ # If too small but still reasonable (above 1MB), try to increase quality slightly
257
+ elif file_size_mb < min_size_mb:
258
+ if file_size_mb < 1.0:
259
+ # Very small file, increase quality more aggressively
260
+ if quality < 90:
261
+ quality += 5
262
+ elif scale_factor < 1.0:
263
+ # Increase scale if we reduced it
264
+ scale_factor = min(1.0, scale_factor + 0.1)
265
+ elif quality < 95:
266
+ # Close to target, fine-tune quality
267
+ quality += 2
268
+ if quality > 95:
269
+ quality = 95
270
+ else:
271
+ # Already at max quality and scale, accept current result
272
+ logger.info("File size %.2f MB is below minimum but at max quality, accepting", file_size_mb)
273
+ return resized_img, temp_path
274
+
275
+ finally:
276
+ # Clean up temp file if we're continuing
277
+ if os.path.exists(temp_path) and attempt < 19:
278
+ try:
279
+ os.unlink(temp_path)
280
+ except:
281
+ pass
282
+
283
+ # Return the last attempt's result
284
+ return resized_img, temp_path
285
+
286
  # -------------------------------------------------
287
  # 🗄️ MongoDB Initialization
288
  # -------------------------------------------------
 
838
 
839
  result_id = f"{uuid.uuid4()}.png"
840
  output_path = os.path.join(RESULTS_DIR, result_id)
841
+ # Save original image as PNG (uncompressed) - this is what the model produces
842
  output_img.save(output_path, "PNG")
843
 
844
+ # Create compressed version targeting 2-3MB file size
845
+ logger.info("Creating compressed version targeting 2-3MB file size...")
846
+ compressed_img, temp_compressed_path = compress_image_to_target_size(
847
+ output_img,
848
+ target_size_mb=2.5,
849
+ min_size_mb=2.0,
850
+ max_size_mb=3.0
851
+ )
852
+
853
+ # Move compressed image to final location
854
+ compressed_filename = result_id.replace(".png", "_compressed.jpg")
855
+ compressed_path = os.path.join(COMPRESSED_DIR, compressed_filename)
856
+
857
+ # If temp file exists, move it; otherwise save the compressed image
858
+ if os.path.exists(temp_compressed_path):
859
+ import shutil
860
+ shutil.move(temp_compressed_path, compressed_path)
861
+ else:
862
+ compressed_img.save(compressed_path, "JPEG", quality=75, optimize=True)
863
+
864
+ # Log compressed file size
865
+ compressed_size_mb = os.path.getsize(compressed_path) / (1024 * 1024)
866
+ original_size_mb = os.path.getsize(output_path) / (1024 * 1024)
867
+ logger.info("Original image size: %.2f MB, Compressed image size: %.2f MB",
868
+ original_size_mb, compressed_size_mb)
869
+
870
  base_url = "https://logicgoinfotechspaces-text-guided-image-colorization.hf.space"
871
 
872
  result_id_clean = result_id.replace(".png", "")
 
880
  "download_url": f"{base_url}/results/{result_id}",
881
  "api_download_url": f"{base_url}/download/{result_id_clean}",
882
  "filename": result_id,
883
+ "caption": caption,
884
+ "Compressed_Image_URL": f"{base_url}/compressed/{compressed_filename}"
885
  }
886
 
887
  # Log to MongoDB (colorization_db -> colorizations)
 
1044
  )
1045
  raise HTTPException(status_code=404, detail="File not found")
1046
 
1047
+ # Determine media type based on file extension
1048
+ if filename.lower().endswith('.png'):
1049
+ media_type = "image/png"
1050
+ elif filename.lower().endswith('.jpg') or filename.lower().endswith('.jpeg'):
1051
+ media_type = "image/jpeg"
1052
+ else:
1053
+ media_type = "image/jpeg" # Default to JPEG
1054
+
1055
  log_api_call(
1056
  endpoint=f"/uploads/{filename}",
1057
  method="GET",
 
1061
  )
1062
 
1063
  return FileResponse(path, media_type=media_type)
1064
+
1065
+ # -------------------------------------------------
1066
+ # 🌐 Public Compressed File
1067
+ # -------------------------------------------------
1068
+ @app.get("/compressed/{filename}")
1069
+ def get_compressed(request: Request, filename: str):
1070
+ ip_address = request.client.host if request.client else None
1071
+
1072
+ path = os.path.join(COMPRESSED_DIR, filename)
1073
+ if not os.path.exists(path):
1074
+ log_api_call(
1075
+ endpoint=f"/compressed/{filename}",
1076
+ method="GET",
1077
+ status_code=404,
1078
+ error="Compressed file not found",
1079
+ ip_address=ip_address
1080
+ )
1081
+ raise HTTPException(status_code=404, detail="Compressed file not found")
1082
+
1083
+ # Compressed images are JPEG
1084
+ media_type = "image/jpeg"
1085
+
1086
+ log_api_call(
1087
+ endpoint=f"/compressed/{filename}",
1088
+ method="GET",
1089
+ status_code=200,
1090
+ request_data={"filename": filename},
1091
+ ip_address=ip_address
1092
+ )
1093
+
1094
+ return FileResponse(path, media_type=media_type)