Tengo este proyecto de práctica que permite al usuario dibujar en la pantalla mientras toca con los dedos. Aplicación muy simple que hice como ejercicio hace mucho tiempo. Mi primo se tomó la libertad de dibujar cosas con su dedo con mi iPad en esta aplicación (dibujos para niños: círculos, líneas, etc., lo que se le ocurriera). Luego comenzó a dibujar círculos y luego me pidió que lo hiciera "buen círculo" (según tengo entendido: haga que el círculo dibujado sea perfectamente redondo, ya que sabemos que no importa cuán estable tratemos de dibujar algo con nuestro dedo en la pantalla, un el círculo nunca es realmente tan redondeado como debería ser un círculo).
Entonces, mi pregunta aquí es: ¿hay alguna forma en el código en la que podamos detectar primero una línea dibujada por el usuario que forme un círculo y genere aproximadamente el mismo tamaño del círculo haciéndolo perfectamente redondo en la pantalla? Hacer una línea no tan recta es algo que sabría hacer, pero en cuanto al círculo, no sé cómo hacerlo con Quartz u otros métodos.
Mi razonamiento es que, el punto inicial y final de la línea debe tocarse o cruzarse después de que el usuario levanta el dedo para justificar el hecho de que estaba tratando de dibujar un círculo.
Respuestas:
A veces es realmente útil pasar un tiempo reinventando la rueda. Como ya habrás notado, hay muchos frameworks, pero no es tan difícil implementar una solución simple pero útil sin introducir toda esa complejidad. (Por favor, no me malinterpreten, para cualquier propósito serio es mejor usar un marco estable y probado).
Presentaré mis resultados primero y luego explicaré la idea simple y directa detrás de ellos.
Verá en mi implementación que no hay necesidad de analizar cada punto y hacer cálculos complejos. La idea es detectar alguna metainformación valiosa. Voy a utilizar tangente como un ejemplo:
Identifiquemos un patrón simple y directo, típico de la forma seleccionada:
Por lo tanto, no es tan difícil implementar un mecanismo de detección de círculo basado en esa idea. Vea la demostración de trabajo a continuación (Lo siento, estoy usando Java como la forma más rápida de proporcionar este ejemplo rápido y un poco sucio):
Implementar un comportamiento similar en iOS no debería ser un problema, ya que solo necesita varios eventos y coordenadas. Algo como lo siguiente (ver ejemplo ):
Hay varias mejoras posibles.
Comience en cualquier punto
El requisito actual es comenzar a dibujar un círculo desde el punto medio superior debido a la siguiente simplificación:
Tenga en cuenta que
index
se utiliza el valor predeterminado de . Una simple búsqueda a través de las "partes" disponibles de la forma eliminará esa limitación. Tenga en cuenta que necesitará usar un búfer circular para detectar una forma completa:En sentido horario y antihorario
Para admitir ambos modos, deberá utilizar el búfer circular de la mejora anterior y buscar en ambas direcciones:
Dibujar una elipse
Ya tienes todo lo que necesitas en la
bounds
matriz.Simplemente use esos datos:
Otros gestos (opcional)
Finalmente, solo necesita manejar adecuadamente una situación cuando
dx
(ody
) es igual a cero para admitir otros gestos:Actualizar
Este pequeño PoC recibió bastante atención, por lo que actualicé un poco el código para que funcione sin problemas y proporcione algunas sugerencias de dibujo, resalte puntos de apoyo, etc.
Aquí está el código:
fuente
Una técnica clásica de visión por computadora para detectar una forma es la Transformación de Hough. Una de las cosas buenas de la Transformación Hough es que es muy tolerante con los datos parciales, los datos imperfectos y el ruido. Usando Hough para un círculo: http://en.wikipedia.org/wiki/Hough_transform#Circle_detection_process
Dado que su círculo está dibujado a mano, creo que la transformación de Hough puede ser una buena combinación para usted.
Aquí hay una explicación "simplificada", me disculpo por no ser tan simple. Gran parte es de un proyecto escolar que hice hace muchos años.
La transformación de Hough es un esquema de votación. Se asigna una matriz bidimensional de enteros y todos los elementos se establecen en cero. Cada elemento corresponde a un solo píxel en la imagen que se analiza. Este conjunto se denomina conjunto de acumuladores, ya que cada elemento acumulará información, votos, lo que indica la posibilidad de que un píxel pueda estar en el origen de un círculo o arco.
Se aplica un detector de borde de operador de gradiente a la imagen y se registran los píxeles de borde, o edgels. Un edgel es un píxel que tiene una intensidad o color diferente con respecto a sus vecinos. El grado de diferencia se llama la magnitud del gradiente. Para cada edgel de magnitud suficiente se aplica un esquema de votación que incrementará los elementos del conjunto de acumuladores. Los elementos que se incrementan (votan) corresponden a los posibles orígenes de los círculos que pasan por el edgel en consideración. El resultado deseado es que si existe un arco, el origen verdadero recibirá más votos que los orígenes falsos.
Tenga en cuenta que los elementos del conjunto de acumuladores que se visitan para votar forman un círculo alrededor del edgel en consideración. Calcular las coordenadas x, y para votar es lo mismo que calcular las coordenadas x, y de un círculo que está dibujando.
En su imagen dibujada a mano, puede usar los píxeles del conjunto (de color) directamente en lugar de calcular edgels.
Ahora, con píxeles ubicados de manera imperfecta, no necesariamente obtendrá un solo elemento de matriz de acumulador con el mayor número de votos. Puede obtener una colección de elementos de matriz vecinos con un montón de votos, un clúster. El centro de gravedad de este grupo puede ofrecer una buena aproximación para el origen.
Tenga en cuenta que es posible que deba ejecutar la Transformación de Hough para diferentes valores de radio R. El que produce el grupo de votos más denso es el ajuste "mejor".
Existen varias técnicas para reducir votos por orígenes falsos. Por ejemplo, una ventaja de usar edgels es que no solo tienen una magnitud sino que también tienen una dirección. Al votar solo necesitamos votar por posibles orígenes en la dirección apropiada. Los lugares que reciben votos formarían un arco en lugar de un círculo completo.
Aquí hay un ejemplo. Comenzamos con un círculo de radio uno y una matriz de acumulador inicializada. Como cada píxel se considera, se votan los orígenes potenciales. El verdadero origen recibe la mayoría de los votos, que en este caso son cuatro.
fuente
Aquí hay otra manera. Usar UIView touchesBegan, touchesMoved, touchesEnded y agregar puntos a una matriz. Divide la matriz en mitades y prueba si cada punto de una matriz tiene aproximadamente el mismo diámetro que su contraparte en la otra matriz como todos los otros pares.
Eso suena bien? :)
fuente
No soy un experto en reconocimiento de formas, pero así es como podría abordar el problema.
Primero, mientras muestra la ruta del usuario como a mano alzada, acumule secretamente una lista de muestras de puntos (x, y) junto con los tiempos. Puede obtener ambos hechos de sus eventos de arrastre, envolverlos en un objeto modelo simple y agruparlos en una matriz mutable.
Probablemente quiera tomar las muestras con bastante frecuencia, por ejemplo, cada 0.1 segundos. Otra posibilidad sería comenzar con mucha frecuencia, tal vez cada 0.05 segundos, y observar cuánto tiempo arrastra el usuario; si arrastran más de una cantidad de tiempo, reduzca la frecuencia de muestreo (y suelte las muestras que se habrían perdido) a algo así como 0.2 segundos.
(Y no tome mis números como evangelio, porque los saqué de mi sombrero. Experimente y encuentre mejores valores).
Segundo, analizar las muestras.
Querrás derivar dos hechos. Primero, el centro de la forma, que (IIRC) debería ser el promedio de todos los puntos. Segundo, el radio promedio de cada muestra desde ese centro.
Si, como adivinó @ user1118321, desea admitir polígonos, el resto del análisis consiste en tomar esa decisión: si el usuario quiere dibujar un círculo o un polígono. Puede comenzar a ver las muestras como un polígono para hacer esa determinación.
Hay varios criterios que puede usar:
El tercer y último paso es crear la forma, centrada en el punto central previamente determinado, con el radio previamente determinado.
No hay garantías de que nada de lo que dije anteriormente funcione o sea eficiente, pero espero que al menos lo lleve por el camino correcto, y por favor, si alguien que sabe más sobre el reconocimiento de formas que yo (que es una barra muy baja) ve esto, no dude en publicar un comentario o su propia respuesta.
fuente
He tenido bastante suerte con un reconocedor de $ 1 debidamente entrenado ( http://depts.washington.edu/aimgroup/proj/dollar/ ). Lo usé para círculos, líneas, triángulos y cuadrados.
Fue hace mucho tiempo, antes de UIGestureRecognizer, pero creo que debería ser fácil crear subclases de UIGestureRecognizer adecuadas.
fuente
Una vez que determine que el usuario terminó de dibujar su forma donde comenzó, puede tomar una muestra de las coordenadas que dibujaron e intentar ajustarlas a un círculo.
Aquí hay una solución MATLAB para este problema: http://www.mathworks.com.au/matlabcentral/fileexchange/15060-fitcircle-m
El cual se basa en el ajuste de mínimos cuadrados en papel de círculos y elipses de Walter Gander, Gene H. Golub y Rolf Strebel: http://www.emis.de/journals/BBMS/Bulletin/sup962/gander.pdf
El Dr. Ian Coope de la Universidad de Canterbury, Nueva Zelanda, publicó un artículo con el resumen:
http://link.springer.com/article/10.1007%2FBF00939613
El archivo MATLAB puede calcular tanto el problema TLS no lineal como el problema LLS lineal.
fuente
Aquí hay una manera bastante simple de usar:
asumiendo esta cuadrícula matricial:
Coloque algunas UIViews en las ubicaciones "X" y pruébelas para que sean golpeadas (en secuencia). Si todos son golpeados en secuencia, creo que sería justo dejar que el usuario diga "Bien hecho, dibujaste un círculo"
Suena bien? (y simple)
fuente