Распараллеливание обработки изображений


Я работаю в большом проекте, поэтому я решил взять только часть из него, который вы можете копировать/вставить и скомпилировать в вашей машине. Вы можете получить странные изображения в конце, но это нормально, что я хочу.

Я делюсь с вами этой части кодекса по основной причиной, чтобы сделать его работать быстрее, но если у вас есть любые другие советы или что-нибудь сказать по поводу моего кода, не стесняйтесь.

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

Вы можете скомпилировать эту программу:

g++ -g -std=c++1z -Wall -Weffc++ -Ofast -march=native test5.cpp -o test5 -fopenmp `pkg-config --cflags --libs opencv`

И запустить программу так:

./test5 image1.png image2.png

Это код:

#include <opencv2/core.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/imgcodecs.hpp>
#include <opencv2/core/utility.hpp>

#include <iostream>
#include <chrono>

using std::chrono::high_resolution_clock;
using std::chrono::duration_cast;
using std::chrono::microseconds;


class Parallel_process : public cv::ParallelLoopBody
    {

    private:
        cv::Mat img;
        std::vector<int> A;
        int diff;

    public:
        Parallel_process(cv::Mat inputImgage, std::vector<int> AA, int diffVal)
                           : img(inputImgage), A(AA), diff(diffVal){}

        virtual void operator()(const cv::Range& range) const
        {
            for(int i = range.start; i < range.end; i++)
            {

               cv::Mat in(img, cv::Rect(0, (img.rows/diff)*i, img.cols, img.rows/diff));
               std::vector<int> AAA (A);
               in.forEach<cv::Vec3f>
                (
                  [&AAA](cv::Vec3f &pixel, const int* po) -> void
                  {
                    pixel[0]/=AAA[0];
                    pixel[1]/=AAA[1];
                    pixel[2]/=AAA[2];
                  }
                );
            }
        }
};

cv::Mat dcp(const cv::Mat&, auto, auto, const cv::Mat&, double);
auto calculateSD(const cv::Mat&,auto, auto);
void fftshift(cv::Mat&);
cv::Mat transmission(cv::Mat&, cv::Mat&);

void GammaCorrection(cv::Mat&, unsigned char*, cv::Mat&);

template <typename T, typename ... Ts>
void insert_all(std::vector<T> &vec, Ts ... ts)
{
    (vec.push_back(ts), ...);
}

typedef std::vector<std::vector<int> > Matrix;


int main(int argc, char* argv[])
{

    cv::Mat im_test = cv::imread(argv[1]);// = cv::Mat::zeros(src.rows, src.cols, CV_32FC1);

    auto rows=im_test.rows,
         cols=im_test.cols;
    cv::Mat fin_img;                
    cv::Mat src=cv::imread(argv[2]);
    cv::Mat src_temp = src.clone();

    // build look up table
    unsigned char lut[256];
    auto fGamma=0.4;
    #pragma omp for
    for (size_t i=0; i<256; i++)
        lut[i] = cv::saturate_cast<uchar>(pow((float)(i / 255.0), fGamma) * 255.0f);

    //std::cout<<cv::getBuildInformation()<<std::endl;  

        high_resolution_clock::time_point t1(high_resolution_clock::now());

    GammaCorrection(src_temp, lut, src_temp);
    std::vector<cv::Mat> rgb;
    cv::split(src_temp, rgb);
    Matrix histSum(3, std::vector<int>(256,0));   

    src_temp.forEach<cv::Vec3b>
    (
      [&histSum](cv::Vec3b &pixel, const int* po) -> void
      {
        ++histSum[0][pixel[0]];
        ++histSum[1][pixel[1]];
        ++histSum[2][pixel[2]];
      }
    );

    std::vector<int> A(3, 255);
    auto A_estim_lambda([&A, rows, cols, &histSum]{

        for (auto index=8*rows*cols/1000; index>histSum[0][A[0]]; --A[0])        
         index -= histSum[0][A[0]];
    for (auto index=8*rows*cols/1000; index>histSum[1][A[1]]; --A[1])
             index -= histSum[1][A[1]];
    for (auto index=8*rows*cols/1000; index>histSum[2][A[2]]; --A[2])
             index -= histSum[2][A[2]];
        return A;
   });
    auto AA=A_estim_lambda();

    cv::Mat srcN = src_temp.clone();
    srcN.convertTo(srcN, CV_32FC3);
    im_test.convertTo(im_test, CV_32FC3);

    cv::parallel_for_(cv::Range(0, 91), Parallel_process(srcN, AA, 91));   

    cv::Mat IllumTrans = transmission(srcN, im_test);

    std::vector<cv::Mat> rgbDCP;    
    rgbDCP.reserve(3);
    insert_all(rgbDCP, dcp(rgb[0], rows, cols, IllumTrans, A[0]), 
                       dcp(rgb[1], rows, cols, IllumTrans, A[1]), 
                       dcp(rgb[2], rows, cols, IllumTrans, A[2]));

    cv::merge(rgbDCP, fin_img);

    cv::medianBlur(fin_img, fin_img, 3); //5 c trop

    fin_img.convertTo(fin_img, CV_8UC3, 255.0);

    cv::Mat temp;
    cv::GaussianBlur(fin_img, temp, cv::Size(0, 0), 3);
    cv::addWeighted(fin_img, 1.5, temp, -0.5, 0, fin_img);

        fGamma=1.5;
        for (size_t i=0; i<256; i++)
                lut[i] = cv::saturate_cast<uchar>(pow((float)(i / 255.0), fGamma) * 255.0f);      

        GammaCorrection(fin_img, lut, fin_img);

    high_resolution_clock::time_point t2(high_resolution_clock::now());
    auto timeEnd=1.0/static_cast<double>(duration_cast<microseconds>(t2 - t1).count())*1000000;
    std::cout<<timeEnd<<std::endl;

    cv::imshow("kernel", fin_img);
    cv::waitKey();

    return 0;
}

void GammaCorrection(cv::Mat& src, unsigned char* lut, cv::Mat& dst)
{   
    dst.forEach<cv::Vec3b>
        (
          [&lut](cv::Vec3b &pixel, const int* po) -> void
          {
                  pixel[0] = lut[(pixel[0])];
                  pixel[1] = lut[(pixel[1])];
                  pixel[2] = lut[(pixel[2])];

          }
        );
}


auto calculateSD(const cv::Mat& src, auto rows, auto cols)
{

    double sum{0};
    double sq_sum{0};

    #pragma omp for
    for(auto j=0;j<rows;j++)
        for(auto i=0;i<cols;i++)
        {
            sum += src.at<float>(j,i);
            sq_sum += src.at<float>(j,i) * src.at<float>(j,i);
        }


    double mean = sum / (rows*cols);
    double variance = sq_sum / (rows*cols) - mean * mean;

    return sqrt(variance);
}

cv::Mat transmission(cv::Mat& srcN, cv::Mat& im_test )
{
    cv::Mat srcN_gray;
    cv::cvtColor(srcN, srcN_gray, cv::COLOR_RGB2GRAY);
    cv::cvtColor(im_test, im_test, cv::COLOR_RGB2GRAY);
    cv::Mat srcN_fft;
    dft(srcN_gray, srcN_fft, cv::DFT_COMPLEX_OUTPUT) ;
    dft(im_test, im_test, cv::DFT_COMPLEX_OUTPUT);

    cv::Mat mul_fft;    
    cv::mulSpectrums(im_test, srcN_fft, mul_fft, 0);

    cv::Mat mul_invfft;
    dft(mul_fft, mul_invfft, cv::DFT_INVERSE | cv::DFT_SCALE | cv::DFT_REAL_OUTPUT);

    fftshift(mul_invfft);

    float stddev=calculateSD(mul_invfft, im_test.rows, im_test.cols);

    return (1-(mul_invfft-stddev));

}

cv::Mat dcp(const cv::Mat& src, auto rows, auto cols, const cv::Mat& IllumTrans, double A )
{

    cv::Mat imJ=cv::Mat::zeros(rows, cols, CV_32FC1);
    #pragma omp for
    for(auto j=0;j<rows;j++)
        for(auto i=0;i<cols;i++)
            imJ.at<float>(j,i)= A+((src.at<uchar>(j,i)-A)/std::max(IllumTrans.at<float>(j,i), 0.1f));

    double minVal=0, maxVal=0;
    minMaxLoc(imJ, &minVal, &maxVal);

    return imJ/maxVal;  
}




void fftshift(cv::Mat& src)
{
    int cx = src.cols/2;
    int cy = src.rows/2;

    cv::Mat q0(src, cv::Rect(0, 0, cx, cy));   
    cv::Mat q1(src, cv::Rect(cx, 0, cx, cy));  
    cv::Mat q2(src, cv::Rect(0, cy, cx, cy));  
    cv::Mat q3(src, cv::Rect(cx, cy, cx, cy)); 

    cv::Mat tmp;                           
    q0.copyTo(tmp);
    q3.copyTo(q0);
    tmp.copyTo(q3);

    q1.copyTo(tmp);
    q2.copyTo(q1);
    tmp.copyTo(q2); 
}


494
3
задан 2 февраля 2018 в 02:02 Источник Поделиться
Комментарии
2 ответа

Быстрый, обзор частичный (только одна функция) [может быть, у меня будет больше времени позже, чтобы выяснить, что делает ваш код].

auto calculateSD(const cv::Mat& src, auto rows, auto cols)
{
double sum{0};
double sq_sum{0};

#pragma omp for
for(auto j=0;j<rows;j++)
for(auto i=0;i<cols;i++)
{
sum += src.at<float>(j,i);
sq_sum += src.at<float>(j,i) * src.at<float>(j,i);
}

double mean = sum / (rows*cols);
double variance = sq_sum / (rows*cols) - mean * mean;

return sqrt(variance);
}

Этот код, вероятно, дать вам неправильные результаты. У вас есть несколько потоков все обновления тех же двух переменных. sum += x такой же, как и sum = sum + x. Каждый поток читает sumобновляет значение, и записывает его обратно. Прежде чем он снова мне написал, стоимость sum скорее всего, эти изменения уже не будет.

OpenMP, имеется reduction статья просто для этого типа цикла:

#pragma omp parallel for reduction(+:sum,sq_sum)

С этим положением каждый поток получает свою собственную копию переменных sum и sq_sumи в конце петли эти локальные переменные объединяются в функцию копию этих переменных.

Алгоритм вы используете здесь для вычисления дисперсии быстро, но он неустойчив. Если имеешь в виду большой Ж.Р.Т. дисперсии, вы получите бессмысленные результаты. Увидеть это последний пост на ревью кода для каких-то дискуссий по этому вопросу. Там ссылки есть на ресурсы по алгоритму Велфорд, который является стабильным однопроходной алгоритм, чтобы вычислить среднее значение и дисперсию.

Наконец, я честно не вижу выгоды от объявления функции auto. Пишу double есть только 2 требует больше нажатий. В обмен, интерфейс функция является явным, который позволяет использовать его намного проще. То же самое верно для auto во входных аргументов.* Я не знаю, что целочисленного типа в формате OpenCV использует для строки и столбца размеров, но если это не intтогда ваш код здесь

for(auto j=0;j<rows;j++)

вероятно, не делать то, что вы хотите, потому что j будет объявлен intне типа rows (0 это int).

Редактировать:

* Чтение больше о auto параметры в объявление функции, я узнал, что это понятия ТС вещь, и с помощью этого шаблона спецификация. Это означает, что это не стандарт C++ еще. Это также означает, что эта функция является шаблоном функции. Мне не нравится идея, шаблон, который явно не так, и не нравится идея, шаблон, который будет только когда-либо использоваться с одним типом. Это предотвращает системы типов C++, чтобы делать свое дело, и это может означать, что вы в конечном итоге компиляции несколькими версиями функции, чем это необходимо. Тип является важной частью языка C++, я не в пользу прячет от читателя кода, и тем более не от пользователя из ваших функций. Я бы предпочел явные типы в объявлении функции.

4
ответ дан 2 февраля 2018 в 04:02 Источник Поделиться

Это звучит как очень интересный проект! Вот несколько мыслей на ваш код.

Именования

Я должен признать, что ваш именования-это смесь хорошего и плохого. Конструктор Parallel_process использует inputImgage, который, описки в сторону, говорит мне, что это. Но тогда у вас есть vector имени AA. Когда я вижу AA обработка изображений код, я обычно предполагаю, что это значит либо "ось-союзник" (как в ААВВ = "выровненный по осям ограничивающий прямоугольник"), или "сглаживание". Это не значит ни в этом случае. Что это значит? И вас назначить AA переменной по имени A. Что A?

Имя diffVal и diff почти так же плохо. По крайней мере, я знаю, что это разница между двумя вещами. (Или это дифференциал?) Всякий раз, когда вы используете слово "значение" (или сокращение) имя, вы, вероятно, нужно переосмыслить именем. Что это разница? Вы делите его, так что кажется, будто это на самом деле спектр как разницу между минимальным и максимальным значениями... что-то. Было бы неплохо, чтобы позволить читателю вашего кода знаешь, ЧТО ЭТО.

Тогда у вас внутри operator() имени AAA что представляется копия Aнесмотря на то, что вы не измените его на всех. Почему ты не используешь A где бы вы ни были AAA? Это ускорит дело, как вы не нужно копировать A.

Это не сразу понятно, что dcp стенды для.

Я полагаю calculateSD() вычисляет стандартное отклонение? Я хотел уточнить SD быть standardDeviation. Вы можете сократить его, если вам нравится, но SD перегружен, слишком. (Стандартное определение, супер плотности, среднеквадратического отклонения, функции Якоби.)

Функции

Ваш main() функция может быть сделано гораздо проще и легче читать, если вы нарушили его в функции описательные имена. Это:

// build look up table
unsigned char lut[256];
auto fGamma=0.4;
#pragma omp for
for (size_t i=0; i<256; i++)
lut[i] = cv::saturate_cast<uchar>(pow((float)(i / 255.0), fGamma) * 255.0f);

//std::cout<<cv::getBuildInformation()<<std::endl;

high_resolution_clock::time_point t1(high_resolution_clock::now());

GammaCorrection(src_temp, lut, src_temp);

может быть введен в функцию с именем convertToLinearRGB(). Что сказал, Вы действительно должны использовать гамма-0.4? Если вы имеете дело с самым нормальное изображение (с sRGB) или видео (Зап. 709) форматы, 1.0 / 2.2 = .4545... будет лучшим выбором. Линейное смещение около 0 составляет 2,4 или 2,5 субоптимальные решения для преобразования, несмотря на то, что они используются в реальных расчетов.

Далее вы должны положить на гистограмме расчет в функцию по имени histogram().

Для A_estim_lambdaпочему ты определяешь лямда, а затем сразу же определить переменную, которая просто присваивается лямбда? Почему бы просто не сделать это в 1 строку?

Уменьшить Сложность

Я вижу как минимум 2 разных параллельных вычислительных систем в использовании - OpenMP и параллельных структур в формате OpenCV по. Если вы получаете некоторый материальный перевес из них, тогда, возможно, оно того стоит, но через так много различных систем делает его более сложным для поддержания и понимания.

Кроме того, первый цикл в main() вряд ли бы помогли быть распараллелен. Для 256 значений, накладные расходы на создание нескольких потоков, скорее всего, будет больше, чем время, которое требуется, чтобы просто сделать расчеты.

Производительности

К сожалению, я не могу запустить свой код и профиль его, потому что у меня нет установленного OpenCV на моей машине. Но вы должны профилировать код, чтобы увидеть, какие конкретные строки, в которых определенные функции стоят большую часть времени. Не делая этого, он не имеет смысла, чтобы начать оптимизировать его, потому что вы не знаете, какие части являются самыми медленными. Вы, кажется, компиляции с помощью GCC, так что вы можете, вероятно, использовать gprof для профилирования. В зависимости от того, какую ОС вы, там могут быть дополнительные инструменты для профилирования. Я рекомендую вам проверить их.

3
ответ дан 3 февраля 2018 в 03:02 Источник Поделиться