Spaces:
Runtime error
Runtime error
Initial commit of healthcare AI dashboard
Browse files- .env.example +5 -0
- .gitattributes +2 -0
- .gitignore +14 -0
- .streamlit/secrets.toml +3 -0
- FINAL_MODEL.keras +3 -0
- FINAL_MODEL.zip +3 -0
- LICENSE.md +21 -0
- PROJECT_OVERVIEW.md +70 -0
- app.py +505 -0
- covid_from_website.png +0 -0
- logistic_regression_model.pkl +3 -0
- normal_from_website.jpeg +0 -0
- pneumoni_from_Website.png +0 -0
- requirements.txt +14 -0
- utils/ai71_utils.py +34 -0
.env.example
ADDED
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
AI71_API_KEY= # Get at https://marketplace.ai71.ai/api-keys
|
2 |
+
ROBOFLOW_API_KEY= # Get at https://app.roboflow.com/
|
3 |
+
|
4 |
+
INFERENCE_API_URL=
|
5 |
+
INFERENCE_API_KEY=
|
.gitattributes
CHANGED
@@ -33,3 +33,5 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
|
33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
|
|
|
|
|
33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
36 |
+
FINAL_MODEL.keras filter=lfs diff=lfs merge=lfs -text
|
37 |
+
FINAL_MODEL.zip filter=lfs diff=lfs merge=lfs -text
|
.gitignore
ADDED
@@ -0,0 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
*.pyc
|
2 |
+
__pycache__/
|
3 |
+
db.sqlite3
|
4 |
+
.env
|
5 |
+
.conda/
|
6 |
+
.venv/
|
7 |
+
.vscode/
|
8 |
+
|
9 |
+
backend/.hc_backend
|
10 |
+
backend/api/migrations/*
|
11 |
+
|
12 |
+
frontend/.hc_frontend
|
13 |
+
.aider*
|
14 |
+
frontend/.streamlit/secrets.toml
|
.streamlit/secrets.toml
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
INFERENCE_API_URL = "https://detect.roboflow.com"
|
2 |
+
INFERENCE_API_KEY = "LSbJ0tl3WTLn4Aqar0Sp"
|
3 |
+
AI71_API_KEY = "api71-api-1a7b704d-f950-4507-95cc-c26ce3457d75"
|
FINAL_MODEL.keras
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:dff0eafd711bccd941d3cca9bff29202c4b4e40a4ef2d814986137d997ccfd13
|
3 |
+
size 228465191
|
FINAL_MODEL.zip
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:f52580d34f88e9e82e55de047acdbf8df2ed2e8c3caf54240d1dac4e8fcbc7f3
|
3 |
+
size 185936347
|
LICENSE.md
ADDED
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
MIT License
|
2 |
+
|
3 |
+
Copyright (c) [2024] [Rust Rover]
|
4 |
+
|
5 |
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6 |
+
of this software and associated documentation files (the "Software"), to deal
|
7 |
+
in the Software without restriction, including without limitation the rights
|
8 |
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9 |
+
copies of the Software, and to permit persons to whom the Software is
|
10 |
+
furnished to do so, subject to the following conditions:
|
11 |
+
|
12 |
+
The above copyright notice and this permission notice shall be included in all
|
13 |
+
copies or substantial portions of the Software.
|
14 |
+
|
15 |
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16 |
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17 |
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18 |
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19 |
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20 |
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21 |
+
SOFTWARE.
|
PROJECT_OVERVIEW.md
ADDED
@@ -0,0 +1,70 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
### Project Overview - Medi Scape
|
2 |
+
Medi Scape is a healthcare application that allows users to upload prescription images, which are then processed to extract and analyze the text. The key components include image processing, text recognition, and context understanding using machine learning models.
|
3 |
+
|
4 |
+
### Workflow Steps
|
5 |
+
|
6 |
+
1. **User Uploads Prescription Image**
|
7 |
+
- **Frontend**: User uploads an image via the Streamlit app.
|
8 |
+
- **Action**: The image is sent to the backend for processing.
|
9 |
+
|
10 |
+
2. **Backend Receives Image**
|
11 |
+
- **Backend**: The Django server receives the uploaded image.
|
12 |
+
- **Action**: The backend calls the Doctor's Handwriting Detection API to detect handwriting.
|
13 |
+
|
14 |
+
3. **Doctor's Handwriting Detection API**
|
15 |
+
- **API**: The uploaded image is processed to detect bounding boxes and classify the handwritten text.
|
16 |
+
- **Action**: The API returns the bounding boxes and classified text to the backend.
|
17 |
+
|
18 |
+
4. **Backend Calls OCR Model**
|
19 |
+
- **Backend**: Extract bounding boxes and classes from the API response.
|
20 |
+
- **Action**: Send the bounding boxes to an OCR model to convert the handwriting to text.
|
21 |
+
|
22 |
+
5. **Backend Processes OCR Text**
|
23 |
+
- **Backend**: The OCR model returns the detected text.
|
24 |
+
- **Action**: Send the detected text to the Falcon LLM model via the AI71 API for context understanding.
|
25 |
+
|
26 |
+
6. **Falcon LLM Model via AI71 API**
|
27 |
+
- **API**: The detected text is processed for context understanding by the Falcon LLM model.
|
28 |
+
- **Action**: The processed text is returned to the backend.
|
29 |
+
|
30 |
+
7. **Backend Sends Final Results to Frontend**
|
31 |
+
- **Backend**: The final processed text received from the AI71 API.
|
32 |
+
- **Action**: Send the final results back to the frontend (Streamlit app) for user display.
|
33 |
+
|
34 |
+
### Checklist / To-Do List
|
35 |
+
|
36 |
+
1. **Frontend Development**:
|
37 |
+
- [x] Implement image upload functionality in the Streamlit app.
|
38 |
+
- [x] Ensure the image is correctly sent to the backend.
|
39 |
+
|
40 |
+
2. **Backend Development**:
|
41 |
+
- [x] Set up Django server to receive and handle uploaded images.
|
42 |
+
- [x] Integrate Doctor's Handwriting Detection API to process the images.
|
43 |
+
- [x] Extract bounding boxes and classes from the API response.
|
44 |
+
- [x] Integrate OCR model to convert bounding boxes to text.
|
45 |
+
- [x] Process the OCR text using the Falcon LLM model via AI71 API.
|
46 |
+
- [x] Send the final processed text to the frontend.
|
47 |
+
|
48 |
+
3. **API Integration**:
|
49 |
+
- [x] Ensure proper integration of the Doctor's Handwriting Detection API.
|
50 |
+
- [x] Ensure proper integration of the Falcon LLM model via AI71 API.
|
51 |
+
- [x] Implement fallback solutions (like iframes) for Streamlit if API integration fails.
|
52 |
+
|
53 |
+
4. **Deployment**:
|
54 |
+
- [x] Deploy the handwriting classification API on HuggingFace.
|
55 |
+
- [x] Deploy the Streamlit app and ensure it is linked correctly with the backend.
|
56 |
+
- [x] Hide the HuggingFace name in the deployed interface for security reasons.
|
57 |
+
|
58 |
+
5. **Testing and Validation**:
|
59 |
+
- [x] Test the entire workflow from image upload to final text display.
|
60 |
+
- [x] Validate the accuracy of the handwriting detection and OCR models.
|
61 |
+
- [x] Ensure the Falcon LLM model provides accurate and contextually relevant text analysis.
|
62 |
+
|
63 |
+
6. **Documentation and Communication**:
|
64 |
+
- [x] Document the workflow and API integration steps.
|
65 |
+
- [x] Communicate with the team leader for the exact prompt required for the healthcare automation part.
|
66 |
+
- [x] Keep the team updated on progress and any issues encountered.
|
67 |
+
|
68 |
+
7. **Deadline Management**:
|
69 |
+
- [x] Ensure all tasks are completed by the deadline (August 6).
|
70 |
+
|
app.py
ADDED
@@ -0,0 +1,505 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import streamlit as st
|
2 |
+
import requests
|
3 |
+
from utils.ai71_utils import get_ai71_response
|
4 |
+
from datetime import datetime
|
5 |
+
import cv2
|
6 |
+
import numpy as np
|
7 |
+
from PIL import Image
|
8 |
+
import supervision as sv
|
9 |
+
import matplotlib.pyplot as plt
|
10 |
+
import io
|
11 |
+
import os
|
12 |
+
from inference_sdk import InferenceHTTPClient
|
13 |
+
from bs4 import BeautifulSoup
|
14 |
+
import tensorflow as tf
|
15 |
+
import pandas as pd
|
16 |
+
from sklearn.feature_extraction.text import CountVectorizer
|
17 |
+
from sklearn.model_selection import train_test_split
|
18 |
+
from sklearn.linear_model import LogisticRegression
|
19 |
+
from sklearn.metrics import accuracy_score, classification_report
|
20 |
+
import nltk
|
21 |
+
import re
|
22 |
+
from nltk.tokenize import word_tokenize
|
23 |
+
from nltk.corpus import stopwords
|
24 |
+
|
25 |
+
# Download NLTK data (only needs to be done once)
|
26 |
+
nltk.download('punkt')
|
27 |
+
nltk.download('stopwords')
|
28 |
+
nltk.download('wordnet')
|
29 |
+
|
30 |
+
# --- Preprocess text function (moved outside session state) ---
|
31 |
+
def preprocess_text(text):
|
32 |
+
# Convert to lowercase
|
33 |
+
text = text.lower()
|
34 |
+
|
35 |
+
cleaned_text = re.sub(r'[^a-zA-Z0-9\s\,]', ' ', text)
|
36 |
+
# Tokenize text
|
37 |
+
tokens = word_tokenize(cleaned_text)
|
38 |
+
|
39 |
+
# Remove stop words
|
40 |
+
stop_words = set(stopwords.words('english'))
|
41 |
+
tokens = [word for word in tokens if word not in stop_words]
|
42 |
+
|
43 |
+
# Rejoin tokens into a single string
|
44 |
+
cleaned_text = ' '.join(tokens)
|
45 |
+
|
46 |
+
return cleaned_text
|
47 |
+
|
48 |
+
st.title("Medi Scape Dashboard")
|
49 |
+
|
50 |
+
# --- Session State Initialization ---
|
51 |
+
if 'disease_model' not in st.session_state:
|
52 |
+
try:
|
53 |
+
model_path = 'FINAL_MODEL.zip'
|
54 |
+
print(f"Attempting to load disease model from: {model_path}")
|
55 |
+
print(f"Model file exists: {os.path.exists(model_path)}")
|
56 |
+
st.session_state.disease_model = tf.keras.models.load_model(model_path)
|
57 |
+
print("Disease model loaded successfully!")
|
58 |
+
except FileNotFoundError:
|
59 |
+
st.error("Disease classification model not found. Please ensure 'FINAL_MODEL.zip' is in the same directory as this app.")
|
60 |
+
st.session_state.disease_model = None
|
61 |
+
|
62 |
+
# Load the vectorizer
|
63 |
+
if 'vectorizer' not in st.session_state:
|
64 |
+
try:
|
65 |
+
vectorizer_path = "vectorizer.pkl"
|
66 |
+
print(f"Attempting to load vectorizer from: {vectorizer_path}")
|
67 |
+
print(f"Vectorizer file exists: {os.path.exists(vectorizer_path)}")
|
68 |
+
st.session_state.vectorizer = pd.read_pickle(vectorizer_path)
|
69 |
+
print("Vectorizer loaded successfully!")
|
70 |
+
except FileNotFoundError:
|
71 |
+
st.error("Vectorizer file not found. Please ensure 'vectorizer.pkl' is in the same directory as this app.")
|
72 |
+
st.session_state.vectorizer = None
|
73 |
+
|
74 |
+
if 'model_llm' not in st.session_state:
|
75 |
+
# --- Load pre-trained model and vectorizer ---
|
76 |
+
st.session_state.model_llm = LogisticRegression()
|
77 |
+
try:
|
78 |
+
llm_model_path = "logistic_regression_model.pkl"
|
79 |
+
print(f"Attempting to load LLM model from: {llm_model_path}")
|
80 |
+
print(f"LLM Model file exists: {os.path.exists(llm_model_path)}")
|
81 |
+
st.session_state.model_llm = pd.read_pickle(llm_model_path)
|
82 |
+
print("LLM model loaded successfully!")
|
83 |
+
except FileNotFoundError:
|
84 |
+
st.error("LLM model file not found. Please ensure 'logistic_regression_model.pkl' is in the same directory.")
|
85 |
+
st.session_state.model_llm = None
|
86 |
+
|
87 |
+
# Load datasets (only for reference, not used for training)
|
88 |
+
dataset_1 = pd.read_csv("Symptoms_Detection/training_data.csv")
|
89 |
+
dataset_2 = pd.read_csv("Symptoms_Detection/Symptom2Disease.csv")
|
90 |
+
|
91 |
+
# Create symptoms_text column (only for reference, not used for training)
|
92 |
+
dataset_1['symptoms_text'] = dataset_1.apply(lambda row: ','.join([col for col in dataset_1.columns if row[col] == 1]), axis=1)
|
93 |
+
final_dataset = pd.DataFrame(dataset_1[["prognosis", "symptoms_text"]])
|
94 |
+
final_dataset.columns = ['label', 'text']
|
95 |
+
|
96 |
+
# Combine datasets (only for reference, not used for training)
|
97 |
+
df_combined = pd.concat([final_dataset, dataset_2[['label', 'text']]], axis=0, ignore_index=True)
|
98 |
+
|
99 |
+
# Store in session state (only for reference, not used for training)
|
100 |
+
st.session_state.df_combined = df_combined
|
101 |
+
# --- End of Session State Initialization ---
|
102 |
+
|
103 |
+
# Load the disease classification model
|
104 |
+
try:
|
105 |
+
disease_model = tf.keras.models.load_model('FINAL_MODEL.zip')
|
106 |
+
except FileNotFoundError:
|
107 |
+
st.error("Disease classification model not found. Please ensure 'FINAL_MODEL.zip' is in the same directory as this app.")
|
108 |
+
disease_model = None
|
109 |
+
|
110 |
+
# Sidebar Navigation
|
111 |
+
st.sidebar.title("Navigation")
|
112 |
+
page = st.sidebar.radio("Go to", ["Home", "AI Chatbot Diagnosis", "Drug Identification", "Disease Detection", "Outbreak Alert"])
|
113 |
+
|
114 |
+
# Access secrets using st.secrets
|
115 |
+
if "INFERENCE_API_URL" not in st.secrets or "INFERENCE_API_KEY" not in st.secrets:
|
116 |
+
st.error("Please make sure to set your secrets in the Streamlit secrets settings.")
|
117 |
+
else:
|
118 |
+
# Initialize the Inference Client
|
119 |
+
CLIENT = InferenceHTTPClient(
|
120 |
+
api_url=st.secrets["INFERENCE_API_URL"],
|
121 |
+
api_key=st.secrets["INFERENCE_API_KEY"]
|
122 |
+
)
|
123 |
+
|
124 |
+
# Function to preprocess the image
|
125 |
+
def preprocess_image(image_path):
|
126 |
+
# Load the image
|
127 |
+
image = cv2.imread(image_path)
|
128 |
+
|
129 |
+
# Convert to grayscale
|
130 |
+
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
|
131 |
+
|
132 |
+
# Remove noise
|
133 |
+
blurred = cv2.GaussianBlur(gray, (5, 5), 0)
|
134 |
+
|
135 |
+
# Thresholding/Binarization
|
136 |
+
_, binary = cv2.threshold(blurred, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
|
137 |
+
|
138 |
+
# Dilation and Erosion
|
139 |
+
kernel = np.ones((1, 1), np.uint8)
|
140 |
+
dilated = cv2.dilate(binary, kernel, iterations=1)
|
141 |
+
eroded = cv2.erode(dilated, kernel, iterations=1)
|
142 |
+
|
143 |
+
# Edge detection
|
144 |
+
edges = cv2.Canny(eroded, 100, 200)
|
145 |
+
|
146 |
+
# Deskewing
|
147 |
+
coords = np.column_stack(np.where(edges > 0))
|
148 |
+
angle = cv2.minAreaRect(coords)[-1]
|
149 |
+
if angle < -45:
|
150 |
+
angle = -(90 + angle)
|
151 |
+
else:
|
152 |
+
angle = -angle
|
153 |
+
|
154 |
+
(h, w) = edges.shape[:2]
|
155 |
+
center = (w // 2, h // 2)
|
156 |
+
M = cv2.getRotationMatrix2D(center, angle, 1.0)
|
157 |
+
deskewed = cv2.warpAffine(edges, M, (w, h), flags=cv2.INTER_CUBIC, borderMode=cv2.BORDER_REPLICATE)
|
158 |
+
|
159 |
+
# Find contours
|
160 |
+
contours, _ = cv2.findContours(deskewed, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
|
161 |
+
|
162 |
+
# Draw contours on the original image
|
163 |
+
contour_image = image.copy()
|
164 |
+
cv2.drawContours(contour_image, contours, -1, (0, 255, 0), 2)
|
165 |
+
|
166 |
+
return contour_image
|
167 |
+
|
168 |
+
def get_x1(detection):
|
169 |
+
return detection.xyxy[0][0]
|
170 |
+
|
171 |
+
# Access secrets using st.secrets
|
172 |
+
if "INFERENCE_API_URL" not in st.secrets or "INFERENCE_API_KEY" not in st.secrets:
|
173 |
+
st.error("Please make sure to set your secrets in the Streamlit secrets settings.")
|
174 |
+
else:
|
175 |
+
# Initialize the Inference Client
|
176 |
+
CLIENT = InferenceHTTPClient(
|
177 |
+
api_url=st.secrets["INFERENCE_API_URL"],
|
178 |
+
api_key=st.secrets["INFERENCE_API_KEY"]
|
179 |
+
)
|
180 |
+
|
181 |
+
# Function to preprocess the image
|
182 |
+
def preprocess_image(image_path):
|
183 |
+
# Load the image
|
184 |
+
image = cv2.imread(image_path)
|
185 |
+
|
186 |
+
# Convert to grayscale
|
187 |
+
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
|
188 |
+
|
189 |
+
# Remove noise
|
190 |
+
blurred = cv2.GaussianBlur(gray, (5, 5), 0)
|
191 |
+
|
192 |
+
# Thresholding/Binarization
|
193 |
+
_, binary = cv2.threshold(blurred, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
|
194 |
+
|
195 |
+
# Dilation and Erosion
|
196 |
+
kernel = np.ones((1, 1), np.uint8)
|
197 |
+
dilated = cv2.dilate(binary, kernel, iterations=1)
|
198 |
+
eroded = cv2.erode(dilated, kernel, iterations=1)
|
199 |
+
|
200 |
+
# Edge detection
|
201 |
+
edges = cv2.Canny(eroded, 100, 200)
|
202 |
+
|
203 |
+
# Deskewing
|
204 |
+
coords = np.column_stack(np.where(edges > 0))
|
205 |
+
angle = cv2.minAreaRect(coords)[-1]
|
206 |
+
if angle < -45:
|
207 |
+
angle = -(90 + angle)
|
208 |
+
else:
|
209 |
+
angle = -angle
|
210 |
+
|
211 |
+
(h, w) = edges.shape[:2]
|
212 |
+
center = (w // 2, h // 2)
|
213 |
+
M = cv2.getRotationMatrix2D(center, angle, 1.0)
|
214 |
+
deskewed = cv2.warpAffine(edges, M, (w, h), flags=cv2.INTER_CUBIC, borderMode=cv2.BORDER_REPLICATE)
|
215 |
+
|
216 |
+
# Find contours
|
217 |
+
contours, _ = cv2.findContours(deskewed, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
|
218 |
+
|
219 |
+
# Draw contours on the original image
|
220 |
+
contour_image = image.copy()
|
221 |
+
cv2.drawContours(contour_image, contours, -1, (0, 255, 0), 2)
|
222 |
+
|
223 |
+
return contour_image
|
224 |
+
|
225 |
+
def get_x1(detection):
|
226 |
+
return detection.xyxy[0][0]
|
227 |
+
|
228 |
+
# --- Prediction function (using session state) ---
|
229 |
+
def predict_disease(symptoms):
|
230 |
+
if st.session_state.vectorizer is not None and st.session_state.model_llm is not None:
|
231 |
+
preprocessed_symptoms = preprocess_text(symptoms)
|
232 |
+
symptoms_vectorized = st.session_state.vectorizer.transform([preprocessed_symptoms])
|
233 |
+
prediction = st.session_state.model_llm.predict(symptoms_vectorized)
|
234 |
+
return prediction[0]
|
235 |
+
else:
|
236 |
+
st.error("Unable to make prediction. Vectorizer or LLM model is not loaded.")
|
237 |
+
return None
|
238 |
+
|
239 |
+
# --- New function to analyze X-ray with LLM ---
|
240 |
+
def analyze_xray_with_llm(predicted_class):
|
241 |
+
prompt = f"""
|
242 |
+
Based on a chest X-ray analysis, the predicted condition is {predicted_class}.
|
243 |
+
Please provide a concise summary of this condition, including:
|
244 |
+
- A brief description of the condition.
|
245 |
+
- Common symptoms associated with it.
|
246 |
+
- Potential causes.
|
247 |
+
- General treatment approaches.
|
248 |
+
- Any other relevant information for a patient.
|
249 |
+
"""
|
250 |
+
llm_response = get_ai71_response(prompt)
|
251 |
+
st.write("## LLM Analysis of X-ray Results:")
|
252 |
+
st.write(llm_response)
|
253 |
+
|
254 |
+
if page == "Home":
|
255 |
+
st.markdown("## Welcome to Medi Scape")
|
256 |
+
st.write("Medi Scape is an AI-powered healthcare application designed to streamline the process of understanding and managing medical information. It leverages advanced AI models to provide features such as prescription analysis, disease detection from chest X-rays, and symptom-based diagnosis assistance.")
|
257 |
+
|
258 |
+
st.markdown("## Features")
|
259 |
+
st.write("Medi Scape provides various AI-powered tools for remote healthcare, including:")
|
260 |
+
features = [
|
261 |
+
"**AI Chatbot Diagnosis:** Interact with an AI chatbot for preliminary diagnosis and medical information.",
|
262 |
+
"**Drug Identification:** Upload a prescription image to identify medications and access relevant details.",
|
263 |
+
"**Doctor's Handwriting Identification:** Our system can accurately recognize and process doctor's handwriting.",
|
264 |
+
"**Disease Detection:** Upload a chest X-ray image to detect potential diseases.",
|
265 |
+
"**Outbreak Alert:** Stay informed about potential disease outbreaks in your area."
|
266 |
+
]
|
267 |
+
for feature in features:
|
268 |
+
st.markdown(f"- {feature}")
|
269 |
+
|
270 |
+
st.markdown("## How it Works")
|
271 |
+
steps = [
|
272 |
+
"**Upload:** You can upload a prescription image for drug identification or a chest X-ray image for disease detection.",
|
273 |
+
"**Process:** Our AI models will analyze the image and extract relevant information.",
|
274 |
+
"**Results:** You will receive identified drug names, uses, side effects, and more, or a potential disease diagnosis."
|
275 |
+
]
|
276 |
+
for i, step in enumerate(steps, 1):
|
277 |
+
st.markdown(f"{i}. {step}")
|
278 |
+
|
279 |
+
st.markdown("## Key Features")
|
280 |
+
key_features = [
|
281 |
+
"**AI-Powered:** Leverages advanced AI models for accurate analysis and diagnosis.",
|
282 |
+
"**User-Friendly:** Simple and intuitive interface for easy navigation and interaction.",
|
283 |
+
"**Secure:** Your data is protected and handled with confidentiality."
|
284 |
+
]
|
285 |
+
for feature in key_features:
|
286 |
+
st.markdown(f"- {feature}")
|
287 |
+
|
288 |
+
st.markdown("Please use the sidebar to navigate to different features.")
|
289 |
+
|
290 |
+
elif page == "AI Chatbot Diagnosis":
|
291 |
+
st.write("Enter your symptoms separated by commas:")
|
292 |
+
symptoms_input = st.text_area("Symptoms:")
|
293 |
+
if st.button("Diagnose"):
|
294 |
+
if symptoms_input:
|
295 |
+
# --- Pipeline 1 Implementation ---
|
296 |
+
# 1. Symptom Input (already done with st.text_area)
|
297 |
+
# 2. Regression Prediction
|
298 |
+
regression_prediction = predict_disease(symptoms_input)
|
299 |
+
|
300 |
+
if regression_prediction is not None:
|
301 |
+
# 3. LLM Prompt Enhancement
|
302 |
+
prompt = f"""The predicted condition based on a symptom analysis is {regression_prediction}.
|
303 |
+
Provide a detailed explanation of this condition, including possible causes, common symptoms,
|
304 |
+
and general treatment approaches. Also, suggest when a patient should consult a doctor."""
|
305 |
+
|
306 |
+
# 4. LLM Output
|
307 |
+
llm_response = get_ai71_response(prompt)
|
308 |
+
|
309 |
+
# 5. Combined Output
|
310 |
+
st.write("## Logistic Regression Prediction:")
|
311 |
+
st.write(regression_prediction)
|
312 |
+
|
313 |
+
st.write("## LLM Explanation:")
|
314 |
+
st.write(llm_response)
|
315 |
+
# --- End of Pipeline 1 Implementation ---
|
316 |
+
|
317 |
+
else:
|
318 |
+
st.write("Please enter your symptoms.")
|
319 |
+
|
320 |
+
elif page == "Drug Identification":
|
321 |
+
st.write("Upload a prescription image for drug identification.")
|
322 |
+
uploaded_file = st.file_uploader("Upload prescription", type=["png", "jpg", "jpeg"])
|
323 |
+
|
324 |
+
if uploaded_file is not None:
|
325 |
+
# Display the uploaded image
|
326 |
+
image = Image.open(uploaded_file)
|
327 |
+
st.image(image, caption="Uploaded Prescription", use_column_width=True)
|
328 |
+
|
329 |
+
if st.button("Process Prescription"):
|
330 |
+
# Save the image to a temporary file
|
331 |
+
temp_image_path = "temp_image.jpg"
|
332 |
+
image.save(temp_image_path)
|
333 |
+
|
334 |
+
# Preprocess the image
|
335 |
+
preprocessed_image = preprocess_image(temp_image_path)
|
336 |
+
|
337 |
+
# Perform inference
|
338 |
+
result_doch1 = CLIENT.infer(preprocessed_image, model_id="doctor-s-handwriting/1")
|
339 |
+
|
340 |
+
# Extract labels and detections
|
341 |
+
labels = [item["class"] for item in result_doch1["predictions"]]
|
342 |
+
detections = sv.Detections.from_inference(result_doch1)
|
343 |
+
|
344 |
+
# Sort detections and labels
|
345 |
+
sorted_indices = sorted(range(len(detections)), key=lambda i: get_x1(detections[i]))
|
346 |
+
sorted_detections = [detections[i] for i in sorted_indices]
|
347 |
+
sorted_labels = [labels[i] for i in sorted_indices]
|
348 |
+
|
349 |
+
# Convert list to string
|
350 |
+
resulting_string = ''.join(sorted_labels)
|
351 |
+
|
352 |
+
# Display results
|
353 |
+
st.subheader("Processed Prescription")
|
354 |
+
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(12, 6))
|
355 |
+
|
356 |
+
# Plot bounding boxes
|
357 |
+
image_with_boxes = preprocessed_image.copy()
|
358 |
+
for detection in sorted_detections:
|
359 |
+
x1, y1, x2, y2 = detection.xyxy[0]
|
360 |
+
cv2.rectangle(image_with_boxes, (int(x1), int(y1)), (int(x2), int(y2)), (255, 0, 0), 2)
|
361 |
+
ax1.imshow(cv2.cvtColor(image_with_boxes, cv2.COLOR_BGR2RGB))
|
362 |
+
ax1.set_title("Bounding Boxes")
|
363 |
+
ax1.axis('off')
|
364 |
+
|
365 |
+
# Plot labels
|
366 |
+
image_with_labels = preprocessed_image.copy()
|
367 |
+
for i, detection in enumerate(sorted_detections):
|
368 |
+
x1, y1, x2, y2 = detection.xyxy[0]
|
369 |
+
label = sorted_labels[i]
|
370 |
+
cv2.putText(image_with_labels, label, (int(x1), int(y1) - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.9, (0, 255, 0), 2)
|
371 |
+
ax2.imshow(cv2.cvtColor(image_with_labels, cv2.COLOR_BGR2RGB))
|
372 |
+
ax2.set_title("Labels")
|
373 |
+
ax2.axis('off')
|
374 |
+
|
375 |
+
st.pyplot(fig)
|
376 |
+
|
377 |
+
st.write("Extracted Text from Prescription:", resulting_string)
|
378 |
+
|
379 |
+
# Prepare prompt for LLM
|
380 |
+
prompt = f"""Analyze the following prescription text:
|
381 |
+
{resulting_string}
|
382 |
+
|
383 |
+
Please provide:
|
384 |
+
1. Identified drug name(s)
|
385 |
+
2. Full name of each identified drug
|
386 |
+
3. Primary uses of each drug
|
387 |
+
4. Common side effects
|
388 |
+
5. Recommended dosage (if identifiable from the text)
|
389 |
+
6. Any warnings or precautions
|
390 |
+
7. Potential interactions with other medications (if multiple drugs are identified)
|
391 |
+
8. Any additional relevant information for the patient
|
392 |
+
|
393 |
+
If any part of the prescription is unclear or seems incomplete, please mention that and provide information about possible interpretations or matches. Always emphasize the importance of consulting a healthcare professional for accurate interpretation and advice."""
|
394 |
+
|
395 |
+
# Get LLM response
|
396 |
+
llm_response = get_ai71_response(prompt)
|
397 |
+
|
398 |
+
st.subheader("AI Analysis of the Prescription")
|
399 |
+
st.write(llm_response)
|
400 |
+
|
401 |
+
# Remove the temporary image file
|
402 |
+
os.remove(temp_image_path)
|
403 |
+
|
404 |
+
else:
|
405 |
+
st.info("Please upload a prescription image to proceed.")
|
406 |
+
|
407 |
+
elif page == "Disease Detection":
|
408 |
+
st.write("Upload a chest X-ray image for disease detection.")
|
409 |
+
uploaded_image = st.file_uploader("Choose an image...", type=["jpg", "jpeg", "png"])
|
410 |
+
|
411 |
+
if uploaded_image is not None and st.session_state.disease_model is not None:
|
412 |
+
# Display the image
|
413 |
+
img_opened = Image.open(uploaded_image).convert('RGB')
|
414 |
+
image_pred = np.array(img_opened)
|
415 |
+
image_pred = cv2.resize(image_pred, (150, 150))
|
416 |
+
|
417 |
+
# Convert the image to a numpy array
|
418 |
+
image_pred = np.array(image_pred)
|
419 |
+
|
420 |
+
# Rescale the image (if the model was trained with rescaling)
|
421 |
+
image_pred = image_pred / 255.0
|
422 |
+
|
423 |
+
# Add an extra dimension to match the input shape (1, 150, 150, 3)
|
424 |
+
image_pred = np.expand_dims(image_pred, axis=0)
|
425 |
+
|
426 |
+
# Predict using the model
|
427 |
+
prediction = st.session_state.disease_model.predict(image_pred)
|
428 |
+
|
429 |
+
# Get the predicted class
|
430 |
+
predicted_ = np.argmax(prediction)
|
431 |
+
|
432 |
+
# Decode the prediction
|
433 |
+
if predicted_ == 0:
|
434 |
+
predicted_class = "Covid"
|
435 |
+
elif predicted_ == 1:
|
436 |
+
predicted_class = "Normal Chest X-ray"
|
437 |
+
else:
|
438 |
+
predicted_class = "Pneumonia"
|
439 |
+
|
440 |
+
st.image(image_pred, caption='Input image by user', use_column_width=True)
|
441 |
+
st.write("Prediction Classes for different types:")
|
442 |
+
st.write("COVID: 0")
|
443 |
+
st.write("Normal Chest X-ray: 1")
|
444 |
+
st.write("Pneumonia: 2")
|
445 |
+
st.write("\n")
|
446 |
+
st.write("DETECTED DISEASE DISPLAY")
|
447 |
+
st.write(f"Predicted Class : {predicted_}")
|
448 |
+
st.write(predicted_class)
|
449 |
+
|
450 |
+
# Analyze X-ray results with LLM
|
451 |
+
analyze_xray_with_llm(predicted_class)
|
452 |
+
else:
|
453 |
+
st.write("Please upload an image file or ensure the disease model is loaded.")
|
454 |
+
|
455 |
+
elif page == "Outbreak Alert":
|
456 |
+
st.markdown("## **Disease Outbreak News (from WHO)**")
|
457 |
+
|
458 |
+
# Fetch WHO news page
|
459 |
+
url = "https://www.who.int/news-room/events"
|
460 |
+
response = requests.get(url)
|
461 |
+
response.raise_for_status() # Raise an exception for bad status codes
|
462 |
+
|
463 |
+
soup = BeautifulSoup(response.content, 'html.parser')
|
464 |
+
|
465 |
+
# Find news articles (adjust selectors if WHO website changes)
|
466 |
+
articles = soup.find_all('div', class_='list-view--item')
|
467 |
+
|
468 |
+
for article in articles[:5]: # Display the top 5 news articles
|
469 |
+
title_element = article.find('a', class_='link-container')
|
470 |
+
if title_element:
|
471 |
+
title = title_element.text.strip()
|
472 |
+
link = title_element['href']
|
473 |
+
date_element = article.find('span', class_='date')
|
474 |
+
date = date_element.text.strip() if date_element else "Date not found"
|
475 |
+
|
476 |
+
# Format date
|
477 |
+
date_parts = date.split()
|
478 |
+
if len(date_parts) >= 3:
|
479 |
+
try:
|
480 |
+
formatted_date = datetime.strptime(date, "%d %B %Y").strftime("%Y-%m-%d")
|
481 |
+
except ValueError:
|
482 |
+
formatted_date = date # Keep the original date if formatting fails
|
483 |
+
else:
|
484 |
+
formatted_date = date
|
485 |
+
|
486 |
+
# Display news item in a card-like container
|
487 |
+
with st.container():
|
488 |
+
st.markdown(f"**{formatted_date}**")
|
489 |
+
st.markdown(f"[{title}]({link})")
|
490 |
+
st.markdown("---")
|
491 |
+
else:
|
492 |
+
st.write("Could not find article details.")
|
493 |
+
|
494 |
+
# Auto-scroll to the bottom of the chat container
|
495 |
+
st.markdown(
|
496 |
+
"""
|
497 |
+
<script>
|
498 |
+
const chatContainer = document.querySelector('.st-chat-container');
|
499 |
+
if (chatContainer) {
|
500 |
+
chatContainer.scrollTop = chatContainer.scrollHeight;
|
501 |
+
}
|
502 |
+
</script>
|
503 |
+
""",
|
504 |
+
unsafe_allow_html=True,
|
505 |
+
)
|
covid_from_website.png
ADDED
![]() |
logistic_regression_model.pkl
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:cfd5b38cded9c2b4426bb8cc9f337cd078973f5488e2692f3ecd21db3cc41c96
|
3 |
+
size 601655
|
normal_from_website.jpeg
ADDED
![]() |
pneumoni_from_Website.png
ADDED
![]() |
requirements.txt
ADDED
@@ -0,0 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
tensorflow
|
2 |
+
streamlit
|
3 |
+
requests
|
4 |
+
ai71
|
5 |
+
python-dotenv
|
6 |
+
opencv-python
|
7 |
+
supervision
|
8 |
+
matplotlib
|
9 |
+
Pillow
|
10 |
+
inference-sdk
|
11 |
+
tensorflow
|
12 |
+
beautifulsoup4
|
13 |
+
scikit-learn
|
14 |
+
nltk
|
utils/ai71_utils.py
ADDED
@@ -0,0 +1,34 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from ai71 import AI71
|
2 |
+
import os
|
3 |
+
from dotenv import load_dotenv
|
4 |
+
|
5 |
+
load_dotenv()
|
6 |
+
|
7 |
+
AI71_API_KEY = os.getenv("AI71_API_KEY")
|
8 |
+
client = AI71(AI71_API_KEY)
|
9 |
+
|
10 |
+
def get_ai71_response(user_input):
|
11 |
+
system_prompt = """You are an advanced AI assistant specializing in healthcare and prescription analysis. Your role is to:
|
12 |
+
|
13 |
+
1. Analyze prescription images and extracted text to identify medications accurately.
|
14 |
+
2. Provide detailed information about identified drugs, including their full names, primary uses, common side effects, recommended dosages, and any relevant warnings or precautions.
|
15 |
+
3. Assist in interpreting unclear or incomplete drug names, offering possible matches and relevant information.
|
16 |
+
4. Support general healthcare queries, offering informative and helpful responses while maintaining medical accuracy.
|
17 |
+
5. Aid in potential disease diagnosis based on symptoms, always recommending professional medical consultation.
|
18 |
+
6. Provide information on public health issues, including outbreak alerts and preventive measures.
|
19 |
+
|
20 |
+
Remember to always prioritize patient safety. Encourage users to consult healthcare professionals for personalized medical advice, diagnosis, or treatment. Your role is to inform and assist, not to replace professional medical consultation."""
|
21 |
+
|
22 |
+
messages = [
|
23 |
+
{"role": "system", "content": system_prompt},
|
24 |
+
{"role": "user", "content": user_input}
|
25 |
+
]
|
26 |
+
|
27 |
+
response = client.chat.completions.create(
|
28 |
+
messages=messages,
|
29 |
+
model="tiiuae/falcon-180B-chat",
|
30 |
+
stream=False,
|
31 |
+
max_tokens=500 # Added max_tokens parameter
|
32 |
+
)
|
33 |
+
|
34 |
+
return response.choices[0].message.content
|