Câu trả lời của Nikie đã giải quyết vấn đề của tôi, nhưng câu trả lời của anh ấy là ở Mathicala. Vì vậy, tôi nghĩ rằng tôi nên cung cấp thích ứng OpenCV của nó ở đây. Nhưng sau khi triển khai tôi có thể thấy rằng mã OpenCV lớn hơn nhiều so với mã toán học của nikie. Ngoài ra, tôi không thể tìm thấy phương pháp nội suy được thực hiện bởi nikie trong OpenCV (mặc dù nó có thể được thực hiện bằng scipy, tôi sẽ nói với nó khi thời gian đến.)
1. Xử lý trước hình ảnh (thao tác đóng)
import cv2
import numpy as np
img = cv2.imread('dave.jpg')
img = cv2.GaussianBlur(img,(5,5),0)
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
mask = np.zeros((gray.shape),np.uint8)
kernel1 = cv2.getStructuringElement(cv2.MORPH_ELLIPSE,(11,11))
close = cv2.morphologyEx(gray,cv2.MORPH_CLOSE,kernel1)
div = np.float32(gray)/(close)
res = np.uint8(cv2.normalize(div,div,0,255,cv2.NORM_MINMAX))
res2 = cv2.cvtColor(res,cv2.COLOR_GRAY2BGR)
Kết quả :
2. Tìm quảng trường Sudoku và tạo hình ảnh mặt nạ
thresh = cv2.adaptiveThreshold(res,255,0,1,19,2)
contour,hier = cv2.findContours(thresh,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)
max_area = 0
best_cnt = None
for cnt in contour:
area = cv2.contourArea(cnt)
if area > 1000:
if area > max_area:
max_area = area
best_cnt = cnt
cv2.drawContours(mask,[best_cnt],0,255,-1)
cv2.drawContours(mask,[best_cnt],0,0,2)
res = cv2.bitwise_and(res,mask)
Kết quả :
3. Tìm đường dọc
kernelx = cv2.getStructuringElement(cv2.MORPH_RECT,(2,10))
dx = cv2.Sobel(res,cv2.CV_16S,1,0)
dx = cv2.convertScaleAbs(dx)
cv2.normalize(dx,dx,0,255,cv2.NORM_MINMAX)
ret,close = cv2.threshold(dx,0,255,cv2.THRESH_BINARY+cv2.THRESH_OTSU)
close = cv2.morphologyEx(close,cv2.MORPH_DILATE,kernelx,iterations = 1)
contour, hier = cv2.findContours(close,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)
for cnt in contour:
x,y,w,h = cv2.boundingRect(cnt)
if h/w > 5:
cv2.drawContours(close,[cnt],0,255,-1)
else:
cv2.drawContours(close,[cnt],0,0,-1)
close = cv2.morphologyEx(close,cv2.MORPH_CLOSE,None,iterations = 2)
closex = close.copy()
Kết quả :
4. Tìm đường ngang
kernely = cv2.getStructuringElement(cv2.MORPH_RECT,(10,2))
dy = cv2.Sobel(res,cv2.CV_16S,0,2)
dy = cv2.convertScaleAbs(dy)
cv2.normalize(dy,dy,0,255,cv2.NORM_MINMAX)
ret,close = cv2.threshold(dy,0,255,cv2.THRESH_BINARY+cv2.THRESH_OTSU)
close = cv2.morphologyEx(close,cv2.MORPH_DILATE,kernely)
contour, hier = cv2.findContours(close,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)
for cnt in contour:
x,y,w,h = cv2.boundingRect(cnt)
if w/h > 5:
cv2.drawContours(close,[cnt],0,255,-1)
else:
cv2.drawContours(close,[cnt],0,0,-1)
close = cv2.morphologyEx(close,cv2.MORPH_DILATE,None,iterations = 2)
closey = close.copy()
Kết quả :
Tất nhiên, cái này không tốt lắm.
5. Tìm điểm lưới
res = cv2.bitwise_and(closex,closey)
Kết quả :
6. Sửa lỗi
Ở đây, nikie thực hiện một số loại nội suy, mà tôi không có nhiều kiến thức. Và tôi không thể tìm thấy bất kỳ chức năng tương ứng cho OpenCV này. (có thể là nó ở đó, tôi không biết).
Hãy xem SOF này giải thích cách thực hiện điều này bằng SciPy, cái mà tôi không muốn sử dụng: Chuyển đổi hình ảnh trong OpenCV
Vì vậy, ở đây tôi lấy 4 góc của mỗi hình vuông phụ và áp dụng phối cảnh dọc cho mỗi góc.
Đối với điều đó, đầu tiên chúng ta tìm thấy các trung tâm.
contour, hier = cv2.findContours(res,cv2.RETR_LIST,cv2.CHAIN_APPROX_SIMPLE)
centroids = []
for cnt in contour:
mom = cv2.moments(cnt)
(x,y) = int(mom['m10']/mom['m00']), int(mom['m01']/mom['m00'])
cv2.circle(img,(x,y),4,(0,255,0),-1)
centroids.append((x,y))
Nhưng kết quả là centroid sẽ không được sắp xếp. Kiểm tra hình ảnh dưới đây để xem thứ tự của họ:
Vì vậy, chúng tôi sắp xếp chúng từ trái sang phải, từ trên xuống dưới.
centroids = np.array(centroids,dtype = np.float32)
c = centroids.reshape((100,2))
c2 = c[np.argsort(c[:,1])]
b = np.vstack([c2[i*10:(i+1)*10][np.argsort(c2[i*10:(i+1)*10,0])] for i in xrange(10)])
bm = b.reshape((10,10,2))
Bây giờ xem bên dưới thứ tự của họ:
Cuối cùng, chúng tôi áp dụng chuyển đổi và tạo một hình ảnh mới có kích thước 450x450.
output = np.zeros((450,450,3),np.uint8)
for i,j in enumerate(b):
ri = i/10
ci = i%10
if ci != 9 and ri!=9:
src = bm[ri:ri+2, ci:ci+2 , :].reshape((4,2))
dst = np.array( [ [ci*50,ri*50],[(ci+1)*50-1,ri*50],[ci*50,(ri+1)*50-1],[(ci+1)*50-1,(ri+1)*50-1] ], np.float32)
retval = cv2.getPerspectiveTransform(src,dst)
warp = cv2.warpPerspective(res2,retval,(450,450))
output[ri*50:(ri+1)*50-1 , ci*50:(ci+1)*50-1] = warp[ri*50:(ri+1)*50-1 , ci*50:(ci+1)*50-1].copy()
Kết quả :
Kết quả gần giống như của nikie, nhưng độ dài mã lớn. Có thể, phương pháp tốt hơn có sẵn ngoài đó, nhưng cho đến lúc đó, điều này hoạt động tốt.
Trân trọng ARK.