本文采用灰度图—> 二值化 —> 去除孔洞 —> 得到轮廓 —> 轮廓标记—> 批量处理 的方法检测简单证件照人脸。
初学opencv,做个笔记。借鉴以下代码段:(侵删)
https://blog.csdn.net/chenxjhit/article/details/80549194
https://www.cnblogs.com/meadow-glog/p/4709928.html
https://blog.csdn.net/qq_30241709/article/details/78759726
先贴出效果图
完整代码
#include<opencv2/opencv.hpp>
#include<string>
#include <iostream>
using namespace cv;
using namespace std;
void fillHole(const Mat srcBw,Mat &dstBw); //孔洞填充函数
int main()
{
cv::String path = "/home/voluntino/Documents/face/pic/";//待处理图片文件夹地址
cv::String dest = "/home/voluntino/Documents/face/pic_after/";//处理后图片的保存地址
cv::String savedfilename;
std::vector<cv::String> filenames;
cv::Mat img0,img;
cv::Mat bw,imgGray,dstbw;
cv::glob(path, filenames); //opencv用来读取指定路径下文件名
for (int i = 0; i < filenames.size(); i++) //遍历文件夹中的图片
{
img0 = cv::imread(filenames[i]);
resize(img0, img, Size(1000,1000)); //指定图片尺寸,方便确定轮廓线长度最大最小值
cv::cvtColor(img,imgGray,CV_BGR2GRAY);
cv::threshold(imgGray,bw,120,255,CV_THRESH_BINARY);
fillHole(bw,dstbw); //孔洞填充函数
vector <vector<Point>>contours;
findContours(dstbw, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE); //获取连通区域
//在白色图像上绘制黑色轮廓
Mat lunkuo(dstbw.size(), CV_8U, Scalar(255));
drawContours(lunkuo, contours, -1, Scalar(0), 2); //轮廓线的黑色,宽度为2
//imshow("contours", lunkuo);
//移除过长或过短的轮廓
int cmin = 1000; //最小轮廓长度
int cmax = 4000; //最大轮廓长度
vector<vector<Point>>::const_iterator itc = contours.begin();
while (itc!=contours.end())
{
if (itc->size() < cmin || itc->size() > cmax)
itc = contours.erase(itc);
else
++itc;
}
//绘制移除后的轮廓
Mat lunkuo_erase(dstbw.size(), CV_8U, Scalar(255));
drawContours(lunkuo_erase, contours,-1,Scalar(0), 2);
//imshow("contours", lunkuo_erase);
//标记轮廓
cv::Rect rect = cv::boundingRect(contours[0]); //包围轮廓的最小正矩形
cv::rectangle(lunkuo_erase, rect, cv::Scalar(0), 2); //在轮廓图中画矩形
cv::rectangle(img,rect,cv::Scalar(0,255,0),2); //在原图中画出绿色矩形
//imshow("result",img);
//cv::imshow("detected rectangle", lunkuo_erase);
savedfilename = dest + filenames[i].substr(35); //对处理后图片批量命名,注意substr()的使用
std::cout << "处理:" << savedfilename << std::endl;
cv::imwrite(savedfilename, img); //保存图片
}
while(1){
if(cv::waitKey(20) == 'q'){
break;
}
}
return 0;
}
void fillHole(const Mat srcBw, Mat &dstBw)
{
Size m_Size = srcBw.size();
Mat Temp=Mat::zeros(m_Size.height+2,m_Size.width+2,srcBw.type());//延展图像
srcBw.copyTo(Temp(Range(1, m_Size.height + 1), Range(1, m_Size.width + 1)));
cv::floodFill(Temp, Point(0, 0), Scalar(255));
Mat cutImg;//裁剪延展的图像
Temp(Range(1, m_Size.height + 1), Range(1, m_Size.width + 1)).copyTo(cutImg);
dstBw = srcBw | (~cutImg);
}
心得:未使用分类器,仅简单使用图片处理方式,可识别单人的证件照,且证件照应身着深色衣服。
学习使用了opencv的一些图像处理函数,重点学习了批量处理的方法,为以后的机器视觉有一定铺垫。