1.形态学操作
图像形态学操作:基于形状的一系列图像处理操作的合集,主要是基于集合论基础上的形态学数学。
形态学有四个基本操作:膨胀、腐蚀、开、闭。
2.膨胀与腐蚀
2.1.膨胀
跟卷积操作类似,假设有图像A和结构元素B,结构元素B在A上面移动,其中B定义其中心为锚点,计算B覆盖下A的最大像素值用来替换锚点的像素,其中B作为结构体可以是任意形状。
(左图为原图,右图为膨胀操作之后的输出图像。)
2.2.腐蚀
腐蚀跟膨胀操作的过程类似,唯一不同的是以最小值替换锚点重叠下图像的像素值。
(左图为原图,右图为腐蚀操作之后的输出图像。)
2.3.相关API
2.3.1.getStructuringElement
getStructuringElement
函数会返回指定形状和尺寸的结构元素。
Mat getStructuringElement(
int shape,
Size ksize,
Point anchor=Point(-1,-1)
);
- 参数
int shape
表示内核的形状,有三种形状可以选择:- 矩形
MORPH_RECT
- 十字形
MORPH_CROSS
- 椭圆
MORPH_ELLIPSE
- 矩形
- 参数
Size ksize
表示内核的大小(必须为正奇数)。 - 参数
Point anchor
表示内核的锚点,默认位置在中心。
三种内核形状的说明,以5*5
大小的内核为例:
#MORPH_RECT
1 1 1 1 1
1 1 1 1 1
1 1 1 1 1
1 1 1 1 1
1 1 1 1 1
#MORPH_CROSS
0 0 1 0 0
0 0 1 0 0
1 1 1 1 1
0 0 1 0 0
0 0 1 0 0
#MORPH_ELLIPSE
0 0 1 0 0
1 1 1 1 1
1 1 1 1 1
1 1 1 1 1
0 0 1 0 0
2.3.2.膨胀
void dilate(
InputArray src,//原始图像
OutputArray dst,//输出图像
InputArray kernel,//结构元素
Point anchor = Point(-1,-1),//结构元素的锚点位置
int iterations = 1,//膨胀操作执行次数
int borderType = BORDER_CONSTANT,//图像边缘处理方式
const Scalar& borderValue = morphologyDefaultBorderValue()//处理边缘用的值
);
2.3.3.腐蚀
腐蚀操作的API参数基本和膨胀相同,不再赘述。
void erode(
InputArray src,
OutputArray dst,
InputArray kernel,
Point anchor = Point(-1,-1),
int iterations = 1,
int borderType = BORDER_CONSTANT,
const Scalar& borderValue = morphologyDefaultBorderValue()
);
3.其他形态学操作
3.1.开操作与闭操作
3.1.1.开操作
开操作:先腐蚀后膨胀。
上图左为原图,右为执行开操作之后的图。
3.1.2.闭操作
闭操作:先膨胀后腐蚀。
上图左为原图,右为执行闭操作之后的图。
3.2.形态学梯度
形态学梯度:膨胀减去腐蚀。
上图左为原图,右为执行形态学梯度之后的图。
很明显,该操作可用于提取边缘。
3.3.顶帽与黑帽
3.3.1.顶帽
顶帽:原图像与开操作之间的差值图像(对应像素值差的绝对值)。
以3.1.1部分的原图作为例子,顶帽操作得到的图像见下:
3.3.2.黑帽
黑帽:原图像与闭操作之间的差值图像。
以3.1.2部分的原图作为例子,黑帽操作得到的图像见下:
3.4.相关API
void morphologyEx(
InputArray src,//原图像
OutputArray dst,//输出图像
int op,//形态学操作类型
InputArray kernel,//结构元素,使用getStructuringElement构建
Point anchor = Point(-1,-1),//锚点
int iterations = 1,//操作执行次数
int borderType = BORDER_CONSTANT,//边界处理方式
const Scalar& borderValue = morphologyDefaultBorderValue()//处理边界时所使用的值
);
其中,参数int op
有以下几种选择:
MORPH_ERODE=0
:腐蚀MORPH_DILATE=1
:膨胀MORPH_OPEN=2
:开操作MORPH_CLOSE=3
:闭操作MORPH_GRADIENT=4
:形态学梯度MORPH_TOPHAT=5
:顶帽操作MORPH_BLACKHAT=6
:黑帽操作MORPH_HITMISS=7
4.代码地址
C++实现
#include<opencv2/opencv.hpp>
#include<iostream>
using namespace cv;
using namespace std;
Mat src, dst;//设为全局变量,否则CallBack_Demo函数中无法使用
char winName[] = "output";
int element_size = 3;
int max_size = 21;
void CallBack_Demo(int, void*);//Trackbarcallback func的原型必须是这样!
int main(int argc, char**argv)
{
src = imread("p1.png", -1);
if (!src.data){
printf("the image could not find...\n");
return -1;
}
namedWindow("input image", WINDOW_AUTOSIZE);
imshow("input image", src);
namedWindow(winName, WINDOW_AUTOSIZE);
//如果没有callback函数,就只有数值变化,即轨迹条可以正常滑动,数值可以正常随着滑块变化,但是图片不会变化
//第三和第四个参数指的是运行callback函数的次数,本例中,callback函数运行几次就相当于dilate执行几次
createTrackbar("Element Size", winName, &element_size, max_size, CallBack_Demo);
CallBack_Demo(0, 0);
//CallBack_Demo(1, &element_size);两个参数值随便设,好像并不会影响结果
waitKey(0);
return 0;
}
void CallBack_Demo(int, void*)
{
int s = element_size * 2 + 1;
Mat structureElement = getStructuringElement(MORPH_RECT, Size(s, s), Point(-1, -1));
//dilate(src, dst, structureElement, Point(-1, -1), 1);//kernel就是structureElement,1是迭代次数,迭代次数指的是对原图整体做几次膨胀操作,次数越多,图片越偏白
erode(src, dst, structureElement);
imshow(winName, dst);
}
Python实现
import cv2
import numpy as np
element_size = 3
max_size = 21
def callback_demo(val):
global element_size
element_size = val
s = element_size * 2 + 1
structure_element = cv2.getStructuringElement(cv2.MORPH_RECT, (s, s))
dst = cv2.erode(src, structure_element)
cv2.imshow("output", dst)
src = cv2.imread("p1.png", -1)
if src is None:
print("The image could not be found...")
exit()
cv2.imshow("input image", src)
cv2.namedWindow("output", cv2.WINDOW_AUTOSIZE)
cv2.createTrackbar("Element Size", "output", element_size, max_size, callback_demo)
callback_demo(element_size) # Initial call to display the image
cv2.waitKey(0)
cv2.destroyAllWindows()
标签:src,MORPH,int,cv2,形态学,OpenCV,图像处理,操作,size
From: https://blog.csdn.net/matt45m/article/details/142991489