Страница 1 из 1

Сравнение произвольных фигур (кривых)

СообщениеДобавлено: 27 мар 2018, 00:22
Clindatu
Друзья, привет.
Подскажите, насколько сложно реализовать такую вещь как сравнение произвольных фигур (кривых)?

Суть в чём: хочу мышкой (пальцем, в дальнейшем несколькими пальцами) двигать курсор. Система запоминает траекторию движения (кривую, фигуру) и сверяет с образцом.
Например, повторите рисунок - кружок, треугольник и тд.

Как новичок, я разделил проблему на части:
1. Запись траектории движения курсора. Т.е. запись координат курсора каждый тик.
(Решена. Правда не знаю на сколько правильно. С помощью двух объектов List);
2. Настроить систему так, чтобы вне зависимости от скорости движения количество координат не менялось. Сейчас, если быстро провести, например, прямую, то допустим получится 10 точек. А если эту же прямую провести медленно, то и все 100 точек получится;
3. Найти алгоритм сравнения двух кривых/фигур, нарисованную пользователем с образцами.

В разделах алгебры, что-то есть похожее, но это для точного соответствия, а как реализовать похожесть? То ли каждую точку сравнивать, то ли отрезками, то ли ещё как, плюс учитывая коэффициент отклонения/погрешности/похожести.

Ниже код скрипта. Удалось пока только записывать координаты и после отпускания мышки система повторяет траекторию. Со спецэффектами)
Пример работы на текущем этапе.
[youtube]https://youtu.be/YU_zjFdPeg8[/youtube]

Синтаксис:
Используется csharp
// СХЕМА:
//1. Зажимаем мышку и двигаем курсором. Система записывает траекторию движения курсором.
//2. Отпускаем мышку. Система повторяет траекторию движения курсором.
//+ за курсором передвигается и объекты ТрайлРендерер (спецэффекты)

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;

public class Swipe : MonoBehaviour, IBeginDragHandler, IDragHandler, IEndDragHandler
{
        // Переменные для вывода текста на экран
    public Text TextX;
    public Text TextY;

    // Переменные для хранения координат курсора мыши, т.е. запись траектории движения мыши после зажатия ЛКМ
    List<float> listX = new List<float>();
    List<float> listY = new List<float>();

    public GameObject MouseFollow;
    private GameObject inst_MyGO1;
    private GameObject inst_MyGO2;

    bool GoMove = false; // хранится флаг запуска повтора движения курсора
    int i = -1;
    public float distanceFromCamera = 5.0f;

    private void OnMouseDown()
    {
        // ПРИ НАЖАТИИ НА МЫШЬ

        if (GoMove)
        { }
        else
        {
                // Повтор движения курсора мыши выключен

                // Получим координату курсора
            Vector3 mousePosition = Input.mousePosition;
            // Координаты по z устанавливаем как расстояние до камеры (хз зачем, но работает)
            mousePosition.z = distanceFromCamera;
            // Преобразуем одни координаты курсора в другие (хз зачем, но работает)
            Vector3 mouseScreenToWorld = Camera.main.ScreenToWorldPoint(mousePosition);

            // Уничтожим объект существующий ТрайлРендерер, иначе будут клонироваться до бесконечности
            Destroy(inst_MyGO2);
            // Добавим новый ТрайлРендерер
            inst_MyGO1 = Instantiate(MouseFollow, mouseScreenToWorld, Quaternion.identity) as GameObject;

            GoMove = false;

            //Выведем текущие координаты курсора на экран
            TextX.text = "X: " + mouseScreenToWorld.x + " - Начало";
            TextY.text = "Y: " + mouseScreenToWorld.y + " - Начало";

            // Добавим текущую координату в список
            listX.Add(mouseScreenToWorld.x);
            listY.Add(mouseScreenToWorld.y);
        }
         
    }

    public void OnDrag(PointerEventData eventData)
    {
        //ДВИЖЕНИЕ МЫШИ ПРОДОЛЖАЕТСЯ

        // Получаем координаты курсора мыши
        Vector3 mousePosition = Input.mousePosition;
        // Координаты по z устанавливаем как расстояние до камеры (хз зачем, но работает)
        mousePosition.z = distanceFromCamera;
        // Преобразуем одни координаты курсора в другие (хз зачем, но работает)
        Vector3 mouseScreenToWorld = Camera.main.ScreenToWorldPoint(mousePosition);

        //Выведем текущие координаты курсора на экран
        TextX.text = "X: " + mouseScreenToWorld.x;
        TextY.text = "Y: " + mouseScreenToWorld.y;

        // Добавим текущую координату в список
        listX.Add(mouseScreenToWorld.x);
        listY.Add(mouseScreenToWorld.y);

        // Передвигаем объект ТрайРендерер (спецэффекты) туда где курсор мыши, т.е. следуем за мышью
        inst_MyGO1.transform.position = new Vector3(mouseScreenToWorld.x, mouseScreenToWorld.y, distanceFromCamera);

    }

    public void OnEndDrag(PointerEventData eventData)
    {
        // ПРИ ОТПУСКАНИИ МЫШКИ
        if (GoMove)
        {
        }
        else
        {
                // Повтор движения курсора мыши выключен

                // Получаем координаты курсора мыши
            Vector3 mousePosition = Input.mousePosition;
            // Координаты по z устанавливаем как расстояние до камеры (хз зачем, но работает)
            mousePosition.z = distanceFromCamera;
            // Преобразуем одни координаты курсора в другие (хз зачем, но работает)
            Vector3 mouseScreenToWorld = Camera.main.ScreenToWorldPoint(mousePosition);

            //Выведем текущие координаты курсора на экран
            TextX.text = "X: " + mouseScreenToWorld.x + " - Конец";
            TextY.text = "Y: " + mouseScreenToWorld.y + " - Конец";

            // Запускаем повтор движения курсора
            GoMove = true;

            //Уничтожаем объект ТрайлРендерер (спецэффекты), который следовал за курсором пользователя
            Destroy(inst_MyGO1);

            // СОздаём новый объект ТрайлРендерер (спецэффекты), который будет следовать за курсором повтора
            inst_MyGO2 = Instantiate(MouseFollow, new Vector3(listX[0], listY[0], distanceFromCamera), Quaternion.identity) as GameObject;
        }
    }

    void Start()
    {
    }

    void Update()
    {
        // КАЖДЫЙ КАДР

        if (GoMove)
        {
                // ПОВТОР ДВИЖЕНИЯ КУРСОРА ВКЛЮЧЁН, т.е. сейчас система сама повторяет траекторию движения курсора
                // Увеличиваем счётчик
            i++;
            if (i < listX.Count)
            {
                // Текущий номер списка меньше, чем количество в списке координат
                //Переместим объект ТрайлРендерер в следующую координату
                inst_MyGO2.transform.position = new Vector3(listX[i], listY[i], distanceFromCamera);
            }
            else
            {
                //Все координаты пройдены, очистим списки координат и установим переменные в исходное положение
                listX.Clear();
                listY.Clear();
                i = -1;
                GoMove = false;
            }

        }
    }

}
 

Re: Сравнение произвольных фигур (кривых)

СообщениеДобавлено: 27 мар 2018, 02:35
1max1
Читайте про распознавание образов, это не так просто как кажется, конечно можно пойти самыми примитивными вариантами например, сделать заранее много шаблонов, а потом по ним сравнивать те же координаты к примеру, если (pos[0] / posTemplate[0] > 0.9) && (pos[0] / posTemplate[0] < 1.1), считаем, что координата совпала, если совпало 90% координат - фигура такая-то, но думаю багов буде-е-е-т, ммм))

Re: Сравнение произвольных фигур (кривых)

СообщениеДобавлено: 27 мар 2018, 08:48
Bill Gates
1. Нормализуйте кривые по длине.
2. Каждой точке эталона сопоставьте точку нарисованной фигуры.
3. Представляте контур в виде массива центромассных расстояний.
4. Дальше используйте что-то вроде коэффициента корреляции Пирсона или придумайте свое.
5. ?????
6. PROFIT!!!

Вообще, конечно, не получится решить данную задачу на должном уровне при таком простом подходе.

Re: Сравнение произвольных фигур (кривых)

СообщениеДобавлено: 28 мар 2018, 16:55
artk
готовых решений много. И знаю что есть очень простые алгоритмы для такого распознования, без использования нейронок.
https://assetstore.unity.com/packages/t ... izer-86410

Re: Сравнение произвольных фигур (кривых)

СообщениеДобавлено: 29 мар 2018, 22:13
Clindatu
Спасибо, ребят. Эх, я думал, в Юнити уже есть стандартный механизм какой-то.
Скорее всего откажусь пока от идеи, т.к. я только начал изучать Юнити, к тому же только как хобби.
Или буду изобретать колхозный велосипед.

Re: Сравнение произвольных фигур (кривых)

СообщениеДобавлено: 29 мар 2018, 22:16
Clindatu
artk писал(а):готовых решений много. И знаю что есть очень простые алгоритмы для такого распознования, без использования нейронок.
https://assetstore.unity.com/packages/t ... izer-86410


Спасибо за подсказку.
Но платные решения меня пока не интересуют, т.к., во-первых, я новичёк в Юнити и C#, а во-вторых, это просто хобби.

Re: Сравнение произвольных фигур (кривых)

СообщениеДобавлено: 29 мар 2018, 22:18
Clindatu
Bill Gates писал(а):1. Нормализуйте кривые по длине.
2. Каждой точке эталона сопоставьте точку нарисованной фигуры.
3. Представляте контур в виде массива центромассных расстояний.
4. Дальше используйте что-то вроде коэффициента корреляции Пирсона или придумайте свое.
5. ?????
6. PROFIT!!!

Вообще, конечно, не получится решить данную задачу на должном уровне при таком простом подходе.


Буду пока решать проблему нормализовать кривые. И скорее всего буду просто ограничивать минимальное расстояние от предыдущей точки для фиксации новой точки.
А в Юнити нету типовой настройки шага движения курсором?

Re: Сравнение произвольных фигур (кривых)

СообщениеДобавлено: 30 мар 2018, 11:37
юnity
Можно проверять позицию курсора, например:
Синтаксис:
Используется csharp
if(Input.GetMouseButton(0))Debug.Log(Input.mousePosition);

Re: Сравнение произвольных фигур (кривых)

СообщениеДобавлено: 01 апр 2018, 21:42
vovihro
Делать проще, опираться на особенности фигур. Смотрим количество и качество линий, пересечений, углов, удаленность от центра фигуры, симметричность. Нужно распознать крестик? 2 линии с одним пересечением, квадрат - 4 линии с 4 прямыми углами, круг - замкнутая линия без углов. Когда линия рисуется смотреть нахождение точек на одной прямой, из таких прямых строить фигуры и соответственно считать углы и стороны.