Nikies Antwort löste mein Problem, aber seine Antwort war in Mathematica. Also dachte ich, ich sollte hier seine OpenCV-Anpassung geben. Aber nach der Implementierung konnte ich sehen, dass OpenCV-Code viel größer ist als Nikies Mathematica-Code. Außerdem konnte ich in OpenCV keine von Nikie durchgeführte Interpolationsmethode finden (obwohl dies mit scipy möglich ist, werde ich es zu gegebener Zeit mitteilen.)
1. Bildvorverarbeitung (Schließvorgang)
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)
Ergebnis:
![Ergebnis des Schließens](https://i.stack.imgur.com/VFgm5.png)
2. Sudoku-Quadrat finden und Maskenbild erstellen
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)
Ergebnis:
![Geben Sie hier die Bildbeschreibung ein](https://i.stack.imgur.com/bJdQp.png)
3. Vertikale Linien finden
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()
Ergebnis:
![Geben Sie hier die Bildbeschreibung ein](https://i.stack.imgur.com/GVu0x.png)
4. Horizontale Linien finden
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()
Ergebnis:
![Geben Sie hier die Bildbeschreibung ein](https://i.stack.imgur.com/9kP9q.png)
Natürlich ist dieser nicht so gut.
5. Gitterpunkte finden
res = cv2.bitwise_and(closex,closey)
Ergebnis:
![Geben Sie hier die Bildbeschreibung ein](https://i.stack.imgur.com/KrQSi.png)
6. Behebung der Mängel
Hier macht Nikie eine Art Interpolation, über die ich nicht viel weiß. Und ich konnte keine entsprechende Funktion für dieses OpenCV finden. (Vielleicht ist es da, ich weiß es nicht).
Schauen Sie sich dieses SOF an, in dem erklärt wird, wie dies mit SciPy gemacht wird, das ich nicht verwenden möchte: Bildtransformation in OpenCV
Also habe ich hier 4 Ecken von jedem Unterquadrat genommen und auf jedes eine Warp-Perspektive angewendet.
Dafür finden wir zuerst die Zentroide.
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))
Die resultierenden Zentroide werden jedoch nicht sortiert. Schauen Sie sich das Bild unten an, um die Reihenfolge zu sehen:
![Geben Sie hier die Bildbeschreibung ein](https://i.stack.imgur.com/9zm61.png)
Also sortieren wir sie von links nach rechts, von oben nach unten.
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))
Nun siehe unten ihre Reihenfolge:
![Geben Sie hier die Bildbeschreibung ein](https://i.stack.imgur.com/m298c.png)
Schließlich wenden wir die Transformation an und erstellen ein neues Bild der Größe 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()
Ergebnis:
![Geben Sie hier die Bildbeschreibung ein](https://i.stack.imgur.com/Docxa.png)
Das Ergebnis ist fast das gleiche wie bei Nikie, aber die Codelänge ist groß. Möglicherweise gibt es bessere Methoden, aber bis dahin funktioniert dies in Ordnung.
Grüße ARK.