cv :: Mat序列的像素中位数

时间:2016-08-12 06:00:45

标签: c++ opencv

注意:我不是在询问Median Filter。

我有一系列图像让我们说:

std::array<cv::Mat,N> sequence;

我想把所有这些图片合二为一。这一张图片应该满足:

新图像的每个像素是来自序列的其对应像素的中值。换句话说:

Result(i,j)=median(sequence[0](i,j), sequence[1](i,j), ..., sequence[N](i,j));

这样做有内置功能吗?什么是最快的方式?

我到现在为止尝试:迭代所有序列中的每个像素并排序然后取中位数然后将其存储在结果中。但是,它太过分了。

1 个答案:

答案 0 :(得分:6)

您可以使用直方图计算每个位置的顺序中位数。

假设您正在使用Mat1b图像,每个直方图将具有256个值。 您需要存储直方图以及所有箱的总和:

struct Hist {
    vector<short> h;
    int count;
    Hist() : h(256, 0), count(0) {};
};

中值是直方图中与一半像素count / 2对应的索引。来自Rosetta Code的代码段:

int i;
int n = hist.count / 2; // 'hist' is the Hist struct at a given pixel location
for (i = 0; i < 256 && ((n -= hist.h[i]) >= 0); ++i);
// 'i' is the median value

添加或删除图像时,更新每个像素位置的直方图,然后重新计算中值。此操作非常快,因为您不需要排序。

这有一些缺点:

  1. 这仅适用于uchar值,否则每个直方图的长度会太大
  2. 这种方法会使用大量的RAM,因为它需要rows x cols个直方图。它可能不适用于大型图像。
  3. 以下实施适用于单通道图像,但可以轻松扩展到3个通道。
  4. 您可以使用基于两个堆或近似方法的方法。你可以在这里找到详细信息:

  5. 代码:

    #include <vector>
    #include <opencv2/opencv.hpp>
    
    using namespace std;
    using namespace cv;
    
    struct Hist {
        vector<short> h;
        int count;
        Hist() : h(256, 0), count(0) {};
    };
    
    void addImage(vector<Mat1b>& images, Mat1b& img, vector<vector<Hist>>& M, Mat1b& med)
    {
        assert(img.rows == med.rows);
        assert(img.cols == med.cols);
    
        for (int r = 0; r < img.rows; ++r) {
            for (int c = 0; c < img.cols; ++c){
    
                // Add pixel to histogram
                Hist& hist = M[r][c];
                ++hist.h[img(r, c)];
                ++hist.count;
    
                // Compute median
                int i;
                int n = hist.count / 2;
                for (i = 0; i < 256 && ((n -= hist.h[i]) >= 0); ++i);
    
                // 'i' is the median value
                med(r,c) = uchar(i);
            }
        }
    
        // Add image to my list
        images.push_back(img.clone());
    }
    
    void remImage(vector<Mat1b>& images, int idx, vector<vector<Hist>>& M, Mat1b& med)
    {
        assert(idx >= 0 && idx < images.size());
    
        Mat1b& img = images[idx];
        for (int r = 0; r < img.rows; ++r) {
            for (int c = 0; c < img.cols; ++c){
    
                // Remove pixel from histogram
                Hist& hist = M[r][c];
                --hist.h[img(r, c)];
                --hist.count;
    
                // Compute median
                int i;
                int n = hist.count / 2;
                for (i = 0; i < 256 && ((n -= hist.h[i]) >= 0); ++i);
    
                // 'i' is the median value
                med(r, c) = uchar(i);
            }
        }
    
        // Remove image from list
        images.erase(images.begin() + idx);
    }
    
    void init(vector<vector<Hist>>& M, Mat1b& med, int rows, int cols)
    {
        med = Mat1b(rows, cols, uchar(0));
        M.resize(rows);
        for (int i = 0; i < rows; ++i) {
            M[i].resize(cols);
        }
    }
    
    
    int main()
    {
        // Your images... be sure that they have the same size
        Mat1b img0 = imread("path_to_image", IMREAD_GRAYSCALE);
        Mat1b img1 = imread("path_to_image", IMREAD_GRAYSCALE);
        Mat1b img2 = imread("path_to_image", IMREAD_GRAYSCALE);
        resize(img0, img0, Size(800, 600));
        resize(img1, img1, Size(800, 600));
        resize(img2, img2, Size(800, 600));
    
    
    
        int rows = img0.rows;
        int cols = img0.cols;
    
        vector<Mat1b> images;   // All your images, needed only if you need to remove an image
        vector<vector<Hist>> M; // histograms
        Mat1b med;              // median image
    
        // Init data strutctures
        init(M, med, rows, cols);
    
        // Add images. 'med' will be the median image and will be updated each time
        addImage(images, img0, M, med);
        addImage(images, img1, M, med);
        addImage(images, img2, M, med);
    
        // You can also remove an image from the median computation
        remImage(images, 2, M, med);
    
        // Hey, same median as img0 and img1 ;D
    
        return 0;
    }