Python-OpenCV中的图像处理-图像轮廓
- 轮廓
- 什么是轮廓
- 查找轮廓
- 绘制轮廓
- 轮廓特征
- 图像的矩
- 轮廓面积
- 轮廓周长(弧长)
- 轮廓近似
- 凸包
- 轮廓边界矩形
轮廓
什么是轮廓
轮廓可以简单认为成将连续的点(连着边界)连在一起的曲线,具有相同的颜色或者灰度。轮廓在形状分析和物体的检测和识别中很有用。
- 为了更加准确,要使用二值化图像。在寻找轮廓之前,要进行阈值化处理或者 Canny 边界检测。
- 查找轮廓的函数会修改原始图像。如果你在找到轮廓之后还想使用原始图像的话,你应该将原始图像存储到其他变量中。
- 在 OpenCV 中,查找轮廓就像在黑色背景中超白色物体。你应该记住,要找的物体应该是白色而背景应该是黑色。
查找轮廓
函数 cv2.findContours() 有三个参数,第一个是输入图像,第二个是轮廓检索模式,第三个是轮廓近似方法。返回值有三个,第一个是图像,第二个是轮廓,第三个是(轮廓的)层析结构。轮廓(第二个返回值)是一个 Python列表,其中存储这图像中的所有轮廓。每一个轮廓都是一个 Numpy 数组,包含对象边界点( x, y)的坐标。
ret, thresh = cv2.threshold(imgray, 127, 255, 0)
contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
cv2.CHAIN_APPROX_NONE:储存所有的边界点(点数很多)
cv2.CHAIN_APPROX_SIMPLE:储存所有的近似直线点(点数很少)
绘制轮廓
函数 cv2.drawContours() 可以被用来绘制轮廓。它可以根据你提供的边界点绘制任何形状。它的第一个参数是原始图像,第二个参数是轮廓,一个 Python 列表。第三个参数是轮廓的索引(在绘制独立轮廓是很有用,当设置为 -1 时绘制所有轮廓)。接下来的参数是轮廓的颜色和厚度等。
- 绘制所有轮廓 image = cv2.drawContours(img, contours, -1, (0, 255, 0), 3)
- 绘制指定轮廓 image = cv2.drawContours(img, contours, 0, (0, 255, 0), 3)
import numpy as np
import cv2
# 轮廓:连着边界连续的点连在一起的曲线,具有相同的颜色或者灰度。
# 轮廓在形状分析和物体的检测和识别中很有用。
# 1.为了准确,要使用二值化图像。需要进行阀值化处理或Canny边界检测。
# 2.查找轮廓的函数会修改元素图像。
# 3.在OpenCV中,查找轮廓就像在黑色背景中找白色物体。
# cv2.findContours() # 查找轮廓
# cv2.drawContours() # 绘制轮廓
img = cv2.imread('./resource/image/opencv-logo2.png')
imgcp = cv2.imread('./resource/image/opencv-logo2.png')
imgray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
ret, thresh = cv2.threshold(imgray, 127, 255, 0)
contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
# contours 轮廓
# hierarchy 层次
# image = cv2.drawContours(img, contours, -1, (0, 255, 0), 3) # 绘制所有轮廓
image = cv2.drawContours(img, contours, 3, (0, 255, 0), 3) # 绘制第4个轮廓
print(cv2.getVersionString())
print(type(contours))
print(len(contours))
cv2.imshow('img', imgcp)
cv2.imshow('image', image)
cv2.waitKey(0)
cv2.destroyAllWindows()
轮廓特征
- 查找轮廓的不同特征:矩、面积、周长(也叫弧长)、重心、边界框等
图像的矩
- 在图像处理、计算机视觉和相关领域,图像矩是图像像素强度的某个特定的加权平均值(矩),或者是这种矩的函数,通常被选择为具有某种吸引人的特性或解释。图像矩在分割后对描述物体很有用。通过图像矩找到的图像的简单属性包括面积(或总强度)、其中心点和关于其方向的信息。
- 图像的矩可以帮助我们计算图像的质心,面积等。
- 函数cv2.moments()计算得到矩,返回一个字典。
根据矩值可以计算对象的重心:
C x = M 10 M 00 , C y = M 01 M 00 C_x=\frac{M_{10}}{M_{00}},C_y=\frac{M_{01}}{M_{00}} Cx=M00M10,Cy=M00M01
import numpy as np
import cv2
from matplotlib import pyplot as plt
# 矩:图像的矩可以帮助我们计算图像的质心,面积等
# cv2.moments() 计算得到矩,以一个字典形式返回
# 读取图像
img = cv2.imread('./resource/opencv/image/box2.png', cv2.IMREAD_COLOR)
img1 = img.copy()
gray = cv2.imread('./resource/opencv/image/box2.png', cv2.IMREAD_GRAYSCALE)
# 阀值处理
(ret, thresh) = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)
# 查找所有轮廓
(contours, hierarchy) = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
print('找到轮廓数:',len(contours))
# 计算轮廓索引为0的图像矩
cnt = contours[0]
M = cv2.moments(cnt)
print('moments()计数结果M:',M)
# 计算重心(质点)
#根据这些矩值计算出对象的重心:
# Cx = M10/M00
# Cy = M01/M00
cx = int(M['m10']/M['m00'])
cy = int(M['m01']/M['m00'])
print(cx,cy)
# 绘制轮廓
cv2.drawContours(img1, contours, 0, (0, 255, 0), 2)
# 绘制质点
cv2.circle(img1, (cx, cy), 5, (255, 0, 0), -1)
plt.subplot(121), plt.imshow(img)
plt.subplot(122), plt.imshow(img1)
plt.show()
程序运行结果:
找到轮廓数: 1
moments()计数结果M: {‘m00’: 10032.0, ‘m10’: 1294128.0, ‘m01’: 1284096.0, ‘m20’: 177807168.0, ‘m11’: 165648384.0, ‘m02’: 170838272.0, ‘m30’: 25740205920.0, ‘m21’: 22759317504.0, ‘m12’: 22038137088.0, ‘m03’: 23524638720.0, ‘mu20’: 10864656.0, ‘mu11’: 0.0, ‘mu02’: 6473984.0, ‘mu30’: 0.0, ‘mu21’: 0.0, ‘mu12’: 0.0, ‘mu03’: 0.0, ‘nu20’: 0.10795454545454546, ‘nu11’: 0.0, ‘nu02’: 0.06432748538011696, ‘nu30’: 0.0, ‘nu21’: 0.0, ‘nu12’: 0.0, ‘nu03’: 0.0}
质点: 129 128
下图红色圆点是质点,绿色框是轮廓:
轮廓面积
- 轮廓的面积可以使用函数 cv2.contourArea() 计算得到,也可以使用矩
( 0 阶矩), M[‘m00’]。 - area = cv2.contourArea(cnt)
import numpy as np
import cv2
from matplotlib import pyplot as plt
img = cv2.imread('./resource/opencv/image/box2.png', cv2.IMREAD_COLOR)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
(ret, thresh) = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)
(contours, hierarchy) = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
cnt = contours[0]
M = cv2.moments(cnt)
area = cv2.contourArea(cnt)
print(cnt)
print(area)
print(M['m00'])
[[[ 72 84]]
[[ 72 172]]
[[186 172]]
[[186 84]]]
10032.0
10032.0
轮廓周长(弧长)
- 也被称为弧长。可以使用函数 cv2.arcLength() 计算得到。这个函数的第二参数可以用来指定对象的形状是闭合的( True),还是打开的(一条曲线)。
- perimeter = cv2.arcLength(cnt,True)
import numpy as np
import cv2
img = cv2.imread('./resource/opencv/image/box2.png', cv2.IMREAD_COLOR)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
(ret, thresh) = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)
(contours, hierarchy) = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
# 第一个参数轮廓,第二次参数形状闭合的(True),还是打开的一条曲线
perimeter = cv2.arcLength(contours[0], True)
print(perimeter) #404.0
轮廓近似
- 将轮廓形状近似到另外一种由更少点组成的轮廓形状,新轮廓的点的数目由我们设定的准确度来决定。使用的Douglas-Peucker算法,维基百科获得更多此算法的细节。
- 假设要在一幅图像中查找一个矩形,但是由于图像的种种原因,我们不能得到一个完美的矩形,而是一个“坏形状”。现在你就可以使用这个函数来近似这个形状()了。
- epsilon = 0.1*cv2.arcLength(cnt,True)
approx = cv2.approxPolyDP(cnt,epsilon,True)
import numpy as np
import cv2
from matplotlib import pyplot as plt
img = cv2.imread('./resource/opencv/image/approx2.jpg', cv2.IMREAD_COLOR)
img_draw1 = img.copy()
img_draw2 = img.copy()
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
(ret, thresh) = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)
(contours, hierarchy) = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
# epsilon=10%
epsilon01 = 0.1 * cv2.arcLength(contours[0], True)
# epsilon=1%
epsilon001 = 0.01 * cv2.arcLength(contours[0], True)
approx_01 = cv2.approxPolyDP(contours[0], epsilon01, True)
approx_001 = cv2.approxPolyDP(contours[0], epsilon001, True)
cv2.drawContours(img_draw1, approx_01, -1, (0, 0, 255), 5)
cv2.drawContours(img_draw2, approx_001, -1, (0, 0, 255), 5)
plt.subplot(131), plt.imshow(img), plt.title('original')
plt.subplot(132), plt.imshow(img_draw1), plt.title('epsilon=10%')
plt.subplot(133), plt.imshow(img_draw2), plt.title('epsilon=1%')
plt.show()
凸包
轮廓边界矩形
import numpy as np
import cv2
from matplotlib import pyplot as plt
# 轮廓边界矩形
# 读取图像
img = cv2.imread('./resource/image/opencv-logo2.png', cv2.IMREAD_COLOR)
# 转成灰度
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 图像阀值处理
ret, thresh = cv2.threshold(img_gray, 127, 255, 0)
# 查找轮廓
contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
# 绘制轮廓边界矩形
for i in range(0, len(contours)):
x, y, w, h = cv2.boundingRect(contours[i])
print(x,y,w,h)
img = cv2.rectangle(img, (x,y), (x+w, y+h), (0,255,255), 3)
cv2.imshow('img', img)
cv2.waitKey(0)
cv2.destroyAllWindows()