For the prototype:
┌──────────────────────┐
│ FRONTEND │
│ │
│ Patient / Screening │
│ Upload / Results │
│ Reports / Dashboard │
└──────────┬───────────┘
│
HTTP
│
┌──────────▼───────────┐
│ BACKEND │
│ │
│ Auth │
│ Patients │
│ Screening Sessions │
│ Image Management │
│ ML orchestration │
│ Results │
│ Reports │
└──────┬───────┬───────┘
│ │
┌──────▼───┐ ┌─▼──────────┐
│ DATABASE │ │ FILE STORE │
│ │ │ │
│ Patients │ │ Fundus │
│ Sessions │ │ images │
│ Results │ │ Grad-CAM │
└──────────┘ │ reports │
└────────────┘
┌───────────────┐
│ ML ENGINE │
│ │
│ Quality │
│ DR grading │
│ Lesions │
│ Grad-CAM │
└───────────────┘
The critical architectural decision is:
The backend should not care how the ML model works.
Today the ML team may have a Python prototype. Later it may become MATLAB code, a MATLAB-generated service, or something else. The backend should simply send an image and receive a standardized result.
Responsible for:
Responsible for:
Responsible for metadata, not necessarily the actual retinal images.
Database
│
├── users
├── patients
├── screening_sessions
├── images
├── predictions
├── lesions
└── reports
File/Object Storage
│
├── original images
├── processed images
├── Grad-CAM images
└── generated reports
React + TypeScript
React
TypeScript
Vite
Python + FastAPI
FastAPI
│
├── REST API
├── authentication
├── image handling
├── ML orchestration
└── database access
PostgreSQL
For development:
/local storage
Later:
S3-compatible object storage
Keep this completely separate:
ML model
The backend talks to it through an interface.
Who operates the system.
users
-----
id
name
email
password_hash
role
created_at
Possible roles:
ADMIN
SCREENING_OPERATOR
OPHTHALMOLOGIST
The person being screened.
patients
--------
id
patient_code
name
age
sex
contact
created_at
One screening event.
screening_sessions
-------------------
id
patient_id
operator_id
status
created_at
completed_at
Possible statuses:
CREATED
IMAGE_UPLOADED
PROCESSING
COMPLETED
REVIEW_REQUIRED
FAILED
The actual image metadata.
images
------
id
session_id
original_path
processed_path
width
height
quality_status
created_at
Example:
quality_status:
PENDING
ACCEPTABLE
BORDERLINE
UNGRADABLE
predictionspredictions
-----------
id
session_id
dr_grade
referable_dr
confidence
model_name
model_version
created_at
For example:
{
"dr_grade": 3,
"referable_dr": true,
"confidence": 0.91,
"model_name": "DRClassifier",
"model_version": "0.1.0"
}
lesionslesions
-------
id
session_id
type
confidence
x
y
width
height
severity
Possible:
MICROANEURYSM
EXUDATE
HEMORRHAGE
NEOVASCULARIZATION
reportsreports
-------
id
session_id
report_path
generated_at
review_status
reviewer_id
reviewed_at
review_notes
ML
↓
Result
↓
Ophthalmologist
↓
Review
↓
Confirm / modify
↓
Final report
POST /api/patients
GET /api/patients
GET /api/patients/{id}
POST /api/screenings
GET /api/screenings/{id}
POST /api/screenings/{id}/image
POST /api/screenings/{id}/analyze
GET /api/screenings/{id}/status
GET /api/screenings/{id}/result
GET /api/screenings/{id}/report
POST /api/screenings/{id}/review
Create an interface such as:
POST /ml/analyze
Input:
fundus image
Output:
{
"quality": {
"status": "ACCEPTABLE",
"confidence": 0.96
},
"dr": {
"grade": 3,
"referable": true,
"confidence": 0.91
},
"explainability": {
"gradcam_path": "...",
"annotated_image_path": "..."
},
"lesions": []
}
┌──────────────────────────┐
│ DR Screening │
│ │
│ Email │
│ [____________________] │
│ │
│ Password │
│ [____________________] │
│ │
│ [ Login ] │
└──────────────────────────┘
Dashboard
Today's screenings 42
Pending reviews 7
Completed 35
Ungradable 3
[ New Screening ]
Recent screenings
─────────────────────────
Patient Status Grade
P-001 Complete 1
P-002 Review 3
P-003 Complete 0
Patient
Patient ID: P-001
Name: ...
Age: ...
Sex: ...
[ Start New Screening ]
Previous screenings
New Screening
Patient: P-001
Upload fundus image
┌────────────────────────┐
│ │
│ Drop image here │
│ │
└────────────────────────┘
[ Analyze ]
Analyzing retinal image...
✓ Image uploaded
✓ Quality assessment
● DR analysis
○ Explainability
○ Report
SCREENING RESULT
┌───────────────────────────────┐
│ Fundus Image │
│ │
│ [ IMAGE ] │
└───────────────────────────────┘
DR Grade
Level 3
Referable DR
YES
Confidence
91%
────────────────────────────────
Explainability
[ Original ] [ Grad-CAM ] [ Annotated ]
────────────────────────────────
Lesion Evidence
Microaneurysms Detected
Exudates Detected
Hemorrhages Detected
────────────────────────────────
[ Review ] [ Generate Report ]
The whole application can then be thought of as one pipeline:
LOGIN
↓
DASHBOARD
↓
SELECT PATIENT
↓
NEW SCREENING
↓
UPLOAD FUNDUS IMAGE
↓
QUALITY CHECK
│
├──── UNGRADABLE
│ ↓
│ Recapture
│
└──── ACCEPTABLE
↓
ML ANALYSIS
↓
DR CLASSIFICATION
↓
EXPLAINABILITY
↓
RESULT
↓
┌───────┴────────┐
↓ ↓
Auto complete Human review
↓ ↓
└───────┬────────┘
↓
REPORT
Avoid:
Frontend → ML
Use:
Frontend
↓
Backend
↓
ML
Because eventually the backend needs to handle:
dr-screening/
│
├── frontend/
│ ├── src/
│ ├── public/
│ └── README.md
│
├── backend/
│ ├── app/
│ │ ├── api/
│ │ ├── models/
│ │ ├── schemas/
│ │ ├── services/
│ │ ├── database/
│ │ └── main.py
│ │
│ └── tests/
│
├── ml/
│ └── README.md
│
├── storage/
│ └── .gitkeep
│
├── docs/
│ ├── architecture.md
│ ├── api.md
│ └── ml-contract.md
│
├── docker-compose.yml
└── README.md
comments (0)