Emmanuel Frimpong Asante
commited on
Commit
·
edbf3ae
1
Parent(s):
b0de8e6
update space
Browse files- .idea/.gitignore +0 -8
- .idea/Generative_AI_with_poultry_disease_detection_system_v2.iml +0 -11
- .idea/git_toolbox_blame.xml +0 -6
- .idea/git_toolbox_prj.xml +0 -15
- .idea/inspectionProfiles/profiles_settings.xml +0 -6
- .idea/jsLibraryMappings.xml +0 -6
- .idea/misc.xml +0 -7
- .idea/modules.xml +0 -8
- .idea/vcs.xml +0 -6
- app.py +20 -8
- requirements.txt +2 -1
- routes/disease_detection.py +26 -33
- routes/health_dashboard.py +25 -0
- services/disease_detection_service.py +128 -28
- services/health_monitoring_service.py +17 -24
- services/image_preprocessing.py +26 -5
- templates/dashboard.html +21 -0
.idea/.gitignore
DELETED
@@ -1,8 +0,0 @@
|
|
1 |
-
# Default ignored files
|
2 |
-
/shelf/
|
3 |
-
/workspace.xml
|
4 |
-
# Editor-based HTTP Client requests
|
5 |
-
/httpRequests/
|
6 |
-
# Datasource local storage ignored files
|
7 |
-
/dataSources/
|
8 |
-
/dataSources.local.xml
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.idea/Generative_AI_with_poultry_disease_detection_system_v2.iml
DELETED
@@ -1,11 +0,0 @@
|
|
1 |
-
<?xml version="1.0" encoding="UTF-8"?>
|
2 |
-
<module type="PYTHON_MODULE" version="4">
|
3 |
-
<component name="NewModuleRootManager">
|
4 |
-
<content url="file://$MODULE_DIR$">
|
5 |
-
<excludeFolder url="file://$MODULE_DIR$/.venv" />
|
6 |
-
</content>
|
7 |
-
<orderEntry type="jdk" jdkName="Python 3.12 (Generative_AI_with_poultry_disease_detection_system_v2)" jdkType="Python SDK" />
|
8 |
-
<orderEntry type="sourceFolder" forTests="false" />
|
9 |
-
<orderEntry type="library" name="jquery-3.6.0" level="application" />
|
10 |
-
</component>
|
11 |
-
</module>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.idea/git_toolbox_blame.xml
DELETED
@@ -1,6 +0,0 @@
|
|
1 |
-
<?xml version="1.0" encoding="UTF-8"?>
|
2 |
-
<project version="4">
|
3 |
-
<component name="GitToolBoxBlameSettings">
|
4 |
-
<option name="version" value="2" />
|
5 |
-
</component>
|
6 |
-
</project>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.idea/git_toolbox_prj.xml
DELETED
@@ -1,15 +0,0 @@
|
|
1 |
-
<?xml version="1.0" encoding="UTF-8"?>
|
2 |
-
<project version="4">
|
3 |
-
<component name="GitToolBoxProjectSettings">
|
4 |
-
<option name="commitMessageIssueKeyValidationOverride">
|
5 |
-
<BoolValueOverride>
|
6 |
-
<option name="enabled" value="true" />
|
7 |
-
</BoolValueOverride>
|
8 |
-
</option>
|
9 |
-
<option name="commitMessageValidationEnabledOverride">
|
10 |
-
<BoolValueOverride>
|
11 |
-
<option name="enabled" value="true" />
|
12 |
-
</BoolValueOverride>
|
13 |
-
</option>
|
14 |
-
</component>
|
15 |
-
</project>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.idea/inspectionProfiles/profiles_settings.xml
DELETED
@@ -1,6 +0,0 @@
|
|
1 |
-
<component name="InspectionProjectProfileManager">
|
2 |
-
<settings>
|
3 |
-
<option name="USE_PROJECT_PROFILE" value="false" />
|
4 |
-
<version value="1.0" />
|
5 |
-
</settings>
|
6 |
-
</component>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.idea/jsLibraryMappings.xml
DELETED
@@ -1,6 +0,0 @@
|
|
1 |
-
<?xml version="1.0" encoding="UTF-8"?>
|
2 |
-
<project version="4">
|
3 |
-
<component name="JavaScriptLibraryMappings">
|
4 |
-
<file url="file://$PROJECT_DIR$" libraries="{jquery-3.6.0}" />
|
5 |
-
</component>
|
6 |
-
</project>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.idea/misc.xml
DELETED
@@ -1,7 +0,0 @@
|
|
1 |
-
<?xml version="1.0" encoding="UTF-8"?>
|
2 |
-
<project version="4">
|
3 |
-
<component name="Black">
|
4 |
-
<option name="sdkName" value="Python 3.12 (Generative_AI_with_poultry)" />
|
5 |
-
</component>
|
6 |
-
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.12 (Generative_AI_with_poultry_disease_detection_system_v2)" project-jdk-type="Python SDK" />
|
7 |
-
</project>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.idea/modules.xml
DELETED
@@ -1,8 +0,0 @@
|
|
1 |
-
<?xml version="1.0" encoding="UTF-8"?>
|
2 |
-
<project version="4">
|
3 |
-
<component name="ProjectModuleManager">
|
4 |
-
<modules>
|
5 |
-
<module fileurl="file://$PROJECT_DIR$/.idea/Generative_AI_with_poultry_disease_detection_system_v2.iml" filepath="$PROJECT_DIR$/.idea/Generative_AI_with_poultry_disease_detection_system_v2.iml" />
|
6 |
-
</modules>
|
7 |
-
</component>
|
8 |
-
</project>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.idea/vcs.xml
DELETED
@@ -1,6 +0,0 @@
|
|
1 |
-
<?xml version="1.0" encoding="UTF-8"?>
|
2 |
-
<project version="4">
|
3 |
-
<component name="VcsDirectoryMappings">
|
4 |
-
<mapping directory="" vcs="Git" />
|
5 |
-
</component>
|
6 |
-
</project>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
app.py
CHANGED
@@ -3,7 +3,6 @@
|
|
3 |
import os
|
4 |
import time
|
5 |
import logging
|
6 |
-
from datetime import datetime
|
7 |
from fastapi import FastAPI, Request, HTTPException, BackgroundTasks
|
8 |
from fastapi.templating import Jinja2Templates
|
9 |
from fastapi.responses import HTMLResponse
|
@@ -11,13 +10,22 @@ from fastapi.staticfiles import StaticFiles
|
|
11 |
import tensorflow as tf
|
12 |
from routes.authentication import auth_router
|
13 |
from routes.disease_detection import disease_router
|
14 |
-
from
|
|
|
|
|
15 |
|
16 |
# Setup logging
|
17 |
logging.basicConfig(level=logging.INFO)
|
18 |
logger = logging.getLogger(__name__)
|
19 |
|
20 |
-
#
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
21 |
gpu_devices = tf.config.list_physical_devices('GPU')
|
22 |
if gpu_devices:
|
23 |
logger.info(f"GPUs available: {len(gpu_devices)}")
|
@@ -32,14 +40,15 @@ templates = Jinja2Templates(directory="templates")
|
|
32 |
static_dir = "static"
|
33 |
if os.path.isdir(static_dir):
|
34 |
app.mount("/static", StaticFiles(directory=static_dir), name="static")
|
35 |
-
logger.info(
|
36 |
else:
|
37 |
logger.error("Static directory not found.")
|
38 |
raise HTTPException(status_code=500, detail="Static directory not found.")
|
39 |
|
40 |
-
# Include routers for authentication and
|
41 |
app.include_router(auth_router, prefix="/auth", tags=["Authentication"])
|
42 |
app.include_router(disease_router, prefix="/disease", tags=["Disease Detection"])
|
|
|
43 |
|
44 |
@app.get("/", response_class=HTMLResponse)
|
45 |
async def landing_page(request: Request):
|
@@ -47,7 +56,7 @@ async def landing_page(request: Request):
|
|
47 |
logger.info("Landing page accessed")
|
48 |
return templates.TemplateResponse("landing.html", {"request": request})
|
49 |
|
50 |
-
#
|
51 |
health_metrics = {
|
52 |
"weight_loss_percentage": 6,
|
53 |
"mortality_rate": 1,
|
@@ -55,15 +64,18 @@ health_metrics = {
|
|
55 |
}
|
56 |
|
57 |
def monitor_health():
|
58 |
-
"""
|
59 |
while True:
|
|
|
60 |
notifications = evaluate_health_data(health_metrics)
|
61 |
if notifications["notifications"]:
|
62 |
logger.info(f"Health Notifications: {notifications['notifications']}")
|
|
|
|
|
63 |
time.sleep(3600) # Run every hour
|
64 |
|
65 |
@app.on_event("startup")
|
66 |
async def startup_event():
|
67 |
-
"""
|
68 |
logger.info("Starting background health monitoring.")
|
69 |
BackgroundTasks().add_task(monitor_health)
|
|
|
3 |
import os
|
4 |
import time
|
5 |
import logging
|
|
|
6 |
from fastapi import FastAPI, Request, HTTPException, BackgroundTasks
|
7 |
from fastapi.templating import Jinja2Templates
|
8 |
from fastapi.responses import HTMLResponse
|
|
|
10 |
import tensorflow as tf
|
11 |
from routes.authentication import auth_router
|
12 |
from routes.disease_detection import disease_router
|
13 |
+
from routes.health_dashboard import dashboard_router
|
14 |
+
from services.health_monitoring_service import evaluate_health_data, get_health_alerts, send_alerts
|
15 |
+
from huggingface_hub import login
|
16 |
|
17 |
# Setup logging
|
18 |
logging.basicConfig(level=logging.INFO)
|
19 |
logger = logging.getLogger(__name__)
|
20 |
|
21 |
+
# Load Hugging Face Token
|
22 |
+
HF_TOKEN = os.getenv('HF_Token')
|
23 |
+
if HF_TOKEN:
|
24 |
+
login(token=HF_TOKEN, add_to_git_credential=True)
|
25 |
+
else:
|
26 |
+
logger.warning("Hugging Face token not found in environment variables.")
|
27 |
+
|
28 |
+
# Check GPU availability for TensorFlow
|
29 |
gpu_devices = tf.config.list_physical_devices('GPU')
|
30 |
if gpu_devices:
|
31 |
logger.info(f"GPUs available: {len(gpu_devices)}")
|
|
|
40 |
static_dir = "static"
|
41 |
if os.path.isdir(static_dir):
|
42 |
app.mount("/static", StaticFiles(directory=static_dir), name="static")
|
43 |
+
logger.info("Mounted static directory at /static")
|
44 |
else:
|
45 |
logger.error("Static directory not found.")
|
46 |
raise HTTPException(status_code=500, detail="Static directory not found.")
|
47 |
|
48 |
+
# Include routers for authentication, disease detection, and health dashboard
|
49 |
app.include_router(auth_router, prefix="/auth", tags=["Authentication"])
|
50 |
app.include_router(disease_router, prefix="/disease", tags=["Disease Detection"])
|
51 |
+
app.include_router(dashboard_router, prefix="/dashboard", tags=["Dashboard"])
|
52 |
|
53 |
@app.get("/", response_class=HTMLResponse)
|
54 |
async def landing_page(request: Request):
|
|
|
56 |
logger.info("Landing page accessed")
|
57 |
return templates.TemplateResponse("landing.html", {"request": request})
|
58 |
|
59 |
+
# health metrics for periodic monitoring
|
60 |
health_metrics = {
|
61 |
"weight_loss_percentage": 6,
|
62 |
"mortality_rate": 1,
|
|
|
64 |
}
|
65 |
|
66 |
def monitor_health():
|
67 |
+
"""Evaluate health data and log notifications if thresholds are crossed."""
|
68 |
while True:
|
69 |
+
# Evaluate health metrics and send alerts
|
70 |
notifications = evaluate_health_data(health_metrics)
|
71 |
if notifications["notifications"]:
|
72 |
logger.info(f"Health Notifications: {notifications['notifications']}")
|
73 |
+
# Optionally, send alerts to the farmer
|
74 |
+
send_alerts(notifications["notifications"], os.getenv("FARMER_EMAIL"))
|
75 |
time.sleep(3600) # Run every hour
|
76 |
|
77 |
@app.on_event("startup")
|
78 |
async def startup_event():
|
79 |
+
"""Initialize background health monitoring on startup."""
|
80 |
logger.info("Starting background health monitoring.")
|
81 |
BackgroundTasks().add_task(monitor_health)
|
requirements.txt
CHANGED
@@ -10,4 +10,5 @@ jinja2
|
|
10 |
python-multipart
|
11 |
numpy
|
12 |
pillow
|
13 |
-
tensorflow[and-cuda]~=2.9
|
|
|
|
10 |
python-multipart
|
11 |
numpy
|
12 |
pillow
|
13 |
+
tensorflow[and-cuda]~=2.9
|
14 |
+
huggingface_hub
|
routes/disease_detection.py
CHANGED
@@ -3,16 +3,11 @@
|
|
3 |
from fastapi import APIRouter, UploadFile, File, HTTPException
|
4 |
from fastapi.responses import JSONResponse
|
5 |
from PIL import Image
|
6 |
-
import io
|
7 |
import numpy as np
|
8 |
-
|
9 |
-
from services.disease_detection_service import
|
10 |
-
from services.health_monitoring_service import get_treatment_recommendation
|
11 |
-
|
12 |
|
13 |
disease_router = APIRouter()
|
14 |
-
model = load_disease_model()
|
15 |
-
|
16 |
|
17 |
|
18 |
@disease_router.post("/detect_disease", response_class=JSONResponse)
|
@@ -21,36 +16,34 @@ async def detect_disease(file: UploadFile = File(...)):
|
|
21 |
Detect disease from uploaded poultry fecal image and provide treatment suggestions.
|
22 |
|
23 |
Parameters:
|
24 |
-
file (UploadFile):
|
25 |
|
26 |
Returns:
|
27 |
-
JSONResponse: Prediction
|
28 |
"""
|
29 |
if file.content_type not in ["image/jpeg", "image/png"]:
|
30 |
raise HTTPException(status_code=400, detail="Invalid file type. Please upload a JPEG or PNG image.")
|
31 |
|
32 |
-
|
33 |
-
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
-
|
38 |
-
|
39 |
-
|
40 |
-
|
41 |
-
|
42 |
-
|
43 |
-
disease
|
44 |
-
|
45 |
-
|
46 |
-
|
47 |
-
|
48 |
-
|
49 |
-
|
50 |
-
"
|
51 |
"confidence": f"{confidence:.2f}%",
|
52 |
-
"
|
53 |
-
"
|
54 |
-
}
|
55 |
-
|
56 |
-
return JSONResponse(content=result)
|
|
|
3 |
from fastapi import APIRouter, UploadFile, File, HTTPException
|
4 |
from fastapi.responses import JSONResponse
|
5 |
from PIL import Image
|
|
|
6 |
import numpy as np
|
7 |
+
import io
|
8 |
+
from services.disease_detection_service import bot
|
|
|
|
|
9 |
|
10 |
disease_router = APIRouter()
|
|
|
|
|
11 |
|
12 |
|
13 |
@disease_router.post("/detect_disease", response_class=JSONResponse)
|
|
|
16 |
Detect disease from uploaded poultry fecal image and provide treatment suggestions.
|
17 |
|
18 |
Parameters:
|
19 |
+
file (UploadFile): Image file to analyze.
|
20 |
|
21 |
Returns:
|
22 |
+
JSONResponse: Prediction results including disease, status, confidence, recommendation, and details.
|
23 |
"""
|
24 |
if file.content_type not in ["image/jpeg", "image/png"]:
|
25 |
raise HTTPException(status_code=400, detail="Invalid file type. Please upload a JPEG or PNG image.")
|
26 |
|
27 |
+
try:
|
28 |
+
# Read and convert image to numpy array
|
29 |
+
image_data = await file.read()
|
30 |
+
image = Image.open(io.BytesIO(image_data)).convert("RGB")
|
31 |
+
image_np = np.array(image)
|
32 |
+
except Exception as e:
|
33 |
+
raise HTTPException(status_code=500, detail=f"Error loading image: {e}")
|
34 |
+
|
35 |
+
# Use bot to diagnose disease and generate a response
|
36 |
+
detailed_response, disease_name, status, recommendation, confidence = bot.diagnose_and_respond(image_np)
|
37 |
+
|
38 |
+
# Check if disease was detected successfully
|
39 |
+
if disease_name is None:
|
40 |
+
return JSONResponse(content={"error": "Unable to detect disease. Please try again."}, status_code=500)
|
41 |
+
|
42 |
+
# Return JSON response with prediction details
|
43 |
+
return JSONResponse(content={
|
44 |
+
"disease": disease_name,
|
45 |
+
"status": status,
|
46 |
"confidence": f"{confidence:.2f}%",
|
47 |
+
"recommendation": recommendation,
|
48 |
+
"details": detailed_response
|
49 |
+
})
|
|
|
|
routes/health_dashboard.py
ADDED
@@ -0,0 +1,25 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# routes/health_dashboard.py
|
2 |
+
|
3 |
+
from fastapi import APIRouter, Request
|
4 |
+
from fastapi.templating import Jinja2Templates
|
5 |
+
from datetime import datetime
|
6 |
+
|
7 |
+
from starlette.responses import HTMLResponse
|
8 |
+
|
9 |
+
dashboard_router = APIRouter()
|
10 |
+
templates = Jinja2Templates(directory="templates")
|
11 |
+
|
12 |
+
# Simulate a database or storage for alerts
|
13 |
+
ALERT_HISTORY = []
|
14 |
+
|
15 |
+
@dashboard_router.get("/dashboard", response_class=HTMLResponse)
|
16 |
+
async def health_dashboard(request: Request):
|
17 |
+
"""Render the health dashboard with current and historical alerts."""
|
18 |
+
# For demonstration purposes, we're displaying recent alerts.
|
19 |
+
# In a real implementation, fetch this data from a database.
|
20 |
+
context = {
|
21 |
+
"request": request,
|
22 |
+
"alerts": ALERT_HISTORY,
|
23 |
+
"last_updated": datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
24 |
+
}
|
25 |
+
return templates.TemplateResponse("dashboard.html", context)
|
services/disease_detection_service.py
CHANGED
@@ -1,35 +1,135 @@
|
|
1 |
# services/disease_detection_service.py
|
2 |
|
|
|
|
|
3 |
import tensorflow as tf
|
4 |
-
from
|
|
|
5 |
import numpy as np
|
6 |
-
from
|
7 |
-
import
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
8 |
|
9 |
-
#
|
10 |
gpus = tf.config.list_physical_devices('GPU')
|
11 |
if gpus:
|
12 |
-
|
13 |
-
|
14 |
-
|
15 |
-
|
16 |
-
|
17 |
-
|
18 |
-
|
19 |
-
|
20 |
-
|
21 |
-
|
22 |
-
|
23 |
-
|
24 |
-
|
25 |
-
|
26 |
-
|
27 |
-
|
28 |
-
|
29 |
-
|
30 |
-
|
31 |
-
|
32 |
-
|
33 |
-
|
34 |
-
|
35 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
# services/disease_detection_service.py
|
2 |
|
3 |
+
import os
|
4 |
+
import logging
|
5 |
import tensorflow as tf
|
6 |
+
from keras.models import load_model
|
7 |
+
import cv2
|
8 |
import numpy as np
|
9 |
+
from huggingface_hub import login
|
10 |
+
from pymongo import MongoClient
|
11 |
+
from transformers import AutoModelForCausalLM, AutoTokenizer
|
12 |
+
|
13 |
+
# Logging setup
|
14 |
+
logging.basicConfig(level=logging.INFO)
|
15 |
+
logger = logging.getLogger(__name__)
|
16 |
+
|
17 |
+
# Load environment variables
|
18 |
+
MONGO_URI = os.getenv("MONGO_URI")
|
19 |
+
HF_TOKEN = os.getenv('HF_Token')
|
20 |
+
db_client = MongoClient(MONGO_URI)
|
21 |
+
db = db_client.poultry_farm # MongoDB for record-keeping
|
22 |
+
|
23 |
+
# Hugging Face login for Llama 3.2
|
24 |
+
if HF_TOKEN:
|
25 |
+
login(token=HF_TOKEN, add_to_git_credential=True)
|
26 |
+
else:
|
27 |
+
logger.warning("Hugging Face token not found in environment variables.")
|
28 |
|
29 |
+
# Mixed precision and GPU configuration
|
30 |
gpus = tf.config.list_physical_devices('GPU')
|
31 |
if gpus:
|
32 |
+
for gpu in gpus:
|
33 |
+
tf.config.experimental.set_memory_growth(gpu, True)
|
34 |
+
from tensorflow.keras import mixed_precision
|
35 |
+
policy = mixed_precision.Policy('mixed_float16')
|
36 |
+
mixed_precision.set_global_policy(policy)
|
37 |
+
logger.info("Using mixed precision with GPU.")
|
38 |
+
else:
|
39 |
+
logger.info("Using CPU without mixed precision.")
|
40 |
+
|
41 |
+
# Load the disease detection model
|
42 |
+
try:
|
43 |
+
device_name = '/GPU:0' if gpus else '/CPU:0'
|
44 |
+
with tf.device(device_name):
|
45 |
+
disease_model = load_model('models/Final_Chicken_disease_model.h5', compile=True)
|
46 |
+
logger.info(f"Disease detection model loaded on {device_name}.")
|
47 |
+
except Exception as e:
|
48 |
+
logger.error(f"Error loading disease model: {e}")
|
49 |
+
|
50 |
+
# Llama 3.2 setup
|
51 |
+
model_name = "meta-llama/Llama-3.2-1B"
|
52 |
+
tokenizer = AutoTokenizer.from_pretrained(model_name)
|
53 |
+
llama_model = AutoModelForCausalLM.from_pretrained(model_name)
|
54 |
+
|
55 |
+
# Set a padding token if it doesn’t already exist
|
56 |
+
if tokenizer.pad_token is None:
|
57 |
+
tokenizer.add_special_tokens({'pad_token': '[PAD]'})
|
58 |
+
llama_model.resize_token_embeddings(len(tokenizer))
|
59 |
+
|
60 |
+
# Disease mapping and treatment guidelines
|
61 |
+
name_disease = {0: 'Coccidiosis', 1: 'Healthy', 2: 'New Castle Disease', 3: 'Salmonella'}
|
62 |
+
status_map = {0: 'Critical', 1: 'No issue', 2: 'Critical', 3: 'Critical'}
|
63 |
+
recommendations = {
|
64 |
+
0: 'Administer anti-coccidial medication, maintain hygiene, and ensure proper litter management.',
|
65 |
+
1: 'No treatment necessary; maintain regular monitoring and hygiene.',
|
66 |
+
2: 'Isolate affected birds and seek veterinary consultation for targeted treatment.',
|
67 |
+
3: 'Administer antibiotics as prescribed by a veterinarian and ensure biosecurity.'
|
68 |
+
}
|
69 |
+
|
70 |
+
class PoultryFarmBot:
|
71 |
+
def __init__(self, db):
|
72 |
+
self.db = db
|
73 |
+
|
74 |
+
def preprocess_image(self, image):
|
75 |
+
"""Preprocess image for model input."""
|
76 |
+
try:
|
77 |
+
image = cv2.resize(image, (224, 224))
|
78 |
+
image = np.expand_dims(image, axis=0) / 255.0 # Normalize and add batch dimension
|
79 |
+
logger.info("Image preprocessed successfully.")
|
80 |
+
return image
|
81 |
+
except Exception as e:
|
82 |
+
logger.error(f"Error in image preprocessing: {e}")
|
83 |
+
return None
|
84 |
+
|
85 |
+
def predict_disease(self, image):
|
86 |
+
"""Predict the disease from a preprocessed image and return detailed information."""
|
87 |
+
preprocessed_image = self.preprocess_image(image)
|
88 |
+
if preprocessed_image is None:
|
89 |
+
return "Image preprocessing failed.", None, None, None
|
90 |
+
|
91 |
+
try:
|
92 |
+
prediction = disease_model.predict(preprocessed_image)
|
93 |
+
predicted_class = np.argmax(prediction, axis=1).item()
|
94 |
+
disease_name = name_disease.get(predicted_class, "Unknown disease")
|
95 |
+
disease_status = status_map.get(predicted_class, "Unknown status")
|
96 |
+
recommendation = recommendations.get(predicted_class, "No recommendation available")
|
97 |
+
confidence = prediction[0][predicted_class] * 100
|
98 |
+
logger.info(f"Predicted: {disease_name} with {confidence:.2f}% confidence.")
|
99 |
+
|
100 |
+
# Generate detailed response using Llama 3.2
|
101 |
+
detailed_response = self.generate_detailed_response(disease_name, disease_status, recommendation)
|
102 |
+
return detailed_response, disease_name, disease_status, recommendation, confidence
|
103 |
+
except Exception as e:
|
104 |
+
logger.error(f"Prediction error: {e}")
|
105 |
+
return "Prediction failed.", None, None, None, None
|
106 |
+
|
107 |
+
def generate_detailed_response(self, disease_name, status, recommendation):
|
108 |
+
"""Generate a detailed response using Llama 3.2 model for additional disease insights."""
|
109 |
+
prompt = (
|
110 |
+
f"The disease detected is {disease_name}, classified as {status}. "
|
111 |
+
f"Recommended action: {recommendation}. "
|
112 |
+
f"Here is more information about {disease_name}, including its causes, symptoms, and treatments "
|
113 |
+
"for effective management on a poultry farm."
|
114 |
+
)
|
115 |
+
response = self.llama_response(prompt)
|
116 |
+
return response.replace(prompt, "").strip()
|
117 |
+
|
118 |
+
def llama_response(self, prompt):
|
119 |
+
"""Generate a response from the Llama 3.2 model based on a given prompt."""
|
120 |
+
try:
|
121 |
+
inputs = tokenizer(prompt, return_tensors="pt", max_length=150, truncation=True, padding=True)
|
122 |
+
outputs = llama_model.generate(inputs["input_ids"], max_length=150, do_sample=True, temperature=0.7)
|
123 |
+
return tokenizer.decode(outputs[0], skip_special_tokens=True)
|
124 |
+
except Exception as e:
|
125 |
+
logger.error(f"Llama model response error: {e}")
|
126 |
+
return "Error generating detailed response."
|
127 |
+
|
128 |
+
def diagnose_and_respond(self, image):
|
129 |
+
"""Diagnose the disease and respond with detailed information and recommendations."""
|
130 |
+
if image is None or image.size == 0:
|
131 |
+
return "Please provide a valid poultry fecal image.", None, None, None, None
|
132 |
+
return self.predict_disease(image)
|
133 |
+
|
134 |
+
# Initialize the bot instance
|
135 |
+
bot = PoultryFarmBot(db)
|
services/health_monitoring_service.py
CHANGED
@@ -2,38 +2,36 @@
|
|
2 |
|
3 |
from datetime import datetime, timedelta
|
4 |
from typing import Dict, List
|
5 |
-
|
6 |
from services.email_notification_service import send_email
|
7 |
|
8 |
# Dictionary to map diseases to suggested treatments
|
9 |
TREATMENT_GUIDELINES = {
|
10 |
"Healthy": {
|
11 |
"notification": "The flock is healthy.",
|
12 |
-
"suggestion": "
|
13 |
-
},
|
14 |
-
"Infected": {
|
15 |
-
"notification": "Disease detected in poultry.",
|
16 |
-
"suggestion": "Isolate affected birds and administer prescribed antibiotics. Consult a veterinarian for further assistance."
|
17 |
},
|
18 |
"Coccidiosis": {
|
19 |
"notification": "Coccidiosis symptoms detected.",
|
20 |
-
"suggestion": "Administer
|
21 |
},
|
22 |
-
"
|
23 |
-
"notification": "
|
24 |
-
"suggestion": "
|
|
|
|
|
|
|
|
|
25 |
}
|
26 |
}
|
27 |
|
28 |
-
# Notification
|
29 |
HEALTH_THRESHOLDS = {
|
30 |
"weight_loss_percentage": 5, # Alert if weight loss > 5% in a week
|
31 |
-
"mortality_rate": 2,
|
32 |
"reduced_feed_intake_percentage": 10 # Alert if feed intake drops > 10%
|
33 |
}
|
34 |
|
35 |
-
|
36 |
-
def get_health_alerts(health_metrics: dict) -> Dict[str, str]:
|
37 |
"""
|
38 |
Check health metrics and return alerts if thresholds are crossed.
|
39 |
|
@@ -41,7 +39,7 @@ def get_health_alerts(health_metrics: dict) -> Dict[str, str]:
|
|
41 |
health_metrics (dict): Health metrics like weight loss, mortality rate, and feed intake.
|
42 |
|
43 |
Returns:
|
44 |
-
|
45 |
"""
|
46 |
alerts = []
|
47 |
if health_metrics.get("weight_loss_percentage", 0) > HEALTH_THRESHOLDS["weight_loss_percentage"]:
|
@@ -55,16 +53,13 @@ def get_health_alerts(health_metrics: dict) -> Dict[str, str]:
|
|
55 |
|
56 |
return alerts
|
57 |
|
58 |
-
|
59 |
-
def send_alerts(alerts: list, farmer_email: str):
|
60 |
"""Send alert notifications to the farmer's email."""
|
61 |
if alerts:
|
62 |
message = "\n".join(alerts)
|
63 |
subject = "Poultry Health Alert"
|
64 |
send_email(farmer_email, subject, message)
|
65 |
|
66 |
-
|
67 |
-
|
68 |
def get_treatment_recommendation(disease: str) -> Dict[str, str]:
|
69 |
"""Get notification and treatment suggestion based on disease detected."""
|
70 |
return TREATMENT_GUIDELINES.get(disease, {
|
@@ -72,8 +67,7 @@ def get_treatment_recommendation(disease: str) -> Dict[str, str]:
|
|
72 |
"suggestion": "Consult a veterinarian for diagnosis and treatment."
|
73 |
})
|
74 |
|
75 |
-
|
76 |
-
def evaluate_health_data(health_metrics: dict) -> dict[str, list[str]]:
|
77 |
"""
|
78 |
Evaluate health data and trigger alerts if thresholds are crossed.
|
79 |
|
@@ -81,7 +75,7 @@ def evaluate_health_data(health_metrics: dict) -> dict[str, list[str]]:
|
|
81 |
health_metrics (dict): Health metrics like weight loss, mortality rate, and feed intake.
|
82 |
|
83 |
Returns:
|
84 |
-
dict: Notifications
|
85 |
"""
|
86 |
notifications = []
|
87 |
|
@@ -89,8 +83,7 @@ def evaluate_health_data(health_metrics: dict) -> dict[str, list[str]]:
|
|
89 |
notifications.append("Significant weight loss detected. Monitor feed quality and check for underlying issues.")
|
90 |
|
91 |
if health_metrics.get("mortality_rate", 0) > HEALTH_THRESHOLDS["mortality_rate"]:
|
92 |
-
notifications.append(
|
93 |
-
"Increased mortality rate. Review flock conditions and investigate potential health issues.")
|
94 |
|
95 |
if health_metrics.get("reduced_feed_intake_percentage", 0) > HEALTH_THRESHOLDS["reduced_feed_intake_percentage"]:
|
96 |
notifications.append("Feed intake has dropped. Check for signs of illness or environmental stress.")
|
|
|
2 |
|
3 |
from datetime import datetime, timedelta
|
4 |
from typing import Dict, List
|
|
|
5 |
from services.email_notification_service import send_email
|
6 |
|
7 |
# Dictionary to map diseases to suggested treatments
|
8 |
TREATMENT_GUIDELINES = {
|
9 |
"Healthy": {
|
10 |
"notification": "The flock is healthy.",
|
11 |
+
"suggestion": "No treatment necessary; maintain regular monitoring and hygiene."
|
|
|
|
|
|
|
|
|
12 |
},
|
13 |
"Coccidiosis": {
|
14 |
"notification": "Coccidiosis symptoms detected.",
|
15 |
+
"suggestion": "Administer anti-coccidial medication, maintain hygiene, and ensure proper litter management."
|
16 |
},
|
17 |
+
"New Castle Disease": {
|
18 |
+
"notification": "New Castle Disease suspected.",
|
19 |
+
"suggestion": "Isolate affected birds and seek veterinary consultation for targeted treatment."
|
20 |
+
},
|
21 |
+
"Salmonella": {
|
22 |
+
"notification": "Salmonella infection detected.",
|
23 |
+
"suggestion": "Administer antibiotics as prescribed by a veterinarian and ensure biosecurity."
|
24 |
}
|
25 |
}
|
26 |
|
27 |
+
# Notification thresholds for health metrics
|
28 |
HEALTH_THRESHOLDS = {
|
29 |
"weight_loss_percentage": 5, # Alert if weight loss > 5% in a week
|
30 |
+
"mortality_rate": 2, # Alert if mortality rate > 2% in a week
|
31 |
"reduced_feed_intake_percentage": 10 # Alert if feed intake drops > 10%
|
32 |
}
|
33 |
|
34 |
+
def get_health_alerts(health_metrics: dict) -> List[str]:
|
|
|
35 |
"""
|
36 |
Check health metrics and return alerts if thresholds are crossed.
|
37 |
|
|
|
39 |
health_metrics (dict): Health metrics like weight loss, mortality rate, and feed intake.
|
40 |
|
41 |
Returns:
|
42 |
+
list: Notifications and suggestions if any health issues are detected.
|
43 |
"""
|
44 |
alerts = []
|
45 |
if health_metrics.get("weight_loss_percentage", 0) > HEALTH_THRESHOLDS["weight_loss_percentage"]:
|
|
|
53 |
|
54 |
return alerts
|
55 |
|
56 |
+
def send_alerts(alerts: List[str], farmer_email: str):
|
|
|
57 |
"""Send alert notifications to the farmer's email."""
|
58 |
if alerts:
|
59 |
message = "\n".join(alerts)
|
60 |
subject = "Poultry Health Alert"
|
61 |
send_email(farmer_email, subject, message)
|
62 |
|
|
|
|
|
63 |
def get_treatment_recommendation(disease: str) -> Dict[str, str]:
|
64 |
"""Get notification and treatment suggestion based on disease detected."""
|
65 |
return TREATMENT_GUIDELINES.get(disease, {
|
|
|
67 |
"suggestion": "Consult a veterinarian for diagnosis and treatment."
|
68 |
})
|
69 |
|
70 |
+
def evaluate_health_data(health_metrics: dict) -> Dict[str, List[str]]:
|
|
|
71 |
"""
|
72 |
Evaluate health data and trigger alerts if thresholds are crossed.
|
73 |
|
|
|
75 |
health_metrics (dict): Health metrics like weight loss, mortality rate, and feed intake.
|
76 |
|
77 |
Returns:
|
78 |
+
dict: Notifications if any health issues are detected.
|
79 |
"""
|
80 |
notifications = []
|
81 |
|
|
|
83 |
notifications.append("Significant weight loss detected. Monitor feed quality and check for underlying issues.")
|
84 |
|
85 |
if health_metrics.get("mortality_rate", 0) > HEALTH_THRESHOLDS["mortality_rate"]:
|
86 |
+
notifications.append("Increased mortality rate. Review flock conditions and investigate potential health issues.")
|
|
|
87 |
|
88 |
if health_metrics.get("reduced_feed_intake_percentage", 0) > HEALTH_THRESHOLDS["reduced_feed_intake_percentage"]:
|
89 |
notifications.append("Feed intake has dropped. Check for signs of illness or environmental stress.")
|
services/image_preprocessing.py
CHANGED
@@ -2,6 +2,11 @@
|
|
2 |
|
3 |
from PIL import Image
|
4 |
import numpy as np
|
|
|
|
|
|
|
|
|
|
|
5 |
|
6 |
|
7 |
def preprocess_image(image: Image.Image, target_size=(224, 224)):
|
@@ -15,8 +20,24 @@ def preprocess_image(image: Image.Image, target_size=(224, 224)):
|
|
15 |
Returns:
|
16 |
np.ndarray: Preprocessed image ready for model input.
|
17 |
"""
|
18 |
-
|
19 |
-
|
20 |
-
|
21 |
-
|
22 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2 |
|
3 |
from PIL import Image
|
4 |
import numpy as np
|
5 |
+
import logging
|
6 |
+
|
7 |
+
# Setup logging
|
8 |
+
logging.basicConfig(level=logging.INFO)
|
9 |
+
logger = logging.getLogger(__name__)
|
10 |
|
11 |
|
12 |
def preprocess_image(image: Image.Image, target_size=(224, 224)):
|
|
|
20 |
Returns:
|
21 |
np.ndarray: Preprocessed image ready for model input.
|
22 |
"""
|
23 |
+
try:
|
24 |
+
# Ensure the image has 3 color channels (RGB)
|
25 |
+
image = image.convert("RGB")
|
26 |
+
logger.info("Image converted to RGB.")
|
27 |
+
|
28 |
+
# Resize image to the target size
|
29 |
+
image = image.resize(target_size)
|
30 |
+
logger.info(f"Image resized to {target_size}.")
|
31 |
+
|
32 |
+
# Normalize the image pixels to [0, 1] range
|
33 |
+
image_array = np.array(image) / 255.0
|
34 |
+
logger.info("Image normalized to [0, 1] range.")
|
35 |
+
|
36 |
+
# Add a batch dimension to the image array
|
37 |
+
image_array = np.expand_dims(image_array, axis=0)
|
38 |
+
logger.info("Batch dimension added to image array.")
|
39 |
+
|
40 |
+
return image_array
|
41 |
+
except Exception as e:
|
42 |
+
logger.error(f"Error preprocessing image: {e}")
|
43 |
+
return None
|
templates/dashboard.html
ADDED
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<!-- templates/dashboard.html -->
|
2 |
+
|
3 |
+
<!DOCTYPE html>
|
4 |
+
<html lang="en">
|
5 |
+
<head>
|
6 |
+
<meta charset="UTF-8">
|
7 |
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
8 |
+
<title>Poultry Health Dashboard</title>
|
9 |
+
</head>
|
10 |
+
<body>
|
11 |
+
<h1>Poultry Health Dashboard</h1>
|
12 |
+
<p>Last Updated: {{ last_updated }}</p>
|
13 |
+
|
14 |
+
<h2>Recent Alerts</h2>
|
15 |
+
<ul>
|
16 |
+
{% for alert in alerts %}
|
17 |
+
<li>{{ alert }}</li>
|
18 |
+
{% endfor %}
|
19 |
+
</ul>
|
20 |
+
</body>
|
21 |
+
</html>
|