胶囊质量检测

capsules.zip

#功能:胶囊缺陷识别

import cv2
import numpy as np
import os


def empty_detection(img_path, img_file, img, img_gray):
    # 先模糊,在做二值化
    blured = cv2.GaussianBlur(img_gray, (3, 3), 0)

    # 二值化
    t, img_bin = cv2.threshold(blured, 210, 255, cv2.THRESH_BINARY)
    # cv2.imshow('img_binary',img_bin)

    # 查找轮廓
    image, cnts, hie = cv2.findContours(img_bin,
                                        cv2.RETR_CCOMP,  # 两层轮廓
                                        cv2.CHAIN_APPROX_NONE)  # 保存所有坐标点

    new_cnts = []
    # 轮廓过滤
    for i in range(len(cnts)):
        cir_len = cv2.arcLength(cnts[i], True)
        # print('轮廓:{},周长:{}'.format(i,cir_len))

        if cir_len >= 1000:
            new_cnts.append(cnts[i])

    # 绘制轮廓周长大于1000的轮廓
    img_cnt = cv2.drawContours(img,
                               new_cnts,
                               -1,
                               (0, 0, 255),
                               2)
    # cv2.imshow('img_cnt',img_cnt)

    if len(new_cnts) == 1:
        print(img_path, ':', '空胶囊')
        # 移动文件
        new_path = os.path.join('capsules/empty', img_file)
        os.rename(img_path, new_path)
        print('文件移动成功:{}-->{}'.format(img_path, new_path))
        return True
    else:
        return False


def bub_detection(img_path, img_file, img, img_gray):
    blured = cv2.GaussianBlur(img_gray, (3, 3), 0)
    # t,img_bin = cv2.threshold(img_gray,180,255,cv2.THRESH_BINARY)
    # cv2.imshow('img_bin',img_bin)
    img_canny = cv2.Canny(blured, 60, 240)
    # cv2.imshow('img_canny',img_canny)

    # 提取轮廓
    image, cnts, hie = cv2.findContours(img_canny,
                                        cv2.RETR_CCOMP,
                                        cv2.CHAIN_APPROX_NONE)

    new_cnts = []
    # 轮廓过滤
    for i in range(len(cnts)):
        cir_len = cv2.arcLength(cnts[i], True)  # 周长
        area = cv2.contourArea(cnts[i])  # 面积

        if area >= 10000 or cir_len > 900 or area < 5:
            continue

        if hie[0][i][3] != -1:  # 有父轮廓
            new_cnts.append(cnts[i])

    # 将找到的气泡轮廓绘制
    img_cnt = cv2.drawContours(img,
                               new_cnts,
                               -1,
                               (0, 0, 255),
                               1)
    # cv2.imshow('img_cnt',img_cnt)

    # 移动目录
    if len(new_cnts) > 0:
        print(img_path, ':', '黑点气泡瑕疵')
        new_path = os.path.join('capsules/bub', img_file)
        os.rename(img_path, new_path)
        print('文件移动成功:{}-->{}'.format(img_path, new_path))

        return True
    else:
        return False


def balance_detection(img_path, img_file, img, img_gray):
    # 要找到胶囊的轮廓  :面积排第二
    blured = cv2.GaussianBlur(img_gray, (5, 5), 0)
    # 膨胀
    kenel = np.ones((5, 5), np.uint8)
    dilate = cv2.dilate(blured, kenel)

    canny = cv2.Canny(dilate, 60, 200)
    # cv2.imshow('canny',canny)

    # 提取轮廓
    image, cnts, hie = cv2.findContours(canny,
                                        cv2.RETR_LIST,
                                        cv2.CHAIN_APPROX_NONE)
    # print(len(cnts))
    # print('------')

    new_cnts = []
    cnts = np.array(cnts)
    # 轮廓过滤
    if len(cnts) > 0:
        for c in cnts:
            area = cv2.contourArea(c)  # 面积
            cir_len = cv2.arcLength(c, True)
            # print('area:{},arclength:{}'.format(area,cir_len))

            # 注意缩进
            if cir_len >= 1000:
                new_cnts.append(c)

    new_cnts = sorted(new_cnts,
                      key=cv2.contourArea,
                      reverse=True)

    # 取出面积第二大的
    new_cnts = new_cnts[1:2]
    # print(new_cnts)
    # print('----')

    img_cnt = cv2.drawContours(img,
                               new_cnts,
                               -1,
                               (0, 0, 255),
                               2)
    cv2.imshow('img_cnt', img_cnt)

    # 求药丸轮廓边界
    max_x = new_cnts[0][0][0][0]
    max_y = new_cnts[0][0][0][1]
    min_x = max_x
    min_y = max_y

    for cnt in new_cnts[0]:
        if cnt[0][0] >= max_x:  # 轮廓点的x左边比最大还大
            max_x = cnt[0][0]
        if cnt[0][0] < min_x:
            min_x = cnt[0][0]
        if cnt[0][1] >= max_y:
            max_y = cnt[0][1]
        if cnt[0][1] < min_y:
            min_y = cnt[0][1]
    # 绘制边界
    red = (0, 0, 255)
    # cv2.line(img, (min_x, min_y), (max_x, min_y), red, 2)
    # cv2.line(img, (max_x, min_y), (max_x, max_y), red, 2)
    # cv2.line(img, (max_x, max_y), (min_x, max_y), red, 2)
    # cv2.line(img, (min_x, max_y), (min_x, min_y), red, 2)

    # 水平中线
    center_y = int((min_y + max_y) / 2)
    # 上中线
    center_up = int((min_y + center_y) / 2)
    # 下中线
    center_down = int((max_y + center_y) / 2)

    # cv2.line(img, (min_x,center_y), (max_x,center_y), red, 2)
    cv2.line(img, (min_x, center_up), (max_x, center_up), red, 2)
    cv2.line(img, (min_x, center_down), (max_x, center_down), red, 2)

    cv2.imshow("img_line", img)

    # 求药丸轮廓和上下中线的交点
    cross_up = set()
    cross_down = set()

    for cnt in new_cnts[0]:
        x, y = cnt[0][0], cnt[0][1]  # 取轮廓上点的x,y坐标
        if y == center_up:
            cross_up.add((x, y))
        if y == center_down:
            cross_down.add((x, y))

    cross_up = list(cross_up)
    cross_down = list(cross_down)

    # 在交点处绘制小圆圈
    for x, y in cross_up:
        cv2.circle(img, (x, y), 8, red, 2)
    for x, y in cross_down:
        cv2.circle(img, (x, y), 8, red, 2)
    cv2.imshow("im_circle", img)

    # 计算上中线、下中线长度
    len_up = abs(cross_up[0][0] - cross_up[1][0])
    len_down = abs(cross_down[0][0] - cross_down[1][0])
    print("上中线长度:", len_up)
    print("下中线长度:", len_down)

    if abs(len_up - len_down) > 8:  # 上下中线长度相差超过8
        print("大小头:", img_file)
        new_path = os.path.join('capsules/imbal', img_file)
        os.rename(img_path, new_path)
        print('文件移动成功:{}-->{}'.format(img_path, new_path))
        return True
    else:
        return False


if __name__ == '__main__':
    # 加载所有待预测的图片
    img_dir = 'capsules'
    img_fils = os.listdir(img_dir)

    # 拼接图片路径
    for img_file in img_fils:
        img_path = os.path.join(img_dir, img_file)
        if os.path.isdir(img_path):
            continue

        # 拿到每一个图像
        img = cv2.imread(img_path)
        # 灰度化
        img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

        # cv2.imshow('img',img)
        # cv2.imshow('img_gray',img_gray)

        # 空胶囊的检测
        is_emptey = False
        is_emptey = empty_detection(img_path,
                                    img_file,
                                    img,
                                    img_gray)

        # 黑点气泡的胶囊检测
        is_bub = False
        if not is_emptey:
            is_bub = bub_detection(img_path,
                                   img_file,
                                   img,
                                   img_gray)

        # 大小头检测
        if (not is_emptey) and (not is_bub):
            balance_detection(img_path,
                              img_file,
                              img,
                              img_gray)

        cv2.waitKey()
        cv2.destroyAllWindows()
  • 测试代码
import cv2
import numpy as np
import os

def get_file():
    dir = './capsules'
    list = os.listdir(dir)
    file = []

    for i in list:
        join_dir = os.path.join(dir,i)
        if os.path.isdir(join_dir):
            continue
        file.append(join_dir)
    return file


def cap_is_empty(img, img_gray, file_path, file_name=''):
    # 模糊
    img_gas = cv2.GaussianBlur(img_gray,(5,5),0)
    # 二值化
    t,img_bin = cv2.threshold(img_gas,210,255,cv2.THRESH_BINARY)

    # 轮廓判断
    img_find,cont,hie = cv2.findContours(img_bin,cv2.RETR_CCOMP,cv2.CHAIN_APPROX_NONE)
    # cv2.imshow('img_gray', img_find)

    # 轮廓过滤 根据周长进行判断 只保留周长大于某个值的轮廓
    # cont_new = []
    # for i in range(len(cont)):
    #     cir_len = cv2.arcLength(cont[i],True)
    #     if cir_len >= 1000:
    #         print(cir_len)
    #         cont_new.append(cont[i])
    # print(len(cont_new))
    # # 绘制边沿
    # img_cnt = cv2.drawContours(img,cont_new,-1,(0,0,255),2)
    # # 查看轮廓
    # cv2.imshow('img_gray', img_cnt)

    if len(cont) == 1:
        # 移动图片
        os.rename(file_path,os.path.join('./capsules/empty',file_path[file_path.rfind('/')+1:]))
        return True
    else:
        return False


def cap_is_bub(img, img_gray, file_path):
    # 模糊
    img_gas = cv2.GaussianBlur(img_gray, (3, 3), 0)
    # 二值化
    # t, img_bin = cv2.threshold(img_gas, 210, 255, cv2.THRESH_BINARY)
    img_candy = cv2.Canny(img_gas,50,240)
    # 轮廓判断
    img_find,cont,hie = cv2.findContours(img_candy,cv2.RETR_CCOMP,cv2.CHAIN_APPROX_NONE)
    # 轮廓过滤 根据周长面积进行判断
    cont_new = []
    for i in range(len(cont)):
        cir_len = cv2.arcLength(cont[i],True)
        cir_area = cv2.contourArea(cont[i])
        if 5 < cir_area <= 10000 and cir_len <= 2300 and hie[0][i][3] != -1:
            cont_new.append(cont[i])
    #         print('周长%s面积%s,%s' % (cir_len, cir_area,hie[0][i][3]))
    # print('--------------')
    #
    # # 绘制边沿
    # img_cnt = cv2.drawContours(img,cont_new,-1,(0,0,255),2)
    # cv2.imshow('candy',img_find)
    # # 查看轮廓
    # cv2.imshow('img_gray', img_cnt)

    if len(cont_new) > 0:
        # 移动图片
        os.rename(file_path,os.path.join('./capsules/bub',file_path[file_path.rfind('/')+1:]))
        return True
    else:
        return False


def cap_is_imbal(img, img_gray, file_path):
    # 模糊
    img_gas = cv2.GaussianBlur(img_gray, (5, 5), 0)
    # 膨胀
    kernel = np.ones((3,3),np.uint8)
    img_de = cv2.dilate(img_gray,kernel)
    #candy
    img_candy = cv2.Canny(img_de,50,240)
    # 轮廓判断
    img_find,cont,hie = cv2.findContours(img_candy,cv2.RETR_LIST,cv2.CHAIN_APPROX_NONE)

    # 轮廓过滤 面积排第二
    cont_new = []
    for i in range(len(cont)):
        cir_len = cv2.arcLength(cont[i],True)
        cir_area = cv2.contourArea(cont[i])
        if cir_len > 900:
            cont_new.append(cont[i])
            # print('周长%s面积%s,%s' % (cir_len, cir_area,hie[0][i][3]))
    # print('--------------')
    # 排序
    cont_new = sorted(cont_new,key=cv2.contourArea,reverse=True)
    cont_new = cont_new[1:2]
    # 绘制边沿
    img_cnt = cv2.drawContours(img,cont_new,-1,(0,0,255),2)
    # cv2.imshow('candy',img_find)

    max_x = cont_new[0][0][0][0]
    max_y = cont_new[0][0][0][1]
    min_x = max_x
    min_y = max_y
    # 寻找x,y最大值和最小值
    for i in cont_new[0]:
        if i[0][0] >= max_x:
            max_x = i[0][0]
        if i[0][0] < min_x:
            min_x = i[0][0]
        if i[0][1] >= max_y:
            max_y = i[0][1]
        if i[0][1] < min_y:
            min_y = i[0][1]

    # print(min_x,max_x,min_y,max_y)
    brcnt = np.array([[[min_x,min_y]],[[min_x,max_y]],[[max_x,max_y]],[[max_x,min_y]]])
    # print(brcnt)
    # 绘制边沿
    img_cnt = cv2.drawContours(img,[brcnt],-1,(0,0,255),2)

    # 绘制线段
    yline1 = int(((max_y-min_y)/4)+min_y)
    yline2 = int(((max_y-min_y)/4*3)+min_y)

    img_cnt = cv2.line(img,(min_x,yline1),(max_x,yline1),(0,0,255),2)
    img_cnt = cv2.line(img,(min_x,yline2),(max_x,yline2),(0,0,255),2)

    up_line = []
    down_line = []
    # 求焦点
    for i in cont_new[0]:
        if i[0][1] == yline1:
            up_line.append(i[0][0])
            # 画圆
            cv2.circle(img, (i[0][0], i[0][1]), 10, (0, 255, 0), 2)
        if i[0][1] == yline2:
            down_line.append(i[0][0])
            # 画圆
            cv2.circle(img, (i[0][0], i[0][1]), 10, (0, 255, 0), 2)

    up = up_line[0]-up_line[1]
    down = down_line[0]-down_line[1]
    # print("up %s,down %s all %s"%(up,down,abs(up-down)))

    # 查看图片
    # cv2.imshow('img_gray', img_cnt)

    if abs(up-down) > 10:
        # 移动图片
        os.rename(file_path,os.path.join('./capsules/imbal',file_path[file_path.rfind('/')+1:]))
        return True
    else:
        return False


if __name__ == '__main__':
    # 获取所有文件路径
    for file_path in get_file():
        img = cv2.imread(file_path)
        img_gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
        # 判断是否为空
        ie = False
        ib = False
        ie = cap_is_empty(img,img_gray,file_path)
        if ie != True:
            # 判断是否有气泡
            ib = cap_is_bub(img,img_gray,file_path)

        if ie == False and ib == False:
            # 判断是否大小头
            ii = cap_is_imbal(img,img_gray,file_path)
        cv2.waitKey()
        cv2.destroyAllWindows()

轨道交叉点检测

rail_1.png

# 利用图像技术实现铁轨交叉点检测
import cv2
import numpy as np
import math

# 生成线段上所有点的坐标
def calc_line_points(x1, y1, x2, y2):
    # 线段斜率
    k = float(y2 - y1) / float(x2 - x1)
    # 偏置
    b = float(y1) - float(x1) * k

    points = []
    for x in range(x1, x2 + 1):
        y = int(k * x + b) # 计算y
        points.append((x, y)) # 将每个点的坐标添加到列表
    return points

# 判断两个线段是否存在交点
def cross_point(line1, line2):
    is_exist = False  # 是否存在交点返回值
    x, y = 0, 0  # 交点坐标初始值
    x1, y1, x2, y2 = line1  # 取出第一个线段的端点坐标
    x3, y3, x4, y4 = line2  # 取出第二个线段的端点坐标

    # 排除不可能存在交点的情况
    k1 = float(y2 - y1) / float(x2 - x1)  # 第一个线段斜率
    k2 = float(y4 - y3) / float(x4 - x3)  # 第二个线段斜率
    if abs(k1 - k2) < 0.3: # 斜率很接近
        return False, [0, 0] # 不存在交点

    # 如果一条线段最大x值小于另一条最小x值,不可能产生交点
    if max(x2, x1) < min(x3, x4):
        return False, [0, 0]
    if max(x3, x4) < min(x2, x1):
        return False, [0, 0]
    # 如果一条线段最大y值小于另一条最小y值,不可能产生交点
    if max(y2, y1) < min(y3, y4):
        return False, [0, 0]
    if max(y3, y4) < min(y2, y1):
        return False, [0, 0]

    # 计算两个线段上所有点的坐标值,比较是否有交点
    points_1 = calc_line_points(x1, y1, x2, y2)
    points_2 = calc_line_points(x3, y3, x4, y4)
    for p1 in points_1:
        for p2 in points_2:
            x1, y1 = p1
            x2, y2 = p2
            # 计算两点的距离
            dist = math.sqrt((x1-x2)**2 + (y1-y2)**2)
            if dist < 4: # 距离非常接近,交点
                return True, [x1, y1]
    return False, [0, 0]

def cross_detection(img_path):
    im = cv2.imread(img_path)  # 读取图像
    cv2.imshow("im", im)

    # 灰度化
    im_gray = cv2.cvtColor(im, cv2.COLOR_BGR2GRAY)
    cv2.imshow("im_gray", im_gray)

    # 对图像进行卷积,加强垂直方向的纹理
    flt = np.array([[-1, 0, 1],
                    [-2, 0, 2],
                    [-1, 0, 1]])
    im_conv = cv2.filter2D(im_gray, -1, flt,
                           borderType=1)
    cv2.imshow("im_conv", im_conv)

    # 二值化
    t, im_bin = cv2.threshold(im_conv, 180, 255,
                              cv2.THRESH_BINARY)
    cv2.imshow("im_bin", im_bin)

    # 霍夫变换检测图像中的线段
    lines = cv2.HoughLinesP(
        im_bin,  # 输入图像
        1,  # 以像素作为距离精度单位
        np.pi / 180,  # 角度, π/180表示所有可能的角度
        1,  # 该值越小,检测出的线段越多
        minLineLength=70,  # 线段最小长度
        maxLineGap=20)  # 最大间隔距离,小于该值认为属同一个线段
    print(type(lines))
    print(lines.shape)
    # print(lines)
    # 在原图上绘制找到的线段
    red = (0, 0, 255)
    for ln in lines:
        x1, y1, x2, y2 = ln[0]  # 取出端点坐标
        cv2.line(im, (x1, y1), (x2, y2), red, 2)
    cv2.imshow("im_lines", im)

    lines = lines[:, 0, :]
    # 线段两两比较
    for x1, y1, x2, y2 in lines:
        for x3, y3, x4, y4 in lines:
            # 通过函数判断两个线段是否存在交点
            is_exist, [x, y] = cross_point(
                [x1, y1, x2, y2], [x3, y3, x4, y4])
            if is_exist:# 存在交点
                cv2.circle(im, (x,y), 5, red, 3)
    cv2.imshow("result", im) # 显示结果

    cv2.waitKey()
    cv2.destroyAllWindows()


if __name__ == "__main__":
    cross_detection("rail_1.png")
  • 测试代码
import cv2
import numpy as np

# 计算线段上所有的点
def calc_line_points(x1, y1, x2, y2):
    k = float(y2-y1)/float(x2-x1)
    b = float(y1)-float(x1)*k
    point = []
    for i in range(x1,x2+1):
        point.append([i,int(i*k+b)])
    return point

def cross_point(line1, line2):
    is_exist = False
    x,y = 0,0
    x1, y1, x2, y2 = line1
    x3, y3, x4, y4 = line2

    # 排除斜率过小的情况
    k1 = float(y2-y1)/float(x2-x1)
    k2 = float(y4-y3)/float(x4-x3)
    if abs(k2-k1)<0.3:
        return False,[0,0]

    # 线段最大值小于另一条线段最小值 不可能有交点
    if max(x1,x2) < min(x3,x4):
        return False,[0,0]
    if max(x3,x4) < min(x1,x2):
        return False,[0,0]
    # 线段最大值小于另一条线段最小值 不可能有交点
    if max(y1,y2) < min(y3,y4):
        return False,[0,0]
    if max(y3,y4) < min(y1,y2):
        return False,[0,0]

    point1 = calc_line_points(x1,y1,x2,y2)
    point2 = calc_line_points(x3,y3,x4,y4)

    for i in point1:
        for j in point2:
            x1,y1 = i
            x2,y2 = j
            #计算斜边长度
            dict = np.math.sqrt((x1-x2)**2 + (y1-y2)**2)
            if dict < 4:
                return True,[x1,y1]
    return False,[0,0]

def cross_d(img_path):
    img = cv2.imread(img_path)
    im_gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)

    # 二值化
    # t,img_bin = cv2.threshold(im_gray,170,255,cv2.THRESH_BINARY)
    # cv2.imshow('img_bin',img_bin)

    # 卷积
    fit = np.array([[-1,0,1],
                    [-2,0,2],
                    [-1,0,1]])
    img_con = cv2.filter2D(im_gray,-1,fit,borderType=1)
    # cv2.imshow('im_gray',img_con)

    # 二值化
    t,img_bin = cv2.threshold(img_con,180,255,cv2.THRESH_BINARY)
    # cv2.imshow('img_bin',img_bin)

    line = cv2.HoughLinesP(
        img_bin,#输入图像
        1,#以像素作为距离精度单位
        np.pi/180,#角度,pi/180表示所有可能角度
        1,#该值越小,检出线段越多
        minLineLength=70,#线段最小长度
        maxLineGap=20)#最大间隔距离,小于该值认为属于同一个线段
    print(type(line))
    print(line.shape)

    for i in line:
        x1,y1,x2,y2 = i[0]
        cv2.line(img,(x1,y1),(x2,y2),(0,0,255),2)

    line = line[:,0,:]
    for x1,y1,x2,y2 in line:
        for x3, y3, x4, y4 in line:
            # 判断焦点是否存在
            is_exist,[x,y] = cross_point([x1,y1,x2,y2],[x3, y3, x4, y4])
            if is_exist:
                cv2.circle(img,(x,y),10,(0,255,0),2)

    cv2.imshow('im',img)
    cv2.waitKey()
    cv2.destroyAllWindows()


if __name__ == '__main__':
    cross_d('./rail_1.png')