基于OpenCV?差分法實(shí)現(xiàn)綠葉識(shí)別
實(shí)現(xiàn)原理
物體識(shí)別是圖像處理學(xué)在現(xiàn)實(shí)生活中較多的應(yīng)用之一,目前最為流行的就是運(yùn)用AI、機(jī)器學(xué)習(xí)等技術(shù)結(jié)合圖像處理學(xué),大量訓(xùn)練數(shù)據(jù)集,以實(shí)現(xiàn)智能且精確的識(shí)別。說(shuō)到人工智能,很多人可能覺(jué)得它非常深?yuàn)W和復(fù)雜,其實(shí)說(shuō)白了它最底層的識(shí)別邏輯還是基于普通的圖像分析,像特征提取、輪廓分析、比對(duì)分析等等,再在龐大的數(shù)據(jù)集中按照相似程度,分析出一個(gè)最可能的結(jié)果。
本文提供了一種相對(duì)簡(jiǎn)單的思路來(lái)實(shí)現(xiàn)綠葉識(shí)別,適合初學(xué)圖像處理的新人研究參考。該方法為差分法:首先對(duì)圖像進(jìn)行高斯濾波處理預(yù)處理,平滑圖像數(shù)據(jù);其次,將圖像顏色通道按RGB拆分,因?yàn)樽R(shí)別物為綠葉,其最明顯的特征就是顏色;差分法,將綠色通道減去藍(lán)色通道,之所以選擇這兩個(gè)通道,是因?yàn)樗{(lán)色通道和綠葉的關(guān)系較遠(yuǎn),而紅色搭配綠色可是黃色哦,綠葉中存在黃色特征信息可是再正常不過(guò)了;之后,對(duì)差分圖進(jìn)行OTSU閾值處理,得到掩膜感興趣ROI區(qū)域;再后,就是對(duì)區(qū)域進(jìn)行閉運(yùn)算和孔洞閉合處理,保持區(qū)域完整性;最后,根據(jù)掩膜提取綠葉,完成。
功能函數(shù)代碼
1)識(shí)別綠葉函數(shù)。
// 識(shí)別綠葉 Mat IdentifyLeaves(cv::Mat input) { CV_Assert(input.channels() == 3); Mat temp, result, mask, hole; int row = input.rows; int col = input.cols; // 高斯濾波 GaussianBlur(input, temp, Size(5, 5), 0); // 通道拆分 vector<cv::Mat> c; split(temp, c); // 綠通道-藍(lán)通道,提取綠色區(qū)域 Mat diff = c[1] - c[0]; threshold(diff, mask, 0, 255, THRESH_OTSU); // 閉運(yùn)算封口 cv::Mat element = getStructuringElement(MORPH_ELLIPSE, Size(9, 9)); cv::morphologyEx(mask, mask, MORPH_CLOSE, element); // 孔洞閉合 hole = 255 - mask; Clear_MicroConnected_Areas(hole, hole, row*col / 300); mask = 255 - hole; Clear_MicroConnected_Areas(mask, mask, row*col / 300); // 識(shí)別區(qū)域標(biāo)記 result = input.clone(); result.setTo(Scalar(0, 0, 0), mask == 0); return result; }
2)清除微小面積連通區(qū)函數(shù),用于孔洞閉合。具體介紹見(jiàn):
/** * @brief Clear_MicroConnected_Areas 清除微小面積連通區(qū)函數(shù) * @param src 輸入圖像矩陣 * @param dst 輸出結(jié)果 * @return min_area 設(shè)定的最小面積清除閾值 */ void Clear_MicroConnected_Areas(cv::Mat src, cv::Mat &dst, double min_area) { // 備份復(fù)制 dst = src.clone(); std::vector<std::vector<cv::Point> > contours; // 創(chuàng)建輪廓容器 std::vector<cv::Vec4i> hierarchy; // 尋找輪廓的函數(shù) // 第四個(gè)參數(shù)CV_RETR_EXTERNAL,表示尋找最外圍輪廓 // 第五個(gè)參數(shù)CV_CHAIN_APPROX_NONE,表示保存物體邊界上所有連續(xù)的輪廓點(diǎn)到contours向量?jī)?nèi) cv::findContours(src, contours, hierarchy, cv::RETR_TREE, cv::CHAIN_APPROX_NONE, cv::Point()); if (!contours.empty() && !hierarchy.empty()) { std::vector<std::vector<cv::Point> >::const_iterator itc = contours.begin(); // 遍歷所有輪廓 while (itc != contours.end()) { // 定位當(dāng)前輪廓所在位置 cv::Rect rect = cv::boundingRect(cv::Mat(*itc)); // contourArea函數(shù)計(jì)算連通區(qū)面積 double area = contourArea(*itc); // 若面積小于設(shè)置的閾值 if (area < min_area) { // 遍歷輪廓所在位置所有像素點(diǎn) for (int i = rect.y; i < rect.y + rect.height; i++) { uchar *output_data = dst.ptr<uchar>(i); for (int j = rect.x; j < rect.x + rect.width; j++) { // 將連通區(qū)的值置0 if (output_data[j] == 255) { output_data[j] = 0; } } } } itc++; } } }
C++測(cè)試代碼
#include <iostream> #include <opencv2/opencv.hpp> using namespace std; using namespace cv; void Clear_MicroConnected_Areas(cv::Mat src, cv::Mat &dst, double min_area); Mat IdentifyLeaves(cv::Mat input); int main() { Mat src = imread("test1.png"); Mat result = IdentifyLeaves(src); imshow("src", src); imshow("result", result); waitKey(0); return 0; } /** * @brief Clear_MicroConnected_Areas 清除微小面積連通區(qū)函數(shù) * @param src 輸入圖像矩陣 * @param dst 輸出結(jié)果 * @return min_area 設(shè)定的最小面積清除閾值 */ void Clear_MicroConnected_Areas(cv::Mat src, cv::Mat &dst, double min_area) { // 備份復(fù)制 dst = src.clone(); std::vector<std::vector<cv::Point> > contours; // 創(chuàng)建輪廓容器 std::vector<cv::Vec4i> hierarchy; // 尋找輪廓的函數(shù) // 第四個(gè)參數(shù)CV_RETR_EXTERNAL,表示尋找最外圍輪廓 // 第五個(gè)參數(shù)CV_CHAIN_APPROX_NONE,表示保存物體邊界上所有連續(xù)的輪廓點(diǎn)到contours向量?jī)?nèi) cv::findContours(src, contours, hierarchy, cv::RETR_TREE, cv::CHAIN_APPROX_NONE, cv::Point()); if (!contours.empty() && !hierarchy.empty()) { std::vector<std::vector<cv::Point> >::const_iterator itc = contours.begin(); // 遍歷所有輪廓 while (itc != contours.end()) { // 定位當(dāng)前輪廓所在位置 cv::Rect rect = cv::boundingRect(cv::Mat(*itc)); // contourArea函數(shù)計(jì)算連通區(qū)面積 double area = contourArea(*itc); // 若面積小于設(shè)置的閾值 if (area < min_area) { // 遍歷輪廓所在位置所有像素點(diǎn) for (int i = rect.y; i < rect.y + rect.height; i++) { uchar *output_data = dst.ptr<uchar>(i); for (int j = rect.x; j < rect.x + rect.width; j++) { // 將連通區(qū)的值置0 if (output_data[j] == 255) { output_data[j] = 0; } } } } itc++; } } } // 識(shí)別綠葉 Mat IdentifyLeaves(cv::Mat input) { CV_Assert(input.channels() == 3); Mat temp, result, mask, hole; int row = input.rows; int col = input.cols; // 高斯濾波 GaussianBlur(input, temp, Size(5, 5), 0); // 通道拆分 vector<cv::Mat> c; split(temp, c); // 綠通道-藍(lán)通道,提取綠色區(qū)域 Mat diff = c[1] - c[0]; threshold(diff, mask, 0, 255, THRESH_OTSU); // 閉運(yùn)算封口 cv::Mat element = getStructuringElement(MORPH_ELLIPSE, Size(9, 9)); cv::morphologyEx(mask, mask, MORPH_CLOSE, element); // 孔洞閉合 hole = 255 - mask; Clear_MicroConnected_Areas(hole, hole, row*col / 300); mask = 255 - hole; Clear_MicroConnected_Areas(mask, mask, row*col / 300); // 識(shí)別區(qū)域標(biāo)記 result = input.clone(); result.setTo(Scalar(0, 0, 0), mask == 0); return result; }
測(cè)試效果
圖1 原圖1
圖2 效果圖1
圖3 原圖2
圖4 效果圖2
圖5 原圖3
圖6 效果圖3
本文只是提供了一種簡(jiǎn)單的識(shí)別思路,不可能滿足所有的場(chǎng)景。舉幾個(gè)例子,如圖6所示,因?yàn)榭锥撮]合的緣故,導(dǎo)致綠葉間的間隙也被涵蓋了;又或者,當(dāng)所識(shí)別的綠葉沒(méi)那么綠,有點(diǎn)偏暗時(shí),藍(lán)色通道的比例自然也提高了,此時(shí)用差分法效果就不會(huì)那么好了。
總而言之,不同的場(chǎng)景和需求還是需要結(jié)合實(shí)際進(jìn)行算法的設(shè)計(jì),天下沒(méi)有一種算法是可以解決一切問(wèn)題的,即便是人工智能也不可能,特殊問(wèn)題特殊對(duì)待,加油!
到此這篇關(guān)于基于OpenCV 差分法實(shí)現(xiàn)綠葉識(shí)別(圖像差分+顏色通道)的文章就介紹到這了,更多相關(guān)OpenCV 內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C語(yǔ)言實(shí)現(xiàn)學(xué)生學(xué)籍管理系統(tǒng)
這篇文章主要為大家詳細(xì)介紹了C語(yǔ)言實(shí)現(xiàn)學(xué)生學(xué)籍管理系統(tǒng),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-01-01VSstudio中scanf返回值被忽略的原因及解決方法(推薦)
這篇文章主要介紹了VSstudio中scanf返回值被忽略的原因及其解決方法,scanf返回值被忽略,接下來(lái)我就告訴大家該如何解決這個(gè)問(wèn)題,需要的朋友可以參考下2022-09-09五個(gè)經(jīng)典鏈表OJ題帶你進(jìn)階C++鏈表篇
做題之前呢,小編想提醒下大家,要三思而后行,不要一上來(lái)就嘎嘎敲代碼,要先學(xué)會(huì)自己畫(huà)圖分析,把自己的思路捋清楚,不要到時(shí)候?qū)懘a五分鐘,調(diào)試兩小時(shí),記住,編程思路很重要2022-03-03