OpenCV4.9更多形态转换

返回: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,它表示我们的内核是什么样的结构

    cpp 复制代码
     createTrackbar( "Element:\n 0: Rect - 1: Cross - 2: Ellipse", window_name,
     &morph_elem, max_elem,
     Morphology_Operations );

    最终的跟踪栏 Kernel Size 返回要使用的内核大小 (morph_size)

    cpp 复制代码
     createTrackbar( "Kernel size:\n 2n +1", window_name,
     &morph_size, max_kernel_size,
     Morphology_Operations );

    每次我们移动任何滑块时,都会调用用户的函数Morphology_Operations来执行新的形态操作,并且它将根据当前的跟踪栏值更新输出图像。

    cpp 复制代码
     
    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 );
    }

    我们可以观察到,执行形态变换的关键函数是 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

相关推荐
minstbe几秒前
AI开发:使用支持向量机(SVM)进行文本情感分析训练 - Python
人工智能·python·支持向量机
月眠老师4 分钟前
AI在生活各处的利与弊
人工智能
四口鲸鱼爱吃盐19 分钟前
Pytorch | 从零构建MobileNet对CIFAR10进行分类
人工智能·pytorch·分类
苏言の狗20 分钟前
Pytorch中关于Tensor的操作
人工智能·pytorch·python·深度学习·机器学习
yuanbenshidiaos1 小时前
c++---------数据类型
java·jvm·c++
bastgia1 小时前
Tokenformer: 下一代Transformer架构
人工智能·机器学习·llm
菜狗woc1 小时前
opencv-python的简单练习
人工智能·python·opencv
十年一梦实验室1 小时前
【C++】sophus : sim_details.hpp 实现了矩阵函数 W、其导数,以及其逆 (十七)
开发语言·c++·线性代数·矩阵
15年网络推广青哥1 小时前
国际抖音TikTok矩阵运营的关键要素有哪些?
大数据·人工智能·矩阵
taoyong0011 小时前
代码随想录算法训练营第十一天-239.滑动窗口最大值
c++·算法