实现原理
物体识别是图像处理学在现实生活中较多的应用之一,目前最为流行的就是运用AI、机器学习等技术结合图像处理学,大量训练数据集,以实现智能且精确的识别。说到人工智能,很多人可能觉得它非常深奥和复杂,其实说白了它最底层的识别逻辑还是基于普通的图像分析,像特征提取、轮廓分析、比对分析等等,再在庞大的数据集中按照相似程度,分析出一个最可能的结果。
本文提供了一种相对简单的思路来实现绿叶识别,适合初学图像处理的新人研究参考。该方法为差分法:首先对图像进行高斯滤波处理预处理,平滑图像数据;其次,将图像颜色通道按RGB拆分,因为识别物为绿叶,其最明显的特征就是颜色;差分法,将绿色通道减去蓝色通道,之所以选择这两个通道,是因为蓝色通道和绿叶的关系较远,而红色搭配绿色可是黄色哦,绿叶中存在黄色特征信息可是再正常不过了;之后,对差分图进行OTSU阈值处理,得到掩膜感兴趣ROI区域;再后,就是对区域进行闭运算和孔洞闭合处理,保持区域完整性;最后,根据掩膜提取绿叶,完成。
功能函数代码
1)识别绿叶函数。
// 识别绿叶 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); // 绿通道-蓝通道,提取绿色区域 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); // 识别区域标记 result = input.clone(); result.setTo(Scalar(0, 0, 0), mask == 0); return result; }
2)清除微小面积连通区函数,用于孔洞闭合。具体介绍见:
/** * @brief Clear_MicroConnected_Areas 清除微小面积连通区函数 * @param src 输入图像矩阵 * @param dst 输出结果 * @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; // 创建轮廓容器 std::vector<cv::Vec4i> hierarchy; // 寻找轮廓的函数 // 第四个参数CV_RETR_EXTERNAL,表示寻找最外围轮廓 // 第五个参数CV_CHAIN_APPROX_NONE,表示保存物体边界上所有连续的轮廓点到contours向量内 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函数计算连通区面积 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++) { // 将连通区的值置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 清除微小面积连通区函数 * @param src 输入图像矩阵 * @param dst 输出结果 * @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; // 创建轮廓容器 std::vector<cv::Vec4i> hierarchy; // 寻找轮廓的函数 // 第四个参数CV_RETR_EXTERNAL,表示寻找最外围轮廓 // 第五个参数CV_CHAIN_APPROX_NONE,表示保存物体边界上所有连续的轮廓点到contours向量内 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函数计算连通区面积 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++) { // 将连通区的值置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); // 绿通道-蓝通道,提取绿色区域 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); // 识别区域标记 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所示,因为孔洞闭合的缘故,导致绿叶间的间隙也被涵盖了;又或者,当所识别的绿叶没那么绿,有点偏暗时,蓝色通道的比例自然也提高了,此时用差分法效果就不会那么好了。
总而言之,不同的场景和需求还是需要结合实际进行算法的设计,天下没有一种算法是可以解决一切问题的,即便是人工智能也不可能,特殊问题特殊对待,加油!
如果函数有什么可以改进完善的地方,非常欢迎大家指出,一同进步何乐而不为呢~
如果文章帮助到你了,可以点个赞让我知道,我会很快乐~加油!