在本教程中,您将学习如何使用 OpenCV 执行连通分量标记和分析。具体来说,我们将重点介绍 OpenCV 最常用的连通分量标记函数:cv2.connectedComponentsWithStats。

连通分量标记(也称为连通分量分析、斑点提取或区域标记)是图论的一种算法应用,用于确定二进制图像中“斑点”状区域的连通性。

我们经常在与使用轮廓相同的情况下使用连通分量分析;然而,连通分量标记通常可以让我们对二值图像中的斑点进行更细粒度的过滤。在使用轮廓分析时,我们经常受到轮廓层次结构的限制(即一个轮廓包含在另一个轮廓中)。通过连通分量分析,我们可以更轻松地分割和分析这些结构。

连通分量分析的一个很好的例子是计算二值(即阈值后的)车牌图像的连通分量,并根据它们的属性(例如宽度、高度、面积、solidity等)过滤斑点。这正是我们今天在这里要做的。

1.OpenCV 连通分量标记和分析

在本教程的第一部分,我们将回顾 OpenCV 提供的用于执行连通分量标记和分析的四个函数。这些函数中最受欢迎的是cv2.connectedComponentsWithStats。

首先,我们将配置我们的开发环境并查看我们的项目目录结构。

接下来,我们将实现两种形式的连通分量分析:

一种方法将演示如何使用 OpenCV 的连通分量标记和分析函数,计算每个连通分量的统计数据,然后单独提取/可视化每个连通分量。

第二种方法显示了连接分量分析的实际示例。我们对车牌进行阈值化,然后使用连通分量分析仅提取车牌字符。

1.1 OpenCV 连通分量标记和分析函数

OpenCV 提供了四种连通分量分析函数:

  • cv2.connectedComponents
  • cv2.connectedComponentsWithStats
  • cv2.connectedComponentsWithAlgorithm
  • cv2.connectedComponentsWithStatsWithAlgorithm

最流行的方法是 cv2.connectedComponentsWithStats,它返回以下信息:

  • 连通分量的边界框
  • 连通分量的面积(以像素为单位)
  • 连通分量的质心/中心 (x, y) 坐标

第一种方法,cv2.connectedComponents,和第二种方法一样,只是不返回上面的统计信息。在绝大多数情况下,您将需要统计信息,因此简单地使用 cv2.connectedComponentsWithStats 即可。

第三种方法 cv2.connectedComponentsWithAlgorithm 实现了更快、更有效的连通分量分析算法。

如果您使用并行处理支持编译 OpenCV,则 cv2.connectedComponentsWithAlgorithm 和 cv2.connectedComponentsWithStatsWithAlgorithm 将比前两个运行得更快。

但一般来说,坚持使用 cv2.connectedComponentsWithStats 直到您熟悉连通分量标记。

1.2 项目结构

在我们使用 OpenCV 实现连通分量标记和分析之前,让我们先来看看我们的项目目录结构。

我们将应用连通分量分析来自动过滤车牌 (license_plate.png) 中的字符。

为了完成这项任务并了解有关连通分量分析的更多信息,我们将实现两个 Python 脚本:

basic_connected_components.py:演示如何应用连通分量标记,提取每个组件及其统计数据,并在我们的屏幕上可视化它们。

filtering_connected_components.py:应用连通分量标记,通过检查每个连通分量的宽度、高度和面积(以像素为单位)过滤掉非牌照字符。

2.案例实现

2.1 使用 OpenCV 实现基本的连通分量标记

让我们开始使用 OpenCV 实现连通分量分析。

打开项目文件夹中的 basic_connected_components.py 文件,让我们开始工作:

# 导入相关包
# 导入必要的包
import argparse
import cv2

# 解析构建的参数解析器
ap = argparse.ArgumentParser()
ap.add_argument("-i", "--image", required=True, help="path to input image")
ap.add_argument("-c", "--connectivity", type=int, default=4, help="connectivity for connected analysis")
args = vars(ap.parse_args())  # 将参数转为字典格式

我们有两个命令行参数

–image:输入图像路径

–connectivity:4连通或者8连通

接下来,进行图像预处理操作

# 加载输入图像,将其转换为灰度,并对其进行阈值处理
image = cv2.imread(args["image"])
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1]

阈值处理以后,将得到如下图像:

请注意车牌字符在黑色背景上显示为白色。但是,输入图像中也有一堆噪声也显示为前景(白色)。我们的目标是应用连通分量分析来过滤掉这些噪声区域,只留下车牌字符。

但在我们开始之前,让我们先学习如何使用 cv2.connectedComponentsWithStats 函数:

output = cv2.connectedComponentsWithStats(thresh, args["connectivity"], cv2.CV_32S)
(numLabels, labels, stats, centroids) = output

使用OpenCV的cv2.connectedComponentsWithStats 执行连通分量分析。我们在这里传入三个参数:

  • 阈值化后的图像
  • 4连通还是8连通
  • 数据类型(应该使用cv2.CV_32S)

然后 cv2.connectedComponentsWithStats 返回一个 4 元组:

  • 检测到的唯一标签总数(即总连通分量数)
  • 一个名为labels的掩码, 掩码与我们的输入阈值图像具有相同的空间维度。对于labels中的每个位置,我们都有一个整数 ID 值,该值对应于像素所属的连通分量。您将在本节后面学习如何过滤labels矩阵。
  • stats:每个连通分量的统计信息,包括边界框坐标和面积(以像素为单位)。
  • 每个连通分量的质心(即中心)(x,y)坐标。

让我们开始解析这些数值:

# 遍历每个连通分量
for i in range(0, numLabels):
    # 0表示的是背景连通分量,忽略
    if i == 0:
        text = "examining component {}/{} (background)".format(
            i   1, numLabels)
    # otherwise, we are examining an actual connected component
    else:
        text = "examining component {}/{}".format(i   1, numLabels)
    # 打印当前的状态信息
    print("[INFO] {}".format(text))
    # 提取当前标签的连通分量统计信息和质心
    x = stats[i, cv2.CC_STAT_LEFT]
    y = stats[i, cv2.CC_STAT_TOP]
    w = stats[i, cv2.CC_STAT_WIDTH]
    h = stats[i, cv2.CC_STAT_HEIGHT]
    area = stats[i, cv2.CC_STAT_AREA]
    (cX, cY) = centroids[i]

if/else语句说明:

  • 第一个连通分量,即ID 为 0,始终是背景。我们通常会忽略背景,但如果您需要它,请记住 ID=0 包含它。
  • 否则,如果 i > 0,那么我们知道该连通分量值得进一步探索。

解析我们的统计数据和质心列表:

  • 连通分量的起始x坐标
  • 连通分量的起始y坐标
  • 连通分量的宽(w)
  • 连通分量的高(h)
  • 连通分量的质心坐标(x,y)
    # 可视化边界框和当前连通分量的质心
    # clone原始图,在图上画当前连通分量的边界框以及质心
    output = image.copy()
    cv2.rectangle(output, (x, y), (x   w, y   h), (0, 255, 0), 3)
    cv2.circle(output, (int(cX), int(cY)), 4, (0, 0, 255), -1)

创建一个我们可以绘制的输出图像。然后我们将当前的连通分量的边界框绘制为绿色矩形,将质心绘制为红色圆圈。

我们的最终代码块演示了如何为当前连通分量创建掩码:

    # 创建掩码
    componentMask = (labels == i).astype("uint8") * 255
    # 显示输出图像和掩码
    cv2.imshow("Output", output)
    cv2.imshow("Connected Component", componentMask)
    cv2.waitKey(0)

首先在labels中找到与当前组件 ID 相等的所有位置。然后我们将结果转换为一个无符号的 8 位整数,其中背景值为 0,前景值为 255。最后显示原始图以及掩码图。

第一个连通分量实际上是我们的背景。我们通常会跳过,因为通常不需要背景。 然后显示其余连通分量。对于每个连通分量,我们绘制边界框(绿色矩形)和质心/中心(红色圆圈)。 您可能已经注意到,其中一些连接的组件是车牌字符,而另一些则只是“噪音”。我们将在下一部分解决这个问题。

2.2 完整代码

# 导入必要的包
import argparse
import cv2

# 解析构建的参数解析器
ap = argparse.ArgumentParser()
ap.add_argument("-i", "--image", default="plate.jpg", help="path to input image")
ap.add_argument("-c", "--connectivity", type=int, default=4, help="connectivity for connected analysis")
args = vars(ap.parse_args())  # 将参数转为字典格式

# 加载输入图像,将其转换为灰度,并对其进行阈值处理
image = cv2.imread(args["image"])
cv2.imshow("src", image)
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1]
cv2.imshow("threshold", thresh)

# 对阈值化后的图像应用连通分量分析
output = cv2.connectedComponentsWithStats(thresh, args["connectivity"], cv2.CV_32S)
(numLabels, labels, stats, centroids) = output

# 遍历每个连通分量
for i in range(0, numLabels):
    # 0表示的是背景连通分量,忽略
    if i == 0:
        text = "examining component {}/{} (background)".format(
            i   1, numLabels)
    # otherwise, we are examining an actual connected component
    else:
        text = "examining component {}/{}".format(i   1, numLabels)
    # 打印当前的状态信息
    print("[INFO] {}".format(text))
    # 提取当前标签的连通分量统计信息和质心
    x = stats[i, cv2.CC_STAT_LEFT]
    y = stats[i, cv2.CC_STAT_TOP]
    w = stats[i, cv2.CC_STAT_WIDTH]
    h = stats[i, cv2.CC_STAT_HEIGHT]
    area = stats[i, cv2.CC_STAT_AREA]
    (cX, cY) = centroids[i]

    # 可视化边界框和当前连通分量的质心
    # clone原始图,在图上画当前连通分量的边界框以及质心
    output = image.copy()
    cv2.rectangle(output, (x, y), (x   w, y   h), (0, 255, 0), 3)
    cv2.circle(output, (int(cX), int(cY)), 4, (0, 0, 255), -1)

    # 创建掩码
    componentMask = (labels == i).astype("uint8") * 255
    # 显示输出图像和掩码
    cv2.imshow("Output", output)
    cv2.imshow("Connected Component", componentMask)
    cv2.waitKey(0)

2.3 过滤连通分量

我们之前的代码示例演示了如何使用 OpenCV 提取连接的组件,但没有演示如何过滤它们。

import numpy as np
import argparse
import cv2

ap = argparse.ArgumentParser()
ap.add_argument("-i", "--image", default="plate.jpg", help="path to image")
ap.add_argument("-c", "--connectivity", type=int, default=4, help="connectivity for connected component analysis")

args = vars(ap.parse_args())

# 加载图像,转为灰度,二值化
image = cv2.imread(args["image"])
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
_, thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_OTSU | cv2.THRESH_BINARY)

# 应用连通分量分析
output = cv2.connectedComponentsWithStats(thresh, connectivity=args["connectivity"], ltype=cv2.CV_32S)
(numLabels, labels, stats, centriods) = output

mask = np.zeros(gray.shape, dtype="uint8")

for i in range(1, numLabels):  # 忽略背景
    x = stats[i, cv2.CC_STAT_LEFT]  # [i, 0]
    y = stats[i, cv2.CC_STAT_TOP]  # [i, 1]
    w = stats[i, cv2.CC_STAT_WIDTH]  # [i, 2]
    h = stats[i, cv2.CC_STAT_HEIGHT]  # [i, 3]
    area = stats[i, cv2.CC_STAT_AREA]  # [i, 4]
    # 确保宽高以及面积既不太大也不太小
    keepWidth = w > 50 and w < 500
    keepHeight = h > 150 and h < 650
    keepArea = area > 500 and area < 25000
    # 我使用print语句显示每个连接组件的宽度、高度和面积,
    # 同时将它们单独显示在屏幕上。我记录了车牌字符的宽度、高度和面积,并找到了它们的最小/最大值,
    # 对于您自己的应用程序也应该这样做。

    if all((keepWidth, keepHeight, keepArea)):
        print("[INFO] keep connected component '{}'".format(i))
        componentMask = (labels == i).astype("uint8") * 255
        mask = cv2.bitwise_or(mask, componentMask)

cv2.imshow("Image", image)
cv2.imshow("Chracters", mask)
cv2.waitKey(0)

如果我们正在构建一个自动牌照/车牌识别(ALPR/ANPR)系统,我们将获取这些字符,然后将它们传递给光学字符识别(OCR)算法进行识别。但这一切都取决于我们是否能够将字符二值化并提取它们,连通分量分析使我们能够做到这一点!

2.4 C 代码案例

#include <opencv2/core/utility.hpp>
#include "opencv2/imgproc.hpp"
#include "opencv2/imgcodecs.hpp"
#include "opencv2/highgui.hpp"
#include <iostream>
using namespace cv;
using namespace std;
Mat img;
int threshval = 100;
static void on_trackbar(int, void*)
{
    Mat bw = threshval < 128 ? (img < threshval) : (img > threshval);
    Mat labelImage(img.size(), CV_32S);
    int nLabels = connectedComponents(bw, labelImage, 8);
    std::vector<Vec3b> colors(nLabels);
    colors[0] = Vec3b(0, 0, 0);//background
    for(int label = 1; label < nLabels;   label){
        colors[label] = Vec3b( (rand()&255), (rand()&255), (rand()&255) );
    }
    Mat dst(img.size(), CV_8UC3);
    for(int r = 0; r < dst.rows;   r){
        for(int c = 0; c < dst.cols;   c){
            int label = labelImage.at<int>(r, c);
            Vec3b &pixel = dst.at<Vec3b>(r, c);
            pixel = colors[label];
         }
     }
    imshow( "Connected Components", dst );
}
int main( int argc, const char** argv )
{
    CommandLineParser parser(argc, argv, "{@image|stuff.jpg|image for converting to a grayscale}");
    parser.about("\nThis program demonstrates connected components and use of the trackbar\n");
    parser.printMessage();
    cout << "\nThe image is converted to grayscale and displayed, another image has a trackbar\n"
            "that controls thresholding and thereby the extracted contours which are drawn in color\n";
    String inputImage = parser.get<string>(0);
    img = imread(samples::findFile(inputImage), IMREAD_GRAYSCALE);
    if(img.empty())
    {
        cout << "Could not read input image file: " << inputImage << endl;
        return EXIT_FAILURE;
    }
    imshow( "Image", img );
    namedWindow( "Connected Components", WINDOW_AUTOSIZE);
    createTrackbar( "Threshold", "Connected Components", &threshval, 255, on_trackbar );
    on_trackbar(threshval, 0);
    waitKey(0);
    return EXIT_SUCCESS;
}

到此这篇关于详解OpenCV执行连通分量标记的方法和分析的文章就介绍到这了,更多相关OpenCV连通分量标记内容请搜索Devmax以前的文章或继续浏览下面的相关文章希望大家以后多多支持Devmax!

详解OpenCV执行连通分量标记的方法和分析的更多相关文章

  1. iOS使用openCV检测来自摄像头的矩形

    如果我在处理它之前克隆matimage,通过记录它,它似乎处理图像甚至找到矩形,但矩形不会被绘制到图像输出到imageView.我很确定我错过了一些东西,可能是因为我没有正确传递某个对象,指向对象的指针等等,而我需要修改的对象则没有.无论如何,如果这不是正确的方法,我真的很感谢他们做这样的事情的教程或例子,使用openCV或GPUImage…它不需要尝试使用matimage来设置imageView.image,而只需要将matimage转换为在imageView中实际修改,因为CvVideoCamera已

  2. 使用Xcode为OS X Lion / Mountain Lion编译OpenCV(2.3.1)

    任何人都可以为我提供一些如何使用Xcode在OSXLion上编译OpenCV2.3.1的详细指南吗?我对此感到生气…我得到了源码,使用cmake创建Xcode模板并尝试构建它,但它失败了大约200个错误.提前致谢,大教堂解答我的回答帖子.解决方法详细指南如何使用MacPorts在Xcode4.2.1的OSXLion下启动和运行OpenCV2.3.1编辑08/06/2012:这也适用于OpenCV2.4.1.只需确保您获得最新版本的Xcode并安装“命令行工具”.编辑15/08/2012:使用Mountai

  3. ios – OpenCV构建问题,找不到ext/atomicity.h

    我得到编译器错误抱怨在构建包含OpenCV的项目时.环境是针对iOS的Xcode4.5.它为模拟器编译良好,但在为设备构建时失败.这是错误文本:我正在使用opencv2.framework,使用指令here构建cmake.解决方法默认情况下,XCode4.5使用libc(支持C11的LLVMC标准库)生成要构建的新项目.但OpenCV期望针对GNUlibstd

  4. 从IOS / iPad / iPhone的最大速度

    我使用OpenCVforiOS完成计算密集型应用程序.当然这很慢.但它比我的PC原型慢了200倍.所以我正在优化它.从最初的15秒,我能够获得0.4秒的速度.我想知道我是否找到了所有的东西以及别人想要分享的东西.我做了什么:>将OpenCV中的“double”数据类型替换为“float”.双倍是64位,32位cpu不能轻易处理,所以浮动给了我一些速度.OpenCV经常使用双倍.>为编译器选项添加了

  5. 在Swift iOS中使用OpenCV

    在我的xcode项目中添加OpenCV2框架后,我试图搜索samlpes或教程与swift集成。有什么好的教程同样吗?OpenCV是用C编写的框架。苹果的reference告诉我们YoucannotimportC++codedirectlyintoSwift.Instead,createanObjective-CorCwrapperforC++code.所以你不能在一个swift项目中直接导入和使用OpenCV,但这实际上并不坏,因为你(需要)继续使用框架的C语法,这是在网络上有很多文档。那么你怎么进行呢

  6. 在Android Studio中解决已弃用的NDK警告

    或者除了添加所有已编译的代码并设置我之前提到的标志之外,还有其他方法可以将OpenCV导入Android项目吗?任何有关这方面的帮助将不胜感激.解决方法我建议迁移到使用cmake的本机支持.您可以查看链接,该链接提供了使用cmake通过以下link添加OpenCV的分步教程.您的代码应该保持不变而不做任何更改,只有必要的操作才能弄清楚如何使用CMakeLists.txt将它们包含在构建过程中.

  7. android – opencv管理器包没找到?如何自动安装?

    我正在使用openCV,每当我运行代码时它都会给我包管理器没有安装,那么如何通过我的应用程序安装它.是必须从Play商店下载它还是我们可以在设备中自动安装它.请帮我.提前致谢.解决方法您需要使用静态初始化来包含apk中的所有OpenCV二进制文件.请参阅此文档:ApplicationDevelopmentwithStaticInitialization

  8. 如何从Android相机中找到框架的轮廓并将其转换为box2d实体?

    使用openframeworks,OpenCV和Box2D,我能够以良好的帧速率实现它.使用Android似乎是一项更复杂的任务(部分原因是我是JAVA新手).这就是我的开始:>使用“OpenCV示例–图像处理”并删除除“canny”效果之外的所有内容,这会产生一个漂亮的黑色&白色图像,非常适合找到轮廓.>从“OpenCVSample–color-blob-detection”中我抓住了在Mat中

  9. 在Android上的OpenCV中逐帧处理视频

    如果是的话,你知道任何例子.>编译适用于Android的FFMPEG也是一种选择.但是,我认为自己编写FrameGrabber和FrameRecorder有点过分.我认为除了JavaCV之外,还必须存在一些解决方案.>从API18开始,Android中有MediaCodec和Mediamuxer.也许他们可以帮助我?

  10. 使用opencv进行android角点跟踪

    当我相对于Android相机移动它时,我试图跟踪一张纸的角落的位置(您可以假设纸张将是与背景完全不同的颜色).我想找到android屏幕上每个角落的x,y坐标.我也希望能够改变纸张的角度,因此它不一定会一直呈现出完美的矩形.我正在使用opencv2.4.1forAndroid,但我在包中找不到cvgoodfeaturetotrack或cvfindcornersubpix.现在我正在考虑使用CvCa

随机推荐

  1. 10 个Python中Pip的使用技巧分享

    众所周知,pip 可以安装、更新、卸载 Python 的第三方库,非常方便。本文小编为大家总结了Python中Pip的使用技巧,需要的可以参考一下

  2. python数学建模之三大模型与十大常用算法详情

    这篇文章主要介绍了python数学建模之三大模型与十大常用算法详情,文章围绕主题展开详细的内容介绍,具有一定的参考价值,感想取得小伙伴可以参考一下

  3. Python爬取奶茶店数据分析哪家最好喝以及性价比

    这篇文章主要介绍了用Python告诉你奶茶哪家最好喝性价比最高,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习吧

  4. 使用pyinstaller打包.exe文件的详细教程

    PyInstaller是一个跨平台的Python应用打包工具,能够把 Python 脚本及其所在的 Python 解释器打包成可执行文件,下面这篇文章主要给大家介绍了关于使用pyinstaller打包.exe文件的相关资料,需要的朋友可以参考下

  5. 基于Python实现射击小游戏的制作

    这篇文章主要介绍了如何利用Python制作一个自己专属的第一人称射击小游戏,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起动手试一试

  6. Python list append方法之给列表追加元素

    这篇文章主要介绍了Python list append方法如何给列表追加元素,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教

  7. Pytest+Request+Allure+Jenkins实现接口自动化

    这篇文章介绍了Pytest+Request+Allure+Jenkins实现接口自动化的方法,文中通过示例代码介绍的非常详细。对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下

  8. 利用python实现简单的情感分析实例教程

    商品评论挖掘、电影推荐、股市预测……情感分析大有用武之地,下面这篇文章主要给大家介绍了关于利用python实现简单的情感分析的相关资料,文中通过示例代码介绍的非常详细,需要的朋友可以参考下

  9. 利用Python上传日志并监控告警的方法详解

    这篇文章将详细为大家介绍如何通过阿里云日志服务搭建一套通过Python上传日志、配置日志告警的监控服务,感兴趣的小伙伴可以了解一下

  10. Pycharm中运行程序在Python console中执行,不是直接Run问题

    这篇文章主要介绍了Pycharm中运行程序在Python console中执行,不是直接Run问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教

返回
顶部