c++ - 为什么使用 cv::Mat (opencv) 更改指向数据的指针对 cv::filter2D 有问题?
问题描述
我想迁移到使用 opencv 来获取自定义成像检测器数据。数据集可能非常庞大(有时有 100 多个数千帧),我使用 boost 内存映射加载它们。在测试行为时,我创建了一个适当大小的 cv::mat(对于单个帧)并将指针更改为我正在查看的帧的数据。这适用于显示数据 (cv::imshow) 或应用颜色图,但当我使用 cv::filter2D 之类的东西时会失败。如果我克隆数据或使用一些副本,它可以工作,但我不想开始复制/克隆,因为我认为这会降低性能(也许我错了)。
那么 - 我做错了什么?为什么 cv::filter2D 在这里不起作用,还有更好的方法吗?
运行时(现在使用 Windows 10)我在终端中得到以下信息:
OpenCV(4.0.1) Error: Assertion failed (data == datastart + ofs.y*step[0] + ofs.x*esz) in cv::Mat::locateROI, file c:\build\master_winpack-build-win64-vc15\opencv\modules\core\src\matrix.cpp, line 767
OpenCV: terminate handler is called! The last OpenCV error is:
OpenCV(4.0.1) Error: Assertion failed (data == datastart + ofs.y*step[0] + ofs.x*esz) in cv::Mat::locateROI, file c:\build\master_winpack-build-win64-vc15\opencv\modules\core\src\matrix.cpp, line 767
这只发生在 filter2D 与从内存映射数组构造的矩阵一起使用时。
我使用未注释的克隆版本运行程序,并将循环中的 std::cout 行替换为:
std::cout<<"Is it continuous: " << img.isContinuous() << std::endl;
并且 cv::Mat 确实是连续的。
#include <QCoreApplication>
#include<opencv2/opencv.hpp>
#include<opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/imgcodecs.hpp>
#include<iostream>
#include <boost/iostreams/device/mapped_file.hpp>
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
boost::iostreams::mapped_file_source mem_ifile;
mem_ifile.open(std::string("someimage.raw"));
std::cout<<"file size: "<<mem_ifile.size()/(396*266*sizeof (float))<<std::endl;
cv::Mat adjMap;
cv::Mat img(266,396,CV_32FC1);
cv::Mat falseColorsMap;
cv::Mat b_hist;
cv::Mat kernel;
cv::Mat filsMap;
kernel = cv::Mat::ones( 2, 2,CV_32FC1)/double(4.0);
cv::namedWindow("image", cv::WINDOW_NORMAL);
cv::namedWindow("false color", cv::WINDOW_NORMAL);
cv::namedWindow("filtered", cv::WINDOW_NORMAL);
for(unsigned long long i = 0;i< mem_ifile.size()/(396*266*sizeof (float)); i++)
{
img.data = (reinterpret_cast<uchar *>(const_cast<char *>(mem_ifile.data()))+(i*396*266*sizeof (float)));
cv::convertScaleAbs(img, adjMap, 255 / 500.0);
applyColorMap(adjMap, falseColorsMap, cv::COLORMAP_JET);
// PROBLEM HERE - FIRST TWO WORK, LAST ONE DOESN'T
//cv::filter2D(adjMap,filsMap,-1,kernel); // works
//cv::filter2D(img.clone(),filsMap,-1,kernel); // works
cv::filter2D(img,filsMap,-1,kernel); // doesn't work
std::cout<<"mean of adjmap: " << cv::mean(adjMap) << std::endl;
cv::imshow("image", adjMap);
cv::imshow("false color",falseColorsMap);
cv::imshow("filtered", filsMap);
cv::waitKey(20);
}
mem_ifile.close();
return a.exec();
}
基于 Beaker 建议的工作代码:
#include <QCoreApplication>
#include<opencv2/opencv.hpp>
#include<opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/imgcodecs.hpp>
#include<iostream>
#include <boost/iostreams/device/mapped_file.hpp>
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
boost::iostreams::mapped_file_source mem_ifile;
mem_ifile.open(std::string("/home/hugh/Documents/APS2017April/Ti7_nr2_e_x36000.raw"));
std::cout<<"file size: "<<mem_ifile.size()/(396*266*sizeof (float))<<std::endl;
cv::Mat adjMap;
cv::Mat *img;
cv::Mat falseColorsMap;
cv::Mat b_hist;
cv::Mat kernel;
cv::Mat filsMap;
kernel = cv::Mat::ones( 2, 2,CV_32FC1)/double(4.0);
cv::namedWindow("image", cv::WINDOW_NORMAL);
cv::namedWindow("false color", cv::WINDOW_NORMAL);
cv::namedWindow("filtered", cv::WINDOW_NORMAL);
for(unsigned long long i = 0;i< mem_ifile.size()/(396*266*sizeof (float)); i++)
{
//img.data = (reinterpret_cast<uchar *>(const_cast<char *>(mem_ifile.data()))+(i*396*266*sizeof (float)));
img = new cv::Mat(266,396,CV_32FC1,(reinterpret_cast<uchar *>(const_cast<char *>(mem_ifile.data()))+(i*396*266*sizeof (float))));
cv::convertScaleAbs(*img, adjMap, 255 / 500.0);
applyColorMap(adjMap, falseColorsMap, cv::COLORMAP_JET);
//cv::filter2D(adjMap,filsMap,-1,kernel); // works
//cv::filter2D(img->clone(),filsMap,-1,kernel); // works
cv::filter2D(*img,filsMap,-1,kernel); // works
std::cout<<"Is it continuous: " << img->isContinuous() << std::endl;
cv::imshow("image", adjMap);
cv::imshow("false color",falseColorsMap);
cv::imshow("filtered", filsMap);
cv::waitKey(20);
delete img;
}
mem_ifile.close();
return a.exec();
}
解决方案
所以看起来您正在修改该data
字段而不更新任何其他字段。(请参阅公共属性。)具体而言,dataend
和datalimit
属性datastart
被标识为
在locateROI和 adjustROI中使用的辅助字段
locateROI
是您的错误消息中给出的方法。
正确的方法是使用cv::Mat
data* 构造函数。如文档中所述,
采用数据和步骤参数的矩阵构造函数不分配矩阵数据。相反,它们只是初始化指向指定数据的矩阵头,这意味着没有数据被复制。此操作非常高效,可用于使用 OpenCV 函数处理外部数据。
这确保了正确创建所有标题数据,而无需进行不必要的复制。
另请注意,在使用这些构造函数时,您应该清理自己的数据:
外部数据不会自动释放,因此您应该注意它。
推荐阅读
- regression - 对二元结果变量使用 OLS 回归
- javafx - JavaFX - 在布局创建完成时收听
- flutter - SizedBox 哪个高度是屏幕大小或子项的最大值
- c# - 使用 HTTPClient 调用 REST API 时在 httpResponseMessage 中获取 null
- javascript - 当用户单击网站中的某些链接时,我如何启动 ElectronJs 桌面应用程序
- inno-setup - 如何抑制wizard.close的提示
- google-sheets-formula - 使用谷歌表格公式加入表格的列
- reactjs - 如何从 React 中的材料表中检索 selectedRows
- java - Google OR-Tools:在 IDE 中使用
- windows-10 - 对于 Windows 10 ANDROID_HOME 和 ANDROID_SDK_ROOT 环境变量均未导出