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

文章目录

  • 前言
  • 获取凸包
  • 凸缺陷
  • 几何学测试
    • 测试轮廓是否是凸形的
    • 点到轮廓的距离
  • 形状场景算法比较轮廓
  • 轮廓的特征值
    • 宽高比
    • 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 cv2o = 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 cv2o = 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 cv2o = 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 npo = 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 npo = 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.rhkb.cn/news/421603.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

VR 尺寸美学主观评价-解决方案-现场体验研讨会报名

棣拓科技VR创新解决方案助力尺寸美学所见即所得! 诚邀各位行业专家莅临指导交流 请扫描海报二维码踊跃报名,谢谢 中国上海 2024.10.25 亮点介绍 1、通过精湛渲染技术,最真实展现设计效果,并通过VR设备一比一比例进行展现。 2、设置相关设…

ctfshow-PHP反序列化

web254 源码 <?php/* # -*- coding: utf-8 -*- # Author: h1xa # Date: 2020-12-02 17:44:47 # Last Modified by: h1xa # Last Modified time: 2020-12-02 19:29:02 # email: h1xactfer.com # link: https://ctfer.com //mytime 2023-12-4 0:22 */ error_reporting(0)…

谈谈PCIe VID、DID、SSID、SSVID背后的智慧

PCIe Vendor ID 想了半天还是觉得从“ID是什么”这个问题开始比较好。那么ID是什么&#xff1f;ID就是身份。那身份又是什么&#xff1f;身份就是一个合理存在&#xff0c;用于区分不同个体。为什么叫“合理存在”呢&#xff1f;如果国家不给你发身份证&#xff0c;你就是黑户…

[笔记]电参数测量的现有方案

1.关键字&#xff1a; 电参数测量 Electrical Parameter Measurement 2.相关信息搜集 》》电参数测量仪是如何测量电压电流相位差的&#xff1f;对于变频器那种比较毛的波形&#xff0c;也能测量&#xff1f; 电参数测量仪测量电压电流相位差的方法主要依赖于其内部的高精度…

信号保存和处理

把上一篇回顾一下吧&#xff1a;共享内存区是最快的IPC形式。一旦这样的内存映射到共享它的进程的地址空间&#xff0c;这些进程间数据传递不再涉及到内核&#xff0c;进程不再通过执行进入内核的系统调用来传递彼此的数据 共享内存的数据结构&#xff1a; struct shmid_ds {…

Pycharm使用debug运行时,一直显示collecting data...,但是变量一直显示不出来,显示超时

一、问题&#xff1a; 二、解决办法 1.File—>Setting 2.Build---->Python Debugger 3.勾选Gevent compatible &#xff0c;然后Apply 三、解释Gevent compatible 1.在 PyCharm 中&#xff0c;Gevent compatible 通常与 gevent 库的兼容性设置有关。gevent 是一个基于协…

NC字典树的实现

系列文章目录 文章目录 系列文章目录前言 前言 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到网站&#xff0c;这篇文章男女通用&#xff0c;看懂了就去分享给你的码吧。 描述 字典树又称为…

Ubuntu系统修改静态IP

1.先查看一下我们的ip rootcaiji:~# ip a 2.查看此时的网卡配置文件 rootcaiji:~# cat /etc/netplan/00-installer-config.yaml # This is the network config written by subiquity network: ethernets: ens33: dhcp4: true version: 2 此时可以看出来dhcp…

SpringCloud Alibaba入门简介

1、诞生 2018.10.31&#xff0c;Spring Cloud Alibaba 正式入驻了 Spring Cloud 官方孵化器&#xff0c;并在 Maven 中央库发布了第一个版本。 2、是什么&#xff0c;去哪下 官网&#xff1a;Spring Cloud Alibaba官网_基于Springboot的微服务教程-阿里云-阿里云Spring Cloud …

【论文阅读笔记】Tackling the Generative Learning Trilemma with Denoising Diffusion GANs

【论文阅读笔记】Tackling the Generative Learning Trilemma with Denoising Diffusion GANs Introduction方法 使用传统GANS建模去噪分布理解模式覆盖率 Paper&#xff1a;https://arxiv.org/abs/2112.07804 Code&#xff1a;https://github.com/NVlabs/denoising-diffusion-…

Docker启动Mysql镜像报错问题?

docker中启动mysql镜像报错如下&#xff1a;ls: cannot access /docker-entrypoint-initdb.d/: Operation not permitted 百度上查到了很多解决方案&#xff0c;也咨询了很多大佬&#xff0c;加权限&#xff0c;改用户&#xff0c;均无果。最终在阿里巴巴上找到了解决方案&…

[论文笔记]ChatQA: Surpassing GPT-4 on Conversational QA and RAG

引言 今天来看一下上篇论文笔记中反复介绍的 ChatQA: Surpassing GPT-4 on Conversational QA and RAG。 为了简单&#xff0c;下文中以翻译的口吻记录&#xff0c;比如替换"作者"为"我们"。 我们介绍了 ChatQA&#xff0c;这是一个模型套件&#xff0c;一…

算法设计(一)

1.汉诺塔 介绍 汉诺塔&#xff08;Hanoi Tower&#xff09;:它描述了如何将一堆大小不同、穿在一根柱子上的盘子移动到另一根柱子上&#xff0c;同时满足以下规则&#xff1a; 每次只能移动一个盘子。 每个移动盘子时&#xff0c;大的盘子不能放在小的盘子上面。 可以使用一根…

curl和ping

curl获取页面内容&#xff0c;ping测试连通 curl和ping是两个在网络环境中常用的命令行工具&#xff0c;但它们的目的和应用场景有很大的不同。 curl 用途&#xff1a;curl是一个命令行工具&#xff0c;用于传输数据&#xff0c;支持多种协议&#xff0c;包括HTTP、HTTPS、FT…

1. 运动控制指令概要(omron 机器自动化控制器)

机器自动化控制器——第一章 运动控制指令概要 1-1 运动控制指令PLCopen运动控制用功能块运动控制指令概要▶ 运动控制指令的种类▶ 状态变化▶ 运动控制指令的启动和状态▶ 异常处理▶ 执行运动控制指令时输入变量的变更(指令重启)▶ 通过选择缓存模式执行指令多重启动▶ 通过…

【生成模型系列(初级)】嵌入(Embedding)方程——自然语言处理的数学灵魂【通俗理解】

【通俗理解】嵌入&#xff08;Embedding&#xff09;方程——自然语言处理的数学灵魂 关键词提炼 #嵌入方程 #自然语言处理 #词向量 #机器学习 #神经网络 #向量空间模型 #Siri #Google翻译 #AlexNet 第一节&#xff1a;嵌入方程的类比与核心概念【尽可能通俗】 嵌入方程可以…

C语言程序设计——函数(一)

一、概述 一个较大的程序可分为若干个程序模块,每一个模块用来实现一个特定的功能。在高级语言中用子程序实现模块的功能。子程序由函数来完成。一个C程序可由一个主函数和若干个其他函数构成。 由主函数调用其他函数,其他函数也可以互相调用。同一个函数可以被一个或多个函…

C语言-数据结构 无向图克鲁斯卡尔算法(Kruskal)邻接矩阵存储

相比普里姆算法来说&#xff0c;克鲁斯卡尔的想法是从边出发&#xff0c;不管是理解上还是实现上都更简单&#xff0c;实现思路&#xff1a;我们先把找到所有边存到一个边集数组里面&#xff0c;并进行升序排序&#xff0c;然后依次从里面取出每一条边&#xff0c;如果不存在回…

99.游戏安全项目-可见数据的搜索与技巧

免责声明&#xff1a;内容仅供学习参考&#xff0c;请合法利用知识&#xff0c;禁止进行违法犯罪活动&#xff01; 内容参考于&#xff1a;易道云信息技术研究院 上一个内容&#xff1a;98.游戏的启动与多开-分析与实现多开器 下图中红框位置显示的数据&#xff0c;只有下图…

横版闯关手游【全明星时空阿拉德】Linux手工服务端+运营后台+双app端

横版闯关手游【时空阿拉德】&#xff08;【全明星阿拉德】&#xff09;阿拉德系列2022整理Linux手工服务端余额充值后台安卓苹果双端。 运营后台看目录结构是thinkphp开发的。 代码免费下载&#xff1a;百度网盘