- Create a virtual environment and install CUDA PyTorch for your GPU, then install dependencies.
On Windows, pip install torch from the default PyPI index often installs +cpu, so torch.cuda.is_available() stays False even with an NVIDIA driver. Always install from the PyTorch CUDA wheel index, and confirm the version string contains +cu124 (or your chosen CUDA flavor), not +cpu:
pip uninstall -y torch torchvision torchaudio
pip install torch torchvision --index-url https://download.pytorch.org/whl/cu124
python -c "import torch; print(torch.__version__); print(torch.cuda.is_available())"
pip install -r requirements.txtThe CUDA 12.4 wheel is typically compatible with newer drivers (your nvidia-smi “CUDA Version” can be higher). Adjust the index URL if you use a different wheel line from PyTorch install.
If import torch fails after an interrupted download, re-run only the pip install torch torchvision --index-url ... line (the wheel is large, ~2.5 GB).
If pip prints No space left on device / [Errno 28]: the CUDA wheel needs several gigabytes free on the drive where Python and pip cache live (often C:). Aim for at least ~5 GB free before installing. Free space (Disk Cleanup, empty Recycle Bin, move large files), optionally clear pip cache: pip cache purge, then install again.
Quick check: python -c "import torch; print(torch.__version__); print(torch.cuda.is_available())" — you want a version like 2.x.x+cu124 and True, not +cpu and False.
- Dataset:
online exam cheating detection.v15i.folder/(Roboflow export).Unlabeledimages were moved to_unlabeled_review/so training uses two classes only.
python train_cheating_yolo.pyOptional: python train_cheating_yolo.py --epochs 50 --model yolo11n-cls.pt
Low-memory GPU/Windows-safe example:
python train_cheating_yolo.py --batch 8 --workers 0
Note: Ultralytics classification expects a dataset directory. This script accepts either:
--data cheating_dataset.yaml(default; auto-resolves to dataset folder), or--data "online exam cheating detection.v15i.folder"
Weights are written under runs/classify/.
If you see WinError 1455 (paging file is too small) during training, keep --workers 0 and lower --batch (e.g. 4), then retry. You can also increase the Windows pagefile size.
Your Roboflow export folder in this repo may be empty until you add images. To still run inference end-to-end, generate a tiny synthetic cheating / normal dataset, fine-tune yolo11n-cls for a few epochs, and copy weights to weights/best.pt:
python prepare_demo_weights.pyThe first run downloads yolo11n-cls.pt from Ultralytics (needs internet). Uses CUDA if available, otherwise CPU (slower). Then start uvicorn — the API auto-resolves weights/best.pt or the newest runs/**/weights/best.pt unless PROCTOR_MODEL_PATH is set.
Your Roboflow train4 run: weight resolution always tries train4 first (best.pt, then last.pt, in both nested and flat Ultralytics layouts), before weights/best.pt, other runs, or demo_quiz. Set PROCTOR_MODEL_PATH only if you want to override that. If train4 has no weights/ folder, restore best.pt from backup or re-run python train_cheating_yolo.py.
from cheating_detector import CheatingDetector
d = CheatingDetector("runs/classify/train/weights/best.pt")
print(d.predict("path/to/image.jpg"))Uses your trained best.pt and the default webcam (--camera 0). Press Q in the video window to quit.
python webcam_proctor.pyIf weights are not found automatically, pass the path Ultralytics printed at the end of training, e.g.:
python webcam_proctor.py --weights runs\classify\train2\weights\best.ptOn a 4GB GPU, increase --stride so inference runs less often (e.g. --stride 5). Use --device cpu if the GPU is busy.
If OpenCV says imshow / cvShowImage is not implemented, you usually have opencv-python-headless. Fix:
python -m pip uninstall -y opencv-python-headless
python -m pip install opencv-pythonAlways use python -m pip so packages install into the same Python you run scripts with (plain pip can target a different install).
If you see ModuleNotFoundError: No module named 'cv2' even though pip says opencv-python is installed, the cv2 files are missing — reinstall:
python -m pip uninstall -y opencv-python
python -m pip install --no-cache-dir opencv-python
python -c "import cv2; print(cv2.__version__)"Or skip OpenCV windows and use the built-in Tk viewer: python webcam_proctor.py --gui tk (still requires a working cv2 for the camera).
Set PROCTOR_API_KEY (optional but recommended). Set PROCTOR_MODEL_PATH only if you want an explicit file; otherwise the app searches weights/best.pt then the newest runs/**/weights/best.pt.
set PROCTOR_API_KEY=your-secret
uvicorn backend.main:app --host 0.0.0.0 --port 8000Open http://127.0.0.1:8000/ for the UI. POST /api/predict with multipart field file and header X-API-Key when configured.
- Simple: Link or iframe
https://your-host/from Moodle/Canvas/Blackboard. - LTI 1.3: Use a separate LTI tool or middleware that launches your app with signed context; this repo exposes a stable JSON API at
/api/predictfor that integration.
The API can persist registered users and prediction audit rows when DB_CONNECTION_STRING is set.
- Install ODBC Driver 18 for SQL Server on the machine that runs the app (Microsoft ODBC driver).
- Install Python deps:
python -m pip install -r requirements.txt(includessqlalchemy,pyodbc,bcrypt). - Set the environment variable only on the server (never commit it). See
.env.examplefor the shape of the value. - Start the app: on startup it runs
CREATE TABLEforapp_usersandproctor_predictionsif they do not exist. - Check
GET /health:databaseisconnected,disabled,error, ormisconfigured(env set but connection/schema init failed — seedatabase_error).
If you see IM002 / “Data source name not found”: Windows does not have the ODBC driver named in your string (often ODBC Driver 18 for SQL Server). Install it from Microsoft, then confirm:
python -c "import pyodbc; print(pyodbc.drivers())"You should see ODBC Driver 18 for SQL Server in the list. If you only have 17, change Driver={...} in DB_CONNECTION_STRING to match.
Azure TLS note: if you still cannot connect after the driver is installed, try Encrypt=yes with TrustServerCertificate=yes only for isolated testing (less strict than no); prefer proper certificates in production.
Endpoints
POST /api/register— JSON body{"email":"...","password":"...","display_name":null}creates a user (bcrypt password hash).POST /api/login— JSON body{"email":"...","password":"..."}verifies credentials (returnsuser_id; add JWT/session later if you need full auth).POST /api/predict— unchanged response; if DB is configured, each call also inserts a row. Optional headers:X-User-Email— if it matches a registered user,user_idis stored on the row.X-Client-Reference— optional LMS/exam id string (max 256 chars).
Security: database passwords must not live in source control. If a password was shared in chat or logs, rotate it in Azure and update the server environment only.
| Variable | Description |
|---|---|
PROCTOR_MODEL_PATH |
Optional explicit path to best.pt. If unset: weights/best.pt, else newest runs/**/weights/best.pt, else legacy runs/classify/train/weights/best.pt |
PROCTOR_DEVICE |
cuda, cuda:0, or cpu (default: cuda if available) |
PROCTOR_API_KEY |
If set, required as X-API-Key for /api/predict |
PROCTOR_CHEAT_THRESHOLD |
Probability above which label is treated as cheating alert (default 0.5) |
DB_CONNECTION_STRING |
Optional ODBC connection string for Azure SQL / SQL Server |
The UI is a normal web page served by FastAPI. Start the API on the machine where your trained weights live:
uvicorn backend.main:app --host 127.0.0.1 --port 8000Then open http://127.0.0.1:8000/ in a browser. The Quiz & camera tab captures frames with getUserMedia and POSTs them to /api/predict on that same host, so YOLO inference runs in your local Python process (not a hosted “cloud model” API). Training is still done separately with train_cheating_yolo.py on your machine.
Static assets live under static/; the main template is templates/exam_portal.html. The minimal single-file upload UI remains at /upload.
API flow (same as before, now driven from the portal):
- Organization:
POST /api/org/signup,POST /api/org/login - Organization creates accounts:
POST /api/org/teachers,POST /api/org/students - List recent accounts:
POST /api/org/accountswith JSON{"org_email","org_password"}(GET with query parameters still exists for compatibility but avoid it for real passwords). - Student:
POST /api/student/login, first loginPOST /api/student/change-password, facePOST /api/student/face, dashboardPOST /api/student/dashboardwith{"email","password"}(GET with query string still supported but discouraged). - Live proctoring:
POST /api/predict— optionalX-API-KeyifPROCTOR_API_KEYis set; alert whencheat_probability >= PROCTOR_CHEAT_THRESHOLD(default0.5; setPROCTOR_CHEAT_THRESHOLD=0.7if you want alerts only at 0.7 and up). The portal shows thumbnails when the server returnsalert: true.
Demo note: the portal stores organization and student passwords in sessionStorage for convenience on localhost only; do not rely on that for production authentication.