写这篇博客源于博友的提问,想提取图片中的红色圆圈坐标,并绘制封闭的轮廓。
1. 霍夫圆提取
2. 圆圈均是红色,可以转换HSV色彩空间提取
3. 应用一系列图像处理:灰度图、形态学操作、阈值化进行简单提取
一个一个尝试,霍夫圆不是很理想,调整了参数,也没有全部提取到,只提取到了部分。
本文将演示2,3俩种提取方法:
HSV色彩空间提取红色,需要对红色的HSV范围设置合理,才可以有比较好的提取效果。
使用一系列形态学图像处理,得到了比较理想的结果。
检测原始图 VS 效果图如下:
可以看到右图中,轮廓被用闭合的绿色轮廓连接起来。
检测过程1——原图 VS 剪裁后图如下:
检测过程2—— 灰度图 VS 白帽图 VS 梯度图 VS 形态学闭合图如下:
检测过程3——形态学闭合图 VS 二值化图1 VS 阈值化图2 如下:
识别过程3——轮廓过滤图 VS 提取最终效果图 如下:
轮廓过滤图:根据面积只保留够大的轮廓图(截取完的图片只包含俩个轮廓,无需过滤,用原始图则需要过滤)
最终效果图:俩个大的轮廓被成功提取
绘制检测到的轮廓的组成点为蓝色空心圆形,绘制轮廓为绿色矩形。
检测结果——裁剪后图检测效果图 VS 原图直接检测效果图对比:
可以看到当直接使用原图进行上述检测,坐标轴边界会有干扰,结果中会检测出多余的轮廓。
对红色的HSV空间设置合理后,发现转换HSV色彩空间提取的效果图也挺好。
原始图 VS 提取后绘制轮廓效果图如下:
检测过程图1:HSV图 VS HSV提取红色Mask图:
检测过程图2:轮廓膨胀图 VS 轮廓腐蚀图:
# 提取圆圈的轮廓,并绘制
import cv2
import imutils
import numpy as np
# 初始化矩形和方形结构内核
# 在图像上滑动它来进行(卷积)操作,如模糊、锐化、边缘检测或其他图像处理操作。
# 使用矩形函数作为Top-hat形态学运算符,使用方形函数作为闭合运算。
rectKernel = cv2.getStructuringElement(cv2.MORPH_RECT, (9, 3))
sqKernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5))
# 加载输入图像,转换为灰度图
origin = cv2.imread('images/circle.png')
image = origin.copy()
width = 75
height = 34
# # 只截取有轮廓的区域,避免坐标轴线等干扰
image = image[height:369, width:512]
cv2.imshow("origin", origin)
cv2.imshow("crop", image)
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
cv2.imshow("gray", gray)
# 执行形态学操作
# 应用tophat(白帽)形态学操作以在暗的背景中提取出亮的区域
# Top hat操作在深色背景(即信用卡号)下显示浅色区域
tophat = cv2.morphologyEx(gray, cv2.MORPH_TOPHAT, rectKernel)
cv2.imshow("tophat", tophat)
# 计算Scharr梯度,计算梯度值
# 在白色礼帽上,计算x方向的Scharr梯度,然后缩放到范围[0, 255]
gradX = cv2.Sobel(tophat, ddepth=cv2.CV_32F, dx=1, dy=0, ksize=-1)
gradX = np.absolute(gradX)
(minVal, maxVal) = (np.min(gradX), np.max(gradX))
# 最小/最大归一化, 由float转换gradX到uint8范围[0-255]
gradX = (255 * ((gradX - minVal) / (maxVal - minVal)))
gradX = gradX.astype("uint8")
cv2.imshow("gradient", gradX)
# 使用矩形框应用闭合操作以帮助闭合信用卡数字之间的小的缝隙
# 应用Otsu's阈值方法二值化图像
gradX = cv2.morphologyEx(gradX, cv2.MORPH_CLOSE, rectKernel)
cv2.imshow("morphologyEx", gradX)
thresh = cv2.threshold(gradX, 0, 255,
cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1]
cv2.imshow("thresh1", thresh)
# 在二值化图像上,应用二次闭合操作
# 再一次方形框形态学操作,帮助闭合小的的缝隙
thresh = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, sqKernel)
cv2.imshow("thresh2", thresh)
# 阈值图像中查找轮廓,然后初始化数字位置列表
cnts = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL,
cv2.CHAIN_APPROX_SIMPLE)
cnts = imutils.grab_contours(cnts)
print(len(cnts))
img = image.copy()
# 遍历轮廓
for (i, c) in enumerate(cnts):
area = cv2.contourArea(c)
print(i, area)
# 根据面积过滤掉不符合的轮廓
if (area < 4000):
continue
# print(' c: ', type(c), c.shape, c[0], type([c]))
cc = c.copy()
cc[:, :, 0] += width
cc[:, :, 1] += height
# print('cc: ', type(cc), cc.shape, cc[0])
for coor in c:
# 在轮廓的每个小圆上绘制蓝色圆圈
cv2.circle(img, (coor[0][0],coor[0][1]),5, (255, 0, 0), 1)
cv2.drawContours(origin, [cc], -1, (0, 255, 0), 2)
cv2.drawContours(image, [c], -1, (0, 255, 0), 2)
cv2.imshow("origin res", origin)
cv2.imshow("img res", img)
cv2.imshow("image res", image)
cv2.imwrite("images/crop_circle_extract.jpg",image)
cv2.waitKey(0)
# 将图像从一种颜色空间转换为另一种颜色空间,例如 BGR、RGB、Gray、HSV 等。
# 创建一个应用程序来提取视频中的彩色对象
# OpenCV 中有超过 150 种颜色空间转换方法可用,本文只介绍最经典的2中:BGR、HSV及互转
from collections import deque
import cv2
import imutils
# 遍历查看所有颜色空间
flags = [i for i in dir(cv2) if i.startswith('COLOR_')]
print(flags)
print(len(flags))
# 对于 HSV,色调范围为 [0,179],饱和度范围为 [0,255],值范围为 [0,255]。不同的软件使用不同的尺度。因此,如果您将 OpenCV 值与它们进行比较,则需要对这些范围进行归一化。
# 对象跟踪,可以使用HSV来提取彩色对象。这是最简单的对象追踪,找到物体的质心,就可以绘制轨迹来追踪物体;
import cv2
import numpy as np
origin = cv2.imread('images/circle.png')
frame = origin.copy()
origin1 = origin.copy()
cv2.imshow("origin", frame) # 转换BGR为HSV
hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
cv2.imshow("hsv", hsv)
# 定义绿色的HSV空间值
# lower_blue = np.array([29, 86, 6])
# upper_blue = np.array([64, 255, 255])
# 定义红色的HSV空间值
lower_red = np.array([-30, 43, 46])
upper_red = np.array([30, 255, 255])
# 阈值化图像,只获取红色
mask = cv2.inRange(hsv, lower_red, upper_red)
cv2.imshow("mask hsv", mask)
mask = cv2.dilate(mask, None, iterations=3)
cv2.imshow("mask dilate", mask)
mask = cv2.erode(mask, None, iterations=2)
cv2.imshow("mask erode", mask)
# 寻找mask中的轮廓,并初始化球当前的中心(x,y)
cnts = cv2.findContours(mask.copy(), cv2.RETR_EXTERNAL,
cv2.CHAIN_APPROX_SIMPLE)
cnts = imutils.grab_contours(cnts)
print('cnts: ', len(cnts))
center = None
pts = deque(maxlen=500)
# 发现至少一个轮廓继续处理
if len(cnts) > 0:
for c in cnts:
# 计算最小外接圆,质心
((x, y), radius) = cv2.minEnclosingCircle(c)
M = cv2.moments(c)
center = (int(M["m10"] / M["m00"]), int(M["m01"] / M["m00"]))
pts.appendleft(center)
# 绘制轮廓
cv2.drawContours(frame, [c], -1, (0, 255, 0), 2)
# 在帧上绘制红色球的外接圆及中心
# cv2.circle(frame, (int(x), int(y)), int(radius),
# (0, 255, 0), -1)
# cv2.circle(frame, (int(x), int(y)), 2,
# (255, 255, 0), 2)
cv2.imshow("Res", frame)
cv2.waitKey(0)
# 如何找到HSV空间值呢?
# 比如要寻找绿色,可以找到BGR(0,255,0) ,转换为HSV,然后取[ H-10,S,V]为下限 -- [ H+10,S,V]为上限;
green = np.uint8([[[0, 255, 0]]])
hsv_green = cv2.cvtColor(green, cv2.COLOR_BGR2HSV)
print(hsv_green)