基于OpenCV?差分法實現(xiàn)綠葉識別
實現(xiàn)原理
物體識別是圖像處理學在現(xiàn)實生活中較多的應用之一,目前最為流行的就是運用AI、機器學習等技術結(jié)合圖像處理學,大量訓練數(shù)據(jù)集,以實現(xiàn)智能且精確的識別。說到人工智能,很多人可能覺得它非常深奧和復雜,其實說白了它最底層的識別邏輯還是基于普通的圖像分析,像特征提取、輪廓分析、比對分析等等,再在龐大的數(shù)據(jù)集中按照相似程度,分析出一個最可能的結(jié)果。
本文提供了一種相對簡單的思路來實現(xiàn)綠葉識別,適合初學圖像處理的新人研究參考。該方法為差分法:首先對圖像進行高斯濾波處理預處理,平滑圖像數(shù)據(jù);其次,將圖像顏色通道按RGB拆分,因為識別物為綠葉,其最明顯的特征就是顏色;差分法,將綠色通道減去藍色通道,之所以選擇這兩個通道,是因為藍色通道和綠葉的關系較遠,而紅色搭配綠色可是黃色哦,綠葉中存在黃色特征信息可是再正常不過了;之后,對差分圖進行OTSU閾值處理,得到掩膜感興趣ROI區(qū)域;再后,就是對區(qū)域進行閉運算和孔洞閉合處理,保持區(qū)域完整性;最后,根據(jù)掩膜提取綠葉,完成。
功能函數(shù)代碼
1)識別綠葉函數(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);
// 綠通道-藍通道,提取綠色區(qū)域
Mat diff = c[1] - c[0];
threshold(diff, mask, 0, 255, THRESH_OTSU);
// 閉運算封口
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);
// 識別區(qū)域標記
result = input.clone();
result.setTo(Scalar(0, 0, 0), mask == 0);
return result;
}
2)清除微小面積連通區(qū)函數(shù),用于孔洞閉合。具體介紹見:
/**
* @brief Clear_MicroConnected_Areas 清除微小面積連通區(qū)函數(shù)
* @param src 輸入圖像矩陣
* @param dst 輸出結(jié)果
* @return min_area 設定的最小面積清除閾值
*/
void Clear_MicroConnected_Areas(cv::Mat src, cv::Mat &dst, double min_area)
{
// 備份復制
dst = src.clone();
std::vector<std::vector<cv::Point> > contours; // 創(chuàng)建輪廓容器
std::vector<cv::Vec4i> hierarchy;
// 尋找輪廓的函數(shù)
// 第四個參數(shù)CV_RETR_EXTERNAL,表示尋找最外圍輪廓
// 第五個參數(shù)CV_CHAIN_APPROX_NONE,表示保存物體邊界上所有連續(xù)的輪廓點到contours向量內(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())
{
// 定位當前輪廓所在位置
cv::Rect rect = cv::boundingRect(cv::Mat(*itc));
// contourArea函數(shù)計算連通區(qū)面積
double area = contourArea(*itc);
// 若面積小于設置的閾值
if (area < min_area)
{
// 遍歷輪廓所在位置所有像素點
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++測試代碼
#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 設定的最小面積清除閾值
*/
void Clear_MicroConnected_Areas(cv::Mat src, cv::Mat &dst, double min_area)
{
// 備份復制
dst = src.clone();
std::vector<std::vector<cv::Point> > contours; // 創(chuàng)建輪廓容器
std::vector<cv::Vec4i> hierarchy;
// 尋找輪廓的函數(shù)
// 第四個參數(shù)CV_RETR_EXTERNAL,表示尋找最外圍輪廓
// 第五個參數(shù)CV_CHAIN_APPROX_NONE,表示保存物體邊界上所有連續(xù)的輪廓點到contours向量內(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())
{
// 定位當前輪廓所在位置
cv::Rect rect = cv::boundingRect(cv::Mat(*itc));
// contourArea函數(shù)計算連通區(qū)面積
double area = contourArea(*itc);
// 若面積小于設置的閾值
if (area < min_area)
{
// 遍歷輪廓所在位置所有像素點
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++;
}
}
}
// 識別綠葉
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);
// 綠通道-藍通道,提取綠色區(qū)域
Mat diff = c[1] - c[0];
threshold(diff, mask, 0, 255, THRESH_OTSU);
// 閉運算封口
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);
// 識別區(qū)域標記
result = input.clone();
result.setTo(Scalar(0, 0, 0), mask == 0);
return result;
}
測試效果

圖1 原圖1

圖2 效果圖1

圖3 原圖2

圖4 效果圖2

圖5 原圖3

圖6 效果圖3
本文只是提供了一種簡單的識別思路,不可能滿足所有的場景。舉幾個例子,如圖6所示,因為孔洞閉合的緣故,導致綠葉間的間隙也被涵蓋了;又或者,當所識別的綠葉沒那么綠,有點偏暗時,藍色通道的比例自然也提高了,此時用差分法效果就不會那么好了。
總而言之,不同的場景和需求還是需要結(jié)合實際進行算法的設計,天下沒有一種算法是可以解決一切問題的,即便是人工智能也不可能,特殊問題特殊對待,加油!
到此這篇關于基于OpenCV 差分法實現(xiàn)綠葉識別(圖像差分+顏色通道)的文章就介紹到這了,更多相關OpenCV 內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
VSstudio中scanf返回值被忽略的原因及解決方法(推薦)
這篇文章主要介紹了VSstudio中scanf返回值被忽略的原因及其解決方法,scanf返回值被忽略,接下來我就告訴大家該如何解決這個問題,需要的朋友可以參考下2022-09-09

