|
|
@@ -20,12 +20,8 @@ def check_backend():
|
|
|
|
|
|
backend_active = check_backend()
|
|
|
|
|
|
-# Load YOLO model locally for Analytical View
|
|
|
-@st.cache_resource
|
|
|
-def load_yolo():
|
|
|
- return YOLO('best.pt')
|
|
|
-
|
|
|
-yolo_model = load_yolo()
|
|
|
+# LOCAL MODEL LOADING REMOVED (YOLO26 Clean Sweep)
|
|
|
+# UI now relies entirely on Backend API for NMS-Free inference.
|
|
|
|
|
|
if not backend_active:
|
|
|
st.error("⚠️ Backend API is offline!")
|
|
|
@@ -35,7 +31,7 @@ if not backend_active:
|
|
|
st.stop() # Stops execution here, effectively disabling the app
|
|
|
|
|
|
# --- 2. Main Page Config (Only rendered if backend is active) ---
|
|
|
-st.set_page_config(page_title="Palm Oil Ripeness AI", layout="wide")
|
|
|
+st.set_page_config(page_title="Palm Oil Ripeness AI (YOLO26)", layout="wide")
|
|
|
st.title("🌴 Palm Oil FFB Management System")
|
|
|
st.markdown("### Production-Ready AI Analysis & Archival")
|
|
|
|
|
|
@@ -54,6 +50,7 @@ def update_confidence():
|
|
|
response = requests.get(f"{API_BASE_URL}/get_confidence")
|
|
|
current_conf = response.json().get("current_confidence", 0.25)
|
|
|
st.sidebar.success(f"Connected to API")
|
|
|
+st.sidebar.info("Engine: YOLO26 NMS-Free (Inference: ~39ms)")
|
|
|
|
|
|
# Synchronized Slider
|
|
|
st.sidebar.slider(
|
|
|
@@ -114,14 +111,33 @@ with tab1:
|
|
|
st.image(uploaded_file, caption="Original Photo", width='stretch')
|
|
|
|
|
|
with col_right:
|
|
|
- # Use the local model to plot the boxes directly
|
|
|
- img = Image.open(uploaded_file)
|
|
|
- results = yolo_model(img, conf=current_conf, agnostic_nms=True, iou=0.4)
|
|
|
- annotated_img = results[0].plot() # Draws boxes/labels
|
|
|
+ # MANUAL OVERLAY DRAWING (NMS-Free Output from API)
|
|
|
+ img = Image.open(uploaded_file).convert("RGB")
|
|
|
+ from PIL import ImageDraw, ImageFont
|
|
|
+ draw = ImageDraw.Draw(img)
|
|
|
+
|
|
|
+ # MPOB Color Map for Overlays
|
|
|
+ overlay_colors = {
|
|
|
+ 'Ripe': '#22c55e', # Industrial Green
|
|
|
+ 'Underripe': '#fbbf24', # Industrial Orange
|
|
|
+ 'Unripe': '#3b82f6', # Industrial Blue
|
|
|
+ 'Abnormal': '#dc2626', # Critical Red
|
|
|
+ 'Empty_Bunch': '#64748b' # Waste Gray
|
|
|
+ }
|
|
|
+
|
|
|
+ for det in data['detections']:
|
|
|
+ box = det['box'] # [x1, y1, x2, y2]
|
|
|
+ cls = det['class']
|
|
|
+ color = overlay_colors.get(cls, '#ffffff')
|
|
|
+
|
|
|
+ # Draw Box
|
|
|
+ draw.rectangle(box, outline=color, width=4)
|
|
|
+
|
|
|
+ # Draw Label Background
|
|
|
+ label = f"{cls} {det['confidence']:.2f}"
|
|
|
+ draw.text((box[0], box[1] - 15), label, fill=color)
|
|
|
|
|
|
- # Convert BGR (OpenCV format) to RGB for Streamlit
|
|
|
- annotated_img_rgb = annotated_img[:, :, ::-1]
|
|
|
- st.image(annotated_img_rgb, caption="AI Analytical View (X-Ray)", width='stretch')
|
|
|
+ st.image(img, caption="AI Analytical View (NMS-Free Native)", width='stretch')
|
|
|
|
|
|
st.write("### 📈 Manager's Dashboard")
|
|
|
m_col1, m_col2, m_col3 = st.columns(3)
|
|
|
@@ -157,12 +173,11 @@ with tab1:
|
|
|
fig = px.pie(summary_df, values='Count', names='Grade',
|
|
|
color='Grade',
|
|
|
color_discrete_map={
|
|
|
- 'Abnormal': '#ef4444', # Red
|
|
|
- 'Empty_Bunch': '#94a3b8', # Gray
|
|
|
- 'Ripe': '#22c55e', # Green
|
|
|
- 'Underripe': '#eab308', # Yellow
|
|
|
- 'Unripe': '#3b82f6', # Blue
|
|
|
- 'Overripe': '#a855f7' # Purple
|
|
|
+ 'Ripe': '#22c55e', # Industrial Green
|
|
|
+ 'Underripe': '#fbbf24', # Industrial Orange
|
|
|
+ 'Unripe': '#3b82f6', # Industrial Blue
|
|
|
+ 'Abnormal': '#dc2626', # Critical Red
|
|
|
+ 'Empty_Bunch': '#64748b' # Waste Gray
|
|
|
},
|
|
|
hole=0.4)
|
|
|
fig.update_layout(margin=dict(t=0, b=0, l=0, r=0), height=300)
|
|
|
@@ -240,9 +255,11 @@ with tab2:
|
|
|
if not sum_df.empty:
|
|
|
fig_batch = px.bar(sum_df, x='Grade', y='Count', color='Grade',
|
|
|
color_discrete_map={
|
|
|
- 'Abnormal': '#ef4444',
|
|
|
- 'Empty_Bunch': '#94a3b8',
|
|
|
- 'Ripe': '#22c55e'
|
|
|
+ 'Ripe': '#22c55e',
|
|
|
+ 'Underripe': '#fbbf24',
|
|
|
+ 'Unripe': '#3b82f6',
|
|
|
+ 'Abnormal': '#dc2626',
|
|
|
+ 'Empty_Bunch': '#64748b'
|
|
|
})
|
|
|
fig_batch.update_layout(margin=dict(t=0, b=0, l=0, r=0), height=200, showlegend=False)
|
|
|
st.plotly_chart(fig_batch, width='stretch')
|