OpenCV4.3 Java 编程入门:形态学滤波
文章目录
- 1 形态学
- 2 膨胀:dilate
- 3 腐蚀:erode
- 4 膨胀&腐蚀示例
- 5 开运算、闭运算、形态学梯度、顶帽、黑帽
1 形态学

形态学最开始用于对动植物形态和结构的研究,而数学形态学是一门图像分析学科,是一门通过数学形态运算进行图像处理的技术。其基本的运算包括:二值腐蚀(erode)和膨胀(dilate)、二值开闭运算、骨架抽取、极限腐蚀、击中击不中变换、形态学梯度、Top-hat变换、颗粒分析、流域变换、灰值腐蚀和膨胀、灰值开闭运算、灰值形态学梯度等。
简单来说,形态学操作就是基于形状对图像进行处理的一系列操作。
最基本的形态学操作有两种,分别是:腐蚀和膨胀。腐蚀和膨胀能实现多种多样的功能,比如:
- 消除噪声;
- 分割(isolate)出独立的图像元素,在图像中连接相邻的元素;
- 寻找图像中的明显的极大值区域或极小值区域;
- 求出图像的梯度。
2 膨胀:dilate
膨胀就是求局部最大值的操作。

图像与膨胀核进行卷积,即计算核 B 覆盖的区域的像素点的最大值,并把这个最大值复制给参考点对应的像素。结果就会使图像中的高亮(值大)的区域逐渐膨胀。

方法定义:
/*** 使用特定的结构元素来膨胀图像。** @param src 输入图像* @param dst 输出图像* @param kernel 卷积核,可以通过 getStructuringElement 方法来构建;* @param anchor 参考点,卷积运算时,图像像素点在卷积核图像上的映射位置;* @param iterations 执行次数* @param borderType 像素外推法* @param borderValue 边界值*/public static void dilate(Mat src, Mat dst, Mat kernel, Point anchor, int iterations, int borderType);
3 腐蚀:erode
腐蚀与膨胀是相反的一对操作,因此腐蚀就是求局部最小值的操作。

计算公式:

方法定义:
/*** 使用特定的结构元素来腐蚀图像。** @param src 输入图像* @param dst 输出图像* @param kernel 卷积核,可以通过 getStructuringElement 方法来构建;* @param anchor 参考点,卷积运算时,图像像素点在卷积核图像上的映射位置;* @param iterations 执行次数* @param borderType 像素外推法* @param borderValue 边界值*/public static void erode(Mat src, Mat dst, Mat kernel, Point anchor, int iterations, int borderType, Scalar borderValue);
4 膨胀&腐蚀示例
getStructuringElement 方法定义:
/*** 返回用于形态学操作的指定大小和形状的结构元素;** 该函数不仅可以构造用于形态学操作的结构元素,也可以构造一个任意的二进制掩码,并将其作为结构元素使用。** @param shape 元素形状:MORPH_RECT,MORPH_CROSS,MORPH_ELLIPSE* @param ksize 元素尺寸* @param anchor 参考点*/public static Mat getStructuringElement(int shape, Size ksize, Point anchor)
示例代码:
import org.bytedeco.javacpp.Loader;
import org.junit.Test;
import org.opencv.core.Mat;
import org.opencv.core.Point;
import org.opencv.core.Size;
import org.opencv.highgui.HighGui;
import org.opencv.imgcodecs.Imgcodecs;
import org.opencv.imgproc.Imgproc;import javax.swing.*;
import java.awt.*;
public class ErodeTest {static {Loader.load(org.bytedeco.opencv.opencv_java.class);}private static final String[] ELEMENT_TYPE = { "Rectangle", "Cross", "Ellipse" };private static final String[] MORPH_OP = { "Erosion", "Dilatation" };private static final int MAX_KERNEL_SIZE = 21;private Mat matImgSrc;private Mat matImgDst = new Mat();private int elementType = Imgproc.CV_SHAPE_RECT;private int kernelSize = 0;private boolean doErosion = true;private JFrame frame;private JLabel imgLabel;private void addComponentsToPane(Container pane, Image img) {JPanel sliderPanel = new JPanel();sliderPanel.setLayout(new BoxLayout(sliderPanel, BoxLayout.PAGE_AXIS));// 1. 添加卷积核结构类型选择框JComboBox<String> elementTypeBox = new JComboBox<>(ELEMENT_TYPE);elementTypeBox.addActionListener((e)->{@SuppressWarnings("unchecked")JComboBox<String> cb = (JComboBox<String>)e.getSource();if (cb.getSelectedIndex() == 0) {elementType = Imgproc.CV_SHAPE_RECT;} else if (cb.getSelectedIndex() == 1) {elementType = Imgproc.CV_SHAPE_CROSS;} else if (cb.getSelectedIndex() == 2) {elementType = Imgproc.CV_SHAPE_ELLIPSE;}update();});sliderPanel.add(elementTypeBox);// 2. 添加卷积核大小滑块sliderPanel.add(new JLabel("Kernel size: 2n + 1"));JSlider slider = new JSlider(0, MAX_KERNEL_SIZE, 0);slider.setMajorTickSpacing(5);slider.setMinorTickSpacing(5);slider.setPaintTicks(true);slider.setPaintLabels(true);slider.addChangeListener((e) ->{JSlider source = (JSlider) e.getSource();kernelSize = source.getValue();update();});sliderPanel.add(slider);// 3. 添加 腐蚀 or 膨胀 选择框;JComboBox<String> morphOpBox = new JComboBox<>(MORPH_OP);morphOpBox.addActionListener(( e) ->{@SuppressWarnings("unchecked")JComboBox<String> cb = (JComboBox<String>)e.getSource();doErosion = cb.getSelectedIndex() == 0;update();});sliderPanel.add(morphOpBox);pane.add(sliderPanel, BorderLayout.PAGE_START);imgLabel = new JLabel(new ImageIcon(img));pane.add(imgLabel, BorderLayout.CENTER);}private void update() {Mat element = Imgproc.getStructuringElement(elementType, new Size(2 * kernelSize + 1, 2 * kernelSize + 1),new Point(kernelSize, kernelSize));if (doErosion) {Imgproc.erode(matImgSrc, matImgDst, element);} else {Imgproc.dilate(matImgSrc, matImgDst, element);}Image img = HighGui.toBufferedImage(matImgDst);imgLabel.setIcon(new ImageIcon(img));frame.repaint();}@Testpublic void erodeTest() throws Throwable{matImgSrc = Imgcodecs.imread("./images/mao.PNG");// 创建窗口并初始化frame = new JFrame("Erosion and dilatation demo");frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);// 图像显示看板Image img = HighGui.toBufferedImage(matImgSrc);addComponentsToPane(frame.getContentPane(), img);frame.pack();frame.setVisible(true);while (frame.isValid()) {Thread.sleep(10000);}}
}
效果:

5 开运算、闭运算、形态学梯度、顶帽、黑帽
通过膨胀和腐蚀这两种基本的形态学操作,可以实现更多高级的形态学变换,比如开闭运算、形态学梯度、顶帽、黑帽等。
- 开运算:是先腐蚀后膨胀的过程;开运算可以消除小物体,在纤细点处分离物体,并且在平滑较大物体边界的同时,不明显改变其面积。
- 闭运算:是先膨胀后腐蚀,能够排除小型黑洞;
- 形态学梯度:膨胀与腐蚀结果之差;对二值图像进行这一操作,可以将团块的边缘突出出来;
- 顶帽:开运算结果与原图的差;往往用来分离比邻近点亮一些的斑块,在一幅图具有大幅的背景,而微小物品比较有规律时,可以使用顶帽进行背景提取;
- 黑帽:闭运算结果与原图的差;黑帽运算用来分离比邻近点暗一点的斑块。

示例代码:
import org.bytedeco.javacpp.Loader;
import org.junit.Test;
import org.opencv.core.Mat;
import org.opencv.core.Point;
import org.opencv.core.Size;
import org.opencv.highgui.HighGui;
import org.opencv.imgcodecs.Imgcodecs;
import org.opencv.imgproc.Imgproc;import javax.swing.*;
import javax.swing.event.ChangeEvent;
import java.awt.*;
public class OtherTest {static {Loader.load(org.bytedeco.opencv.opencv_java.class);}private static final String[] MORPH_OP = { "Opening", "Closing", "Gradient", "Top Hat", "Black Hat" };private static final int[] MORPH_OP_TYPE = { Imgproc.MORPH_OPEN, Imgproc.MORPH_CLOSE,Imgproc.MORPH_GRADIENT, Imgproc.MORPH_TOPHAT, Imgproc.MORPH_BLACKHAT };private static final String[] ELEMENT_TYPE = { "Rectangle", "Cross", "Ellipse" };private static final int MAX_KERNEL_SIZE = 21;private Mat matImgSrc;private Mat matImgDst = new Mat();private int morphOpType = Imgproc.MORPH_OPEN;private int elementType = Imgproc.CV_SHAPE_RECT;private int kernelSize = 0;private JFrame frame;private JLabel imgLabel;private void addComponentsToPane(Container pane, Image img) {if (!(pane.getLayout() instanceof BorderLayout)) {pane.add(new JLabel("Container doesn't use BorderLayout!"));return;}JPanel sliderPanel = new JPanel();sliderPanel.setLayout(new BoxLayout(sliderPanel, BoxLayout.PAGE_AXIS));JComboBox<String> morphOpBox = new JComboBox<>(MORPH_OP);morphOpBox.addActionListener((e) ->{@SuppressWarnings("unchecked")JComboBox<String> cb = (JComboBox<String>)e.getSource();morphOpType = MORPH_OP_TYPE[cb.getSelectedIndex()];update();});sliderPanel.add(morphOpBox);JComboBox<String> elementTypeBox = new JComboBox<>(ELEMENT_TYPE);elementTypeBox.addActionListener((e) ->{@SuppressWarnings("unchecked")JComboBox<String> cb = (JComboBox<String>)e.getSource();if (cb.getSelectedIndex() == 0) {elementType = Imgproc.CV_SHAPE_RECT;} else if (cb.getSelectedIndex() == 1) {elementType = Imgproc.CV_SHAPE_CROSS;} else if (cb.getSelectedIndex() == 2) {elementType = Imgproc.CV_SHAPE_ELLIPSE;}update();});sliderPanel.add(elementTypeBox);sliderPanel.add(new JLabel("Kernel size: 2n + 1"));JSlider slider = new JSlider(0, MAX_KERNEL_SIZE, 0);slider.setMajorTickSpacing(5);slider.setMinorTickSpacing(5);slider.setPaintTicks(true);slider.setPaintLabels(true);slider.addChangeListener((ChangeEvent e) ->{JSlider source = (JSlider) e.getSource();kernelSize = source.getValue();update();});sliderPanel.add(slider);pane.add(sliderPanel, BorderLayout.PAGE_START);imgLabel = new JLabel(new ImageIcon(img));pane.add(imgLabel, BorderLayout.CENTER);}private void update() {Mat element = Imgproc.getStructuringElement(elementType, new Size(2 * kernelSize + 1, 2 * kernelSize + 1),new Point(kernelSize, kernelSize));Imgproc.morphologyEx(matImgSrc, matImgDst, morphOpType, element);Image img = HighGui.toBufferedImage(matImgDst);imgLabel.setIcon(new ImageIcon(img));frame.repaint();}@Testpublic void morphologyTest() throws Throwable{matImgSrc = Imgcodecs.imread("./images/mao.PNG");// 创建窗口frame = new JFrame("Morphology Transformations demo");frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);Image img = HighGui.toBufferedImage(matImgSrc);addComponentsToPane(frame.getContentPane(), img);frame.pack();frame.setVisible(true);while (frame.isValid()) {Thread.sleep(10000);}}
}
效果图:

本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!
