OpenCV 图像处理四(轮廓查找、画线、矩特征、轮廓)

图像处理四(轮廓查找)

一、前言

在OpenCV中,边缘检测和轮廓查找是两个不同的图像处理任务,它们有不同的目标和应用。

1.1 边缘检测和轮廓查找的区别是什么

1.1.1 边缘检测:

  • 定义: 边缘检测是指寻找图像中灰度级别变化明显的地方,即图像中物体之间的界限。这些变化通常表示图像中的边缘或轮廓。
  • 方法: 常用的边缘检测算法包括Sobel、Canny、Laplacian等。这些算法通过在图像中寻找灰度级别变化最大的地方来标记边缘。
import cv2

# 读取图像
image = cv2.imread('image.jpg', cv2.IMREAD_GRAYSCALE)

# 使用Canny边缘检测
edges = cv2.Canny(image, 100, 200)

# 显示边缘检测结果
cv2.imshow('Edges', edges)
cv2.waitKey(0)
cv2.destroyAllWindows()

1.1.2 轮廓查找:

  • 定义: 轮廓是图像中连续的边界线,表示相同颜色或灰度的区域的边界。轮廓查找的目标是找到图像中对象的外形。
  • 方法: OpenCV提供了findContours函数来查找图像中的轮廓。这个函数返回轮廓的坐标点,然后可以通过绘制这些坐标点来可视化轮廓。
import cv2

# 读取图像
image = cv2.imread('image.jpg', cv2.IMREAD_GRAYSCALE)

# 使用Canny边缘检测
edges = cv2.Canny(image, 100, 200)

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

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

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

总结:
边缘检测强调的是图像中灰度级别的变化,而轮廓查找强调的是图像中相邻区域的边界。在实际应用中,这两种技术通常可以结合使用,先进行边缘检测,然后通过轮廓查找来识别和分析图像中的对象。

1.2 边缘检测和轮廓查找在图像处理中的关系和流程

边缘检测用于发现图像中灰度变化明显的区域,但这些边缘通常是不连续的。为了构成完整的对象轮廓,需要将这些边缘连接在一起。

为了进行轮廓查找,首先需要将图像转换为二值图像,其中对象是白色,背景是黑色。这可以通过预先进行阈值分割或者边缘检测处理来实现。

轮廓查找通常会修改原始图像,因此为了保留原始图像的完整性,我们通常会在原始图像的一份拷贝上进行操作。

OpenCV中,默认情况下假设对象是白色,背景是黑色。因此,在进行轮廓查找时,确保对象是白色,背景是黑色,以确保正确识别图像中的对象轮廓。

二、查找并绘制轮廓

cv2.findContours()
cv2.drawContours()

在OpenCV中,cv2.findContours() 是用于查找图像轮廓的函数,
而 cv2.drawContours() 则是用于将查找到的轮廓绘制到图像上的函数。

2.1 cv2.findContours():

  • 这个函数用于在二值图像中查找对象的轮廓。
  • 返回轮廓的坐标点列表和层次结构(hierarchy)信息。
import cv2

# 读取图像并转换为灰度图
image = cv2.imread('image.jpg')
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

# 进行阈值分割,得到二值图像
_, thresh = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)

# 查找轮廓
contours, hierarchy = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

# contours 包含了所有轮廓的坐标点
# hierarchy 包含了轮廓的层次结构信息

2.1.1 详细介绍:

cv2.findContours() 是 OpenCV 中用于查找图像轮廓的函数。它的基本用法如下:

image, contours, hierarchy = cv2.findContours(image, mode, method)

其中各参数的含义如下:

  • image: 输入的二值图像(通常是经过阈值处理的图像)。要确保输入图像是单通道的(灰度图像)且为二值图像,可以使用 cv2.cvtColor()cv2.threshold() 进行转换和阈值处理。

  • mode: 轮廓检索模式。指定轮廓的检索模式,有以下几个可选值:

    • cv2.RETR_EXTERNAL:只检索最外层的轮廓。
    • cv2.RETR_LIST:检索所有的轮廓,并将其保存到列表中。
    • cv2.RETR_CCOMP:检索所有轮廓,并将其组织为两层的层次结构(目前不常用)。

    【建立两个等级的轮廓,上面的一层为外边界,里面的一层为内孔的边界信息。如果内孔内还有一个连通物体,这个物体的边界也在顶层。】

    • cv2.RETR_TREE:检索所有轮廓,并重构轮廓之间的完整层次结构。

    建立一个等级树结构的轮廓。

  • method: 轮廓逼近方法。指定轮廓的逼近方法,有以下几个可选值

    • cv2.CHAIN_APPROX_NONE:保存所有的轮廓点。

    存储所有的轮廓点,相邻的两个点的像素位置差不超过1,max (abs (x1-x2) , abs (y2-y1) ) ==1

    • cv2.CHAIN_APPROX_SIMPLE:压缩水平、垂直和对角方向,只保留端点。

    只保留该方向的终点坐标,例如一个矩形轮廓只需4个点来保存轮廓信息[节约空间]

    cv2.CHAIN_APPROX_NONE 存储的轮廓,保存
    了轮廓中的每一个点;右图是使用参数值 cv2.CHAIN_APPROX_SIMPLE 存储的轮廓,仅仅保
    存了边界上的四个点。
    在这里插入图片描述

    • cv2.CHAIN_APPROX_TC89_L1:使用 Teh-Chin 链逼近算法。
    • cv2.CHAIN_APPROX_TC89_KCOS:使用 Teh-Chin 链逼近算法。

函数返回三个值:

  • image: 输入图像,通常不会改变。
  • contours: 包含轮廓坐标的列表。每个轮廓由一系列坐标点表示。
  • hierarchy: 轮廓的层次结构信息,用于表示轮廓之间的嵌套关系。

示例代码:

import cv2

# 读取图像并转换为灰度图
image = cv2.imread('image.jpg')
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

# 进行阈值分割,得到二值图像
_, thresh = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)

# 查找轮廓
contours, hierarchy = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

# contours 包含了所有轮廓的坐标点
# hierarchy 包含了轮廓的层次结构信息

这个函数在图像处理中常用于对象检测、形状分析等任务。

2.1.2 注意事项:

遇到的错误 “not enough values to unpack (expected 3, got 2)” 可能是因为在OpenCV 4.x中,cv.findContours 函数只返回两个值:轮廓(contours)和层次结构(hierarchy)。在你的代码中,你试图解包三个值 (img, contours, hierachy),这导致了错误

在OpenCV的3.x版本和4.x版本之间,主要的变化之一是cv2.findContours()函数的返回值。在3.x版本中,该函数返回三个值,而在4.x版本中,只返回两个值。下面是一个简要对比:

OpenCV 3.x:

# OpenCV 3.x
img, contours, hierarchy = cv2.findContours(image, mode, method)
  • img: 原始图像
  • contours: 轮廓坐标的列表
  • hierarchy: 轮廓的层次结构信息

OpenCV 4.x:

# OpenCV 4.x
contours, hierarchy = cv2.findContours(image, mode, method)
  • contours: 轮廓坐标的列表
  • hierarchy: 轮廓的层次结构信息

如上所示,主要的变化是在4.x版本中去除了原始图像的返回,使得函数的返回结果更加简洁。如果你从3.x版本迁移到4.x版本,需要注意修改相关代码以适应新的函数返回形式。

2.2 cv2.drawContours():

  • 这个函数用于将查找到的轮廓绘制到图像上。
  • 可以选择绘制所有轮廓或者仅绘制特定的轮廓。
# 绘制所有轮廓
cv2.drawContours(image, contours, -1, (0, 255, 0), 2)

# 显示绘制轮廓后的图像
cv2.imshow('Contours', image)
cv2.waitKey(0)
cv2.destroyAllWindows()

这两个函数通常一起使用,cv2.findContours() 用于获取轮廓信息,然后 cv2.drawContours() 用于将轮廓绘制到图像上,以便进一步分析或可视化。

2.2.1 详细介绍:

cv2.drawContours() 是 OpenCV 中用于在图像上绘制轮廓的函数。它的基本用法如下:

result = cv2.drawContours(image, contours, contourIdx, color, thickness)

其中各参数的含义如下:

  • image: 要绘制轮廓的图像。

  • contours: 包含轮廓坐标的列表,通常是由 cv2.findContours() 函数返回的轮廓。

  • contourIdx: 要绘制的轮廓在 contours 列表中的索引。如果为负数(默认值为 -1),则绘制所有的轮廓。

  • color: 绘制轮廓的颜色,通常是一个包含三个整数值的元组,表示BGR颜色。

  • thickness: 绘制轮廓的线条厚度。如果为负数,表示填充轮廓。

函数返回一个新的图像,包含了绘制了轮廓的结果。

示例代码:

import cv2
import numpy as np

# 创建一张空白图像
image = np.zeros((300, 300, 3), dtype=np.uint8)

# 创建一个包含轮廓坐标的列表
contours = np.array([[[50, 50]], [[150, 50]], [[100, 150]]], dtype=np.int32)

# 绘制轮廓
result = cv2.drawContours(image, [contours], -1, (0, 255, 0), 2)

# 显示绘制轮廓后的图像
cv2.imshow('Contours', result)
cv2.waitKey(0)
cv2.destroyAllWindows()

这个函数在图像处理中用于可视化检测到的轮廓,使得我们能够直观地观察到图像中对象的形状和边界。

2.3 实际运用

import cv2 as cv
import matplotlib.pyplot as plt

# 读取图像
image = cv.imread('img8/ss.jpg')

# 转换为灰度图
gray_image = cv.cvtColor(image, cv.COLOR_BGR2GRAY)

# 阈值处理得到二值图像
ret, binary = cv.threshold(gray_image, 127, 255, cv.THRESH_BINARY)

# 寻找轮廓
contours, hierarchy = cv.findContours(binary, cv.RETR_TREE, cv.CHAIN_APPROX_NONE)

# 在原始图像的副本上绘制轮廓
image_with_contours = image.copy()
cv.drawContours(image_with_contours, contours, -1, (122, 55, 215), 10)

# 使用 matplotlib 显示原图和带有轮廓的图像
plt.figure(figsize=(12, 6))

# 显示原图
plt.subplot(1, 2, 1)
plt.title('Original Image')
plt.imshow(cv.cvtColor(image, cv.COLOR_BGR2RGB))
plt.axis('off')

# 显示带有轮廓的图像
plt.subplot(1, 2, 2)
plt.title('Image with Contours')
plt.imshow(cv.cvtColor(image_with_contours, cv.COLOR_BGR2RGB))
plt.axis('off')

plt.show()

在这里插入图片描述

2.4 标记记数,再说先前函数参数

import cv2 as cv
import matplotlib.pyplot as plt

image = cv.imread('img8/ss.jpg')
gray_image = cv.cvtColor(image, cv.COLOR_BGR2GRAY)

ret, binary = cv.threshold(gray_image, 127, 255, cv.THRESH_BINARY)

# 寻找轮廓
contours, hierarchy = cv.findContours(binary, cv.RETR_TREE, cv.CHAIN_APPROX_SIMPLE)

# 在原始图像的副本上绘制轮廓并标注序号
image_with_contours = image.copy()
for i, contour in enumerate(contours):
    cv.drawContours(image_with_contours, [contour], -1, (122, 55, 215), 2)
    # 标注轮廓序号
    cv.putText(image_with_contours, str(i+1), tuple(contour[0][0]), cv.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 2)

# 使用 matplotlib 显示结果
plt.subplot(121), plt.imshow(cv.cvtColor(image, cv.COLOR_BGR2RGB)), plt.title('Original Image')
plt.subplot(122), plt.imshow(cv.cvtColor(image_with_contours, cv.COLOR_BGR2RGB)), plt.title('Image with Contours')
plt.show()

在这里插入图片描述

2.4.1 分析代码的走向:

  1. cv.imread('img8/ss.jpg'): 从文件中读取一张图像(文件路径为 'img8/ss.jpg')。

  2. cv.cvtColor(image, cv.COLOR_BGR2GRAY): 将彩色图像转换为灰度图像。

  3. cv.threshold(gray_image, 127, 255, cv.THRESH_BINARY): 对灰度图像进行阈值处理,得到二值图像。

  4. cv.findContours(binary, cv.RETR_TREE, cv.CHAIN_APPROX_SIMPLE): 寻找二值图像中的轮廓,并返回轮廓的坐标和层次结构。

  5. image_with_contours = image.copy(): 复制原始图像,用于在其上绘制轮廓。

  6. for i, contour in enumerate(contours):: 对于每个轮廓,使用enumerate获取轮廓的索引和轮廓本身。

    a. cv.drawContours(image_with_contours, [contour], -1, (122, 55, 215), 2): 在图像副本上绘制轮廓,颜色为(122, 55, 215),线条粗细为2。

    b. cv.putText(image_with_contours, str(i+1), tuple(contour[0][0]), cv.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 2): 标注轮廓序号,以文本形式显示在图像上,颜色为(0, 255, 0)(绿色),字体大小为0.5,线条粗细为2。

  7. plt.subplot(121), plt.imshow(cv.cvtColor(image, cv.COLOR_BGR2RGB)), plt.title('Original Image'): 创建Matplotlib子图,显示原始图像。

  8. plt.subplot(122), plt.imshow(cv.cvtColor(image_with_contours, cv.COLOR_BGR2RGB)), plt.title('Image with Contours'): 创建Matplotlib子图,显示带有轮廓的图像。

  9. plt.show(): 显示Matplotlib绘制的原始图像和带有轮廓的图像。

总体而言,这段代码的目的是可视化图像处理中的轮廓查找过程,通过标注轮廓序号,使用户能够更清晰地理解图像中检测到的对象的形状和位置。

2.4.2 在给轮廓标注序号的过程中,使用了OpenCV的 cv.putText() 函数。这个函数用于在图像上绘制文本,具体的用法如下:

cv.putText(img, text, org, fontFace, fontScale, color, thickness, lineType, bottomLeftOrigin)

各参数含义如下:

  • img: 需要绘制文本的图像。
  • text: 要绘制的文本内容。
  • org: 文本的起始坐标,即文本左下角的坐标。
  • fontFace: 字体类型,例如 cv.FONT_HERSHEY_SIMPLEX
  • fontScale: 字体缩放因子。
  • color: 文本的颜色。
  • thickness: 文本线条的粗细。
  • lineType: 线条类型。
  • bottomLeftOrigin: 如果为真,文本起始坐标将被认为是左下角;如果为假(默认),则为左上角。

在给轮廓标注序号的代码中,这个函数的具体应用如下:

# 标注轮廓序号
cv.putText(image_with_contours, str(i+1), tuple(contour[0][0]), cv.FONT_HERSHEY_SIMPLEX, 0.5, (255, 0, 0), 2)

这一行代码的作用是将轮廓的序号以文本的形式标注在原始图像的副本上。str(i+1) 将轮廓的索引加1转换为字符串,tuple(contour[0][0]) 是轮廓的第一个点的坐标,cv.FONT_HERSHEY_SIMPLEX 是字体类型,0.5 是字体缩放因子,(255, 0, 0) 是文本颜色(蓝色),2 是文本线条的粗细。这样,每个轮廓的序号就被标注在了图像上。

(1)findContours函数的contours参数

这个呢,是标记的时候字段+1了,所以和下标错位1的

print (type(contours))
print (len(contours[0])) 
print (len(contours[1])) 
print (len(contours[2])) 
print (len(contours))

print (contours[0])
print(contours[1].shape)
print(contours[2].shape)

在这里插入图片描述

(2)findContours函数的hierarchy参数
[Next,Previous,First_Child,Parent]

 Next:后一个轮廓的索引编号。
 Previous:前一个轮廓的索引编号。
 First_Child:第 1 个子轮廓的索引编号。
 Parent:父轮廓的索引编号。
如果上述各个参数所对应的关系为空时,也就是没有对应的关系时,则将该参数所对应的
值设为“-1”。
在这里插入图片描述

2.4.3 分开显示

import cv2
import numpy as np
import matplotlib.pyplot as plt

o = cv2.imread('img8/ss.jpg')
plt.imshow(cv2.cvtColor(o, cv2.COLOR_BGR2RGB))
plt.title('Original Image')
plt.show()

gray = cv2.cvtColor(o, cv2.COLOR_BGR2GRAY)
ret, binary = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)
contours, hierarchy = cv2.findContours(binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

n = len(contours)
contoursImg = []

for i in range(n):
    temp = np.zeros(o.shape, np.uint8)
    contoursImg.append(temp)
    contoursImg[i] = cv2.drawContours(contoursImg[i], contours, i, (255, 255, 255), 5)

    # Display each contour using plt
    plt.imshow(cv2.cvtColor(contoursImg[i], cv2.COLOR_BGR2RGB))
    plt.title(f'Contour {i + 1}')
    plt.show()

在这里插入图片描述

逐步解释:

以下是对上述代码每一步的分析:

  1. 读取原始图像:

    o = cv2.imread('contours.bmp')
    plt.imshow(cv2.cvtColor(o, cv2.COLOR_BGR2RGB))
    plt.title('Original Image')
    plt.show()
    
    • 使用 OpenCV 读取名为 ‘contours.bmp’ 的图像。
    • 将图像转换为 RGB 格式,并使用 Matplotlib 显示原始图像。
  2. 灰度转换和二值化:

    gray = cv2.cvtColor(o, cv2.COLOR_BGR2GRAY)
    ret, binary = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)
    
    • 将原始图像转换为灰度图。
    • 对灰度图进行二值化处理。
  3. 查找轮廓:

    image, contours, hierarchy = cv2.findContours(binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    
    • 使用 cv2.findContours() 函数查找二值图像中的轮廓。
    • cv2.RETR_EXTERNAL 参数表示只检测外部轮廓。
    • cv2.CHAIN_APPROX_SIMPLE 参数表示使用简化的轮廓表示。
  4. 绘制并显示每个轮廓:

    n = len(contours)
    contoursImg = []
    
    for i in range(n):
        temp = np.zeros(o.shape, np.uint8)
        contoursImg.append(temp)
        contoursImg[i] = cv2.drawContours(contoursImg[i], contours, i, (255, 255, 255), 5)
    
        # Display each contour using plt
        plt.imshow(cv2.cvtColor(contoursImg[i], cv2.COLOR_BGR2RGB))
        plt.title(f'Contour {i + 1}')
        plt.show()
    
    • 遍历所有检测到的轮廓,每个轮廓都被绘制在 contoursImg 的相应元素中。
    • 使用 Matplotlib 显示每个绘制了轮廓的图像,标题显示轮廓的序号。

总体而言,这段代码的目的是在原始图像中找到轮廓,然后将每个轮廓在图像上绘制出来并使用 Matplotlib 逐个显示,以便用户更好地理解轮廓检测的结果。

三、矩特征:用于描述图像的形状、轮廓和其他特征的数学描述符

矩可以得到图像的形状和结构信息,可以用来识别图像中的对象、测量物体的大小和姿态等。

矩的能力包括对图像的特征提取、形状匹配、边缘检测等,可以帮助计算机视觉系统理解和处理图像信息。

3.1 cv2.moments():该函数计算图像的矩

函数原型为:

cv2.moments(array[, binaryImage])
请注意,要正确使用`cv2.moments()`函数,
需要先加载图像并将其转换为灰度图像

参数

  • array:这个参数可以是一个点集(contour),也可以是灰度图像或二值图像。当array是一个点集时,函数会将这些点看作轮廓的顶点,将整个点集作为一个轮廓,而不是将它们视为独立点

  • binaryImage:可选参数,默认为False。如果设置为Truearray内的所有非零值都会被处理为1,这样函数会将其视为二值图像进行处理。

返回值
字典对象,包含了图像的矩特征。

(1)空间矩
零阶矩:m00
一阶矩:m10, m01
二阶矩:m20, m11, m02
三阶矩:m30, m21, m12, m03
(2)中心矩
二阶中心矩:mu20, mu11, mu02
三阶中心矩:mu30, mu21, mu12,mu03
(3)归一化中心矩
二阶 Hu 矩:nu20, nu11, nu02
三阶 Hu 矩:nu30, nu21, nu12, nu03

GPT
在这里插入图片描述

Demo

import cv2
import numpy as np

# 读取图片
image = cv2.imread("img/img.png")
cv2.imshow("Original Image", image)

# 形态学处理
kernel = np.ones((14, 14), np.uint8)
morphology_result = cv2.morphologyEx(image, cv2.MORPH_OPEN, kernel)
cv2.imshow("Morphology Result", morphology_result)

# 灰度转换
gray_image = cv2.cvtColor(morphology_result, cv2.COLOR_BGR2GRAY)

# 二值化
_, binary_image = cv2.threshold(gray_image, 100, 255, cv2.THRESH_OTSU)
cv2.imshow("Binary Image", binary_image)

# 寻找轮廓
contours, _ = cv2.findContours(binary_image, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)

n = len(contours)
contoursImg = []
for i in range(n):
    temp = np.zeros(image.shape, np.uint8)
    contoursImg.append(temp)
    contoursImg[i] = cv2.drawContours(contoursImg[i], contours, i, 255, 3)
    cv2.imshow("contours[" + str(i) + "]", contoursImg[i])
print("观察各个轮廓的矩(moments):")
for i in range(n):
    print("轮廓" + str(i) + "的矩:\n", cv2.moments(contours[i]))
print("观察各个轮廓的面积:")
for i in range(n):
    print("轮廓" + str(i) + "的面积:%d" % cv2.moments(contours[i])['m00'])
cv2.waitKey(0)
cv2.destroyAllWindows()

在这里插入图片描述

在这里插入图片描述

3.2 cv2.contourArea()计算图像中对象的大小

函数原型为:

cv2.contourArea(contour [, oriented] ))

参数:

  • contour: 轮廓对象,是一个包含轮廓点坐标的数组。

  • oriented (可选): 一个布尔值,指定是否计算有方向的面积。默认为 False,表示计算无方向的面积。如果设置为 True,则计算有方向的面积。

返回值:

该函数返回轮廓的面积,单位为像素。如果计算有方向的面积,返回的值可能为负数。

示例:

import cv2
import numpy as np

# 创建一张图像并找到轮廓
image = np.zeros((100, 100), dtype=np.uint8)
cv2.rectangle(image, (20, 20), (80, 80), 255, -1)
contours, _ = cv2.findContours(image, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

# 计算轮廓的面积
area = cv2.contourArea(contours[0])

print("Contour Area:", area)

在这个例子中,我们创建了一张100x100的黑色图像,在其中绘制了一个白色矩形。然后使用cv2.findContours找到轮廓,最后使用cv2.contourArea计算轮廓的面积。

Demo

import cv2

# 读取图像并灰度化
import matplotlib.pyplot as plt

image = cv2.imread('img/S1.jpg')
gray_image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

# 轮廓提取
contours, hierarchy = cv2.findContours(gray_image, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

# 检查是否找到轮廓
if len(contours) > 0:
    # 计算m00
    m00 = cv2.moments(contours[0])['m00']
    print("m00: ", m00)

    # 计算contourArea
    area = cv2.contourArea(contours[0])
    print("contourArea: ", area)
else:
    print("No contours found.")
plt.imshow(gray_image, cmap='gray')

在这里插入图片描述

3.3 cv2.arcLength():这个函数计算给定轮廓的弧长。弧长用于度量轮廓的周长或曲线的长度。

cv2.arcLength(contour, closed)

这个函数是OpenCV库中的一个函数,用于计算给定轮廓的弧长。弧长是指曲线的长度,可以用于测量轮廓的周长或曲线的总长度。

参数

  • contour: 这是输入的轮廓参数,通常是一个由点组成的列表或数组,表示图像中的对象的边界。
  • closed: 一个布尔值,指定轮廓是否是封闭的。如果为True,函数会假设轮廓是封闭的,计算封闭曲线的周长;如果为False,函数将计算非封闭曲线的长度。

返回值

  • retval: 弧长或曲线的长度。

示例

import cv2
import numpy as np

# 创建一个简单的闭合轮廓
contour = np.array([[0, 0], [0, 1], [1, 1], [1, 0]], dtype=np.float32)
closed = True

# 计算轮廓的弧长
arc_length = cv2.arcLength(contour, closed)

print("Arc Length:", arc_length)

Demo

import numpy as np
import cv2
import matplotlib.pyplot as plt
from matplotlib.collections import LineCollection

# 创建图像并在中心设置一个3x3的方块
image = np.zeros((5,5), dtype=np.uint8)
image[2:5, 2:5] = 1

# 找到轮廓
contours, _ = cv2.findContours(image, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)

# 假设只有一个轮廓
contour = contours[0]

# 轮廓点的数量
num_points = contour.shape[0]

# 创建一个线段集合,用于绘制不同颜色的边
lines = []
colors = ['r', 'g', 'b', 'c']  # 红、绿、蓝、青(这里只用于示例,实际情况下可能需要更多颜色或循环使用)

# 遍历轮廓的边
for i in range(num_points):
    # 获取当前点和下一个点(或第一个点,如果我们在最后一个点上)
    point1 = tuple(contour[i][0])
    point2 = tuple(contour[(i + 1) % num_points][0])

    # 添加线段到集合中
    lines.append([point1, point2])

# 创建线段集合
lc = LineCollection(lines, colors=colors, linewidths=2)

# 使用matplotlib显示图像
fig, ax = plt.subplots()

# 添加线段集合到图像上
ax.add_collection(lc)

# 设置坐标轴限制
ax.set_xlim(0, 9)
ax.set_ylim(0, 9)

# 关闭坐标轴
ax.axis('off')

# 显示图像
plt.show()

# 注意:这里我们假设了颜色和线段数量的匹配,
# 在实际应用中,您可能需要处理颜色和线段数量的对应关系。

# 遍历每个轮廓
for contour in contours:
    # 轮廓是一个点的数组,我们需要遍历这些点来计算相邻点之间的距离
    # 注意:轮廓是闭合的,所以最后一个点和第一个点是相连的
    num_points = contour.shape[0]
    for i in range(num_points):
        # 获取当前点和下一个点(或第一个点,如果我们在最后一个点上)
        point1 = contour[i]
        point2 = contour[(i + 1) % num_points]

        # 计算两点之间的距离(使用欧几里得距离)
        edge_length = np.linalg.norm(point1 - point2)

        # 输出边的长度
        print(f"Edge length {i + 1}: {edge_length}")

# 计算所有轮廓的总弧长
total_arc_length = 0
for contour in contours:
    arc_length = cv2.arcLength(contour, True)
    total_arc_length += arc_length

print("Total Arc Length:", total_arc_length)

在这里插入图片描述
在这里插入图片描述

import cv2
import numpy as np

o = cv2.imread('img/S1.jpg')
if o is None:
    print("Error loading image!")
    exit()

cv2.imshow("original", o)

# 轮廓
gray = cv2.cvtColor(o, cv2.COLOR_BGR2GRAY)
ret, binary = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)

# 注意:根据OpenCV版本,findContours的返回值可能会有所不同
contours, hierarchy = cv2.findContours(binary, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)

# --------------计算各轮廓的长度之和、平均长度--------------------
n = len(contours)  # 获取轮廓的个数
cntLen = [cv2.arcLength(contours[i], True) for i in range(n)]
cntLenSum = np.sum(cntLen)  # 各轮廓的长度之和
cntLenAvr = cntLenSum / n  # 轮廓长度的平均值

print("轮廓的总长度为:%f" % cntLenSum)
print("轮廓的平均长度为:%f" % cntLenAvr)

# --------------显示长度超过平均值的轮廓--------------------
result = np.zeros_like(o)
for i in range(n):
    if cv2.arcLength(contours[i], True) > cntLenAvr:
        cv2.drawContours(result, contours, i, (255, 255, 255), 3)

cv2.imshow("Contours above average", result)
cv2.waitKey()
cv2.destroyAllWindows()

在这里插入图片描述
在这里插入图片描述

四、Hu矩

Hu矩是通过对图像的几何矩进行归一化和线性组合得到的,主要用于图像的旋转、缩放和平移不变性的描述

4.1 Hu矩函数 cv2.HuMoments()

该函数使用 cv2.moments()函数的返回值作为参数,返回 7 个 Hu 矩值。

二阶 Hu 矩:eta20, eta11, eta02
 三阶 Hu 矩:eta30, eta21, eta12, eta03
在这里插入图片描述

4.1.1 验证hu1 = eta20 + eta02

import cv2 as cv
import numpy as np
import matplotlib.pyplot as plt

img = cv.imread('img/S1.jpg')
gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
HuM=cv.HuMoments(cv.moments(gray))
print("cv.monents:\n",cv.moments(gray))

print("\nHuMoments:\n",HuM)
print("HuM1[0]=",HuM[0])
x=cv.moments(gray)['nu20']+cv.moments(gray)['nu02']
print(x)
print(x-HuM[0])

plt.imshow()

在这里插入图片描述

4.2 比较Hu矩差异:形状匹配

cv2.matchShapes()是OpenCV中的一个函数,用于计算两个形状之间的相似性或匹配度。它通过计算形状的Hu矩来比较两个形状的差异程度。

retval = cv2.matchShapes(contour1, contour2, method, parameter)

其中:

  • contour1contour2分别是两个轮廓(即形状)或者灰度图像。
  • method指定了计算相似性的方法,可以是以下值之一:
    • cv2.CONTOURS_MATCH_I1:使用I1方法进行匹配。
    • cv2.CONTOURS_MATCH_I2:使用I2方法进行匹配。
    • cv2.CONTOURS_MATCH_I3:使用I3方法进行匹配。
  • parameter是可选参数,用于控制匹配精度。

ℎ𝑖𝐴和ℎ𝑖𝐵分别是对象 A 和对象 B 的 Hu 矩。
在这里插入图片描述

返回值retval表示两个形状之间的匹配程度。值越小表示形状越相似,值越大表示形状越不相似。

请注意,cv2.matchShapes()需要传入两个轮廓作为参数,因此在使用之前,你需要先获得两个轮廓。可以使用OpenCV的findContours()函数来获取图像中的轮廓。

import cv2
# 读取图像
image = cv2.imread('img/test_img.jpg',33)
# 图像旋转180度
rows, cols = image.shape[:2]
M = cv2.getRotationMatrix2D((cols/2, rows/2), 180, 1)
rotated_image = cv2.warpAffine(image, M, (cols, rows))
# 转换为灰度图像
gray_image = cv2.cvtColor(rotated_image, cv2.COLOR_BGR2GRAY)
# 二值化图像(可选)
# ret, binary_image = cv2.threshold(gray_image, 127, 255, cv2.THRESH_BINARY)
# 进行形状匹配
contours, _ = cv2.findContours(gray_image, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
if len(contours) > 0:
    contour = max(contours, key=cv2.contourArea)
    shape_similarity = cv2.matchShapes(contour, contour, cv2.CONTOURS_MATCH_I2, 0.0)
    print('形状相似度:', shape_similarity)
cv2.imshow("orgin",image)
cv2.imshow("rotated",rotated_image)
cv2.waitKey()
cv2.destroyAllWindows()

在这里插入图片描述

4.2.1 矩形包围cv2.boundingRect( array ): b用于计算给定轮廓或二进制图像的边界矩形。

参数:
array:可以是OpenCV中的轮廓对象,也可以是二值化的图像。轮廓对象是用于表示图像中物体边界的连续曲线,可以通过OpenCV的函数(如cv2.findContours())获得。
返回值:
(x, y, w, h):一个四元组,分别表示边界矩形的左上角点的x坐标和y坐标,以及矩形的宽度和高度。
以下是函数的示例用法:

import cv2
# 读取图像并进行二值化处理
image = cv2.imread('image.jpg')
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
_, binary = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)
# 寻找图像的轮廓
contours, _ = cv2.findContours(binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
# 遍历轮廓,并计算边界矩形
for contour in contours:
    x, y, w, h = cv2.boundingRect(contour)
    cv2.rectangle(image, (x, y), (x+w, y+h), (0, 255, 0), 2)
# 显示结果图像
cv2.imshow('Bounding Rectangle', image)
cv2.waitKey(0)
cv2.destroyAllWindows()

下面演示六张
(1)
在这里插入图片描述
(2)
在这里插入图片描述
(3)
在这里插入图片描述
(4)
在这里插入图片描述
(5)
在这里插入图片描述
(6)
在这里插入图片描述

上述代码读取一张图像,将其转换为灰度图像,然后进行二值化处理。接着使用cv2.findContours()函数找到图像中的轮廓。最后,利用cv2.boundingRect()函数计算每个轮廓的边界矩形,并在原图像上绘制矩形。

五、轮廓拟合

5.1 最小包围矩形框

(1) 2D 点集的最小面积的旋转矩形:cv2.minAreaRect()

在这里插入图片描述
参数:

points:需要计算最小包围矩形的轮廓点集合。可以通过cv2.findContours()等函数获取。

返回值:

retval:最小包围矩形的特征信息,是一个((中心点x, 中心点y),(宽度, 高度),旋转角度)的元组形式。

(中心点x, 中心点y):矩形的中心点坐标。

(宽度, 高度):矩形的宽度和高度(尺寸)。

旋转角度:矩形相对于x轴正方向的顺时针旋转角度(角度范围为-90到0度)。

(2)cv2.drawContour

cv2.drawContours(image, contours, contourIdx, color, thickness)

cv2.drawContours()函数会在原始图像上绘制指定的轮廓,并返回修改后的图像。绘制的轮廓可以用于目标检测、图像分割和形状分析等应用。
需要注意的是,为了使用cv2.drawContours()函数,你需要先找到要绘制的轮廓。通常,可以通过使用cv2.findContours()函数从二值图像或边缘图像中检测轮廓。

参数

image: 要绘制轮廓的图像。可以是单通道灰度图像或三通道彩色图像。

contours: 轮廓数组。每个轮廓由一组点构成,通常由cv2.findContours()函数返回。

contourIdx: 指定要绘制的轮廓的索引。如果为负数,则绘制所有的轮廓。

color: 绘制轮廓的颜色。可以是单个整数值(灰度图像)或三个整数值(彩色图像)。

thickness: 绘制轮廓的线条粗细。如果为负数或cv2.FILLED,则填充轮廓内部。
通过调用cv2.drawContours()函数,可以在指定图像上绘制轮廓

cv2.findContours
(contours, _) = cv2.findContours(image, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

在上述代码中,cv2.findContours()函数会返回一个存储轮廓的列表,以及其他相关信息。然后,你可以根据需要选择指定的轮廓,并使用cv2.drawContours()函数在图像上绘制它们。
请注意,以上是对cv2.drawContours()函数的基础介绍。实际使用中,你可能需要根据具体情况调整参数,并结合其他函数和技术来完成更复杂的图像处理任务

(3)cv2.boxPoints() 用于将cv2.minAreaRect()返回的矩形信息转换为符合要求的轮廓点集结构

将矩形的四个角点坐标转换为轮廓点集形式,以便可以直接传递给cv2.drawContours()函数进行绘制。

cv2.boxPoints()的参数是矩形的信息box,它是从cv2.minAreaRect()返回的矩形信息中获得的。box表示最小外接矩形的四个顶点坐标,每个顶点坐标有两个值(x, y)。返回的结果是一个NumPy数组,包含了四个顶点的坐标,每个坐标为一个二维点
在这里插入图片描述

(4)Demo

import cv2
import numpy as np
# 读取图像并转化为灰度图像
image = cv2.imread('img/usecvClose.jpg')
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
# 执行边缘检测
edges = cv2.Canny(gray, 50, 150)
# 查找轮廓
contours, _ = cv2.findContours(edges, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
# 计算最小包围矩形并绘制
for contour in contours:
    rect = cv2.minAreaRect(contour)
    box = cv2.boxPoints(rect)
    box = np.intp(box)
    cv2.drawContours(image, [box], 0, (0, 255, 0), 2)
# 显示绘制结果
cv2.imshow('Image', image)
cv2.waitKey(0)
cv2.destroyAllWindows()

(1)
在这里插入图片描述
(2)
在这里插入图片描述
(3)
在这里插入图片描述
(4)
在这里插入图片描述
(5)
在这里插入图片描述

5.2 最小包围圆形

5.2.1 查找包围 2D 点集的最小面积的圆 cv2.minEnclosingCircle()

在这里插入图片描述

(center, radius) = cv2.minEnclosingCircle(points)

参数

points:输入的点集,可以是一个2D的numpy数组,每个点的坐标为(points[i][0], points[i][1])。

返回值:

center:返回最小外接圆的圆心坐标,类型是一个元组,表示为(center_x, center_y)。

radius:返回最小外接圆的半径。

5.2.2 Demo

import cv2

# 读取图像并进行二值化处理
o = cv2.imread('img/useCvOpenxx.jpg')
gray = cv2.cvtColor(o, cv2.COLOR_BGR2GRAY)
_, binary = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)

# 寻找图像的轮廓
contours, _ = cv2.findContours(binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

# 遍历轮廓,并计算最小面积外接圆
for contour in contours:
    (x, y), radius = cv2.minEnclosingCircle(contour)
    center = (int(x), int(y))
    radius = int(radius)

    # 在原图上绘制外接圆
    cv2.circle(o, center, radius, (0, 255, 255), 2)

# 显示结果图像
cv2.imshow("result", o)
cv2.waitKey(0)  # 等待任意键按下
cv2.destroyAllWindows()  # 关闭所有窗口

演示
(1)
在这里插入图片描述
(2)
在这里插入图片描述

(3)(4) 关于半圆的差异,可能的原因有多种:
【1】图像读取的问题:如果这些数值碰巧影响了图像读取的质量或者是否包含透明通道等特性,那么在轮廓检测和外接圆计算时就可能出现不同的结果。
【2】边缘处理:由于二值化阈值选取、轮廓查找方法等因素,对于不同读取方式得到的图像,在边缘处可能存在细微差别,这可能导致部分轮廓未被正确识别。
(3)
17在这里插入图片描述>(4)
33在这里插入图片描述

(5)
在这里插入图片描述

5.3 最优拟合椭圆

cv2.fitEllipse函数要求轮廓中至少包含5个点才能进行椭圆拟合。如果轮廓中的点数量少于5个,将无法进行拟合椭圆操作,从而导致出现错误。
你可以在for循环中添加一个条件来排除点数少于5个的轮廓

for contour in contours:
    if len(contour) >= 5:
        ellipse = cv2.fitEllipse(contour)
        print("ellipse=", ellipse)
        cv2.ellipse(o, ellipse, (0, 255, 0), 3)

在这里插入图片描述

import cv2

o = cv2.imread('img/img.png')
gray = cv2.cvtColor(o, cv2.COLOR_BGR2GRAY)
ret, binary = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)
contours, hierarchy = cv2.findContours(binary, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)

for contour in contours:
    if len(contour) >= 5:
        ellipse = cv2.fitEllipse(contour)
        print("ellipse=",ellipse)
        cv2.ellipse(o,ellipse,(0,255,0),3)
cv2.imshow("result", o)
cv2.waitKey()
cv2.destroyAllWindows()

(1)
在这里插入图片描述
(2)
在这里插入图片描述
(3)
在这里插入图片描述

import cv2

# 仅导入所需的函数
import cv2

# 加载图像
o = cv2.imread('img/test_img.jpg')

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

# 阈值化
ret, binary = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)

# 寻找轮廓
contours, hierarchy = cv2.findContours(binary, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)

# 遍历所有轮廓
for contour in contours:
    if len(contour) >= 5:
        ellipse = cv2.fitEllipse(contour)
        # 添加对椭圆尺寸的验证
        if ellipse[1][0] > 0 and ellipse[1][1] > 0:  # 验证长轴和短轴尺寸
            print("ellipse=", ellipse)
            cv2.ellipse(o, ellipse, (0, 255, 0), 3)

# 显示结果
cv2.namedWindow("result")
cv2.imshow("result", o)
cv2.waitKey()
cv2.destroyAllWindows()

在这里插入图片描述

5.4 最优拟合直线

cv2.fitLine( points, distType, param, reps, aeps )

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

import cv2

# 读取图片
o = cv2.imread('img/S1.jpg')

# 转为灰度图像
gray = cv2.cvtColor(o, cv2.COLOR_BGR2GRAY)

# 二值化处理
ret, binary = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)

# 查找轮廓
contours, hierarchy = cv2.findContours(binary, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)

# 获取图片尺寸
rows, cols = o.shape[:2]

# 计算第一条轮廓的拟合直线参数
[vx, vy, x, y] = cv2.fitLine(contours[0], cv2.DIST_L2, 0, 0.01, 0.01)

# 根据直线参数计算y坐标
lefty = max(0, min(rows - 1, int((-x[0] * vy[0] / vx[0]) + y[0])))
righty = max(0, min(rows - 1, int(((cols - x[0]) * vy[0] / vx[0]) + y[0])))

# 在原图上画出直线
cv2.line(o, (cols - 1, righty), (0, lefty), (0, 255, 0), 2)

# 显示结果
cv2.imshow("result", o)

# 等待按键,然后关闭所有窗口
cv2.waitKey()
cv2.destroyAllWindows()

在这里插入图片描述
在这里插入图片描述

5.5 最小外包三角形

retval, triangle = cv2.minEnclosingTriangle( points )

返回值:
retval:最小外包三角形的面积。
triangle:最小外包三角形的三个顶点集

import cv2
import numpy as np

# 读取图像并进行二值化处理
o = cv2.imread('img/useCvOpenxx.jpg')
gray = cv2.cvtColor(o, cv2.COLOR_BGR2GRAY)
_, binary = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)
# 寻找图像的轮廓
contours, _ = cv2.findContours(binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
# 遍历轮廓,并计算最小外包三角形
for contour in contours:
    # 计算最小外包三角形
    triangle = cv2.minEnclosingTriangle(contour)[1]

    # 在原图上绘制最小外包三角形
    cv2.polylines(o, [triangle.astype(int)], True, (0, 255, 255), 2)

    # 计算最小外包三角形的面积
    triangle_area = cv2.contourArea(triangle)

    # 输出最小外包三角形的面积和三个顶点坐标
    print("最小外包三角形的面积:", triangle_area)
    print("三个顶点坐标:")
    for point in triangle:
        print(tuple(point[0]))
# 显示结果图像
cv2.imshow("result", o)
cv2.waitKey(0)  # 等待任意键按下
cv2.destroyAllWindows()  # 关闭所有窗口

(1)
在这里插入图片描述
(2)
在这里插入图片描述
(3)
在这里插入图片描述
(4)
在这里插入图片描述

5.6 近多边形

cv2.approxPolyDP( curve, epsilon, closed )

contour:要近似的输入轮廓,通常是通过cv2.findContours()函数获得的。

epsilon:指定近似精度的参数

closed 是布尔型值。该值为 True 时,逼近多边形是封闭的;否则,逼近多边形是不封
闭的。

  • 76
    点赞
  • 33
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 16
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 16
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

你会魔法吗✧(≖ ◡ ≖✿)

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值