若該文爲原創文章,未經允許不得轉載
原博主博客地址:https://blog.csdn.net/qq21497936
原博主博客導航:https://blog.csdn.net/qq21497936/article/details/102478062
本文章博客地址:https://blog.csdn.net/qq21497936/article/details/105943604
各位讀者,知識無窮而人力有窮,要麼改需求,要麼找專業人士,要麼自己研究
目錄
入坑一:錯誤“Error: Assertion failed (hpoints > 0) in convexityDefects”
紅胖子(紅模仿)的博文大全:開發技術集合(包含Qt實用技術、樹莓派、三維、OpenCV、OpenGL、ffmpeg、OSG、單片機、軟硬結合等等)持續更新中...(點擊傳送門)
OpenCV開發專欄(點擊傳送門)
OpenCV開發筆記(五十):紅胖子8分鐘帶你深入瞭解輪廓凸包(圖文並茂+淺顯易懂+程序源碼)
前言
紅胖子來也!!!
輪過提取完後,可以尋找物體輪廓的凸包,這樣可以把凸包區域提取出來,作爲特徵點提取識別的依據,這是一種場景,還有更多的場景,凸包提取出來後做凸包缺陷分析,那麼就可以識別物體種類等等,不同的物體一般都具有不同的凸包缺陷。
Demo
.
輪廓凸包
概述
凸包(convex Hull)是能最小包圍給定二維平面上的點集合的凸多邊形,將二維平面上的點當作釘子,然後使用橡皮筋將所有的釘子包括進去,最後得出的就是凸包。
如下圖手勢:
尋找凸包函數原型
void convexHull( InputArray points,
OutputArray hull,
bool clockwise = false,
bool returnPoints = true );
- 參數一:InpuArray類型的points,std::vector<cv::Point>即可,輸入二維點;
- 參數二:OutputArray類型的hull,外殼輸出凸面外殼。它可以是索引的整數向量,也可以是點的向量。在第一種情況,外殼元素是原始外殼點凸的基於0的索引數組(因爲凸殼點集是原始點集的子集)std::vector<int>。在第二個外殼元素是凸面外殼點本身,std::vector<cv::Point>。
- 參數三:bool類型的clockwise,默認值爲false,逆時針方向。順時針方向標誌。如果爲真,則輸出凸包將順時針方向。否則,它是逆時針方向的。假定座標系有其X軸指向向右,Y軸向上。
- 參數四:boo類型的returnPoints,默認值爲true,參數二返回點的集合。返回點操作標誌。對於矩陣,當標誌爲真時,函數返回凸面外殼點。否則,返回凸殼點的索引。當輸出數組是std::vector,標誌被忽略,輸出取決於returnPoints,false返回std::vector<int>,true時返回std::vector<Point>。
輪廓凸包缺陷
概述
對於凸包後的物體,有凹陷進去的情況,任何2點外圍邊界凹進去的都認爲是一個凸缺陷。
凸包缺陷,就是凸包裏面凹陷最深的點到當前缺錢外圍最短的距離(做處置線)
對之前的輪廓手掌做凸包檢測,如下圖(純目測收工,可能有點不準確,以Demo效果爲準):
當知道識別物體是手掌,通過凸包缺陷,最簡單的,我們通過手掌正面反面的缺陷,可以識別出手掌的手勢了(當然較簡單的)。
尋找凸包缺陷函數原型
void convexityDefects( InputArray contour,
InputArray convexhull,
OutputArray convexityDefects );
- 參數一:InpuArray類型的contour,輪廓輸入;
- 參數二:InputArray類型的hull,外殼輸出凸面外殼;
- 參數三:OutputArray類型的convexityDefects,凸性缺陷的輸出向量。數組向量:開始索引、結束索引、最遠點索引、固定深度,其中索引是基於0的索引在凸性缺陷的起始、結束和最遠點的原始輪廓中,以及fixpt_depth是最遠的像素點距離。
Demo源碼
void OpenCVManager::testConvexHull()
{
QString fileName1 =
"E:/qtProject/openCVDemo/openCVDemo/modules/openCVManager/images/22.jpg";
cv::Mat srcMat = cv::imread(fileName1.toStdString());
cv::Mat dstMat;
int width = 300;
int height = 200;
cv::resize(srcMat, srcMat, cv::Size(width, height));
cv::String windowName = _windowTitle.toStdString();
cvui::init(windowName);
cv::Mat windowMat = cv::Mat(cv::Size(srcMat.cols * 3,
srcMat.rows * 3),
srcMat.type());
int sigmaS = 100;
int sigmaR = 1.0;
int thresh = 215;
int maxval = 255;
while(true)
{
// 刷新全圖黑色
windowMat = cv::Scalar(0, 0, 0);
// 原圖複製
cv::Mat mat = windowMat(cv::Range(srcMat.rows * 0, srcMat.rows * 1),
cv::Range(srcMat.cols * 0, srcMat.cols * 1));
cv::addWeighted(mat, 0.0f, srcMat, 1.0f, 0.0f, mat);
cv::Mat tempMat;
{
{
cvui::printf(windowMat, 75 + width * 1, 40 + height * 0, "sigmaS");
cvui::trackbar(windowMat, 75 + width * 1, 50 + height * 0, 165, &sigmaS, 101, 10000);
cvui::printf(windowMat, 75 + width * 1, 90 + height * 0, "sigmaR");
cvui::trackbar(windowMat, 75 + width * 1, 100, 165 + height * 0, &sigmaR, 1, 100);
// 使用自適應流形應用高維濾波。
cv::Ptr<cv::ximgproc::AdaptiveManifoldFilter> pAdaptiveManifoldFilter
= cv::ximgproc::createAMFilter(sigmaS/100.0f, sigmaR/100.0f, true);
pAdaptiveManifoldFilter->filter(srcMat, tempMat);
// 效果圖copy
mat = windowMat(cv::Range(srcMat.rows * 1, srcMat.rows * 2),
cv::Range(srcMat.cols * 0, srcMat.cols * 1));
cv::addWeighted(mat, 0.0f, tempMat, 1.0f, 0.0f, mat);
}
// 轉爲灰度圖像
cv::cvtColor(tempMat, tempMat, cv::COLOR_BGR2GRAY);
{
// 調整閾值化的參數thresh
cvui::printf(windowMat, 75 + width * 1, 20 + height * 1, "thresh");
cvui::trackbar(windowMat, 75 + width * 1, 40 + height * 1, 165, &thresh, 0, 255);
// 調整閾值化的參數maxval
cvui::printf(windowMat, 75 + width * 1, 80 + height * 1, "maxval");
cvui::trackbar(windowMat, 75 + width * 1, 100 + height * 1, 165, &maxval, 0, 255);
// 閾值化,注意:此處使用了THRESH_BINARY_INV,白色是255,255,255所以反轉閾值化
cv::threshold(tempMat, tempMat, thresh, maxval, cv::THRESH_BINARY_INV);
// 效果圖copy
mat = windowMat(cv::Range(srcMat.rows * 2, srcMat.rows * 3),
cv::Range(srcMat.cols * 0, srcMat.cols * 1));
// 轉還圖像
cv::Mat grayMat;
cv::cvtColor(tempMat, grayMat, cv::COLOR_GRAY2BGR);
cv::addWeighted(mat, 0.0f, grayMat, 1.0f, 0.0f, mat);
}
// 尋找輪廓
{
std::vector<std::vector<cv::Point>> contours;
std::vector<cv::Vec4i> hierarchy;
// 查找輪廓:RETR_EXTERNAL-最外層輪廓
cv::findContours(tempMat, contours, hierarchy, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE);
// 遍歷所有頂層輪廓,並繪製出來
dstMat = srcMat.clone();
cv::Mat emptyMat = srcMat.clone();
emptyMat = cv::Scalar(0,0,0);
// 輪廓contours[i]對應4個hierarchy元素hierarchy[i][0]~ hierarchy[i][3],
// hierarchy[i][0]表示後一個輪廓的索引編號
// hierarchy[i][1]前一個輪廓的索引編號
// hierarchy[i][2]父輪廓的索引編號
// hierarchy[i][3]內嵌輪廓的索引編號
for(int index = 0; index >=0; index = hierarchy[index][0])
{
if(hierarchy.size() <= 0)
{
break;
}
cv::Scalar color;
if(index < hierarchy.size() / 3)
{
color = cv::Scalar(250 / (hierarchy.size() / 3) * index, 125, 255);
}else if(index < hierarchy.size() / 3 * 2)
{
color = cv::Scalar(255, 250 / (hierarchy.size() / 3) * (index - hierarchy.size() / 3), 125);
}else
{
color = cv::Scalar(125, 255, 250 /
(hierarchy.size() / 3 == 0? 1 : hierarchy.size() / 3) * (index - hierarchy.size() / 3 * 2));
}
// 繪製輪廓裏面的第幾個
cv::drawContours(emptyMat, contours, index, color, CV_FILLED, 8, hierarchy);
// 尋找最大凸包
std::vector<cv::Point> hullPoints;
std::vector<int> hullIndex;
cv::convexHull(contours[index], hullPoints, false, true);
cv::convexHull(contours[index], hullIndex, false, false);
// 繪製點
for(int index2 = 1; index2 < hullPoints.size(); index2++)
{
cv::line(mat, hullPoints.at(index2 - 1), hullPoints.at(index2), cv::Scalar(0, 0, 255));
cv::line(dstMat, hullPoints.at(index2 - 1), hullPoints.at(index2), cv::Scalar(0, 0, 255));
if(index == hullPoints.size() - 1)
{
cv::line(dstMat, hullPoints.at(0), hullPoints.at(index2), cv::Scalar(0, 0, 255));
cv::line(dstMat, hullPoints.at(0), hullPoints.at(index2), cv::Scalar(0, 0, 255));
}
}
qDebug() << __FILE__ << __LINE__ << "index =" << index << "total =" << hierarchy.size();
if(hullPoints.size() > 0)
{
// 繪製缺陷的點,打印調試出缺陷的距離
std::vector<cv::Vec4i> detectVec4i;
qDebug() << __FILE__ << __LINE__ << contours.at(index).size() << hullPoints.size() << index;
cv::convexityDefects(contours[index], hullIndex, detectVec4i);
// 輸出輪廓的缺陷點與值,畫出最遠距離的點
for(int index2 = 0; index2 < detectVec4i.size(); index2++)
{
QString str = QString("detect %1:").arg(index2);
str += QString("start point(%1,%2),").arg(contours[index][detectVec4i[index2][0]].x).arg(contours[index][detectVec4i[index2][0]].y);
str += QString("end point(%1,%2),").arg(contours[index][detectVec4i[index2][1]].x).arg(contours[index][detectVec4i[index2][1]].y);
str += QString("defect point(%1,%2),").arg(contours[index][detectVec4i[index2][2]].x).arg(contours[index][detectVec4i[index2][2]].y);
str += QString("distance: %1").arg(detectVec4i[index2][3]);
cvui::printf(windowMat, 0, index2 * 16, 0.5, 0xFF0000, str.toUtf8().constData());
// 繪製點
cv::circle(dstMat, contours[index][detectVec4i[index2][2]], 2.0, cv::Scalar(255, 0, 0), 2);
}
}
}
// 效果圖copy
mat = windowMat(cv::Range(srcMat.rows * 2, srcMat.rows * 3),
cv::Range(srcMat.cols * 1, srcMat.cols * 2));
cv::addWeighted(mat, 0.0f, emptyMat, 1.0f, 0.0f, mat);
// 效果圖copy
mat = windowMat(cv::Range(srcMat.rows * 2, srcMat.rows * 3),
cv::Range(srcMat.cols * 2, srcMat.cols * 3));
cv::addWeighted(mat, 0.0f, dstMat, 1.0f, 0.0f, mat);
}
}
// 更新
cvui::update();
// 顯示
cv::imshow(windowName, windowMat);
// esc鍵退出
if(cv::waitKey(25) == 27)
{
break;
}
}
}
工程模板:對應版本號v1.45.0
對應版本號v1.45.0
入坑
入坑一:錯誤“Error: Assertion failed (hpoints > 0) in convexityDefects”
原因:
輸入的hullPoints錯誤;
解決:
輸入第一個參數,由輸入點std::vector<cv::Point>改爲索引整數,std::vector<int>(這個需要在尋找凸包裏面第四個參數返回點改爲false,即返回索引)。
原博主博客地址:https://blog.csdn.net/qq21497936
原博主博客導航:https://blog.csdn.net/qq21497936/article/details/102478062
本文章博客地址:https://blog.csdn.net/qq21497936/article/details/105943604