Manith Marapperuma 👾 commited on
Commit
6ca0975
·
1 Parent(s): 760d7de

initial_commit

Browse files
Files changed (2) hide show
  1. app.py +257 -0
  2. requirements.txt +6 -0
app.py ADDED
@@ -0,0 +1,257 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Streamlit Deployment Script
2
+ import cv2
3
+ import numpy as np
4
+ import streamlit as st
5
+ import io
6
+ import base64
7
+ from PIL import Image
8
+ import rembg # Import rembg for background removal
9
+
10
+ st.set_page_config(page_title="SpotRadar", layout="wide")
11
+
12
+ st.title("SpotRadar Disease Detection App Phase 1 Demo by Manith Jayaba")
13
+ st.write("Upload an image to detect and analyze disease spots")
14
+
15
+ # Function to convert cv2 image to downloadable link
16
+ def get_image_download_link(img, filename, text):
17
+ buffered = io.BytesIO()
18
+ img_pil = Image.fromarray(img)
19
+ img_pil.save(buffered, format="PNG")
20
+ img_str = base64.b64encode(buffered.getvalue()).decode()
21
+ href = f'<a href="data:file/png;base64,{img_str}" download="{filename}">{text}</a>'
22
+ return href
23
+
24
+ # Remove background from the image
25
+ def remove_background(image):
26
+ try:
27
+ # Convert BGR to RGB for rembg
28
+ rgb_image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
29
+
30
+ # Remove the background
31
+ output = rembg.remove(rgb_image)
32
+
33
+ # Convert back to BGR for OpenCV processing
34
+ output_bgr = cv2.cvtColor(output, cv2.COLOR_RGBA2BGR)
35
+
36
+ return output_bgr
37
+ except Exception as e:
38
+ st.error(f"Error in background removal: {e}")
39
+ return image
40
+
41
+ # Preprocessing: Enhance contrast and reduce noise
42
+ def preprocess_image(image, clip_limit=2.0, tile_size=8, blur_kernel_size=5):
43
+ try:
44
+ hsv_image = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)
45
+ clahe = cv2.createCLAHE(clipLimit=clip_limit, tileGridSize=(tile_size, tile_size))
46
+ h, s, v = cv2.split(hsv_image)
47
+ v = clahe.apply(v)
48
+ hsv_image = cv2.merge((h, s, v))
49
+ enhanced_image = cv2.cvtColor(hsv_image, cv2.COLOR_HSV2BGR)
50
+ blurred_image = cv2.GaussianBlur(enhanced_image, (blur_kernel_size, blur_kernel_size), 0)
51
+ return blurred_image
52
+ except Exception as e:
53
+ st.error(f"Error in preprocessing: {e}")
54
+ return image
55
+
56
+ # Detect disease marks using thresholding and edge detection
57
+ def detect_disease_marks(image, threshold_block_size=11, threshold_c=2, canny_low=50, canny_high=150):
58
+ try:
59
+ gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
60
+ thresh = cv2.adaptiveThreshold(gray, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C,
61
+ cv2.THRESH_BINARY_INV, threshold_block_size, threshold_c)
62
+ edges = cv2.Canny(gray, canny_low, canny_high)
63
+ combined = cv2.bitwise_or(thresh, edges)
64
+ kernel = np.ones((3, 3), np.uint8)
65
+ cleaned = cv2.morphologyEx(combined, cv2.MORPH_CLOSE, kernel)
66
+ return cleaned
67
+ except Exception as e:
68
+ st.error(f"Error in disease detection: {e}")
69
+ return np.zeros_like(cv2.cvtColor(image, cv2.COLOR_BGR2GRAY))
70
+
71
+ # Highlight detected marks and extract color/size info
72
+ def highlight_disease(image, disease_mask):
73
+ try:
74
+ result = image.copy()
75
+ contours, _ = cv2.findContours(disease_mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
76
+
77
+ # Lists to store disease spot info
78
+ spot_info = []
79
+
80
+ # Total image area for percentage calculation
81
+ total_pixels = disease_mask.size
82
+
83
+ for i, contour in enumerate(contours):
84
+ # Calculate area (size in pixels)
85
+ area = cv2.contourArea(contour)
86
+
87
+ # Calculate individual spot coverage percentage
88
+ spot_percentage = (area / total_pixels) * 100
89
+
90
+ # Create a mask for this specific contour
91
+ spot_mask = np.zeros_like(disease_mask)
92
+ cv2.drawContours(spot_mask, [contour], -1, 255, thickness=cv2.FILLED)
93
+
94
+ # Extract average color from the original image within the contour
95
+ mean_color = cv2.mean(image, mask=spot_mask)[:3] # BGR values
96
+ mean_color_rgb = (mean_color[2], mean_color[1], mean_color[0]) # Convert to RGB
97
+
98
+ # Store spot info
99
+ spot_info.append({
100
+ 'spot_number': i + 1,
101
+ 'size_pixels': area,
102
+ 'color_rgb': mean_color_rgb,
103
+ 'coverage_percentage': spot_percentage # Add individual coverage
104
+ })
105
+
106
+ # Draw contour and label on the image
107
+ cv2.drawContours(result, [contour], -1, (0, 0, 255), 2)
108
+ # Add spot number near the contour
109
+ M = cv2.moments(contour)
110
+ if M["m00"] != 0:
111
+ cX = int(M["m10"] / M["m00"])
112
+ cY = int(M["m01"] / M["m00"])
113
+ cv2.putText(result, str(i + 1), (cX, cY), cv2.FONT_HERSHEY_SIMPLEX,
114
+ 0.5, (255, 255, 255), 1, cv2.LINE_AA)
115
+
116
+ # Optional: Semi-transparent overlay
117
+ mask_colored = np.zeros_like(image)
118
+ mask_colored[disease_mask == 255] = [0, 0, 255]
119
+ result = cv2.addWeighted(result, 0.8, mask_colored, 0.2, 0)
120
+
121
+ return result, spot_info
122
+ except Exception as e:
123
+ st.error(f"Error in highlighting: {e}")
124
+ return image, []
125
+
126
+ # Calculate total disease coverage
127
+ def calculate_disease_coverage(disease_mask):
128
+ total_pixels = disease_mask.size
129
+ disease_pixels = np.count_nonzero(disease_mask)
130
+ percentage = (disease_pixels / total_pixels) * 100
131
+ return percentage
132
+
133
+ # Sidebar for parameters
134
+ with st.sidebar:
135
+ st.header("Parameters")
136
+
137
+ # Preprocessing parameters
138
+ st.subheader("Preprocessing")
139
+ clip_limit = st.slider("CLAHE Clip Limit", 0.5, 5.0, 2.0, 0.1)
140
+ tile_size = st.slider("CLAHE Tile Size", 2, 16, 8, 1)
141
+ blur_kernel = st.slider("Blur Kernel Size", 1, 11, 5, 2)
142
+
143
+ # Disease detection parameters
144
+ st.subheader("Disease Detection")
145
+ threshold_block_size = st.slider("Threshold Block Size", 3, 21, 11, 2)
146
+ threshold_c = st.slider("Threshold C Value", 0, 10, 2, 1)
147
+ canny_low = st.slider("Canny Low Threshold", 10, 100, 50, 5)
148
+ canny_high = st.slider("Canny High Threshold", 100, 300, 150, 5)
149
+
150
+ # Background removal option
151
+ remove_bg = st.checkbox("Remove Background", True)
152
+
153
+ # File uploader
154
+ uploaded_file = st.file_uploader("Choose an image file", type=["jpg", "jpeg", "png"])
155
+
156
+ if uploaded_file is not None:
157
+ # Convert uploaded file to OpenCV image
158
+ file_bytes = np.asarray(bytearray(uploaded_file.read()), dtype=np.uint8)
159
+ image = cv2.imdecode(file_bytes, cv2.IMREAD_COLOR)
160
+
161
+ # Create a placeholder for the processed images
162
+ result_placeholder = st.empty()
163
+
164
+ # Process button
165
+ if st.button("Process Image"):
166
+ with st.spinner("Processing image..."):
167
+ # Remove background if option is selected
168
+ if remove_bg:
169
+ no_bg_image = remove_background(image)
170
+ process_image = no_bg_image
171
+ else:
172
+ no_bg_image = image
173
+ process_image = image
174
+
175
+ # Continue with the normal processing
176
+ processed_image = preprocess_image(process_image, clip_limit, tile_size, blur_kernel)
177
+ disease_mask = detect_disease_marks(processed_image, threshold_block_size, threshold_c, canny_low, canny_high)
178
+ result_image, spot_info = highlight_disease(process_image, disease_mask)
179
+
180
+ # Calculate total disease coverage
181
+ disease_percentage = calculate_disease_coverage(disease_mask)
182
+
183
+ # Display results in columns
184
+ col1, col2 = st.columns(2)
185
+
186
+ with col1:
187
+ st.subheader("Original Image")
188
+ st.image(cv2.cvtColor(image, cv2.COLOR_BGR2RGB), use_container_width=True)
189
+
190
+ if remove_bg:
191
+ st.subheader("Background Removed")
192
+ st.image(cv2.cvtColor(no_bg_image, cv2.COLOR_BGR2RGB), use_container_width=True)
193
+
194
+ with col2:
195
+ st.subheader("Disease Mask")
196
+ st.image(disease_mask, use_container_width=True)
197
+
198
+ st.subheader(f"Detected Disease Marks (Coverage: {disease_percentage:.2f}%)")
199
+ st.image(cv2.cvtColor(result_image, cv2.COLOR_BGR2RGB), use_container_width=True)
200
+
201
+ # Download link for the result image
202
+ st.markdown(
203
+ get_image_download_link(
204
+ cv2.cvtColor(result_image, cv2.COLOR_BGR2RGB),
205
+ "disease_detection_result.png",
206
+ "Download Processed Image"
207
+ ),
208
+ unsafe_allow_html=True
209
+ )
210
+
211
+ # Display spot information
212
+ st.subheader("Disease Spot Analysis")
213
+
214
+ if not spot_info:
215
+ st.info("No disease spots detected.")
216
+ else:
217
+ # Sort spot_info by coverage_percentage in descending order
218
+ spot_info_sorted = sorted(spot_info, key=lambda x: x['coverage_percentage'], reverse=True)
219
+
220
+ # Create a table for spot info
221
+ spot_data = []
222
+ for i, spot in enumerate(spot_info_sorted):
223
+ spot_data.append({
224
+ "Rank": i + 1,
225
+ "Spot Number": spot['spot_number'],
226
+ "Size (pixels)": f"{spot['size_pixels']:.1f}",
227
+ "Coverage (%)": f"{spot['coverage_percentage']:.2f}%",
228
+ "Color (RGB)": f"({int(spot['color_rgb'][0])}, {int(spot['color_rgb'][1])}, {int(spot['color_rgb'][2])})"
229
+ })
230
+
231
+ st.table(spot_data)
232
+
233
+ # Summary statistics
234
+ st.subheader("Summary Statistics")
235
+ col1, col2, col3 = st.columns(3)
236
+
237
+ with col1:
238
+ st.metric("Total Spots", len(spot_info))
239
+
240
+ with col2:
241
+ st.metric("Total Coverage", f"{disease_percentage:.2f}%")
242
+
243
+ with col3:
244
+ avg_size = sum(spot['size_pixels'] for spot in spot_info) / len(spot_info)
245
+ st.metric("Average Spot Size", f"{avg_size:.1f} px")
246
+ else:
247
+ st.info("Please upload an image to begin analysis.")
248
+
249
+ # Sample image display
250
+ st.subheader("How it works")
251
+ st.write("""
252
+ 1. Upload a plant image
253
+ 2. Adjust parameters if needed
254
+ 3. Click 'Process Image'
255
+ 4. View disease detection results and analysis
256
+ 5. Download the processed image
257
+ """)
requirements.txt ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ opencv-python
2
+ numpy
3
+ matplotlib
4
+ rembg
5
+ onnxruntime
6
+ streamlit