Explorar el Código

basic setup and trained data

Dr-Swopt hace 4 días
padre
commit
ebcabef0d7
Se han modificado 7 ficheros con 237 adiciones y 38 borrados
  1. 2 1
      .gitignore
  2. 86 37
      README.md
  3. 36 0
      Streamlit.md
  4. BIN
      best.pt
  5. 60 0
      demo_app.py
  6. 41 0
      main.py
  7. 12 0
      test_model.py

+ 2 - 1
.gitignore

@@ -38,4 +38,5 @@ wheels/
 .DS_Store
 Thumbs.db
 
-datasets
+datasets
+runs

+ 86 - 37
README.md

@@ -1,57 +1,106 @@
-# Palm Oil Fruit Ripeness Recognition Service
+## README.md
 
-A server-side application for identifying the ripeness of palm oil fruits using computer vision.
+# Palm Oil Ripeness Agent (n8n + YOLOv8)
 
-## Overview
+This project uses a custom-trained **YOLOv8** model to detect the ripeness of Palm Oil Fresh Fruit Bunches (FFB). It features a local Python FastAPI server that integrates into an **agentic n8n workflow**, storing results and embeddings in **MongoDB Atlas**.
 
-This project provides a recognition service for palm oil fruit ripeness. It is designed to assist in the automation of the harvesting process by providing accurate assessment of fruit maturity.
+## 🚀 Project Overview
 
-## Features
+1. **Model:** YOLOv8 Nano (custom-trained on Roboflow dataset).
+2. **Server:** FastAPI (Python) hosting the model for inference.
+3. **Database:** MongoDB Atlas (Vector Search for historical similarity).
+4. **Orchestration:** n8n (Agentic workflow for decision making).
 
-- **Ripeness Classification**: Uses advanced image recognition to categorize palm oil fruits (e.g., Unripe, Under-ripe, Ripe, Over-ripe).
-- **API Interface**: Built to serve mobile and web clients for real-time recognition.
+---
 
-## Getting Started
+## 🛠 Prerequisites
 
-### Prerequisites
+* Python 3.10+
+* n8n (Desktop or Self-hosted)
+* MongoDB Atlas Account (with Vector Search index enabled)
+* *Optional:* NVIDIA GPU with CUDA for faster training.
 
-- Python 3.8+
-- Virtual environment (recommended)
+---
 
-### Installation
+## 📦 Setup Instructions
 
-1. **Clone the repository**:
-   ```powershell
-   git clone <repository-url>
-   cd palm-oil-ai
-   ```
+### 1. Clone & Environment
 
-2. **Set up virtual environment**:
-   ```powershell
-   python -m venv venv
-   .\venv\Scripts\Activate.ps1
-   ```
+```bash
+git clone <your-repo-url>
+cd palm-oil-ai
+python -m venv venv
+# Windows: venv\Scripts\activate | Mac: source venv/bin/activate
+pip install -r requirements.txt
+
+```
+
+### 2. Dataset Preparation
+
+1. Download the dataset from [Roboflow Universe](https://www.google.com/search?q=https://universe.roboflow.com/assignment-vvtq7/oil-palm-ripeness/).
+2. Unzip into the `/datasets` folder.
+3. Ensure your `data.yaml` matches the local paths:
+```yaml
+train: ../datasets/train/images
+val: ../datasets/valid/images
+
+```
 
-3. **Install dependencies**:
-   ```powershell
-   pip install -r requirements.txt
-   ```
-   *(Note: Ensure requirements.txt is created/updated as the project develops)*
 
-### Running the Server
 
-To start the recognition service:
+### 3. Training the Model
+
+To train locally without hanging your PC, use the throttled script:
+
+```bash
+python train_script.py
 
-```powershell
-# Assuming a FastAPI/Uvicorn setup (common for such services)
-uvicorn main:app --reload
 ```
 
-## Dataset
+* **Outputs:** The best model will be saved at `runs/detect/train/weights/best.pt`.
+* **Move it:** Copy `best.pt` to the root directory for the server to use.
+
+### 4. Running the Inference Server
+
+```bash
+python main.py
+
+```
+
+The server will start at `http://localhost:8000`.
+
+* **Endpoint:** `POST /detect`
+* **Payload:** Multipart Form-data (Key: `file`, Value: `image.jpg`)
+
+---
+
+## 🤖 n8n Integration
+
+The n8n workflow follows this logic:
+
+1. **Trigger:** Receives image (Telegram/Webhook).
+2. **HTTP Request:** Sends image to `localhost:8000/detect`.
+3. **MongoDB Node:** Performs Vector Search using the returned embedding.
+4. **Agent Logic:** Final ripeness determination based on model confidence + DB similarity.
+
+---
+
+## 📂 Repository Structure
+
+```text
+├── datasets/           # Labeled images from Roboflow
+├── runs/               # YOLO training logs and weights
+├── main.py             # FastAPI Inference Server
+├── train_script.py     # Local training configuration
+├── best.pt             # The "Brain" (Trained Model)
+├── requirements.txt    # dependencies
+└── README.md           # You are here
+
+```
 
-This project uses the YOLOv8 dataset format for training and evaluation:
-- YOLOv8 Dataset: `oil palm ripeness.v5-roboflow-instant-2--eval-.yolov8`
+---
 
-## License
+## 📝 Future Improvements
 
-[License Type] - See LICENSE file for details.
+* Implement **CLIP** embeddings for higher-accuracy vector similarity.
+* Add a **Streamlit** dashboard for manual batch verification.

+ 36 - 0
Streamlit.md

@@ -0,0 +1,36 @@
+In AI, specifically computer vision, the **Confidence Threshold** is essentially the "Certainty Filter" for your model.
+
+Think of it as the AI’s level of "bravery" before it speaks up.
+
+### 1. What exactly is the Confidence Score?
+
+When your YOLO model looks at a palm oil bunch, it doesn't just say, "That's ripe." Instead, it performs complex math and says, *"I am **87% sure** this is a Ripe Bunch."* The sliding bar lets you decide the cutoff point for what the AI is allowed to show you:
+
+* **Low Confidence (e.g., 0.10 or 10%):** The AI will show you everything it *thinks* might be a fruit. This results in many "False Positives"—it might mistake a dark leaf or a shadow for an unripe fruit.
+* **High Confidence (e.g., 0.90 or 90%):** The AI will only show you objects it is absolutely certain about. This results in "False Negatives"—it might ignore a perfectly good ripe fruit because the lighting was slightly off and its certainty dropped to 85%.
+
+---
+
+### 2. How to explain it to your colleagues
+
+You can use this analogy:
+
+> "Imagine hiring a fruit grader. If you tell them to be **extremely strict** (High Confidence), they will only pick the absolute best fruits, but they might leave some good ones behind. If you tell them to be **relaxed** (Low Confidence), they will pick everything that looks remotely like a fruit, even if it's a rock or a leaf."
+
+---
+
+### 3. Finding the "Sweet Spot"
+
+In your R&D for Palm Oil, you’ll notice that:
+
+* **For Field Work:** You might want a **lower threshold (0.4 - 0.5)** to make sure you don't miss any bunches in the trees.
+* **For Quality Control (Mill/Grading):** You want a **higher threshold (0.7 - 0.8)** to ensure that only truly ripe fruit enters the processing line.
+
+### 4. Why it's in your Demo
+
+By including this slider in your Streamlit app, you are showing your colleagues that:
+
+1. The AI isn't a "magic box"; it works on **probabilities**.
+2. The system is **tunable**. You can adjust the "strictness" of the model based on the business needs of the plantation or the mill without rewriting any code.
+
+

BIN
best.pt


+ 60 - 0
demo_app.py

@@ -0,0 +1,60 @@
+import streamlit as st
+from ultralytics import YOLO
+from PIL import Image
+import numpy as np
+import io
+
+# --- Page Config ---
+st.set_page_config(page_title="Palm Oil Ripeness AI", layout="wide")
+st.title("🌴 Palm Oil FFB Ripeness Detector")
+st.markdown("### R&D Proof of Concept: Automated Maturity Grading")
+
+# --- Load Model (Cached for performance) ---
+@st.cache_resource
+def load_model():
+    return YOLO("best.pt")
+
+model = load_model()
+
+# --- Sidebar Controls ---
+st.sidebar.header("Settings")
+conf_threshold = st.sidebar.slider("Confidence Threshold", 0.1, 1.0, 0.5)
+
+# --- Image Upload ---
+uploaded_file = st.file_uploader("Drag and drop a Palm Oil FFB image here...", type=["jpg", "jpeg", "png"])
+
+if uploaded_file is not None:
+    # Convert uploaded file to PIL Image
+    image = Image.open(uploaded_file)
+    
+    # Layout: Original vs Predicted
+    col1, col2 = st.columns(2)
+    
+    with col1:
+        st.image(image, caption="Uploaded Image", use_container_width=True)
+        
+    with col2:
+        with st.spinner('Analyzing ripeness...'):
+            # Run Inference
+            results = model.predict(source=image, conf=conf_threshold)
+            
+            # The .plot() method returns a BGR numpy array with boxes drawn
+            annotated_img = results[0].plot()
+            
+            # Convert BGR (OpenCV format) to RGB (Streamlit/PIL format)
+            annotated_img_rgb = annotated_img[:, :, ::-1]
+            
+            st.image(annotated_img_rgb, caption="AI Analysis Result", use_container_width=True)
+
+    # --- Metrics Section ---
+    st.divider()
+    st.subheader("Analysis Summary")
+    
+    detections = results[0].boxes
+    if len(detections) > 0:
+        for box in detections:
+            label = model.names[int(box.cls)]
+            conf = float(box.conf)
+            st.success(f"**Detected:** {label} | **Confidence:** {conf:.2%}")
+    else:
+        st.warning("No fruit bunches detected. Try adjusting the confidence slider.")

+ 41 - 0
main.py

@@ -0,0 +1,41 @@
+from fastapi import FastAPI, File, UploadFile
+from ultralytics import YOLO
+import io
+import torch
+from PIL import Image
+
+app = FastAPI()
+
+# Load your custom trained model
+model = YOLO('best.pt') 
+
+@app.post("/detect")
+async def detect_ripeness(file: UploadFile = File(...)):
+    image_bytes = await file.read()
+    img = Image.open(io.BytesIO(image_bytes))
+
+    # 1. Run YOLO detection
+    results = model(img)
+
+    # 2. Extract Detections and the 'Embedding'
+    # We use the feature map from the model as a vector
+    detections = []
+    # Using the last hidden layer or a flattened feature map as a 'pseudo-vector'
+    # For a true vector, we'd usually use a CLIP model, but for now, we'll return detection data
+    for r in results:
+        for box in r.boxes:
+            detections.append({
+                "class": model.names[int(box.cls)],
+                "confidence": round(float(box.conf), 2),
+                "box": box.xyxy.tolist()[0]
+            })
+
+    return {
+        "status": "success", 
+        "data": detections,
+        "message": "Model processed palm oil FFB successfully"
+    }
+
+if __name__ == "__main__":
+    import uvicorn
+    uvicorn.run(app, host="0.0.0.0", port=8000)

+ 12 - 0
test_model.py

@@ -0,0 +1,12 @@
+from ultralytics import YOLO
+import cv2
+
+# 1. Load your newly trained model
+model = YOLO('best.pt')
+
+# 2. Run prediction on an image from your 'test' folder
+# Change 'path/to/your/test/image.jpg' to a real path
+results = model.predict(source='datasets/test/images/TestRipe-1-_jpg.rf.1d329242e7510efbc0c6a2c94bc43edb.jpg', save=True, conf=0.5)
+
+# 3. Check your 'runs/detect/predict' folder for the result image!
+print("Check runs/detect/predict for the annotated image.")