本篇簡介
從本小節(jié)開始刁憋,我們將嘗試使用基礎(chǔ)框架搭建篇完成的各類工具滥嘴,實(shí)現(xiàn)一些實(shí)戰(zhàn)向的應(yīng)用場景。
本篇作為實(shí)戰(zhàn)篇的第一個案例职祷,首先來看看如何實(shí)現(xiàn)視頻流人臉跟蹤這個較為常見的功能氏涩。
原理簡介
實(shí)現(xiàn)對人臉時別/跟蹤,需要解決的主要問題就是將人臉的部分與場景中其他的部分區(qū)分開來有梆。這就涉及到兩方面的問題:
- 如何提取人臉的特征
- 如何使用提取的特征對場景中的對象進(jìn)行分類
對此是尖,一種常見的實(shí)現(xiàn)方式為Haar特征檢測提取特征 + 級聯(lián)分類器(Cascading Classifier)進(jìn)行分類的組合。而OpenCV中也提供了相應(yīng)的分類器訓(xùn)練及分類工具泥耀。關(guān)于Haar特征提取和級聯(lián)分類器的原理饺汹,在這里就不詳細(xì)說明了,有興趣的話可以參考文末參考連接中的相關(guān)資料[1]和[2]痰催。
此外兜辞,簡明起見迎瞧,接下來的實(shí)現(xiàn)將只關(guān)注如何使用已訓(xùn)練好的分類器進(jìn)行人臉檢測,如何對分類器進(jìn)行訓(xùn)練不在本文的討論范圍內(nèi)逸吵。
那么接下來我們就來看一看如何使用我們實(shí)現(xiàn)好的QCvCamView控件和OpenCV提供的分類器工具實(shí)現(xiàn)視頻流人臉跟蹤凶硅。
實(shí)現(xiàn)人臉檢測濾波器
首先,秉承QCvCamView中通過濾波鏈來實(shí)現(xiàn)視頻流處理的思路(參考:擴(kuò)展視頻流控件)扫皱,我們需要實(shí)現(xiàn)一個人臉檢測濾波器足绅,聲明如下:
class QCvFaceDetectFilter : public QCvMatFilter
{
public:
QCvFaceDetectFilter(QString name) : QCvMatFilter(name) {}
public:
bool load(QString fileName);
bool isClassifierValid();
protected:
virtual void execFilter(const cv::Mat& inMat, cv::Mat& outMat);
protected:
cv::CascadeClassifier m_classifier;
};
這個濾波器除了實(shí)現(xiàn)了濾波器通用的execFilter方法外,還提供了讀取分類器配置和判斷分類器是否有效的公共方法韩脑。OpenCV提供的級聯(lián)分類器作為組件以成員變量的形式在這里聲明氢妈。
注:要使用這個分類器,需要引用opencv2/objdetect.hpp頭文件
讀取分類器配置的具體實(shí)現(xiàn)如下:
bool QCvFaceDetectFilter::load(QString fileName)
{
return m_classifier.load(fileName.toStdString().c_str());
}
也就是直接使用了OpenCV提供的讀取方法段多。
濾波處理的具體實(shí)現(xiàn)如下:
void QCvFaceDetectFilter::execFilter(const cv::Mat& inMat, cv::Mat &outMat)
{
outMat = inMat.clone();
if (m_classifier.empty())
{
return;
}
std::vector<cv::Rect> faces;
cv::Mat frameGray;
cv::cvtColor(outMat, frameGray, cv::COLOR_BGR2GRAY);
cv::equalizeHist(frameGray, frameGray);
//-- Detect faces
m_classifier.detectMultiScale(frameGray, faces, 1.1, 2, 0 | cv::CASCADE_SCALE_IMAGE,
cv::Size(30, 30));
for (size_t i = 0; i < faces.size(); i++)
{
cv::Point center(faces[i].x + faces[i].width / 2, faces[i].y + faces[i].height / 2);
cv::ellipse(outMat, center,
cv::Size(faces[i].width / 2, faces[i].height / 2), 0, 0, 360,
cv::Scalar(255, 0, 255), 4, 8, 0);
}
}
上面的實(shí)現(xiàn)中首量,首先借助cvtColor方法將RGB圖像轉(zhuǎn)化為灰度圖像,并通過直方圖匹配(eualizeHist)對灰度圖像進(jìn)行了預(yù)處理进苍,以提升光線較暗時識別的準(zhǔn)確率加缘。
之后,將預(yù)處理后的灰度圖像傳入級聯(lián)分類器的多級檢測方法(detectMultiScale)進(jìn)行分類琅捏,并將結(jié)果矩形保存到faces數(shù)據(jù)中生百。
最后,在RGB圖像上將檢測到的人臉結(jié)果繪制為橢圓形柄延。這樣基于級聯(lián)分類器的人臉檢測濾波器就實(shí)現(xiàn)完成了。
注:這里的實(shí)現(xiàn)參考了OpenCV官方級聯(lián)分類器教程中的實(shí)現(xiàn)缀程,并進(jìn)行了一定簡化搜吧。
界面實(shí)現(xiàn)
整體界面設(shè)計(jì)如下:
灰色部分即為嵌入的QCvCamView控件,下方兩個按鈕分別為加載分類器的按鈕和開始人臉檢測的按鈕杨凑。其中開始檢測的按鈕在沒有完成分類器加載前處于不可用狀態(tài)滤奈。
基于上述界面設(shè)計(jì),我們初始化QCvCamView控件撩满,并將上面實(shí)現(xiàn)好的人臉檢測濾波器添加到濾波鏈中:
m_camView = new QCvCamView(ui->frame);
m_faceFilter = new QCvFaceDetectFilter("face");
m_camView->appendFilter(m_faceFilter);
然后將配置好的視頻流控件添加到布局中即可蜒程。這里限于篇幅就不貼布局相關(guān)的代碼了。
接下來我們來實(shí)現(xiàn)兩個功能按鈕伺帘。首先是加載分類器的按鈕昭躺,比較關(guān)鍵的實(shí)現(xiàn)步驟如下:
void FaceVideoDlg::onBtnLoad()
{
// 省略界面布局及控件相關(guān)的操作
...
QString dataDir = QDir::currentPath() + "/../../../../sdk/opencv_release/share/OpenCV/haarcascades";
if (!QDir(dataDir).exists())
{
dataDir = ".";
}
QString classifierName = QFileDialog::getOpenFileName(this,
"Please Select a Cascade Classifier",
dataDir,
"XML Files (*.xml)");
if (!classifierName.isEmpty())
{
bool isValid = m_faceFilter->load(classifierName);
// 省略界面布局及控件相關(guān)的操作
...
}
else
{
// 省略界面布局及控件相關(guān)的操作
...
}
}
上面的實(shí)現(xiàn)分為三個步驟:
- 讀取分類器。在上面的實(shí)現(xiàn)中伪嫁,默認(rèn)路徑指向了OpenCV官方提供的訓(xùn)練結(jié)果樣例路徑(參考第一節(jié)有關(guān)SDK目錄的說明)领炫,方便后續(xù)測試。
- 加載分類器张咳。這個步驟委托給了人臉檢測濾波器處理帝洪。
- 根據(jù)加載結(jié)果似舵,執(zhí)行各控件的啟用/禁用操作。
另一方面葱峡,開始人臉檢測的按鈕實(shí)現(xiàn)就相對比較簡單了砚哗,只需要啟動視頻流控件即可:
void FaceVideoDlg::onBtnDetect(bool clicked)
{
m_camView->onStreamSwitch(clicked);
}
測試檢測效果
整個界面實(shí)現(xiàn)完成后,我們來測試下檢測的效果砰奕。首先點(diǎn)擊加載分類器按鈕频祝,選擇官方樣例中的haarcascade_frontalface_alt2.xml訓(xùn)練器,然后點(diǎn)擊開始檢測按鈕脆淹,效果如下:
有關(guān)視頻流人臉檢測的實(shí)現(xiàn)就說明到這里常空。
>>本篇參考代碼
>>返回系列索引
參考鏈接
[1] Haar-like Feature Wikipedia
[2] Cascading Classfier Wikipedia
[3] OpenCV官方級聯(lián)分類器教程