opencv2(2017.5.5)

1.图像的腐蚀和膨胀

前提:输入的是二值图像
腐蚀:用该像素周围像素集合中最小像素替换当前像素;
膨胀:用该像素周围像素集合中最大像素替换当前像素;
//读取图像
cv::Mat image=imread("binary.bmp");
//腐蚀图像

cv::erode(image,eroded,cv::Mat());```
//显示腐蚀后的图像
```cv:namedWindow("Eroded image");
cv::imshow("Eroded Image",eroded);```
//膨胀图像
```cv::Mat dilated;//目标图像
cv::dilate(image,dilated,cv::Mat());//cv::Mat()表示使用3*3的方形结构像素```
//显示膨胀后的图像
```cv:namedWindow("Dilated image");
cv::imshow("Dilated Image",dilated);```
#2.开闭计算
***闭运算:对图像先膨胀再腐蚀;***
```cv:Mat element5(5,5,CV_8U,cv::Scalar(1));
cv::Mat closed;
cv::morphologyEx(image,closed,cv::MORPH_CLOSE,element5);```
***开运算:对图像先腐蚀再膨胀;***
```cv::Mat opened;
cv::morphologyEx(image,opened,cv::MORPH_OPEN,element5);```
如果想先处理噪点,可以先进行开运算,再进行闭运算;
#3.使用形态学滤波对图像进行边缘及角点检测
***下面是如何进行直线检测:***

class MorphoFeatures{
private:
int threshold;//用于生成二值图像的阈值
cv::Mat cross;//角点检测中用到的结构元素//十字形
cv::Mat diamond;//菱形
cv::Mat x;//x型
cv::Mat square;//方形
public:
cv::Mat getEdges(const cv::Mat &image);
void applyThreshold(cv::Mat& result);
}
cv::Mat getEdges(const cv::Mat &image){
//得到梯度图
cv::Mat result;
cv::morphologyEx(image,result,cv::MORPH_GRADIENT,cv::Mat());
//阈值化以得到二值图像
applyThreshold(result);
return result;
}
void applyThreshold(cv::Mat& result){
//使用阈值化
if(threshold>0)
cv::threshold(result,result,threshold,255,cv::THRESH_BINARY);
}
int main(){
//创建形态学实例对象
MorphoFeatures morpho;
morpho.setThreshold(40);
//获取边缘
cv::Mat edges;
edges=morpho.getEdges(image);
}
下面是角点检测(opencv 没有直接实现它,事实上需要四种结构元素:方形,菱形,十字形,X形):
//构造函数
MorphoFeatures():threshold(-1),cross(5,5,CV_8U,cv::Scalar(0)),diamond(5,5,CV_8U,cv::Scalar(1)),square(5,5,CV_8U,cv::Scalar(1)),x(5,5,CV_8U,cv::Scalar(0)){
//创建十字形元素
for(int i=0;i<5;i++){
cross.at<unchar>(2,i)=1;
cross.at<unchar>(i,2)=1;
}
//创建菱形元素
diamond.at<unchar>(0,0)=0;
diamond.at<unchar>(0,1)=0;
diamond.at<unchar>(1,0)=0;
diamond.at<unchar>(4,4)=0;
diamond.at<unchar>(4,3)=0;
diamond.at<unchar>(3,4)=0;
diamond.at<unchar>(0,4)=0;
diamond.at<unchar>(0,3)=0;
diamond.at<unchar>(1,4)=0;
diamond.at<unchar>(4,0)=0;
diamond.at<unchar>(3,0)=0;
diamond.at<unchar>(4,1)=0;
//创建X形
for(i=0;i<5;i++){
x.at<unchar>(i,i)=1;
x.at<unchar>(4-i,i)=1;
}
}
cv::Mat getCorners(const cv::Mat &image){
cv::Mat result;
cv::dilate(image,result,cross);//十字形膨胀
cv::erode(result,result,diamond);//菱形腐蚀
cv::Mat result2;
cv::dilate(image,result2,x)//x形膨胀
cv::erode(result2,result2,square);//方形腐蚀
** cv::absdiff(result2,result,result);//通过对两张图像做差值得到角点图像**
applyThreshold(result);//阈值化以得到二值图
return result;
}
为了更好地可视化,在二值化图像上每个检测点绘制一个圆:
void drawOnImge(const cv::Mat& binary,cv::Mat& image){
cv::Mat_<unchar>::const_iterator it=binary.begin<unchar>();
cv::Mat_<unchar>::const_iterator itend=binary.end<unchar>();
//遍历每个元素
for(int i=0;i<itend;it++){
if(!*it)
cv::circle(image,cv::Point(i%image.step,i/image.step),5,cv::Scalar(255,0,0));
}
}

#4.使用分水岭算法对图像进行分割
分水岭分割方法,是一种基于拓扑理论的数学形态学的分割方法,其基本思想是把图像看作是测地学上的拓扑地貌,图像中每一点像素的灰度值表示该点的海拔高度,每一个局部极小值及其影响区域称为集水盆,而集水盆的边界则形成分水岭。分水岭的概念和形成可以通过模拟浸入过程来说明。在每一个局部极小值表面,刺穿一个小孔,然后把整个模型慢慢浸入水中,随着浸入的加深,每一个局部极小值的影响域慢慢向外扩展,在两个集水盆汇合处构筑大坝,即形成分水岭。
分水岭算法一般和区域生长法或聚类分析法相结合。
分水岭算法一般用于分割感兴趣的图像区域,应用如细胞边界的分割,分割出相片中的头像等等。

class WatershedSegmenter {
private:
cv::Mat markers;
public:
void setMarkers(const cv::Mat& markerImage) {
// Convert to image of ints
marker Image.convertTo(markers,CV_32S);
}
cv::Mat process(const cv::Mat &image) {
// Apply watershed
cv::watershed(image,markers);
return markers;
}
// Return result in the form of an image
cv::Mat getSegmentation() {
cv::Mat tmp;
// all segment with label higher than 255
// will be assigned value 255
markers.convertTo(tmp,CV_8U);
returntmp;
}
// Return watershed in the form of an image
cv::Mat getWatersheds() {
cv::Mat tmp;
markers.convertTo(tmp,CV_8U,255,255);
returnt tmp;
}
};```

5.grabcut算法进行图像分割
void cv::grabCut( const Mat& img, Mat& mask, Rect rect, Mat& bgdModel, Mat& fgdModel, int iterCount, int mode )
-img——待分割的源图像,必须是8位3通道(CV_8UC3)图像,在处理的过程中不会被修改;
-mask——掩码图像,如果使用掩码进行初始化,那么mask保存初始化掩码信息;在执行分割的时候,也可以将用户交互所设定的前景与背景保 存到mask中,然后再传入grabCut函数;在处理结束之后,mask中会保存结果。mask只能取以下四种值:
GCD_BGD(=0),背景;
GCD_FGD(=1),前景;
GCD_PR_BGD(=2),可能的背景;
GCD_PR_FGD(=3),可能的前景。
如果没有手工标记GCD_BGD或者GCD_FGD,那么结果只会有GCD_PR_BGD或GCD_PR_FGD;
-rect——用于限定需要进行分割的图像范围,只有该矩形窗口内的图像部分才被处理;
-bgdModel——背景模型,如果为null,函数内部会自动创建一个bgdModel;bgdModel必须是单通道浮点型(CV_32FC1)图像,且行数只能为 1,列数只能为13x5;
-fgdModel——前景模型,如果为null,函数内部会自动创建一个fgdModel;fgdModel必须是单通道浮点型(CV_32FC1)图像,且行数只能为1, 列数只能为13x5;
-iterCount——迭代次数,必须大于0;
-mode——用于指示grabCut函数进行什么操作,可选的值有:
GC_INIT_WITH_RECT(=0),用矩形窗初始化GrabCut;
GC_INIT_WITH_MASK(=1),用掩码图像初始化GrabCut;
GC_EVAL(=2),执行分割
您可以按以下方式来使用GrabCut函数:
(1)用矩形窗或掩码图像初始化grabCut;
(2)执行分割;
(3)如果对结果不满意,在掩码图像中设定前景和(或)背景,再次执行分割;
(4)使用掩码图像中的前景或背景信息。

#include<cv.h>
#include<highgui.h> 
#include<opencv2/opencv.hpp> 
usingnamespacecv;
usingnamespacestd;
#include 
void getBinMask(constMat& comMask, Mat& binMask )
{
     binMask.create( comMask.size(), CV_8UC1 );
     binMask = comMask & 1;
}
int main(int argc,char** argv )
{
     Mat image = imread("f:\\img\\lena.jpg", 1 );
     conststring winName ="image";
     imshow("src",image);
/***********************************/
Mat bg;Mat fg;
Rect rect = Rect(47,48,408,464);
Mat mask,res;
mask.create( image.size(), CV_8UC1);
grabCut( image, mask, rect, bg, fg, 1, 0 );
Mat binMask;
getBinMask( mask, binMask );
image.copyTo( res, binMask );
imshow("cut",res);
/***********************************/
waitKey(0);
return0;
}

6.Harris特征、SURF特征
基于特征点的图像匹配是图像处理中经常会遇到的问题,手动选取特征点太麻烦了。比较经典常用的特征点自动提取的办法有Harris特征、SIFT特征、SURF特征。
先介绍利用SURF特征的特征描述办法,其操作封装在类Surf FeatureDetector中,利用类内的detect函数可以检测出SURF特征的关键点,保存在vector容器中。第二部利用Surf DescriptorExtractor类进行特征向量的相关计算。将之前的vector变量变成向量矩阵形式保存在Mat中。最后强行匹配两幅图像的特征向量,利用了类BruteForceMatcher中的函数match。代码如下:

#include<stdio.h>
#include<iostream>
#include "opencv2/core/core.hpp"
#include "opencv2/features2d/features2d.hpp"
#include "opencv2/highgui/highgui.hpp"
using namespace cv;
int main(int argc,char** argv )
{
     if( argc != 3 )
     {return-1; }
     Mat img_1 = imread( argv[1], CV_LOAD_IMAGE_GRAYSCALE );
     Mat img_2 = imread( argv[2], CV_LOAD_IMAGE_GRAYSCALE );
     if( !img_1.data || !img_2.data )
     {return-1; }
//-- Step 1: Detect the keypoints using SURF Detector
     int minHessian = 400;
     SurfFeatureDetector detector( minHessian );
     std::vector keypoints_1, keypoints_2;
     detector.detect( img_1, keypoints_1 );
     detector.detect( img_2, keypoints_2 );
//-- Step 2: Calculate descriptors (feature vectors)
     SurfDescriptorExtractor extractor;
     Mat descriptors_1, descriptors_2;
     extractor.compute( img_1, keypoints_1, descriptors_1 );
     extractor.compute( img_2, keypoints_2, descriptors_2 );
//-- Step 3: Matching descriptor vectors with a brute force matcher
     BruteForceMatcher< L2 > matcher;
     std::vector< DMatch > matches;
     matcher.match( descriptors_1, descriptors_2, matches );
//-- Draw matches
     Mat img_matches;
     drawMatches( img_1, keypoints_1, img_2, keypoints_2, matches, img_matches );
//-- Show detected matches
     imshow("Matches", img_matches );
     waitKey(0);
     return 0;
}```
当然,进行强匹配的效果不够理想,这里再介绍一种FLANN特征匹配算法。前两步与上述代码相同,第三步利用FlannBasedMatcher类进行特征匹配,并只保留好的特征匹配点,代码如下:

//-- Step 3: Matching descriptor vectors using FLANN matcher
FlannBasedMatcher matcher;
std::vector< DMatch > matches;
matcher.match( descriptors_1, descriptors_2, matches );
doublemax_dist = 0;doublemin_dist = 100;
//-- Quick calculation of max and min distances between keypoints
for(inti = 0; i < descriptors_1.rows; i++ )
{
doubledist = matches[i].distance;
if( dist < min_dist ) min_dist = dist;
if( dist > max_dist ) max_dist = dist;
}
printf("-- Max dist : %f \n", max_dist );
printf("-- Min dist : %f \n", min_dist );
//-- Draw only "good" matches (i.e. whose distance is less than 2min_dist )
//-- PS.- radiusMatch can also be used here.
std::vector< DMatch > good_matches;
for(inti = 0; i < descriptors_1.rows; i++ )
{
if( matches[i].distance < 2
min_dist )
good_matches.push_back( matches[i]);
}
//-- Draw only "good" matches
Mat img_matches;
drawMatches( img_1, keypoints_1, img_2, keypoints_2,
good_matches, img_matches, Scalar::all(-1), Scalar::all(-1),
vector(), DrawMatchesFlags::NOT_DRAW_SINGLE_POINTS );
//-- Show detected matches
imshow("Good Matches", img_matches );

然后再看一下Harris特征检测,在计算机视觉中,通常需要找出两帧图像的匹配点,如果能找到两幅图像如何相关,就能提取出两幅图像的信息。我们说的特征的最大特点就是它具有唯一可识别这一特点,图像特征的类型通常指边界、角点(兴趣点)、斑点(兴趣区域)。角点就是图像的一个局部特征,应用广泛。harris角点检测是一种直接基于灰度图像的角点提取算法,稳定性高,尤其对L型角点检测精度高,但由于采用了高斯滤波,运算速度相对较慢,角点信息有丢失和位置偏移的现象,而且角点提取有聚簇现象。具体实现就是使用函数cornerHarris实现。
除了利用Harris进行角点检测,还可以利用Shi-Tomasi方法进行角点检测。使用函数goodFeaturesToTrack对角点进行检测,效果也不错。也可以自己制作角点检测的函数,需要用到cornerMinEigenVal函数和minMaxLoc函数,最后的特征点选取,判断条件要根据自己的情况编辑。如果对特征点,角点的精度要求更高,可以用cornerSubPix函数将角点定位到子像素。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,189评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,577评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,857评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,703评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,705评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,620评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,995评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,656评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,898评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,639评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,720评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,395评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,982评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,953评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,195评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,907评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,472评论 2 342

推荐阅读更多精彩内容