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

Для таких задач есть, к примеру библиотека libjpeg, разработанная группой Томом Лэйном и IJG (Independent JPEG Group). Или можно использовать библиотеку libjpeg-turbo, которая на некоторых системах может работать в 2-6 раз быстрее стандартной libjpeg библиотеки (как указано на их сайте).

Для работы с объектами линейной алгебры, то есть матрицами я буду использовать библиотеку armadillo.

В процессе реализации алгоритма проверил на сколько отличается скорость работы двух библиотек (libjpeg, libjpeg-turbo). На моей машине, работа libjpeg-turbo работает примерно в 2 раза быстрее, чем стандартная реализация libjpeg.

Так же покапался в исходниках библиотеки OpenCV - внутри используется api стандартной библиотеки libjpeg, если на вашей не установлена библиотека libjpeg-turbo, то будет использоваться стандартная библиотека libjpeg. Стоит задуматься об установки turbo версии для более быстрой работы. Так же сам код в библиотеки OpenCV можно оптимизировать еще лучше чем из коробки (например, не делать преобразований bgr в rgb), однако, делать это не рекомендую без веской причины. К тому же библиотека OpenCV достатоно хорошо оптимизирована из коробки и работает быстро. В примере ниже привожу работу 3 варианта кода (libjpeg, libjpeg-turbo (high level api), opencv).

Так же мне удалось написать более быстрый код для создания матриц, чем вариант OpenCV - оптимизированный код по созданию матриц из jpeg файлов с помощью libjpeg-turbo и armadillo. Далее матрицы armadillo можно использовать в библиотеке mlpack

Код внизу:

#include <iostream>
#include <armadillo>
#include <turbojpeg.h>
#include <jpeglib.h>
#include <chrono>
#include <opencv2/opencv.hpp>

using namespace arma;
using namespace std;
using namespace std::chrono;

struct jpeg_error_mgr jerr;
typedef unsigned short ushort;
typedef unsigned int uint;

int method1(char *jpegUrl) {
    auto start = high_resolution_clock::now();
    FILE *infile;
    struct jpeg_decompress_struct dinfo;
    JSAMPARRAY buffer = 0;

    dinfo.err = jpeg_std_error(&jerr);

    infile = fopen(jpegUrl, "rb");
    jpeg_create_decompress(&dinfo);
    jpeg_stdio_src(&dinfo, infile);
    jpeg_read_header(&dinfo, TRUE);
    jpeg_start_decompress(&dinfo);

    int img_width = dinfo.image_width;
    int img_height = dinfo.image_height;
    int color_num = dinfo.output_components;

    /* Make a one-row-high sample array that will go away when done with image */
    buffer = (*dinfo.mem->alloc_sarray)((j_common_ptr) &dinfo, JPOOL_IMAGE, img_width * color_num, 1);

    // rgb image
    Cube<short> image(img_height, img_width, 3);
    int i = 0, j = 0, k1 = 0;
    while (dinfo.output_scanline < dinfo.output_height) {
        jpeg_read_scanlines(&dinfo, buffer, 1);
        for (j = 0; j < img_width; j++) {
            k1 = (j * 3);
            image(i, j, 0) = (ushort) buffer[0][k1 + 0];
            image(i, j, 1) = (ushort) buffer[0][k1 + 1];
            image(i, j, 2) = (ushort) buffer[0][k1 + 2];
        }
        i++;
    }

    jpeg_finish_decompress(&dinfo);
    jpeg_destroy_decompress(&dinfo);

    auto stop = high_resolution_clock::now();
    auto duration = duration_cast<microseconds>(stop - start);
    return duration.count();
}

int method2(char *jpegUrl) {
    auto start = high_resolution_clock::now();
    FILE *jpegFile = fopen(jpegUrl, "rb");
    unsigned char *jpegBuf = NULL, *imgBuf = NULL;
    uint jpegSize;
    int width, height, pixelFormat, size;

    fseek(jpegFile, 0, SEEK_END);
    size = ftell(jpegFile);
    fseek(jpegFile, 0, SEEK_SET);

    jpegSize = size;

    if ((jpegBuf = tjAlloc(jpegSize)) == NULL)
        cout << ("allocating JPEG buffer") << endl;
    if (fread(jpegBuf, jpegSize, 1, jpegFile) < 1)
        cout << ("reading input file") << endl;
    fclose(jpegFile);

    tjhandle jpegInstance = tjInitDecompress();
    tjDecompressHeader(jpegInstance, jpegBuf, jpegSize, &width, &height);

    pixelFormat = TJPF_RGB;
//    pixelFormat = TJPF_GRAY; // если GRAYSCALE, то 1 байт на пиксель
    int i, j, pix_size = tjPixelSize[pixelFormat]; // если  RGB, то 4 байта на пиксель (в libjpeg 3 байта), GRAYSCALE - 1 байт.

    imgBuf = tjAlloc(width * height * tjPixelSize[pixelFormat]);
    tjDecompress2(jpegInstance, jpegBuf, jpegSize, imgBuf, width, 0, height, pix_size, TJFLAG_FASTUPSAMPLE);

    Cube<short> image(height, width, 3);
    int k1, k2;
    for (i = 0; i < height; i++) {
        k1 = (i * width * pix_size);
        for (j = 0; j < width; j++) {
            k2 = (j * pix_size);
            image(i, j, 0) = imgBuf[k1 + k2 + 0];
            image(i, j, 1) = imgBuf[k1 + k2 + 1];
            image(i, j, 2) = imgBuf[k1 + k2 + 2];
        }
    }

    tjFree(imgBuf);
    tjFree(jpegBuf);
    tjDestroy(jpegInstance);
    auto stop = high_resolution_clock::now();
    auto duration = duration_cast<microseconds>(stop - start);
    return duration.count();
}

int method3(char *jpegUrl) {
    auto start = high_resolution_clock::now();
    cv::Mat image = cv::imread(jpegUrl, 1);
    auto stop = high_resolution_clock::now();
    auto duration = duration_cast<microseconds>(stop - start);
    return duration.count();
}

int main(int argc, char **argv) {
    char *url = argv[1];
    auto t1 = method1(url);
    auto t2 = method2(url);
    auto t3 = method3(url);

    cout << "Time taken by method1: " << t1 << " microseconds" << endl;
    cout << "Time taken by method2: " << t2 << " microseconds" << endl;
    cout << "Time taken by method2: " << t3 << " microseconds" << endl;

    float t2t1 = (float) t2 / t1;
    float t1t2 = (float) t1 / t2;
    if (t1 > t2)
        printf("method2 faster than method1 in %.2f times \n", t1t2);
    if (t2 > t1)
        printf("method1 faster than method2 in %.2f times \n", t2t1);
    return 0;
}

via GIPHY

Удачи!