Как отсортировать контуры? - PullRequest
3 голосов
/ 20 февраля 2020

Мне интересно, может ли кто-нибудь объяснить стратегию, которая способна сортировать контуры для каждого кадра.

Я пытаюсь обнаружить «события» - в этом случае событие определяется как рост движение для 4 кадров.

Если контур «растет» / имеет большую площадь контура для 4 последовательных кадров, событие регистрируется, и я должен сохранить и вывести центральную позицию контура для первого кадра роста.

Если обнаруживается только одно событие, я могу грубо определить происхождение события, выполнив попарную проверку списка областей контура и, если это станет правдой, взяв позиционный (currentFrameNo - 4) элемент списка контурных позиций.

Однако попытка обнаружить несколько событий кажется совершенно другой игрой с мячом.

В любом данном кадре может быть (n) количество найденных контуров. Каждый контур передается в объект-кандидат с атрибутами, которые характеризуют контур, такими как номер кадра, положение и размер контура.

В конечном итоге мне нужен способ сортировки этих контуров для каждого кадра, например что я могу организовать их на основе их относительного положения, а затем выполнить попарную проверку «правильного списка» контуров.

Я не уверен, нужно ли мне несколько (4+) списков, по одному для каждого возможного события, а затем в каждом кадре передавать кандидатов в отдельный список, основанный на ближайших центральных позициях, или мне следует продолжать добавьте их в один список, а затем сделайте запрос к списку.

Я надеюсь, что кто-то с большим опытом использования коллекций linq / sorting поможет определить подходящий подход.

Спасибо, что приняли время читать этот пост.

public class CandidateList
{
    public List<Candidate> candidates;

    public CandidateList()
    {
        candidates = new List<Candidate> candidates;
    }

    public void Add(Candidate candidate)
    {
        candidates.Add(candidate)
    }
}

public class Candidate
{
    //Attributes shown in constructor.

    public Candidate(VectorOfPoint contour, int frameNumber, double contourSize, Point location)
    {
        Contour = contour;
        FrameNumber = frameNumber;
        ContourSize = contourSize;
        Location = location;
        Location_x = Location.X;
        Location_y = Location.Y;
    }
}

_vc = new VideoCapture(someURLorFilePath);
_candidates = new CandidateList();
_vc.ImageGrabbed += ProcessFrame;

public void ProcessFrame(object sender, EventArgs e)
{
    Mat _frame = new Mat(); 
    // read frame.. + other operations to get desired data.

    Mat _contourOutput = _frame.Clone();
    VectorOfVectorOfPoint _contours = new VectorOfVectorOfPoint();

    CvInvoke.FindContours(_contourOutput, _contours, new Mat(), RetrType.External, ChainApproxMethod.ChainApproxSimple);

    // If there are any contours
    if (_contours.Size > 0)
    {
        // Iterate through contours
        for (var i = 0; i < _contours.Size; i++)
        {
            // Find contour area of each contour (VectorOfPoint)
            double _contourArea = CvInvoke.ContourArea(_contours[i]);

            // Find centre of contour
            Moments M = CvInvoke.Moments(_contours[i]);

            Point _contourCentre = new Point(Convert.ToInt16(M.M10 / M.M00), Convert.ToInt16(M.M01 / M.M00));

            //  Create a candidate based on frame number, contourSize and location
            Candidate _candidate = new Candidate(_contours[i], _currentFrameNo, _contourArea, _contourCentre);
            _candidates.Add(_candidate)                     

        }
    }

    _currentFrameNo ++
}



Вот картинки, изображающие очень вероятный сценарий, с которым мне приходится иметь дело:

Кадр 1 - Четыре кандидата.

Frame 1 - four candidates

Кадр 2 - Четыре кандидата, слегка смещенная позиция

Frame 2 - four candidates

Кадр 3 - Четыре кандидата, слегка смещенные позиция

Frame 3 - four candidates

Кадр 4 - четыре кандидата, смещенная позиция Обнаружены два события. Получить центральные позиции из кадра 1.

Frame 4 - Two events detected.

Ответы [ 2 ]

1 голос
/ 26 февраля 2020

Вы можете использовать список циклических буферов для хранения истории каждого кандидата:

public class CandidateBufferList
{
    private List<CircularBuffer<Candidate>> _candidateList = new List<CircularBuffer<Candidate>>();   
    private void Add(Candidate candidate)
    {           
        //Find a matching buffer for the candidate based on distance. More on this later
        //here maxDistance is the maximum distance a candidate can move each frame
        var matches = _candidateList.Where(cb => Distance(candidate.Location, cb.Last.Location) < maxDistance);
        int matchCount = matches.Count();

        if (matchCount == 0)
        {
            var cb = new CircularBuffer<Candidate>();
            cb.Add(candidate);
            _candidateList.Add(cb);
        }
        else if (matchCount == 1)
        {
            var match = matches.First();
            if (match.Last.FrameNumber == candidate.FrameNumber)
            {
                // Ambiguous match 1.
                throw new Exception("A candidate was already added to this buffer this frame.");
            }
            match.Add(candidate);
        }
        else
        {
            // Ambiguous match 2.
            throw new Exception("More than one matching buffer was found for this candidate");
        }
    }

    public void Update(int frameNumber, List<Candidate> candidates)
    {
        candidates.ForEach(c => Add(c));
        //Remove buffers that didn't match this frame.
        _candidateList.RemoveAll(cb => cb.Last.FrameNumber != frameNumber);
    }

    public List<Point> GetEvents()
    {
        return _candidateList
            .Where(cb => ContourHasGrouwn(cb))                
            .Select(cb => cb.First.Location)
            .ToList();
    }

    private bool ContourHasGrouwn(CircularBuffer<Candidate> cb)
    {
        //if contour is not older than 4 frames
        if (!cb.IsFull) return false;

        for (int i = 1; i < cb.Size; i++)
        {
            if (cb[i].ContourSize < cb[i - 1].ContourSize) return false;
        }
        return true;
    }
}

И на каждом ProcessFrame:

//CandidateBufferList candidatesHistory
//List<Candidate> candidatesThisFrame
candidatesHistory.Update(frameNumber, candidatesThisFrame);
var events = candidatesHistory.GetEvents();

Я думал Я должен упомянуть, что если вы пытаетесь найти совпадение только по расстоянию, могут возникнуть или не возникнуть проблемы, в зависимости от вашей конкретной c проблемы:

  • кандидат может быть ближе к другому Центр кандидатов, например. новый кандидат добавляется близко к центру другого (неоднозначное совпадение 1)

  • кандидат может получить несколько совпадений, вы можете взять один с наименьшим расстоянием, но как вы можете быть уверены, что это правильно? (неоднозначное совпадение 2)

  • хуже, у вас может быть 2 кандидата, и оба могут быть ближе к центру другого в следующем кадре.

Вот реализация CircularBuffer:

class CircularBuffer<T>
{
    private const int _size = 4;
    private int _index;
    private T[] _elements = new T[_size];

    public int Size => _size;
    public int Count { get; private set; }
    public bool IsFull => Count == Size;

    public T this[int i] => _elements[(_index + i)%_size];
    public T First => this[0];
    public T Last => this[_size-1];

    public void Add(T element)
    {
        _elements[_index] = element;
        _index = (_index+1) % _size;
        if (Count < _size) Count++;
    }
}
1 голос
/ 21 февраля 2020

Не уверен, что все правильно понимаю. По сути, это звучит как задача отслеживания.

Вы, вероятно, хотите отслеживать свои контуры кадр за кадром, "один и тот же" контур в кадре f + 1 имеет положение, близкое к тому, которое находится в кадр ф . Вы могли бы достичь этого, скажем, с помощью класса TrackedContour, который хранит историю последних 4 (или более, если хотите) счетчиков.

Соответствие контура в кадре f + 1 для некоторых в кадре f можно было бы сделать путем парного сравнения контуров, что было бы легко реализовать, но неэффективно для многих контуров, n (n-1) / 2 сравнения для n контуров. Чтобы сделать его более эффективным, в двухмерном пространстве можно хранить TrackedContours в 2 списках, один из которых отсортирован по X , другой по координате Y . Тогда количество новых проверяемых контуров уменьшается, потому что нужно проверять только те, у которых есть «подобные» X и Y .

Таким образом, в основном стратагии будут be:

  • Удерживайте ваши TrackedContours в 2 отсортированных списках.
  • Делайте покадровое совпадение.
  • Сохраняйте историю контуров в ваших TrackedContours.
  • Обнаружение условий события и поиск в истории необходимых свойств.

Надеюсь, это поможет.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...