形态学操作——腐蚀膨胀、开闭运算、顶帽变换底帽变换 c++实现

腐蚀

卷积核沿着图像滑动,像素值为卷积核范围里的最小值。腐蚀之后图像会变暗。

opencv实现

//第一个参数MORPH_RECT表示矩形的卷积核,还有椭圆形的、交叉型的
Mat element = getStructuringElement(MORPH_RECT, Size(15, 15)); 
   
//腐蚀操作
erode(img, out, Mat kernel, Point anchor=Point(-1,-1), int iterations=1)
/*src:输入图像
dst:目标图像
kernel:結構元素,如果kernel=Mat()則為預設的3×3矩形,越大腐蚀效果越明显。
anchor:原點位置,預設為結構元素的中央。
iterations:執行次數,預設為1次。*/

c++实现

void my_erode(Mat src, Mat dst, int x, int y) {
	if ((x % 2 == 1) && (y % 2 == 1)) {
        //卷积核长宽需为奇数
		int xx = x / 2;
		int yy = y / 2;
		for (int rows = yy; rows < src.rows - yy; rows++) {
			for (int cols = xx; cols < src.cols - xx; cols++) {
				int my_min = 255;
	
				for (int i = rows - yy; i < rows + yy + 1; i++) {
					for (int j = cols - xx; j < cols + xx + 1; j++) {
						my_min = MIN(my_min, src.at<uchar>(i, j));
					}
				}
				dst.at<uchar>(rows, cols) = my_min;
			}
		}
	}
}

膨胀

卷积核沿着图像滑动,像素值为卷积核范围里的最大值,膨胀后图像会变亮。

opencv实现

//第一个参数MORPH_RECT表示矩形的卷积核,还有椭圆形的、交叉型的
Mat element = getStructuringElement(MORPH_RECT, Size(15, 15)); 
   
//膨胀操作
dilate(img, out, Mat kernel, Point anchor=Point(-1,-1), int iterations=1)
/*src:输入图像
dst:目标图像
kernel:結構元素,如果kernel=Mat()則為預設的3×3矩形,越大膨胀效果越明显。
anchor:原點位置,預設為結構元素的中央。
iterations:執行次數,預設為1次。*/

c++实现

void my_dilate(Mat src, Mat dst, int x, int y) {
	if ((x % 2 == 1) && (y % 2 == 1)) {
        //卷积核长宽需为奇数
		int xx = x / 2;
		int yy = y / 2;
		for (int rows = yy; rows < src.rows - yy; rows++) {
			for (int cols = xx; cols < src.cols - xx; cols++) {
				int my_max = 0
	
				for (int i = rows - yy; i < rows + yy + 1; i++) {
					for (int j = cols - xx; j < cols + xx + 1; j++) {
						my_max = MAX(my_max, src.at<uchar>(i, j));
					}
				}
				dst.at<uchar>(rows, cols) = my_max;
			}
		}
	}
}

开运算

开运算 = 先腐蚀运算,再膨胀运算(看上去把细微连在一起的两块目标分开了) 

开运算能够除去孤立的小点,毛刺和小桥,而总的位置和形状不便。

opencv实现

Mat element = getStructuringElement(MORPH_RECT, Size(18, 18)); 

    //具体要选择哪种操作,就修改第三个参数就可以了。这里演示的是形态学开运算处理

    morphologyEx(img, out, MORPH_OPEN, element);

//腐蚀和膨胀也可以调用此模板
/*MORPH_OPEN    开运算
 MORPH_CLOSE    闭运算
 MORPH_ERODE    腐蚀
 MORPH_DILATE    膨胀*/

c++实现

void my_open(Mat src, Mat dst, int x, int y) {
	Mat dst1;
	src.copyTo(dst1);
	my_erode(src, dst1, x, y);
	my_dilate(dst1, dst, x, y);
}

闭运算

闭运算 = 先膨胀运算,再腐蚀运算(看上去将两个细微连接的图块封闭在一起) 

闭运算能够填平小湖(即小孔),弥合小裂缝,而总的位置和形状不变。 

c++实现

void my_close(Mat src, Mat dst, int x, int y) {
	Mat dst1;
	dst.copyTo(dst1);
	my_dilate(src, dst1, x, y);
	my_erode(dst1, dst, x, y);
}

Trackbar使用

只以开运算为例:

#include <iostream>
#include<opencv2/opencv.hpp>
using namespace cv;
using namespace std;

Mat img, oimg;//为了方便定义为全局变量(这是一个不好的习惯)
int width = 1;
int hight = 1;

void my_dilate(Mat src, Mat dst, int x, int y) {
	if ((x % 2 == 1) && (y % 2 == 1)) {
		int xx = x / 2;
		int yy = y / 2;
		int rows, cols;	
		for (rows = yy; rows < src.rows - yy; rows++) {
			for (cols = xx; cols < src.cols - xx; cols++) {
				int my_max = 0;		
				for (int i = rows - yy; i < rows + yy + 1; i++) {
					for (int j = cols - xx; j < cols + xx + 1; j++) {
						my_max = MAX(my_max, src.at<uchar>(i, j));
					}
				}
				dst.at<uchar>(rows, cols) = my_max;
			}
		}
	}
}

void my_erode(Mat src, Mat dst, int x, int y) {
	if ((x % 2 == 1) && (y % 2 == 1)) {
		int xx = x / 2;
		int yy = y / 2;		
		for (int rows = yy; rows < src.rows - yy; rows++) {
			for (int cols = xx; cols < src.cols - xx; cols++) {
				int my_min = 255;	
				for (int i = rows - yy; i < rows + yy + 1; i++) {
					for (int j = cols - xx; j < cols + xx + 1; j++) {
						my_min = MIN(my_min, src.at<uchar>(i, j));
					}
				}
				dst.at<uchar>(rows, cols) = my_min;
			}
		}
	}
}

void my_open(Mat src, Mat dst, int x, int y) {
	Mat dst1;
	src.copyTo(dst1);
	my_erode(src, dst1, x, y);
	my_dilate(dst1, dst, x, y);
}

void open_track(int, void*) {
	my_open(img, oimg, 2 * width + 1, 2 * hight + 1);
	imshow("开运算", oimg);
}

int main()
{
	img = imread("C:/Users/45374/Desktop/code/img/lina320.bmp");
	if (img.channels() == 3) {
		cvtColor(img, img, CV_BGR2GRAY);
	}
	img.copyTo(oimg);
	img.copyTo(oimg2);
	
	namedWindow("开运算");
	createTrackbar("x:", "trackbar", &width, 10, open_track);
	createTrackbar("y:", "trackbar", &hight, 10, open_track);
	open_track(0, 0);

	waitKey();
	return 0;
}

效果如下:

顶帽变换

顶帽变换=原图减去开运算。如果再与原图相加可增强轮廓。

opencv实现:

Mat element = getStructuringElement(MORPH_RECT, Size(8, 8)); 


    morphologyEx(img, out, MORPH_TOPHAT, element);

c++实现:

void my_tophat(Mat src, Mat dst, int x, int y) {
	Mat dst1;
	src.copyTo(dst1);
	my_open(src, dst1, x, y);
	imshow("open", dst1);
	for (int rows = 0; rows < src.rows; rows++) {
		for (int cols = 0; cols < src.cols; cols++) {
			dst.at<uchar>(rows, cols) = saturate_cast<uchar>(src.at<uchar>(rows, cols) - dst1.at<uchar>(rows, cols));
		}
	}
	namedWindow("顶帽", CV_WINDOW_AUTOSIZE);
	imshow("顶帽", dst);
}

效果如下:

黑帽变换

黑帽变换(又叫底帽变换)=闭运算减去原图。如果再与原图相加可模糊噪声。

opencv实现:

Mat element = getStructuringElement(MORPH_RECT, Size(18, 18)); 

    //具体要选择哪种操作,就修改第三个参数就可以了。这里演示的是形态学黑帽变换

    morphologyEx(img, out, MORPH_BLACKHAT, element);

c++实现:

void my_underhat(Mat src, Mat dst, int x, int y) {
	Mat dst1;
	src.copyTo(dst1);
	my_close(src, dst1, x, y);
	imshow("colse", dst1);
	for (int rows = 0; rows < src.rows; rows++) {
		for (int cols = 0; cols < src.cols; cols++) {
			dst.at<uchar>(rows, cols) = saturate_cast<uchar>(dst1.at<uchar>(rows, cols) - src.at<uchar>(rows, cols));
		}
	}
	namedWindow("底帽", CV_WINDOW_AUTOSIZE);
	imshow("底帽", dst);
}

trackbar使用举例

#include "pch.h"
#include <iostream>
#include<opencv2/opencv.hpp>
using namespace cv;
using namespace std;

const char* WINDOW_NAME = "处理后";

Mat img, oimg;
int width = 1;
int hight = 1;

void on_track(int, void*) {
	Mat element = getStructuringElement(MORPH_RECT, Size(2 * width + 1, 2 * hight + 1));
	//进行形态学操作
	morphologyEx(img, oimg, MORPH_TOPHAT, element);
	addWeighted(img, 1, oimg, 1,0, oimg2);//顶帽变换后加上原图,增强纹理和边界
	imshow(WINDOW_NAME, oimg);
}

int main()
{
	img = imread("C:/Users/45374/Desktop/code/img/lena.jpg",1);

	img.copyTo(oimg);

	namedWindow(WINDOW_NAME);
	createTrackbar("x:", WINDOW_NAME, &width, 10, on_track);
	createTrackbar("y:", WINDOW_NAME, &hight, 10, on_track);
	on_track(0, 0);
	imshow(WINDOW_NAME, img);

	waitKey(0);
	return 0;
}

效果如下:

发布了33 篇原创文章 · 获赞 148 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/qq_40692109/article/details/102778941