คำตอบของ Nikie แก้ปัญหาของฉันได้ แต่คำตอบของเขาอยู่ใน Mathematica ดังนั้นฉันคิดว่าฉันควรจะปรับ OpenCV ที่นี่ แต่หลังจากนำไปใช้ฉันจะเห็นว่าโค้ด OpenCV นั้นใหญ่กว่าโค้ด mathematica ของ nikie มาก และฉันไม่สามารถหาวิธีการแก้ไขที่ทำโดย nikie ใน OpenCV (แม้ว่าจะสามารถทำได้โดยใช้ scipy ฉันจะบอกได้ว่าเมื่อถึงเวลา)
1. การประมวลผลภาพล่วงหน้า (ปิดการทำงาน)
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)
ผลลัพธ์ :
2. ค้นหาจัตุรัส Sudoku และสร้างรูปหน้ากาก
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)
ผลลัพธ์ :
3. การค้นหาเส้นแนวตั้ง
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()
ผลลัพธ์ :
4. การค้นหาเส้นแนวนอน
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()
ผลลัพธ์ :
แน่นอนว่าอันนี้ไม่ค่อยดี
5. ค้นหาคะแนนกริด
res = cv2.bitwise_and(closex,closey)
ผลลัพธ์ :
6. การแก้ไขข้อบกพร่อง
นี่นิกกี้ทำการแก้ไขบางอย่างซึ่งฉันไม่มีความรู้มากนัก และฉันไม่พบฟังก์ชันที่เกี่ยวข้องสำหรับ OpenCV นี้ (อาจจะเป็นที่นั่นฉันไม่รู้)
ตรวจสอบ SOF นี้ซึ่งอธิบายวิธีการใช้ SciPy ซึ่งฉันไม่ต้องการใช้: การแปลงภาพใน OpenCV
ดังนั้นที่นี่ฉันเอา 4 มุมของแต่ละตารางย่อยและใช้มุมมองวิปริตกับแต่ละมุม
ก่อนอื่นเราจะหาเซนทรอยด์
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))
แต่เซนทรอยด์ที่ได้จะไม่ถูกจัดเรียง ตรวจสอบภาพด้านล่างเพื่อดูคำสั่งซื้อ:
ดังนั้นเราจึงจัดเรียงมันจากซ้ายไปขวาบนลงล่าง
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))
ตอนนี้ดูด้านล่างคำสั่งของพวกเขา:
ในที่สุดเราใช้การแปลงและสร้างภาพใหม่ขนาด 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()
ผลลัพธ์ :
ผลลัพธ์ใกล้เคียงกับของ nikie แต่ความยาวโค้ดมีขนาดใหญ่ อาจเป็นไปได้ว่ามีวิธีการที่ดีกว่า แต่ก่อนหน้านี้ก็ใช้งานได้ดี
ขอแสดงความนับถือ ARK