检测两排孔并拟合直线测孔间距

# 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']

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值