##形態學及邊緣角點檢測
形態學濾波理論于上世紀90年代提出,目前被廣泛用于分析及處理離散圖像。其基本運算有4個: 膨脹、腐蝕、開啟和閉合, 它們在二值圖像和灰度圖像中各有特點。基于這些基本運算還可推導和組合成各種數學形態學實用算法,用它們可以進行圖像形狀和結構的分析及處理,包括圖像分割、特征抽取、邊緣檢測、 圖像濾波、圖像增強和恢復等。數學形態學方法利用一個稱作結構元素的“探針”收集圖像的信息,當探針在圖像中不斷移動時, 便可考察圖像各個部分之間的相互關系,從而了解圖像的結構特征。數學形態學基于探測的思想,與人的FOA(Focus Of Attention)的視覺特點有類似之處。其中最重要的結構元素,可直接攜帶知識(形態、大小、甚至加入灰度和色度信息)來探測、研究圖像的結構特點。鑒于研究所需,記錄一些知識點,開發平臺為OpenCV2.4.9+Qt5.3.2。
一:圖像腐蝕、膨脹和開閉運算
這些運算的基本公式和原理參考:[http://blog.csdn.net/liyuefeilong/article/details/43374777](http://blog.csdn.net/liyuefeilong/article/details/43374777)?
圖像的腐蝕:替換為當前像素位像素集合中的最小像素值,函數為cv::erode?
圖像的膨脹:替換為當前像素位像素集合中的最大像素值,函數為cv::dilate?
圖像的開運算:先腐蝕后膨脹,函數為cv::morphologyEx,對應的參數為MORPH_CLOSE?
圖像的閉運算:先膨脹后腐蝕,函數為cv::morphologyEx,對應的參數為MORPH_OPEN?
如morphologyEx(image, opened, cv::MORPH_OPEN, element2, cv::Point(-1,-1), 1); 中,輸入圖像為image,輸出圖像為opened,執行開操作,結構元素為element2,原點參數cv::Point(-1,-1)表示原點位于矩陣的中心(默認),最后的1則表示對圖像的操作次數(注:對一幅圖像多次使用開操作和閉操作時效果不會有改善,這些運算是等冪的)。形態學濾波本是基于二值圖像上,但以上這些運算同樣適用于灰度圖像。
新建一個Qt控制臺應用,創建一個類:MorphoFeatures:
~~~
#ifndef MORPHOFEATURES_H
#define MORPHOFEATURES_H
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
class MorphoFeatures
{
public:
void fourFunctions(cv::Mat &image); // 腐蝕、膨脹、開操作、閉操作
}
#endif // MORPHOFEATURES_H
~~~
~~~
void MorphoFeatures::fourFunctions(cv::Mat &image)
{
// 腐蝕運算,替換為當前像素位像素集合中的最小像素值
cv::Mat eroded;
cv::erode(image, eroded, cv::Mat());
// 膨脹運算,替換為當前像素位像素集合中的最大像素值
cv::Mat dilated;
cv::dilate(image, dilated, cv::Mat());
// 閉運算,先膨脹后腐蝕
cv::Mat closed;
cv::Mat element1(3, 3, CV_8U, cv::Scalar(1));
cv::morphologyEx(image, // 輸入圖像
closed, // 輸出圖像
cv::MORPH_CLOSE, // 指定操作
element1, // 結構元素設置
cv::Point(-1,-1), // 操作的位置
1); // 操作的次數
//開運算,先腐蝕后膨脹
cv::Mat opened;
cv::Mat element2(3, 3, CV_8U, cv::Scalar(1));
cv::morphologyEx(image, opened, cv::MORPH_OPEN, element2, cv::Point(-1,-1), 1);
cv::namedWindow("Eroded Image");
cv::imshow("Eroded Image", eroded);
cv::namedWindow("Dilated Image");
cv::imshow("Dilated Image", dilated);
cv::namedWindow("Orginal Image");
cv::imshow("Orginal Image", image);
cv::namedWindow("Closed Image");
cv::imshow("Closed Image", closed);
cv::namedWindow("Opened Image");
cv::imshow("Opened Image", opened);
}
~~~
得出四種操作的處理效果:?


這里你會覺得腐蝕與膨脹、開操作與閉操作的效果和期望是相反的。這是因為我們認為圖像素材中黑色字體是前景,白色為背景。而一般的,形態學規定用高像素表示前景物體,用低像素表示背景,因此**使用這些基本運算之前,可以根據實際情況給原圖像取反。**
二、利用形態學濾波進行邊緣檢測
形態學濾波利用梯度進行邊緣檢測,其原理是計算膨脹后的圖像和腐蝕后的圖像的差值,由于兩個變換后的圖像不同之處主要在邊緣處,圖像邊緣將通過求差得到強化。函數為morphologyEx,參數為MORPH_GRADIENT。若架構元素尺寸越大,檢測出的邊緣越厚。最簡單的邊緣檢測運算是用原圖減去腐蝕后的圖像,或者用膨脹后的圖像減去原圖或腐蝕圖像,效果很直觀,缺點是得到的邊緣較薄。
以下給出形態學濾波進行邊緣檢測的基本方法:?
在class MorphoFeatures中添加幾個函數:
~~~
public:
cv::Mat getEdges(const cv::Mat &image);
void setThreshold(int gate); // 設定閾值
private:
void applyThreshold(cv::Mat &result);
~~~
在morphofeatures.cpp中添加:
~~~
void MorphoFeatures::setThreshold(int gate)
{
threshold = gate;
}
cv::Mat MorphoFeatures::getEdges(const cv::Mat &image)
{
// 得到梯度圖
cv::Mat result;
cv::morphologyEx(image, result, cv::MORPH_GRADIENT, cv::Mat());
// 閾值化以得到二值圖像
applyThreshold(result);
return result;
}
void MorphoFeatures::applyThreshold(cv::Mat &result)
{
// 使用閾值化
if(threshold > 0)
{
cv::threshold(result, result, threshold, 255, cv::THRESH_BINARY);
}
}
~~~
簡單修改main函數:
~~~
#include <QCoreApplication>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include "morphofeatures.h"
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
MorphoFeatures h;
cv::Mat image = cv::imread("c:\\gray.jpg");
// h.fourFunctions(image);
// 邊緣檢測
h.setThreshold(80); // 設定閾值
cv::Mat result = h.getEdges(image);
cv::namedWindow("Input Image");
cv::imshow("Input Image",image);
cv::namedWindow("Edge");
cv::imshow("Edge",result);
return a.exec();
}
~~~
效果:?


效果差強人意……除此之外,還可以用sobel算子、Canny算子等對圖像進行邊緣檢測,這些方法都可以通過改變結構元素來實現,如下圖所示為幾種3*3的Sobel算子。基于Sobel算子的邊緣檢測見:[http://blog.csdn.net/liyuefeilong/article/details/43452711](http://blog.csdn.net/liyuefeilong/article/details/43452711)?

另外一個應用是對圖像進行形態學tophat變換,用h表示,定義為:?
?
其中,f是輸入圖像,B是結構元素函數。tophat變換對于增強灰度圖像的陰影細節很有用處。
三、利用形態學濾波進行圖像角點檢測
這里使用四種不同的結構元素檢測圖像角點,分別為十字型、菱型、x型和方形元素,尺寸規定為5*5。與邊緣檢測不同,角點的檢測復雜。運算過程主要分三步:
第一步,先用十字形的結構元素膨脹原圖像,這種情況下只會在邊緣處“擴張”,角點不發生變化。接著用菱形的結構元素腐蝕原圖像,只有拐角處才會被“收縮”,而直線邊緣不發生變化。
第二步,用X型的結構元素膨脹原圖像,角點膨脹的比邊要多。這樣第二次用方塊腐蝕時,角點恢復原狀,而邊要腐蝕的更多。
第三步,將一二步的兩幅輸出圖像相減,結果只保留了各個拐角處的細節。
首先在類MorphoFeatures.h中添加:
~~~
public:
cv::Mat getCorners(const cv::Mat &image); // 角點檢測函數
void drawOnImage(const cv::Mat &binary, cv::Mat &image); // 在角點處標記圓圈
// 以下構造四種不同的結構元素用來檢測灰度圖像的角點
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))
{
// 5*5的十字形元素
for (int i=0; i<5; i++) {
cross.at<uchar>(2,i)= 1;
cross.at<uchar>(i,2)= 1;
}
// 5*5的菱形元素
diamond.at<uchar>(0,0)= 0;
diamond.at<uchar>(0,1)= 0;
diamond.at<uchar>(1,0)= 0;
diamond.at<uchar>(4,4)= 0;
diamond.at<uchar>(3,4)= 0;
diamond.at<uchar>(4,3)= 0;
diamond.at<uchar>(4,0)= 0;
diamond.at<uchar>(4,1)= 0;
diamond.at<uchar>(3,0)= 0;
diamond.at<uchar>(0,4)= 0;
diamond.at<uchar>(0,3)= 0;
diamond.at<uchar>(1,4)= 0;
// 5*5的x型元素
for (int i=0; i<5; i++)
{
x.at<uchar>(i,i)= 1;
x.at<uchar>(4-i,i)= 1;
}
}
~~~
接著,在morphofeatures.cpp中添加:
~~~
cv::Mat MorphoFeatures::getCorners(const cv::Mat &image)
{
cv::Mat result;
// 十字膨脹
cv::dilate(image, result, cross);
// 棱形腐蝕 形態學函數支持原地操作
cv::erode(result, result, diamond);
cv::Mat result2;
// x型膨脹
cv::dilate(image, result2, x);
// 方形腐蝕
cv::erode(result2, result2, square);
// 對result和result2這兩張圖像相減,得到焦點圖像
cv::absdiff(result2, result, result);
// 閾值化,得到二值圖像
applyThreshold(result);
cv::namedWindow("Corners");
cv::imshow("Corners", result);
return result;
}
void MorphoFeatures::drawOnImage(const cv::Mat &binary, cv::Mat &image)
{
cv::Mat_<uchar>::const_iterator it = binary.begin<uchar>();
cv::Mat_<uchar>::const_iterator itend = binary.end<uchar>();
for(int i=0; it!=itend; ++it, ++i)
{
if(*it) // 若該像素被標定為角點則畫白色圈圈
{
cv::circle(image, cv::Point(i%image.step, i/image.step), 5, cv::Scalar(255, 255, 255));
}
}
}
~~~
在main.cpp中簡單添加:
~~~
cv::Mat corners;
corners = h.getCorners(image);
h.drawOnImage(corners, image);
cv::namedWindow("Corners On Image");
cv::imshow("Corners On Image", image);
~~~
效果:


在這里,需要把輸入圖像轉化為二值圖像,因此閾值的選擇會影響角點檢測效果,如下圖所示:


盡管角點檢測效果有好有壞,不過實現該方法,對于理解腐蝕、膨脹、開操作、閉操作有很好的幫助。
歡迎轉載或分享,但請務必聲明文章出處~
- 前言
- Win8.1下OpenCV2.4.9+Qt5.3.2開發環境搭建
- OpenCV2學習筆記(一)
- OpenCV2學習筆記(二)
- OpenCV2學習筆記(三)
- OpenCV2學習筆記(四)
- OpenCV2學習筆記(五)
- OpenCV2學習筆記(六)
- OpenCV2學習筆記(七)
- OpenCV2學習筆記(八)
- OpenCV2學習筆記(九)
- OpenCV2學習筆記(十)
- OpenCV2學習筆記(十一)
- OpenCV2學習筆記(十二)
- OpenCV2學習筆記(十三)
- OpenCV2學習筆記(十四)
- OpenCV2學習筆記(十五)
- OpenCV2學習筆記(十六)
- OpenCV2學習筆記(十七)
- OpenCV2學習筆記(十八)
- OpenCV2學習筆記(十九)
- OpenCV2學習筆記(二十)
- OpenCV2學習筆記(二十一)
- OpenCV2學習筆記(二十二)