Ошибка памяти при отображении матрицы opencv - PullRequest
0 голосов
/ 29 июня 2018

Я создал класс, использующий сериализованную матрицу Opencv. Он работает нормально, и матрица десериализована. Если я попытаюсь отобразить его внутри метода класса с помощью метода imshow, он будет работать отлично, отображаться без ошибок. Тем не менее, я передаю ссылку на параметр на указатель Matrix из своей основной функции для дальнейшей обработки этой матрицы. Когда я пытаюсь отобразить его в основном, у меня возникает ошибка сегментации. Странная часть заключается в том, что если я попытаюсь отобразить матрицу как в методе класса, так и в главном, то у меня получится два окна, работающие нормально (иногда я получаю ошибку сегментации, но в большинстве случаев она работает хорошо). Если я удалю отображаемый код из метода, я даже не смогу отобразить один кадр, прежде чем получит ошибку сегментации.

Я пытался использовать shared_ptr, передавая указатель на указатель вместо ссылки, или даже просто возвращая значение. Мой код немного грязный, но главная цель - тестирование.

Вот код метода:

void VideoConsumer::getVideoFrame(cv::Mat* &mat) {
    Message msg = _consumer->poll();
    mat = NULL;
    if(!msg) {
        cerr << "No message received" << endl;
        return;
    }
    if(msg.get_error()) {
        if(!msg.is_eof()) {
            cerr << "[+] Received error notification: " << msg.get_error() << endl;
        }
        return;
    }

    Document document;
    string jsonPayload = "";
    for(auto i=msg.get_payload().begin(); i != msg.get_payload().end();i++) {
        jsonPayload += *i;
    }


    document.Parse(jsonPayload.c_str());
    if(document.HasMember("rows") && document.HasMember("cols") && document.HasMember("data")) {
        int rows = document["rows"].GetInt();
        int cols = document["cols"].GetInt();
        int type = document["type"].GetInt();
        string data = document["data"].GetString();
        std::vector<BYTE> decodedBytes = base64_decode(data);

        stringstream ss;
        for(int i=0;i< decodedBytes.size(); i++) {
            ss << decodedBytes[i];
        }
        string decoded_data = ss.str();
        cout << "Constructed string" << endl;
        mat = new cv::Mat(rows,cols,type,(void *)decoded_data.data());
        /*cv::imshow("test",*mat);
        while(cv::waitKey(10) != 27)*/ //This is where it is displayed
        return;
    } else {
        return;
    }  

}

И код в основном:

 ...
if(parser.has("stream")) {
      VideoConsumer consumer("localhost:9092","video-stream-topic","testId2");
      consumer.setConsumer();

      while(1) {
        Mat *frame = NULL;
        consumer.getVideoFrame(frame);
        if(frame == NULL) {
          cout << "Null frame" << endl; 
          continue;
        }
        if(!frame->empty() && frame->rows > 0 && frame->cols > 0) {
          imshow("Test",*frame);
          waitKey(10);
          frame->release();
        }

      }
    }

У меня совершенно нет идей, и я попробовал все, что знал или нашел в своих исследованиях.

РЕДАКТИРОВАТЬ: Добавлено frame-> release () для освобождения выделения, все та же проблема.

1 Ответ

0 голосов
/ 29 июня 2018

Проблема с инициализацией вашей матрицы ... В частности, здесь:

mat = new cv::Mat(rows,cols,type,(void *)decoded_data.data());

Вот этот конструктор

Mat (int, int, cols, int, void * data, size_t step = AUTO_STEP)

, который в документации говорит следующее о * параметре данных

data Указатель на данные пользователя. Матричные конструкторы, которые принимают данные и параметры шага не выделяют матричные данные. Вместо этого они просто инициализировать заголовок матрицы, который указывает на указанные данные, которые означает, что данные не копируются. Эта операция очень эффективна и может использоваться для обработки внешних данных с использованием функций OpenCV. Внешний данные не освобождаются автоматически, поэтому вам следует позаботиться об этом.

Это означает, что, как только она выйдет из области видимости (функция выходит), созданная вами строка (decoded_data) выйдет и данные будут освобождены строкой, а затем ваш cv :: Mat будет иметь ссылку на данные, которые больше не действительны ...

Вы всегда можете инициализировать матрицу чем-то вроде

cv::Mat(rows,cols,type)

, а затем используйте что-то вроде std :: memcpy или подобное для копирования данных в mat.data член. На самом деле, AFAIK не нужно передавать байт в строку, а затем в мат, который приводится в void, а затем в uchar ....

попробуйте что-то вроде:

mat  = cv::Mat(rows,cols,type);
std::memcpy(&decodedBytes[0], mat.data, decodedBytes.size());

Просто небольшое предупреждение для этого решения, вам нужно проверить, что decodedBytes не пусто и что mat.data имеет достаточно места для получения всего содержимого decodedBytes. Для этого просто убедитесь, что:

// size in bytes to copy  ==  size of the allocated data of mat in bytes
decodedBytes.size() == (mat.elemSize() * mat.rows * mat.cols)

Еще пара замечаний, которые могут и не быть проблемой сейчас, но могут укусить вас позже:

  1. Не используйте указатели cv :: Mat ... Поведение cv :: Mat уже похоже на умный указатель.
  2. Остерегайтесь приведения / копирования данных из знака в unsign и наоборот. Я думаю, что теперь это сделано правильно, но это может стать проблемой позже.
...