Eu tenho esse projeto de prática que permite ao usuário desenhar na tela enquanto toca com os dedos. App muito simples que fiz como exercício há muito tempo. Meu primo teve a liberdade de desenhar coisas com o dedo com o meu iPad neste aplicativo (desenhos para crianças: círculo, linhas, etc., o que lhe veio à cabeça). Então ele começou a desenhar círculos e então me pediu para torná-lo "bom círculo" (pelo meu entendimento: faça o círculo desenhado perfeitamente redondo, pois sabemos, por mais estável que seja, tentemos desenhar algo com o dedo na tela, um círculo nunca é realmente tão arredondado como deveria ser).
Portanto, minha pergunta aqui é que, existe alguma maneira no código em que possamos primeiro detectar uma linha desenhada pelo usuário que forma um círculo e gerar aproximadamente o mesmo tamanho do círculo, fazendo-o perfeitamente redondo na tela. Fazer uma linha não tão reta é algo que eu saberia fazer, mas, quanto ao círculo, não sei como fazê-lo com quartzo ou outros métodos.
Meu raciocínio é que, o ponto inicial e final da linha devem se tocar ou cruzar após o usuário levantar o dedo para justificar o fato de que ele estava tentando desenhar um círculo.
Respostas:
Às vezes, é realmente útil gastar algum tempo reinventando a roda. Como você já deve ter notado, existem muitas estruturas, mas não é tão difícil implementar uma solução simples, mas útil, sem introduzir toda essa complexidade. (Por favor, não me entenda mal, para qualquer propósito sério, é melhor usar alguma estrutura madura e comprovadamente estável).
Apresentarei meus resultados primeiro e depois explicarei a idéia simples e direta por trás deles.
Você verá na minha implementação que não há necessidade de analisar todos os pontos e fazer cálculos complexos. A ideia é identificar algumas informações meta valiosas. Vou usar a tangente como exemplo:
Vamos identificar um padrão simples e direto, típico da forma selecionada:
Portanto, não é tão difícil implementar um mecanismo de detecção de círculos com base nessa ideia. Veja a demonstração de trabalho abaixo (desculpe, estou usando Java como a maneira mais rápida de fornecer este exemplo rápido e um pouco sujo):
Não deve ser um problema implementar um comportamento semelhante no iOS, pois você só precisa de vários eventos e coordenadas. Algo como o seguinte (veja o exemplo ):
Existem várias melhorias possíveis.
Comece a qualquer momento
O requisito atual é começar a desenhar um círculo a partir do ponto médio superior devido à seguinte simplificação:
Observe que o valor padrão de
index
é usado. Uma simples pesquisa nas "partes" disponíveis da forma removerá essa limitação. Observe que você precisará usar um buffer circular para detectar uma forma completa:No sentido horário e anti-horário
Para oferecer suporte a ambos os modos, você precisará usar o buffer circular do aprimoramento anterior e pesquisar nas duas direções:
Desenhar uma elipse
Você tem tudo o que precisa já na
bounds
matriz.Basta usar esses dados:
Outros gestos (opcional)
Finalmente, você só precisa lidar adequadamente com uma situação em que
dx
(oudy
) seja igual a zero para suportar outros gestos:Atualizar
Esse pequeno PoC recebeu muita atenção, então eu atualizei o código um pouco para fazê-lo funcionar sem problemas e fornecer algumas dicas de desenho, destacar pontos de apoio etc.:
Aqui está o código:
fonte
Uma técnica clássica de visão computacional para detectar uma forma é a transformação de Hough. Uma das coisas boas da Hough Transform é que ela é muito tolerante com dados parciais, dados imperfeitos e ruídos. Usando Hough para um círculo: http://en.wikipedia.org/wiki/Hough_transform#Circle_detection_process
Dado que seu círculo é desenhado à mão, acho que a transformação de Hough pode ser uma boa combinação para você.
Aqui está uma explicação "simplificada", peço desculpas por não ser tão simples assim. Muito disso é de um projeto escolar que eu fiz muitos anos atrás.
A Hough Transform é um esquema de votação. Uma matriz bidimensional de números inteiros é alocada e todos os elementos são definidos como zero. Cada elemento corresponde a um único pixel na imagem que está sendo analisada. Essa matriz é chamada de matriz acumuladora, pois cada elemento acumula informações, votos, indicando a possibilidade de um pixel estar na origem de um círculo ou arco.
Um detector de borda do operador de gradiente é aplicado à imagem e os pixels da borda, ou bordas, são gravados. Um edgel é um pixel que possui uma intensidade ou cor diferente em relação aos seus vizinhos. O grau de diferença é chamado de magnitude do gradiente. Para cada edgel de magnitude suficiente é aplicado um esquema de votação que incrementará elementos da matriz do acumulador. Os elementos que estão sendo incrementados (votados) correspondem às possíveis origens dos círculos que passam pelo edgel em consideração. O resultado desejado é que, se existir um arco, a verdadeira origem receberá mais votos do que as falsas origens.
Observe que os elementos da matriz do acumulador que estão sendo visitados para votação formam um círculo em torno do edgel em consideração. Calcular as coordenadas x, y para votar é o mesmo que calcular as coordenadas x, y de um círculo que você está desenhando.
Na imagem desenhada à mão, você poderá usar os pixels definidos (coloridos) diretamente, em vez de calcular os bordos.
Agora, com pixels imperfeitamente localizados, você não obterá necessariamente um único elemento da matriz do acumulador com o maior número de votos. Você pode obter uma coleção de elementos de matriz vizinhos com muitos votos, um cluster. O centro de gravidade deste aglomerado pode oferecer uma boa aproximação para a origem.
Observe que talvez você precise executar a Transformação de Hough para diferentes valores do raio R. O que produz o conjunto de votos mais denso é o ajuste "melhor".
Existem várias técnicas a serem usadas para reduzir votos em origens falsas. Por exemplo, uma vantagem do uso de bordas é que elas não apenas possuem uma magnitude, mas também têm uma direção. Ao votar, precisamos votar apenas em possíveis origens na direção apropriada. Os locais que receberam votos formariam um arco em vez de um círculo completo.
Aqui está um exemplo. Começamos com um círculo de raio um e uma matriz de acumulador inicializada. Como cada pixel é considerado, origens potenciais são votadas. A verdadeira origem recebe mais votos, que neste caso são quatro.
fonte
Aqui está outro caminho. Usando o UIView touchesBegan, touchesMoved, touchesEnded e adicionando pontos a uma matriz. Você divide a matriz em metades e testa se cada ponto em uma matriz tem aproximadamente o mesmo diâmetro de sua contraparte na outra matriz que todos os outros pares.
Isso soa bem? :)
fonte
Não sou especialista em reconhecimento de formas, mas eis como posso abordar o problema.
Primeiro, enquanto exibe o caminho do usuário à mão livre, acumule secretamente uma lista de amostras de pontos (x, y) juntamente com o tempo. Você pode obter os dois fatos dos eventos de arrastar, agrupá-los em um objeto de modelo simples e empilhá-los em uma matriz mutável.
Você provavelmente deseja colher as amostras com bastante frequência - digamos, a cada 0,1 segundos. Outra possibilidade seria começar muito frequente, talvez a cada 0,05 segundos, e observar quanto tempo o usuário se arrasta; se eles arrastarem mais do que um certo período de tempo, reduza a frequência da amostra (e largue todas as amostras que seriam perdidas) para algo como 0,2 segundos.
(E não tome meus números para o evangelho, porque eu os tirei do meu chapéu. Experimente e encontre valores melhores.)
Segundo, analise as amostras.
Você vai querer derivar dois fatos. Primeiro, o centro da forma, que (IIRC) deve ser apenas a média de todos os pontos. Segundo, o raio médio de cada amostra desse centro.
Se, como @ user1118321 adivinhou, você deseja suportar polígonos, o restante da análise consiste em tomar essa decisão: se o usuário deseja desenhar um círculo ou polígono. Você pode ver as amostras como um polígono para começar a fazer essa determinação.
Existem vários critérios que você pode usar:
O terceiro e último passo é criar a forma, centralizada no ponto central previamente determinado, com o raio previamente determinado.
Não há garantias de que qualquer coisa que eu disse acima funcione ou seja eficiente, mas espero que pelo menos o coloque no caminho certo - e, por favor, se alguém que sabe mais sobre o reconhecimento de formas do que eu (que é uma barra muito baixa) vê isso, fique à vontade para postar um comentário ou sua própria resposta.
fonte
Eu tive muita sorte com um reconhecedor de US $ 1 devidamente treinado ( http://depts.washington.edu/aimgroup/proj/dollar/ ). Usei-o para círculos, linhas, triângulos e quadrados.
Isso foi há muito tempo, antes do UIGestureRecognizer, mas acho que deve ser fácil criar subclasses apropriadas do UIGestureRecognizer.
fonte
Depois de determinar que o usuário terminou de desenhar sua forma onde eles começaram, você pode pegar uma amostra das coordenadas pelas quais ele desenhou e tentar ajustá-las em um círculo.
Há uma solução MATLAB para esse problema aqui: http://www.mathworks.com.au/matlabcentral/fileexchange/15060-fitcircle-m
Que é baseado no trabalho de mínimos quadrados de círculos e elipses de Walter Gander, Gene H. Golub e Rolf Strebel: http://www.emis.de/journals/BBMS/Bulletin/sup962/gander.pdf
O Dr. Ian Coope, da Universidade de Canterbury, Nova Zelândia, publicou um artigo com o resumo:
http://link.springer.com/article/10.1007%2FBF00939613
O arquivo MATLAB pode calcular o problema TLS não linear e LLS linear.
fonte
Aqui está uma maneira bastante simples de usar:
assumindo esta grade matricial:
Coloque algumas UIViews nos locais "X" e teste-as para serem atingidas (em sequência). Se todos eles forem atingidos em sequência, acho que seria justo deixar o usuário dizer "Muito bem, você desenhou um círculo"
Parece bom? (e simples)
fonte