Robiłem zabawny projekt: Rozwiązywanie sudoku z obrazu wejściowego za pomocą OpenCV (jak w goglach Google itp.). I wykonałem zadanie, ale na koniec znalazłem mały problem, po który tu przyjechałem.
Programowałem za pomocą API Python OpenCV 2.3.1.
Poniżej jest to, co zrobiłem:
- Przeczytaj obraz
- Znajdź kontury
- Wybierz ten z maksymalnym obszarem (a także nieco równym kwadratowi).
Znajdź punkty narożne.
np. podane poniżej:
( Zauważ tutaj, że zielona linia prawidłowo pokrywa się z prawdziwą granicą Sudoku, więc Sudoku można poprawnie wypaczyć . Sprawdź następny obraz)
wypacz obraz do idealnego kwadratu
np. obraz:
Wykonaj OCR (dla którego użyłem metody, którą podałem w OCR dla Simple Digit Recognition w OpenCV-Python )
Metoda zadziałała dobrze.
Problem:
Sprawdzić ten obraz.
Wykonanie kroku 4 na tym obrazie daje wynik poniżej:
Narysowana czerwona linia jest oryginalnym konturem, który jest prawdziwym konturem granicy sudoku.
Narysowana zielona linia jest przybliżonym konturem, który będzie konturem wypaczonego obrazu.
Która oczywiście jest różnica między zieloną linią a czerwoną linią na górnej krawędzi sudoku. Więc podczas wypaczania nie otrzymuję oryginalnej granicy Sudoku.
Moje pytanie :
Jak mogę wypaczyć obraz na prawidłowej granicy Sudoku, tj. Czerwoną linię LUB jak usunąć różnicę między czerwoną linią a zieloną linią? Czy jest jakaś metoda na to w OpenCV?
źródło
Odpowiedzi:
Mam rozwiązanie, które działa, ale sam musisz je przetłumaczyć na OpenCV. Jest napisane w Mathematica.
Pierwszym krokiem jest dostosowanie jasności obrazu, dzieląc każdy piksel w wyniku operacji zamykania:
Następnym krokiem jest znalezienie obszaru sudoku, dzięki czemu mogę zignorować (maskować) tło. W tym celu korzystam z analizy połączonych komponentów i wybieram komponent, który ma największy obszar wypukły:
Wypełniając ten obraz, otrzymuję maskę dla siatki sudoku:
Teraz mogę użyć filtra pochodnego drugiego rzędu, aby znaleźć pionowe i poziome linie na dwóch osobnych obrazach:
Ponownie używam analizy połączonych komponentów, aby wyodrębnić linie siatki z tych obrazów. Linie siatki są znacznie dłuższe niż cyfry, więc mogę użyć długości suwmiarki, aby wybrać tylko komponenty połączone z liniami siatki. Sortując je według pozycji, otrzymuję 2x10 obrazów maski dla każdej pionowej / poziomej linii siatki na obrazie:
Następnie biorę każdą parę pionowych / poziomych linii siatki, rozszerzam je, obliczam przecięcie piksel po pikselu i obliczam środek wyniku. Te punkty to przecięcia linii siatki:
Ostatnim krokiem jest zdefiniowanie dwóch funkcji interpolacji dla mapowania X / Y przez te punkty i przekształcenie obrazu za pomocą tych funkcji:
Wszystkie operacje są podstawową funkcją przetwarzania obrazu, więc powinno to być również możliwe w OpenCV. Transformacja obrazu oparta na splajnie może być trudniejsza, ale nie sądzę, żebyś jej naprawdę potrzebował. Prawdopodobnie użycie transformacji perspektywy, której używasz teraz w każdej komórce, da wystarczająco dobre wyniki.
źródło
Odpowiedź Nikie rozwiązała mój problem, ale jego odpowiedź brzmiała Mathematica. Pomyślałem więc, że powinienem tutaj dostosować adaptację OpenCV. Ale po wdrożeniu zauważyłem, że kod OpenCV jest znacznie większy niż kod matematyczny Nikie. Poza tym nie mogłem znaleźć metody interpolacji wykonanej przez Nikie w OpenCV (chociaż można to zrobić za pomocą scipy, powiem to, gdy nadejdzie czas).
1. Wstępne przetwarzanie obrazu (operacja zamykania)
Wynik:
2. Znajdowanie placu Sudoku i tworzenie obrazu maski
Wynik:
3. Znajdowanie linii pionowych
Wynik:
4. Znajdowanie linii poziomych
Wynik:
Oczywiście ten nie jest tak dobry.
5. Znajdowanie punktów siatki
Wynik:
6. Korygowanie wad
Tutaj nikie dokonuje interpolacji, o której nie mam dużej wiedzy. I nie mogłem znaleźć żadnej odpowiedniej funkcji dla tego OpenCV. (być może tam jest, nie wiem).
Sprawdź ten SOF, który wyjaśnia, jak to zrobić za pomocą SciPy, którego nie chcę używać: Transformacja obrazu w OpenCV
Więc tutaj wziąłem 4 rogi każdego kwadratu i zastosowałem perspektywę wypaczenia dla każdego.
W tym celu najpierw znajdziemy centroidy.
Ale powstałe centroidy nie zostaną posortowane. Sprawdź poniższe zdjęcie, aby zobaczyć ich kolejność:
Więc sortujemy je od lewej do prawej, od góry do dołu.
Teraz zobacz poniżej ich kolejność:
Na koniec stosujemy transformację i tworzymy nowy obraz o rozmiarze 450x450.
Wynik:
Wynik jest prawie taki sam jak w przypadku nikie, ale długość kodu jest duża. Być może dostępne są lepsze metody, ale do tego czasu działa to poprawnie.
Pozdrawiam ARK.
źródło
Możesz spróbować użyć pewnego rodzaju modelowania opartego na siatce twojego arbitralnego wypaczania. A ponieważ sudoku już jest siatką, nie powinno to być zbyt trudne.
Możesz więc spróbować wykryć granice każdego podregionu 3x3, a następnie wypaczać każdy region osobno. Jeśli wykrycie się powiedzie, da ci to lepsze przybliżenie.
źródło
Chcę dodać, że powyższa metoda działa tylko wtedy, gdy plansza sudoku stoi prosto, w przeciwnym razie test stosunku wysokości do szerokości (lub odwrotnie) najprawdopodobniej się nie powiedzie i nie będzie można wykryć krawędzi sudoku. (Chcę również dodać, że jeśli linie, które nie są prostopadłe do granic obrazu, operacje sobel (dx i dy) będą nadal działać, ponieważ linie będą miały krawędzie względem obu osi.)
Aby móc wykryć linie proste, powinieneś pracować nad analizą konturową lub pikselową, taką jak contourArea / boundingRectArea, lewy górny i prawy dolny punkt ...
Edycja: Udało mi się sprawdzić, czy zestaw konturów tworzy linię, czy nie, stosując regresję liniową i sprawdzając błąd. Jednak regresja liniowa działała słabo, gdy nachylenie linii jest zbyt duże (tj.> 1000) lub jest bardzo bliskie 0. Dlatego zastosowanie powyższego testu proporcji (w najbardziej pozytywnej odpowiedzi) przed regresją liniową jest logiczne i zadziałało dla mnie.
źródło
Aby usunąć niezauważone rogi zastosowałem korekcję gamma o wartości gamma 0,8.
Narysowane jest czerwone kółko, aby pokazać brakujący narożnik.
Kod to:
Jest to dodatek do odpowiedzi Abida Rahmana, jeśli brakuje niektórych punktów narożnych.
źródło
Myślałem, że to świetny post i świetne rozwiązanie ARK; bardzo dobrze rozplanowane i wyjaśnione.
Pracowałem nad podobnym problemem i zbudowałem całość. Były pewne zmiany (tj. Xrange do zakresu, argumenty w cv2.findContours), ale powinno to działać od razu (Python 3.5, Anaconda).
Jest to kompilacja powyższych elementów z dodanym brakującym kodem (tj. Etykietowaniem punktów).
źródło