当前位置: 首页 > 知识库问答 >
问题:

cv序列的像素中值::垫

孔安阳
2023-03-14

注意:我不是在问中值滤波器。

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

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

我想把所有这些图像融为一体。这一个图像应该满足:

新图像的每个像素都是序列中对应像素的中位数。换句话说:

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

有内置功能吗?最快的方法是什么?

到目前为止,我尝试的是:迭代所有序列中的每个像素,排序,然后取中值,然后将其存储在结果中。然而,它是如此矫枉过正。

共有2个答案

孔城
2023-03-14

如果序列中的图像数为奇数,则可以使用以下技术。

  • 从您拥有的图像序列中准备一个N(这是奇数)通道图像
  • 将此图像重塑为1通道列向量
  • 现在对这个列向量应用一个大小为N的中值过滤器,由于图像是1通道的列向量,过滤器会计算通道的中值(当然会有一些额外的计算对我们没有用)
  • 将此过滤后的图像重塑为具有N个通道和原始行数和列数的原始形式
  • 选择这个N通道图像的中间通道,其中包含序列的中值图像

下面我用一个简单的代码及其输出说明了上述项目。

个别频道

channel 0:
[1, 1, 1;
  1, 1, 1;
  1, 1, 1]
channel 1:
[2, 2, 2;
  2, 2, 2;
  2, 2, 2]
channel 2:
[3, 3, 3;
  3, 3, 3;
  3, 3, 3]
channel 3:
[4, 4, 4;
  4, 4, 4;
  4, 4, 4]
channel 4:
[5, 5, 5;
  5, 5, 5;
  5, 5, 5]

N=3的输出

3-channel image data:
[1, 2, 3, 1, 2, 3, 1, 2, 3;
  1, 2, 3, 1, 2, 3, 1, 2, 3;
  1, 2, 3, 1, 2, 3, 1, 2, 3]
1-channel column vector image data:
[1; 2; 3; 1; 2; 3; 1; 2; 3; 1; 2; 3; 1; 2; 3; 1; 2; 3; 1; 2; 3; 1; 2; 3; 1; 2; 3]
median of the 1-channel column vector image data:
[1; 2; 2; 2; 2; 2; 2; 2; 2; 2; 2; 2; 2; 2; 2; 2; 2; 2; 2; 2; 2; 2; 2; 2; 2; 2; 3]
reshaped filtered image data:
[1, 2, 2, 2, 2, 2, 2, 2, 2;
  2, 2, 2, 2, 2, 2, 2, 2, 2;
  2, 2, 2, 2, 2, 2, 2, 2, 3]
median image data:
[2, 2, 2;
  2, 2, 2;
  2, 2, 2]

N = 5时的输出

5-channel image data:
[1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 1, 2, 3, 4, 5;
  1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 1, 2, 3, 4, 5;
  1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 1, 2, 3, 4, 5]
1-channel column vector image data:
[1; 2; 3; 4; 5; 1; 2; 3; 4; 5; 1; 2; 3; 4; 5; 1; 2; 3; 4; 5; 1; 2; 3; 4; 5; 1; 2
; 3; 4; 5; 1; 2; 3; 4; 5; 1; 2; 3; 4; 5; 1; 2; 3; 4; 5]
median of the 1-channel column vector image data:
[1; 2; 3; 3; 3; 3; 3; 3; 3; 3; 3; 3; 3; 3; 3; 3; 3; 3; 3; 3; 3; 3; 3; 3; 3; 3; 3
; 3; 3; 3; 3; 3; 3; 3; 3; 3; 3; 3; 3; 3; 3; 3; 3; 4; 5]
reshaped filtered image data:
[1, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3;
  3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3;
  3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 5]
median image data:
[3, 3, 3;
  3, 3, 3;
  3, 3, 3]

代码:

// number of channels (= number of images in the sequence)
// N MUST BE ODD
const int N = 5;    
// channel data
uchar ch0[] = {1, 1, 1, 1, 1, 1, 1, 1, 1};
uchar ch1[] = {2, 2, 2, 2, 2, 2, 2, 2, 2};
uchar ch2[] = {3, 3, 3, 3, 3, 3, 3, 3, 3};
uchar ch3[] = {4, 4, 4, 4, 4, 4, 4, 4, 4};
uchar ch4[] = {5, 5, 5, 5, 5, 5, 5, 5, 5};
// images
Mat m0 = Mat(3, 3, CV_8U, ch0);
Mat m1 = Mat(3, 3, CV_8U, ch1);
Mat m2 = Mat(3, 3, CV_8U, ch2);
Mat m3 = Mat(3, 3, CV_8U, ch3);
Mat m4 = Mat(3, 3, CV_8U, ch4);
// prepare image sequence
Mat channels[] = {m0, m1, m2, m3, m4};
// put the images into channels of matrix m
Mat m;
merge(channels, N, m);
// reshape data so that we have a single channel column vector as the image
Mat n = m.reshape(1, m.rows * m.cols * m.channels());
// apply median filter to the 1-channel column vector image. filter size must be the number of channels
Mat med;
medianBlur(n, med, N);

cout << N << "-channel image data:" << endl;
cout << m << endl;

cout << "1-channel column vector image data:" << endl;
cout << n << endl;

cout << "median of the 1-channel column vector image data:" << endl;
cout << med << endl;

// reshape the filtered 1-channel column vector image into its original form having N channels
med = med.reshape(N, m.rows);

cout << "reshaped filtered image data:" << endl;
cout << med << endl;

// split the image
split(med, channels);

// extract the middle channel which contains the median image of the sequence
cout << "median image data:" << endl;
cout << channels[(N+1)/2 - 1] << endl;
田成仁
2023-03-14

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

假设您使用的是< code>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

添加或删除图像时,您会更新每个像素位置的直方图,并重新计算中值。这个操作相当快,因为不需要排序。

这有一些缺点:

>

  • 这将仅适用于uchar值,否则每个直方图的长度将太大
  • 这种方法将使用大量RAM,因为它需要row x ol直方图。它可能不适用于大图像。
  • 以下实现适用于单通道图像,但可以轻松扩展到3通道。
  • 您可以使用基于两个堆的方法或近似方法。您可以在此处找到详细信息:

    • 从整数流中找到运行中位数
    • C中的滚动中值算法
    • 从增长集合中找到中值

    代码:

    #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;
    }
    

  •  类似资料:
    • 实际上我想对每个像素做一些计算,为此我需要显式地访问每个像素。 如有任何帮助或建议,我们将不胜感激。

    • 我有一系列图像,我想计算中间图像(以去除运动元素)。直观地说,硬编码一个循环以遍历所有像素将有一个总的运行时间,以及相当大的内存使用量。有没有办法在OpenCV中轻松实现这一点?(我对平均值不感兴趣,我需要做一个中位数)。我是为Android写这篇文章的(使用OpenCV4Android),所以显然计算能力是有限的。

    • 问题内容: 我需要在python中更改图像的像素颜色。除了像素值(255,0,0)红色外,我需要将每个像素颜色值更改为黑色(0,0,0)。我尝试了以下代码,但没有帮助。 问题答案: 参见此Wikibook:https : //zh.wikibooks.org/wiki/Python_Imaging_Library/Editing_Pixels 修改该代码以适合您的问题:

    • 我只想弄清楚我的概念-访问CV::MAT的所有矩阵元素是否意味着我实际上访问了图像的所有像素值(灰度-1通道,颜色-3通道)?假设我的代码打印灰度矩阵的值(加载1通道图像,类型为CV_32FC1),如下所示,那么这意味着我只访问cv::mat的成员,还是我也访问图像的像素值(具有1通道灰度,类型为CV_32FC1)? 我对使用OpenCV进行图像处理还是一个新手,我想澄清一下我的想法。如果我错了,

    • 问题内容: 我打算在窗口内显示28x28像素的图像。像素的值为“ 0”,因此我希望它显示一个带有28x28黑色正方形的窗口。但是没有图像显示。也许数组的数据(我不确定像素值是否必须为0到255之间的整数)必须是其他数据才能显示图像。谢谢! 公共课程ASD { 问题答案: 返回栅格的 副本 。也许如果在修改栅格后调用,您将看到结果。 同样,应该给setPixels一个足够大的数组,以填充栅格的所有波

    • 我正在尝试使用图像编辑器(如MS paint或paint.net)在图像上绘制固定颜色的边界框(如RGB=255,0,0的纯红色),然后在python中加载图像(如opencv imread),方法是查找具有此RGB值(或0,0,255中的BGR值)的像素,以便创建用于对象检测的标签。 但是,在保存并加载图像后,我看不到任何具有这种RGB或BGR值的像素。相反,这些像素在一个值范围内,这个值可能与