Spaces:
Running
Running
AMontiB
commited on
Commit
·
0b2abd8
1
Parent(s):
ac2a7d8
deal with large images
Browse files- __pycache__/app.cpython-310.pyc +0 -0
- app.py +54 -2
- test_cleanup.py +56 -0
- test_crop.py +44 -0
__pycache__/app.cpython-310.pyc
CHANGED
|
Binary files a/__pycache__/app.cpython-310.pyc and b/__pycache__/app.cpython-310.pyc differ
|
|
|
app.py
CHANGED
|
@@ -5,6 +5,7 @@ import sys
|
|
| 5 |
import json
|
| 6 |
import argparse
|
| 7 |
from types import SimpleNamespace
|
|
|
|
| 8 |
|
| 9 |
# Try to import detector - if this fails, we'll show an error in the UI
|
| 10 |
try:
|
|
@@ -30,6 +31,42 @@ if os.environ.get("SPACE_ID"):
|
|
| 30 |
# Available detectors based on launcher.py
|
| 31 |
DETECTORS = ['R50_TF', 'R50_nodown', 'CLIP-D', 'P2G', 'NPR']
|
| 32 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 33 |
def predict(image_path, detector_name):
|
| 34 |
# Check if detector is available
|
| 35 |
if not DETECTOR_AVAILABLE:
|
|
@@ -42,12 +79,20 @@ def predict(image_path, detector_name):
|
|
| 42 |
if not image_path:
|
| 43 |
return json.dumps({"error": "Please upload an image."}, indent=2)
|
| 44 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 45 |
# Create a temporary output file path
|
| 46 |
output_path = "temp_result.json"
|
| 47 |
|
| 48 |
# Mock args object
|
| 49 |
args = SimpleNamespace(
|
| 50 |
-
image=
|
| 51 |
detector=detector_name,
|
| 52 |
config_dir='configs',
|
| 53 |
output=output_path,
|
|
@@ -99,6 +144,13 @@ def predict(image_path, detector_name):
|
|
| 99 |
# Cleanup
|
| 100 |
if os.path.exists(output_path):
|
| 101 |
os.remove(output_path)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 102 |
|
| 103 |
# Create Gradio Interface
|
| 104 |
# Use theme only if gradio version supports it
|
|
@@ -119,7 +171,7 @@ with demo:
|
|
| 119 |
* **Elapsed Time**: The time the model needed to make the prediction (excluding preprocessing or model building).
|
| 120 |
|
| 121 |
### Note
|
| 122 |
-
⚠️ Due to file size limitations, model weights need to be downloaded automatically on first use. This may take a few moments.
|
| 123 |
⚠️ To provide a free service, all models run on CPU.The detection process may take a few seconds, depending on the image size and the selected detector.
|
| 124 |
""")
|
| 125 |
|
|
|
|
| 5 |
import json
|
| 6 |
import argparse
|
| 7 |
from types import SimpleNamespace
|
| 8 |
+
from PIL import Image
|
| 9 |
|
| 10 |
# Try to import detector - if this fails, we'll show an error in the UI
|
| 11 |
try:
|
|
|
|
| 31 |
# Available detectors based on launcher.py
|
| 32 |
DETECTORS = ['R50_TF', 'R50_nodown', 'CLIP-D', 'P2G', 'NPR']
|
| 33 |
|
| 34 |
+
def process_image(image_path):
|
| 35 |
+
"""
|
| 36 |
+
Check if image is larger than 1024x1024 and central crop it if necessary.
|
| 37 |
+
Returns the path to the processed image (or original if no change).
|
| 38 |
+
"""
|
| 39 |
+
try:
|
| 40 |
+
with Image.open(image_path) as img:
|
| 41 |
+
width, height = img.size
|
| 42 |
+
|
| 43 |
+
# Check if both dimensions are larger than 1024
|
| 44 |
+
if width > 1024 and height > 1024:
|
| 45 |
+
print(f"Image size {width}x{height} exceeds 1024x1024. Performing central crop.")
|
| 46 |
+
|
| 47 |
+
# Calculate crop box
|
| 48 |
+
left = (width - 1024) / 2
|
| 49 |
+
top = (height - 1024) / 2
|
| 50 |
+
right = (width + 1024) / 2
|
| 51 |
+
bottom = (height + 1024) / 2
|
| 52 |
+
|
| 53 |
+
# Crop
|
| 54 |
+
img_cropped = img.crop((left, top, right, bottom))
|
| 55 |
+
|
| 56 |
+
# Save to new path
|
| 57 |
+
directory, filename = os.path.split(image_path)
|
| 58 |
+
name, ext = os.path.splitext(filename)
|
| 59 |
+
new_filename = f"{name}_cropped{ext}"
|
| 60 |
+
new_path = os.path.join(directory, new_filename)
|
| 61 |
+
|
| 62 |
+
img_cropped.save(new_path)
|
| 63 |
+
return new_path
|
| 64 |
+
|
| 65 |
+
return image_path
|
| 66 |
+
except Exception as e:
|
| 67 |
+
print(f"Error processing image: {e}")
|
| 68 |
+
return image_path
|
| 69 |
+
|
| 70 |
def predict(image_path, detector_name):
|
| 71 |
# Check if detector is available
|
| 72 |
if not DETECTOR_AVAILABLE:
|
|
|
|
| 79 |
if not image_path:
|
| 80 |
return json.dumps({"error": "Please upload an image."}, indent=2)
|
| 81 |
|
| 82 |
+
# Process image (central crop if too large)
|
| 83 |
+
processed_path = image_path
|
| 84 |
+
try:
|
| 85 |
+
processed_path = process_image(image_path)
|
| 86 |
+
except Exception as e:
|
| 87 |
+
print(f"Warning: Image processing failed: {e}")
|
| 88 |
+
# Continue with original image if processing fails
|
| 89 |
+
|
| 90 |
# Create a temporary output file path
|
| 91 |
output_path = "temp_result.json"
|
| 92 |
|
| 93 |
# Mock args object
|
| 94 |
args = SimpleNamespace(
|
| 95 |
+
image=processed_path,
|
| 96 |
detector=detector_name,
|
| 97 |
config_dir='configs',
|
| 98 |
output=output_path,
|
|
|
|
| 144 |
# Cleanup
|
| 145 |
if os.path.exists(output_path):
|
| 146 |
os.remove(output_path)
|
| 147 |
+
|
| 148 |
+
# Cleanup cropped image if it's different from original
|
| 149 |
+
if processed_path != image_path and os.path.exists(processed_path):
|
| 150 |
+
try:
|
| 151 |
+
os.remove(processed_path)
|
| 152 |
+
except Exception as e:
|
| 153 |
+
print(f"Warning: Could not remove temporary file {processed_path}: {e}")
|
| 154 |
|
| 155 |
# Create Gradio Interface
|
| 156 |
# Use theme only if gradio version supports it
|
|
|
|
| 171 |
* **Elapsed Time**: The time the model needed to make the prediction (excluding preprocessing or model building).
|
| 172 |
|
| 173 |
### Note
|
| 174 |
+
⚠️ Due to file size limitations, model weights need to be downloaded automatically on first use. This may take a few moments. \
|
| 175 |
⚠️ To provide a free service, all models run on CPU.The detection process may take a few seconds, depending on the image size and the selected detector.
|
| 176 |
""")
|
| 177 |
|
test_cleanup.py
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
import json
|
| 3 |
+
from PIL import Image
|
| 4 |
+
from app import predict, process_image
|
| 5 |
+
|
| 6 |
+
# Mock DETECTOR_AVAILABLE to avoid import errors during test
|
| 7 |
+
import app
|
| 8 |
+
app.DETECTOR_AVAILABLE = True
|
| 9 |
+
app.run_detect = lambda args: None # Mock run_detect
|
| 10 |
+
|
| 11 |
+
def test_cleanup():
|
| 12 |
+
# Create a large dummy image
|
| 13 |
+
large_img_path = "test_large_cleanup.png"
|
| 14 |
+
Image.new('RGB', (2000, 2000), color='green').save(large_img_path)
|
| 15 |
+
|
| 16 |
+
# Create a dummy output file because predict expects it
|
| 17 |
+
output_path = "temp_result.json"
|
| 18 |
+
with open(output_path, 'w') as f:
|
| 19 |
+
json.dump({"prediction": "real", "confidence": 0.9, "elapsed_time": 0.1}, f)
|
| 20 |
+
|
| 21 |
+
# We need to monkeypatch process_image to track the file it creates
|
| 22 |
+
original_process_image = app.process_image
|
| 23 |
+
created_files = []
|
| 24 |
+
|
| 25 |
+
def tracked_process_image(path):
|
| 26 |
+
new_path = original_process_image(path)
|
| 27 |
+
if new_path != path:
|
| 28 |
+
created_files.append(new_path)
|
| 29 |
+
return new_path
|
| 30 |
+
|
| 31 |
+
app.process_image = tracked_process_image
|
| 32 |
+
|
| 33 |
+
# Run predict
|
| 34 |
+
try:
|
| 35 |
+
print("Running predict...")
|
| 36 |
+
app.predict(large_img_path, "R50_TF")
|
| 37 |
+
except Exception as e:
|
| 38 |
+
print(f"Predict failed: {e}")
|
| 39 |
+
|
| 40 |
+
# Check if cropped file was created
|
| 41 |
+
assert len(created_files) > 0, "Should have created a cropped file"
|
| 42 |
+
cropped_path = created_files[0]
|
| 43 |
+
print(f"Cropped path was: {cropped_path}")
|
| 44 |
+
|
| 45 |
+
# Check if cropped file was deleted
|
| 46 |
+
assert not os.path.exists(cropped_path), f"Cropped file {cropped_path} should have been deleted"
|
| 47 |
+
print("Cleanup test passed!")
|
| 48 |
+
|
| 49 |
+
# Cleanup original
|
| 50 |
+
if os.path.exists(large_img_path):
|
| 51 |
+
os.remove(large_img_path)
|
| 52 |
+
if os.path.exists(output_path):
|
| 53 |
+
os.remove(output_path)
|
| 54 |
+
|
| 55 |
+
if __name__ == "__main__":
|
| 56 |
+
test_cleanup()
|
test_crop.py
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
from PIL import Image
|
| 3 |
+
from app import process_image
|
| 4 |
+
|
| 5 |
+
def test_process_image():
|
| 6 |
+
# Create a large dummy image
|
| 7 |
+
large_img_path = "test_large.png"
|
| 8 |
+
Image.new('RGB', (2000, 2000), color='red').save(large_img_path)
|
| 9 |
+
|
| 10 |
+
# Process it
|
| 11 |
+
processed_path = process_image(large_img_path)
|
| 12 |
+
|
| 13 |
+
# Check if a new file was created
|
| 14 |
+
assert processed_path != large_img_path, "Should have returned a new path"
|
| 15 |
+
assert "cropped" in processed_path, "New path should contain 'cropped'"
|
| 16 |
+
|
| 17 |
+
# Check dimensions
|
| 18 |
+
with Image.open(processed_path) as img:
|
| 19 |
+
assert img.size == (1024, 1024), f"Expected 1024x1024, got {img.size}"
|
| 20 |
+
|
| 21 |
+
print("Large image test passed!")
|
| 22 |
+
|
| 23 |
+
# Create a small dummy image
|
| 24 |
+
small_img_path = "test_small.png"
|
| 25 |
+
Image.new('RGB', (800, 800), color='blue').save(small_img_path)
|
| 26 |
+
|
| 27 |
+
# Process it
|
| 28 |
+
processed_path_small = process_image(small_img_path)
|
| 29 |
+
|
| 30 |
+
# Check if it returned the same path
|
| 31 |
+
assert processed_path_small == small_img_path, "Should have returned original path"
|
| 32 |
+
|
| 33 |
+
print("Small image test passed!")
|
| 34 |
+
|
| 35 |
+
# Cleanup
|
| 36 |
+
if os.path.exists(large_img_path):
|
| 37 |
+
os.remove(large_img_path)
|
| 38 |
+
if os.path.exists(processed_path):
|
| 39 |
+
os.remove(processed_path)
|
| 40 |
+
if os.path.exists(small_img_path):
|
| 41 |
+
os.remove(small_img_path)
|
| 42 |
+
|
| 43 |
+
if __name__ == "__main__":
|
| 44 |
+
test_process_image()
|