返回:OpenCV系列文章目录(持续更新中......)
上一篇:OpenCV4.9处理平滑图像
下一篇:OpenCV4.9更多形态转换
基于这两者,我们可以对图像进行更复杂的转换。在这里,我们简要讨论 OpenCV 提供的 5 个操作:
开放
它是通过图像的侵蚀和扩张获得的。
- 可用于删除小物体(假设物体在深色前景上是明亮的)
- 例如,请查看下面的示例。左边的图像是原始图像,右边的图像是应用开始变换后的结果。我们可以观察到小点已经消失了。
关闭
它是通过图像的扩张和侵蚀获得的。
- 可用于去除小孔(黑暗区域)。
形态梯度
-
它是图像的膨胀和侵蚀之间的区别。
- 它对于查找对象的轮廓很有用,如下所示:
高顶丝质礼帽
它是输入图像与其开口之间的差异。
黑帽
这是关闭图像与其输入图像之间的差异
代码:
本教程的代码如下所示。您也可以在此处下载
C++
#include "opencv2/imgproc.hpp"
#include "opencv2/imgcodecs.hpp"
#include "opencv2/highgui.hpp"
#include <iostream>using namespace cv;Mat src, dst;int morph_elem = 0;
int morph_size = 0;
int morph_operator = 0;
int const max_operator = 4;
int const max_elem = 2;
int const max_kernel_size = 21;const char* window_name = "Morphology Transformations Demo";void Morphology_Operations( int, void* );int main( int argc, char** argv )
{CommandLineParser parser( argc, argv, "{@input | baboon.jpg | input image}" );src = imread( samples::findFile( parser.get<String>( "@input" ) ), IMREAD_COLOR );if (src.empty()){std::cout << "Could not open or find the image!\n" << std::endl;std::cout << "Usage: " << argv[0] << " <Input image>" << std::endl;return EXIT_FAILURE;}namedWindow( window_name, WINDOW_AUTOSIZE ); // Create windowcreateTrackbar("Operator:\n 0: Opening - 1: Closing \n 2: Gradient - 3: Top Hat \n 4: Black Hat", window_name, &morph_operator, max_operator, Morphology_Operations );createTrackbar( "Element:\n 0: Rect - 1: Cross - 2: Ellipse", window_name,&morph_elem, max_elem,Morphology_Operations );createTrackbar( "Kernel size:\n 2n +1", window_name,&morph_size, max_kernel_size,Morphology_Operations );Morphology_Operations( 0, 0 );waitKey(0);return 0;
}void Morphology_Operations( int, void* )
{// Since MORPH_X : 2,3,4,5 and 6int operation = morph_operator + 2;Mat element = getStructuringElement( morph_elem, Size( 2*morph_size + 1, 2*morph_size+1 ), Point( morph_size, morph_size ) );morphologyEx( src, dst, operation, element );imshow( window_name, dst );
}
Java:
import java.awt.BorderLayout;
import java.awt.Container;
import java.awt.Image;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;import javax.swing.BoxLayout;
import javax.swing.ImageIcon;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JSlider;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;import org.opencv.core.Core;
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;public class MorphologyDemo2 {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;public MorphologyDemo2(String[] args) {String imagePath = args.length > 0 ? args[0] : "../data/LinuxLogo.jpg";matImgSrc = Imgcodecs.imread(imagePath);if (matImgSrc.empty()) {System.out.println("Empty image: " + imagePath);System.exit(0);}// Create and set up the window.frame = new JFrame("Morphology Transformations demo");frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);// Set up the content pane.Image img = HighGui.toBufferedImage(matImgSrc);addComponentsToPane(frame.getContentPane(), img);// Use the content pane's default BorderLayout. No need for// setLayout(new BorderLayout());// Display the window.frame.pack();frame.setVisible(true);}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(new ActionListener() {@Overridepublic void actionPerformed(ActionEvent 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(new ActionListener() {@Overridepublic void actionPerformed(ActionEvent 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(new ChangeListener() {@Overridepublic void stateChanged(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();}public static void main(String[] args) {// Load the native OpenCV librarySystem.loadLibrary(Core.NATIVE_LIBRARY_NAME);// Schedule a job for the event dispatch thread:// creating and showing this application's GUI.javax.swing.SwingUtilities.invokeLater(new Runnable() {@Overridepublic void run() {new MorphologyDemo2(args);}});}
}
Python:
from __future__ import print_function
import cv2 as cv
import numpy as np
import argparsemorph_size = 0
max_operator = 4
max_elem = 2
max_kernel_size = 21
title_trackbar_operator_type = 'Operator:\n 0: Opening - 1: Closing \n 2: Gradient - 3: Top Hat \n 4: Black Hat'
title_trackbar_element_type = 'Element:\n 0: Rect - 1: Cross - 2: Ellipse'
title_trackbar_kernel_size = 'Kernel size:\n 2n + 1'
title_window = 'Morphology Transformations Demo'
morph_op_dic = {0: cv.MORPH_OPEN, 1: cv.MORPH_CLOSE, 2: cv.MORPH_GRADIENT, 3: cv.MORPH_TOPHAT, 4: cv.MORPH_BLACKHAT}def morphology_operations(val):morph_operator = cv.getTrackbarPos(title_trackbar_operator_type, title_window)morph_size = cv.getTrackbarPos(title_trackbar_kernel_size, title_window)morph_elem = 0val_type = cv.getTrackbarPos(title_trackbar_element_type, title_window)if val_type == 0:morph_elem = cv.MORPH_RECTelif val_type == 1:morph_elem = cv.MORPH_CROSSelif val_type == 2:morph_elem = cv.MORPH_ELLIPSEelement = cv.getStructuringElement(morph_elem, (2*morph_size + 1, 2*morph_size+1), (morph_size, morph_size))operation = morph_op_dic[morph_operator]dst = cv.morphologyEx(src, operation, element)cv.imshow(title_window, dst)parser = argparse.ArgumentParser(description='Code for More Morphology Transformations tutorial.')
parser.add_argument('--input', help='Path to input image.', default='LinuxLogo.jpg')
args = parser.parse_args()src = cv.imread(cv.samples.findFile(args.input))
if src is None:print('Could not open or find the image: ', args.input)exit(0)cv.namedWindow(title_window)
cv.createTrackbar(title_trackbar_operator_type, title_window , 0, max_operator, morphology_operations)
cv.createTrackbar(title_trackbar_element_type, title_window , 0, max_elem, morphology_operations)
cv.createTrackbar(title_trackbar_kernel_size, title_window , 0, max_kernel_size, morphology_operations)morphology_operations(0)
cv.waitKey()
解释
让我们检查一下 C++ 程序的一般结构:
createTrackbar("Operator:\n 0: Opening - 1: Closing \n 2: Gradient - 3: Top Hat \n 4: Black Hat", window_name, &morph_operator, max_operator, Morphology_Operations );
如您所见,值范围为 <2-6>,这就是为什么我们在 Trackbar 输入的值中添加 (+2) 的原因:
int operation = morph_operator + 2;
element:要使用的内核。我们使用函数 cv::getStructuringElement 来定义我们自己的结构。
-
返回:OpenCV系列文章目录(持续更新中......)
-
上一篇:OpenCV4.9侵蚀和扩张
下一篇:OpenCV系列文章目录(持续更新中......)
目标
在本教程中,您将学习如何:
- 使用 OpenCV 函数 cv::morphologyEx 应用形态转换,例如:
- 开放
- 关闭
- 形态梯度
- 高顶丝质礼帽
- 黑帽
-
理论
注意
下面的解释属于 Bradski 和 Kaehler 的 Learning OpenCV 一书。
在上一教程中,我们介绍了两个基本的形态操作:
- 侵蚀
- 扩张。
-
它是通过图像的侵蚀和扩张获得的。
- 可用于删除小物体(假设物体在深色前景上是明亮的)
- 例如,请查看下面的示例。左边的图像是原始图像,右边的图像是应用开始变换后的结果。我们可以观察到小点已经消失了。
-
它是通过图像的扩张和侵蚀获得的。
-
- 可用于去除小孔(黑暗区域)。
-
它是图像的膨胀和侵蚀之间的区别。
-
- 它对于查找对象的轮廓很有用,如下所示:
-
它是输入图像与其开口之间的差异。
-
-
这是关闭图像与其输入图像之间的差异
- 加载图像
- 创建一个窗口以显示形态操作的结果
- 创建三个跟踪栏供用户输入参数:
- 第一个跟踪栏运算符返回要使用的形态操作类型 (morph_operator)
-
第二个 trackbar Element 返回 morph_elem,它表示我们的内核是什么样的结构
createTrackbar( "Element:\n 0: Rect - 1: Cross - 2: Ellipse", window_name,&morph_elem, max_elem,Morphology_Operations );
最终的跟踪栏 Kernel Size 返回要使用的内核大小 (morph_size)
createTrackbar( "Kernel size:\n 2n +1", window_name,&morph_size, max_kernel_size,Morphology_Operations );
每次我们移动任何滑块时,都会调用用户的函数Morphology_Operations来执行新的形态操作,并且它将根据当前的跟踪栏值更新输出图像。
void Morphology_Operations( int, void* ) {// Since MORPH_X : 2,3,4,5 and 6int operation = morph_operator + 2;Mat element = getStructuringElement( morph_elem, Size( 2*morph_size + 1, 2*morph_size+1 ), Point( morph_size, morph_size ) );morphologyEx( src, dst, operation, element );imshow( window_name, dst ); }
我们可以观察到,执行形态变换的关键函数是 cv::morphologyEx 。在此示例中,我们使用四个参数(其余参数保留为默认值):
- src : 源(输入)图像
- dst:输出图像
-
operation:要执行的形态转换类型。请注意,我们有 5 种替代方案:
- 开幕: MORPH_OPEN : 2
- 结束语: MORPH_CLOSE: 3
- 梯度:MORPH_GRADIENT:4
- 礼帽:MORPH_TOPHAT:5
- 黑帽:MORPH_BLACKHAT:6
-
结果
- 编译上面的代码后,我们可以执行它,给出一个图像路径作为参数。使用图像的结果:baboon.png:
- 这是显示窗口的两个快照。第一张图片显示了使用带有交叉内核的运算符 Opening 后的输出。第二张图片(右侧)显示了使用带有椭圆内核的 Blackhat 运算符的结果。
参考文献:
1、《More Morphology Transformations》 ----Ana Huamán