Эффективно загружать большой мат в память в OpenCV

Есть ли более эффективный способ загрузить большой объект Mat в память, чем метод FileStorage в OpenCV?

У меня есть большой коврик со 192 столбцами и 1 миллионом строк, которые я хочу сохранить локально в файле и загрузить в память, после чего запустится мое приложение. Нет проблем с использованием FileStorage, но мне было интересно, существует ли более эффективный способ сделать это. На данный момент загрузка мата в память в режиме отладки в Visual Studio занимает около 5 минут, а в режиме выпуска — около 3 минут, а размер файла данных составляет около 1,2 ГБ.

Является ли метод FileStorage единственным доступным методом для выполнения этой задачи?


person Community    schedule 01.09.2015    source источник


Ответы (1)


Вы согласны с ускорением в 100x?


Вы должны сохранять и загружать изображения в двоичном формате. Вы можете сделать это с помощью функций matwrite и matread в приведенном ниже коде.

Я протестировал загрузку как из FileStorage, так и из бинарного файла, а также для меньшего изображения с 250 тыс. строк, 192 столбца, тип CV_8UC1 и получил следующие результаты (время в мс):

// Mat: 250K rows, 192 cols, type CV_8UC1
Using FileStorage: 5523.45
Using Raw:         50.0879    

На изображении с 1 миллионом строк и 192 столбцами с использованием двоичного режима я получил (время в мс):

// Mat: 1M rows, 192 cols, type CV_8UC1
Using FileStorage: (can't load, out of memory)
Using Raw:         197.381

ПРИМЕЧАНИЕ

  1. Никогда не измеряйте производительность в отладке.
  2. 3 минуты на загрузку матрицы кажутся слишком большими, даже для FileStorages. Тем не менее, вы много выиграете, переключившись на двоичный режим.

Вот код с функциями matwrite и matread и тест:

#include <opencv2\opencv.hpp>
#include <iostream>
#include <fstream>

using namespace std;
using namespace cv;


void matwrite(const string& filename, const Mat& mat)
{
    ofstream fs(filename, fstream::binary);

    // Header
    int type = mat.type();
    int channels = mat.channels();
    fs.write((char*)&mat.rows, sizeof(int));    // rows
    fs.write((char*)&mat.cols, sizeof(int));    // cols
    fs.write((char*)&type, sizeof(int));        // type
    fs.write((char*)&channels, sizeof(int));    // channels

    // Data
    if (mat.isContinuous())
    {
        fs.write(mat.ptr<char>(0), (mat.dataend - mat.datastart));
    }
    else
    {
        int rowsz = CV_ELEM_SIZE(type) * mat.cols;
        for (int r = 0; r < mat.rows; ++r)
        {
            fs.write(mat.ptr<char>(r), rowsz);
        }
    }
}

Mat matread(const string& filename)
{
    ifstream fs(filename, fstream::binary);

    // Header
    int rows, cols, type, channels;
    fs.read((char*)&rows, sizeof(int));         // rows
    fs.read((char*)&cols, sizeof(int));         // cols
    fs.read((char*)&type, sizeof(int));         // type
    fs.read((char*)&channels, sizeof(int));     // channels

    // Data
    Mat mat(rows, cols, type);
    fs.read((char*)mat.data, CV_ELEM_SIZE(type) * rows * cols);

    return mat;
}

int main()
{
    // Save the random generated data
    {
        Mat m(1024*256, 192, CV_8UC1);
        randu(m, 0, 1000);

        FileStorage fs("fs.yml", FileStorage::WRITE);
        fs << "m" << m;

        matwrite("raw.bin", m);
    }

    // Load the saved matrix

    {
        // Method 1: using FileStorage
        double tic = double(getTickCount());

        FileStorage fs("fs.yml", FileStorage::READ);
        Mat m1;
        fs["m"] >> m1;

        double toc = (double(getTickCount()) - tic) * 1000. / getTickFrequency();
        cout << "Using FileStorage: " << toc << endl; 
    }

    {
        // Method 2: usign raw binary data
        double tic = double(getTickCount());

        Mat m2 = matread("raw.bin");

        double toc = (double(getTickCount()) - tic) * 1000. / getTickFrequency();
        cout << "Using Raw: " << toc << endl;
    }

    int dummy;
    cin >> dummy;

    return 0;
}
person Miki    schedule 02.09.2015
comment
какая польза от хранения вывода каналов? вы, кажется, не используете его при чтении файла. Спрашиваю только потому, что у меня проблемы с аналогичной функцией - person Neal; 06.03.2016
comment
@sturkmen 1) у меня есть продакшн-код с этой версией, и я не хочу его тормозить 2) информация о канале может пригодиться для загрузки изображения, скажем, в матлаб 3) экономия одного байта не имеет большого значения 4) довольно легко обновить код без каналов;) - person Miki; 09.09.2017