C++ 不必在OpenCV中复制Mat的最佳方法
我有一段代码,基本上是在两帧上进行“哑”背景减法C++ 不必在OpenCV中复制Mat的最佳方法,c++,opencv,C++,Opencv,我有一段代码,基本上是在两帧上进行“哑”背景减法 void FrameDifferenceBGS::operator()(cv::InputArray _image, cv::OutputArray _fgmask, double learningRate) { cv::Mat img_input = _image.getMat(); if(img_input.empty()) return; _fgmask.create(img_input.size(), CV_8U)
void FrameDifferenceBGS::operator()(cv::InputArray _image, cv::OutputArray _fgmask, double learningRate)
{
cv::Mat img_input = _image.getMat();
if(img_input.empty())
return;
_fgmask.create(img_input.size(), CV_8U);
cv::Mat img_foreground = _fgmask.getMat();
if(img_input_prev.empty())
{
img_input.copyTo(img_input_prev);
return;
}
cv::absdiff(img_input_prev, img_input, img_foreground);
if(img_foreground.channels() == 3)
cv::cvtColor(img_foreground, img_foreground, CV_BGR2GRAY);
if(enableThreshold)
cv::threshold(img_foreground, img_foreground, threshold, 255, cv::THRESH_BINARY);
if(showOutput)
cv::imshow("Frame Difference", img_foreground);
img_input.copyTo(img_input_prev);
img_foreground.copyTo(_fgmask);
firstTime = false;
}
如果我最后不添加img\u foreground.copyTo(\u fgmask)
,则输出数组不会使用img\u foreground
的结果进行更新,从而在调用时生成黑色图像
我做错了什么/应该在这里做什么?我再次检查了您的代码。看起来您正在为_fgmask创建新对象
_fgmask.create(img_input.size(), CV_8U);
我想这就是为什么你有这个问题。因为参数中的引用与此语句后面的引用不同。为什么不在调用函数之前先调用该行。fix
- 更改fgmask.create(img\u input.size(),CV\u 8U)代码>到
或\u fgmask.create(img\u input.size(),CV\u 8UC3)
\u fgmask.create(img\u input.size(),img\u input.type())代码>
- 这是因为
cv::absdiff(img_input_prev,img_input,img_front)代码>每次在内部重新创建一个新数组。它确实会更新img_前台结构,但在分配之后,_fgmask中的内存地址数据无法更改,因为头是按值传递的。
- 通过执行
cv::Mat&img_foreground=\u fgmask.getMatRef(),似乎可以解决这个问题(但仍然会产生创建成本)代码>
- 通过执行
- 这是因为CV_8U与CV_8UC3不同,因此Mat.hpp中的check@Mat::create()总是由于类型差异而分配新数组
#include "opencv2/opencv.hpp"
using namespace cv;
class FrameDifferenceBGS
{
public:
Mat prev;
Mat diff;
bool enableThreshold;
bool showOutput;
bool firstTime;
uchar threshold;
FrameDifferenceBGS():firstTime(false),enableThreshold(false),showOutput(false),threshold(0)
{
}
void FrameDifferenceBGS::operator()(cv::Mat& _in, cv::Mat &_fg, double _lr)
{
if(_in.empty())
return;
if(prev.empty())
{
prev=_in.clone();
_fg=cv::Mat::zeros(_in.size(),CV_8UC1);
return;
}
cv::absdiff(prev, _in, diff);
if(diff.channels() == 3)
cv::cvtColor(diff, _fg, CV_BGR2GRAY);
else
_fg=diff;
if(enableThreshold)
cv::threshold(_fg, _fg, threshold, 255, cv::THRESH_BINARY);
if(showOutput)
cv::imshow("Frame Difference", _fg);
prev=_in.clone();
firstTime = false;
}
};
int main()
{
VideoCapture cap(0);
FrameDifferenceBGS bgs;
Mat frame,fg;
for(;;)
{
cap >> frame;
bgs(frame,fg,0);
imshow("frame", frame);
imshow("fg", fg);
if(waitKey(1) ==27) exit(0);
}
return 0;
}
编辑2(修改后的原件)
\u fgmask
不是类的成员,只是参数。此外,添加这些参数并没有解决它:(我想这更多的是与OpenCV内部的一些模糊的事情有关,而不是由C++本身构造的。OpenCV的结构应该是按值传递的,它们都是轻量级结构(如CV:VEC)或动态存储器的头(如CV::MAT)。。所以原始原型是好的。@changelog在这种情况下,我认为参数已经被引用了。我再次检查了您的代码并更新了我的答案。您是否为调用方的\u fgmask
的实际参数分配了内存?我不尝试向OpenCV提交补丁,而只是尝试使用加载的subt修改库使用与OpenCV相同的算法。当您调用BackgroundSubtractor.operator()
时,该方法签名不起作用。我已经尝试了您的建议(通过将传入.type()
)但是,当它将颜色转换为黑白时,它必须再次复制它,因为在另一面我得到了一个彩色图像。原因与在absdiff中发生新分配的原因完全相同。cvtcolor dst类型是8UC1,而给定类型是8UC3,因此opencv分配了新内存。而在absdiff的情况下,fgmask没有更新。您可以避免复制由cv::cvtColor(img_前台,\u fgmask,cv_bgr2灰色);
编写的语句,但由于无论如何都会发生分配/复制,因此它的效率并不更高。我已经用一个完整的程序编辑了答案,该程序坚持原始结构,并尽可能重复使用(或者我可以弄清楚)我只是想回答为什么它不工作,而不是什么是最节省内存的方法(可能不需要关心,因为这不太可能是最大的瓶颈)。使用mat可以帮助您忘记这一点,因为它看起来更自动化。无论如何,更新的程序每周期的分配应该更少。@ZawLin,absDiff不适用于BGR mat图像,而只适用于灰度图像,对吗?这是因为,当我对彩色图像进行absDiff时,返回的都是空白图像,而如果我对其进行灰度化处理,则返回的都是空白图像首先是absDiff,我可以看到一个不同点,但是有很多信息丢失。@ZawLin,这是因为,在转换为灰度之前,你把absDiff放在第一位,而你不是唯一的一个。当我这样做的时候,即使我从相机的另一端挥手,如果给我一个空白图像,但是,如果我先将其灰度化,就不会发生这样的问题,因此我只想再确认一下(:
#include "opencv2/opencv.hpp"
class FrameDifferenceBGS
{
public:
cv::Mat img_input_prev;
cv::Mat diff;
cv::Mat img_foreground;//put this in class in stead of inside the function
bool enableThreshold;
bool showOutput;
bool firstTime;
uchar threshold;
FrameDifferenceBGS():firstTime(false),enableThreshold(false),showOutput(false),threshold(0)
{
}
void FrameDifferenceBGS::operator()(cv::InputArray _image, cv::OutputArray _fgmask, double learningRate)
{
cv::Mat img_input = _image.getMat();
if(img_input.empty())
return;
if(_fgmask.empty())
_fgmask.create(img_input.size(), CV_8UC1);
if(img_input_prev.empty())
{
img_input.copyTo(img_input_prev);
return;
}
cv::absdiff(img_input_prev, img_input, img_foreground);
if(img_foreground.channels() == 3)
cv::cvtColor(img_foreground, _fgmask, CV_BGR2GRAY);
if(enableThreshold)
cv::threshold(img_foreground, img_foreground, threshold, 255, cv::THRESH_BINARY);
if(showOutput)
cv::imshow("Frame Difference", img_foreground);
img_input.copyTo(img_input_prev);
//img_foreground.copyTo(_fgmask);
firstTime = false;
}
};
int main()
{
cv::VideoCapture cap(0);
FrameDifferenceBGS bgs;
cv::Mat frame,fg;
for(;;)
{
cap >> frame;
bgs(frame,fg,0);
cv::imshow("frame", frame);
cv::imshow("fg", fg);
if(cv::waitKey(1) ==27) exit(0);
}
return 0;
}