最大极值稳定区域,是一种类似分水岭图像的分割与匹配算法。它具有SIFT SURF及 ORB等特征不具备的仿射不变性,近年来广泛应用于图像分割与匹配领域。
详细算法原理介绍可参见链接 http://blog.csdn.net/zhaocj/article/details/40742191
创建MSER类
[cpp] view plain copy //开发环境 vs2013+opencv3.1.0 // 创建MSER对象 cv::Ptr<cv::MSER> mesr1 = cv::MSER::create(2, 10, 5000, 0.5, 0.3); //如果想要了解各参数的含义,首先需要通过以上链接了解算法原理。2表示灰度值的变化量,10和5000表示检测到的组块面积的范围,0.5为最大的变化率,0.3为稳定区域的最小变换量申明输出参数
[cpp] view plain copy std::vector<std::vector<cv::Point> > regContours; std::vector<cv::Rect> bboxes1;MSER检测 [cpp] view plain copy mesr1->detectRegions(gray, regContours, bboxes1);//gray为处理的图像,为单通道灰度图 保存检测到的结果 [cpp] view plain copy cv::Mat mserMapMat =cv::Mat::zeros(gray.size(), CV_8UC1); for (int i = (int)regContours.size() - 1; i >= 0; i--) { // 根据检测区域点生成mser+结果 const std::vector<cv::Point>& r = regContours[i]; for (int j = 0; j < (int)r.size(); j++) { cv::Point pt = r[j]; mserMapMat.at<unsigned char>(pt) = 255; } }
MSER根据需要检测的白色区域和黑色区域,又分为MSER+和MSER-
下面贴上Mser车牌目标检测示例 完整的C++代码 示例图片可到 此处下载
[cpp] view plain copy #include "opencv2/highgui/highgui.hpp" #include "opencv2/features2d.hpp" #include "opencv2/imgproc/imgproc.hpp" #include <iostream> // Mser车牌目标检测 std::vector<cv::Rect> mserGetPlate(cv::Mat srcImage) { // HSV空间转换 cv::Mat gray, gray_neg; cv::Mat hsi; cv::cvtColor(srcImage, hsi, CV_BGR2HSV); // 通道分离 std::vector<cv::Mat> channels; cv::split(hsi, channels); // 提取h通道 gray = channels[1]; // 灰度转换 cv::cvtColor(srcImage, gray, CV_BGR2GRAY); // 取反值灰度 gray_neg = 255 - gray; std::vector<std::vector<cv::Point> > regContours; std::vector<std::vector<cv::Point> > charContours; // 创建MSER对象 cv::Ptr<cv::MSER> mesr1 = cv::MSER::create(2, 10, 5000, 0.5, 0.3); cv::Ptr<cv::MSER> mesr2 = cv::MSER::create(2, 2, 400, 0.1, 0.3); std::vector<cv::Rect> bboxes1; std::vector<cv::Rect> bboxes2; // MSER+ 检测 mesr1->detectRegions(gray, regContours, bboxes1); // MSER-操作 mesr2->detectRegions(gray_neg, charContours, bboxes2); cv::Mat mserMapMat =cv::Mat::zeros(srcImage.size(), CV_8UC1); cv::Mat mserNegMapMat =cv::Mat::zeros(srcImage.size(), CV_8UC1); for (int i = (int)regContours.size() - 1; i >= 0; i--) { // 根据检测区域点生成mser+结果 const std::vector<cv::Point>& r = regContours[i]; for (int j = 0; j < (int)r.size(); j++) { cv::Point pt = r[j]; mserMapMat.at<unsigned char>(pt) = 255; } } // MSER- 检测 for (int i = (int)charContours.size() - 1; i >= 0; i--) { // 根据检测区域点生成mser-结果 const std::vector<cv::Point>& r = charContours[i]; for (int j = 0; j < (int)r.size(); j++) { cv::Point pt = r[j]; mserNegMapMat.at<unsigned char>(pt) = 255; } } // mser结果输出 cv::Mat mserResMat; // mser+与mser-位与操作 mserResMat = mserMapMat & mserNegMapMat; cv::imshow("mserMapMat", mserMapMat); cv::imshow("mserNegMapMat", mserNegMapMat); cv::imshow("mserResMat", mserResMat); // 闭操作连接缝隙 cv::Mat mserClosedMat; cv::morphologyEx(mserResMat, mserClosedMat, cv::MORPH_CLOSE, cv::Mat::ones(1, 20, CV_8UC1)); cv::imshow("mserClosedMat", mserClosedMat); // 寻找外部轮廓 std::vector<std::vector<cv::Point> > plate_contours; cv::findContours(mserClosedMat, plate_contours,CV_RETR_EXTERNAL,CV_CHAIN_APPROX_SIMPLE, cv::Point(0, 0)); // 候选车牌区域判断输出 std::vector<cv::Rect> candidates; for (size_t i = 0; i != plate_contours.size(); ++i) { // 求解最小外界矩形 cv::Rect rect = cv::boundingRect(plate_contours[i]); // 宽高比例 double wh_ratio = rect.width / double(rect.height); // 不符合尺寸条件判断 if (rect.height > 20 && wh_ratio > 4 && wh_ratio < 7) candidates.push_back(rect); } return candidates; } int main() { cv::Mat srcImage = cv::imread("car.jpg"); if (srcImage.empty()) return-1; cv::imshow("src Image", srcImage); // 候选车牌区域检测 std::vector<cv::Rect> candidates; candidates = mserGetPlate(srcImage); // 车牌区域显示 for (int i = 0; i < candidates.size(); ++i) { cv::imshow("rect", srcImage(candidates[i])); cv::waitKey(); } cv::waitKey(0); return 0; }MSER(Maximally Stable Extrernal Regions)是区域检测中影响最大的算法
MSER基于分水岭的概念:对图像进行二值化,二值化阈值取[0, 255],这样二值化图像就经历一个从全黑到全白的过程(就像水位不断上升的俯瞰图)。在这个过程中,有些连通区域面积随阈值上升的变化很小,这种区域就叫MSER。
,其中Qi表示第i个连通区域的面积,Δ表示微小的阈值变化(注水),当vi小于给定阈值时认为该区域为MSER。
显然,这样检测得到的MSER内部灰度值是小于边界的,想象一副黑色背景白色区域的图片,显然这个区域是检测不到的。因此对原图进行一次MSER检测后需要将其反转,再做一次MSER检测,两次操作又称MSER+和MSER-
从上节可以看到,MSER的基本思路很简单,但编码实现是很需要算法和编程技巧的
以下算法步骤基于改进的分水岭算法:注水的地方固定,只有当该处的沟壑水漫出来后才能注入到另一个沟壑
此外,为方便编程,面积变化的计算方式也从双边改为单边检测,即
import sys sys.path.append('/usr/local/lib/python2.7/dist-packages/') from PIL import Image import numpy as np import cv2 import matplotlib.pyplot as plt img = cv2.imread('img/origin2/31.jpg') mser = cv2.MSER_create(_min_area=300) gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) regions, boxes = mser.detectRegions(gray) for box in boxes: x, y, w, h = box cv2.rectangle(img, (x,y),(x+w, y+h), (255, 0, 0), 2) plt.imshow(img,'brg') plt.show()
原文地址:http://blog.csdn.net/hust_bochu_xuchao/article/details/52230694
http://www.cnblogs.com/jkmiao/p/6797252.html
