减去两个 CV_64FC3 矩阵并保持负值

Posted

技术标签:

【中文标题】减去两个 CV_64FC3 矩阵并保持负值【英文标题】:Subtracting two CV_64FC3 matrices and keeping the negative values 【发布时间】:2021-03-17 21:29:11 【问题描述】:

据我所知,减去两个非浮点矩阵会产生预期结果,但减去两个 CV_64FC3(以及 CV_32F)矩阵会将负值裁剪为 0.0 并且将结果归一化在 0.0 和1.0。我知道这些限制并阅读了相关文档,但仍然无法减去两个矩阵。

    Mat lowpass1, lowpass2, mask;
    
    mask.convertTo(mask, CV_32F);
    lowpass1.convertTo(lowpass1, CV_32F);
    lowpass2.convertTo(lowpass2, CV_32F);           
    
    // all other variables (high_b1, high_a0 etc.) are double 
    // and in the range of -1.0 to 1.0 but never 0.0
    // frame and prev_frame are CV_32F matrices

    lowpass1 = (-high_b1 * lowpass1 + high_a0 * frame + high_a1 * prev_frame) / high_b0;
    lowpass2 = (-low_b1 * lowpass2 + low_a0 * frame + low_a1 * prev_frame) / low_b0;

    mask = lowpass1 - lowpass2

尽管 lowpass1 和 lowpass2 包含非零正值,但减法始终为 0。我假设它正在裁剪和/或四舍五入到最接近的整数。

    mask = lowpass2 - lowpass1

这也给出了与上述完全相同的结果。

工作的最小示例;

#include <iostream>
#include <vector>
#include <list>
#include <opencv2/opencv.hpp>
    
using std::cout;
using std::string;
using std::list;
using std::vector;
using cv::Mat;

int main() 
    cv::VideoCapture cap("small.mp4");
    const int level = 2;
    vector<Mat> *data = new vector<Mat>;
    vector<vector<Mat>> pyramid;
    pyramid.resize(level);
    Mat frame;

    while (true) 
        cap >> frame;
        if (frame.empty())
            break;
        frame.convertTo(frame, CV_32F, 1.0 / 255.0f);
        data->push_back(frame.clone());

        Mat current = frame.clone();
        for (int i = 0; i < level; i++) 
            Mat down, up;
            if (i == (level - 1)) 
                pyramid[i].push_back(current);
                break;
            
            cv::pyrDown(current, down);
            cv::pyrUp(down, up, current.size());
            pyramid[i].push_back(current - up);
            current = down;
        
    

    double low_a0 = 0.04979798;
    double low_a1 = 0.04979798;
    double low_b0 = 1;
    double low_b1 = -0.90040404;
    double high_a0 = 0.13672874;
    double high_a1 = 0.13672874;
    double high_b0 = 1;
    double high_b1 = -0.72654253;
    
    vector<vector<Mat>> filtered;
    filtered.resize(level);

    for (int i = 1; i < pyramid.size(); i++) 
        Mat lowpass1 = pyramid[i][0], lowpass2 = pyramid[i][0];
        for (int j = 1; j < pyramid[i].size(); j++)        
            lowpass1 = (-high_b1 * lowpass1 + high_a0 * pyramid[i][j] + high_a1 * pyramid[i][j-1]) / high_b0;
            lowpass2 = (-low_b1 * lowpass2 + low_a0 * pyramid[i][j] + low_a1 * pyramid[i][j - 1]) / low_b0;

            filtered[i].push_back(lowpass1 - lowpass2);
        
    


源视频; http://techslides.com/demos/sample-videos/small.mp4

【问题讨论】:

所有高低帧prev_frame变量的类型是什么?如果原始类型或大小不合适,OpenCV 运算符可以轻松地用另一种类型的矩阵覆盖输出变量。作为一个快速的技巧,你可以在计算低通 1 和低通 2 之后做 lowpass1.convertTo(lowpass1, CV_32F); lowpass2.convertTo(lowpass2, CV_32F); 问题可能是你实际上并没有转换lowpass1lowpass2——你有lowpass1.convertTo(filtre, CV_32F);lowpass2 一样的东西......所以你写了转换后的数组到您在以后的语句中不使用的其他矩阵。 你如何测试掩码的值? imshow 将缩放和裁剪值以进行渲染。 imwrite 也不能很好地工作,因为图像类型通常不知道负值。您可能需要以不同的方式进行测试。如果您想保存和加载 32F 或 64F 矩阵(或任何其他非图像类型),您可以使用将矩阵保存为某种 xml 文件中的字符串的 Serializer 函数(抱歉,不记得名称 atm)。跨度> @DanMašek,从我的母语翻译时出错,在原始代码中是lowpass1.convertTo(lowpass1, CV_32F);,很抱歉造成混淆。 @Micka,我同时使用 imshow 并将值输出到 txt 文件。计算后尝试了转换破解,但得到了相同的结果。尽管在 imshow 和 txt 文件中,lowpass1 和 lowpass2 确实有效。 【参考方案1】:

问题出在这部分;

        lowpass1 = (-high_b1 * lowpass1 + high_a0 * pyramid[i][j] + high_a1 * pyramid[i][j-1]) / high_b0;
        lowpass2 = (-low_b1 * lowpass2 + low_a0 * pyramid[i][j] + low_a1 * pyramid[i][j - 1]) / low_b0;

由于在公式和代码的左侧使用了 lowpass1 和 lowpass2,它搞砸了一些东西,但我不确定它到底做了什么。改成这个后;

            Mat lowpass1_, lowpass2_;
            lowpass1_ = ( high_b1 * lowpass1 + high_a0 * pyramid[i][j] + high_a1 * pyramid[i][j - 1]);
            lowpass2_ = ( low_b1 * lowpass2 + low_a0 * pyramid[i][j] + low_a1 * pyramid[i][j - 1]) ;
            

计算是正确的,没有归零等。感谢所有回复。

【讨论】:

以上是关于减去两个 CV_64FC3 矩阵并保持负值的主要内容,如果未能解决你的问题,请参考以下文章

在 OpenCV 中分配矩阵元素

[0] CV Notes - 琐碎

基于黑白蒙版组合两个图像

如何访问 CV_32F/CV_64F Mat 的像素值?

访问 OpenCV 中的每个单独通道

合并两个 cv::Mat 而不复制数据