一、环境搭建
环境搭建推荐肆十二的文章:手把手教你使用YOLOV5训练自己的目标检测模型-口罩检测-视频教程
里面有关YOLO v5前期的环境搭建讲解十分详细,且与本项目的YOLO v5前期环境搭建大差不差,这里不再赘述。
二、编码标记物数据集
编码标记物数据集采用labelimg手动标注的方法,详细数据集见:环形编码标记物数据集
编码标记物及相关数据集(部分)展示如下图:
三、编码标记物识别
通过YOLO v5网络实现编码标记物的智能识别与定位,即通过训练后的网络实现对编码标记物的识别,训练好的模型详见:best与last模型
识别结果如图所示,可见即使是对小而密集的编码标记物也有较好的识别效果:
四、编码标记物的解码
在完成了编码标记物的定位与识别之后,再需要在预测框图片的基础上完成编码标记物的解码工作,这里采用Opencv的辅助方法。
1. 循环识别图像获得码值
def getcode():
debug=True
img = cv2.imread('./images/tmp/test/tmp_upload.png') #图片路径
gray_all=img
contours_all=[]
contours_circle=[]
hierarchy_all=[]
gray_all = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
#gray_all=cv2.GaussianBlur(gray_all,)
ret,binary=cv2.threshold(gray_all,100,255,cv2.THRESH_BINARY)
gray_show=binary
# 使用findcontours函数对二值化后的图像进行轮廓提取,第三个参数为轮廓点的存储方式,这里选返回所有轮廓点,方便后面做筛选
contours_all,hierarchy_all= cv2.findContours(binary,cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
vec_center=[]
vec_radius=[]
vec_rrect=[]
#连续采样,计算码值,预设三个扫描起点,进行顺时针扫描,若三个扫描值相同,即为初步码值。
for i in range(0,len(contours_all)):
(x, y), radius = cv2.minEnclosingCircle(contours_all[i])
center = (int(x), int(y))
radius = int(radius)+0.0000001
contorarea=cv2.contourArea(contours_all[i])
ratiol=contorarea/(3.1415*radius*radius)
if (ratiol>0.9) and len(contours_all[i])>50:
contours_circle.append(contours_all[i])
vec_center.append(center)
vec_radius.append(radius)
if len(contours_all[i])>50:
fitelliprect=cv2.fitEllipse(contours_all[i])
(fitelliprectw,fitelliprecth)=fitelliprect[1]
minarearect=cv2.minAreaRect(contours_all[i])
(minarearectw,minarearecth)=minarearect[1]
fitrectarea=fitelliprectw*fitelliprecth
minarea=minarearectw*minarearecth
radio2=fitrectarea/minarea
if radio2>0.95 and radio2<1.05:
vec_rrect.append(fitelliprect)
for i in range(0,len(vec_center)):
rect=gray_show[int(vec_center[i][1] - 3 * vec_radius[i]):int(vec_center[i][1] + 3 * vec_radius[i]),
int(vec_center[i][0] - 3 * vec_radius[i]):int(vec_center[i][0] + 3 * vec_radius[i]) ]
t=rect
codenew=0
for scanpoint in range(0,3):
code=0
for range0 in range(0,8):
count=0
starpoint=range0*45+scanpoint*15
endpoint=(range0+1)*45+scanpoint*15
angle=starpoint
while angle<endpoint:
pointx=3*vec_radius[i]+(vec_radius[i] / 150 * 350) * math.cos(angle / 180.0 * 3.1415)
pointy=3*vec_radius[i] + (vec_radius[i] / 150 * 350) * math.sin(angle / 180.0 * 3.1415)
x=(t[int(pointx)][int(pointy)])
if x==255:
count=count+1
angle=angle+3
if count<=8:
code=code+math.pow(2,range0)
codenew=findmincode(int(code))
print(codenew)
if debug:
title=str(codenew)
cv2.imshow(title,t)
cv2.waitKey(0)
2. 码值后处理
因为环形编码标记物并没有固定起点,所以要对得到的码值进行循环移位以得到最小码值,即为最后的结果码值。
#码值选择函数(找到最小码值)
def findmincode(code):
minresult=255
for i in range(1,8):
left=circular_shift_left(code,i,8)
right=circular_shift_right(code,i,8)
result=min(left,right)
if(result<minresult):
minresult=result
return minresult
#辅助码值选择函数(左移位函数)
# int_value是输入的整数,k是位移的位数,bit是整数对应二进制的位数
def circular_shift_left(int_value, k, bit=8):
bit_string = '{:0%db}' % bit
bin_value = bit_string.format(int_value) # 8 bit binary
bin_value = bin_value[k:] + bin_value[:k]
int_value = int(bin_value, 2)
return int_value
#辅助码值选择函数(右移位函数)
# right circular shift
def circular_shift_right(int_value, k, bit=8):
bit_string = '{:0%db}' % bit
bin_value = bit_string.format(int_value) # 8 bit binary
bin_value = bin_value[-k:] + bin_value[:-k]
int_value = int(bin_value, 2)
return int_value