OpenCV против байтового массива - PullRequest
0 голосов
/ 18 ноября 2018

Я работаю над простым приложением C ++ для обработки изображений и решаю, использовать ли OpenCV для загрузки изображения и доступа к отдельным пикселям. Мой текущий подход состоит в том, чтобы просто загрузить изображение, используя fopen, читая 54-байтовый заголовок, и загрузить остальные байты в массив char*.

Для доступа к определенному пикселю я использую

long q = (long*)(bmpData + x*3 + (bmpSize.height - y - 1) * bmpSize.stride);

Чтобы выполнить простую проверку цвета, например, "синий?"

if (((long*)q | 0xFF000000) == 0xFFFF0000) //for some reason RGB is reversed to BGR
  //do something here

Работает ли OpenCV быстрее, учитывая все вызовы функций, синтаксический анализ и т. Д .?

1 Ответ

0 голосов
/ 18 ноября 2018

Заголовок растрового файла на самом деле составляет 54 байта, и вы не можете его пропустить. Вы должны прочитать его, чтобы найти ширину, высоту, количество битов ... при необходимости рассчитать отступы ... и другую информацию.

В зависимости от того, как файл открыт, OpenCV считывает заголовок и считывает пиксели непосредственно в буфер. Единственное изменение заключается в том, что строки переворачиваются, поэтому изображение перевернуто вправо.

cv::Mat mat = cv::imread("filename.bmp", CV_LOAD_IMAGE_COLOR);
uint8_t* data = (uint8_t*)mat.data;

Проверка заголовка и небольшие изменения, внесенные OpenCV, не окажут существенного влияния на производительность. Горлышко бутылки в основном в чтении файла с диска. Изменение производительности будет трудно измерить, если вы не выполняете очень специфическую задачу, например, вы хотите, чтобы в очень большом файле было только 3 байта, и вы не хотите читать весь файл.

OpenCV излишне подходит для этой задачи, поэтому вы можете выбрать другие библиотеки, например CImg, как это предлагается в комментариях. Если вы используете меньшие библиотеки, они загружаются быстрее, это может быть заметно при запуске вашей программы.


Следующий код является тестовым запуском в Windows.

Для большого 16-мегабайтного растрового файла результат почти идентичен для opencv и обычного c ++.

Для небольшого 200-килобайтного файла растрового изображения результат составляет 0,00013 секунды для чтения на простом C ++ и 0,00040 секунд для opencv. Обратите внимание, что простой c ++ не делает ничего, кроме чтения байтов.

class stopwatch
{
    std::chrono::time_point<std::chrono::system_clock> time_start, time_end;
public:
    stopwatch() { reset();}
    void reset(){ time_start = std::chrono::system_clock::now(); }
    void print(const char* title)
    {
        time_end = std::chrono::system_clock::now();
        std::chrono::duration<double> diff = time_end - time_start;
        if(title) std::cout << title;
        std::cout << diff.count() << "\n";
    }
};

int main()
{
    const char* filename = "filename.bmp";

    //I use `fake` to prevent the compiler from over-optimization 
    //and skipping the whole loop. But it may not be necessary here
    int fake = 0;

    //open the file 100 times
    int count = 100;

    stopwatch sw;
    for(int i = 0; i < count; i++)
    {
        //plain c++
        std::ifstream fin(filename, std::ios::binary);
        fin.seekg(0, std::ios::end);
        int filesize = (int)fin.tellg();
        fin.seekg(0, std::ios::beg);
        std::vector<uint8_t> pixels(filesize - 54);

        BITMAPFILEHEADER hd;
        BITMAPINFOHEADER bi;
        fin.read((char*)&hd, sizeof(hd));
        fin.read((char*)&bi, sizeof(bi));
        fin.read((char*)pixels.data(), pixels.size());

        fake += pixels[i];
    }
    sw.print("time fstream: ");

    sw.reset();
    for(int i = 0; i < count; i++)
    {
        //opencv:
        cv::Mat mat = cv::imread(filename, CV_LOAD_IMAGE_COLOR);
        uint8_t* pixels = (uint8_t*)mat.data;
        fake += pixels[i];
    }
    sw.print("time opencv:  ");

    printf("show some fake calculation: %d\n", fake);

    return 0;
}
...