# coding:utf-8
import math
import cv2
import numpy as np
from labelutils.plots import Annotator, colors
global DPI
DPI = 0.00245
def get_distance_point2line(point, line_ab): # 求点到直线的距离
"""
Args:
point: [x0, y0]
line_ab: [k, b]
"""
k, b = line_ab
distance = abs(k * point[0] - point[1] + b) / math.sqrt(k ** 2 + 1)
return distance
def drawLines(img0, allCirclesCenter, wxdis_thres, DPI):
img = img0.copy()
# 所有点按照x轴排序elem[0] y轴排序elem[1]
def takeFirst(elem):
return elem[0]
allCirclesCenter.sort(key=takeFirst) #
# 计算两排点的中心线
nptest = np.array(allCirclesCenter)
line = cv2.fitLine(nptest, cv2.DIST_L2, 0, 0.001, 0.0)
k = line[1] / line[0]
b = line[3] - k * line[2]
print("k is ", k)
# 区分上下各点
line1_yx, line2_yx = [], []
if -10e5 < k < 10e5: # 如果是水平线,可以正常判断出上下点
# print('中间线line0: \ny = {:0.8f}x + {:0.8f}'.format(k[0], b[0]))
print('中间线line0: \ny = {:0.8f}x + {:0.8f}'.format(k[0], b[0]))
ptStart, ptEnd = (int(allCirclesCenter[0][0]), int(k * allCirclesCenter[0][0] + b)), (int(allCirclesCenter[-1][0]), int(k * allCirclesCenter[-1][0] + b))
cv2.line(img, ptStart, ptEnd, (0, 0, 255), thickness=2, lineType=3) # 画线
# cv2.imwrite('./test/lin0_gray.png', img)
# 区分上下各点,拟合两条直线
for i in allCirclesCenter:
if i[1] < float(k * i[0] + b):
line1_yx.append(i)
else:
line2_yx.append(i)
else: # 对于斜率过大的直线,寻找x坐标相距最远的两个点,得到中心线,根据x坐标区分各点(其实也不影响拟合直线,只不过需要根据x轴做判断)
print('中间线line0: \ny = {:0.8f}x + {:0.8f}'.format(k[0], b[0]))
ptStart, ptEnd = (int((0-b)/k), 0), (int((img.shape[0]-b)/k), img.shape[0]) # 根据y值画线
cv2.line(img, ptStart, ptEnd, (0, 0, 255), thickness=2, lineType=3)
minx_point, maxx_point = allCirclesCenter[0], allCirclesCenter[0]
for po1 in allCirclesCenter:
if po1[0] < minx_point[0]:
minx_point = po1
if po1[0] > maxx_point[0]:
maxx_point = po1
line0_x = (maxx_point[0] - minx_point[0])/2 + minx_point[0]
for i in allCirclesCenter:
if i[0] < line0_x:
line1_yx.append(i)
else:
line2_yx.append(i)
is_defect = 0
annotator1 = Annotator(img, line_width=4, example='distance: ') # 初始化生成框函数
# line1 求第一条直线###################
nptest1 = np.array(line1_yx)
line1 = cv2.fitLine(nptest1, cv2.DIST_L2, 0, 0.01, 0.0)
k1 = line1[1] / line1[0]
b1 = line1[3] - k1 * line1[2]
print('第1排line1: \ny = {:0.8f}x + {:0.8f}'.format(k1[0], b1[0]))
if -10e5 < k < 10e5: # 若斜率不大
def takeFirst(elem):
return elem[0]
line1_yx.sort(key=takeFirst)
# pd_1 = (abs(line1_yx[-1][0] - line1_yx[0][0]) / (len(line1_yx) - 1))
# pd_2 = 0
# for pd_i in range(len(line1_yx)):
# if pd_i < len(line1_yx) - 2:
# pd_2 += ((line1_yx[pd_i + 1][0] - line1_yx[pd_i][0]) / (len(line1_yx) - 1))
# pd_3 = (pd_1 + pd_2) / 2 # 判断针孔沿直线方向歪斜的判断标准
pd_1, pd_2 = 0, 0
dis_pdi = []
for pd_i in range(len(line1_yx)):
if pd_i < len(line1_yx) - 2:
dis_pdi.append(math.sqrt(pow(line1_yx[pd_i + 1][1] - line1_yx[pd_i][1], 2) + pow(line1_yx[pd_i + 1][0] - line1_yx[pd_i][0], 2)))
# pd_2 += ((line1_yx[pd_i + 1][0] - line1_yx[pd_i][0]) / (len(line1_yx) - 1))
for dis_i in dis_pdi:
pd_1 += (dis_i / len(dis_pdi)) # 平均值
print(pd_1)
ptStart, ptEnd = (int(line1_yx[0][0]), int(k1 * line1_yx[0][0] + b1)), (int(line1_yx[-1][0]), int(k1 * line1_yx[-1][0] + b1))
cv2.line(img, ptStart, ptEnd, (255, 0, 0), 2)
# 计算各点到对应直线的距离,如果从超过最大距离判断为缺陷(********需要使用标定板,计算图片对应的实际距离)
for i in line1_yx: # 可以显示存在缺陷的点的位置
distance = abs(k1 * i[0] - i[1] + b1) / math.sqrt(k1 ** 2 + 1)
# 在原图绘制点的偏差值
# cv2.putText(img, 'distance: ' + str(distance[0]), (int(i[0] + 5), int(i[1] + 5)), 0, 2, (0, 255, 0), thickness=3)
# a = int(distance[0])
if round((distance[0] * DPI), 2) >= 0.05:
# pass
annotator1.box_label([int(i[0]), int(i[1]), int(i[0] + 10), int(i[1] + 10)], f'{round((distance[0] * DPI), 2)}', color=(0, 255, 0))
# cv2.namedWindow("img", cv2.WINDOW_NORMAL) # 显示大分辨率图片
# cv2.imshow('img', img)
# cv2.waitKey(0)
# cv2.imwrite('./txt.png', img)
if (distance * DPI) > wxdis_thres: # 判断不合格的距离,具体范围需要调整***********
print("第1排点存在不合格: ({},{})".format(str(i[0]), str(i[1])))
# cv2.circle(img, (int(i[0]), int(i[1])), 4, (0, 0, 255), 2) # 显示有缺陷的针孔
cv2.rectangle(img, (int(i[0]-25), int(i[1])-25), (int(i[0] + 25), int(i[1] + 25)), (0, 0, 255), 2)
is_defect = 1
# 判断针孔沿直线方向歪斜
for i in range(1, len(line1_yx)):
if ((abs(line1_yx[i][0]-line1_yx[i-1][0])-pd_1) * DPI) >= wxdis_thres:
print("第1排点直线方向存在不合格: ({},{})".format(str(line1_yx[i][0]), str(line1_yx[i][1])))
cv2.rectangle(img, (int(line1_yx[i][0] - 25), int(line1_yx[i][1] - 25)), (int(line1_yx[i][0] + 25), int(line1_yx[i][1] + 25)), (0, 0, 255), 2)
is_defect = 1
else:
def takeSecond(elem):
return elem[1]
line1_yx.sort(key=takeSecond)
# pd_1 = (abs(line1_yx[-1][1] - line1_yx[0][1]) / (len(line1_yx) - 1))
# pd_2 = 0
# for pd_i in range(len(line1_yx)):
# if pd_i < len(line1_yx) - 2:
# pd_2 += ((line1_yx[pd_i + 1][1] - line1_yx[pd_i][1]) / (len(line1_yx) - 1))
# pd_3 = (pd_1 + pd_2) / 2 # 判断针孔沿直线方向歪斜的判断标准
pd_1, pd_2 = 0, 0
dis_pdi = []
for pd_i in range(len(line1_yx)):
if pd_i < len(line1_yx) - 2:
dis_pdi.append(math.sqrt(pow(line1_yx[pd_i + 1][1] - line1_yx[pd_i][1], 2) + pow(line1_yx[pd_i + 1][0] - line1_yx[pd_i][0], 2)))
# pd_2 += ((line1_yx[pd_i + 1][0] - line1_yx[pd_i][0]) / (len(line1_yx) - 1))
for dis_i in dis_pdi:
pd_1 += (dis_i / len(dis_pdi)) # 平均值
print(pd_1)
ptStart, ptEnd = (int((0-b1)/k1), 0), (int((img.shape[0]-b1)/k1), img.shape[0])
cv2.line(img, ptStart, ptEnd, (255, 0, 0), 2)
for i in line1_yx: # 可以显示存在缺陷的点的位置
distance = abs(i[0] - int((i[1]-b1)/k1))
# 在原图绘制点的偏差值
cv2.putText(img, str(distance[0]), (int(i[0] + 5), int(i[1] + 5)), 0, 2, (0, 255, 0), thickness=3)
if round((distance[0] * DPI), 2) >= 0.05:
# pass
annotator1.box_label([int(i[0]), int(i[1]), int(i[0] + 10), int(i[1] + 10)], f'{round((distance[0] * DPI), 2)}', color=(0, 255, 0))
if (distance * DPI) > wxdis_thres: # 判断不合格的距离,具体范围需要调整***********
print("第1排点存在不合格: ({},{})".format(str(i[0]), str(i[1])))
# cv2.circle(img, (int(i[0]), int(i[1])), 4, (0, 0, 255), 2) # 显示有缺陷的针孔
cv2.rectangle(img, (int(i[0] - 25), int(i[1]) - 25), (int(i[0] + 25), int(i[1] + 25)), (0, 0, 255), 2)
is_defect = 1
# 判断针孔沿直线方向歪斜
for i in range(1, len(line1_yx)):
if ((abs(line1_yx[i][1]-line1_yx[i-1][1])-pd_1) * DPI) >= wxdis_thres:
print("第1排点直线方向存在不合格: ({},{})".format(str(line1_yx[i][0]), str(line1_yx[i][1])))
cv2.rectangle(img, (int(line1_yx[i][0] - 25), int(line1_yx[i][1] - 25)), (int(line1_yx[i][0] + 25), int(line1_yx[i][1] + 25)), (0, 0, 255), 2)
is_defect = 1
# cv2.imwrite('./test/lin1.png', img)
# lin2 求第二条直线#########################
nptest2 = np.array(line2_yx)
line2 = cv2.fitLine(nptest2, cv2.DIST_L2, 0, 0.01, 0.0)
k2 = line2[1] / line2[0]
b2 = line2[3] - k2 * line2[2]
print('第2排line2: \ny = {:0.8f}x + {:0.8f}'.format(k2[0], b2[0]))
if -10e5 < k < 10e5: # 若斜率不大
def takeFirst(elem):
return elem[0]
line2_yx.sort(key=takeFirst)
# pd_1 = (abs(line2_yx[-1][0] - line2_yx[0][0]) / (len(line2_yx) - 1))
# pd_2 = 0
# for pd_i in range(len(line2_yx)):
# if pd_i < len(line2_yx) - 2:
# pd_2 += ((line2_yx[pd_i + 1][0] - line2_yx[pd_i][0]) / (len(line2_yx) - 1))
# pd_3 = (pd_1 + pd_2) / 2 # 判断针孔沿直线方向歪斜的判断标准
pd_1, pd_2 = 0, 0
dis_pdi = []
for pd_i in range(len(line1_yx)):
if pd_i < len(line1_yx) - 2:
dis_pdi.append(math.sqrt(pow(line1_yx[pd_i + 1][1] - line1_yx[pd_i][1], 2) + pow(line1_yx[pd_i + 1][0] - line1_yx[pd_i][0], 2)))
# pd_2 += ((line1_yx[pd_i + 1][0] - line1_yx[pd_i][0]) / (len(line1_yx) - 1))
for dis_i in dis_pdi:
pd_1 += (dis_i / len(dis_pdi)) # 平均值
print(pd_1)
# ptStart, ptEnd = (0, int(k2 * 0 + b2)), (img.shape[1], int(k2 * img.shape[1] + b2))
ptStart, ptEnd = (int(line2_yx[0][0]), int(k2 * line2_yx[0][0] + b2)), (int(line2_yx[-1][0]), int(k2 * line2_yx[-1][0] + b2))
cv2.line(img, ptStart, ptEnd, (255, 0, 0), 2) # 像素值必须是整数****************
# 计算各点到对应直线的距离,如果从超过最大距离判断为缺陷(********需要使用标定板,计算图片对应的实际距离)
for j in line2_yx:
distance = abs(k2 * j[0] - j[1] + b2) / math.sqrt(k2 ** 2 + 1)
# 在原图绘制点的偏差值
# cv2.putText(img, 'distance: ' + str(distance[0]), (int(j[0] + 5), int(j[1] + 5)), 0, 2, (0, 255, 0), thickness=3)
# cv2.putText(img, str(distance[0]), (int(j[0] + 5), int(j[1] + 5)), 0, 2, (0, 255, 0), thickness=3)
if round((distance[0] * DPI), 2) >= 0.05:
# pass
annotator1.box_label([int(j[0]), int(j[1]), int(j[0] + 10), int(j[1] + 10)], f'{round((distance[0] * DPI), 2)}', color=(0, 255, 0))
if (distance * DPI) > wxdis_thres:
print("第2排点存在不合格: ({},{})".format(str(j[0]), str(j[1])))
# cv2.circle(img, (int(j[0]), int(j[1])), 4, (0, 0, 255), 2) # 显示有缺陷的针孔
cv2.rectangle(img, (int(j[0] - 25), int(j[1]) - 25), (int(j[0] + 25), int(j[1] + 25)), (0, 0, 255), 2)
is_defect = 1
for j in range(1, len(line2_yx)):
if ((abs(line2_yx[j][0]-line2_yx[j-1][0])-pd_1) * DPI) >= wxdis_thres:
print("第2排点存在不合格: ({},{})".format(str(line2_yx[j][0]), str(line2_yx[j][1])))
cv2.rectangle(img, (int(line2_yx[j][0] - 25), int(line2_yx[j][1] - 25)), (int(line2_yx[j][0] + 25), int(line2_yx[j][1] + 25)), (0, 0, 255), 2)
is_defect = 1
else:
def takeSecond(elem):
return elem[1]
line2_yx.sort(key=takeSecond)
# pd_1 = (abs(line2_yx[-1][1] - line2_yx[0][1]) / (len(line2_yx) - 1))
# pd_2 = 0
# for pd_i in range(len(line2_yx)):
# if pd_i < len(line2_yx) - 2:
# pd_2 += ((line2_yx[pd_i + 1][1] - line2_yx[pd_i][1]) / (len(line2_yx) - 1))
# pd_3 = (pd_1 + pd_2) / 2 # 判断针孔沿直线方向歪斜的判断标准
# # pd_3 = 80
pd_1, pd_2 = 0, 0
dis_pdi = []
for pd_i in range(len(line1_yx)):
if pd_i < len(line1_yx) - 2:
dis_pdi.append(math.sqrt(pow(line1_yx[pd_i + 1][1] - line1_yx[pd_i][1], 2) + pow(line1_yx[pd_i + 1][0] - line1_yx[pd_i][0], 2)))
# pd_2 += ((line1_yx[pd_i + 1][0] - line1_yx[pd_i][0]) / (len(line1_yx) - 1))
for dis_i in dis_pdi:
pd_1 += (dis_i / len(dis_pdi)) # 平均值
print(pd_1)
ptStart, ptEnd = (int((0-b2)/k2), 0), (int((img.shape[0] - b2) / k2), img.shape[0]) # 坐标点取得整数需要修改***************
cv2.line(img, ptStart, ptEnd, (255, 0, 0), 2)
for j in line2_yx:
distance = abs(j[0] - int((j[1]-b2)/k2))
# 在原图绘制点的偏差值
cv2.putText(img, 'distance: ' + str(distance[0]), (int(j[0] + 5), int(j[1] + 5)), 0, 2, (0, 255, 0), thickness=3)
if round((distance[0] * DPI), 2) >= 0.05:
# pass
annotator1.box_label([int(j[0]), int(j[1]), int(j[0] + 10), int(j[1] + 10)], f'{round((distance[0] * DPI), 2)}', color=(0, 255, 0))
if (distance * DPI) > wxdis_thres:
print("第2排点存在不合格: ({},{})".format(str(j[0]), str(j[1])))
# cv2.circle(img, (int(j[0]), int(j[1])), 4, (0, 0, 255), 2) # 显示有缺陷的针孔
cv2.rectangle(img, (int(j[0] - 25), int(j[1]) - 25), (int(j[0] + 25), int(j[1] + 25)), (0, 0, 255), 2)
is_defect = 1
for j in range(1, len(line2_yx)):
if ((abs(line2_yx[j][1] - line2_yx[j - 1][1]) - pd_1) * DPI) >= wxdis_thres:
print("第2排点直线方向存在不合格: ({},{})".format(str(line2_yx[j][0]), str(line2_yx[j][1])))
cv2.rectangle(img, (int(line2_yx[j][0] - 25), int(line2_yx[j][1] - 25)),
(int(line2_yx[j][0] + 25), int(line2_yx[j][1] + 25)), (0, 0, 255), 2)
is_defect = 1
# cv2.imwrite('./test/lin2.png', img)
cv2.namedWindow("test",2)
cv2.imshow("test", img)
cv2.waitKey()
return is_defect, img
# def lj_img(img):
# wlj, hlj = img.shape
# lj_dis = 5 # 连接白色区域的判定距离
# for ilj in range(wlj-1):
# for jlj in range(hlj-1):
# if img[ilj, jlj] == 255: # 判断上下左右是否存在白色区域并连通
# if ilj-lj_dis >= 0 and img[ilj-lj_dis, jlj] == 255: img[ilj-lj_dis:ilj, jlj]=255
# if ilj+lj_dis < wlj and img[ilj+lj_dis, jlj] == 255: img[ilj:ilj+lj_dis, jlj]=255
# if jlj-lj_dis >= 0 and img[ilj, jlj-lj_dis] == 255: img[ilj, jlj-lj_dis:jlj] = 255
# if jlj+lj_dis < hlj and img[ilj, jlj+lj_dis] == 255: img[ilj, jlj:jlj+lj_dis] = 255
# return img
def imgBrightness(img1, c, b):
rows, cols, channels = img1.shape
blank = np.zeros([rows, cols, channels], img1.dtype)
rst = cv2.addWeighted(img1, c, blank, 1 - c, b)
return rst
def mainFigure(img, im0, bc, wxdis_thres, DPI):
# global im_res
params = cv2.SimpleBlobDetector_Params() # 黑色斑点面积大小:1524--1581--1400--周围干扰面积: 1325--1695--1688--
# Filter by Area. 设置斑点检测的参数
params.filterByArea = True # 根据大小进行筛选
params.minArea = 10e2
params.maxArea = 10e4
params.minDistBetweenBlobs = 40 # 设置两个斑点间的最小距离 10*7.5
# params.filterByColor = True # 跟据颜色进行检测
params.filterByConvexity = False # 根据凸性进行检测
params.minThreshold = 30 # 二值化的起末阈值,只有灰度值大于当前阈值的值才会被当成特征值
params.maxThreshold = 30*2.5 # 75
params.filterByColor = True # 检测颜色限制,0黑色,255白色
params.blobColor = 255
params.filterByCircularity = True
params.minCircularity = 0.5
# HSV二值化
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
# cv2.imshow('Images/dectCircle/HSV.png', hsv)
# cv2.waitKey()
# cv2.imwrite('Images/dectCircle/HSV.png', hsv)
# lower_color = np.array([12, 80, 45]) # 分别对应着HSV中的最小值
# upper_color = np.array([255, 255, 255]) # 分别对应着HSV中的最大值
# gray = cv2.inRange(hsv, lower_color, upper_color) # inrange函数将根据最小最大HSV值检测出自己想要的颜色部分
# cv2.imwrite('./hsv_mask.png', gray)
# 根据RGB阈值黑白化图片
w_hsv, h_hsv = hsv.shape[1], hsv.shape[0]
for i_hsv in range(w_hsv):
for j_hsv in range(h_hsv):
if hsv[j_hsv, i_hsv][0] < 100 and hsv[j_hsv, i_hsv][1] < 100 and hsv[j_hsv, i_hsv][2] > 160:
hsv[j_hsv, i_hsv] = 255, 255, 255
else:
hsv[j_hsv, i_hsv] = 0, 0, 0
# cv2.imwrite('Images/dectCircle/HSV_hb.png', hsv)
# cv2.imshow('hsv', hsv)
kernel1 = np.ones((9, 9), dtype=np.uint8)
kernel2 = np.ones((3, 3), dtype=np.uint8)
hsv = cv2.dilate(hsv, kernel1, 1) # 1:迭代次数,也就是执行几次膨胀操作
gray = cv2.erode(hsv, kernel2, 1)
# cv2.imwrite('Images/dectCircle/fushi_pengzhang_gray.png', gray)
gray = cv2.cvtColor(gray, cv2.COLOR_BGR2GRAY)
# cv2.imwrite('Images/dectCircle/fushi_pengzhang_gray2.png', gray)
# cv2.imshow('gray', gray)
# cv2.waitKey()
# 对于连接不完全的区域,进行连接
# gray = lj_img(gray)
# cv2.imwrite('./lj_img.png', gray)
# gray = lj_img(gray)
# cv2.imwrite('./lj_img2.png', gray)
try:
# 使用霍夫圆填充圆形
# gray_circles=cv2.HoughCircles(gray,cv2.HOUGH_GRADIENT,4,56,param1=100,param2=40,minRadius=20,maxRadius=30)
gray_circles=cv2.HoughCircles(gray,cv2.HOUGH_GRADIENT,4,56,param1=100,param2=40,minRadius=12,maxRadius=30)
# # 注意看参数 测试的话 可以把参数卡的死一点
# # dp =4 因为要检测的圆比较小
# # minDist 要检测的圆之间圆心之间的距离 自己目测看吧 越准确越好。。。 如果这个值过大或者过小,会发现很多圆都偏移的厉害(这个算法对设置的参数非常看重,有些地方明明没有圆,只要设置了这个参数,有点的差不多的都给你画出个圆来)
# # param1 是跟着一个教程走的
# # param2 如果你的图中的圆非常标准 这个数就可以大点,越大表示所要检测的圆越标准
# # minRadius和maxRadius就根据自己图的实际情况来定就好
print('霍夫圆个数:', len(gray_circles[0]))
for (x,y,r) in gray_circles[0]:
x=int(x)
y=int(y)
# if 0<x<50 and 100<y<550:
# pass
# else:
# output_circle=cv2.circle(hole_right,(x,y),int(r),(0,0,255),3)
# output=cv2.rectangle(output_circle,(x-2,y-2),(x+2,y+2),(255,255,255),1)
gray = cv2.circle(gray, (x, y), int(r)+3, (255, 255, 255), -1)
# output_circle=cv2.rectangle(output_circle,(x-2,y-2),(x+2,y+2),(255,255,255),1)
# cv2.imwrite('Images/dectCircle/img_hf.jpg', gray)
# cv2.imshow('img_hf.jpg', gray)
# cv2.waitKey()
except:
params0 = cv2.SimpleBlobDetector_Params() # 黑色斑点面积大小:1524--1581--1400--周围干扰面积: 1325--1695--1688--
# Filter by Area. 设置斑点检测的参数
params0.filterByArea = True # 根据大小进行筛选
params0.minArea = 10e2
params0.maxArea = 10e3
params0.minDistBetweenBlobs = 40 # 设置两个斑点间的最小距离 10*7.5
params0.filterByConvexity = False # 根据凸性进行检测
params0.minThreshold = 30 # 二值化的起末阈值,只有灰度值大于当前阈值的值才会被当成特征值
params0.maxThreshold = 30 * 2.5 # 75
params0.filterByColor = True # 检测颜色限制,0黑色,255白色
params0.blobColor = 0
params0.filterByCircularity = True
params0.minCircularity = 0.5
detector0 = cv2.SimpleBlobDetector_create(params0)
keypoints0 = list(detector0.detect(gray))
for poi0 in keypoints0: # 回归到原大图坐标系
x_poi0, y_poi0 = poi0.pt[0], poi0.pt[1]
gray = cv2.circle(gray, (int(x_poi0), int(y_poi0)), 20, (255, 255, 255), -1)
cv2.imwrite('Images/dectCircle/img_blob.jpg', gray)
# 找到距离原点(0,0)最近和最远的点
detector = cv2.SimpleBlobDetector_create(params)
keypoints = list(detector.detect(gray))
w0, h0 = img.shape[1], img.shape[0]
# 将边缘部分像素设为黑色
del_dis = 17
for poi in keypoints: # 回归到原大图坐标系
x_poi, y_poi = poi.pt[0], poi.pt[1]
if abs(x_poi - w0)<del_dis or abs(y_poi - h0)<del_dis or x_poi<del_dis or y_poi<del_dis:
x_poi, y_poi = 0, 0
else:
# 将圆心还原至原图
x_poi += bc[0]
y_poi += bc[1]
poi.pt = (x_poi, y_poi)
im_with_keypoints = cv2.drawKeypoints(im0, keypoints, np.array([]), (0, 0, 255), cv2.DRAW_MATCHES_FLAGS_DEFAULT) # 在原图绘制圆心点
cv2.imwrite('Images/dectCircle/im_with_keypoints.png', im_with_keypoints)
allCirclesCenter = [] # 保存所有孔的圆心
if keypoints is not None:
for i in range(len(keypoints)):
if keypoints[i].pt[0] and keypoints[i].pt[1]:
allCirclesCenter.append(keypoints[i].pt)
is_defect = 0 # 判断插针是否歪斜,在原图画线并且显示偏离距离
# 当检测到的数量大于一定值则画线
if len(allCirclesCenter) > 5:
is_defect, im0 = drawLines(im0, allCirclesCenter, wxdis_thres, DPI)
# cv2.namedWindow("im_res", cv2.WINDOW_NORMAL) # 显示大分辨率图片
# cv2.imshow('im_res', im0)
# cv2.waitKey(0)
im_res = cv2.drawKeypoints(im0, keypoints, np.array([]), (0, 0, 255), cv2.DRAW_MATCHES_FLAGS_DEFAULT) # 在原图绘制圆心点
# cv2.imwrite('./test/final_.png', im_res)
# cv2.namedWindow("im_res", cv2.WINDOW_NORMAL) # 显示大分辨率图片
# cv2.imshow('im_res', im_res)
# cv2.waitKey(0)
return is_defect, im_res
if __name__ == "__main__":
#
# DPI = 0.019553336388862412
# im0 = cv2.imread("1102/J30JHole.jpeg", 1)
# im_area = im0[1090:1435, 1222:2203] # Y0:Y1,X0:X1
# is_defect, im0 = mainFigure(im_area, im0, [1222, 1090], 0.05, DPI)
#
im0 = cv2.imread("Images/Final/A_0_1.jpg", 1)
im_area = im0[1322:1756, 2205:2620] # Y0:Y1,X0:X1
# cv2.imwrite('./test/im_area.png', im_area)
is_defect, im0 = mainFigure(im_area, im0, [2205, 1322], 0.5, 0.019553336388862412)
cv2.imwrite("Images/dectCircle/imres.jpg", im0)
# # 所有图片测试
# for i in range(17):
# fileName = "Hole" + str(i+1) + ".jpg"
# # img = cv2.imread("circles/Snap_007.jpg",1)
# img = cv2.imread("images/Holes/" + fileName,1)
# print(fileName)
# mainFigure(img)
检测结果
labelutils文件夹和上面的检测脚本在同一个目录下
这部分功能是借鉴yolov5的操作
plots.py
# YOLOv5 🚀 by Ultralytics, GPL-3.0 license
"""
Plotting utils
"""
import os
from pathlib import Path
from urllib.error import URLError
import cv2
import matplotlib
import numpy as np
from PIL import Image, ImageDraw, ImageFont
from labelutils.general import (FONT, check_font, check_requirements, is_ascii)
# Settings
RANK = int(os.getenv('RANK', -1))
matplotlib.rc('font', **{'size': 11})
matplotlib.use('Agg') # for writing to files only
class Colors:
# Ultralytics color palette https://ultralytics.com/
def __init__(self):
# hex = matplotlib.colors.TABLEAU_COLORS.values()
hexs = ('FF3838', 'FF9D97', 'FF701F', 'FFB21D', 'CFD231', '48F90A', '92CC17', '3DDB86', '1A9334', '00D4BB',
'2C99A8', '00C2FF', '344593', '6473FF', '0018EC', '8438FF', '520085', 'CB38FF', 'FF95C8', 'FF37C7')
self.palette = [self.hex2rgb(f'#{c}') for c in hexs]
self.n = len(self.palette)
def __call__(self, i, bgr=False):
c = self.palette[int(i) % self.n]
return (c[2], c[1], c[0]) if bgr else c
@staticmethod
def hex2rgb(h): # rgb order (PIL)
return tuple(int(h[1 + i:1 + i + 2], 16) for i in (0, 2, 4))
colors = Colors() # create instance for 'from utils.plots import colors'
def check_pil_font(font=FONT, size=10):
# Return a PIL TrueType Font, downloading to CONFIG_DIR if necessary
font = Path(font)
# font = font if font.exists() else (CONFIG_DIR / font.name)
font = font if font.exists() else (font.name)
try:
return ImageFont.truetype(str(font) if font.exists() else font.name, size)
except Exception: # download if missing
try:
check_font(font)
return ImageFont.truetype(str(font), size)
except TypeError:
check_requirements('Pillow>=8.4.0') # known issue https://github.com/ultralytics/yolov5/issues/5374
except URLError: # not online
return ImageFont.load_default()
class Annotator:
# YOLOv5 Annotator for train/val mosaics and jpgs and detect/hub inference annotations
def __init__(self, im, line_width=None, font_size=None, font='Arial.ttf', pil=False, example='abc'):
assert im.data.contiguous, 'Image not contiguous. Apply np.ascontiguousarray(im) to Annotator() input images.'
non_ascii = not is_ascii(example) # non-latin labels, i.e. asian, arabic, cyrillic
self.pil = pil or non_ascii
if self.pil: # use PIL
self.im = im if isinstance(im, Image.Image) else Image.fromarray(im)
self.draw = ImageDraw.Draw(self.im)
self.font = check_pil_font(font='Arial.Unicode.ttf' if non_ascii else font,
size=font_size or max(round(sum(self.im.size) / 2 * 0.035), 12))
else: # use cv2
self.im = im
self.lw = line_width or max(round(sum(im.shape) / 2 * 0.003), 2) # line width
def box_label(self, box, label='', color=(128, 128, 128), txt_color=(255, 255, 255)):
# Add one xyxy box to image with label
if self.pil or not is_ascii(label):
self.draw.rectangle(box, width=self.lw, outline=color) # box
if label:
w, h = self.font.getsize(label) # text width, height
outside = box[1] - h >= 0 # label fits outside box
self.draw.rectangle(
(box[0], box[1] - h if outside else box[1], box[0] + w + 1,
box[1] + 1 if outside else box[1] + h + 1),
fill=color,
)
# self.draw.text((box[0], box[1]), label, fill=txt_color, font=self.font, anchor='ls') # for PIL>8.0
self.draw.text((box[0], box[1] - h if outside else box[1]), label, fill=txt_color, font=self.font)
else: # cv2
p1, p2 = (int(box[0]), int(box[1])), (int(box[2]), int(box[3]))
cv2.rectangle(self.im, p1, p2, color, thickness=self.lw, lineType=cv2.LINE_AA)
if label:
tf = max(self.lw - 1, 1) # font thickness
w, h = cv2.getTextSize(label, 0, fontScale=self.lw / 3, thickness=tf)[0] # text width, height
outside = p1[1] - h >= 3
p2 = p1[0] + w, p1[1] - h - 3 if outside else p1[1] + h + 3
cv2.rectangle(self.im, p1, p2, color, -1, cv2.LINE_AA) # filled
cv2.putText(self.im,
label, (p1[0], p1[1] - 2 if outside else p1[1] + h + 2),
0,
self.lw / 3,
txt_color,
thickness=tf,
lineType=cv2.LINE_AA)
def rectangle(self, xy, fill=None, outline=None, width=1):
# Add rectangle to image (PIL-only)
self.draw.rectangle(xy, fill, outline, width)
def text(self, xy, text, txt_color=(255, 255, 255)):
# Add text to image (PIL-only)
w, h = self.font.getsize(text) # text width, height
self.draw.text((xy[0], xy[1] - h + 1), text, fill=txt_color, font=self.font)
def result(self):
# Return annotated image as array
return np.asarray(self.im)
general.py
"""
General utils
"""
import logging
import platform
from pathlib import Path
from subprocess import check_output
import pkg_resources as pkg
FONT = 'Arial.ttf' # https://ultralytics.com/assets/Arial.ttf
LOGGER = logging.getLogger("yolov5") # define globally (used in train.py, val.py, detect.py, etc.)
def try_except(func):
# try-except function. Usage: @try_except decorator
def handler(*args, **kwargs):
try:
func(*args, **kwargs)
except Exception as e:
print(e)
return handler
def is_ascii(s=''):
# Is string composed of all ASCII (no UTF) characters? (note str().isascii() introduced in python 3.7)
s = str(s) # convert list, tuple, None, etc. to str
return len(s.encode().decode('ascii', 'ignore')) == len(s)
#
def emojis(str=''):
# Return platform-dependent emoji-safe version of string
return str.encode().decode('ascii', 'ignore') if platform.system() == 'Windows' else str
#
def check_online():
# Check internet connectivity
import socket
try:
socket.create_connection(("1.1.1.1", 443), 5) # check host accessibility
return True
except OSError:
return False
@try_except
def check_requirements(requirements= 'requirements.txt', exclude=(), install=True, cmds=()):
# Check installed dependencies meet requirements (pass *.txt file or list of packages)
prefix = colorstr('red', 'bold', 'requirements:')
# check_python() # check python version
if isinstance(requirements, (str, Path)): # requirements.txt file
file = Path(requirements)
assert file.exists(), f"{prefix} {file.resolve()} not found, check failed."
with file.open() as f:
requirements = [f'{x.name}{x.specifier}' for x in pkg.parse_requirements(f) if x.name not in exclude]
else: # list or tuple of packages
requirements = [x for x in requirements if x not in exclude]
n = 0 # number of packages updates
for i, r in enumerate(requirements):
try:
pkg.require(r)
except Exception: # DistributionNotFound or VersionConflict if requirements not met
s = f"{prefix} {r} not found and is required by YOLOv5"
if install: # check environment variable
LOGGER.info(f"{s}, attempting auto-update...")
try:
assert check_online(), f"'pip install {r}' skipped (offline)"
LOGGER.info(check_output(f'pip install "{r}" {cmds[i] if cmds else ""}', shell=True).decode())
n += 1
except Exception as e:
LOGGER.warning(f'{prefix} {e}')
else:
LOGGER.info(f'{s}. Please install and rerun your command.')
if n: # if packages updated
source = file.resolve() if 'file' in locals() else requirements
s = f"{prefix} {n} package{'s' * (n > 1)} updated per {source}\n" \
f"{prefix} ⚠️ {colorstr('bold', 'Restart runtime or rerun command for updates to take effect')}\n"
LOGGER.info(emojis(s))
def check_font(font=FONT, progress=False):
# Download font to CONFIG_DIR if necessary
font = Path(font)
file = font.name
if not font.exists() and not file.exists():
url = "https://ultralytics.com/assets/" + font.name
LOGGER.info(f'Downloading {url} to {file}...')
# torch.hub.download_url_to_file(url, str(file), progress=progress)
def colorstr(*input):
# Colors a string https://en.wikipedia.org/wiki/ANSI_escape_code, i.e. colorstr('blue', 'hello world')
*args, string = input if len(input) > 1 else ('blue', 'bold', input[0]) # color arguments, string
colors = {
'black': '\033[30m', # basic colors
'red': '\033[31m',
'green': '\033[32m',
'yellow': '\033[33m',
'blue': '\033[34m',
'magenta': '\033[35m',
'cyan': '\033[36m',
'white': '\033[37m',
'bright_black': '\033[90m', # bright colors
'bright_red': '\033[91m',
'bright_green': '\033[92m',
'bright_yellow': '\033[93m',
'bright_blue': '\033[94m',
'bright_magenta': '\033[95m',
'bright_cyan': '\033[96m',
'bright_white': '\033[97m',
'end': '\033[0m', # misc
'bold': '\033[1m',
'underline': '\033[4m'}
return ''.join(colors[x] for x in args) + f'{string}' + colors['end']