Mam ten projekt ćwiczeń, który pozwala użytkownikowi rysować na ekranie podczas dotykania palcami. Bardzo prosta aplikacja, którą zrobiłem jako ćwiczenie w przeszłości. Mój mały kuzyn pozwolił sobie rysować palcem za pomocą mojego iPada w tej aplikacji (rysunki dla dzieci: kółka, linie itp., Cokolwiek przyszło mu do głowy). Potem zaczął rysować koła, a potem poprosił mnie, żebym zrobił to „dobre koło” (z mojego rozumienia: spraw, aby narysowany okrąg był idealnie okrągły, ponieważ wiemy, jak stabilnie próbujemy narysować coś palcem na ekranie, okrąg nigdy nie jest tak zaokrąglony, jak powinien być).
Więc moje pytanie jest takie, czy jest jakiś sposób w kodzie, w którym możemy najpierw wykryć linię narysowaną przez użytkownika, która tworzy okrąg i generuje mniej więcej ten sam rozmiar koła, czyniąc go idealnie okrągłym na ekranie. Umiem zrobić niezbyt prostą linię prostą, ale jeśli chodzi o koło, nie bardzo wiem, jak to zrobić za pomocą kwarcu lub innych metod.
Moje rozumowanie jest takie, że punkt początkowy i końcowy linii muszą się stykać lub przecinać po tym, jak użytkownik podniesie palec, aby uzasadnić fakt, że faktycznie próbował narysować okrąg.
Odpowiedzi:
Czasami naprawdę warto poświęcić trochę czasu na wymyślenie koła na nowo. Jak być może zauważyłeś, istnieje wiele frameworków, ale nie jest tak trudno wdrożyć proste, ale użyteczne rozwiązanie bez wprowadzania całej złożoności. (Proszę, nie zrozumcie mnie źle, dla każdego poważnego celu lepiej jest użyć dojrzałej i sprawdzonej, stabilnej platformy).
Najpierw przedstawię moje wyniki, a następnie wyjaśnię prostą i prostą ideę, która się za nimi kryje.
Zobaczysz, że w mojej realizacji nie ma potrzeby analizowania każdego punktu i wykonywania skomplikowanych obliczeń. Chodzi o to, aby znaleźć cenne metainformacje. Będzie używać styczną jak np
Określmy prosty i nieskomplikowany wzór, typowy dla wybranego kształtu:
Dlatego nie jest trudno wdrożyć mechanizm wykrywania koła w oparciu o ten pomysł. Zobacz działające demo poniżej (przepraszam, używam Javy jako najszybszego sposobu na przedstawienie tego szybkiego i nieco brudnego przykładu):
Wdrożenie podobnego zachowania na iOS nie powinno stanowić problemu, ponieważ potrzebujesz tylko kilku zdarzeń i współrzędnych. Coś podobnego do następującego (patrz przykład ):
Istnieje kilka możliwych ulepszeń.
Zacznij w dowolnym momencie
Aktualnym wymaganiem jest rozpoczęcie rysowania okręgu od górnego środkowego punktu ze względu na następujące uproszczenie:
Zwróć uwagę, że
index
używana jest domyślna wartość . Proste przeszukiwanie dostępnych „części” kształtu usunie to ograniczenie. Pamiętaj, że aby wykryć pełny kształt, musisz użyć okrągłego bufora:Zgodnie z ruchem wskazówek zegara i przeciwnie do ruchu wskazówek zegara
Aby obsługiwać oba tryby, będziesz musiał użyć okrągłego bufora z poprzedniego rozszerzenia i przeszukiwać w obu kierunkach:
Narysuj elipsę
Masz już wszystko, czego potrzebujesz w
bounds
tablicy.Po prostu użyj tych danych:
Inne gesty (opcjonalnie)
Na koniec wystarczy odpowiednio poradzić sobie z sytuacją, gdy
dx
(lubdy
) jest równe zero, aby obsługiwać inne gesty:Aktualizacja
Ten mały dokument PoC wzbudził dość duże zainteresowanie, więc zaktualizowałem nieco kod, aby działał płynnie i zawierał wskazówki dotyczące rysowania, wyróżnienia punktów pomocniczych itp.:
Oto kod:
źródło
Klasyczną techniką widzenia komputerowego do wykrywania kształtu jest transformacja Hougha. Jedną z fajnych rzeczy w Transformacie Hougha jest to, że jest bardzo odporny na częściowe dane, niedoskonałe dane i szum. Używanie Hough dla kręgu: http://en.wikipedia.org/wiki/Hough_transform#Circle_detection_process
Biorąc pod uwagę, że twój okrąg jest narysowany ręcznie, myślę, że transformacja Hough może być dla ciebie dobrym dopasowaniem.
Oto „uproszczone” wyjaśnienie, przepraszam, że nie jest takie proste. Wiele z tego pochodzi z projektu szkolnego, który zrobiłem wiele lat temu.
Transformacja Hougha to schemat głosowania. Dwuwymiarowa tablica liczb całkowitych jest przydzielana, a wszystkie elementy są ustawiane na zero. Każdy element odpowiada pojedynczemu pikselowi w analizowanym obrazie. Ta tablica jest nazywana tablicą akumulatorów, ponieważ każdy element będzie gromadził informacje, głosy, wskazujące na możliwość, że piksel może znajdować się na początku okręgu lub łuku.
Do obrazu stosuje się detektor krawędzi operatora gradientu, a piksele krawędzi lub krawędzie są rejestrowane. Krawędź to piksel, który ma inną intensywność lub kolor w porównaniu z sąsiadami. Stopień różnicy nazywany jest wielkością gradientu. Dla każdego obrzeża o dostatecznej wielkości stosowany jest schemat głosowania, który zwiększy elementy tablicy akumulatorów. Elementy, które są zwiększane (na które głosowano) odpowiadają możliwemu pochodzeniu okręgów przechodzących przez rozważany obrzeże. Pożądanym rezultatem jest to, że jeśli łuk istnieje, to prawdziwe pochodzenie otrzyma więcej głosów niż fałszywe pochodzenie.
Zwróć uwagę, że elementy zestawu akumulatorów odwiedzane w celu głosowania tworzą okrąg wokół rozważanego krawędzi. Obliczanie współrzędnych x, y, na które chcesz głosować, jest tym samym, co obliczanie współrzędnych x, y okręgu, który rysujesz.
Na swoim ręcznie narysowanym obrazie możesz być w stanie bezpośrednio użyć zestawu (kolorowych) pikseli zamiast obliczać krawędzie.
Teraz z niedokładnie zlokalizowanymi pikselami niekoniecznie otrzymasz pojedynczy element tablicy akumulatorów z największą liczbą głosów. Możesz otrzymać kolekcję sąsiednich elementów tablicy z kilkoma głosami, klaster. Środek ciężkości tej gromady może stanowić dobre przybliżenie pochodzenia.
Zauważ, że być może będziesz musiał uruchomić transformatę Hough dla różnych wartości promienia R. Tym, który tworzy gęstszą grupę głosów, jest „lepsze” dopasowanie.
Istnieją różne techniki zmniejszania liczby głosów za fałszywe pochodzenie. Na przykład jedną z zalet używania obrzeży jest to, że mają nie tylko wielkość, ale także kierunek. Podczas głosowania wystarczy tylko głosować na ewentualne pochodzenie w odpowiednim kierunku. Lokalizacje otrzymujące głosy utworzyłyby raczej łuk niż pełne koło.
Oto przykład. Zaczynamy od koła o promieniu jeden i zainicjalizowanej tablicy akumulatorów. Ponieważ każdy piksel jest rozpatrywany, głosowane są na potencjalne źródła. Prawdziwe pochodzenie otrzymuje najwięcej głosów, w tym przypadku cztery.
źródło
Oto inny sposób. Korzystanie z UIView touchBegan, touchesMoved, touchesEnded i dodawania punktów do tablicy. Dzielisz tablicę na połówki i sprawdzasz, czy każdy punkt w jednej tablicy ma mniej więcej taką samą średnicę jak jego odpowiednik w drugiej tablicy, co wszystkie inne pary.
Ten dźwięk w porządku? :)
źródło
Nie jestem ekspertem w rozpoznawaniu kształtów, ale oto jak mogę podejść do problemu.
Po pierwsze, wyświetlając ścieżkę użytkownika jako odręczną, potajemnie zbierz listę próbek punktowych (x, y) wraz z czasami. Możesz pobrać oba fakty ze zdarzeń przeciągania, zawinąć je w prosty obiekt modelu i ułożyć je w zmiennej tablicy.
Prawdopodobnie chcesz pobierać próbki dość często - powiedzmy co 0,1 sekundy. Inną możliwością byłoby rozpoczynanie pracy bardzo często, może co 0,05 sekundy, i obserwowanie, jak długo użytkownik się przeciąga; jeśli przeciągają się dłużej niż jakiś czas, zmniejsz częstotliwość próbkowania (i upuść wszystkie próbki, które zostałyby pominięte) do około 0,2 sekundy.
(I nie bierz moich liczb do ewangelii, ponieważ właśnie wyciągnąłem je z kapelusza. Eksperymentuj i znajduj lepsze wartości.)
Po drugie, przeanalizuj próbki.
Będziesz chciał wyprowadzić dwa fakty. Po pierwsze, środek kształtu, który (IIRC) powinien być średnią wszystkich punktów. Po drugie, średni promień każdej próbki z tego środka.
Jeśli, jak domyślił się @ user1118321, chcesz obsługiwać wielokąty, to reszta analizy polega na podjęciu decyzji: czy użytkownik chce narysować okrąg, czy wielokąt. Na początek możesz spojrzeć na próbki jako wielokąt, aby to określić.
Istnieje kilka kryteriów, których możesz użyć:
Trzecim i ostatnim krokiem jest utworzenie kształtu, wyśrodkowanego na wcześniej ustalonym punkcie środkowym, z uprzednio określonym promieniem.
Nie ma gwarancji, że wszystko, co powiedziałem powyżej, będzie działać lub będzie wydajne, ale mam nadzieję, że przynajmniej zaprowadzi Cię na właściwą ścieżkę - i proszę, jeśli ktoś, kto wie więcej o rozpoznawaniu kształtów niż ja (co jest bardzo niską poprzeczką), zobaczy to, nie krępuj się opublikować komentarz lub własną odpowiedź.
źródło
Miałem sporo szczęścia z odpowiednio wyszkolonym narzędziem rozpoznającym 1 USD ( http://depts.washington.edu/aimgroup/proj/dollar/ ). Użyłem go do okręgów, linii, trójkątów i kwadratów.
To było dawno temu, zanim UIGestureRecognizer, ale myślę, że powinno być łatwo stworzyć odpowiednie podklasy UIGestureRecognizer.
źródło
Gdy określisz, że użytkownik skończył rysować swój kształt w miejscu, w którym zaczął, możesz pobrać próbkę współrzędnych, przez które narysował, i spróbować dopasować je do koła.
Jest tutaj rozwiązanie tego problemu MATLAB: http://www.mathworks.com.au/matlabcentral/fileexchange/15060-fitcircle-m
Który jest oparty na artykule Dopasowanie kół i elips metodą najmniejszych kwadratów autorstwa Waltera Gandera, Gene H. Goluba i Rolfa Strebela: http://www.emis.de/journals/BBMS/Bulletin/sup962/gander.pdf
Dr Ian Coope z University of Canterbury w Nowej Zelandii opublikował artykuł zawierający streszczenie:
http://link.springer.com/article/10.1007%2FBF00939613
Plik MATLAB może obliczyć zarówno problem nieliniowego TLS, jak i liniowego LLS.
źródło
Oto dość prosty sposób użycia:
zakładając tę siatkę macierzową:
Umieść kilka UIVview w miejscach „X” i przetestuj je pod kątem trafienia (po kolei). Jeśli wszystkie zostaną trafione w kolejności, myślę, że uczciwe byłoby pozwolenie użytkownikowi powiedzieć „Dobra robota, narysowałeś okrąg”
Brzmi dobrze? (i proste)
źródło