实现:
1.熟悉二值形态学膨胀/腐蚀/开/闭运算,理解二值形态学算法的几何意义和数学原理。
2.掌握二值形态学骨架提取算法原理及其实现。
3.掌握形态学方法检测角点算法原理及实现。
1.编写二值形态学膨胀/腐蚀/开/闭运算算法程序,并调试实现。
二值形态学膨胀/腐蚀/开/闭运算均可通过opencv的接口函数实现,膨胀的接口函数为:dilate(image, out, element);腐蚀的接口函数为:erode(image, out, element);
开运算的接口函数为:morphologyEx(thresh, open_result, MORPH_OPEN, element);
闭运算的接口函数为:morphologyEx(thresh, close_result, MORPH_CLOSE, element);
同时,以上操作均可通过morphologyEx函数完成,对其中的参数进行转换即可。
MORPH_ERODE为腐蚀,MORPH_DILATE为膨胀,MORPH_OPEN为开运算,MORPH_CLOSE为闭运算。
实现结果:
二值形态学膨胀/腐蚀/开/闭运算实现效果图如图1-图4所示。
图像膨胀:
图像腐蚀:
图像开运算:
2.编写利用二值形态学对二值图像进行边缘细化(骨架提取)的算法程序。
使用K3M算法进行骨架提取操作,本算法属于迭代腐蚀边界的一类算法,该类算法的思想是,假定从二值图像中物体的边界处同时开始燃烧,物体就会被逐步细化,但在燃烧过程中要保证满足-定条件的点被保留或者被“烧掉”,以确定燃烧结束后,剩下的最后一像素宽的图像为图像的骨架。这些条件的确定没有统一的标准,各个算法采取了不同的方案。一般来讲,为了满足计算的速度要求和算法的准确,迭代中算法会对图像边界上某点的3*3邻域内进行检查,判断是否满足要求。本算法每次循环都会进行六次检测,分别是:
Phase0:标记出图像的边界,
Phase1:如果该点的邻域中有3个点(非零,以下皆如此)相邻,删除该点
Phase2:如果该点的邻域中有3或4个点相邻,删除该点。
Phase3:如果该点的邻域中有3,4, 5个点相邻,删除该点。
Phase4:如果该点的邻域中有3, 4, 5, 6个点相邻,删除该点。
Phase5:如果该点的邻域中有3, 4, 5, 6, 7个点相邻,删除该点。
Phase6:剩余的边界点取消标记,如果Phase 5中没有点被修改,停止迭代,否则返回Phase 0。
主要实现代码如下所示:
set<int> GetAi(int a[], int length)//获取A0~A5
{
set<int> vec;
int neighbour[] = { 1,2,4,8,16,32,64,128,1,2,4,8,16,32,64 };
for (int i = 0; i<length; i++)
for (int j = 0; j<8; j++)
{
int sum = 0;
for (int k = j; k <= j + a[i]; k++)
sum += neighbour[k];
vec.insert(sum); //在vec首部插入sum
std::cout << sum << " ";
}
std::cout << std::endl;
return vec;
}
//迭代腐蚀
bool erodephase(list<cv::Point> &border, cv::Mat&Input, int neighbour[][3], const set<int>& A)
{
auto pt = border.begin();
bool result = false;
while (pt != border.end())
{
int weight = 0;
for (int j = -1; j <= 1; ++j)
for (int k = -1; k <= 1; k++)
weight += neighbour[j + 1][k + 1] * Input.at<uchar>(pt->y + j, pt->x + k);
if (std::find(A.begin(), A.end(), weight) != A.end())
{
Input.at<uchar>(pt->y, pt->x) = 0;
pt = border.erase(pt);
result = true;
}
else
++pt;
}
return result;
}
//找边界
void findborder(list<cv::Point2i>& border, const cv::Mat&Input)
{
int cnt = 0;
int rows = Input.rows;
int cols = Input.cols;
cv::Mat bordermat = Input.clone();
for (int row = 1; row<rows - 1; ++row)
for (int col = 1; col<cols - 1; ++col)
{
int weight = 0;
for (int j = -1; j <= 1; ++j)
for (int k = -1; k <= 1; k++)
{
if (Input.at<uchar>(row + j, col + k) == 1)
++cnt;
}
if (cnt == 9)
bordermat.at<uchar>(row, col) = 0;
cnt = 0;
}
for (int row = 1; row<rows - 1; ++row)
for (int col = 1; col < cols - 1; ++col)
{
if (bordermat.at<uchar>(row, col) == 1)
border.push_back(cv::Point2i(col, row));
}
}
//最后一步,得到骨架
void finalerode(cv::Mat&Input, int neighbour[][3], const set<int>& A)
{
int rows = Input.rows;
int cols = Input.cols;
for (int m = 1; m<rows - 1; ++m)
for (int n = 1; n<cols - 1; ++n)
{
int weight = 0;
for (int j = -1; j <= 1; ++j)
for (int k = -1; k <= 1; k++)
{
weight += neighbour[j + 1][k + 1] * Input.at<uchar>(m + j, n + k);
}
if (std::find(A.begin(), A.end(), weight) != A.end())
Input.at<uchar>(m, n) = 0;
}
}
void thin(Mat &Input) //Input是二值图像
{
int a0[] = { 1,2,3,4,5,6 };
int a1[] = { 2 };
int a2[] = { 2,3 };
int a3[] = { 2,3,4 };
int a4[] = { 2,3,4,5 };
int a5[] = { 2,3,4,5,6 };
set<int> A0 = GetAi(a0, 6);
set<int> A1 = GetAi(a1, 1);
set<int> A2 = GetAi(a2, 2);
set<int> A3 = GetAi(a3, 3);
set<int> A4 = GetAi(a4, 4);
set<int> A5 = GetAi(a5, 5);
list<cv::Point2i> border;
bool continue_ = true;
int neighbour[3][3] =
{
{ 128,1,2 },
{ 64,0,4 },
{ 32,16,8 }
};
while (continue_)
{
continue_ = false;
findborder(border, Input);
erodephase(border, Input, neighbour, A1);
erodephase(border, Input, neighbour, A2);
erodephase(border, Input, neighbour, A3);
erodephase(border, Input, neighbour, A4);
continue_ = erodephase(border, Input, neighbour, A5); border.clear();
}
finalerode(Input, neighbour, A0);}
实现效果:
二值形态学对二值图像进行边缘细化(骨架提取)实现效果图如图5所示。
图像骨架提取:
3.编写形态学检测角点算法程序,并调试实现。
定义十字形核,菱形形状核,X形状核,分别进行两次膨胀、腐蚀操作,在不同的方向上进行膨胀腐蚀操作,并画出角点。主要代码如下所示:
int point(Mat & srcImg)
{
Mat srcGray;
imshow("【原图】原始图像", srcImg);
cvtColor(srcImg, srcGray, CV_RGB2GRAY);
Mat thresh;
thresh = srcGray.clone();
threshold(srcGray, thresh, 176, 255, CV_THRESH_BINARY_INV);
imshow("【效果图】二值图像", thresh);
//定义核
Mat CrossMat(5, 5, CV_8U, Scalar(0));
Mat diamondMat(5, 5, CV_8U, Scalar(1));
Mat squareMat(5, 5, CV_8U, Scalar(1));
Mat X(5, 5, CV_8U, Scalar(0));
//十字形核
for (int i = 0; i < 5; i++) {
CrossMat.at<uchar>(2, i) = 1;
CrossMat.at<uchar>(i, 2) = 1;
}
//菱形形状核
diamondMat.at<uchar>(0, 0) = 0;
diamondMat.at<uchar>(0, 1) = 0;
diamondMat.at<uchar>(1, 0) = 0;
diamondMat.at<uchar>(4, 4) = 0;
diamondMat.at<uchar>(3, 4) = 0;
diamondMat.at<uchar>(4, 3) = 0;
diamondMat.at<uchar>(4, 0) = 0;
diamondMat.at<uchar>(4, 1) = 0;
diamondMat.at<uchar>(3, 0) = 0;
diamondMat.at<uchar>(0, 4) = 0;
diamondMat.at<uchar>(0, 3) = 0;
diamondMat.at<uchar>(1, 4) = 0;
//X形状核
for (int i = 0; i < 5; i++) {
X.at<uchar>(i, i) = 1;
X.at<uchar>(4 - i, i) = 1;
}
Mat result;
dilate(thresh, result, CrossMat);
imshow("【效果图】膨胀操作", result);
erode(result, result, diamondMat);
imshow("【效果图】腐蚀操作_腐蚀角点", result);
Mat result2;
dilate(thresh, result2, X);
imshow("【效果图】膨胀操作_膨胀水平、垂直及45°、135°方向", result2);
erode(result2, result2, squareMat);
imshow("【效果图】腐蚀操作_腐蚀水平和垂直方向", result2);
//计算差值
absdiff(result2, result, result);
threshold(result, result, 30, 255, THRESH_BINARY);
//绘制
for (int i = 0; i < result.rows; i++) {
//获取行指针
const uchar* data = result.ptr<uchar>(i);
for (int j = 0; j < result.cols; j++) {
//如果是角点,则绘制圆圈
if (data[j]) {
circle(srcImg, Point(j, i), 8,
Scalar(0, 0, 255));
}
}
}
imshow("【效果图】圈出角点", srcImg);
imshow("【效果图】提取角点", result);
waitKey(0);
return 0;
}
实现效果:
形态学检测角点实现效果图如图6所示。
图像角点提取:
合集入口
测试资源下载入口