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

相关推荐
Navicat中国14 分钟前
Navicat 18 内置 AI 助手让数据库开发从繁琐操作迈向智能协作
大数据·人工智能·数据库开发
4SAPI36 分钟前
多模型请求路由怎么设计?成本、速度和能力如何平衡
人工智能·python·gateway·php
魔乐社区1 小时前
翼芯・智算测试与适配优化专区,让国产算力“选得准、跑得通、用得好“
人工智能·开源·大模型
XCDoris3 小时前
郴州logo设计机构
大数据·人工智能·python
Goboy3 小时前
一个人带三个Agent,我用全新TRAE从0到1搭了个资讯周刊站
人工智能·trae
俊哥V3 小时前
每日 AI 研究简报 · 2026-10-19
人工智能·ai
AI_AGENT_DEV_AI3 小时前
AI英语教培智能体的开发
人工智能
物联网软硬件开发-轨物科技3 小时前
【技术白皮书】光伏清检一体化机器人系统解决方案技术白皮书
大数据·运维·网络·人工智能·机器人
海宇数据3 小时前
零信任架构实战:基于海宇网上踪迹欺诈行为识别构建自动化信贷资信评估网关
运维·人工智能·架构·自动化
冬奇Lab4 小时前
开源项目第242期:HyperFrames —— 写 HTML 就能渲染视频,专为 AI Agent 设计的确定性视频框架
人工智能·开源·音视频开发