Контейнеры Mat интерфейса OpenCV с blas для умножения матриц

Я обрабатываю изображения UHD (2160 x 3840). Одна из обработок, которые я выполняю, состоит в обработке фильтрации Собеля по осям X и Y, затем мне нужно умножить каждую выходную матрицу на ее транспонирование, а затем я обрабатываю градиентное изображение как квадратный корень из суммы градиента.

Итак: S = sqrt(S_x * S_x^t + S_y * S_y^t).

Из-за размера изображения OpenCV обрабатывает его до двадцати секунд без многопоточности и до десяти с многопоточностью.

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

Для матричного умножения я испытываю некоторую нестабильность из-за реализации ядра OpenCV OpenCL gemm.

Поэтому я хотел бы попробовать использовать OpenBLAS.

Мои вопросы:

1.)

Я написал следующий код, но столкнулся с проблемой для объектов интерфейса OpenCV Mat:

template<class _Ty>
void mm(cv::Mat& A,cv::Mat& B,cv::Mat& C)
{
    static_assert(true,"support matrix_multiply is only defined for floating precision numbers.");
}

template<>
inline void mm<float>(cv::Mat& A,cv::Mat& B,cv::Mat& C)
{
    const int M = A.rows;
    const int N = B.cols;
    const int K = A.cols;

    cblas_sgemm( CblasRowMajor ,// 1
                 CblasNoTrans, // 2 TRANSA
                 CblasNoTrans, // 3 TRANSB
                 M,       // 4 M
                 N,       // 5 N
                 K,       // 6 K
                 1.,           // 7 ALPHA
                 A.ptr<float>(),//8 A
                 A.rows,        //9 LDA
                 B.ptr<float>(),//10 B
                 B.rows,        //11 LDB
                 0.,            //12 BETA
                 C.ptr<float>(),//13 C
                 C.rows);       //14 LDC

}

template<>
inline void mm<double>(cv::Mat& A,cv::Mat& B,cv::Mat& C)
{
    cblas_dgemm(CblasRowMajor,CblasNoTrans,CblasNoTrans,A.rows,B.cols,A.cols,1.,A.ptr<double>(),A.rows,B.ptr<double>(),B.cols,0.,C.ptr<double>(),C.rows);
}
    void matrix_multiply(cv::InputArray _src1, cv::InputArray _src2, cv::OutputArray _dst)
    {

        CV_DbgAssert(  (_src1.isMat() || _src1.isUMat()) && (_src1.kind() == _src2.kind()) &&
                      (_src1.depth() == _src2.depth()) && (_src1.depth() == CV_32F) && (_src1.depth() == _src1.type()) &&
                      (_src1.rows() == _src2.cols())
                       );


        cv::Mat src1 = _src1.getMat();
        cv::Mat src2 = _src2.getMat();
        cv::Mat dst;

        bool cpy(false);

        if(_dst.rows() == _src1.rows() && _dst.cols() == _src2.cols() && _dst.type() == _src1.type())
            dst = _dst.getMat();
        else
        {
            dst = cv::Mat::zeros(src1.rows,src2.cols,src1.type());
            cpy = true;
        }

        if(cpy)
            dst.copyTo(_dst);
    }

Я попытался организовать данные, как указано здесь: http://www.netlib.org/lapack/explore-html/db/dc9/group__single__blas__level3.html#gafe51bacb54592ff5de056acabd83c260

без успеха. Это моя основная проблема

2.) Я подумал, чтобы попытаться немного ускорить свою реализацию, чтобы применить подход «разделяй и властвуй», показанный здесь:

https://en.wikipedia.org/wiki/Matrix_multiplication_algorithm

Но только для четырех подматриц. Кто-нибудь пробовал подобный подход или нашел лучший способ повысить производительность при умножении матриц (без использования графического процессора)?

Спасибо заранее за любую помощь.


person John_Sharp1318    schedule 03.06.2016    source источник


Ответы (1)


Я нашел решение вопроса 1). Свою первую реализацию я основывал на документации библиотеки BLAS. BLAS написан на языке Fortran, в этом языке индекс начинается с 1, а не с 0, как в C или C++. Другое дело, что многие библиотеки, написанные на языке Fortran, организуют свою память в порядке столбцов (например, BLAS, LAPACK), а не большинство библиотек C или C++ (например, OpenCV) организуют память в порядке строк.

После учета этих двух свойств я изменил свой код на:

template<class _Ty>
void mm(cv::Mat& A,cv::Mat& B,cv::Mat& C)
{
    static_assert(true,"The function gemm is only defined for floating precision numbers.");
}

template<>
void mm<float>(cv::Mat& A,cv::Mat& B,cv::Mat& C)
{
    const int M = A.cols+1;
    const int N = B.rows;
    const int K = A.cols;

    cblas_sgemm( CblasRowMajor ,// 1
                 CblasNoTrans, // 2 TRANSA
                 CblasNoTrans, // 3 TRANSB
                 M,       // 4 M
                 N,       // 5 N
                 K,       // 6 K
                 1.,           // 7 ALPHA
                 A.ptr<float>(),//8 A
                 A.step1(),        //9 LDA
                 B.ptr<float>(),//10 B
                 B.step1(),        //11 LDB
                 0.,            //12 BETA
                 C.ptr<float>(),//13 C
                 C.step1());       //14 LDC
}

template<>
void mm<double>(cv::Mat& A,cv::Mat& B,cv::Mat& C)
{
    const int M = A.cols+1;
    const int N = B.rows;
    const int K = A.cols;

    cblas_dgemm( CblasRowMajor ,// 1
                 CblasNoTrans, // 2 TRANSA
                 CblasNoTrans, // 3 TRANSB
                 M,       // 4 M
                 N,       // 5 N
                 K,       // 6 K
                 1.,           // 7 ALPHA
                 A.ptr<double>(),//8 A
                 A.step1(),        //9 LDA
                 B.ptr<double>(),//10 B
                 B.step1(),        //11 LDB
                 0.,            //12 BETA
                 C.ptr<double>(),//13 C
                 C.step1());       //14 LDC
}

И все работает хорошо. Без дополнительной многопоточности или подхода «разделяй и властвуй» мне удалось сократить время обработки одного шага моего кода со 150 мс до 500 мкс. Так что это исправить все для меня :).

person John_Sharp1318    schedule 06.06.2016