返回:OpenCV系列文章目录(持续更新中......)
上一篇:OpenCV4.9处理平滑图像
下一篇:OpenCV4.9更多形态转换
基于这两者,我们可以对图像进行更复杂的转换。在这里,我们简要讨论 OpenCV 提供的 5 个操作:
开放
它是通过图像的侵蚀和扩张获得的。
- 可用于删除小物体(假设物体在深色前景上是明亮的)
- 例如,请查看下面的示例。左边的图像是原始图像,右边的图像是应用开始变换后的结果。我们可以观察到小点已经消失了。
关闭
它是通过图像的扩张和侵蚀获得的。
- 可用于去除小孔(黑暗区域)。
形态梯度
-
它是图像的膨胀和侵蚀之间的区别。
-
它对于查找对象的轮廓很有用,如下所示:
高顶丝质礼帽
它是输入图像与其开口之间的差异。
黑帽
这是关闭图像与其输入图像之间的差异
代码:
本教程的代码如下所示。您也可以在此处下载
C++
cpp
#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 window
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 );
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 6
int 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:
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() {
@Override
public 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() {
@Override
public 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() {
@Override
public 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 library
System.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() {
@Override
public void run() {
new MorphologyDemo2(args);
}
});
}
}
Python:
python
from __future__ import print_function
import cv2 as cv
import numpy as np
import argparse
morph_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 = 0
val_type = cv.getTrackbarPos(title_trackbar_element_type, title_window)
if val_type == 0:
morph_elem = cv.MORPH_RECT
elif val_type == 1:
morph_elem = cv.MORPH_CROSS
elif val_type == 2:
morph_elem = cv.MORPH_ELLIPSE
element = 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++ 程序的一般结构:
cpp
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) 的原因:
cpp
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,它表示我们的内核是什么样的结构
cppcreateTrackbar( "Element:\n 0: Rect - 1: Cross - 2: Ellipse", window_name, &morph_elem, max_elem, Morphology_Operations );
最终的跟踪栏 Kernel Size 返回要使用的内核大小 (morph_size)
cppcreateTrackbar( "Kernel size:\n 2n +1", window_name, &morph_size, max_kernel_size, Morphology_Operations );
每次我们移动任何滑块时,都会调用用户的函数Morphology_Operations来执行新的形态操作,并且它将根据当前的跟踪栏值更新输出图像。
cppvoid Morphology_Operations( int, void* ) { // Since MORPH_X : 2,3,4,5 and 6 int 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