ภาพรวม
บทความนี้สาธิตวิธี ตรวจจับลูกตา (Eye Detection) และ ติดตามตำแหน่งลูกตา (Eyeball Tracking) แบบ Real-time โดยใช้ OpenCV และ C++
แนวคิดหลัก
- Face Detection: ใช้ Haar Cascade Classifier (
haarcascade_frontalface_alt.xml) ตรวจจับใบหน้า - Eye Detection: ใช้ Haar Cascade Classifier (
haarcascade_eye.xml) ตรวจจับตาในบริเวณใบหน้า - Eyeball Detection: ใช้ Hough Circle Transform ตรวจจับวงกลมในบริเวณตา แล้วเลือกวงกลมที่มีความเข้มพิกเซลต่ำที่สุด (ลูกตาสีเข้ม)
- Tracking: ใช้ Moving Average ของตำแหน่งลูกตาใน 5 เฟรมล่าสุด เพื่อลด Noise
โค้ดหลัก (สรุปฟังก์ชันสำคัญ)
1. ตรวจจับลูกตาจากวงกลม Hough Circles
Vec3f eyeBallDetection(Mat& eye, vector<Vec3f>& circles) {
vector<int> sums(circles.size(), 0);
for (int y = 0; y < eye.rows; y++) {
uchar* data = eye.ptr<uchar>(y);
for (int x = 0; x < eye.cols; x++) {
int pixel_value = *data;
for (int i = 0; i < circles.size(); i++) {
Point center(cvRound(circles[i][0]), cvRound(circles[i][1]));
int radius = cvRound(circles[i][2]);
if (pow(x - center.x, 2) + pow(y - center.y, 2) < pow(radius, 2))
sums[i] += pixel_value;
}
++data;
}
}
// เลือกวงกลมที่มีผลรวมพิกเซลน้อยที่สุด (สีเข้มที่สุด = ลูกตา)
int smallestSum = INT_MAX, smallestSumIndex = -1;
for (int i = 0; i < circles.size(); i++)
if (sums[i] < smallestSum) { smallestSum = sums[i]; smallestSumIndex = i; }
return circles[smallestSumIndex];
}
2. หาตาซ้าย (Left Eye)
Rect detectLeftEye(vector<Rect>& eyes) {
int leftEye = INT_MAX, index = 0;
for (int i = 0; i < eyes.size(); i++)
if (eyes[i].tl().x < leftEye) { leftEye = eyes[i].tl().x; index = i; }
return eyes[index];
}
3. ทำให้ตำแหน่งลูกตาเสถียร (Moving Average)
Point makeStable(vector<Point>& points, int iteration) {
float sumX = 0, sumY = 0;
int count = 0;
int start = max(0, (int)points.size() - iteration);
for (int j = start; j < points.size(); j++) {
sumX += points[j].x;
sumY += points[j].y;
count++;
}
return Point(sumX/count, sumY/count);
}
4. ฟังก์ชันหลักตรวจจับตา
void eyeDetection(Mat& frame, CascadeClassifier& faceCascade, CascadeClassifier& eyeCascade) {
Mat gray; cvtColor(frame, gray, COLOR_BGR2GRAY); equalizeHist(gray, gray);
// Detect Face
vector<Rect> faces;
faceCascade.detectMultiScale(gray, faces, 1.1, 2, 0|CASCADE_SCALE_IMAGE, Size(150,150));
if (faces.empty()) return;
// Draw Face
rectangle(frame, faces[0].tl(), faces[0].br(), Scalar(255,0,255), 2);
// Detect Eyes in Face ROI
Mat faceROI = gray(faces[0]);
vector<Rect> eyes;
eyeCascade.detectMultiScale(faceROI, eyes, 1.1, 2, 0|CASCADE_SCALE_IMAGE, Size(30,30));
if (eyes.size() != 2) return;
// Draw Eyes
for (Rect& eye : eyes)
rectangle(frame, faces[0].tl() + eye.tl(), faces[0].tl() + eye.br(), Scalar(0,255,0), 2);
// Process Left Eye
Rect eyeRect = detectLeftEye(eyes);
Mat eye = faceROI(eyeRect);
equalizeHist(eye, eye);
// Hough Circles
vector<Vec3f> circles;
HoughCircles(eye, circles, HOUGH_GRADIENT, 1, eye.cols/8, 250, 15, eye.rows/8, eye.rows/3);
if (!circles.empty()) {
Vec3f eyeball = eyeBallDetection(eye, circles);
Point center(eyeball[0], eyeball[1]);
centers.push_back(center);
center = makeStable(centers, 5);
track_Eyeball = center;
int radius = eyeball[2];
circle(frame, faces[0].tl() + eyeRect.tl() + center, radius, Scalar(0,0,255), 2);
circle(eye, center, radius, Scalar(255,255,255), 2);
}
cout << "Eyeball location: " << track_Eyeball << endl;
imshow("Eye", eye);
}
โค้ดหลัก (Main)
int main() {
CascadeClassifier faceCascade, eyeCascade;
faceCascade.load("haarcascade_frontalface_alt.xml");
eyeCascade.load("haarcascade_eye.xml");
VideoCapture cap(0);
Mat frame;
while (true) {
cap >> frame;
eyeDetection(frame, faceCascade, eyeCascade);
imshow("Webcam", frame);
if (waitKey(30) >= 0) break;
}
return 0;
}
ผลลัพธ์
แสดงหน้าต่าง Webcam พร้อมกรอบใบหน้า ตา และวงกลมสีแดงบนลูกตา พร้อมพิมพ์พิกัดลูกตาใน Console

