制作一个 Mat 类型的对象数组。输出窗口显示相同的帧

Posted

技术标签:

【中文标题】制作一个 Mat 类型的对象数组。输出窗口显示相同的帧【英文标题】:Making an array of Mat type objects. The output window shows the same frame 【发布时间】:2015-05-03 09:20:08 【问题描述】:

以下是我的代码。它有一组 Mat 类型的对象。我将在 for 循环中制作的 Mat 添加为 imgArr[index] = img.但是当我输出所有帧以查看窗口上的动画时,它只显示最后一帧并显示同一帧。

namedWindow( "Display window", WINDOW_AUTOSIZE );// Create a window for display.

int numFrames = endFrame - startFrame;          // Total number of frames
Mat imgArr[100];

for(long int FrameNumber = startFrame; FrameNumber < endFrame; FrameNumber++)

    fp.seekg( BytesPerFrame*(FrameNumber), std::ios::beg);
    char buffer[BytesPerImage];

    fp.read(buffer, BytesPerImage);
    short image[512*512];

    short min = 20000, max=1000;


    for ( int i = 0; i < BytesPerImage; i=i+2 )
    
        int a;
        a = floor(i/2)+1;
        //  take first character
        image[a] = (static_cast<unsigned int>(static_cast<unsigned char>(buffer[i+1]))*256+static_cast<unsigned int>(static_cast<unsigned char>(buffer[i])));
        if(image[a] < min)
            min = image[a];
        
        if(image[a] > max)
            max = image[a];
        

    

    // Processing the image
    Mat img(512, 512, CV_16S, image);
    img -= (min);
    img *= (32767/max); // (330000/2500);
    img *= ((max/min)/2) + 2;    // 16;
    imgArr[FrameNumber-startFrame] = img;


for(int i = 0; i<numFrames; i++)
    imshow( "Display window", imgArr[i]);                   // Show our image inside it.
    waitKey(50);

【问题讨论】:

你试过'Mat::copyTo()'吗? 您的问题是您没有复制图像,而只是将 mat 链接到“image”的(固定)内存地址,这对于每个 mat 都是相同的。试试 imgArr[FrameNumber-startFrame] = img.clone();而是为每个垫子深度复制图像内存。或使用标志复制menory wgen 创建img 【参考方案1】:

您的代码有很多地方没有做好。我将尝试列出它们:

namedWindow( "Display window", WINDOW_AUTOSIZE );// Create a window for display.

int numFrames = endFrame - startFrame;          // Total number of frames
Mat imgArr[100];

第一个问题:如果你的帧数numFrames 大于100 怎么办?这样会更安全:

std::vector<Mat> imgVector;
imgVector.reserve(numFrames);

然后在每个新帧你push_back 一个图像。让我们继续吧。

for(long int FrameNumber = startFrame; FrameNumber < endFrame; FrameNumber++)

    fp.seekg( BytesPerFrame*(FrameNumber), std::ios::beg); //Hmmm, when did you compute BytesPerFrame?
    char buffer[BytesPerImage]; //This is actually not C++, you probably even got a warning

您应该将char buffer[BytesPerImage] 替换为char* buffer = new char[BytesPerImage];。您还应该在循环之前预分配这个中间缓冲区,这样您就必须只分配一次并多次使用它。然后,在循环之后,你释放它:delete[] buffer;

    fp.read(buffer, BytesPerImage); //This seems fine
    short image[512*512]; //What's this?

512 是什么?我可以理解稍后查看您的代码,但您应该在某个地方定义如下:

const int MYWIDTH = 512;
const int MYHEIGHT = 512;
const int BYTES_PER_IMAGE = MYWIDTH * MYHEIGHT * 2; //Maybe also the 2 should be a constant named `BYTES_PER_PIXEL`

此外,在这种情况下,让我们使用short* image = new short[MYWIDTH*MYHEIGHT]; 动态分配您的数据。但是,这不会正常工作:不幸的是,如果您从外部缓冲区构造Mat,则不会自动管理释放。最好反过来:创建您的Mat,然后将其用作您的缓冲区。它看起来像这样:

    Mat img(MYHEIGHT, MYWIDTH, CV_16S); //
    short* image = static_cast<short*> img.ptr();

进一步操作的一个问题是可能存在“填充字节”。 512x512 图像不太可能,但谁知道呢。请断言以下将是正确的(请参阅doc):

    (img.cols == img.step1() )

然后:

    short min = 20000, max=1000; 

为什么不max=0?此外,min 可以初始化为 32767,或者更优雅地初始化为 std::numeric_limits&lt;short&gt;::max() (#include &lt;limits.h&gt;)

    for ( int i = 0; i < BytesPerImage; i=i+2 )
    
        int a;
        a = floor(i/2)+1;
        //  take first character
        image[a] = (static_cast<unsigned int>(static_cast<unsigned char>(buffer[i+1]))*256+static_cast<unsigned int>(static_cast<unsigned char>(buffer[i])));
        if(image[a] < min)
            min = image[a];
        
        if(image[a] > max)
            max = image[a];
        

    

我的理解是:您的输入缓冲区是以大端表示的 16 位图像(最重要的字节在最不重要的字节之前)。我看到的问题:

如果最高有效字节超过 127 怎么办?那么你的输出值就会溢出,如128*256=32768 &gt; 32767floor(i/2)floor 不是必需的:当你除一个整数值时,它总是返回结果的整数部分。此外,鉴于您的 for 循环的定义,i 始终是偶数(您增加 2),因此 floor 操作是两次不必要的int a; a = floor(i/2)+1; 删除+1:想想0 索引像素,您会立即看到您将值分配给错误的像素。对于最后一个像素,您实际上会遇到分段错误。你的指令变成:const int a = i/2;(Ehi,多么简单!:)) image[a] = [...];:你正在做的一些演员阵容实际上是必要的,尤其是unsigned char的演员阵容。不过,我想知道,您为什么不首先将buffer 读作unsigned char 的缓冲区。所有unsigned int 转换都可以省略,因为最低有效字节不需要它,而使用整数值256 已经可以提升像素数据

客串:minmax 更新函数可以写成:

min = std::min(min,image[a]);
max = std::max(max,image[a]);

让我们继续吧:

    // Processing the image
    Mat img(512, 512, CV_16S, image); //Already done, now remove

Mat 的创建已经完成。

   img -= (min);
   img *= (32767/max); // (330000/2500);

好的,这有一个使用 opencv 库的更简单的等价物,我们稍后会谈到。这里有一个问题:这次你真的应该为你的部门使用float

   img *= (float(32767)/max);

顺便说一句,我认为在这种情况下,您希望max-min 成为分母:

   img *= (float(32767)/(max-min));

以下我不明白:

   img *= ((max/min)/2) + 2;    // 16;

往下看,

 imgArr[FrameNumber-startFrame] = img;

鉴于我上面建议的更改(图像的std::vector),这变成:

 imgVector.push_back(img); 

完成!


最后一点:在我看来,您正在尝试做的事情可以通过cv::normalize 获得。你可以这样做:

cv::normalize(img, img, 0, 32767, NORM_MINMAX, CV_16UC1);

【讨论】:

注意:This guide 与正确获取此答案的格式相关。【参考方案2】:

如果你像这样构造一个 cv::Mat:

short pixels[512*512];
Mat img(512, 512, CV_16S, pixels);

一旦离开范围,指向像素的指针将无效。

要么克隆你的垫子:

imgArr[FrameNumber-startFrame] = img.clone();

或预先分配它,并读入现有像素:

Mat img(512, 512, CV_16S);
fp.read(img.data, BytesPerImage);

另外:请将整个 for ( int i = 0; i &lt; BytesPerImage; i=i+2 )(恐怖!!)循环替换为:

double minVal,maxVal;
minMaxLoc(img, &minVal, &maxVal, 0,0);

【讨论】:

以上是关于制作一个 Mat 类型的对象数组。输出窗口显示相同的帧的主要内容,如果未能解决你的问题,请参考以下文章

无法同时显示五个 Mat 图像(C++、OpenCV)

OpenCV inRange 更改 Mat 类型

怎样将.mat文件生成图像显示

OpenCV源代码赏析: Mat对象step属性含义及使用深入分析

OpenCV源代码赏析: Mat对象step属性含义及使用深入分析

如何在设计师的“收藏编辑器窗口”中制作可编辑的对象?