若該文爲原創文章,未經容許不得轉載
原博主博客地址:https://blog.csdn.net/qq21497936
原博主博客導航:http://www.javashuo.com/article/p-wxwjppoc-mo.html
本文章博客地址:https://blog.csdn.net/qq21497936/article/details/106180872
各位讀者,知識無窮而人力有窮,要麼改需求,要麼找專業人士,要麼本身研究
紅胖子(紅模仿)的博文大全:開發技術集合(包含Qt實用技術、樹莓派、三維、OpenCV、OpenGL、ffmpeg、OSG、單片機、軟硬結合等等)持續更新中...(點擊傳送門)算法
上一篇:《OpenCV開發筆記(五十五):紅胖子8分鐘帶你深刻了解Haar、LBP特徵以及級聯分類器識別過程(圖文並茂+淺顯易懂+程序源碼)》
下一篇:持續補充中… app
紅胖子,來也!
識別目標,能夠經過圖形擬合,將目標提取出來。函數
參考博文《OpenCV開發筆記(四十九):紅胖子8分鐘帶你深刻了解輪廓識別(圖文並茂+淺顯易懂+程序源碼)》 ui
參考博文《OpenCV開發筆記(五十):紅胖子8分鐘帶你深刻了解輪廓凸包(圖文並茂+淺顯易懂+程序源碼)》 spa
尋找輪廓以後,openCV提供了對輸入點集合進行多種圖形進行擬合的方法,基本都是輸入以前尋找凸包後再進行操做,固然也能夠直接對了輪廓進行操做。
識別不一樣的目標物體,根據形狀能夠剔除,還能夠作不少其餘的操做,好比車牌識別,提取車牌號碼,那麼直接能夠拿到每一個車牌字符的矩形,直接對矩形進行roi,而後在進行下一步的識別操做(補充:這部分能夠拿到座標後,本身寫算法也是同樣,看我的習慣)。.net
(返回的是水平的矩形)code
Rect boundingRect( InputArray points );
(最小面積則其返回的矩形基本都是旋轉的,注意返回的類型)blog
RotatedRect minAreaRect( InputArray points );
void minEnclosingCircle( InputArray points, Point2f& center, float& radius );
(注意:至少須要輸入6個點)索引
RotatedRect fitEllipse( InputArray points );
返回提取四個點的代碼:圖片
cv::RotatedRect rotateRect = cv::minAreaRect(hullPoints); cv::Point2f vertex[4]; rotateRect.points(vertex);
void approxPolyDP( InputArray curve, OutputArray approxCurve, double epsilon, bool closed );
double minEnclosingTriangle( InputArray points, OutputArray triangle );
void OpenCVManager::testFitting() { QString fileName1 = "E:/qtProject/openCVDemo/openCVDemo/modules/openCVManager/images/10.jpg"; cv::Mat srcMat = cv::imread(fileName1.toStdString()); cv::Mat dstMat; int width = 400; int height = 300; 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 * 4), srcMat.type()); int sigmaS = 100; int sigmaR = 1.0; int thresh = 232; 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); // 車牌時,對灰度圖取反操做 // tempMat = ~tempMat; { // 調整閾值化的參數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 * 0, srcMat.rows * 1), cv::Range(srcMat.cols * 2, srcMat.cols * 3)); // 轉換圖像 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); // 擬合矩形框 cv::Mat fittingRectMat = srcMat.clone(); cv::Mat fittingMinAreaRectMat = srcMat.clone(); cv::Mat fittingMinAreaCircleMat = srcMat.clone(); cv::Mat fittingEllipseMat = srcMat.clone(); cv::Mat fittingPolyMat = srcMat.clone(); // 輪廓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, 0), 2); cv::line(dstMat, hullPoints.at(index2 - 1), hullPoints.at(index2), cv::Scalar(0, 0, 0), 2); } qDebug() << __FILE__ << __LINE__ << "index =" << index << "total =" << hierarchy.size(); // 使用形狀擬合 // 使用外部包圍矩形 { cv::Rect rect = cv::boundingRect(hullPoints); cv::rectangle(fittingRectMat, rect, cv::Scalar(0, 255, 0), 2); } // 使用外部最小包圍矩形 { cv::RotatedRect rotateRect = cv::minAreaRect(hullPoints); cv::Point2f vertex[4]; rotateRect.points(vertex); for(int index = 0; index < 4; index++) { cv::line(fittingMinAreaRectMat, vertex[index % 4], vertex[(index + 1) % 4], cv::Scalar(255, 0, 0), 2); } } // 使用外部包圍圓形(圓形就是最小了,不存在形變) { cv::Point2f center; float radius; cv::minEnclosingCircle(hullPoints, center, radius); cv::circle(fittingMinAreaCircleMat, center, radius, cv::Scalar(0, 0, 0), 2); } // 使用外部橢圓擬合:至少要6個點 { qDebug() << __FILE__ << __LINE__ << hullPoints.size(); if(hullPoints.size() >= 6) { cv::RotatedRect rotateRect = cv::fitEllipse(hullPoints); cv::ellipse(fittingEllipseMat, rotateRect, cv::Scalar(0, 0, 0), 2); } } // 使用多邊形擬合 { std::vector<cv::Point> polyPoints; cv::approxPolyDP(hullPoints, polyPoints, 3, true); std::vector<std::vector<cv::Point>> contour; contour.push_back(polyPoints); cv::drawContours(fittingPolyMat, contour, 0, cv::Scalar(0, 0, 0), 2); } } // 效果圖copy:輪廓圖 mat = windowMat(cv::Range(srcMat.rows * 1, srcMat.rows * 2), cv::Range(srcMat.cols * 2, srcMat.cols * 3)); 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 * 0, srcMat.cols * 1)); cv::addWeighted(mat, 0.0f, dstMat, 1.0f, 0.0f, mat); // 效果圖copy:黑色圖擬合矩形 mat = windowMat(cv::Range(srcMat.rows * 2, srcMat.rows * 3), cv::Range(srcMat.cols * 1, srcMat.cols * 2)); cv::addWeighted(mat, 0.0f, fittingRectMat, 1.0f, 0.0f, mat); // 效果圖copy:原圖擬合矩形 mat = windowMat(cv::Range(srcMat.rows * 2, srcMat.rows * 3), cv::Range(srcMat.cols * 1, srcMat.cols * 2)); cv::addWeighted(mat, 0.0f, fittingRectMat, 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, fittingMinAreaRectMat, 1.0f, 0.0f, mat); // 效果圖copy:原圖擬合最小圓形 mat = windowMat(cv::Range(srcMat.rows * 3, srcMat.rows * 4), cv::Range(srcMat.cols * 0, srcMat.cols * 1)); cv::addWeighted(mat, 0.0f, fittingMinAreaCircleMat, 1.0f, 0.0f, mat); // 效果圖copy:原圖擬合最小橢圓 mat = windowMat(cv::Range(srcMat.rows * 3, srcMat.rows * 4), cv::Range(srcMat.cols * 1, srcMat.cols * 2)); cv::addWeighted(mat, 0.0f, fittingEllipseMat, 1.0f, 0.0f, mat); // 效果圖copy:原圖擬合多邊形 mat = windowMat(cv::Range(srcMat.rows * 3, srcMat.rows * 4), cv::Range(srcMat.cols * 2, srcMat.cols * 3)); cv::addWeighted(mat, 0.0f, fittingPolyMat, 1.0f, 0.0f, mat); } } // 更新 cvui::update(); // 顯示 cv::imshow(windowName, windowMat); // esc鍵退出 if(cv::waitKey(25) == 27) { break; } } }
對應版本號v1.50.0