首先让我们熟悉两个概念:
1 浅拷贝
复制指针时只复制指针指向内存的地址 而不复制指针指向内存的内容
举例:
Mat a1;
Mat a2=a1;
则无论修改 a1还是a2的内容 a1 a2都会被一同修改 因为他们指向同一块内存
举例:
当一个容器 vector<Mat> v 有新的 元素被 push_back
则容器中的 所有元素 都会被更新成最后一个元素的内容 因为始终只有一个Mat对象 被引用了多次
他们都指向了相同的内存区域
2 引用计数
引用计数是一种内存管理技术,用于追踪每个对象(或内存块)被引用的次数。当一个对象的引用次数降至零时,表明没有任何引用指向该对象,此时可以安全地回收其占用的内存。
3写时复制
无法触发写实复制的情况:
当你将 frame 添加到如 vector<Mat> v 容器中时,如果仅使用赋值(v.push_back(frame);),实际上并没有修改数据,只是增加了一个新的引用。这时,写时复制不会触发,因为没有进行修改操作。容器中的每个元素仍然只是指向同一个数据块的引用。如果 frame 在循环的下一次迭代中被 vc.read() 修改,这些修改会影响到容器中所有之前添加的 Mat 对象,因为它们都共享同一数据块。
可以触发写时复制的情况:
如果你尝试修改 B 的内容(如 B.at<int>(0,0) = 255;),并且 A 和 B 是数据共享的,这时候写时复制会触发,B 会先复制一份数据进行修改,保证 A 的数据不被更改。
所以当如下代码被执行:
不会触发写时复制 也就是说不会单独拷贝对象的副本
容器中所有的元素都是同一个Mat对象指向最后一帧的内容
引用计数器的大小就是v.size
#include <QCoreApplication>
#include <QDebug>
#include <QDir>
#include <QStringList>
#include <opencv.hpp>
#include <vector>
using namespace cv;
using namespace std;
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
VideoCapture vc;
vc.open("C:/Users/wang/Desktop/ft.mp4");
Mat frame;
vector<Mat> v;
while (1) {
vc.read(frame);
if (frame.empty()) {
break;
}
v.push_back(frame);
}
qDebug() << v.size() << "-----------";
for (int var = 0; var < v.size(); ++var) {
imshow("frame", v[var]);
waitKey(1);
}
// Mat frame;
// while (1) {
// vc.read(frame);
// if (frame.empty()) {
// break;
// }
// imshow("frame", frame);
// waitKey(1);
// }
vc.release();
destroyAllWindows();
return a.exec();
}
解决方案:
v.push_back(frame.clone()); 在压入容器时 明确指出压入的是一个副本 而不是引用
为什么在读取后立即显示不会出现这种情况?
大概是因为读取后 立即显示 没有做复制多个对象的操作 frame只是一个临时中转变量
#include <QCoreApplication>
#include <QDebug>
#include <QDir>
#include <QStringList>
#include <opencv.hpp>
#include <vector>
using namespace cv;
using namespace std;
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
VideoCapture vc;
vc.open("C:/Users/wang/Desktop/ft.mp4");
// Mat frame;
// vector<Mat> v;
// while (1) {
// vc.read(frame);
// if (frame.empty()) {
// break;
// }
// v.push_back(frame.clone());
// }
// qDebug() << v.size() << "-----------";
// for (int var = 0; var < v.size(); ++var) {
// imshow("frame", v[var]);
// waitKey(1);
// }
Mat frame;
while (1) {
//读取
vc.read(frame);
if (frame.empty()) {
break;
}
//立即显示
imshow("frame", frame);
waitKey(1);
}
vc.release();
destroyAllWindows();
return a.exec();
}