简单加速C ++ OpenMP内核

时间:2015-06-08 12:26:55

标签: c++ opencv openmp

我从未使用OpenMP或C ++优化,所以欢迎所有帮助。我可能正在做一些非常愚蠢的事情,这会大大减缓这个过程。它不需要是最快的,但我认为一些简单的技巧将显着加快它。任何人?非常感谢!

在给定内核大小和灰度OpenCV图像的情况下,此函数计算补丁的标准差。如果补丁的中间像素低于给定阈值,则保留其中间像素,否则拒绝。这是为除边框之外的每个像素完成的。

#include "stdafx.h"
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/photo/photo.hpp"
#include <stdlib.h>
#include <stdio.h>
#include "utils.h"
#include <windows.h>
#include <string.h>
#include <math.h>
#include <numeric>

using namespace cv;
using namespace std;
Mat low_pass_filter(Mat img, int threshold, int kernelSize)
{
    unsigned char *input = (unsigned char*)(img.data);
    Mat output = Mat::zeros(img.size(), CV_8UC1);
    unsigned char *output_ptr = (unsigned char*)(output.data);

    #pragma omp parallel for
    for (int i = (kernelSize - 1) / 2; i < img.rows - (kernelSize - 1) / 2; i++){
        for (int j = (kernelSize - 1) / 2; j < img.cols - (kernelSize - 1) / 2; j++){
            double sum, m, accum, stdev;
            vector<double> v;
            // Kernel Patch
            for (int kx = i - (kernelSize - 1) / 2; kx <= i + (kernelSize - 1) / 2; kx++){
                for (int ky = j - (kernelSize - 1) / 2; ky <= j + (kernelSize - 1) / 2; ky++){
                    v.push_back((double)input[img.step * kx + ky]);//.at<uchar>(kx, ky));
                }
            }
            sum = std::accumulate(std::begin(v), std::end(v), 0.0);
            m = sum / v.size();

            accum = 0.0;
            std::for_each(std::begin(v), std::end(v), [&](const double d) {
                accum += (d - m) * (d - m);
            });

            stdev = sqrt(accum / (v.size() - 1));
            if (stdev < threshold){
                output_ptr[img.step * i + j] = input[img.step * i + j];
            }
        }
    }
    return output;
}

1 个答案:

答案 0 :(得分:2)

不需要Vector v。不要向其中添加项目,而是维护dd*d的累加器,然后使用variance = E(v²) / E(v)²以便内部代码变为:

        double sum = 0;
        double sum2 = 0;
        int n = kernelSize * kernelSize;
        // Kernel Patch
        for (int kx = ...) {
            for (int ky = ...) {
                sum += d;
                sum2 += d*d;
            }
        }

        double mean = sum/n;
        double stddev = sqrt(sum2/n - mean*mean);
        if (stddev < threshold) {
            ...;
        }

之后,考虑以(x + 1,y)为中心的元素之和可以从(x,y)的结果中找到,只需减去前一个左栏中的所有元素,然后添加全部新右栏中的元素。类似的操作垂直工作。

另外,检查您的编译器选项 - 您是自动向量化循环,还是使用SIMD指令(如果可用)?