OpenCV快速入门:目标检测——轮廓检测、轮廓的距、点集拟合和二维码检测

文章目录

  • 前言
  • 一、轮廓检测
    • 1.1 图像轮廓的概念
    • 1.2 轮廓检测算法简介
    • 1.3 轮廓检测基本步骤
    • 1.4 轮廓检测函数说明
      • 1.4.1 轮廓发现
      • 1.4.2 轮廓面积
      • 1.4.3 轮廓周长
      • 1.4.4 轮廓外接多边形
      • 1.4.5 点到轮廓距离
      • 1.4.6 凸包检测
    • 1.5 轮廓检测代码实现
  • 二、轮廓的距
    • 2.1 几何距
    • 2.2 中心距
    • 2.3 Hu距
    • 2.4 代码实现
  • 三、点集拟合
      • 最小包围三角形
      • 最小包围圆形
  • 四、二维码检测
  • 总结


前言

在当今数字化时代,计算机视觉的崛起使得目标检测成为科技领域中的一项关键技术。本文将带您快速入门OpenCV中的目标检测,深入探讨轮廓检测、轮廓的距、点集拟合以及二维码检测等核心概念。

OpenCV,作为一种强大的开源计算机视觉库,为开发者提供了丰富的工具和算法,使得目标检测不再是高门槛的技术难题。在本文中,我们将逐步了解目标检测中的关键步骤,从轮廓检测到轮廓的距,再到点集拟合和二维码检测。
OpenCV Logo

一、轮廓检测

1.1 图像轮廓的概念

图像轮廓是由一系列连续的边界点组成的曲线,表示了图像中目标的形状和结构。这些边界点连接在一起,形成了目标的外部轮廓。在计算机视觉中,理解和提取图像轮廓是进行目标检测和形状分析的基础。

1.2 轮廓检测算法简介

轮廓检测的算法旨在识别图像中的显著变化,即目标与背景之间的边界。常用的算法包括Sobel、Canny等边缘检测算法,它们通过检测图像中的梯度变化来确定轮廓位置。

1.3 轮廓检测基本步骤

在OpenCV中,轮廓检测主要使用findContours函数。该函数接受输入图像,并返回轮廓的列表。通过设定合适的阈值,可以在图像中找到目标的轮廓。接着,可以使用drawContours函数将轮廓绘制在原始图像上,使得我们能够直观地观察到目标的形状。

以下是轮廓检测的基本步骤:

  1. 读取图像并将其转换为灰度图像。
  2. 使用合适的边缘检测算法(如Canny)找到图像的边缘。
  3. 应用阈值,将边缘图像转换为二值图像。
  4. 使用findContours函数找到图像中的轮廓。
  5. 绘制轮廓,以便可视化或进一步的分析。

通过深入学习轮廓检测,我们为后续的目标检测过程奠定了坚实的基础。这一章节将帮助读者理解轮廓检测的核心原理以及在OpenCV中的具体实现方法。

1.4 轮廓检测函数说明

在进行轮廓检测时,我们不仅仅关注轮廓的发现,还要深入了解轮廓的一些重要属性。下面我们将通过Python和OpenCV代码演示如何实现轮廓检测及其相关操作。

1.4.1 轮廓发现

在计算机视觉和图像处理中,轮廓是表示图像中对象边界的一种重要方式。OpenCV库提供了 findContours 函数,用于在灰度图像中查找对象的轮廓。

下面是一个简单的代码示例,演示如何使用OpenCV发现轮廓:

# 转换为灰度图
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
# 使用Canny边缘检测
edges = cv2.Canny(gray, 50, 150)
# 查找轮廓
_, contours, _ = cv2.findContours(edges, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
# 在原图上绘制轮廓
cv2.drawContours(image, contours, -1, (0, 255, 0), 2)

findContours 函数基本信息

def findContours(image, mode, method, contours=None, hierarchy=None, offset=None)

参数解释

  1. image: 输入的二值图像,通常为一个8位单通道图像(灰度图像),非零像素被视为1,零像素保持为0。可以使用其他OpenCV函数(例如
    compareinRangethresholdadaptiveThresholdCanny
    等)将灰度图像或彩色图像转换为二值图像。如果mode参数等于RETR_CCOMPRETR_FLOODFILL,则输入也可以是一个32位整数标签图像(CV_32SC1)。

  2. mode: 轮廓检索模式,控制轮廓的层次关系。有几种模式可选,常见的有 RETR_EXTERNAL(只检测最外面的轮廓)、RETR_LIST(检测所有的轮廓,不建立层次关系)、RETR_CCOMP(检测所有轮廓,但只保留两个层次的轮廓信息)和 RETR_TREE(检测所有轮廓,保留完整的层次信息)。

  3. method: 轮廓逼近方法,控制轮廓的表示精度。有几种方法可选,常见的有 CHAIN_APPROX_SIMPLE(压缩水平、垂直和对角方向的轮廓,只保留其端点)、CHAIN_APPROX_TC89_L1CHAIN_APPROX_TC89_KCOS

  4. contours: 输出参数,用于存储检测到的轮廓。每个轮廓以一组点的形式存储,例如 std::vector<std::vector<cv::Point>>

  5. hierarchy: 输出参数,可选,用于存储图像拓扑结构的信息。对于每个轮廓,hierarchy 中的一个元素是一个包含四个整数的数组,分别表示在同一层次上的下一个轮廓、上一个轮廓、第一个子轮廓和父轮廓的索引。如果某个轮廓在相应的方向上没有下一个、上一个、子轮廓或父轮廓,则对应的索引将为负数。

  6. offset: 可选参数,是一个偏移量,用于将每个轮廓点进行偏移。这在从图像ROI提取轮廓后,需要在整个图像上进行分析时很有用。

findContours 函数的主要作用是在给定的二值图像中查找对象的轮廓。它使用Suzuki算法进行轮廓检测,并返回检测到的轮廓,以及可选的图像拓扑结构信息。轮廓是用一组点表示的,这些点描述了对象的边界。这个函数在形状分析、对象检测和识别等领域中非常有用。在检测到轮廓后,你可以进一步进行轮廓的绘制、分析、过滤或者在原图像上进行标记等操作。

1.4.2 轮廓面积

轮廓面积在图像处理中具有广泛的应用。通过计算对象的轮廓面积,我们可以进行目标识别、大小过滤和形状分析。例如,在目标检测中,我们可以通过设定一定的面积阈值来排除过小或过大的轮廓,从而过滤掉不感兴趣的区域。
在OpenCV中,我们可以使用cv2.contourArea(contour)函数来计算轮廓的面积,其中contour是轮廓的点集。

下面是一个简单的代码示例,演示如何使用OpenCV计算轮廓的面积:

# 转换为灰度图
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
# 进行阈值处理
_, thresh = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)
# 寻找轮廓
contours, _ = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
# 计算每个轮廓的面积并输出
for contour in contours:
    area = cv2.contourArea(contour)
    print(f"Contour Area: {area} pixels")

contourArea函数是OpenCV中用于计算轮廓面积的函数。以下是该函数的参数和功能的简要说明:

def contourArea(contour, oriented=None)

参数:

  • contour: 输入参数,表示轮廓的点集,通常是一个包含2D点(轮廓顶点)的向量。
  • oriented: 可选参数,表示是否计算有向面积。如果设置为True,函数将返回一个带有方向的面积值,具体取决于轮廓的方向(顺时针或逆时针)。默认值为False,即返回面积的绝对值。

功能:

  • 该函数计算轮廓的面积,使用的是Green公式。
  • 返回的面积值可能与使用drawContoursfillPoly绘制轮廓时得到的非零像素数不同。这是因为Green公式计算的是理论上的面积,而绘制轮廓时计算的是像素的数量。
  • 注意,对于具有自交点的轮廓,该函数可能给出错误的结果。

1.4.3 轮廓周长

轮廓周长是形状描述的一个重要特征,特别在目标检测和边缘检测中经常被用到。通过计算周长,我们可以获取有关轮廓的详细信息,例如对象的形状复杂程度。这对于区分不同形状的目标或者进行形状分析非常有帮助。

轮廓周长是指轮廓的闭合曲线的长度。在OpenCV中,我们可以使用cv2.arcLength(curve, closed)函数来计算轮廓的周长,其中curve是轮廓的点集,而closed是一个标志,指示轮廓是否闭合。

下面是一个简单的代码示例,演示如何使用OpenCV计算轮廓的周长:

# 计算每个轮廓的周长并输出
for contour in contours:
    perimeter = cv2.arcLength(contour, True)
    print(f"Contour Perimeter: {perimeter}")

arcLength函数是OpenCV中用于计算轮廓周长的函数。以下是该函数的参数和功能的简要说明:

def arcLength(curve, closed)

参数:

  • curve: 输入参数,表示轮廓的点集,通常是一个包含2D点(轮廓顶点)的向量。
  • closed: 标志参数,指示轮廓是否闭合。如果轮廓是封闭的,则为True;否则为False。

功能:

  • 该函数计算轮廓的周长或者曲线的长度。
  • 如果closed参数为True,函数将计算封闭轮廓的周长;如果为False,则计算曲线的长度。

1.4.4 轮廓外接多边形

轮廓外接多边形提供了一种简单但有效的方式来描述和表示目标的形状。这种方法对于快速计算目标的边界框以及后续的目标跟踪和分析非常有用。通过绘制外接矩形,我们可以更直观地了解目标的位置和大小。

轮廓外接多边形是指能够完全包围轮廓的最小矩形,通常是一个矩形框。在OpenCV中,我们可以使用cv2.boundingRect(points)函数来计算轮廓的外接矩形,其中points是轮廓的点集。

下面是一个简单的代码示例,演示如何使用OpenCV计算轮廓的外接矩形并在图像上绘制矩形:

# 外接矩形
for contour in contours:
    x, y, w, h = cv2.boundingRect(contour)
    cv2.rectangle(image, (x, y), (x+w, y+h), (0, 255, 0), 2)

boundingRect函数是OpenCV中用于计算轮廓外接多边形的函数。以下是该函数的参数和功能的简要说明:

def boundingRect(points)

参数:

  • points: 输入参数,表示轮廓的点集,通常是一个包含2D点(轮廓顶点)的向量。

功能:

  • 该函数计算并返回指定点集的最小外接矩形,即能够完全包围点集的矩形框。

1.4.5 点到轮廓距离

点到轮廓的距离是进行形状分析和目标识别的关键步骤之一。通过计算点到轮廓的距离,我们可以判断一个点是否属于某个目标,以及该点相对于目标的具体位置。这对于许多应用场景,如手势识别、物体定位等都是至关重要的。

点到轮廓的距离是指一个给定点到轮廓的最短距离,这可以帮助我们确定点相对于轮廓的位置关系,是在轮廓内部、外部还是在轮廓上。

下面是一个简单的代码示例,演示如何使用OpenCV的pointPolygonTest函数计算点到轮廓的最短距离:

# 计算点到轮廓的最短距离
point = (100, 100)
for contour in contours:
    distance = cv2.pointPolygonTest(contour, point, True)
    print(f'Distance from point to contour: {distance}')

pointPolygonTest函数是OpenCV中用于计算点到轮廓距离的函数。以下是该函数的参数和功能的简要说明:

def pointPolygonTest(contour, pt, measureDist)

参数:

  • contour: 输入参数,表示轮廓的点集,通常是一个包含2D点(轮廓顶点)的向量,可以存储在std::vectorMat中。
  • pt: 输入参数,表示测试点的坐标。
  • measureDist: 输入参数,表示是否测量点到轮廓的距离。如果为True,函数返回点到轮廓的有向距离;如果为False,函数仅检查点相对于轮廓的位置关系,返回+1、-1或0。

功能:

  • 该函数用于执行点在轮廓内的测试。
  • 返回正值表示点在轮廓内部,负值表示点在轮廓外部,零值表示点在轮廓上或与轮廓上的顶点重合。
  • measureDist为True时,返回值为点到最近轮廓边缘的有向距离。

1.4.6 凸包检测

凸包检测在图像处理和计算机视觉中广泛应用,特别是在目标检测和形状分析中。通过计算凸包,我们可以更好地理解目标的整体形状,从而帮助进行目标识别和分析。

凸包检测是寻找一个点集的最小凸多边形的过程。在OpenCV中,我们可以使用cv2.convexHull(points)函数来计算给定点集的凸包。

下面是一个简单的代码示例,演示如何使用OpenCV的convexHull函数进行凸包检测并在图像上绘制凸包:

# 凸包检测
for contour in contours:
    hull = cv2.convexHull(contour)
    cv2.drawContours(image, [hull], 0, (0, 0, 255), 2)

convexHull函数是OpenCV中用于凸包检测的函数。以下是该函数的参数和功能的简要说明:

def convexHull(points, hull=None, clockwise=None, returnPoints=None)

参数:

  • points: 输入参数,表示点集的2D坐标,通常是一个包含2D点的向量,可以存储在std::vectorMat中。
  • hull: 输出参数,表示凸包的点集或索引。可以是一个整数向量,表示凸包点在原始点集中的索引;也可以是一个包含凸包点的向量,表示凸包的实际坐标点。
  • clockwise: 可选参数,表示凸包的方向。如果为True,表示凸包的方向是顺时针的;如果为False,表示凸包的方向是逆时针的。
  • returnPoints: 可选参数,操作标志。如果为True,函数返回凸包的实际坐标点;如果为False,函数返回凸包点在原始点集中的索引。

功能:

  • 该函数用于找到给定点集的凸包。
  • 函数返回凸包的点集或索引,取决于returnPoints参数的设置。
  • 可以选择指定凸包的方向是顺时针还是逆时针。

1.5 轮廓检测代码实现

首先,我们创建一个空白的图像,然后定义了两个函数,一个用于绘制随机椭圆,另一个用于检查新椭圆是否与已存在的椭圆重叠。

# 创建一个空白的图像
width, height = 800, 800
image = np.zeros((height, width, 3), dtype=np.uint8)

# 定义绘制椭圆的函数
def draw_random_ellipse(img):
    # 生成随机椭圆的参数
    # ...

# 定义检查椭圆是否重叠的函数
def check_overlap(new_ellipse, existing_ellipses):
    # ...

接下来,通过调用draw_random_ellipse函数生成一组不重叠的椭圆,并将它们绘制在图像上。

# 生成不重叠的椭圆
num_ellipses = 15
ellipses = []
for _ in range(num_ellipses):
    while True:
        new_ellipse = (
            (random.randint(0, width - 1), random.randint(0, height - 1)),
            (random.randint(10, 100), random.randint(10, 100)),
            random.randint(0, 360),
            (random.randint(0, 255), random.randint(0, 255), random.randint(0, 255))
        )
        if not check_overlap(new_ellipse, ellipses):
            ellipses.append(new_ellipse)
            break

# 绘制不重叠的椭圆
for ellipse in ellipses:
    cv2.ellipse(image, ellipse[0], ellipse[1], ellipse[2], 0, 360, ellipse[3], -1)

然后,我们定义了两个参数字典,用于设置绘制文本的共享参数。

# 共享的参数
shared_params = {
    "fontFace": cv2.FONT_HERSHEY_SIMPLEX,
    "fontScale": 0.5,
    "thickness": 2,
    "color": (0, 0, 0),
    "lineType": cv2.LINE_AA,
}
shared_params2 = {
    "fontFace": cv2.FONT_HERSHEY_SIMPLEX,
    "fontScale": 0.5,
    "thickness": 1,
    "color": (0, 255, 0),
    "lineType": cv2.LINE_AA,
}

接下来,将图像转换为灰度图,并使用Canny边缘检测。

# 转换为灰度图
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

# 使用Canny边缘检测
edges = cv2.Canny(gray, 10, 150)

通过调用cv2.findContours函数,找到图像中的轮廓。

# 查找轮廓
_, contours, _ = cv2.findContours(edges, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

然后,对每个轮廓进行一系列操作,包括计算轮廓面积、周长、中心位置,绘制外接矩形和最短距离的线段。

# 在原图上绘制轮廓并输出面积和周长信息
for contour in contours:
    # 计算轮廓面积和周长
    area = int(cv2.contourArea(contour))
    perimeter = int(cv2.arcLength(contour, True))
    moments = cv2.moments(contour)

    # ...

最后,对每个轮廓进行凸包检测,并在图像上绘制凸包。

    # 凸包检测
    hull = cv2.convexHull(contour)
    cv2.drawContours(image, [hull], 0, (0, 0, 255), 1)

最终,显示包含轮廓信息的图像。

下面是完整的代码内容:

import cv2
import numpy as np
import random

# 创建一个空白的图像
width, height = 800, 800
image = np.zeros((height, width, 3), dtype=np.uint8)


# 定义绘制椭圆的函数
def draw_random_ellipse(img):
    # 生成随机椭圆的参数
    center = (random.randint(0, width - 1), random.randint(0, height - 1))
    axes = (random.randint(10, 100), random.randint(10, 100))
    angle = random.randint(0, 360)
    color = (random.randint(0, 255), random.randint(0, 255), random.randint(0, 255))

    # 绘制椭圆
    cv2.ellipse(img, center, axes, angle, 0, 360, color, -1)


# 定义检查椭圆是否重叠的函数
def check_overlap(new_ellipse, existing_ellipses):
    for existing_ellipse in existing_ellipses:
        # 获取椭圆的掩码
        new_mask = np.zeros_like(image, dtype=np.uint8)
        existing_mask = np.zeros_like(image, dtype=np.uint8)
        cv2.ellipse(new_mask, new_ellipse[0], new_ellipse[1], new_ellipse[2], 0, 360, (255, 255, 255), -1)
        cv2.ellipse(existing_mask, existing_ellipse[0], existing_ellipse[1], existing_ellipse[2], 0, 360,
                    (255, 255, 255), -1)

        # 检查是否有重叠的部分
        overlap = cv2.bitwise_and(new_mask, existing_mask)
        if np.sum(overlap) > 0:
            return True
    return False


# 生成不重叠的椭圆
num_ellipses = 15
ellipses = []
for _ in range(num_ellipses):
    while True:
        new_ellipse = (
            (random.randint(0, width - 1), random.randint(0, height - 1)),
            (random.randint(10, 100), random.randint(10, 100)),
            random.randint(0, 360),
            (random.randint(0, 255), random.randint(0, 255), random.randint(0, 255))
        )
        if not check_overlap(new_ellipse, ellipses):
            ellipses.append(new_ellipse)
            break

# 绘制不重叠的椭圆
for ellipse in ellipses:
    cv2.ellipse(image, ellipse[0], ellipse[1], ellipse[2], 0, 360, ellipse[3], -1)

# 共享的参数
shared_params = {
    "fontFace": cv2.FONT_HERSHEY_SIMPLEX,
    "fontScale": 0.5,
    "thickness": 2,
    "color": (0, 0, 0),
    "lineType": cv2.LINE_AA,
}
shared_params2 = {
    "fontFace": cv2.FONT_HERSHEY_SIMPLEX,
    "fontScale": 0.5,
    "thickness": 1,
    "color": (0, 255, 0),
    "lineType": cv2.LINE_AA,
}

# 转换为灰度图
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

# 使用Canny边缘检测
edges = cv2.Canny(gray, 10, 150)

# 查找轮廓
_, contours, _ = cv2.findContours(edges, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

# 在原图上绘制轮廓
cv2.drawContours(image, contours, -1, (0, 255, 0), 2)


# 在原图上绘制轮廓并输出面积和周长信息
for contour in contours:
    # 计算轮廓面积
    area = int(cv2.contourArea(contour))
    # 计算轮廓周长
    perimeter = int(cv2.arcLength(contour, True))
    moments = cv2.moments(contour)

    # 检查 moments['m00'](轮廓的面积) 是否为零
    if moments['m00'] != 0:
        # 计算轮廓的中心位置
        cx = int(moments['m10'] / moments['m00'])
        cy = int(moments['m01'] / moments['m00'])

        # 在中心位置绘制一个点
        cv2.circle(image, (cx, cy), 5, (255, 255, 255), -1)
        cv2.circle(image, (cx, cy), 3, (0, 0, 255), -1)

        # 外接矩形
        x, y, w, h = cv2.boundingRect(contour)
        cv2.rectangle(image, (x, y), (x + w, y + h), (255, 255, 0), 1)

        # 提取每个点的坐标,将contour转换为二维数组
        contour_2d = contour[:, 0, :]
        # 计算每个点到目标点的距离的平方和
        distances = np.sum((contour_2d - np.array([cx, cy])) ** 2, axis=1)
        # 找到最小值的索引
        closest_point_index = np.argmin(distances)
        closest_point = contour[closest_point_index][0]
        # 在图中画出这个最短距离的线段
        cv2.line(image, (cx, cy), (int(closest_point[0]), int(closest_point[1])), (0, 0, 255), 3)
        cv2.line(image, (cx, cy), (int(closest_point[0]), int(closest_point[1])), (255, 0, 0), 1)

        # 输出计算结果
        cv2.putText(image, f"S= {area}", (max(int(cx - 20), 0), cy + 20), **shared_params)
        cv2.putText(image, f"C= {perimeter}", (max(int(cx - 20), 0), cy + 35), **shared_params)
        cv2.putText(image, f"S= {area}", (max(int(cx - 20), 0), cy + 20), **shared_params2)
        cv2.putText(image, f"C= {perimeter}", (max(int(cx - 20), 0), cy + 35), **shared_params2)

        # 计算每个中心点到轮廓的最短距离
        dist = abs(int(cv2.pointPolygonTest(contour, (cx, cy), True)))
        cv2.putText(image, f"D_min= {dist}", (max(int(cx - 20), 0), cy + 50), **shared_params)
        cv2.putText(image, f"D_min= {dist}", (max(int(cx - 20), 0), cy + 50), **shared_params2)

        # 凸包检测
        hull = cv2.convexHull(contour)
        cv2.drawContours(image, [hull], 0, (0, 0, 255), 1)

# 显示结果
cv2.imshow('Contours', image)
cv2.waitKey(0)
cv2.destroyAllWindows()

Contours

二、轮廓的距

在目标检测中,轮廓不仅仅是一个形状的抽象表示,还包含了有关形状的重要信息,其中之一就是轮廓的距。距是用于描述轮廓形状和结构的一种度量,它可以分为几何距、中心距和Hu距。

2.1 几何距

几何距是通过计算轮廓到某个点的距离的方式来度量轮廓的形状。常见的几何距包括轮廓的面积、周长等,它们提供了关于轮廓整体尺寸的信息。
以下是几何距的一些常见计算公式:

  1. 轮廓面积(Area): 轮廓包围的区域的面积。

Area = ∫ ∫ D 1   d x   d y \text{Area} = \int\int_D 1 \,dx\,dy Area=D1dxdy

其中 D D D 是轮廓包围的区域。

  1. 轮廓周长(Perimeter): 轮廓的周长,即轮廓上所有点到一个参考点的距离之和。

Perimeter = ∮ C d s \text{Perimeter} = \oint_C ds Perimeter=Cds

其中 C C C 是轮廓的曲线, d s ds ds 是轮廓上一点到下一点的弧长元素。

这些几何距的计算提供了有关轮廓形状和结构的基本信息,是轮廓分析中的重要工具。在使用OpenCV进行轮廓分析时,可以利用cv2.contourArea()cv2.arcLength()函数分别计算轮廓的面积和周长。

2.2 中心距

中心距是通过计算轮廓中心到轮廓上所有点的距离来度量轮廓形状的一种距离。中心距包括一阶中心距、二阶中心距等,它们对于形状的平移和旋转具有不变性。以下是中心距的一些常见计算公式:

  1. 一阶中心距(m_10 和 m_01): 描述形状的平移。

m 10 = ∫ ∫ D x   d x   d y m_{10} = \int\int_D x \,dx\,dy m10=Dxdxdy
m 01 = ∫ ∫ D y   d x   d y m_{01} = \int\int_D y \,dx\,dy m01=Dydxdy

其中 D D D 是轮廓包围的区域, x x x y y y 是图像坐标。

  1. 二阶中心距(m_20、m_02 和 m_11): 描述形状的旋转。

m 20 = ∫ ∫ D ( x − x ˉ ) 2   d x   d y m_{20} = \int\int_D (x - \bar{x})^2 \,dx\,dy m20=D(xxˉ)2dxdy
m 02 = ∫ ∫ D ( y − y ˉ ) 2   d x   d y m_{02} = \int\int_D (y - \bar{y})^2 \,dx\,dy m02=D(yyˉ)2dxdy
m 11 = ∫ ∫ D ( x − x ˉ ) ( y − y ˉ )   d x   d y m_{11} = \int\int_D (x - \bar{x})(y - \bar{y}) \,dx\,dy m11=D(xxˉ)(yyˉ)dxdy

其中 x ˉ \bar{x} xˉ y ˉ \bar{y} yˉ 是轮廓的质心坐标。

在OpenCV中,可以使用cv2.moments()函数计算轮廓的矩,进而得到一阶中心距和二阶中心距。给定一个轮廓,该函数返回一个字典,其中包含轮廓的一些矩的信息,如质心、面积等。
这个函数的语法如下:

moments = cv2.moments(contour)

其中,contour是输入的轮廓,而moments是包含轮廓矩信息的字典。

返回的字典包含以下键值对:

  • 'm00': 轮廓的面积。
  • 'm10', 'm01': 分别是x和y方向上的一阶矩。
  • 'm20', 'm02', 'm11': 分别是x和y方向上的二阶矩和xy方向上的一阶矩。
  • 'm30', 'm03', 'm21', 'm12': 分别是x和y方向上的三阶矩,xy方向上的二阶矩和一阶矩。
  • 'mu20', 'mu02', 'mu11': 中心矩,是二阶矩关于质心的矩。
  • 'mu30', 'mu03', 'mu21', 'mu12': 中心矩,是三阶矩关于质心的矩。
  • 'nu20', 'nu02', 'nu11': 归一化中心矩,是中心矩除以面积的二阶矩。
  • 'nu30', 'nu03', 'nu21', 'nu12': 归一化中心矩,是中心矩除以面积的三阶矩。

2.3 Hu距

Hu距是一种通过中心距来构建的轮廓描述符,具有平移、旋转和缩放不变性。Hu距是一组七个独立的距离,通过对中心距的组合计算而得。以下是Hu距的计算公式:

  1. Hu距 1-7:
    Hu1 = η 20 + η 02 Hu2 = ( η 20 − η 02 ) 2 + 4 η 11 2 Hu3 = ( η 30 − 3 η 12 ) 2 + ( 3 η 21 − η 03 ) 2 Hu4 = ( η 30 + η 12 ) 2 + ( η 21 + η 03 ) 2 Hu5 = ( η 30 − 3 η 12 ) ( η 30 + η 12 ) [ ( η 30 + η 12 ) 2 − 3 ( η 21 + η 03 ) 2 ] Hu6 = ( η 20 − η 02 ) [ ( η 30 + η 12 ) 2 − ( η 21 + η 03 ) 2 ] + 4 η 11 ( η 30 + η 12 ) ( η 21 + η 03 ) Hu7 = ( 3 η 21 − η 03 ) ( η 30 + η 12 ) [ ( η 30 + η 12 ) 2 − 3 ( η 21 + η 03 ) 2 ] − ( η 30 − 3 η 12 ) ( η 21 + η 03 ) [ 3 ( η 30 + η 12 ) 2 − ( η 21 + η 03 ) 2 ] \begin{split} & \text{Hu1} = \eta_{20} + \eta_{02} \\ & \text{Hu2} = (\eta_{20} - \eta_{02})^2 + 4\eta_{11}^2 \\ & \text{Hu3} = (\eta_{30} - 3\eta_{12})^2 + (3\eta_{21} - \eta_{03})^2 \\ & \text{Hu4} = (\eta_{30} + \eta_{12})^2 + (\eta_{21} + \eta_{03})^2 \\ & \text{Hu5} = (\eta_{30} - 3\eta_{12})(\eta_{30} + \eta_{12})[(\eta_{30} + \eta_{12})^2 - 3(\eta_{21} + \eta_{03})^2] \\ & \text{Hu6} = (\eta_{20} - \eta_{02})[(\eta_{30} + \eta_{12})^2 - (\eta_{21} + \eta_{03})^2] + 4\eta_{11}(\eta_{30} + \eta_{12})(\eta_{21} + \eta_{03}) \\ & \text{Hu7} = (3\eta_{21} - \eta_{03})(\eta_{30} + \eta_{12})[(\eta_{30} + \eta_{12})^2 - 3(\eta_{21} + \eta_{03})^2] - (\eta_{30} - 3\eta_{12})(\eta_{21} + \eta_{03})[3(\eta_{30} + \eta_{12})^2 - (\eta_{21} + \eta_{03})^2] \\ \end{split} Hu1=η20+η02Hu2=(η20η02)2+4η112Hu3=(η303η12)2+(3η21η03)2Hu4=(η30+η12)2+(η21+η03)2Hu5=(η303η12)(η30+η12)[(η30+η12)23(η21+η03)2]Hu6=(η20η02)[(η30+η12)2(η21+η03)2]+4η11(η30+η12)(η21+η03)Hu7=(3η21η03)(η30+η12)[(η30+η12)23(η21+η03)2](η303η12)(η21+η03)[3(η30+η12)2(η21+η03)2]

    其中 η p q \eta_{pq} ηpq 是归一化中心距,计算公式如下:

η p q = μ p q μ 00 1 + p + q 2 \eta_{pq} = \frac{\mu_{pq}}{\mu_{00}^{1+\frac{p+q}{2}}} ηpq=μ001+2p+qμpq

其中 μ p q \mu_{pq} μpq 是轮廓的中心距。

这七个Hu距对于图像的平移、旋转和缩放具有不变性,是图像识别和匹配中常用的特征描述符。在OpenCV中,可以使用cv2.HuMoments()函数计算Hu距。

2.4 代码实现

下面是使用OpenCV实现轮廓的距的代码示例:

import cv2
import numpy as np
import random

# 创建一个空白的图像
width, height = 800, 800
image = np.zeros((height, width, 3), dtype=np.uint8)


# 定义绘制椭圆的函数
def draw_random_ellipse(img):
    # 生成随机椭圆的参数
    center = (random.randint(0, width - 1), random.randint(0, height - 1))
    axes = (random.randint(10, 100), random.randint(10, 100))
    angle = random.randint(0, 360)
    color = (random.randint(0, 255), random.randint(0, 255), random.randint(0, 255))

    # 绘制椭圆
    cv2.ellipse(img, center, axes, angle, 0, 360, color, -1)


# 定义检查椭圆是否重叠的函数
def check_overlap(new_ellipse, existing_ellipses):
    for existing_ellipse in existing_ellipses:
        # 获取椭圆的掩码
        new_mask = np.zeros_like(image, dtype=np.uint8)
        existing_mask = np.zeros_like(image, dtype=np.uint8)
        cv2.ellipse(new_mask, new_ellipse[0], new_ellipse[1], new_ellipse[2], 0, 360, (255, 255, 255), -1)
        cv2.ellipse(existing_mask, existing_ellipse[0], existing_ellipse[1], existing_ellipse[2], 0, 360,
                    (255, 255, 255), -1)

        # 检查是否有重叠的部分
        overlap = cv2.bitwise_and(new_mask, existing_mask)
        if np.sum(overlap) > 0:
            return True
    return False


# 生成不重叠的椭圆
num_ellipses = 5
ellipses = []
for _ in range(num_ellipses):
    while True:
        new_ellipse = (
            (random.randint(0, width - 1), random.randint(0, height - 1)),
            (random.randint(10, 100), random.randint(10, 100)),
            random.randint(0, 360),
            (random.randint(0, 255), random.randint(0, 255), random.randint(0, 255))
        )
        if not check_overlap(new_ellipse, ellipses):
            ellipses.append(new_ellipse)
            break

# 绘制不重叠的椭圆
for ellipse in ellipses:
    cv2.ellipse(image, ellipse[0], ellipse[1], ellipse[2], 0, 360, ellipse[3], -1)

# 转换为灰度图
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

# 使用Canny边缘检测
edges = cv2.Canny(gray, 50, 150)

# 查找轮廓
_, contours, _ = cv2.findContours(edges, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

# 假设contours是你的轮廓列表
for contour in contours:
    # 计算轮廓的面积和周长
    area = cv2.contourArea(contour)
    perimeter = cv2.arcLength(contour, True)
    print(f'轮廓面积: {area}, 周长: {perimeter}')

    # 计算轮廓的中心位置
    moments = cv2.moments(contour)
    cx = int(moments['m10'] / moments['m00'])
    cy = int(moments['m01'] / moments['m00'])
    print(f'中心位置: ({cx}, {cy})')

    # 计算二阶中心距
    mu = cv2.moments(contour)
    nu20 = mu['nu20']
    nu02 = mu['nu02']
    nu11 = mu['nu11']
    print(f'二阶中心距: nu20={nu20}, nu02={nu02}, nu11={nu11}')

    # 计算Hu距
    hu_moments = cv2.HuMoments(mu)
    print(f'Hu距: \n {hu_moments}')

这段代码演示了如何计算轮廓的面积、周长以及中心位置,并且计算了二阶中心距和Hu距。通过这些距离的计算,我们可以更全面地了解轮廓的形状特征,为目标检测提供更多信息。

三、点集拟合

在目标检测中,点集拟合是一项重要的任务,它通过数学模型来逼近一组离散的点,从而更好地理解和描述目标的形状。在OpenCV中,我们通常使用最小包围三角形、最小包围圆形等方法进行点集拟合。

最小包围三角形

最小包围三角形是将一组点拟合为一个最小的包围三角形,该三角形能够包含所有的离散点。

import cv2
import numpy as np
import random

# 创建一个空白的图像
width, height = 800, 800
image = np.zeros((height, width, 3), dtype=np.uint8)


# 定义绘制椭圆的函数
def draw_random_ellipse(img):
    # 生成随机椭圆的参数
    center = (random.randint(0, width - 1), random.randint(0, height - 1))
    axes = (random.randint(10, 100), random.randint(10, 100))
    angle = random.randint(0, 360)
    color = (random.randint(0, 255), random.randint(0, 255), random.randint(0, 255))

    # 绘制椭圆
    cv2.ellipse(img, center, axes, angle, 0, 360, color, -1)


# 定义检查椭圆是否重叠的函数
def check_overlap(new_ellipse, existing_ellipses):
    for existing_ellipse in existing_ellipses:
        # 获取椭圆的掩码
        new_mask = np.zeros_like(image, dtype=np.uint8)
        existing_mask = np.zeros_like(image, dtype=np.uint8)
        cv2.ellipse(new_mask, new_ellipse[0], new_ellipse[1], new_ellipse[2], 0, 360, (255, 255, 255), -1)
        cv2.ellipse(existing_mask, existing_ellipse[0], existing_ellipse[1], existing_ellipse[2], 0, 360,
                    (255, 255, 255), -1)

        # 检查是否有重叠的部分
        overlap = cv2.bitwise_and(new_mask, existing_mask)
        if np.sum(overlap) > 0:
            return True
    return False


# 生成不重叠的椭圆
num_ellipses = 15
ellipses = []
for _ in range(num_ellipses):
    while True:
        new_ellipse = (
            (random.randint(0, width - 1), random.randint(0, height - 1)),
            (random.randint(10, 100), random.randint(10, 100)),
            random.randint(0, 360),
            (random.randint(0, 255), random.randint(0, 255), random.randint(0, 255))
        )
        if not check_overlap(new_ellipse, ellipses):
            ellipses.append(new_ellipse)
            break

# 绘制不重叠的椭圆
for ellipse in ellipses:
    cv2.ellipse(image, ellipse[0], ellipse[1], ellipse[2], 0, 360, ellipse[3], -1)


# 转换为灰度图
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

# 使用Canny边缘检测
edges = cv2.Canny(gray, 10, 150)

# 查找轮廓
_, contours, _ = cv2.findContours(edges, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)


# 在原图上绘制轮廓并输出面积和周长信息
for contour in contours:
    # 寻找最小包围三角形
    retval, triangle = cv2.minEnclosingTriangle(contour)

    # 绘制最小包围三角形
    cv2.polylines(image, [np.int32(triangle)], isClosed=True, color=(0, 255, 0), thickness=2)

    # 计算最小包围三角形的中心坐标
    center = np.mean(triangle, axis=0, dtype=np.int32)
    # 将 retval 的值显示在三角形的中心
    font = cv2.FONT_HERSHEY_SIMPLEX
    cv2.putText(image, f"Area: {int(retval)}", tuple(center[0]), font, 0.8, (0, 0, 255), 3, cv2.LINE_AA)
    cv2.putText(image, f"Area: {int(retval)}", tuple(center[0]), font, 0.8, (0, 255, 0), 2, cv2.LINE_AA)

# 显示结果
cv2.imshow('Contours', image)
cv2.waitKey(0)
cv2.destroyAllWindows()

Minimum Enclosing Triangle

最小包围圆形

最小包围圆形是将一组点拟合为一个最小的包围圆形,该圆形能够包含所有的离散点。

import cv2
import numpy as np
import random

# 创建一个空白的图像
width, height = 800, 800
image = np.zeros((height, width, 3), dtype=np.uint8)


# 定义绘制椭圆的函数
def draw_random_ellipse(img):
    # 生成随机椭圆的参数
    center = (random.randint(0, width - 1), random.randint(0, height - 1))
    axes = (random.randint(10, 100), random.randint(10, 100))
    angle = random.randint(0, 360)
    color = (random.randint(0, 255), random.randint(0, 255), random.randint(0, 255))

    # 绘制椭圆
    cv2.ellipse(img, center, axes, angle, 0, 360, color, -1)


# 定义检查椭圆是否重叠的函数
def check_overlap(new_ellipse, existing_ellipses):
    for existing_ellipse in existing_ellipses:
        # 获取椭圆的掩码
        new_mask = np.zeros_like(image, dtype=np.uint8)
        existing_mask = np.zeros_like(image, dtype=np.uint8)
        cv2.ellipse(new_mask, new_ellipse[0], new_ellipse[1], new_ellipse[2], 0, 360, (255, 255, 255), -1)
        cv2.ellipse(existing_mask, existing_ellipse[0], existing_ellipse[1], existing_ellipse[2], 0, 360,
                    (255, 255, 255), -1)

        # 检查是否有重叠的部分
        overlap = cv2.bitwise_and(new_mask, existing_mask)
        if np.sum(overlap) > 0:
            return True
    return False


# 生成不重叠的椭圆
num_ellipses = 15
ellipses = []
for _ in range(num_ellipses):
    while True:
        new_ellipse = (
            (random.randint(0, width - 1), random.randint(0, height - 1)),
            (random.randint(10, 100), random.randint(10, 100)),
            random.randint(0, 360),
            (random.randint(0, 255), random.randint(0, 255), random.randint(0, 255))
        )
        if not check_overlap(new_ellipse, ellipses):
            ellipses.append(new_ellipse)
            break

# 绘制不重叠的椭圆
for ellipse in ellipses:
    cv2.ellipse(image, ellipse[0], ellipse[1], ellipse[2], 0, 360, ellipse[3], -1)


# 转换为灰度图
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

# 使用Canny边缘检测
edges = cv2.Canny(gray, 10, 150)

# 查找轮廓
_, contours, _ = cv2.findContours(edges, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)


# 在原图上绘制轮廓并输出面积和周长信息
for contour in contours:
    # 寻找最小包围三角形
    (x, y), radius = cv2.minEnclosingCircle(contour)
    # 绘制一个圆
    cv2.circle(image, (int(x), int(y)), int(radius), (255, 255, 255), 2)



# 显示结果
cv2.imshow('Minimum Enclosing Circle', image)
cv2.waitKey(0)
cv2.destroyAllWindows()

Minimum Enclosing Circle

四、二维码检测

我们使用qrcode库生成并解码二维码
首先,检查两个库

pip install --upgrade opencv-python==3.4.4.19
pip install qrcode[pil]

qrcode库的使用方式,这里不做深入探讨,但是需要注意的是:生成的二维码是PIL(Python Imaging Library)图像对象。在后续的代码中,我们需要将PIL图像转换为OpenCV图像。

其次,使用cv2.QRCodeDetector()进行解码,这是OpenCV的二维码解码器。在解码过程中,获取了二维码的信息、顶点坐标和直线形式的二维码。确保理解这些输出的含义,以便根据需要进行处理。

codeinfo, points, straight_qrcode = qr_detector.detectAndDecode(img_read)
import cv2
import qrcode
import numpy as np

# 获取OpenCV版本信息
cv_version = cv2.__version__

# 将版本字符串转换为数字列表
version_numbers = [int(num) for num in cv_version.split('.')]

# 检查版本是否小于3.4.4
if version_numbers < [3, 4, 4]:
    print("OpenCV版本过低,请升级至3.4.4或更高版本。 pip install --upgrade opencv-python==3.4.4.19")

# 随机生成一个二维码
data = "Hello, QR Code!"
qr = qrcode.QRCode(
    version=1,
    error_correction=qrcode.constants.ERROR_CORRECT_L,
    box_size=10,
    border=4,
)
qr.add_data(data)
qr.make(fit=True)
img = qr.make_image(fill_color=( 0 , 0 , 0 ), back_color=( 255 , 255 , 255 ))

# 将PIL图像转换为OpenCV格式
img_np = np.array(img)
img_read = cv2.cvtColor(img_np, cv2.COLOR_RGB2BGR)

# 使用cv2.QRCodeDetector()进行解码
qr_detector = cv2.QRCodeDetector()
# 解码
codeinfo, points, straight_qrcode = qr_detector.detectAndDecode(img_read)

# 描绘轮廓
cv2.drawContours(img_read, [np.int32(points)], 0, (0, 0, 255), 2)
print("QR Code: %s" % codeinfo)
# 添加文字
cv2.putText(img_read, "QR Code:" + str(codeinfo), (10, 20), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 255, 0), 2)
cv2.imshow("QR Code", img_read)

cv2.waitKey(0)
cv2.destroyAllWindows()

QR Code


总结

首先,我们简单介绍了图像轮廓的概念,以及常用的轮廓检测算法。
接着,我们了解了轮廓检测函数,包括轮廓发现、轮廓面积、轮廓周长等功能的使用说明。代码实现部分提供了实际的操作指南,使其能够在实际项目中灵活运用所学知识。
然后,我们学习了轮廓的距,包括几何距、中心距和Hu距等,文章提供了更深层次的图像特征描述方法。
最后,通过点集拟合和二维码检测两个具体案例,我们展示了如何在实际应用中灵活运用轮廓检测技术,更好地理解其实际应用场景。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/173442.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

算法通关村第十二关-青铜挑战字符串

大家好我是苏麟 , 今天带来字符串专题 . 转换成小写字母 描述 : 给你一个字符串 s &#xff0c;将该字符串中的大写字母转换成相同的小写字母&#xff0c;返回新的字符串。 题目 : LeetCode 709.转换成小写字母 : 709. 转换成小写字母 分析 : 这个题可以先遍历整个字符串…

设计模式-访问者模式-笔记

Visitor模式 动机&#xff08;Morivation&#xff09; 在软件构建过程中&#xff0c;由于需求的变化&#xff0c;某些类层次结构中常常需要增加新的行为&#xff08;方法&#xff09;&#xff0c;如果直接在基类中做这样的更改&#xff0c;将会给子类带来很繁重的变更负担&am…

多目标应用:基于多目标粒子群优化算法MOPSO求解微电网多目标优化调度(MATLAB代码)

一、微网系统运行优化模型 微电网优化模型介绍&#xff1a; 微电网多目标优化调度模型简介_IT猿手的博客-CSDN博客 二、多目标粒子群优化算法MOPSO 多目标粒子群优化算法MOPSO简介&#xff1a; 三、多目标粒子群优化算法MOPSO求解微电网多目标优化调度 &#xff08;1&…

springcloud整合seata我踩过的坑

版本问题 seata 1.5和1.5之前的目录结构不同,使用docker修改的配置文件也不同 1.4的左右 1.5之后docker 挂载文件也不同 1.5之前是使用自己写的挂载registry docker run -d -p 8091:8091 -p 7091:7091 --network newlead --name seata-serve -e SEATA_IP192.168.249.132…

万宾科技智能井盖传感器的特性一览

在不断发展的智慧城市技术领域&#xff0c;科学技术的创新永无止境。智能井盖传感器是科学进步带来的高科技产品&#xff0c;为促进城市生命线并保障地上地下连接点安全提供保障。它就在我们脚下&#xff0c;正在悄然改变城市基础设施和公共服务。智能井盖传感器成为现代城市规…

python 实现蚁群算法(simpy带绘图)

这里使用了蚁群算法求解了旅行商问题&#xff0c;同时结合了simpy来绘图 选择下一个食物的函数为&#xff1a; probability[i] pheromone[self.now][self.not_to_foods[i]] ** pheromone_w (1 / distance[self.now][self.not_to_foods[i]]) ** distance_w 该条路概率权重该点…

解锁潜力:创建支持Actions接口调用的高级GPTs

如何创建带有Actions接口调用的GPTs 在本篇博客中&#xff0c;我们将介绍如何创建一个带有Actions接口调用的GPTs &#xff0c;以及如何进行配置和使用。我们将以 https://chat.openai.com/g/g-GMrQhe7ka-gptssearch 为例&#xff0c;演示整个过程。 Ps: 数据来源&#xff1a…

计算机毕业设计 基于SpringBoot的社区物资交易互助平台/系统的设计与实现 Java实战项目 附源码+文档+视频讲解

博主介绍&#xff1a;✌从事软件开发10年之余&#xff0c;专注于Java技术领域、Python人工智能及数据挖掘、小程序项目开发和Android项目开发等。CSDN、掘金、华为云、InfoQ、阿里云等平台优质作者✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精…

Win10 电源选项那选择“关闭显示器“为1分钟,1分钟后就锁屏了?怎么才能关闭显示器后不锁屏

环境&#xff1a; Win10专业版 问题描述&#xff1a; Win10 电源选项那选择"关闭显示器"为1分钟&#xff0c;1分钟后就锁屏了&#xff1f;怎么才能关闭显示器后不锁屏 解决方案&#xff1a; 方法一 更改注册表可以实现关闭显示器而不锁屏的效果。请按照以下步骤…

OpenAI 超 700 名员工联名逼宫董事会;ChatGPT 新功能“阅后即焚”丨 RTE 开发者日报 Vol.89

开发者朋友们大家好&#xff1a; 这里是 「RTE 开发者日报」 &#xff0c;每天和大家一起看新闻、聊八卦。我们的社区编辑团队会整理分享 RTE &#xff08;Real Time Engagement&#xff09; 领域内「有话题的 新闻 」、「有态度的 观点 」、「有意思的 数据 」、「有思考的 文…

JavaScript中的random小案例

前言 Math对象是JavaScript的内置对象&#xff0c;而random是Math对象属性 Math.random&#xff08;&#xff09;函数返回一个浮点数&#xff0c;伪随机数在范围从0 到小于1&#xff0c;也就是说&#xff0c;从 0&#xff08;包括 0&#xff09;往上&#xff0c;但是不包括 1&a…

利用逻辑过期解决缓存击穿问题

需求&#xff1a;修改根据id查询商铺的业务&#xff0c;基于逻辑过期方式来解决缓存击穿问题 思路分析&#xff1a;当用户开始查询redis时&#xff0c;判断是否命中&#xff0c;如果没有命中则直接返回空数据&#xff0c;不查询数据库&#xff0c;而一旦命中后&#xff0c;将v…

OpenGL_Learn14(光照贴图)

1. 漫反射贴图 在光照场景中&#xff0c;它通常叫做一个漫反射贴图(Diffuse Map)&#xff08;3D艺术家通常都这么叫它&#xff09;&#xff0c;它是一个表现了物体所有的漫反射颜色的纹理图像。 我们会将纹理储存为Material结构体中的一个sampler2D 。我们将之前定义的vec3漫反…

算法学习 day27

第二十七天 美化数组的最少删除数 2216. 美化数组的最少删除数 - 力扣&#xff08;LeetCode&#xff09; class Solution { public:int minDeletion(vector<int>& nums) {int len nums.size();if(len 0) return 0;int res 0,cur 0;for(int i 1;i < len;i)…

OpenCV入门9——目标识别(车辆统计)

文章目录 图像轮廓查找轮廓绘制轮廓轮廓的面积与周长多边形逼近与凸包外接矩形项目总览【车辆统计】视频加载【车辆统计】去背景【车辆统计】形态学处理【车辆统计】逻辑处理【车辆统计】显示信息【车辆统计】 图像轮廓 查找轮廓 # -*- coding: utf-8 -*- import cv2 import n…

Vue框架学习笔记——v-bind数据单向绑定和v-model数据双向绑定

文章目录 v-bind&#xff0c;数据单向绑定简写形态&#xff08;省略v-bind&#xff0c;只留冒号&#xff09;示例一&#xff08;将输入框数据改为&#xff1a;哈哈哈哈哈&#xff09;&#xff1a;实例二&#xff08;将Vue实例中的name改为字符串&#xff1a;"单向绑定&quo…

Redis 5 种基本数据类型详解

Redis 共有 5 种基本数据类型&#xff1a;String&#xff08;字符串&#xff09;、List&#xff08;列表&#xff09;、Set&#xff08;集合&#xff09;、Hash&#xff08;散列&#xff09;、Zset&#xff08;有序集合&#xff09;。 这 5 种数据类型是直接提供给用户使用的&…

1、基础入门——操作系统文件下载反弹SHELL防火墙绕过

名词解释 POC&#xff1a;验证漏洞存在的代码&#xff1b; EXP&#xff1a;利用漏洞的代码&#xff1b; payload&#xff1a;漏洞利用载荷&#xff0c; shellcode&#xff1a;漏洞代码&#xff0c; webshell&#xff1a;特指网站后门&#xff1b; 木马&#xff1a;强调控制…

多目标应用:基于非支配排序的鲸鱼优化算法NSWOA求解微电网多目标优化调度(MATLAB代码)

一、微网系统运行优化模型 微电网优化模型介绍&#xff1a; 微电网多目标优化调度模型简介_IT猿手的博客-CSDN博客 二、基于非支配排序的鲸鱼优化算法NSWOA 基于非支配排序的鲸鱼优化算法NSWOA简介&#xff1a; 三、基于非支配排序的鲸鱼优化算法NSWOA求解微电网多目标优化…

年薪30w项目经理都在用的6个项目管理软件

大家好&#xff0c;我是老原。又到了每月一次的好用工具推荐&#xff0c;不少粉丝都在搓手等待了。 要知道&#xff0c;实时掌握项目进度、把关项目质量、应对项目风险、协调资源…如果能好用的工具高效提升你的工作效率&#xff0c;对于领导来说&#xff0c;绝对是加分项。 …