OpenCV瞳孔定位

利用OpenCV自带的分类器识别面部及眼睛

OpenCV自带里许多分类器,可在haarcascades下载

获取面部

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 获取面部
// image 为输入的OpenCV的格式的图像
// out 为输出图像 <- 此处输出图像为裁剪下来的矩形面部图像
bool get_face(cv::Mat image, cv::Mat &out)
{
// 将图像转为灰度图像,可提高识别精度
cv::Mat gray;
cv::cvtColor(image, gray, CV_BGR2GRAY, 0);
// 此处的rect为用来存储识别出的面部的定位 <- 这里为vector是因为识别的面部可能不止为一个
std::vector<cv::Rect> rect;
// 初始化OpenCV的分类器
cv::CascadeClassifier cas = cv::CascadeClassifier("haarcascade_frontalface_default.xml");
cas.detectMultiScale(gray, rect, 1.15, 5);
// 判断rect是否为空,为空的话就是没有被识别的面部
if (rect.empty())
{
return false;
}
// 输出图像,这里的rect可能不止为一个(因为可能不止一张脸),所以实际中应该使用for循环
// 我这里用rect[0]为了简化
out = image(rect[0]);
return true;
}

获取眼睛

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 这里与上面相同(这里我只识别左眼,OpenCV还提供了其他文件可获取右眼和双眼)
bool get_left_eye(cv::Mat image, cv::Mat &out)
{
// 之后与面部相同
cv::Mat gray;
cv::cvtColor(image, gray, CV_BGR2GRAY, 0);
std::vector<cv::Rect> rect;
cv::CascadeClassifier cas = cv::CascadeClassifier("xml/haarcascade_lefteye_2splits.xml");
cas.detectMultiScale(gray, rect, 1.15, 5);
if (rect.empty())
{
return false;
}
out = image(rect[0]);
return true;
}

瞳孔定位

  • 获取的眼睛图像
  • 裁剪去除眉毛
  • 伽马矫正提高图像对比度
  • 转化为灰度图像
  • 利用threshold进行阈值操作(此处可能会改为inRange,目前还在测试)
  • 腐蚀
  • 膨胀
  • medianBlur平滑图像
  • 检测关键点并画在图像上
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
// 定位眼球 -> 检测关键点
std::vector<cv::KeyPoint> dector(cv::Mat image)
{
cv::SimpleBlobDetector::Params detector_params;
detector_params.filterByArea = true;
detector_params.maxArea = 1500;
cv::Ptr<cv::SimpleBlobDetector> blob_dector = cv::SimpleBlobDetector::create(detector_params);
std::vector<cv::KeyPoint> keypoints;
blob_dector->detect(image, keypoints);
return keypoints;
}

bool get_pupil(cv::Mat image, cv::Mat &out)
{
// width -> image.rows ;
// height -> image.cols;
// cut eyebrow 裁剪 去除眉毛
image = image(cv::Rect(image.cols / 10, image.rows / 2.5, image.cols * 8 / 10, image.rows / 2));

// Gamma correction/gamma nonlinearity 伽马校正 -> https://www.cnblogs.com/sdu20112013/p/11597171.html
cv::Mat look_up_table(1, 256, CV_8U);
uchar *p = look_up_table.ptr();
float gamma = 0.7;
for (int i = 0; i < 256; ++i)
p[i] = cv::saturate_cast<uchar>(pow(i / 255.0, gamma) * 255.0);
cv::LUT(image, look_up_table, image);

cv::Mat gray;
// 转化为灰度图像
cv::cvtColor(image, gray, CV_BGR2GRAY, 0);
// 阈值操作
cv::threshold(gray, gray, inRange_threshold, 255, CV_THRESH_BINARY);
//膨胀及腐蚀
cv::erode(gray, gray, cv::Mat(), cv::Point(-1, -1), 2);
cv::dilate(gray, gray, cv::Mat(), cv::Point(-1, -1), 3);
// 平滑图像
cv ::medianBlur(gray, gray, 5);
// 检测并画出关键点
cv::drawKeypoints(image, dector(gray), image, cv::Scalar(0, 0, 255), cv::DrawMatchesFlags::DRAW_RICH_KEYPOINTS);
out = image;
return true;
}

附录

OpenCV在ArchLinux上的使用

1
2
3
4
pacman -S opencv
# vtk和hdf5为opencv的依赖,不安装编译会失败
sudo pacman -S vtk
sudo pacman -Ss hdf5

Cmake文件

1
2
3
4
5
6
7
cmake_minimum_required(VERSION 3.14)
project(eyedector)
add_definitions(-std=c++11)
# 这里只需要添加OpenCV的就能编译成功
find_package(OpenCV REQUIRED)
link_libraries(${OpenCV_LIBS})
add_executable(out opencvtest.cpp)

利用安卓手机充当网络摄像头

手机安装这个IP Webcam,使用时会显示局域网中的地址

OpenCV中

1
2
3
4
5
6
7
8
9
10
11
cv::VideoCapture cap("http://192.168.0.xx:8080/video"); // 这里为网络摄像头的地址,需要在后面加上/video
while (true)
{
cv::Mat frame;
cap >> frame;
cv::imshow("test", frame);
if (cv::waitKey(1) == 27)
{
exit(0);
}
}