Computer >> บทช่วยสอนคอมพิวเตอร์ >  >> การเขียนโปรแกรม >> C++

วิธีตรวจจับและติดตามการเคลื่อนไหวของลูกตาใน OpenCV ด้วย C++


ภาพรวม

บทความนี้สาธิตวิธี ตรวจจับลูกตา (Eye Detection) และ ติดตามตำแหน่งลูกตา (Eyeball Tracking) แบบ Real-time โดยใช้ OpenCV และ C++

แนวคิดหลัก

  1. Face Detection: ใช้ Haar Cascade Classifier (haarcascade_frontalface_alt.xml) ตรวจจับใบหน้า
  2. Eye Detection: ใช้ Haar Cascade Classifier (haarcascade_eye.xml) ตรวจจับตาในบริเวณใบหน้า
  3. Eyeball Detection: ใช้ Hough Circle Transform ตรวจจับวงกลมในบริเวณตา แล้วเลือกวงกลมที่มีความเข้มพิกเซลต่ำที่สุด (ลูกตาสีเข้ม)
  4. 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

วิธีตรวจจับและติดตามการเคลื่อนไหวของลูกตาใน OpenCV ด้วย C++
วิธีตรวจจับและติดตามการเคลื่อนไหวของลูกตาใน OpenCV ด้วย C++