反向应用SVD(奇异值分解)算法检测显示屏缺陷

《诉衷情·旖旎情》

星沉月落夜闻香 素手出锋芒 前缘再续新曲 心有意 爱无伤

江湖远 碧空长 路茫茫 闲愁滋味 多感情怀 无限思量

工作需要,实现论文《Automatic defect inspection for LCDs using singular value decomposition》中反向使用SVD算法检测屏幕缺陷。

显示屏缺陷检测是机器视觉领域的一处较广泛的应用场景,显示屏主要有LCD和OLED,缺陷类型主要包括点线缺陷(亮暗点,亮暗线),Mura缺陷(亮度显示不均),外观缺陷(划伤,破损,脏污)等。

SVD(奇异值分解)算法主要用于图像的降维和重构,取特征值矩阵的前几个主要特征值重构图片,论文作者将其反向使用,用于凸显图片中的各类缺陷,达到缺陷检测的目的。

本文阐述论文的检测思路,提供代码实现以及一些不算结果的结果。为了区别,本文章“论文作者”指该论文作者的观点和方法,“本文作者”为本篇文章作者;

目录

  • SVD算法简介
  • 反向使用SVD算法检测显示屏缺陷原理
  • Python代码实现
  • 不算结果的结果展示

SVD算法简介

SVD算法的基本原理本文不作详细介绍,一整张图片就是一个像素值矩阵,每个像素值0-255之间,SVD可以把一个矩阵分解成左奇异向量矩阵U、奇异值矩阵

和右奇异向量矩阵VT,如下图。

 

Σ矩阵为奇异值矩阵,只有对角线有值,值为奇异值,且依次减小,一般前面几个大的奇异值占据总奇异值总量的绝大部分,可以认为前面几个大的奇异值及其对应的左右奇异向量矩阵包含图形组要特征信息,而后面较小的奇异值则包含噪声等次要信息。

所以通过截取奇异值矩阵前几个较大奇异值,舍弃后面所有较小奇异值,重新构造图像,起到图像过滤去噪以及降维的作用.

反向使用SVD算法检测显示屏缺陷原理

论文作者反向使用SVD算法用于检测LCD显示屏外观缺陷,包括划伤、破点和灰尘。

根据SVD算法原理可知,较大的奇异值包含图像主要特征信息,而较小的特征值包含噪声等其他信息,论文作者认为对于LCD图像而言,由于其通过显示屏上的source线和gate线显示图像,单色点屏画面下图像呈现周期性特性,这种特性对应奇异值矩阵中前面几个较大奇异值,而除此以外的较小奇异值则代表外观缺陷等其他图像信息,所以可以通过将奇异值矩阵中较大的奇异值置零,只保留奇异值矩阵中较小的奇异值,即过滤掉图片中LCD显示的周期性图像信息,保留外观缺陷信息,达到检测外观缺陷的效果。

关键是如何选择这个阈值k,使奇异值矩阵中前k个特征值置零,只保留后面较小的特征值

Python代码实现

论文算法较为简单,本文作者使用Python将其实现,代码放在GitHub上,链接如下:

 https://github.com/tklk610/Defect-detection-of-display-screen-by-SVD

码分两部分,一个是使用SVD算法分解图片,并根据k值重建奇异值矩阵,k值可以根据奇异值自动计算或是人工设置,然后重构图片的模块

def svd(src_path, tar_path, r) :
    #print("path = %s" %(path))
    for file in os.listdir(src_path) :
        file_path = os.path.join(src_path, file)


        if os.path.isdir(file_path):
            os.list_dir(file_path)
        else:
            img = cv2.imread(file_path)
            print(img.shape)
            ch = img.shape[2]
            print(ch)

            if ch == 3:       # color image with 3 channels
                print("This is color image!")
                image = img

                for ch in range(3) :
                    img_index    = img[:, :, ch]
                    U, sigma, VT = linalg.svd(img_index)
                    u_shape      = U.shape[0]
                    vt_shape     = VT.shape[0]

                    # k = k_value(r, *sigma)

                    k = 5  # # set k value artificially

                    if k <0 :
                        raise Exception("k should lager than 0!", k)

                    # print(U.shape)
                    # print(sigma.shape)
                    # print(VT.shape)

                    sigma[0:k] = 0
                    #print(sigma)

                    new_sigma = np.zeros((u_shape, vt_shape))
                    index = u_shape if u_shape < vt_shape else vt_shape

                    for row in range(index) :
                        for col in range(index) :
                            if row == col :
                                new_sigma[row][col] = sigma[row]

                    print(new_sigma.shape)

                    # 重构矩阵
                    # dig = np.mat(np.eye(num) * sigma[:])  # 获得对角矩阵
                    # dim = data.T * U[:,:count] * dig.I      # 降维
                    image[:, :, ch] = np.dot(np.dot(U[:, :], new_sigma), VT[:, :])  # 重构


                print(image.shape)
                new_file = file[:-4] + '_' + str(ch) + '_' + str(k)  + '.bmp'
                #new_file = file[:-5] + '_' + str(ch) + '_' + str(k) + '.tiff'
                new_name = os.path.join(tar_path, new_file)
                cv2.imwrite(new_name, image)

            else :  # gray image
                print("This is gray image!")
                U, sigma, VT = linalg.svd(img)
                u_shape      = U.shape[0]
                vt_shape     = VT.shape[0]

                k = k_value(r, sigma)

                # k = 17   # set k value artificially

                if k < 0:
                    raise Exception("k should lager than 0!", k)

                print(U.shape)
                print(sigma.shape)
                print(VT.shape)
                sigma[0:k] = 0

                new_sigma = np.zeros((u_shape, vt_shape))
                index = u_shape if u_shape < vt_shape else vt_shape

                for row in range(index):
                    for col in range(index):
                        if row == col:
                            new_sigma[row][col] = sigma[row]

                print(new_sigma.shape)

                image = np.dot(np.dot(U[:, :], new_sigma), VT[:, :])  # 重构
                print(image.shape)
                #new_file = file[:-4] + '_' + str(k) + '.bmp'
                new_file = file[:-5] + '_' + str(k) + '.tiff'
                new_name = os.path.join(tar_path, new_file)
                cv2.imwrite(new_name, image)

src_path是原图存放地址,tar_path是结果图保存地址,r为计算k值时的σ'阈值。

另一部分是根据奇异值以及给定的阈值θ自动计算阈值k的模块,并将k传回主模块。

def k_value(r, *args) :
    new  = list(args)
    mean = np.mean(new)
    std  = np.std(new, ddof = 1)

    for i in range(len(new)) :
        new[i] = (new[i] - mean) / std


    sigma_date = []
    num_data   = []

    for i in range(len(new)) :
        if i == 0 :
            pass
        else :
            sigma = new[i-1] - new[i]
            #print(sigma)
            sigma_date.append(sigma)

            j = i-1
            num_data.append(j)

    plt.plot(num_data, sigma_date)        # 绘制输入的结果和值的结果
    plt.title("Sigma data", fontsize=15)  # 标题
    plt.xlabel("num", fontsize=15)        # 横坐标
    plt.ylabel("sigma", fontsize=15)      # 纵坐标
    plt.show()  # 显示
    #cv2.waitKey()


    for i in range(len(sigma_date)) :
        if sigma_date[i] <= r :
            print("Singular value is %g,number is %g" %(sigma_date[i], i))
            return i

不算结果的结果展示

本文作者使用OLED显示屏Mura缺陷做测试,缺陷类型为彩斑和S向条纹状Mura,结果现象较为明显。由于这是项目工作,所以一些数据不方便展示,放一张完全无关的图片测试一下,没有半毛钱参考价值,看看效果而已。

原图如下,k值设为5。

检测图如下。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

千穹凌帝

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值