OpenCV Kalman Filter, удаление из std::vector

У меня такая проблема, см.

Я использую OpenCV для отслеживания руки в видео. Рука обнаруживается с помощью CascadeDetector, а затем отслеживается с помощью CamSHIFT. Я также использую фильтр Калмана, чтобы исправить положение руки, если алгоритм CamShift дает сбой на некоторых кадрах.

Проблема возникает, когда я пытаюсь стереть элемент из std::vector, где я храню свои руки. Причина стирания заключается в том, что из-за некоторых проблем лицо неправильно интерпретируется как рука, поэтому я обнаруживаю лица, и если область руки пересекается с областью лица, я удаляю эту руку. Я знаю, очень наивен, но я сейчас в самом начале пути. Код выглядит следующим образом:

class Hand {
...
public:
    struct 
    {
        ...
        struct {
            cv::Mat_<float> measurement;
            cv::KalmanFilter KF;
            cv::Mat state;
            cv::Mat processNoise;
        } KalmanTracker;
    } Tracker;
};
...
std::vector<Hand> hands;
...
std::vector<cv::Rect> faces;
faceCascade.detectMultiScale(frame, faces, 1.1, 2, CV_HAAR_FIND_BIGGEST_OBJECT);
// iterate through hands
for (std::vector<Hand>::iterator it = hands.begin(); it != hands.end(); ++it) {
    // iterate through faces:
    for (std::vector<cv::Rect>::iterator fc = faces.begin(); fc != faces.end(); ++fc) {
        cv::Rect intersection = (*it).handBox.boundingRect() & (*fc);
        // check if they have at leasy 75% intersection
        if (intersection.area() >= ((*it).handBox.boundingRect().area() * 0.75)) {
            // delete that hand from list
            hands.erase(it);    // this gets me a a EXC_BAD_ACCESS
        }
    }
}

Строка hands.erase(it) дает мне EXC_BAD_ACCESS, указывая на мою структуру KalmanFilterTracker, а также эта строка в mat.hpp с EXC_i386_GPFLT:

inline void Mat::release()
{
    if( refcount && CV_XADD(refcount, -1) == 1 ) // EXC_BAD_ACCESS, code=EXC_i386_GPFLT
    ...
}

Ни hands, ни faces не пусты. Если я полностью удалю фильтр Калмана из своего проекта и любое упоминание о нем или его использование, ошибка исчезнет.


person xonxt    schedule 03.09.2014    source источник
comment
вы не можете повторно использовать старый итератор после удаления из вектора.   -  person berak    schedule 03.09.2014


Ответы (2)


vector::erase(iterator) делает стираемый итератор недействительным. Но он возвращает новый итератор к следующему действительному элементу в векторе. Таким образом, строка должна быть:

it = hands.erase(it);

Изменить после комментария Берака:

Ах, я пропустил внутренний цикл. Поскольку вы аннулируете it внутри внутреннего цикла, все становится немного сложнее. Вам нужно будет прыгнуть через несколько обручей, чтобы сделать это правильно.

for (std::vector<Hand>::iterator it = hands.begin(); it != hands.end(); ) {
    bool found = false;
    // iterate through faces:
    for (std::vector<cv::Rect>::iterator fc = faces.begin(); fc != faces.end() && !found; ++fc) {
        cv::Rect intersection = (*it).handBox.boundingRect() & (*fc);
        // check if they have at leasy 75% intersection
        if (intersection.area() >= ((*it).handBox.boundingRect().area() * 0.75)) {
            found = true;
        }
    }

    if (found) {
        it = hands.erase(it);
    }
    else {
        ++it;
    }
}

Некоторые операции с итератором можно поместить во внутренний цикл, но сохранение их снаружи делает код немного яснее и проще для понимания.

person Gnagn    schedule 03.09.2014
comment
Изменил строку, но ошибка осталась. Я подозреваю, что ошибка связана не с самим итератором, а с экземпляром фильтра Калмана. - person xonxt; 03.09.2014
comment
@Gnagn, к сожалению, это не так просто. поскольку он находится во внутреннем цикле, потребуется еще один it != hands.end(); проверьте после этого (если последний элемент вектора был удален, теперь он == end()) - person berak; 03.09.2014
comment
Но таким образом в операторе if (found) это допустимый итератор, а затем перед следующим циклом вы снова увеличиваете его. Вы пропустили один, делая таким образом, или я ошибаюсь? - person Jepessen; 03.09.2014
comment
@Jepessen, упс. Сейчас лучше? - person Gnagn; 03.09.2014
comment
Я думаю да. Я сказал это, потому что, возможно, это было связано с ошибкой, учитывая, что я плохо понял, в чем ошибка... :( - person Jepessen; 03.09.2014
comment
@Jepessen Учитывая ответ ОП о том, что вектор был пуст, когда он пошел, чтобы стереть итератор, исходная ошибка заключалась в том, что он стирал итераторы во внутреннем цикле, что позволяло вектору быть пустым, потому что он никогда не попадал в условие it != hands.end() в внешняя петля. Этот новый код (надеюсь) позволяет избежать этого. - person Gnagn; 03.09.2014
comment
Конечно. Я уже решил проблему, добавив кучу if(hands.size()‹2 || it == hands.end()) везде, но ваше решение кажется гораздо более элегантным. Спасибо. - person xonxt; 04.09.2014

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

person xonxt    schedule 03.09.2014