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

news2024/9/29 9:19:04

文章目录

  • 前言
  • 一、轮廓检测
    • 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距等,文章提供了更深层次的图像特征描述方法。
最后,通过点集拟合和二维码检测两个具体案例,我们展示了如何在实际应用中灵活运用轮廓检测技术,更好地理解其实际应用场景。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

springcloud医院挂号预约系统源码

开发技术&#xff1a; jdk1.8&#xff0c;mysql5.7&#xff0c;nodejs&#xff0c;idea&#xff0c;vscode springcloud springboot mybatis vue elementui 功能介绍&#xff1a; 用户端&#xff1a; 登录注册 首页显示医生列表&#xff0c;医院简介&#xff0c;点击医生…

P1141 01迷宫(dfs+染色联通块)

染色联通块&#xff1a; 一个格联通的所有格 每个对应的最大可联通格子的个数均相同 分析&#xff1a; 1.只需要计算每个块里的元素个数 2.元素标记对应某个块 3.查找元素时&#xff1a; 由 &#xff08;1&#xff09;元素坐标-> &#xff08;2&#xff09;查找…

Qt程序的自定义安装卸载方案

前言 NSIS 是一个 Open Source 的 Windows 系统下安装程序制作程序&#xff1b; NSIS-UI-Plugin 是一个开源的NSIS UI插件&#xff1b; 0x0 环境搭建 https://www.cnblogs.com/NSIS/p/16581122.html https://github.com/sway913/NSIS-UI-Plugin 0x1 类图 0x2 二次开发 自定…

图形编辑器开发:自定义光标管理

大家好&#xff0c;我是前端西瓜哥。 今天来讲讲如何在图形编辑器中使用自定义光标&#xff0c;并对光标其进行管理。 编辑器 github 地址&#xff1a; https://github.com/F-star/suika 线上体验&#xff1a; https://blog.fstars.wang/app/suika/ 自定义光标的意义是什么&am…

JavaScript的学习之BOM和DOM,就这一篇就OK了!(超详细)

目录 Day28 JavaScript(2) 1、BOM对象 1.1 window对象 1.2 Location ( 地址栏)对象 1.3 本地存储对象 2、DOM对象(JS核心) 2.1 查找标签 2.2 绑定事件 2.3 操作标签 2.4 常用事件 Day28 JavaScript(2) 1、BOM对象 BOM:Broswer object model,即浏览器提供我们开发者…

ssh远程连接不了虚拟机ubuntu

直奔主题 1. 确保linux安装了ssh2.查看网络适配器是否启用3.连接成功 1. 确保linux安装了ssh sudo apt-get install openssh-server2.查看网络适配器是否启用 3.连接成功

“AI在未来”公益计划,亚马逊云科技将教育资源带到更多中西部学校

亚马逊云科技宣布携手中国光华科技基金会启动“AI在未来”公益计划2023至2024学年项目&#xff0c;预计本学年内在内蒙古、江西、湖南和广西四个省份开展该项目&#xff0c;并完成三年内为中西部地区一百所学校的一万名学生提供免费人工智能教育资源及实践机会的目标。 此外&am…

基于像素特征的kmeas聚类的图像分割方案

kmeans聚类代码 将像素进行聚类&#xff0c;得到每个像素的聚类标签&#xff0c;默认聚类簇数为3 def seg_kmeans(img,clusters3):img_flatimg.reshape((-1,3))# print(img_flat.shape)img_flatnp.float32(img_flat)criteria(cv.TERM_CRITERIA_MAX_ITERcv.TERM_CRITERIA_EPS,2…

《白帽子讲web安全》

第十四章 PHP安全 文件包含漏洞是“代码注入”的一种。“代码注入”这种攻击&#xff0c;其原理就是注入一段用户能控制的脚本或代码&#xff0c;并让服务器端执行。“代码注入”的典型代表就是文件包含&#xff08;File Inclusion&#xff09;。文件包含可能会出现在JSP、PHP…

如何有效的禁止Google Chrome自动更新?

禁止Chrome自动更新 1、背景2、操作步骤 1、背景 众所周知&#xff0c;当我们在使用Selenium进行Web自动化操作&#xff08;如爬虫&#xff09;时&#xff0c;一般会用到ChromeDriver。然而Driver的更新速度明显跟不上Chrome的自动更新。导致我们在使用Selenium进行一些操作时就…

新加坡服务器托管-金融企业的选择

新加坡作为一个亚洲金融中心&#xff0c;其优越的地理位置和先进的信息通信技术基础设施&#xff0c;使得其成为了众多金融机构企业选择服务器机房托管的理想地点。金融行业对于服务器的安全性和可靠性要求很高&#xff0c;而将服务器托管在新加坡有许多好处。 首先&#xff0c…

新零售数字化系统提供商怎么选择?2023十大收银系统排行榜-亿发

随着零售业务的日益繁荣和电子商务的迅猛发展&#xff0c;零售收银系统已成为各类商家提高效率、管理库存、提供更好服务的不可或缺的工具。然而&#xff0c;在众多的收银系统中&#xff0c;如何选择一款适合自己的&#xff0c;一直是许多商家头疼的问题。今天我们就来盘点一下…

java项目之木里风景文化管理平台(ssm+vue)

项目简介 木里风景文化管理平台实现了以下功能&#xff1a; 前台功能&#xff1a;用户进入系统可以实现首页&#xff0c;旅游公告&#xff0c;景区&#xff0c;景区商品&#xff0c;景区美食&#xff0c;旅游交通工具&#xff0c;红黑榜&#xff0c;个人中心&#xff0c;后台…

Spark---核心介绍

一、Spark核心 1、RDD 1&#xff09;、概念&#xff1a; RDD&#xff08;Resilient Distributed Datest&#xff09;&#xff0c;弹性分布式数据集。 2&#xff09;、RDD的五大特性&#xff1a; 1、RDD是由一系列的partition组成的 2、函数是作用在每一个partition(split…

SSM高考志愿辅助推荐系统-计算机毕业设计附源码21279

目 录 摘要 1 绪论 1.1 研究背景 1.2研究意义 1.3论文结构与章节安排 2 高考志愿辅助推荐系统分析 2.1 可行性分析 2.1.1 技术可行性分析 2.1.2 经济可行性分析 2.1.3 法律可行性分析 2.2 系统功能分析 2.2.1 功能性分析 2.2.2 非功能性分析 2.3 系统用例分析 2…

基于社交网络算法优化概率神经网络PNN的分类预测 - 附代码

基于社交网络算法优化概率神经网络PNN的分类预测 - 附代码 文章目录 基于社交网络算法优化概率神经网络PNN的分类预测 - 附代码1.PNN网络概述2.变压器故障诊街系统相关背景2.1 模型建立 3.基于社交网络优化的PNN网络5.测试结果6.参考文献7.Matlab代码 摘要&#xff1a;针对PNN神…

wsl安装ubuntu的问题点、处理及连接

WSL安装Ubuntu的参考链接 (41条消息) wsl报错&#xff1a;WslRegisterDistribution failed with error: 0x800701bc_yzpyzp的博客-CSDN博客_0x800701bc wsl (41条消息) 使用Ubuntu安装软件出现Unable to locate package错误解决办法_大灰狼学编程的博客-CSDN博客 手把手教你…

excel自定义函数之汉字转为拼音及大写字母

使用场景&#xff1a;想把姓名转化为拼音格式&#xff0c;然后拼音转为大写字母 至于怎么在excel里面自定义函数&#xff0c;自行百度都有&#xff0c;这里简单截图看看。 步骤&#xff1a;文件》选项》自定义功能区》 打开编辑窗口 把下面这段代码粘贴就能实现汉字转化为拼音…

医疗器械维修售后技术培训与支持的重要性

医疗器械维修售后技术培训与支持的重要性 随着我国医疗器械产业的的高速发展、医疗器械企业的崛起,大量创新医疗器械产品进入医疗机构。但医疗设备在使用和维护过程中,暴露出许多问题和不足,如部分设备故障率较高、临床工程培训不足、售后服务模式整体比较落后等,这影响了医疗…

【JUC】十、ForkJoin

文章目录 1、分支合并框架2、案例3、ForkJoinTask4、工作窃取算法5、ForkJoinPool 一个个任务执行在一个个线程上&#xff0c;倘若某一个任务耗时很久&#xff0c;期间其他线程都无事可做&#xff0c;显然没有利用好多核CPU这一计算机资源&#xff0c;因此&#xff0c;出现了&q…