opencv之图像轮廓(三)--凸包

news2024/11/22 14:07:55

文章目录

  • 前言
  • 获取凸包
  • 凸缺陷
  • 几何学测试
    • 测试轮廓是否是凸形的
    • 点到轮廓的距离
  • 形状场景算法比较轮廓
  • 轮廓的特征值
    • 宽高比
    • Extent
    • Solidity
    • 等效直径(Equivalent Diameter)
    • 方向
    • 掩模和像素点
      • 使用Numpy函数获取轮廓像素点
      • 使用OpenCV函数获取轮廓点
    • 最大值和最小值及它们的位置
    • 平均颜色及平均灰度
    • 极点


前言

逼近多边形是轮廓的高度近似,但是有时候,我们希望使用一个多边形的凸包来简化它。凸包跟逼近多边形很像,只不过它是物体最外层的“凸”多边形。凸包指的是完全包含原有轮廓,并且仅由轮廓上的点所构成的多边形。凸包的每一处都是凸的,即在凸包内连接任意两点的直线都在凸包的内部。在凸包内,任意连续三个点的内角小于180°。

例如,在图1中,最外层的多边形为机械手的凸包,在机械手边缘与凸包之间的部分被称为凸缺陷(Convexity Defect),凸缺陷能够用来处理手势识别等问题。
在这里插入图片描述

图1

获取凸包

OpenCV提供函数cv2.convexHull()用于获取轮廓的凸包。该函数的语法格式为:

hull = cv2.convexHull( points[, clockwise[, returnPoints]] )

式中的返回值hull为凸包角点。

式中的参数如下:

  • points:轮廓。
  • clockwise:布尔型值。该值为True时,凸包角点将按顺时针方向排列;该值为False时,则以逆时针方向排列凸包角点。
  • returnPoints:布尔型值。默认值是True,函数返回凸包角点的x/y轴坐标;当为False时,函数返回轮廓中凸包角点的索引。

【例1】观察函数cv2.convexHull()内参数returnPoints的使用情况。

import cv2

o = cv2.imread('C:\\Users\\Administrator\\Desktop\\caonima.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_TREE,
                                              cv2.CHAIN_APPROX_SIMPLE)
hull = cv2.convexHull(contours[0])  # 返回坐标值
print("returnPoints为默认值True时返回值hull的值:\n", hull)
hull2 = cv2.convexHull(contours[0], returnPoints=False)  # 返回索引值
print("returnPoints为False时返回值hull的值:\n", hull2)

【例2】使用函数cv2.convexHull()获取轮廓的凸包。

import cv2
# --------------读取并绘制原始图像------------------
o = cv2.imread('hand.bmp')
cv2.imshow("original", o)
# --------------提取轮廓------------------
gray = cv2.cvtColor(o, cv2.COLOR_BGR2GRAY)
ret, binary = cv2.threshold(gray,127,255, cv2.THRESH_BINARY)
image, contours, hierarchy = cv2.findContours(binary,
                                              cv2.RETR_LIST,
                                              cv2.CHAIN_APPROX_SIMPLE)
# --------------寻找凸包,得到凸包的角点------------------
hull = cv2.convexHull(contours[0])
# --------------绘制凸包------------------
cv2.polylines(o, [hull], True, (0, 255, 0), 2)
# --------------显示凸包------------------
cv2.imshow("result", o)
cv2.waitKey()
cv2.destroyAllWindows()

运行上述程序,会显示如图2所示的图像。
在这里插入图片描述

图2
  • 左图是图像o。
  • 右图是包含获取的凸包的图像。

凸缺陷

凸包与轮廓之间的部分,称为凸缺陷。在OpenCV中使用函数cv2.convexityDefects()获取凸缺陷。其语法格式如下:

convexityDefects = cv2.convexityDefects( contour, convexhull )

式中的返回值convexityDefects为凸缺陷点集。它是一个数组,每一行包含的值是[起点,终点,轮廓上距离凸包最远的点,最远点到凸包的近似距离]。

需要注意的是,返回结果中[起点,终点,轮廓上距离凸包最远的点,最远点到凸包的近似距离]的前三个值是轮廓点的索引,所以需要到轮廓点中去找它们。

式中的参数如下:

  • contour是轮廓。
  • convexhull是凸包。

需要注意的是,用cv2.convexityDefects()计算凸缺陷时,要使用凸包作为参数。在查找该凸包时,所使用函数cv2.convexHull()的参数returnPoints的值必须是False。为了让大家更直观地观察凸缺陷点集,我们尝试将凸缺陷点集在一幅图内显示出来。实现方式为,将起点和终点用一条线连接,在最远点画一个圆圈。下面我们通过一个例子来展示上述操作。

【例3】使用函数cv2.convexityDefects()计算凸缺陷。

import cv2
#----------------原图--------------------------
img = cv2.imread('hand.bmp')
cv2.imshow('original', img)
#----------------构造轮廓--------------------------
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
ret, binary = cv2.threshold(gray, 127, 255,0)
image, contours, hierarchy = cv2.findContours(binary,
                                              cv2.RETR_TREE,
                                              cv2.CHAIN_APPROX_SIMPLE)
#----------------凸包--------------------------
cnt = contours[0]
hull = cv2.convexHull(cnt, returnPoints = False)
defects = cv2.convexityDefects(cnt, hull)
print("defects=\n", defects)
#----------------构造凸缺陷--------------------------
for i in range(defects.shape[0]):
    s, e, f, d = defects[i,0]
    start = tuple(cnt[s][0])
    end = tuple(cnt[e][0])
    far = tuple(cnt[f][0])
    cv2.line(img, start, end, [0,0,255],2)
    cv2.circle(img, far,5, [255,0,0], -1)
    #----------------显示结果,释放图像--------------------------
    cv2.imshow('result', img)
    cv2.waitKey(0)
    cv2.destroyAllWindows()

在这里插入图片描述

图3

图中用点标出了凸缺陷。

几何学测试

本节介绍几种与凸包有关的几何学测试。

测试轮廓是否是凸形的

在OpenCV中,可以用函数cv2.isContourConvex()来判断轮廓是否是凸形的,其语法格式为:

retval = cv2.isContourConvex( contour )

式中:

  • 返回值retval是布尔型值。该值为True时,表示轮廓为凸形的;否则,不是凸形的。
  • 参数contour为要判断的轮廓。

【例4】使用函数cv2.isContourConvex()来判断轮廓是否是凸形的。

根据题目的要求,编写代码如下:

import cv2
o = cv2.imread('C:\\Users\\Administrator\\Desktop\\caonima.png')
cv2.imshow("original", o)
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)
#--------------凸包----------------------
image1=o.copy()
hull = cv2.convexHull(contours[0])
cv2.polylines(image1, [hull], True, (0, 255, 0), 2)
print("使用函数cv2.convexHull()构造的多边形是否是凸形的:",
      cv2.isContourConvex(hull))
cv2.imshow("result1", image1)
#------------逼近多边形--------------------
image2=o.copy()
epsilon = 0.01*cv2.arcLength(contours[0], True)
approx = cv2.approxPolyDP(contours[0], epsilon, True)
image2=cv2.drawContours(image2, [approx],0, (0,0,255),2)
print("使用函数cv2.approxPolyDP()构造的多边形是否是凸形的:",
      cv2.isContourConvex(approx))
cv2.imshow("result2", image2)
#------------释放窗口--------------------
cv2.waitKey()
cv2.destroyAllWindows()

在这里插入图片描述

图4
  • 左上1图是图像o。
  • 左上2图显示了在图像o上使用函数cv2.convexHull()构造的凸包。
  • 右下图2显示了在图像o上使用函数cv2.approxPolyDP()构造的逼近多边形。

从以上运行结果可以看出:

  • 使用函数cv2.convexHull()构造凸包后,对绘制的凸包使用函数cv2.isContourConvex()判断,返回值为True,说明该轮廓是凸形的。
  • 使用函数cv2.approxPolyDP()构造逼近多边形后,对绘制的逼近多边形使用函数cv2.isContourConvex()判断,返回值为False,说明该轮廓(多边形)不是凸形的。

点到轮廓的距离

在OpenCV中,函数cv2.pointPolygonTest()被用来计算点到多边形(轮廓)的最短距离(也就是垂线距离),这个计算过程又称点和多边形的关系测试。该函数的语法格式为:

retval = cv2.pointPolygonTest( contour, pt, measureDist )

式中的返回值为retval,与参数measureDist的值有关。

式中的参数如下:

  • contour为轮廓。
  • pt为待判定的点。
  • measureDist为布尔型值,表示距离的判定方式。
    • 当值为True时,表示计算点到轮廓的距离。如果点在轮廓的外部,返回值为负数;如果点在轮廓上,返回值为0;如果点在轮廓内部,返回值为正数。
    • 当值为False时,不计算距离,只返回“-1”、“0”和“1”中的一个值,表示点相对于轮廓的位置关系。如果点在轮廓的外部,返回值为“-1”;如果点在轮廓上,返回值为“0”;如果点在轮廓内部,返回值为“1”。

【例5】使用函数cv2.pointPolygonTest()计算点到轮廓的最短距离。使用函数cv2.pointPolygonTest()计算点到轮廓的最短距离,需要将参数measureDist的值设置为True。

import cv2

# ----------------原始图像-------------------------
o = cv2.imread('C:\\Users\\Administrator\\Desktop\\caonima.png')
cv2.imshow("original", o)

# ----------------获取凸包------------------------
gray = cv2.cvtColor(o, cv2.COLOR_BGR2GRAY)
ret, binary = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)

# 使用新版 findContours 函数,只返回 contours 和 hierarchy
contours, hierarchy = cv2.findContours(binary, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)

# 获取第一个轮廓的凸包
hull = cv2.convexHull(contours[0])

# 将灰度图像转换为 BGR 格式以便显示彩色凸包线条
image = cv2.cvtColor(binary, cv2.COLOR_GRAY2BGR)
cv2.polylines(image, [hull], True, (0, 255, 0), 2)

# ----------------内部点A到轮廓的距离-------------------------
distA = cv2.pointPolygonTest(hull, (300, 150), True)
font = cv2.FONT_HERSHEY_SIMPLEX
cv2.putText(image, 'A', (300, 150), font, 1, (0, 255, 0), 3)
print("distA=", distA)

# ----------------外部点B到轮廓的距离-------------------------
distB = cv2.pointPolygonTest(hull, (300, 250), True)
cv2.putText(image, 'B', (300, 250), font, 1, (0, 255, 0), 3)
print("distB=", distB)

# ------------正好处于轮廓上的点C到轮廓的距离-----------------
distC = cv2.pointPolygonTest(hull, (423, 112), True)
cv2.putText(image, 'C', (423, 112), font, 1, (0, 255, 0), 3)
print("distC=", distC)

# ----------------显示-------------------------
cv2.imshow("result1", image)
cv2.waitKey()
cv2.destroyAllWindows()

在这里插入图片描述

在这里插入图片描述

图5

从以上结果可以看出,

  • A点算出来的距离为20.314188617040656,是一个正数,说明A点在轮廓内部。

  • B点算出来的距离为-34.0,是一个负数,说明B点在轮廓外部。

  • C点算出来的距离为7.0,说明C点在轮廓上。

在实际使用中,如果想获取位于轮廓上的点,可以通过打印轮廓点集的方式获取。例如,本例中可以通过语句“print(hull)”获取轮廓上的点。在获取轮廓上的点以后,可以将其用作函数cv2.pointPolygonTest()的参数,以测试函数返回值是否为零。

【例6】使用函数cv2.pointPolygonTest()判断点与轮廓的关系。

使用函数cv2.pointPolygonTest()判断点与轮廓的关系时,需要将参数measureDist的值设置为False。

import cv2
#----------------原始图像-------------------------
o = cv2.imread('cs.bmp')
cv2.imshow("original", o)
#----------------获取凸包------------------------
gray = cv2.cvtColor(o, cv2.COLOR_BGR2GRAY)
ret, binary = cv2.threshold(gray,127,255, cv2.THRESH_BINARY)
image, contours, hierarchy = cv2.findContours(binary,
                                      cv2.RETR_LIST,
                                      cv2.CHAIN_APPROX_SIMPLE)
hull = cv2.convexHull(contours[0])
image = cv2.cvtColor(image, cv2.COLOR_GRAY2BGR)
cv2.polylines(image, [hull], True, (0, 255, 0), 2)
#----------------内部点A与轮廓的关系-------------------------
distA = cv2.pointPolygonTest(hull, (300, 150), False)
font=cv2.FONT_HERSHEY_SIMPLEX
cv2.putText(image, 'A', (300,150), font, 1, (0,255,0),3)
print("distA=", distA)
#----------------外部点B与轮廓的关系-------------------------
distB = cv2.pointPolygonTest(hull, (300, 250), False)
font=cv2.FONT_HERSHEY_SIMPLEX
cv2.putText(image, 'B', (300,250), font, 1, (0,255,0),3)
print("distB=", distB)
#----------------边缘线上的点C与轮廓的关系----------------------
distC = cv2.pointPolygonTest(hull, (423, 112), False)
font=cv2.FONT_HERSHEY_SIMPLEX
cv2.putText(image, 'C', (423,112), font, 1, (0,255,0),3)
print("distC=", distC)
#print(hull)   #测试边缘到底在哪里,然后再使用确定位置的点绘制文字
#----------------显示-------------------------
cv2.imshow("result", image)
cv2.waitKey()
cv2.destroyAllWindows()

在这里插入图片描述

图6

从以上结果可以看出,

  • A点算出来的关系值为1,说明该点在轮廓的内部。
  • B点算出来的关系值为1,说明该点在轮廓的外部。
  • C点算出来的关系值为1,说明该点在轮廓的内部。

形状场景算法比较轮廓

不介绍

轮廓的特征值

轮廓自身的一些属性特征及轮廓所包围对象的特征对于描述图像具有重要意义。本节介绍几个轮廓自身的属性特征及轮廓所包围对象的特征。

宽高比

可以使用宽高比(AspectRation)来描述轮廓,例如矩形轮廓的宽高比为:宽高比=宽度(Width)/高度(Height)

【例7】计算矩形轮廓的宽高比。

import cv2

o = cv2.imread('C:\\Users\\Administrator\\Desktop\\caonima.png')
cv2.imshow("original", o)
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)
x, y, w, h = cv2.boundingRect(contours[0])
cv2.rectangle(o, (x, y), (x + w, y + h), (255, 255, 255), 3)
aspectRatio = float(w) / h
print(aspectRatio)
cv2.imshow("result", o)
cv2.waitKey()
cv2.destroyAllWindows()


########################result###############################
D:\Code\py_demo\.venv\Scripts\python.exe D:\Code\py_demo\operator_cv\凸包.py 
2.1785714285714284  

在这里插入图片描述

图7

可以看出,轮廓的宽高比约为2。

Extent

可以使用轮廓面积与矩形边界(矩形包围框、矩形轮廓)面积之比Extend来描述图像及其轮廓特征。计算方法为:

在这里插入图片描述

【例8】计算图像的轮廓面积与其矩形边界面积之比。

import cv2
o = cv2.imread('C:\\Users\\Administrator\\Desktop\\caonima.png')
cv2.imshow("original", o)
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)
x, y, w, h = cv2.boundingRect(contours[0])
cv2.drawContours(o, contours[0], -1, (0,0,255),3)
cv2.rectangle(o, (x, y), (x+w, y+h), (255,0,0),3)
rectArea=w*h
cntArea=cv2.contourArea(contours[0])
extend=float(cntArea)/rectArea
print(extend)
cv2.imshow("result", o)
cv2.waitKey()
cv2.destroyAllWindows()

################################result#################################
D:\Code\py_demo\.venv\Scripts\python.exe D:\Code\py_demo\operator_cv\凸包.py 
0.680144906323185

在这里插入图片描述

图8

可以看出,本例中图像的轮廓面积与矩形边界面积的比值大约为0.7。

Solidity

可以使用轮廓面积与凸包面积之比Solidity来衡量图像、轮廓及凸包的特征。其计算方法为:

在这里插入图片描述

【例9】计算图像轮廓面积与凸包面积之比。

import cv2

o = cv2.imread('C:\\Users\\Administrator\\Desktop\\caonima.png')
cv2.imshow("original", o)
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)
cv2.drawContours(o, contours[0], -1, (0, 0, 255), 3)
cntArea = cv2.contourArea(contours[0])
hull = cv2.convexHull(contours[0])
hullArea = cv2.contourArea(hull)
cv2.polylines(o, [hull], True, (0, 255, 0), 2)
solidity = float(cntArea) / hullArea
print(solidity)
cv2.imshow("result", o)
cv2.waitKey()
cv2.destroyAllWindows()
###############################result############################
D:\Code\py_demo\.venv\Scripts\python.exe D:\Code\py_demo\operator_cv\凸包.py 
0.9110158068864109

在这里插入图片描述

图9

可以看出,本例中图像的轮廓面积与凸包面积的比值约为0.9。

等效直径(Equivalent Diameter)

可以用等效直径来衡量轮廓的特征值,该值是与轮廓面积相等的圆形的直径。其计算公式为:
在这里插入图片描述

【例10】计算与轮廓面积相等的圆形的直径,并绘制与该轮廓等面积的圆。

import cv2
import numpy as np

o = cv2.imread('C:\\Users\\Administrator\\Desktop\\caonima.png')
cv2.imshow("original", o)
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)
cv2.drawContours(o, contours[0], -1, (0, 0, 255), 3)
cntArea = cv2.contourArea(contours[0])
equiDiameter = np.sqrt(4 * cntArea / np.pi)
print(equiDiameter)
cv2.circle(o, (100, 100), int(equiDiameter / 2), (0, 0, 255), 3)  # 展示等直径大小的圆
cv2.imshow("result", o)
cv2.waitKey()
cv2.destroyAllWindows()

在这里插入图片描述

图10
  • ● 左图是图像o。
  • ● 右图显示了图像o的轮廓及与该轮廓等面积的圆(显示为灰色)。

同时,程序还会显示如下的运行结果:
在这里插入图片描述

可以看出,与本例中与轮廓面积相等的圆形的直径约为99。

方向

在OpenCV中,函数cv2.fitEllipse()可以用来构造最优拟合椭圆,还可以在返回值内分别返回椭圆的中心点、轴长、旋转角度等信息。使用这种形式,能够更直观地获取椭圆的方向等信息。

函数cv2.fitEllipse()返回各个属性值的语法格式为:

(x, y), (MA, ma), angle = cv2.fitEllipse(cnt)

式中几个返回值的意义如下:

  • (x, y):椭圆的中心点。
  • (MA, ma):椭圆水平方向轴和垂直方向轴的长度。
  • angle:椭圆的旋转角度。

【例11】观察函数cv2.fitEllipse()的不同返回值。

import cv2
o = cv2.imread('C:\\Users\\Administrator\\Desktop\\caonima.png')
cv2.imshow("original", o)
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)
ellipse = cv2.fitEllipse(contours[0])
retval=cv2.fitEllipse(contours[0])
print("单个返回值形式:")
print("retval=\n", retval)
(x, y), (MA, ma), angle = cv2.fitEllipse(contours[0])
print("三个返回值形式:")
print("(x, y)=(", x, y, ")")
print("(MA, ma)=(", MA, ma, ")")
print("angle=", angle)
cv2.ellipse(o, ellipse, (0,0,255),2)
cv2.imshow("result", o)
cv2.waitKey()
cv2.destroyAllWindows()
###############################result###########################
D:\Code\py_demo\.venv\Scripts\python.exe D:\Code\py_demo\operator_cv\凸包.py 
单个返回值形式:
retval=
 ((388.0275573730469, 155.02651977539062), (97.1426010131836, 259.5243225097656), 82.80935668945312)
三个返回值形式:
(x, y)=( 388.0275573730469 155.02651977539062 )
(MA, ma)=( 97.1426010131836 259.5243225097656 )
angle= 82.80935668945312

在这里插入图片描述

图11

从以上运行结果可以看出,函数cv2.fitEllipse()以不同形式返回的值是相同的。

掩模和像素点

有时,我们希望获取某对象的掩模图像及其对应的点。12.1.3节介绍了将函数cv2.drawContours()的轮廓宽度参数thickness设置为“-1”,即可获取特定对象的实心轮廓,即特定对象的掩模。

另外,我们可能还希望获取轮廓像素点的具体位置信息。本节介绍如何获取轮廓(实心、空心)的像素点位置信息。

一般情况下,轮廓是图像内非零的像素点,可以通过两种方式获取轮廓像素点的位置信息。一种是使用Numpy函数,另外一种是使用OpenCV函数。

使用Numpy函数获取轮廓像素点

numpy.nonzero()函数能够找出数组内非零元素的位置,但是其返回值是将行、列分别显示的。

【例12】使用Numpy函数获取一个数组内的非零值元素的位置信息。

import numpy as np
#------------生成一个元素都是零值的数组a-------------------
a=np.zeros((5,5), dtype=np.uint8)
#-------随机将其中10个位置上的数值设置为1------------
#---times控制次数
#---i, j是随机生成的行、列位置
#---a[i, j]=1,将随机挑选出来的位置上的值设置为1
for times in range(10):
    i=np.random.randint(0,5)
    j=np.random.randint(0,5)
    a[i, j]=1
    #-------打印数组a,观察数组a内值的情况-----------
    print("a=\n", a)
    #------查找数组a内非零值的位置信息------------
    loc=np.transpose(np.nonzero(a))
    #-----输出数组a内非零值的位置信息------------
    print("a内非零值的位置:\n", loc)

在这里插入图片描述

图12

【例13】使用Numpy函数获取一个图像内的轮廓点位置。

import cv2
import numpy as np
#-----------------读取原始图像----------------------
o = cv2.imread('cc.bmp')
cv2.imshow("original", o)
#-----------------获取轮廓------------------------
gray = cv2.cvtColor(o, cv2.COLOR_BGR2GRAY)
ret, binary = cv2.threshold(gray,127,255, cv2.THRESH_BINARY)
image, contours, hierarchy = cv2.findContours(binary,
                                              cv2.RETR_LIST,
                                              cv2.CHAIN_APPROX_SIMPLE)
cnt=contours[0]
#-----------------绘制空心轮廓------------------------
mask1 = np.zeros(gray.shape, np.uint8)
cv2.drawContours(mask1, [cnt],0,255,2)
pixelpoints1 = np.transpose(np.nonzero(mask1))
print("pixelpoints1.shape=", pixelpoints1.shape)
print("pixelpoints1=\n", pixelpoints1)
cv2.imshow("mask1", mask1)
#-----------------绘制实心轮廓---------------------
mask2 = np.zeros(gray.shape, np.uint8)
cv2.drawContours(mask2, [cnt],0,255, -1)
pixelpoints2 = np.transpose(np.nonzero(mask2))
print("pixelpoints2.shape=", pixelpoints2.shape)
print("pixelpoints2=\n", pixelpoints2)
cv2.imshow("mask2", mask2)
#-----------------释放窗口------------------------
cv2.waitKey()
cv2.destroyAllWindows()

运行结果图像。其中:
在这里插入图片描述

图13
  • 左图是图像o。
  • 中间的是空心轮廓图像mask1。
  • 右图是实心轮廓图像mask2。

使用OpenCV函数获取轮廓点

OpenCV提供了函数cv2.findNonZero()用于查找非零元素的索引。该函数的语法格式为:

idx = cv2.findNonZero( src )

式中:

  • idx为返回值,表示非零元素的索引位置。需要注意的是,在返回的索引中,每个元素对应的是(列号,行号)的格式。
  • src为参数,表示要查找非零元素的图像

【例14】使用OpenCV函数cv2.findNonZero()获取一个数组内的非零值。

import cv2
import numpy as np

# ------------生成一个元素都是零值的数组a-------------------
a = np.zeros((5, 5), dtype=np.uint8)
# -------随机将其中10个位置上的值设置为1------------
# ---times控制次数
# ---i, j是随机生成的行、列位置
# ---a[i, j]=1,将随机挑选出来的位置上的值设置为1
for times in range(10):
    i = np.random.randint(0, 5)
    j = np.random.randint(0, 5)
    a[i, j] = 1
# -------打印数组a,观察数组a内值的情况-----------
print("a=\n", a)
# ------查找数组a内非零值的位置信息------------
loc = cv2.findNonZero(a)
# -----输出数组a内非零值的位置信息------------
print("a内非零值的位置:\n", loc)

运行上述程序,会显示如下的运行结果:

在这里插入图片描述

图14

【例15】使用OpenCV函数cv2.findNonZero()获取一个图像内的轮廓点的位置。

import cv2
import numpy as np

# -----------------读取原始图像----------------------
o = cv2.imread('C:\\Users\\Administrator\\Desktop\\caonima.png')
cv2.imshow("original", o)
# -----------------获取轮廓------------------------
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)
cnt = contours[0]
# -----------------绘制空心轮廓------------------------
mask1 = np.zeros(gray.shape, np.uint8)
cv2.drawContours(mask1, [cnt], 0, 255, 2)
pixelpoints1 = cv2.findNonZero(mask1)
print("pixelpoints1.shape=", pixelpoints1.shape)
print("pixelpoints1=\n", pixelpoints1)
cv2.imshow("mask1", mask1)
# -----------------绘制实心轮廓---------------------
mask2 = np.zeros(gray.shape, np.uint8)
cv2.drawContours(mask2, [cnt], 0, 255, -1)
pixelpoints2 = cv2.findNonZero(mask2)
print("pixelpoints2.shape=", pixelpoints2.shape)
print("pixelpoints2=\n", pixelpoints2)
cv2.imshow("mask2", mask2)
# -----------------释放窗口------------------------
cv2.waitKey()
cv2.destroyAllWindows()

最大值和最小值及它们的位置

OpenCV提供了函数cv2.minMaxLoc(),用于在指定的对象内查找像素最大值、最小值及其位置。该函数的语法格式是:

min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(imgray, mask = mask)

式中的返回值为:

  • min_val:最小值。
  • max_val:最大值。
  • min_loc:最小值的位置。
  • max_loc:最大值的位置。

式中的参数如下:

  • imgray:单通道图像。
  • mask:掩模。通过使用掩模图像,可以得到掩模指定区域内的最值信息。

平均颜色及平均灰度

OpenCV提供了函数cv2.mean(),用于计算一个对象的平均颜色或平均灰度。该函数的语法格式为:

mean_val = cv2.mean(im, mask = mask)

式中的返回值为mean_val,表示返回的平均值。

式中的参数如下:

  • im:原图像。
  • mask:掩模。
import cv2
import numpy as np

# --------读取并显示原始图像-----------------
o = cv2.imread('C:\\Users\\Administrator\\Desktop\\pitu.png')
cv2.imshow("original", o)

# --------获取轮廓-----------------
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)

# 检查是否检测到足够的轮廓
if len(contours) > 2:
    cnt = contours[2]  # 选择第3个轮廓
else:
    print(f"检测到的轮廓数不足: {len(contours)}")
    cnt = contours[-1]  # 选择最后一个轮廓,或根据需要选择其他索引

# --------使用掩模获取感兴趣区域的均值-----------------
mask = np.zeros(gray.shape, np.uint8)  # 构造mean所使用的掩模(必须是单通道的)
cv2.drawContours(mask, [cnt], 0, (255, 255, 255), -1)
meanVal = cv2.mean(o, mask=mask)  # mask是一个区域,所以必须是单通道的
print("meanVal=\n", meanVal)

# --------使用掩模获取感兴趣区域并显示-----------------
masko = np.zeros(o.shape, np.uint8)
cv2.drawContours(masko, [cnt], -1, (255, 255, 255), -1)
loc = cv2.bitwise_and(o, masko)
cv2.imshow("mask", loc)

# --------释放窗口-----------------
cv2.waitKey()
cv2.destroyAllWindows()

在这里插入图片描述

图15
  • 左图是图像o。
  • 右图是获取的感兴趣区域。

在这里插入图片描述

从上述结果可以看出,函数cv2.mean()能够计算各个通道的均值。上述4个值分别是RGB和A通道(alpha通道)的均值。本例中,RGB三个通道的值相同,所以计算出的均值也是一样的。

极点

有时,我们希望获取某个对象内的极值点,例如最左端、最右端、最上端、最下端的四个点。OpenCV提供了相应的函数来找出这些点,通常的语法格式是:

leftmost = tuple(cnt[cnt[:, :,0].argmin()][0])
rightmost = tuple(cnt[cnt[:, :,0].argmax()][0])
topmost = tuple(cnt[cnt[:, :,1].argmin()][0])
bottommost = tuple(cnt[cnt[:, :,1].argmax()][0])

【例16】计算一幅图像内的极值点。

import cv2
import numpy as np

o = cv2.imread('C:\\Users\\Administrator\\Desktop\\caonima.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)
mask = np.zeros(gray.shape, np.uint8)
cnt = contours[0]
cv2.drawContours(mask, [cnt], 0, 255, -1)
# --------计算极值-----------------
leftmost = tuple(cnt[cnt[:, :, 0].argmin()][0])
rightmost = tuple(cnt[cnt[:, :, 0].argmax()][0])
topmost = tuple(cnt[cnt[:, :, 1].argmin()][0])
bottommost = tuple(cnt[cnt[:, :, 1].argmax()][0])
# --------打印极值-----------------
print("leftmost=", leftmost)
print("rightmost=", rightmost)
print("topmost=", topmost)
print("bottommost=", bottommost)
# --------绘制说明文字-----------------
font = cv2.FONT_HERSHEY_SIMPLEX
cv2.putText(o, 'A', leftmost, font, 1, (0, 0, 255), 2)
cv2.putText(o, 'B', rightmost, font, 1, (0, 0, 255), 2)
cv2.putText(o, 'C', topmost, font, 1, (0, 0, 255), 2)
cv2.putText(o, 'D', bottommost, font, 1, (0, 0, 255), 2)
# --------绘制图像-----------------
cv2.imshow("result", o)
# --------释放窗口-----------------
cv2.waitKey()
cv2.destroyAllWindows()

运行结果图像。
在这里插入图片描述

图16

同时,程序还会显示如下的运行结果:

在这里插入图片描述

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

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

相关文章

太牛了!AI大佬的课程!吴恩达、李飞飞、李宏毅、Hinton、LeCun...

本文精心梳理了AI顶级大佬教授的人工智能课程,涵盖了深度学习、机器学习等多个领域的前沿内容。 这些课程将引领您深入了解决策树、朴素贝叶斯、逻辑回归、神经网络和深度学习等核心知识点,同时还将探索贝叶斯学习、支持向量机和核方法、聚类、无监督学…

2024开学季,这五款学生必备好物请不要错过!

转眼又到了开学季,想必许多踏入大学校园的新同学们已经难掩心中的兴奋与期待,正摩拳擦掌准备拥抱即将到来的大学生活。不过,在你们迫不及待地迎接新阶段之前,何不利用开学季的各种优惠活动,为自己挑选一些实用的必备好…

NX二次开发—柱面中心线工具

设计一个柱面中心线工具,可以实现选择对象,画出圆柱的中心线,可以更改中心的线的颜色、线型、线宽和图层,是否延长,是否关联。 先在NX上进行界面设计 添加选择对象,并设置标题,选择设置为多选 添加组,在组里添加线条颜色/线型/线宽,设置颜色ColorValue和线型Value 这…

OrionX GPU算力池助力AI OCR场景应用

01 AI OCR的历史及概念 OCR(Optical Character Recognition,光学字符识别)是指采用光学的方式将纸质文档中的文字转换成为黑白点阵的图像文件,通过检测暗、亮的模式确定其形状,然后用字符识别方法将形状翻译成计算机文…

Java 冒泡排序

1&#xff0e;冒泡排序是最出名的排序算法之一&#xff0c;总共有八大排序&#xff01; 2&#xff0e;冒泡排序的算法相对简单&#xff0c;两层循环&#xff0c;外层冒泡轮数&#xff0c;里层以此比较。 如下&#xff1a; j < array.length - 1-i的作用&#xff1a;下一轮比…

内衣洗衣机哪个牌子好用?汇总五款主流硬核内衣洗衣机

内衣洗衣机是近年来备受关注的小家电产品&#xff0c;虽然市场火爆&#xff0c;但还是存在大部分人对内衣洗衣机的不了解&#xff0c;会购买到质量差、清洗效果不好的内衣洗衣机&#xff0c;面对众多内衣洗衣机品牌&#xff0c;到底内衣迷你洗衣机什么牌子好呢&#xff1f;今天…

多线程篇五——wait和notify

多线程篇五——wait和notify 如笔者理解有误&#xff0c;欢迎交流指正⭐ 线程的执行先后顺序难以预料【抢占式执行】&#xff0c;但是实际开发中我们会需要掌握当下线程的执行顺序. 这就是wait和notify的作用.【都是Object方法即随便定义一个对象豆可以使用wait和notify】 wa…

跟李沐学AI:长短期记忆网络LSTM

输入们、遗忘门和输出门 LSTM引入输入门、忘记门和输出门 输入门计算公式为&#xff1a;。 遗忘门计算公式为&#xff1a;。 输出门计算公式为&#xff1a;。 它们由三个具有sigmoid激活函数的全连接层处理&#xff0c; 以计算输入门、遗忘门和输出门的值。 因此&#xff0c…

为什么不推荐使用Stack

Java已不推荐使用Stack&#xff0c;而是推荐使用更高效的ArrayDeque 为什么不推荐使用 性能低&#xff1a;是因为 Stack 继承自 Vector&#xff0c; 而 Vector 在每个方法中都加了锁。由于需要兼容老的项目&#xff0c;很难在原有的基础上进行优化&#xff0c;因此 Vector 就被…

鸟类目标检测系统源码分享

鸟类目标检测检测系统源码分享 [一条龙教学YOLOV8标注好的数据集一键训练_70全套改进创新点发刊_Web前端展示] 1.研究背景与意义 项目参考AAAI Association for the Advancement of Artificial Intelligence 项目来源AACV Association for the Advancement of Computer Vis…

亲测好用,ChatGPT 3.5/4.0新手使用手册~

都知道ChatGPT很强大&#xff0c;聊聊天、写论文、搞翻译、写代码、写文案、审合同等等&#xff0c;无所不能~ 那么到底怎么使用呢&#xff1f;其实很简单了&#xff0c;国内AI产品发展也很快&#xff0c;很多都很好用了~ 我一直在用&#xff0c;建议收藏下来~ 有最先进、最…

RocketMQ出现The broker does not support consumer to filter message by SQL92

在使用RocketMQ使用SQL过滤消息的时候&#xff0c;出现下面错误 原因是我们的配置文件没有开启SQL过滤功能&#xff0c;我们需要在每个配置文件中添加下面命令 #开启过滤消息时支持SQL92标准 enablePropertyFiltertrue接着我们重启namesrv与broker服务就解决问题 # 1.进入bi…

Robust Image Denoising through Adversarial Frequency Mixup

基于对抗性混频的鲁棒图像去噪 论文链接&#xff1a;https://openaccess.thecvf.com/CVPR2024/Ryou_Robust_Image_Denoising_through_Adversarial_Frequency_Mixup 项目链接&#xff1a;https://github.com/dhryougit/AFM Abstract 基于深度神经网络的图像去噪方法经常与训练…

哈希表的底层实现(1)---C++版

目录 哈希表的基本原理 哈希表的优点 哈希表的缺点 应用场景 闭散列法 开散列法 开放定值法Open Addressing——线性探测的模拟实现 超大重点部分评析 链地址法Separate Chaining——哈希桶的模拟实现 哈希表&#xff08;Hash Table&#xff09;是一种数据结构&#x…

STM32G070 CubeMX配置多通道/单通道ADC+DMA流程 LL库

基础配置不再赘述&#xff0c;时钟这些根据硬件来配置 多通道ADCDMA配置图&#xff1a; 程序配置&#xff1a; 调试查看内存数据&#xff0c;硬件上将PA1接到GND&#xff0c;PA2接到3V3 采集的数据会循环覆盖内存 问题&#xff1a;代码里先初始化ADC_IN1&#xff0c;再初…

Spring扩展点系列-ApplicationContextAwareProcessor

文章目录 简介源码分析示例代码示例一&#xff1a;扩展点的执行顺序运行示例一 示例二&#xff1a;获取配置文件值配置文件application.properties内容定义工具类ConfigUtilcontroller测试调用运行示例二 示例三&#xff1a;实现ResourceLoaderAware读取文件ExtendResourceLoad…

CleanClip - 「CleanClip」是一款专为 Mac 设计的桌面剪贴板工具

官方介绍 欢迎使用 CleanClip —— Mac 上最简洁高效的剪贴板管理工具。CleanClip 专为追求简约操作体验的用户设计&#xff0c;它帮助用户记录系统剪贴板上的内容&#xff0c;并提供强大的分类管理能力&#xff0c;帮助你整理复制的内容&#xff0c;提高办公效率。 智能简洁&…

MAVEN如何导入项目

工作中经常需要导入他人的项目&#xff0c;那么如何导入呢&#xff1f; 1&#xff0c; 选择Maven面板&#xff0c;点 2&#xff0c;选中对应项目的pom.xml&#xff0c;双击即可 3&#xff0c;如果没有maven面板&#xff0c;可以选择view->Appearnce->Tool Window Bars…

HTML5元素定位

1.元素定位 为了实现网页整体布局&#xff0c;我们先要知道&#xff0c;一个元素&#xff0c;是如何定位到页面上的某个位置的&#xff0c;这就是元素定位。 元素定位有四种&#xff0c;可以使用position样式来设置元素定位&#xff0c;所以此属性值有四种&#xff1a; stat…

MybatisPlus新增数据时怎么返回新增数据的id

问&#xff1a;MybatisPlus新增数据时怎么返回新增数据的id&#xff1f;答&#xff1a;当插入操作执行后&#xff0c;MyBatis Plus会自动获取生成的ID并将其设置到传入的实体类对象的id属性中。当然&#xff0c;这需要你的表字段ID是自增的 实体类代码 public class Sites {p…