Project Overview
Giamsat provides a real-time drowsiness monitoring system using a standard webcam. It analyzes facial landmarks to detect eye closure, yawning, and computes PERCLOS (percentage of eye closure) to assess driver fatigue. The tool offers both visual overlays and optional alarms, and logs events for post-drive review.
Why It Exists
Driver fatigue causes a large share of traffic accidents. Giamsat aims to alert drowsy drivers before microsleeps occur, reducing accident risk through proactive, real-time feedback.
Main Features
- Real-time eye-closure detection via Eye Aspect Ratio (EAR)
- Yawn detection through Mouth Aspect Ratio (MAR) and yawn counting
- PERCLOS monitoring: calculates the ratio of closed-eye frames over a sliding window
- Visual overlays: facial landmarks, EAR/MAR values, status messages
- Audio alarm: optional runtime buzz when thresholds exceed limits
- Event logging: CSV output with timestamps, EAR/MAR values, PERCLOS
- Interactive controls via keyboard:
c
– recalibrate EAR/MAR thresholdsa
– toggle audio alarmd
– switch between display modes (landmarks, metrics)q
– quit application
How It Works
- Capture frames from the default webcam.
- Detect facial landmarks using dlib’s pre-trained shape predictor.
- Compute EAR for both eyes and MAR for the mouth.
- Update PERCLOS over a configured time window.
- Overlay visual feedback on the video stream.
- Trigger audio alarms and log events when thresholds are exceeded.
- Respond to keyboard commands for calibration and mode toggling.
Basic Usage
# Install dependencies
pip install opencv-python dlib imutils numpy
# Run real-time drowsiness monitor
python main.py
Controls during runtime:
- Press
c
to recalibrate thresholds based on current face metrics. - Press
a
to enable or disable the audio alarm. - Press
d
to cycle display modes (full metrics, landmarks only). - Press
q
to exit and save the log (events_log.csv
).
Value Proposition
By combining multiple fatigue indicators—eye closure, yawning, and PERCLOS—Giamsat delivers robust, low-latency warnings. Its simple setup and interactive controls make it ideal for in-vehicle deployments, research, or personal alert systems.
Quick Start & Usage
Get Giám sát drowsiness detection up and running in minutes. Follow these steps to install dependencies, launch the app, and interact via keyboard controls.
1. Install Dependencies
- Clone the repository
git clone https://github.com/thianh05/Giamsat.git cd Giamsat
- (Optional) Create and activate a virtual environment
python3 -m venv venv source venv/bin/activate # Linux/macOS venv\Scripts\activate.bat # Windows
- Install core packages
pip install opencv-python dlib imutils numpy scipy
- (Optional) Install audio alarm support
pip install simpleaudio
- Download the facial‐landmark predictor
wget http://dlib.net/files/shape_predictor_68_face_landmarks.dat.bz2 bzip2 -dk shape_predictor_68_face_landmarks.dat.bz2 mv shape_predictor_68_face_landmarks.dat Giamsat/
2. Launch the Application
Run the real-time drowsiness detector pointing to the shape predictor file:
python main.py --shape-predictor shape_predictor_68_face_landmarks.dat
Available CLI options:
• --shape-predictor
(required): path to the dlib .dat
file
• --alarm
(default=on/off): enable or disable runtime audible alarm
• --camera
(default=0): camera index or video file path
Example (disable alarm, use external camera):
python main.py \
--shape-predictor shape_predictor_68_face_landmarks.dat \
--alarm off \
--camera 1
3. First Successful Run
- A window titled “Drowsiness Monitor” opens showing live video feed.
- Eye Aspect Ratio (EAR) and Mouth Aspect Ratio (MAR) overlay on each face.
- Visual alert (“DROWSY” or “YAWN”) appears when thresholds exceed.
- Console logs timestamped events:
EAR drop
,yawn detected
.
4. In-App Keyboard Controls
While the video window is active, use keys to adjust and troubleshoot:
• C – Calibrate thresholds
Resamples baseline EAR/MAR over 100 frames and updates alert levels.
• D – Toggle display mode
Switch between full overlay (landmarks + metrics) and minimal (video + alerts).
• A – Toggle alarm
Enable or silence the audible alarm at runtime.
• Q or Esc – Quit
Close the window and terminate the application.
Example Workflow
- Start the app:
python main.py --shape-predictor shape_predictor_68_face_landmarks.dat
- Press C to auto-calibrate your baseline in a neutral state (eyes open, mouth closed).
- Begin driving or simulating; observe EAR/MAR feedback.
- If you prefer silent mode, press A to mute the alarm.
- Toggle off landmark overlays with D for a cleaner view.
- Press Q or Esc to exit.
Calibration & Configuration
Adjust detection sensitivity by tuning thresholds and counters either at runtime via keyboard or by editing constants in main.py. Fine-tune parameters to your camera, lighting and user’s baseline behavior.
Tunable Parameters
- EAR_THRESHOLD (float)
Eye Aspect Ratio below which eyes count as “closed.”
Default: 0.25 - EAR_CONSEC_FRAMES (int)
Number of consecutive frames EAR must stay below threshold to trigger drowsiness.
Default: 20 - YAWN_THRESHOLD (float)
Mouth aspect ratio above which a yawn registers.
Default: 20.0 - YAWN_CONSEC_FRAMES (int)
Consecutive frames mouth must stay above yawn threshold.
Default: 15 - PERCLOS_WINDOW (int)
Frame window length for PERCLOS (percentage of eye closure).
Default: 1500 (≈25 s at 60 FPS)
Editing Code Constants
Open main.py and locate the top section. Change values, save and restart:
# main.py (top section)
EAR_THRESHOLD = 0.25 # adjust to 0.20–0.30 based on camera
EAR_CONSEC_FRAMES = 20 # increase for fewer false alerts
YAWN_THRESHOLD = 20.0 # raise if you get noisy mouth detections
YAWN_CONSEC_FRAMES = 15 # lengthen if short mouth openings trigger false yawns
PERCLOS_WINDOW = 1500 # adjust to sample period you prefer
Runtime Calibration via Keyboard
While running, press keys to tweak parameters on the fly. The console prints new values after each change.
Key Action Adjustment
q Increase EAR_THRESHOLD by 0.005 EAR_THRESHOLD += 0.005
a Decrease EAR_THRESHOLD by 0.005 EAR_THRESHOLD -= 0.005
w Increase EAR_CONSEC_FRAMES by 1 EAR_CONSEC_FRAMES += 1
s Decrease EAR_CONSEC_FRAMES by 1 EAR_CONSEC_FRAMES -= 1
e Increase YAWN_THRESHOLD by 1 YAWN_THRESHOLD += 1
d Decrease YAWN_THRESHOLD by 1 YAWN_THRESHOLD -= 1
r Increase PERCLOS_WINDOW by 30 PERCLOS_WINDOW += 30
f Decrease PERCLOS_WINDOW by 30 PERCLOS_WINDOW -= 30
h Toggle alarm on/off alarm_enabled = not alarm_enabled
m Cycle display modes (EAR, PERCLOS, all) display_mode = (display_mode+1)%3
Example: Runtime Key Handling
# inside main loop, after cv2.imshow(...)
key = cv2.waitKey(1) & 0xFF
if key == ord('q'): EAR_THRESHOLD += 0.005
elif key == ord('a'): EAR_THRESHOLD -= 0.005
elif key == ord('w'): EAR_CONSEC_FRAMES += 1
elif key == ord('s'): EAR_CONSEC_FRAMES -= 1
elif key == ord('e'): YAWN_THRESHOLD += 1.0
elif key == ord('d'): YAWN_THRESHOLD -= 1.0
elif key == ord('r'): PERCLOS_WINDOW += 30
elif key == ord('f'): PERCLOS_WINDOW -= 30
elif key == ord('h'): alarm_enabled = not alarm_enabled
elif key == ord('m'): display_mode = (display_mode + 1) % 3
# log updated values
print(f"EAR_T={EAR_THRESHOLD:.3f}, EAR_F={EAR_CONSEC_FRAMES}, YAWN_T={YAWN_THRESHOLD}, PERCLOS_W={PERCLOS_WINDOW}")
Best Practices
- Begin with defaults in a controlled environment.
- Adjust EAR_THRESHOLD up/down in small increments (±0.005).
- Watch console feedback and on‐screen alerts to avoid false positives.
- Lock in values by editing constants once you find stable settings.
- Document your final settings for each deployment scenario.
Logging & Data Interpretation
This section describes the format of drowsiness_log.txt, explains each field, shows how to parse and summarise session data, and explains where and how to customise logging in main.py.
Log File Structure
Each session writes a “Session Start” header with configured thresholds, followed by time-stamped events, and ends with “Session End.” Fields are comma-separated:
Timestamp,LogLevel,Event,Details
Example excerpt:
2023-09-01 08:15:32,INFO,Session Start,EAR=0.25|Yawn=20|PERCLOS_win=60
2023-09-01 08:16:10,WARNING,Eyes Closed,Duration=2.1s
2023-09-01 08:17:05,INFO,Yawn Detected,Duration=1.2s
2023-09-01 08:45:00,INFO,Session End,TotalWarnings=5|TotalYawns=3
Field Definitions
- Timestamp
ISO-formatted date and time of the event. - LogLevel
INFO (normal), WARNING (eyes-closed threshold exceeded), ALARM (runtime alarm fired). - Event
• Session Start / Session End
• Eyes Closed
• Yawn Detected
• Alarm Toggled - Details
Key|value pairs:
• At start: EAR threshold, yawn threshold, PERCLOS window (seconds)
• Eyes Closed: duration of continuous eye closure
• Yawn Detected: duration mouth open
• Session End: total counts
Parsing and Summarising Session Data
Use Python and pandas to load and analyse logs:
import pandas as pd
# Read and split “Details” into columns
df = pd.read_csv('drowsiness_log.txt',
names=['ts','level','event','details'],
parse_dates=['ts'])
# Expand details
details = df['details'].str.split(r'[=|]', expand=True)
# For example, total warnings and yawns at session end
end_sessions = df[df.event == 'Session End'].copy()
end_details = end_sessions['details']\
.str.extract(r'TotalWarnings=(\d+)\|TotalYawns=(\d+)')\
.astype(int)
end_sessions['warnings'] = end_details[0]
end_sessions['yawns'] = end_details[1]
# Compute session durations
starts = df[df.event == 'Session Start'][['ts']].reset_index(drop=True)
ends = end_sessions[['ts','warnings','yawns']].reset_index(drop=True)
summary = pd.concat([starts, ends], axis=1)
summary['duration_min'] = (summary['ts_y'] - summary['ts_x']).dt.total_seconds()/60
print(summary[['ts_x','duration_min','warnings','yawns']])
Archiving and Rotation
Archive raw logs daily or weekly:
zip drowsiness_logs_$(date +%F).zip drowsiness_log.txt
> mv drowsiness_logs_*.zip /path/to/archive/
> > drowsiness_log.txt # truncate for new session
To auto-rotate within Python, add a rotating handler (see next section).
Customising Logging in main.py
In main.py, logging configuration lives at the top. By default it appends to drowsiness_log.txt:
import logging
LOG_FILE = 'drowsiness_log.txt'
logging.basicConfig(
filename=LOG_FILE,
level=logging.INFO,
format='%(asctime)s,%(levelname)s,%(message)s',
datefmt='%Y-%m-%d %H:%M:%S'
)
logger = logging.getLogger(__name__)
Enable Log Rotation
Replace basicConfig with a RotatingFileHandler
:
from logging.handlers import RotatingFileHandler
handler = RotatingFileHandler(
LOG_FILE,
maxBytes=5*1024*1024, # 5 MB
backupCount=3 # keep last 3 files
)
formatter = logging.Formatter(
'%(asctime)s,%(levelname)s,%(message)s',
datefmt='%Y-%m-%d %H:%M:%S'
)
handler.setFormatter(formatter)
logger = logging.getLogger()
logger.setLevel(logging.INFO)
logger.addHandler(handler)
Changing Log Path or Levels
• To change the file path, update LOG_FILE
.
• To log debug information (e.g., landmark coordinates), change level=logging.DEBUG
and insert logger.debug()
calls at desired points (e.g., after computing EAR/PERCLOS).
Where Logging Occurs
- Session Start / End: lines ~60 and ~270 in main.py
- Eyes Closed: inside
if ear < EAR_THRESH:
block (~line 120) - Yawn Detected: inside
if mar > YAWN_THRESH:
block (~line 150) - Alarm Toggling: on keypress
'a'
handler (~line 200)
Modify these sections to adjust what gets logged or to add new event types.