赞
踩
在上周的报告中说道,在目前的施工环境下,混凝土坍落扩展度是在一个固定尺寸测量盘上完成的。由于测量盘的真实尺寸是为我们所知的,同时测量盘具有比较独特的金属色泽,因此从图像识别的角度,测量盘是一个比较好的参考物。这将为我们的图像中物体的尺寸识别提供可能。
也就是说,为了确定图像中物体的大小,我们需要有一个参照物,这个参照物应该具有下述两种性质:
1.这个参照物的真实尺寸是为我们所知的。
2.这个参照物能被较容易的找到。
混凝土测量盘就非常符合这两个条件!
由于目前暂无途径接触到实际的混凝土坍落拓展度测量过程,因此用家里的简易道具进行模拟,如下图所示。
其中铁盆模拟测量板,直径经测量为26cm,测试中将其直径设置为260cm;中间海绵模拟混凝土,长宽经测量后为12.7cm×9.0cm,测试中设置为127cm×90cm。
`# 插入包
from scipy.spatial import distance as dist
from imutils import perspective
from imutils import contours
import numpy as np
import argparse
import imutils
import cv2
首先插入程序所需要的包
def midpoint(ptA, ptB):
return ((ptA[0] + ptB[0]) * 0.5, (ptA[1] + ptB[1]) * 0.5)
定义一个函数,用来找中点。
# 设置arg,方便调试
ap = argparse.ArgumentParser()
ap.add_argument("-i", "--image", required=True,
help="path to the input image")
ap.add_argument("-w", "--width", type=float, required=True,
help="width of the left-most object in the image (in inches)")
args = vars(ap.parse_args())
这里是为了程序调试方便,直接在终端输入相关命令即可调试
# 加载图像,转化灰度,同时用高斯过滤器过滤噪声
image = cv2.imread(args["image"])
image = cv2.resize(image,(0,0),fx=0.1,fy=0.1)
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
gray = cv2.GaussianBlur(gray, (7, 7), 0)
读取图像,并将图像进行缩放,将图像转化为灰度图,再用高斯过滤器对图像进行过滤,滤去噪音。处理后的图像如下:
# 利用canny功能对边缘进行识别,同时利用腐蚀和膨胀功能对图像进行形态处理
edged = cv2.Canny(gray, 50, 100)
edged = cv2.dilate(edged, None, iterations=1)
edged = cv2.erode(edged, None, iterations=1)
这里是对图像进行形态处理,包括利用canny功能对边缘进行识别,同时利用腐蚀和膨胀功能进一步边界进行处理。处理后图像如下:
# 对轮廓点进行寻找
cnts = cv2.findContours(edged.copy(), cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[1] if imutils.is_cv3() else cnts[0]
pixelsPerMetric = None
这里对轮廓点进行寻找,RETR_TREE是返回所有轮廓,CHAIN_APPROX_SIMPLE是压缩长、宽和对角线方向的点,最终只返回四个点。
该段代码最后一句对pixelsPerMetric进行了定义。
结果如图所示:
for c in cnts: # 过滤过小的区域,过滤噪声 if cv2.contourArea(c) < 1000: continue # 给轮廓加上外框 orig = image.copy() box = cv2.minAreaRect(c) box = cv2.cv.BoxPoints(box) if imutils.is_cv2() else cv2.boxPoints(box) box = np.array(box, dtype="int") # 旋转外框,使之契合实际物体 box = perspective.order_points(box) cv2.drawContours(orig, [box.astype("int")], 0, (0, 255, 0), 2) for (x, y) in box: cv2.circle(orig, (int(x), int(y)), 5, (0, 0, 255), -1) # 找中点 (tl, tr, br, bl) = box (tltrX, tltrY) = midpoint(tl, tr) (blbrX, blbrY) = midpoint(bl, br) # 找中点的中点 (tlblX, tlblY) = midpoint(tl, bl) (trbrX, trbrY) = midpoint(tr, br) # 把中点在图像中画出来并连接 cv2.circle(orig, (int(tltrX), int(tltrY)), 5, (255, 0, 0), -1) cv2.circle(orig, (int(blbrX), int(blbrY)), 5, (255, 0, 0), -1) cv2.circle(orig, (int(tlblX), int(tlblY)), 5, (255, 0, 0), -1) cv2.circle(orig, (int(trbrX), int(trbrY)), 5, (255, 0, 0), -1) cv2.line(orig, (int(tltrX), int(tltrY)), (int(blbrX), int(blbrY)), (255, 0, 255), 2) cv2.line(orig, (int(tlblX), int(tlblY)), (int(trbrX), int(trbrY)), (255, 0, 255), 2)
由上图可知,找到的轮廓中有许多噪声,影响结果的输出,因此我们可以利用contourArea函数计算轮廓区域面积,对过小的进行过滤。
之后利用Box函数给轮廓套矩形,并旋转矩形使得它能以最小的面积贴合轮廓,从而与实际相符。同时,我们还将把矩形的中点画出来,并进行连接。
# 计算中点间的欧几里得距离 dA = dist.euclidean((tltrX, tltrY), (blbrX, blbrY)) dB = dist.euclidean((tlblX, tlblY), (trbrX, trbrY)) # 如果pixelsPerMetric没有被定义,则将固定值赋给dB,这样就确定了每个像素在现实中对应的实际距离 if pixelsPerMetric is None: pixelsPerMetric = dB / args["width"] # 识别物体尺寸 dimA = dA / pixelsPerMetric dimB = dB / pixelsPerMetric # 将物体尺寸标在图像中 cv2.putText(orig, "{:.1f}cm".format(dimA), (int(tltrX - 15), int(tltrY - 10)), cv2.FONT_HERSHEY_SIMPLEX, 0.65, (255, 255, 255), 2) cv2.putText(orig, "{:.1f}cm".format(dimB), (int(trbrX + 10), int(trbrY)), cv2.FONT_HERSHEY_SIMPLEX, 0.65, (255, 255, 255), 2) # 显示图像 cv2.imshow("Image", res) cv2.waitKey(0) # 寻找海绵的尺寸 if a > dimA: a = dimA if b > dimB: b = dimB
我们先利用euclidean方法测出中点中的欧几里得距离,此时再判断pixelsPerMetric,如果没有被定义,则将固定值赋给dB(由于轮廓默认排序中最外围的是第一个,所以固定值肯定是赋予铁盆的直径)。这样我们就得到了每个像素所对应的实际距离,并可据此对物体尺寸进行识别。结果如下:
结果显示,海绵的尺寸为126.6×89.8,与原尺寸127×90相比,误差在5mm以内,属于可以接受的范围。
同时我们也可以看到,虽然滤去了部分噪声,由于拍摄时的光影问题,程序可能会错误地识别一些轮廓,而为了解决这个问题。可以寻找这些数据中的最小值,即对应海绵的尺寸。最终返回值如下:
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。