提取目标区域并标注
前言
考核题
一、blob分析
名词解释:计算机视觉中的Blob是指图像中的一块连通区域,Blob分析就是对前景/背景分离后的二值图像,进行连通域提取和标记。标记完成的每一个Blob都代表一个前景目标,然后就可以计算Blob的一些相关特征。
搞清楚了这一点,就知道师兄给的这张图是在为我们减小难度
前景与后景的差别非常强烈
上代码
import cv2
import numpy as np
img = cv2.imread('zuobiao.jpg')
imgray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)#读取并处理为单通道图片,为后续做准备
ret, binary = cv2.threshold(imgray, 127, 255, 0)#阈值处理
contours, hierarchy = cv2.findContours(binary, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)#查找检测物体的轮廓
for c in contours:
(x, y, w, h) = cv2.boundingRect(c)
img = cv2.rectangle(img, (x, y), (x + w, y + h), (0, 255, 255), 1)
for i in range(len(contours)):
mom = cv2.moments(contours[i])#计算中心矩
pt = (int(mom['m10'] / mom['m00']), int(mom['m01'] / mom['m00'])) # 使用前三个矩m00, m01和m10计算重心
cv2.circle(img, pt, 2, (0, 0, 255), 2) # 画红点
cv2.line(img, (x, y),((pt[0]),(pt[1])), (0, 0, 255),1)#画半径
text = "(" + str(pt[0]) + ", " + str(pt[1]) + ")"#添加的文字
cv2.putText(img, text, (pt[0]+10, pt[1]+10), cv2.FONT_HERSHEY_PLAIN, 1.5, (255, 255, 255), 2, 8, 0);#加坐标
print("--------- HL分析区域并标注的测试 ---------")
cv2.imshow("center", img)
cv2.waitKey(0)
cv2.destroyAllWindows()
太多库函数注释要写,放到代码里面会显得头重脚轻,索性单句拿出来一句句分析
首先运行以上代码
会发现这样一个错误
值错误:没有足够的值解包(应为3,得到2)
OpenCV4.0的说明文档中
我们只需把这句
contours, hierarchy = cv2.findContours(binary, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
改为
image, contours, hierarchy = cv2.findContours(binary, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
即加上一个参数就好
先放效果图
好的,下面将一些代码注释与用法总结如下
二、有关代码函数的注释与用法
1、cv2.threshold()阈值处理
imgray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)#读取并处理为单通道图片,为后续做准备
ret, thresh = cv2.threshold(imgray, 127, 255, 0)#阈值处理
此语句的原型为
cv2.threshold (src, thresh, maxval, type)
Attention:
src:源图片必须是单通道
thresh:阈值,取值范围0~255
maxval:填充色,取值范围0~255
type:阈值类型,具体见下
阈值 | 小于阈值的像素点 | 大于阈值的像素点 |
---|---|---|
0 | 置0 | 置填充色 |
1 | 置填充色 | 置0 |
2 | 保持原色 | 置灰色 |
3 | 置0 | 保持原色 |
4 | 保持原色 | 置0 |
因为要进行阈值处理,所以在前面一步需将我的彩色图片变为单通道,也就是灰色
为了深刻理解这个概念,运行是最直观的理解
代码如下:
import cv2
img = cv2.imread('pic.jpg',0)#预处理变为单通道图片
cv2.imshow('org',img)
# 运用二值化处理图片
retVal,img_after = cv2.threshold(img, 100 ,100, cv2.THRESH_BINARY)
cv2.imshow('org_retVal',img_after)
print("变化之前的图像是:\n",img)
print("变化之后的图像是:\n",img_after)
cv2.waitKey(0)
cv2.destroyAllWindows()
可以看出,图像中像素大于100的全变成了100,这是因为阈值和填充色我选择了100,而小于100的应该变成0(由于这张图片没有小于100的…所以全为100…)
啊?你说无法说明?emmm那好吧,再来,观察数据,我选择设阈值为110
这里就能充分说明了,小于110的部分已经置为0了,而大于的部分呢,都变为了100。
在考核题的那串代码中,我将阈值设为127,也就是中间值,而填充色设为255,形成鲜明反差,有利于后续处理。
2、cv2.findContours()轮廓检测
image, contours, hierarchy = cv2.findContours(binary, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
python3这里返回三个值:image,contours,hierarchy
- image:一张二值图(binary),但不是灰度图像,应当是黑白图,只有0和1;
- contours:list结构,列表中每个元素代表一个边沿信息。每个元素是(x,1,2)的三维向量,x表示该条边沿里共有多少个像素点,第三维的那个“2”表示每个点的横、纵坐标;
- hierarchy:返回类型是(x,4)的二维ndarray。x和contours里的x是一样的意思。如果输入选择cv2.RETR_TREE,则以树形结构组织输出,hierarchy的四列分别对应下一个轮廓编号、上一个轮廓编号、父轮廓编号、子轮廓编号,该值为负数表示没有对应项;
括号中的参数我绘制了一个表格如下表:
cv2.RETR_EXTERNAL | 只检测外轮廓 |
---|---|
cv2.RETR_LIST | 只检测外轮廓 |
cv2.RETR_CCOMP | 建立两个等级的轮廓,上面的一层为外边界,里面的一层为内孔的边界信息。(如果内孔内还有一个连通物体,这个物体的边界也在顶层) |
cv2.RETR_TREE | 建立一个等级树结构的轮廓 |
简单试试这个函数
import cv2
img = cv2.imread('pic.jpg')
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
ret, binary = cv2.threshold(gray,127,255,cv2.THRESH_BINARY)
cv2.imshow("img", binary)
image,contours, hierarchy = cv2.findContours(binary,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)
cv2.drawContours(img,contours,-1,(0,0,225),1)
print("--------- HL边缘检测的测试 ---------")
cv2.imshow("binary", binary)
cv2.waitKey(0)
3、cv2.boundingRect©矩形边框
(x, y, w, h) = cv2.boundingRect(c)
矩形边框(Bounding Rectangle)是用一个最小的矩形,把找到的形状包起来。
在cv2.boundingRect(cnt)这个函数中
- cnt是一个轮廓点集合,也就是它的参数,可以通过cv2.findContours获取;
- 返回四个值,分别是x,y,w,h;
- x,y是矩阵左上点的坐标;
- w,h是矩阵的宽和高;
img = cv2.rectangle(img, (x, y), (x + w, y + h), (0, 255, 255), 1)
函数原型:cv2.rectangle(img, pt1, pt2, color[, thickness[, lineType[,
shift]]])
- img:要画的圆所在的矩形或图像
- pt1:矩形左上角的点
- pt2:矩形右下角的点
- color:线条颜色,如 (0, 255, 255) 黄色,BGR
- thickness:线条宽度
- lineType:8 (or omitted) : 8-connected line 4:4-connected line CV_AA - antialiased line
- shift:坐标点小数点位数
4、cv2.moments()图像的矩
参数如下:
图像的矩可以帮助我们计算图像的质心,面积等
M = cv2.moments(cnt)
根据这些矩的值,我们可以计算出对象的重心:
cx = int(M[‘m10’]/M[‘m00’])
cy = int(M[‘m01’]/M[‘m00’])
pt = (int(mom['m10'] / mom['m00']), int(mom['m01'] / mom['m00'])) # 使用前三个矩m00, m01和m10计算重心
5、cv2.circle()画圆心
函数原型:cv2.circle(img, center, radius, color[, thickness[, lineType[,
shift]]])
作用就是根据给定的圆心和半径等画圆
- img:输入的图片data
- center:圆心位置
- radius:圆的半径
- color:圆的颜色
- thickness:圆形轮廓的粗细(如果为正)。负厚度表示要绘制实心圆
- lineType: 圆边界的类型。
- shift:中心坐标和半径值中的小数位数。
cv2.circle(img, pt, 2, (0, 0, 255), 2)
画红点啦当然通道应为(0,0,255)红色,过年就要喜气~
6、 cv2.line()画直线
cv2.line(img, (x, y),((pt[0]),(pt[1])), (0, 0, 255),1)
这个是我自作主张加上去的…
函数原型:img=cv.line(img, pt1, pt2, color[, thickness[, lineType[, shift]]])
用途:给定一个图像img,连接点pt1和pt2的坐标,在图中画一条直线,color表明线的颜色,thickness是线条粗细
所以我的起点是前面刚画好的圆心,终点是边框的一个点,用红色线连接,细细的一根。
7、cv2.putText()写文字
text = "(" + str(pt[0]) + ", " + str(pt[1]) + ")"
cv2.putText(img, text, (pt[0]+10, pt[1]+10), cv2.FONT_HERSHEY_PLAIN, 1.5, (255, 255, 255), 2, 8, 0);
因为数值未知,所以用了text来引用,再放到该函数里表示。