图像形态学操作是基于形状的一系列图像处理操作的合集,主要是基于集合论基础上的形态学数学。主要有四个操作:膨胀,腐蚀,开,闭。
形态学操作——膨胀
膨胀与腐蚀跟卷积操作类似,假设有图像A和结构元素B,B在A上面移动,其中B定义其中心是锚点,计算B覆盖下的A的最大像素值用来替代锚点的像素,其中B作为结构元素可以是任意形状。
形态学操作——腐蚀
腐蚀过程和膨胀过程类似,唯一不同的是以最小像素值替代锚点像素。
- 获取结构元素
getStructuringElement(int shape,Size ksize,Point anchor)
其中形状shape可以是MORPH_RECT,MORPH_CROSS,MORPH_ELLIPSE,锚点默认是Point(-1,-1),意思是中心元素。 - 膨胀
dilate(src,dst,kernel)
- 腐蚀
erode(src,dst,kernel)
opencv提供了状态条TrackBar来动态调整结构元素大小。
createTrackBar(const String& trackbarname,const String windowname,int * value,int count,Trackbarcallback func,void * userdata=0) 回调函数如果为NULL,就只会update,不会调用callback函数。
#include "stdafx.h"
#include <opencv2/opencv.hpp>
#include <iostream>
using namespace cv;
int elementsize = 3;
int maxsize = 21;
Mat src, dst, dst_;
void callback(int, void *);
int main(int argc, int ** argv)
{
int ksize = 0;
src = imread("F:/cat.png");
if (!src.data) {
printf("无法加载图片\n");
return -1;
}
namedWindow("input img", CV_WINDOW_AUTOSIZE);
imshow("show img", src);
char TrackbarName[50];
sprintf_s(TrackbarName, "Alpha x %d", elementsize);
namedWindow("output result", CV_WINDOW_AUTOSIZE);
createTrackbar(TrackbarName, "output result", &elementsize, maxsize, callback);
callback(0, 0);
//Mat kernel = getStructuringElement(MORPH_RECT, Size(5, 5), Point(-1, -1));
//dilate(src, dst, kernel);
//erode(src, dst_, kernel);
//namedWindow("dilate result", CV_WINDOW_AUTOSIZE);
//namedWindow("erode result", CV_WINDOW_AUTOSIZE);
//imshow("dilate", dst);
//imshow("erode", dst_);
waitKey(0);
return 0;
}
void callback(int, void *)
{
int s = elementsize * 2 + 1;
Mat sturctEle = getStructuringElement(MORPH_RECT, Size(s, s), Point(-1, -1));
dilate(src, dst, sturctEle);
//erode(src, dst_, sturctEle);
imshow("dilate", dst);
//imshow("erode", dst_);
return;
}
输出结果演示
形态学操作——开操作
先腐蚀后膨胀,可以去掉小的对象
形态学操作——闭操作
先膨胀后腐蚀,可以填充小对象
形态学梯度
膨胀减去腐蚀
形态学操作——顶帽
顶帽是原图像与开操作图像之间的差值图像
形态学操作——黑帽
黑帽是闭操作图像与原图像之间的差值图像
#include "stdafx.h"
#include <opencv2/opencv.hpp>
#include <iostream>
using namespace cv;
Mat src, dst;
int main(int argc, int ** argv)
{
src = imread("F:/black_hole.png");
if (!src.data) {
printf("无法加载图片\n");
return -1;
}
namedWindow("input img", CV_WINDOW_AUTOSIZE);
imshow("show img", src);
Mat kernel = getStructuringElement(MORPH_RECT, Size(15, 15), Point(-1, -1));
//morphologyEx(src, dst, CV_MOP_OPEN, kernel);
//morphologyEx(src, dst, CV_MOP_CLOSE, kernel);
//morphologyEx(src, dst, CV_MOP_GRADIENT, kernel);
//morphologyEx(src, dst, CV_MOP_TOPHAT, kernel);
morphologyEx(src, dst, CV_MOP_BLACKHAT, kernel);
namedWindow("output result", CV_WINDOW_AUTOSIZE);
imshow("erode", dst);
waitKey(0);
return 0;
}
morphologyEx(Mat src,Mat dst,int opt,kernel)
其中opt是形态学操作,可以为CV_MOP_OPEN,CV_MOP_CLOSE,CV_MOP_GRADIENT,CV_MOP_TOPHAT,CV_MOP_BLACKHAT
形态学应用
提取水平和垂直线
可以通过自定义结构元素实现结构元素对输入图像一些对象敏感,一些对象不敏感,这样就会让敏感对象改变而不敏感对象保留输出。通过使用膨胀与腐蚀,使用不同的结构元素来得到不同的结果。
操作步骤:
- 输入图像转换为灰度图像
- 灰度图像转换为二值图像
- 定义结构元素
- 开操作提取水平与垂直线
#include "stdafx.h"
#include <opencv2/opencv.hpp>
#include <iostream>
using namespace cv;
Mat src, dst;
int main(int argc, int ** argv)
{
src = imread("F:/line.png");
if (!src.data) {
printf("无法加载图片\n");
return -1;
}
namedWindow("input img", CV_WINDOW_AUTOSIZE);
Mat gray;
cvtColor(src, gray, CV_BGR2GRAY);
imshow("gray img", gray);
Mat binImg;
adaptiveThreshold(~gray, binImg, 255, ADAPTIVE_THRESH_MEAN_C, THRESH_BINARY, 15, -2);
imshow("binary img", binImg);
Mat hline = getStructuringElement(MORPH_RECT, Size(src.cols/16, 1), Point(-1, -1));
Mat vline = getStructuringElement(MORPH_RECT, Size(1, src.rows/16), Point(-1, -1));
Mat temp;
//erode(binImg, temp, hline);
//dilate(temp, dst, hline);
erode(binImg, temp, vline);
dilate(temp, dst, vline);
bitwise_not(dst, dst);
imshow("final result", dst);
waitKey(0);
return 0;
}
扩展:去除打码平台无用的线条
#include "stdafx.h"
#include <opencv2/opencv.hpp>
#include <iostream>
using namespace cv;
Mat src, dst;
int main(int argc, int ** argv)
{
src = imread("F:/number.png");
if (!src.data) {
printf("无法加载图片\n");
return -1;
}
namedWindow("input img", CV_WINDOW_AUTOSIZE);
Mat gray;
cvtColor(src, gray, CV_BGR2GRAY);
imshow("gray img", gray);
Mat binImg;
adaptiveThreshold(~gray, binImg, 255, ADAPTIVE_THRESH_MEAN_C, THRESH_BINARY, 15, -2);
imshow("binary img", binImg);
Mat hline = getStructuringElement(MORPH_RECT, Size(src.cols/16, 1), Point(-1, -1));
Mat vline = getStructuringElement(MORPH_RECT, Size(1, src.rows/16), Point(-1, -1));
Mat kernel = getStructuringElement(MORPH_RECT, Size(3, 3), Point(-1, -1));
Mat temp;
erode(binImg, temp, kernel);
dilate(temp, dst, kernel);
bitwise_not(dst, dst);
imshow("final result", dst);
waitKey(0);
return 0;
}