Computer >> Hướng Dẫn Máy Tính >  >> Lập Trình >> C++

Cách phát hiện và theo dõi chuyển động nhãn cầu trong OpenCV C++


Giới thiệu

Bài viết hướng dẫn cách phát hiện nhãn cầu (eyeball) và theo dõi chuyển động của nó bằng OpenCV C++.

Ý tưởng chính

  • Phát hiện khuôn mặt bằng haarcascade_frontalface_alt.xml
  • Trong vùng khuôn mặt, phát hiện mắt bằng haarcascade_eye.xml
  • Lấy mắt trái (có tọa độ x nhỏ nhất)
  • Cân bằng histogram vùng mắt để tăng tương phản
  • Áp dụng Hough Circle Transform để tìm các đường tròn trong vùng mắt
  • Chọn đường tròn có tổng độ sáng pixel nhỏ nhất (nhãn cầu tối nhất)
  • Làm mượt tọa độ tâm bằng trung bình trượt 5 khung hình
  • Vẽ đường tròn đỏ đánh dấu nhãn cầu và hiển thị tọa độ

Chương trình hoàn chỉnh

#include <iostream>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/objdetect/objdetect.hpp>
#include <string>
#include <vector>
using namespace cv;
using namespace std;

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 = static_cast<int>(*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 = 9999999, smallestSumIndex = -1;
    for (int i = 0; i < circles.size(); i++) {
        if (sums[i] < smallestSum) {
            smallestSum = sums[i];
            smallestSumIndex = i;
        }
    }
    return circles[smallestSumIndex];
}

Rect detectLeftEye(vector<Rect>& eyes) {
    int leftEye = 99999999, 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];
}

vector<Point> centers;
Point track_Eyeball;

Point makeStable(vector<Point>& points, int iteration) {
    float sumX = 0, sumY = 0;
    int count = 0;
    int j = max(0, (int)points.size() - iteration);
    for (; j < points.size(); j++) {
        sumX += points[j].x;
        sumY += points[j].y;
        count++;
    }
    if (count > 0) {
        sumX /= count;
        sumY /= count;
    }
    return Point(sumX, sumY);
}

void eyeDetection(Mat& frame, CascadeClassifier& faceCascade, CascadeClassifier& eyeCascade) {
    Mat grayImage;
    cvtColor(frame, grayImage, COLOR_BGR2GRAY);
    equalizeHist(grayImage, grayImage);

    vector<Rect> storedFaces;
    faceCascade.detectMultiScale(grayImage, storedFaces, 1.1, 2, 0 | CASCADE_SCALE_IMAGE, Size(150, 150));
    if (storedFaces.empty()) return;

    Mat face = grayImage(storedFaces[0]);
    rectangle(frame, storedFaces[0].tl(), storedFaces[0].br(), Scalar(255, 0, 255), 2);

    vector<Rect> eyes;
    eyeCascade.detectMultiScale(face, eyes, 1.1, 2, 0 | CASCADE_SCALE_IMAGE, Size(30, 30));
    if (eyes.size() != 2) return;

    for (Rect& eye : eyes)
        rectangle(frame, storedFaces[0].tl() + eye.tl(), storedFaces[0].tl() + eye.br(), Scalar(0, 255, 0), 2);

    Rect eyeRect = detectLeftEye(eyes);
    Mat eye = face(eyeRect);
    equalizeHist(eye, eye);

    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(cvRound(eyeball[0]), cvRound(eyeball[1]));
        centers.push_back(center);
        center = makeStable(centers, 5);
        track_Eyeball = center;
        int radius = cvRound(eyeball[2]);
        circle(frame, storedFaces[0].tl() + eyeRect.tl() + center, radius, Scalar(0, 0, 255), 2);
        circle(eye, center, radius, Scalar(255, 255, 255), 2);
    }
    cout << "Vị trí nhãn cầu: " << track_Eyeball << endl;
    imshow("Eye", eye);
}

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;
}

Kết quả

Chương trình hiển thị cửa sổ webcam với khung chữ nhật tím bao quanh khuôn mặt, khung xanh lá quanh mắt, và đường tròn đỏ bao quanh nhãn cầu được theo dõi. Tọa độ nhãn cầu in ra console.