Come rimuovere i difetti di convessità in un quadrato di Sudoku?


193

Stavo facendo un progetto divertente: risolvere un Sudoku da un'immagine di input usando OpenCV (come in Google Goggles ecc.). E ho completato l'attività, ma alla fine ho trovato un piccolo problema per il quale sono venuto qui.

Ho fatto la programmazione usando l'API Python di OpenCV 2.3.1.

Di seguito è quello che ho fatto:

  1. Leggi l'immagine
  2. Trova i contorni
  3. Seleziona quello con la massima area (e anche in qualche modo equivalente al quadrato).
  4. Trova i punti d'angolo.

    ad es. dato di seguito:

    inserisci qui la descrizione dell'immagine

    ( Notare qui che la linea verde coincide correttamente con il vero confine del Sudoku, quindi il Sudoku può essere deformato correttamente . Controlla l'immagine successiva)

  5. deforma l'immagine in un quadrato perfetto

    ad es. immagine:

    inserisci qui la descrizione dell'immagine

  6. Esegui OCR (per il quale ho usato il metodo che ho dato in OCR Simple Digit Recognition in OpenCV-Python )

E il metodo ha funzionato bene.

Problema:

Dai un'occhiata a questa immagine.

L'esecuzione del passaggio 4 su questa immagine fornisce il risultato seguente:

inserisci qui la descrizione dell'immagine

La linea rossa tracciata è il contorno originale che è il vero contorno del confine del sudoku.

La linea verde tracciata è un contorno approssimativo che sarà il contorno dell'immagine deformata.

Che ovviamente c'è differenza tra la linea verde e la linea rossa sul bordo superiore del sudoku. Quindi, mentre ordito, non ottengo il confine originale del Sudoku.

La mia domanda :

Come posso deformare l'immagine sul confine corretto del Sudoku, ovvero la linea rossa O come posso rimuovere la differenza tra linea rossa e linea verde? Esiste un metodo per questo in OpenCV?


1
Stai effettuando il rilevamento in base ai punti d'angolo, su cui le linee rossa e verde concordano. Non conosco OpenCV, ma presumibilmente vorrai rilevare le linee tra quei punti d'angolo e curvare in base a quello.
Dougal,

Forse forzare le linee che collegano i punti d'angolo in modo che coincidano con pixel neri pesanti nell'immagine. Cioè, invece di lasciare che le linee verdi trovino solo una linea retta tra i punti degli angoli, costringili a attraversare pesanti pixel neri. Questo renderà il tuo problema sostanzialmente più difficile, penso, e non conosco alcun built-in OpenCV che ti sarà immediatamente utile.
ely,

@ Dougal: penso che la linea verde tracciata sia la linea retta approssimativa della linea rossa. quindi è la linea tra quei punti d'angolo. Quando ordito secondo la linea verde, ottengo una linea rossa curva nella parte superiore dell'immagine deformata. (spero che tu capisca, la mia spiegazione sembra un po 'male)
Abid Rahman K

@ EMS: penso che la linea rossa disegnata sia esattamente al confine del sudoku. Ma il problema è, come deformare l'immagine esattamente sul bordo del sudoku. (Voglio dire, il problema è con la deformazione, cioè la conversione di quei bordi curvi in ​​un quadrato esatto, come ho mostrato nella seconda immagine)
Abid Rahman K

Risposte:


252

Ho una soluzione che funziona, ma dovrai tradurla tu stesso in OpenCV. È scritto in Mathematica.

Il primo passo è regolare la luminosità dell'immagine, dividendo ciascun pixel con il risultato di un'operazione di chiusura:

src = ColorConvert[Import["http://davemark.com/images/sudoku.jpg"], "Grayscale"];
white = Closing[src, DiskMatrix[5]];
srcAdjusted = Image[ImageData[src]/ImageData[white]]

inserisci qui la descrizione dell'immagine

Il prossimo passo è trovare l'area del sudoku, così posso ignorare (mascherare) lo sfondo. Per questo, utilizzo l'analisi dei componenti collegati e seleziono il componente con l'area convessa più grande:

components = 
  ComponentMeasurements[
    ColorNegate@Binarize[srcAdjusted], {"ConvexArea", "Mask"}][[All, 
    2]];
largestComponent = Image[SortBy[components, First][[-1, 2]]]

inserisci qui la descrizione dell'immagine

Compilando questa immagine, ottengo una maschera per la griglia del sudoku:

mask = FillingTransform[largestComponent]

inserisci qui la descrizione dell'immagine

Ora, posso usare un filtro derivato del 2 ° ordine per trovare le linee verticali e orizzontali in due immagini separate:

lY = ImageMultiply[MorphologicalBinarize[GaussianFilter[srcAdjusted, 3, {2, 0}], {0.02, 0.05}], mask];
lX = ImageMultiply[MorphologicalBinarize[GaussianFilter[srcAdjusted, 3, {0, 2}], {0.02, 0.05}], mask];

inserisci qui la descrizione dell'immagine

Uso di nuovo l'analisi dei componenti collegati per estrarre le linee della griglia da queste immagini. Le linee della griglia sono molto più lunghe delle cifre, quindi posso usare la lunghezza del calibro per selezionare solo i componenti collegati alle linee della griglia. Ordinandoli per posizione, ottengo immagini maschera 2x10 per ciascuna delle linee della griglia verticale / orizzontale nell'immagine:

verticalGridLineMasks = 
  SortBy[ComponentMeasurements[
      lX, {"CaliperLength", "Centroid", "Mask"}, # > 100 &][[All, 
      2]], #[[2, 1]] &][[All, 3]];
horizontalGridLineMasks = 
  SortBy[ComponentMeasurements[
      lY, {"CaliperLength", "Centroid", "Mask"}, # > 100 &][[All, 
      2]], #[[2, 2]] &][[All, 3]];

inserisci qui la descrizione dell'immagine

Quindi prendo ciascuna coppia di linee della griglia verticali / orizzontali, le dilato, calcolo l'intersezione pixel per pixel e calcolo il centro del risultato. Questi punti sono le intersezioni della linea della griglia:

centerOfGravity[l_] := 
 ComponentMeasurements[Image[l], "Centroid"][[1, 2]]
gridCenters = 
  Table[centerOfGravity[
    ImageData[Dilation[Image[h], DiskMatrix[2]]]*
     ImageData[Dilation[Image[v], DiskMatrix[2]]]], {h, 
    horizontalGridLineMasks}, {v, verticalGridLineMasks}];

inserisci qui la descrizione dell'immagine

L'ultimo passo è definire due funzioni di interpolazione per il mapping X / Y attraverso questi punti e trasformare l'immagine usando queste funzioni:

fnX = ListInterpolation[gridCenters[[All, All, 1]]];
fnY = ListInterpolation[gridCenters[[All, All, 2]]];
transformed = 
 ImageTransformation[
  srcAdjusted, {fnX @@ Reverse[#], fnY @@ Reverse[#]} &, {9*50, 9*50},
   PlotRange -> {{1, 10}, {1, 10}}, DataRange -> Full]

inserisci qui la descrizione dell'immagine

Tutte le operazioni sono funzioni di base per l'elaborazione delle immagini, quindi ciò dovrebbe essere possibile anche in OpenCV. La trasformazione delle immagini basata su spline potrebbe essere più difficile, ma non credo che ne abbia davvero bisogno. Probabilmente usando la trasformazione prospettica che usi ora su ogni singola cella otterrai risultati abbastanza buoni.


3
Oh mio Dio !!!!!!!!! È stato meraviglioso. Questo è davvero fantastico. Proverò a farlo in OpenCV. Spero che mi aiuterai con dettagli su determinate funzioni e terminologia ... Grazie.
Abid Rahman K,

@arkiaz: Non sono un esperto di OpenCV, ma ti aiuterò se posso, certo.
Niki,

Potete per favore spiegare a cosa serve la funzione "chiusura"? quello che voglio dire è cosa succede in background? Nella documentazione, si dice che la chiusura rimuove il rumore di sale e pepe? La chiusura del filtro passa-basso?
Abid Rahman K,

2
Risposta incredibile! Dove ti è venuta l'idea di dividere per la chiusura per normalizzare la luminosità dell'immagine? Sto cercando di migliorare la velocità di questo metodo, poiché la divisione in virgola mobile è dolorosamente lenta sui telefoni cellulari. Hai qualche suggerimento? @AbidRahmanK
1 ''

1
@ 1 *: Penso che si chiami "regolazione dell'immagine bianca". Non chiedermi dove l'ho letto, è uno strumento standard di elaborazione delle immagini. Il modello alla base dell'idea è semplice: la quantità di luce riflessa da una superficie (lambertiana) è solo la luminosità della superficie per la quantità di luce che un corpo bianco nella stessa posizione dovrebbe riflettere. Stimare la luminosità apparente di un corpo bianco nella stessa posizione, dividere la luminosità effettiva per quella e ottenere la luminosità della superficie.
Niki,

209

La risposta di Nikie ha risolto il mio problema, ma la sua risposta era in Mathematica. Quindi ho pensato di dare il suo adattamento OpenCV qui. Ma dopo l'implementazione ho visto che il codice OpenCV è molto più grande del codice matematico di Nikie. Inoltre, non sono riuscito a trovare il metodo di interpolazione fatto da Nikie in OpenCV (anche se può essere fatto usando Scipy, lo dirò quando sarà il momento.)

1. Preelaborazione immagine (operazione di chiusura)

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)

Risultato:

Risultato della chiusura

2. Trovare Sudoku Square e creare un'immagine maschera

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)

Risultato:

inserisci qui la descrizione dell'immagine

3. Ricerca di linee verticali

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()

Risultato:

inserisci qui la descrizione dell'immagine

4. Ricerca di linee orizzontali

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()

Risultato:

inserisci qui la descrizione dell'immagine

Certo, questo non è così buono.

5. Trovare punti griglia

res = cv2.bitwise_and(closex,closey)

Risultato:

inserisci qui la descrizione dell'immagine

6. Correzione dei difetti

Qui, nikie fa una sorta di interpolazione, di cui non ho molta conoscenza. E non sono riuscito a trovare alcuna funzione corrispondente per questo OpenCV. (forse è lì, non lo so).

Dai un'occhiata a questo SOF che spiega come farlo usando SciPy, che non voglio usare: trasformazione delle immagini in OpenCV

Quindi, qui ho preso 4 angoli di ogni sotto-quadrato e ho applicato la prospettiva di curvatura a ciascuno.

Per questo, prima troviamo i centroidi.

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))

Ma i centroidi risultanti non verranno ordinati. Guarda l'immagine qui sotto per vedere il loro ordine:

inserisci qui la descrizione dell'immagine

Quindi li ordiniamo da sinistra a destra, dall'alto verso il basso.

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))

Ora vedi sotto il loro ordine:

inserisci qui la descrizione dell'immagine

Finalmente applichiamo la trasformazione e creiamo una nuova immagine di dimensioni 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()

Risultato:

inserisci qui la descrizione dell'immagine

Il risultato è quasi uguale a quello di Nikie, ma la lunghezza del codice è grande. Potrebbero essere disponibili metodi migliori là fuori, ma fino ad allora, questo funziona bene.

Saluti ARK.


4
"Preferisco il crash della mia applicazione piuttosto che ottenere risposte sbagliate." <- Accetto anche questo 100%
Viktor Sehr,

Grazie, la sua vera risposta è data da Nikie. Ma quello era in matematica, quindi l'ho appena convertito in OpenCV. Quindi la vera risposta ha abbastanza voti, penso
Abid Rahman K

Ah, non hai visto che hai anche pubblicato la domanda :)
Viktor Sehr,

Si. La domanda è anche mia. La mia risposta e nikie sono diverse solo alla fine. Ha una sorta di funzione di interpolazione in matematica che non è in numpy o opencv (ma è lì in Scipy, ma non volevo usare Scipy qui)
Abid Rahman K

Ricevo errore: output [ri * 50: (ri + 1) * 50-1, ci * 50: (ci + 1) * 50-1] = ordito [ri * 50: (ri + 1) * 50- 1, ci * 50: (ci + 1) * 50-1] .copy TypeError: l'argomento long () deve essere una stringa o un numero, non 'builtin_function_or_method'
user898678

6

Potresti provare a usare una sorta di modellazione basata su griglia del tuo ordito arbitrario. E poiché il sudoku è già una griglia, non dovrebbe essere troppo difficile.

Quindi potresti provare a rilevare i confini di ogni sottoregione 3x3 e quindi deformare ogni regione singolarmente. Se il rilevamento ha esito positivo, si otterrebbe una migliore approssimazione.


1

Voglio aggiungere che il metodo sopra funziona solo quando la scheda sudoku è dritta, altrimenti il ​​test del rapporto altezza / larghezza (o viceversa) probabilmente fallirà e non sarai in grado di rilevare i bordi del sudoku. (Voglio anche aggiungere che se le linee che non sono perpendicolari ai bordi dell'immagine, le operazioni di sobel (dx e dy) continueranno a funzionare poiché le linee avranno ancora bordi rispetto ad entrambi gli assi.)

Per essere in grado di rilevare le linee rette è necessario lavorare sull'analisi dei contorni o dei pixel come contornoArea / boundingRectArea, punti in alto a sinistra e in basso a destra ...

Modifica: sono riuscito a verificare se un insieme di contorni forma una linea o meno applicando la regressione lineare e controllando l'errore. Tuttavia la regressione lineare ha funzionato male quando l'inclinazione della linea è troppo grande (cioè> 1000) o è molto vicino a 0. Pertanto applicare il test del rapporto sopra (nella maggior parte delle risposte votate) prima che la regressione lineare sia logico e ha funzionato per me.


1

Per rimuovere gli angoli non rilevati ho applicato la correzione gamma con un valore gamma di 0,8.

Prima della correzione gamma

Il cerchio rosso viene disegnato per mostrare l'angolo mancante.

Dopo la correzione gamma

Il codice è:

gamma = 0.8
invGamma = 1/gamma
table = np.array([((i / 255.0) ** invGamma) * 255
                  for i in np.arange(0, 256)]).astype("uint8")
cv2.LUT(img, table, img)

Ciò si aggiunge alla risposta di Abid Rahman se mancano alcuni punti d'angolo.


0

Ho pensato che fosse un ottimo post e un'ottima soluzione di ARK; molto ben strutturato e spiegato.

Stavo lavorando su un problema simile e ho costruito tutto. Ci sono stati alcuni cambiamenti (ad esempio xrange to range, argomenti in cv2.findContours), ma questo dovrebbe funzionare immediatamente (Python 3.5, Anaconda).

Questa è una raccolta degli elementi sopra, con l'aggiunta di alcuni dei codici mancanti (ovvero l'etichettatura dei punti).

'''

/programming/10196198/how-to-remove-convexity-defects-in-a-sudoku-square

'''

import cv2
import numpy as np

img = cv2.imread('test.png')

winname="raw image"
cv2.namedWindow(winname)
cv2.imshow(winname, img)
cv2.moveWindow(winname, 100,100)


img = cv2.GaussianBlur(img,(5,5),0)

winname="blurred"
cv2.namedWindow(winname)
cv2.imshow(winname, img)
cv2.moveWindow(winname, 100,150)

gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
mask = np.zeros((gray.shape),np.uint8)
kernel1 = cv2.getStructuringElement(cv2.MORPH_ELLIPSE,(11,11))

winname="gray"
cv2.namedWindow(winname)
cv2.imshow(winname, gray)
cv2.moveWindow(winname, 100,200)

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)

winname="res2"
cv2.namedWindow(winname)
cv2.imshow(winname, res2)
cv2.moveWindow(winname, 100,250)

 #find elements
thresh = cv2.adaptiveThreshold(res,255,0,1,19,2)
img_c, 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)

winname="puzzle only"
cv2.namedWindow(winname)
cv2.imshow(winname, res)
cv2.moveWindow(winname, 100,300)

# vertical lines
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)

img_d, 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()

winname="vertical lines"
cv2.namedWindow(winname)
cv2.imshow(winname, img_d)
cv2.moveWindow(winname, 100,350)

# find horizontal lines
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)

img_e, 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()

winname="horizontal lines"
cv2.namedWindow(winname)
cv2.imshow(winname, img_e)
cv2.moveWindow(winname, 100,400)


# intersection of these two gives dots
res = cv2.bitwise_and(closex,closey)

winname="intersections"
cv2.namedWindow(winname)
cv2.imshow(winname, res)
cv2.moveWindow(winname, 100,450)

# text blue
textcolor=(0,255,0)
# points green
pointcolor=(255,0,0)

# find centroids and sort
img_f, 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))

# sorting
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 range(10)])
bm = b.reshape((10,10,2))

# make copy
labeled_in_order=res2.copy()

for index, pt in enumerate(b):
    cv2.putText(labeled_in_order,str(index),tuple(pt),cv2.FONT_HERSHEY_DUPLEX, 0.75, textcolor)
    cv2.circle(labeled_in_order, tuple(pt), 5, pointcolor)

winname="labeled in order"
cv2.namedWindow(winname)
cv2.imshow(winname, labeled_in_order)
cv2.moveWindow(winname, 100,500)

# create final

output = np.zeros((450,450,3),np.uint8)
for i,j in enumerate(b):
    ri = int(i/10) # row index
    ci = i%10 # column index
    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()

winname="final"
cv2.namedWindow(winname)
cv2.imshow(winname, output)
cv2.moveWindow(winname, 600,100)

cv2.waitKey(0)
cv2.destroyAllWindows()
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.