整體思路:
1.過濾噪聲
2.由於RGB顏色的離散性轉換爲HSV通道
3.對HSV空間進行量化,得到2值圖像,亮的部分爲手的形狀
4.去除雜點造成的僞輪廓,留下手的真實輪廓
5.對凸出點連線
6.最高點到底部中點的連線即爲手指方向
//部分代碼:
將MFC實現部分給省略了,給出了完整的opencv部分代碼,可以參考實現。
int main()
{
cv::VideoCapture cap(0);
if (!cap.isOpened())
{
return -1;
}
//
cv::Mat frame;
cv::Mat frameHSV;
//
std::vector< std::vector<cv::Point> > OriginalContours;//輪廓
std::vector< cv::Vec4i > hierarchy; // 輪廓的結構信息
//
std::vector< std::vector<cv::Point> > FinalContours;// 篩選後的輪廓
std::vector< cv::Point > hull; // 凸包絡的點集
//
CString Str;
int i, j;
int Width = cap.get(CV_CAP_PROP_FRAME_WIDTH), Height = cap.get(CV_CAP_PROP_FRAME_HEIGHT);
float fHeight = ((float)2 / 3 - (float)1 / 7) * Height;
//
while (0 == pDlg->m_bStopFlag)
{
cap >> frame;
if (frame.empty())
{
break;
}
//左右翻轉
cv::flip(frame, frame, 1);
//中值濾波,用了這個下面的其他濾波全部異常
//medianBlur(frame, frame, 10);
//高斯濾波
cv::GaussianBlur(frame, frame, cv::Size(7, 7), 1.5, 1.5);
//通道轉換
cv::cvtColor(frame, frameHSV, CV_BGR2HSV);
//imshow("frameHSV", frameHSV);
/*
S = 符號整型 U = 無符號整型 F = 浮點型
E.g.:
CV_8UC1 是指一個8位無符號整型單通道矩陣,
CV_32FC2是指一個32位浮點型雙通道矩陣
CV_8UC1 CV_8SC1 CV_16U C1 CV_16SC1
CV_8UC2 CV_8SC2 CV_16UC2 CV_16SC2
CV_8UC3 CV_8SC3 CV_16UC3 CV_16SC3
CV_8UC4 CV_8SC4 CV_16UC4 CV_16SC4
CV_32SC1 CV_32FC1 CV_64FC1
CV_32SC2 CV_32FC2 CV_64FC2
CV_32SC3 CV_32FC3 CV_64FC3
CV_32SC4 CV_32FC4 CV_64FC4
*/
// 對HSV空間進行量化,得到2值圖像,亮的部分爲手的形狀
cv::Mat mask(frame.rows, frame.cols, CV_8UC1);
//inRange(frameHSV, cv::Scalar(0, 30, 30), cv::Scalar(40, 170, 256), mask);
inRange(frameHSV, cv::Scalar(5, 30, 30), cv::Scalar(40, 170, 256), mask);
//
// 腐蝕:去除小亮點 膨脹:連接區塊
cv::erode(mask, mask, cv::Mat(5, 5, CV_8U), cv::Point(-1, -1), 1);
//cv::dilate(mask, mask, cv::Mat(5, 5, CV_8U), cv::Point(-1, -1), 2);
//imshow("frameHSV", frameHSV);
//imshow("mask", mask);
OriginalContours.clear();
hierarchy.clear();
FinalContours.clear();
// 得到手的輪廓
cv::findContours(mask, OriginalContours, hierarchy, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE);
// 去除僞輪廓
for (i = 0; i < OriginalContours.size(); i++)
{
if (fabs(cv::contourArea(cv::Mat(OriginalContours[i]))) > 25000)
{
FinalContours.push_back(OriginalContours[i]);
}
}
// 畫輪廓
cv::drawContours(frame, FinalContours, -1, cv::Scalar(0, 0, 255), 3);
// 得到輪廓的凸包絡
int hullcount;
int iNumOfContours = FinalContours.size();
cv::Point top;
for (j = 0; j < iNumOfContours; j++)
{
convexHull(cv::Mat(FinalContours[j]), hull, true);
hullcount = (int)hull.size();
//
for (i = 0; i<hullcount - 1; i++)
{
cv::line(frame, hull[i + 1], hull[i], cv::Scalar(255, 0, 0), 2, CV_AA);
}
cv::line(frame, hull[hullcount - 1], hull[0], cv::Scalar(255, 0, 0), 2, CV_AA);
//畫點
GetTopPoint(hull, hullcount, top);
cv::line(frame, top, top, cv::Scalar(255, 0, 255), 18, CV_AA);
cv::line(frame, top, cv::Point(Width/2, Height), cv::Scalar(0, 255, 255), 2, CV_AA);
//
if (1 == iNumOfContours)
{
pDlg->m_FingerPosition.bChanged = 1;
pDlg->m_FingerPosition.top = ((float)top.y - (float)Height / 7) / fHeight;
pDlg->m_FingerPosition.left = (float)top.x / (float)Width;
}
}
//
cv::line(frame, cv::Point(0, Height / 7), cv::Point(Width, Height / 7), cv::Scalar(255, 255, 255), 2, CV_AA);
cv::line(frame, cv::Point(0, 2 * Height / 3), cv::Point(Width, 2 * Height / 3), cv::Scalar(255, 255, 255), 2, CV_AA);
imshow("frame", frame);
cv::waitKey(33);
}
return 0;
}
實驗效果圖