1.圖像的腐蝕和膨脹
前提:輸入的是二值圖像
腐蝕:用該像素周圍像素集合中最小像素替換當(dāng)前像素;
膨脹:用該像素周圍像素集合中最大像素替換當(dāng)前像素遭居;
//讀取圖像
cv::Mat image=imread("binary.bmp");
//腐蝕圖像
cv::erode(image,eroded,cv::Mat());```
//顯示腐蝕后的圖像
```cv:namedWindow("Eroded image");
cv::imshow("Eroded Image",eroded);```
//膨脹圖像
```cv::Mat dilated;//目標(biāo)圖像
cv::dilate(image,dilated,cv::Mat());//cv::Mat()表示使用3*3的方形結(jié)構(gòu)像素```
//顯示膨脹后的圖像
```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);```
如果想先處理噪點烂瘫,可以先進(jìn)行開運算精盅,再進(jìn)行閉運算帽哑;
#3.使用形態(tài)學(xué)濾波對圖像進(jìn)行邊緣及角點檢測
***下面是如何進(jìn)行直線檢測:***
class MorphoFeatures{
private:
int threshold;//用于生成二值圖像的閾值
cv::Mat cross;//角點檢測中用到的結(jié)構(gòu)元素//十字形
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(){
//創(chuàng)建形態(tài)學(xué)實例對象
MorphoFeatures morpho;
morpho.setThreshold(40);
//獲取邊緣
cv::Mat edges;
edges=morpho.getEdges(image);
}
下面是角點檢測(opencv 沒有直接實現(xiàn)它,事實上需要四種結(jié)構(gòu)元素:方形叹俏,菱形妻枕,十字形,X形):
//構(gòu)造函數(shù)
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)){
//創(chuàng)建十字形元素
for(int i=0;i<5;i++){
cross.at<unchar>(2,i)=1;
cross.at<unchar>(i,2)=1;
}
//創(chuàng)建菱形元素
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;
//創(chuàng)建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.使用分水嶺算法對圖像進(jìn)行分割
分水嶺分割方法蝌数,是一種基于拓?fù)淅碚摰臄?shù)學(xué)形態(tài)學(xué)的分割方法愕掏,其基本思想是把圖像看作是測地學(xué)上的拓?fù)涞孛玻瑘D像中每一點像素的灰度值表示該點的海拔高度顶伞,每一個局部極小值及其影響區(qū)域稱為集水盆饵撑,而集水盆的邊界則形成分水嶺。分水嶺的概念和形成可以通過模擬浸入過程來說明枝哄。在每一個局部極小值表面肄梨,刺穿一個小孔,然后把整個模型慢慢浸入水中挠锥,隨著浸入的加深众羡,每一個局部極小值的影響域慢慢向外擴(kuò)展,在兩個集水盆匯合處構(gòu)筑大壩蓖租,即形成分水嶺粱侣。
分水嶺算法一般和區(qū)域生長法或聚類分析法相結(jié)合羊壹。
分水嶺算法一般用于分割感興趣的圖像區(qū)域,應(yīng)用如細(xì)胞邊界的分割齐婴,分割出相片中的頭像等等油猫。
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算法進(jìn)行圖像分割
void cv::grabCut( const Mat& img, Mat& mask, Rect rect, Mat& bgdModel, Mat& fgdModel, int iterCount, int mode )
-img——待分割的源圖像,必須是8位3通道(CV_8UC3)圖像柠偶,在處理的過程中不會被修改情妖;
-mask——掩碼圖像,如果使用掩碼進(jìn)行初始化诱担,那么mask保存初始化掩碼信息毡证;在執(zhí)行分割的時候,也可以將用戶交互所設(shè)定的前景與背景保 存到mask中蔫仙,然后再傳入grabCut函數(shù)料睛;在處理結(jié)束之后,mask中會保存結(jié)果摇邦。mask只能取以下四種值:
GCD_BGD(=0)恤煞,背景;
GCD_FGD(=1)施籍,前景居扒;
GCD_PR_BGD(=2),可能的背景法梯;
GCD_PR_FGD(=3)苔货,可能的前景。
如果沒有手工標(biāo)記GCD_BGD或者GCD_FGD立哑,那么結(jié)果只會有GCD_PR_BGD或GCD_PR_FGD夜惭;
-rect——用于限定需要進(jìn)行分割的圖像范圍,只有該矩形窗口內(nèi)的圖像部分才被處理铛绰;
-bgdModel——背景模型诈茧,如果為null,函數(shù)內(nèi)部會自動創(chuàng)建一個bgdModel捂掰;bgdModel必須是單通道浮點型(CV_32FC1)圖像敢会,且行數(shù)只能為 1,列數(shù)只能為13x5这嚣;
-fgdModel——前景模型鸥昏,如果為null,函數(shù)內(nèi)部會自動創(chuàng)建一個fgdModel姐帚;fgdModel必須是單通道浮點型(CV_32FC1)圖像吏垮,且行數(shù)只能為1, 列數(shù)只能為13x5;
-iterCount——迭代次數(shù)膳汪,必須大于0唯蝶;
-mode——用于指示grabCut函數(shù)進(jìn)行什么操作,可選的值有:
GC_INIT_WITH_RECT(=0)遗嗽,用矩形窗初始化GrabCut粘我;
GC_INIT_WITH_MASK(=1),用掩碼圖像初始化GrabCut痹换;
GC_EVAL(=2)征字,執(zhí)行分割
您可以按以下方式來使用GrabCut函數(shù):
(1)用矩形窗或掩碼圖像初始化grabCut;
(2)執(zhí)行分割娇豫;
(3)如果對結(jié)果不滿意柔纵,在掩碼圖像中設(shè)定前景和(或)背景,再次執(zhí)行分割锤躁;
(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特征
基于特征點的圖像匹配是圖像處理中經(jīng)常會遇到的問題系羞,手動選取特征點太麻煩了。比較經(jīng)典常用的特征點自動提取的辦法有Harris特征霸琴、SIFT特征椒振、SURF特征。
先介紹利用SURF特征的特征描述辦法梧乘,其操作封裝在類Surf FeatureDetector中澎迎,利用類內(nèi)的detect函數(shù)可以檢測出SURF特征的關(guān)鍵點,保存在vector容器中选调。第二部利用Surf DescriptorExtractor類進(jìn)行特征向量的相關(guān)計算夹供。將之前的vector變量變成向量矩陣形式保存在Mat中。最后強行匹配兩幅圖像的特征向量仁堪,利用了類BruteForceMatcher中的函數(shù)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;
}```
當(dāng)然,進(jìn)行強匹配的效果不夠理想弦聂,這里再介紹一種FLANN特征匹配算法鸟辅。前兩步與上述代碼相同,第三步利用FlannBasedMatcher類進(jìn)行特征匹配莺葫,并只保留好的特征匹配點匪凉,代碼如下:
//-- 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 < 2min_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特征檢測,在計算機(jī)視覺中捺檬,通常需要找出兩幀圖像的匹配點再层,如果能找到兩幅圖像如何相關(guān),就能提取出兩幅圖像的信息。我們說的特征的最大特點就是它具有唯一可識別這一特點树绩,圖像特征的類型通常指邊界萨脑、角點(興趣點)、斑點(興趣區(qū)域)饺饭。角點就是圖像的一個局部特征渤早,應(yīng)用廣泛。harris角點檢測是一種直接基于灰度圖像的角點提取算法瘫俊,穩(wěn)定性高鹊杖,尤其對L型角點檢測精度高,但由于采用了高斯濾波扛芽,運算速度相對較慢骂蓖,角點信息有丟失和位置偏移的現(xiàn)象,而且角點提取有聚簇現(xiàn)象川尖。具體實現(xiàn)就是使用函數(shù)cornerHarris實現(xiàn)登下。
除了利用Harris進(jìn)行角點檢測,還可以利用Shi-Tomasi方法進(jìn)行角點檢測叮喳。使用函數(shù)goodFeaturesToTrack對角點進(jìn)行檢測被芳,效果也不錯。也可以自己制作角點檢測的函數(shù)馍悟,需要用到cornerMinEigenVal函數(shù)和minMaxLoc函數(shù)畔濒,最后的特征點選取,判斷條件要根據(jù)自己的情況編輯锣咒。如果對特征點侵状,角點的精度要求更高,可以用cornerSubPix函數(shù)將角點定位到子像素毅整。