Dreamy0 commited on
Commit
6fbad87
Β·
verified Β·
1 Parent(s): cc359b0

Upload 3 files

Browse files
Files changed (3) hide show
  1. README.md +12 -13
  2. app.py +250 -0
  3. requirements.txt +6 -0
README.md CHANGED
@@ -1,13 +1,12 @@
1
- ---
2
- title: Detector V1
3
- emoji: πŸ“š
4
- colorFrom: pink
5
- colorTo: yellow
6
- sdk: gradio
7
- sdk_version: 5.49.1
8
- app_file: app.py
9
- pinned: false
10
- short_description: This will detect and crop the seeds
11
- ---
12
-
13
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
1
+ ---
2
+ title: Seed Detection Cropping
3
+ emoji: 🌱
4
+ colorFrom: green
5
+ colorTo: yellow
6
+ sdk: gradio
7
+ sdk_version: 4.44.0
8
+ app_file: app.py
9
+ pinned: false
10
+ ---
11
+
12
+ Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
app.py ADDED
@@ -0,0 +1,250 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import io
3
+ import zipfile
4
+ import gradio as gr
5
+ import torch
6
+ import cv2
7
+ import numpy as np
8
+ from PIL import Image
9
+ import torchvision.transforms as T
10
+ import torchvision
11
+ from torchvision.models.detection import fasterrcnn_resnet50_fpn
12
+ from pathlib import Path
13
+
14
+ # Global variable to store the model
15
+ model = None
16
+ device = None
17
+
18
+ def load_model(model_path="seed_frcnn.pth", num_classes=2):
19
+ """Load the trained Faster R-CNN model"""
20
+ global model, device
21
+
22
+ device = 'cuda' if torch.cuda.is_available() else 'cpu'
23
+ print(f"Loading model on {device}...")
24
+
25
+ try:
26
+ model = fasterrcnn_resnet50_fpn(pretrained=False)
27
+ model.roi_heads.box_predictor = torchvision.models.detection.faster_rcnn.FastRCNNPredictor(
28
+ model.roi_heads.box_predictor.cls_score.in_features, num_classes
29
+ )
30
+ model.load_state_dict(torch.load(model_path, map_location=device))
31
+ model.to(device)
32
+ model.eval()
33
+ print("Model loaded successfully!")
34
+ return f"βœ… Model loaded successfully on {device}!"
35
+ except Exception as e:
36
+ return f"❌ Error loading model: {str(e)}"
37
+
38
+ def run_inference(img_pil, score_thresh=0.5):
39
+ """Run inference on a PIL image"""
40
+ if model is None:
41
+ return []
42
+
43
+ transform = T.ToTensor()
44
+ img_tensor = transform(img_pil).to(device)
45
+
46
+ with torch.no_grad():
47
+ outputs = model([img_tensor])[0]
48
+
49
+ boxes = outputs["boxes"].cpu().numpy()
50
+ scores = outputs["scores"].cpu().numpy()
51
+ labels = outputs["labels"].cpu().numpy()
52
+
53
+ detections = []
54
+ for box, score, label in zip(boxes, scores, labels):
55
+ if score >= score_thresh:
56
+ x1, y1, x2, y2 = box.astype(int)
57
+ detections.append({
58
+ 'bbox': [x1, y1, x2, y2],
59
+ 'score': float(score),
60
+ 'class': 'seed'
61
+ })
62
+
63
+ return detections
64
+
65
+ def draw_boxes(image, detections):
66
+ """Draw bounding boxes on image"""
67
+ img_cv = cv2.cvtColor(np.array(image), cv2.COLOR_RGB2BGR)
68
+
69
+ for det in detections:
70
+ x1, y1, x2, y2 = det['bbox']
71
+ score = det['score']
72
+
73
+ # Draw rectangle
74
+ cv2.rectangle(img_cv, (x1, y1), (x2, y2), (0, 255, 0), 2)
75
+
76
+ # Add label
77
+ label = f"seed: {score:.2f}"
78
+ cv2.putText(img_cv, label, (x1, y1-10),
79
+ cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 2)
80
+
81
+ return cv2.cvtColor(img_cv, cv2.COLOR_BGR2RGB)
82
+
83
+ def process_images(images, threshold, folder_name):
84
+ """Process uploaded images and return crops as a ZIP file"""
85
+ if model is None:
86
+ return None, "❌ Model not loaded! Please wait for the app to initialize.", None
87
+
88
+ if not images:
89
+ return None, "❌ Please upload at least one image!", None
90
+
91
+ if not folder_name:
92
+ folder_name = "seed_crops"
93
+
94
+ # Clean folder name
95
+ folder_name = "".join(c for c in folder_name if c.isalnum() or c in ('-', '_'))
96
+
97
+ # Create in-memory ZIP file
98
+ zip_buffer = io.BytesIO()
99
+ total_crops = 0
100
+ processed_images = 0
101
+ preview_images = []
102
+
103
+ with zipfile.ZipFile(zip_buffer, 'w', zipfile.ZIP_DEFLATED) as zip_file:
104
+ for idx, img_file in enumerate(images):
105
+ try:
106
+ # Load image
107
+ if isinstance(img_file, str):
108
+ img = Image.open(img_file).convert("RGB")
109
+ base_name = Path(img_file).stem
110
+ else:
111
+ img = Image.open(img_file).convert("RGB")
112
+ base_name = f"image_{idx+1}"
113
+
114
+ # Run inference
115
+ detections = run_inference(img, threshold)
116
+
117
+ if len(detections) > 0:
118
+ # Convert to OpenCV format for cropping
119
+ img_cv = cv2.cvtColor(np.array(img), cv2.COLOR_RGB2BGR)
120
+ h, w = img_cv.shape[:2]
121
+
122
+ # Save crops
123
+ for i, det in enumerate(detections):
124
+ x1, y1, x2, y2 = map(int, det['bbox'])
125
+
126
+ # Ensure coordinates are within bounds
127
+ x1 = max(0, x1)
128
+ y1 = max(0, y1)
129
+ x2 = min(w, x2)
130
+ y2 = min(h, y2)
131
+
132
+ # Extract crop
133
+ crop = img_cv[y1:y2, x1:x2]
134
+
135
+ if crop.size > 0:
136
+ # Convert crop to bytes
137
+ _, buffer = cv2.imencode('.jpg', crop)
138
+ crop_bytes = buffer.tobytes()
139
+
140
+ # Add to ZIP with folder structure
141
+ crop_filename = f"{folder_name}/{base_name}_seed_{i:03d}_score_{det['score']:.3f}.jpg"
142
+ zip_file.writestr(crop_filename, crop_bytes)
143
+ total_crops += 1
144
+
145
+ # Create preview with boxes (for first 3 images only)
146
+ if len(preview_images) < 3:
147
+ preview_img = draw_boxes(img, detections)
148
+ preview_images.append(preview_img)
149
+
150
+ processed_images += 1
151
+
152
+ except Exception as e:
153
+ print(f"Error processing image {idx}: {str(e)}")
154
+ continue
155
+
156
+ if total_crops == 0:
157
+ return None, "⚠️ No seeds detected! Try lowering the threshold.", None
158
+
159
+ zip_buffer.seek(0)
160
+
161
+ # Save to temporary file for Gradio
162
+ temp_zip_path = f"/tmp/{folder_name}.zip"
163
+ with open(temp_zip_path, 'wb') as f:
164
+ f.write(zip_buffer.getvalue())
165
+
166
+ status_msg = f"βœ… Processed {processed_images} images\n"
167
+ status_msg += f"🌱 Detected and saved {total_crops} seed crops\n"
168
+ status_msg += f"πŸ“¦ ZIP file ready for download!"
169
+
170
+ # Return ZIP file path, status message, and preview images
171
+ return temp_zip_path, status_msg, preview_images if preview_images else None
172
+
173
+ # Create Gradio interface
174
+ with gr.Blocks(title="Seed Detection & Cropping", theme=gr.themes.Soft()) as demo:
175
+ gr.Markdown("""
176
+ # 🌱 Seed Detection & Cropping Tool
177
+ Upload images to detect seeds using AI-powered Faster R-CNN model. Get all detected seeds as individual cropped images in a ZIP file.
178
+ """)
179
+
180
+ with gr.Row():
181
+ with gr.Column():
182
+ image_input = gr.File(
183
+ label="πŸ“€ Upload Images (One or Multiple)",
184
+ file_count="multiple",
185
+ file_types=["image"]
186
+ )
187
+
188
+ threshold_slider = gr.Slider(
189
+ minimum=0.1,
190
+ maximum=0.95,
191
+ value=0.5,
192
+ step=0.05,
193
+ label="🎚️ Detection Confidence Threshold"
194
+ )
195
+
196
+ folder_name_input = gr.Textbox(
197
+ label="πŸ“ Output Folder Name",
198
+ value="seed_crops",
199
+ placeholder="Enter folder name for crops"
200
+ )
201
+
202
+ process_btn = gr.Button("πŸ” Detect & Crop Seeds", variant="primary", size="lg")
203
+
204
+ with gr.Column():
205
+ status_output = gr.Textbox(label="πŸ“Š Status", lines=3)
206
+ download_output = gr.File(label="πŸ’Ύ Download Cropped Seeds (ZIP)")
207
+ preview_gallery = gr.Gallery(
208
+ label="πŸ–ΌοΈ Preview (First 3 images with detections)",
209
+ columns=3,
210
+ height="auto"
211
+ )
212
+
213
+ # Event handler
214
+ process_btn.click(
215
+ fn=process_images,
216
+ inputs=[image_input, threshold_slider, folder_name_input],
217
+ outputs=[download_output, status_output, preview_gallery]
218
+ )
219
+
220
+ gr.Markdown("""
221
+ ---
222
+ ### πŸ“‹ How to Use:
223
+ 1. **Upload Images**: Click the upload box and select one or multiple images containing seeds
224
+ 2. **Adjust Threshold**: Use the slider to control detection sensitivity
225
+ - **Lower (0.3-0.5)**: Detects more seeds, may include false positives
226
+ - **Higher (0.6-0.8)**: More conservative, only high-confidence detections
227
+ 3. **Set Folder Name**: Name the folder that will organize your crops in the ZIP file
228
+ 4. **Click Detect**: Process your images and wait for results
229
+ 5. **Download**: Get your ZIP file with all cropped seeds!
230
+
231
+ ### πŸ’‘ Tips:
232
+ - Upload clear, well-lit images for best results
233
+ - Start with default threshold (0.5) and adjust as needed
234
+ - Each crop is named with the original filename, seed number, and confidence score
235
+
236
+ ### 🎯 Output Format:
237
+ ```
238
+ your_folder_name/
239
+ β”œβ”€β”€ image1_seed_000_score_0.850.jpg
240
+ β”œβ”€β”€ image1_seed_001_score_0.720.jpg
241
+ └── image2_seed_000_score_0.910.jpg
242
+ ```
243
+ """)
244
+
245
+ # Auto-load model on startup
246
+ print("Initializing model...")
247
+ load_model("seed_frcnn.pth")
248
+
249
+ if __name__ == "__main__":
250
+ demo.launch()
requirements.txt ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ gradio==4.44.0
2
+ torch==2.1.0
3
+ torchvision==0.16.0
4
+ opencv-python-headless==4.8.1.78
5
+ Pillow==10.1.0
6
+ numpy==1.24.3