有关OpenCV中内存消耗的问题

问题描述 投票:-2回答:1

交叉帖子here


我有这么简单的代码:

//OpenCV 3.3.1 project
#include<opencv.hpp>
#include<iostream>
using namespace std;
using namespace cv;


Mat removeBlackEdge(Mat img) {
    if (img.channels() != 1)
        cvtColor(img, img, COLOR_BGR2GRAY);
    Mat bin, whiteEdge;
    threshold(img, bin, 0, 255, THRESH_BINARY_INV + THRESH_OTSU);
    rectangle(bin, Rect(0, 0, bin.cols, bin.rows), Scalar(255));
    whiteEdge = bin.clone();
    floodFill(bin, Point(0, 0), Scalar(0));
    dilate(whiteEdge - bin, whiteEdge, Mat());
    return whiteEdge + img;
}

int main() {               //13.0M
    Mat emptyImg = imread("test.png", 0);   //14.7M
    emptyImg = removeBlackEdge(emptyImg);   //33.0M
    waitKey();
    return 0;
}

这是我的test.png。如Windows所示,它的大小约为1.14M。当然我知道在内存中读取时会更大。但是我无法接受内存的巨大消耗。如果使用F10,我可以知道project.exe占用了13.0M。我无话可说当我运行Mat emptyImg = imread("test.png", 0);时,内存消耗为14.7M。这也是正常的。但是为什么当我运行emptyImg = removeBlackEdge(emptyImg);时,内存消耗却高达33.0M?这意味着功能removeBlackEdge占用了我额外的18.3M内存。有人可以告诉我一件事吗?我不能完全接受这么大的内存消耗。我有什么想念的吗?我使用新的emptyImg代替旧的emptyImg。我认为我不需要额外的内存来花钱吗?

Ps:如果我想让工具通过removeBlackEdge(删除图像中的黑色边缘)执行相同的操作,如何调整它以最小化内存消耗?]

c++ opencv memory memory-management
1个回答
1
投票

以下-猜测和提示。这是一个很大的评论。请不要将其标记为答案,即使它有助于发现/解决问题。相反,如果您设法找到原因,请改写自己的答案。我可能稍后再删除。


[很可能,像threshold(img, bin, ..)这样的图像/位图操作会创建原始图像的副本。很难说出多少,但是它肯定会创建至少一个,如第二个bin变量所示。然后是clonewhiteEdge,因此是第三个副本。同样,whiteEdge - bin之类的操作也可能会创建至少一个副本(结果)。 rectangledilatefloodfill可能就地工作,并使用作为工作区传递并同时输出的Mat变量之一。

这意味着您至少有4张图像的副本,可能还有更多,但是我们说它是4 * 1.7mb,所以您观察到的19mb增加了约7mb。

所以,我们有12mb的解释,或者看来。

首先,图像操作可能会分配一些额外的缓冲区,并保留以备后用。这会花费内存,但是会“摊销”,因此,如果再次执行这些操作,则不会再次花费您的时间。

也有可能在调用这些图像操作时,这一事实可能导致某些动态库被加载。这也是一次性操作,即使再次使用也不会浪费您的内存。

接下来要注意的是,Windows通过简单工具报告的过程和内存使用情况是不正确的。随着进程分配和释放内存,Windows报告的内存消耗通常只会增加,而在释放后不会减少。至少不是立即。该过程可以保留一些“空气”或“真空”。造成这种情况的原因很多,但要点是,当程序再次尝试分配内存时,这种“空气”通常是可重用的。 Windows可能会显示一个基本内存使用率为30mb的进程,该进程会定期分配20mb并释放20mb,这通常会占用50mb。

话虽如此,我不得不对您的记忆测量方法或至少从观察得出的结论表示怀疑。单次运行removeBlackEdge后,您不能说它消耗了那部分内存。您所观察到的只是该进程的工作内存池增长了那么多。这本身并不能告诉您太多。

如果您怀疑图像的临时副本占用了太多空间,请尝试将其清除。这可能意味着显而易见的事情,例如编写代码以使用较少的Mat变量和临时变量,或者重用/重新分配不再需要的位图,而只是等到函数结束,或者看起来不太明显的事情,例如选择不同的算法或编写自己的代码。您也可以尝试通过以下方法来确认这种情况:使用不同大小的输入图像多次运行该程序,然后绘制观察内存-vs-输入图表尺寸。如果它线性增长(即“内存消耗”始终是输入大小的10倍),则可能确实是一些副本。

另一方面,如果怀疑内存泄漏,请多次运行该removeBlackEdge。数百或数千或数百万次。观察“内存消耗”是否随着时间的推移稳定增长。如果是这样,则可能存在泄漏。另一方面,如果它在开始时仅增长一次,然后保持稳定在同一水平,则可能没有什么不对,只有一次一次性初始化或摊销的缓存/工作区/等。

我建议您现在做这两个测试。进一步的工作和分析取决于您在长期内所观察到的情况。另外,我必须注意,这段代码相对简短。您不是太早优化了吗?旁注-确保在编译器中打开适当的优化(速度/内存)。如果您碰巧使用并观察到“调试”版本,则可以立即取消对速度/内存的观察。

© www.soinside.com 2019 - 2024. All rights reserved.