├── picture 1.jpg ├── picture 2.jpg ├── picture 3.jpg ├── picture 4.jpg ├── Testpicture 1.jpg ├── Testpicture 2.jpg ├── Testpicture 3.jpg ├── Testpicture 4.jpg ├── LCD_Mura_detection_Principle.doc ├── README.md └── MDL_mura_AOI_main.py /picture 1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Anton-Chen-0413/LCD-Module-Process-Mura-Defect-Detection/HEAD/picture 1.jpg -------------------------------------------------------------------------------- /picture 2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Anton-Chen-0413/LCD-Module-Process-Mura-Defect-Detection/HEAD/picture 2.jpg -------------------------------------------------------------------------------- /picture 3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Anton-Chen-0413/LCD-Module-Process-Mura-Defect-Detection/HEAD/picture 3.jpg -------------------------------------------------------------------------------- /picture 4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Anton-Chen-0413/LCD-Module-Process-Mura-Defect-Detection/HEAD/picture 4.jpg -------------------------------------------------------------------------------- /Testpicture 1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Anton-Chen-0413/LCD-Module-Process-Mura-Defect-Detection/HEAD/Testpicture 1.jpg -------------------------------------------------------------------------------- /Testpicture 2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Anton-Chen-0413/LCD-Module-Process-Mura-Defect-Detection/HEAD/Testpicture 2.jpg -------------------------------------------------------------------------------- /Testpicture 3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Anton-Chen-0413/LCD-Module-Process-Mura-Defect-Detection/HEAD/Testpicture 3.jpg -------------------------------------------------------------------------------- /Testpicture 4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Anton-Chen-0413/LCD-Module-Process-Mura-Defect-Detection/HEAD/Testpicture 4.jpg -------------------------------------------------------------------------------- /LCD_Mura_detection_Principle.doc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Anton-Chen-0413/LCD-Module-Process-Mura-Defect-Detection/HEAD/LCD_Mura_detection_Principle.doc -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # LCD-Module-Process-Mura-Defect-Detection 2 | Use Laplacian Of Gaussian function filter to generate 3 types of convolution and detect 3 types of mura respectively. The first type: 2D 3x3 convolution to detect defect point and line mura. Type 2: 1D 1x15 convolution, detect H-Band mura The third type: 1D 15x1 convolution, detect V-Band mura 3 | # Laplacian Of Gaussian(LOG) Filter principle 4 | The principle of detecting defects is mainly based on the LOG filter for image processing. The LOG filter is mainly combined with the second-order Laplacian 5 | A filter with differential zero crossing point detection edge function and Gaussian noise smoothing function 6 | -------------------------------------------------------------------------------- /MDL_mura_AOI_main.py: -------------------------------------------------------------------------------- 1 | from math import e 2 | #import cv2 3 | #import pylab as plt 4 | from PIL import Image 5 | import numpy as np 6 | import sys 7 | import os 8 | import datetime 9 | import tkinter 10 | import time 11 | import csv 12 | 13 | window = tkinter.Tk() 14 | window.title('AOI光學自動檢測系統') 15 | window.geometry('400x100') 16 | 17 | def action(): 18 | def LOG(x, y, c): 19 | return (1 / (c ** 4)) * ((x ** 2 + y ** 2) / (c ** 2) - 2) * e ** (-(x ** 2 + y ** 2) / (2 * c ** 2)) 20 | 21 | def FilterSize(c): 22 | return (6 * c + 1) 23 | 24 | #[Wi權重分析] 25 | 26 | #-----------------------------------------------Point 2D Filter 27 | 28 | #print('輸入2D濾波器西格瑪Size:') 29 | c = num1.get() 30 | 31 | print(int(FilterSize(c)), 'X', int(FilterSize(c))) 32 | 33 | n = (int(FilterSize(c)) - 1) / 2 34 | first = n 35 | end = - n 36 | LOGF = [] #連續曲線LOG濾波器 37 | for i in range(int(first), int(end) - 1, -1): 38 | y = i 39 | a = [] 40 | for j in range(int(end), int(first) + 1): 41 | x = j 42 | a.append(- LOG(x, y, c)) 43 | LOGF.append(a) 44 | 45 | T = 0 46 | F = 0 47 | for i in range(len(LOGF)): 48 | for j in range(len(LOGF)): 49 | if LOGF[i][j] < 0: 50 | T += LOGF[i][j] 51 | else: 52 | F += LOGF[i][j] 53 | 54 | N = 1 #倍數 55 | 56 | LOGFilter = [] #離散LOG濾波器 57 | for i in range(len(LOGF)): 58 | p = [] 59 | for j in range(len(LOGF)): 60 | if LOGF[i][j] < 0: 61 | p.append(N * - LOGF[i][j] / T) 62 | else: 63 | p.append(N * LOGF[i][j] / F) 64 | LOGFilter.append(p) 65 | 66 | LOGFilter_array = np.array(LOGFilter) 67 | 68 | #-----------------------------------------------H Band 1D Filter 69 | 70 | #print('輸入1D濾波器西格瑪Size:') 71 | cb = num2.get() 72 | 73 | print(int(FilterSize(cb)), 'X', 1) 74 | 75 | n = (int(FilterSize(cb)) - 1) / 2 76 | first = n 77 | end = - n 78 | 79 | LOGBF = [] #連續曲線LOG濾波器 80 | y = 0 81 | for i in range(int(end), int(first) + 1): 82 | x = i 83 | LOGBF.append(- LOG(x, y, cb)) 84 | T = 0 85 | F = 0 86 | for i in range(len(LOGBF)): 87 | if LOGBF[i] < 0: 88 | T += LOGBF[i] 89 | else: 90 | F += LOGBF[i] 91 | 92 | N = 1#倍數 93 | LOGBFilter1 = [] #離散LOG濾波器 94 | for i in range(len(LOGBF)): 95 | if LOGBF[i] < 0: 96 | LOGBFilter1.append(N * - LOGBF[i] / T) 97 | else: 98 | LOGBFilter1.append(N * LOGBF[i] / F) 99 | 100 | LOGBFilter = [] 101 | LOGBFilter.append(LOGBFilter1) 102 | 103 | LOGBFilter_array = np.array(LOGBFilter) 104 | 105 | #-----------------------------------------------V Band 1D Filter 106 | 107 | #print('輸入1Dv濾波器西格瑪Size:') 108 | cv = num5.get() 109 | 110 | print(1, 'X', int(FilterSize(cv))) 111 | 112 | n = (int(FilterSize(cv)) - 1) / 2 113 | first = n 114 | end = - n 115 | LOGVF = [] #連續曲線LOG濾波器 116 | for i in range(int(first), int(end) - 1, -1): 117 | y = i 118 | x = 0 119 | v = [] 120 | v.append(-LOG(x, y, cv)) 121 | LOGVF.append(v) 122 | #print(LOGVF) 123 | 124 | 125 | T = 0 126 | F = 0 127 | for i in range(len(LOGVF)): 128 | if LOGVF[i][0] < 0: 129 | T += LOGVF[i][0] 130 | else: 131 | F += LOGVF[i][0] 132 | 133 | N = 1 #倍數 134 | 135 | LOGVFilter = [] #離散LOG濾波器 136 | for i in range(len(LOGVF)): 137 | pv = [] 138 | if LOGVF[i][0] < 0: 139 | pv.append(N * - LOGVF[i][0] / T) 140 | else: 141 | pv.append(N * LOGVF[i][0] / F) 142 | LOGVFilter.append(pv) 143 | 144 | LOGVFilter_array = np.array(LOGVFilter) 145 | 146 | #=================================================================================================== 147 | 148 | #[Xi輸入值 x Wi權重分析] 149 | #-----------------------------------------------Point 2D Filter 影像卷積convolution處理 150 | 151 | with open('ID座標資料.csv', 'a', encoding='cp950', newline='') as ff: 152 | idx = 0 153 | while True: 154 | t1 = datetime.datetime.now() 155 | idx += 1 156 | filename = "picture {0}.jpg".format(idx) 157 | if os.path.exists(filename): 158 | img_pil = Image.open(filename)#開啟CCD擷取的不良照片 159 | X, Y = img_pil.size 160 | img_gray = img_pil.convert('L')#轉換成灰階圖片 161 | img_p = np.array(img_pil)#原圖片轉成陣列 162 | img = np.array(img_gray)#灰階圖片轉成陣列 163 | 164 | AfterImage = [[0] * X for _ in range(Y)] 165 | m = 0 166 | n = 0 167 | W = 0 168 | for j in range(Y - int(FilterSize(c)) // 2 * 2): #卷積陣列計算 169 | for i in range(X - int(FilterSize(c)) // 2 * 2): 170 | W = img[j:int(FilterSize(c)) + m, i:int(FilterSize(c)) + n] * LOGFilter_array 171 | #print(sum(sum(W)),j, int(FilterSize(c)) + m, i, int(FilterSize(c)) + n) 172 | AfterImage[j + int(FilterSize(c)) // 2][i + int(FilterSize(c)) // 2] = sum(sum(W)) 173 | n += 1 174 | n = 0 175 | m += 1 176 | #-----------------------------------------------H Band 1D Filter 影像卷積convolution處理 177 | AfterImageB = [[0] * X for _ in range(Y)] 178 | n = 0 179 | W = 0 180 | for j in range(Y): #卷積陣列計算 181 | for i in range(X - int(FilterSize(cb)) // 2 * 2): 182 | W = img[j, i:int(FilterSize(cb)) + n] * LOGBFilter_array 183 | AfterImageB[j][i + int(FilterSize(cb)) // 2] = sum(sum(W)) 184 | n += 1 185 | n = 0 186 | 187 | #----------------------------------------------V Band 1D Filter 影像卷積convolution處理 188 | AfterImageV = [[0] * X for _ in range(Y)] 189 | W = 0 190 | m = 0 191 | for j in range(Y - int(FilterSize(cv)) // 2 * 2): 192 | for i in range(X): 193 | W = img[j:int(FilterSize(cv)) + m, i] * LOGVFilter_array[0:int(FilterSize(cv)), 0] 194 | AfterImageV[j + int(FilterSize(cv)) // 2][i] = sum(W) 195 | m += 1 196 | 197 | #============================================================================================= 198 | 199 | #[閥值] 200 | LimitValue = num3.get()#LOG門檻設定值 201 | LimitValueB = num4.get()#LOG門檻設定值 202 | LimitValueV = num6.get()#LOG門檻設定值 203 | 204 | 205 | for y in range(Y): 206 | for x in range(X): 207 | if abs(AfterImage[y][x]) > LimitValue: 208 | img_p[y, x, 0] = 255 209 | img_p[y, x, 1] = 0 210 | img_p[y, x, 2] = 0 211 | 212 | 213 | if abs(AfterImageB[y][x]) > LimitValueB: 214 | img_p[y, x, 0] = 0 215 | img_p[y, x, 1] = 255 216 | img_p[y, x, 2] = 0 217 | 218 | 219 | if abs(AfterImageV[y][x]) > LimitValueV: 220 | img_p[y, x, 0] = 0 221 | img_p[y, x, 1] = 0 222 | img_p[y, x, 2] = 255 223 | 224 | 225 | 226 | 227 | 228 | im = Image.fromarray(img_p) 229 | im.show() 230 | T = "Tes2tpicture {0}.jpg".format(idx) 231 | im.save(T) 232 | 233 | 234 | 235 | 236 | k = [[0 for i in range(X)] for j in range(Y)] 237 | kb = [[0 for i in range(X)] for j in range(Y)] 238 | kv = [[0 for i in range(X)] for j in range(Y)] 239 | 240 | u = [[-1, 0], [-1, 1], [0, 1], [1, 1], [1, 0], [1, -1], [0, -1], [-1, -1]] 241 | 242 | def limit():#不良紅框圈製作 243 | tempxmax = 0 244 | tempymax = 0 245 | tempxmin = temp[0][0] 246 | tempymin = temp[0][1] 247 | for y in range(len(temp)): 248 | if temp[y][1] > tempymax: 249 | tempymax = temp[y][1] 250 | if temp[y][0] > tempxmax: 251 | tempxmax = temp[y][0] 252 | if temp[y][1] < tempymin: 253 | tempymin = temp[y][1] 254 | if temp[y][0] 0: 285 | j, i = stack.pop() 286 | t.append([j, i]) 287 | for e in u: 288 | jj, ii = j + e[0], i + e[1] 289 | if 0 <= jj < Y and 0 <= ii < X: 290 | x = A[jj][ii] 291 | if abs(x) > L and k[jj][ii] != 1: 292 | t.append([jj, ii]) 293 | stack.append([jj, ii]) 294 | k[jj][ii] = 1 295 | 296 | 297 | result = [] 298 | Defect = [] 299 | for j in range(Y): 300 | for i in range(X): 301 | temp = [] 302 | if abs(AfterImage[j][i]) > LimitValue and k[j][i] != 1: 303 | check(AfterImage, j, i, temp, LimitValue, k) 304 | 305 | if abs(AfterImageB[j][i]) > LimitValueB and kb[j][i] != 1: 306 | check(AfterImageB, j, i, temp, LimitValueB, kb) 307 | 308 | if abs(AfterImageV[j][i]) > LimitValueV and kv[j][i] != 1: 309 | check(AfterImageV, j, i, temp, LimitValueV, kv) 310 | 311 | if len(temp) > 0: 312 | #limit() 313 | imi, jmi = limit() 314 | Defect.append([imi, jmi]) 315 | result.append(temp) 316 | #============================================================================ 317 | im = Image.fromarray(img_p) 318 | im.show() 319 | T = "Testpicture {0}.jpg".format(idx) 320 | im.save(T) 321 | print() 322 | print("不良項目數量:", len(result)) 323 | t2 = datetime.datetime.now() 324 | print() 325 | print("檢查時間:", t2 - t1) 326 | t3 = time.ctime() 327 | 328 | data = [[T, Defect, t3, len(result)]] 329 | csv_writer = csv.writer(ff) 330 | csv_writer.writerows(data) 331 | else: 332 | break 333 | 334 | finish.set('E~N~D') 335 | 336 | 337 | #操作介面====================================================================== 338 | 339 | num1 = tkinter.IntVar() 340 | num2 = tkinter.IntVar() 341 | num3 = tkinter.DoubleVar() 342 | num4 = tkinter.DoubleVar() 343 | num5 = tkinter.IntVar() 344 | num6 = tkinter.DoubleVar() 345 | finish = tkinter.StringVar() 346 | 347 | #-------------------------------------------------------------------- 348 | TwoD_f_item = tkinter.Entry(window, width=10, textvariable=num1) 349 | OneDB_f_item = tkinter.Entry(window, width=10, textvariable=num2) 350 | OneDV_f_item = tkinter.Entry(window, width=10, textvariable=num5) 351 | TwoD_f_item.grid(row=1, column=1) 352 | OneDB_f_item.grid(row=2, column=1) 353 | OneDV_f_item.grid(row=3, column=1) 354 | 355 | TwoD_limitvalue = tkinter.Entry(window, width=10, textvariable=num3) 356 | OneDB_limitvalue = tkinter.Entry(window, width=10, textvariable=num4) 357 | OneDV_limitvalue = tkinter.Entry(window, width=10, textvariable=num6) 358 | TwoD_limitvalue.grid(row=1, column=2) 359 | OneDB_limitvalue.grid(row=2, column=2) 360 | OneDV_limitvalue.grid(row=3, column=2) 361 | 362 | #------------------------------------------------------------------- 363 | 364 | btn1 = tkinter.Button(window, width=15, text='Action', command=action) 365 | btn1.grid(row=0, column=3) 366 | 367 | label = tkinter.Label(window, width=15, textvariable=finish) 368 | label.grid(row=1, column=3) 369 | 370 | #-------------------------------------------------------------------- 371 | 372 | TwoD_f_label = tkinter.Label(window, width=10, text='2D濾波器') 373 | TwoD_f_label.grid(row=1, column=0) 374 | 375 | OneDB_f_label = tkinter.Label(window, width=10, text='1DH濾波器') 376 | OneDB_f_label.grid(row=2, column=0) 377 | 378 | OneDV_f_label = tkinter.Label(window, width=10, text='1DV濾波器') 379 | OneDV_f_label.grid(row=3, column=0) 380 | 381 | filtervalue_label = tkinter.Label(window, width=10, text='西格瑪參數') 382 | filtervalue_label.grid(row=0, column=1) 383 | 384 | limitvalue_label = tkinter.Label(window, width=10, text='閥值') 385 | limitvalue_label.grid(row=0, column=2) 386 | 387 | 388 | 389 | window.mainloop() 390 | 391 | --------------------------------------------------------------------------------