问题描述:
由于需要,把一张图片加载到cv::Mat对象中(image),并调用算法计算出一个特征向量(feature) 和一些关键点(std::vector<cv::KeyPoint> keyPoints ),把以上这三个对象写入sqlite3数据库(BLOB类型), 之后如果需要又重新从数据库中读出来,并还原成对应的类型。
cv::Mat image = imread(...); cv::Mat feature = algorithm.Calc(...); std::vector<cv::KeyPoint> ps = algorithm.Calc(...);
部分代码:
1. 写入:
int MTemplateFactory::New(const MTemplateFeature& feature) { MDatabaseArchive* archive = MDatabaseManager::getSingleton().GetArchive(); if (archive) { MString sql; if (feature.GetColor() <= 0) sql = MString::Format("insert into tabTemplate (name,branch_id,feature,image,keypoints) values ('%s',%d,?,?)",feature.GetName(),feature.GetBranchId()); else sql = MString::Format("insert into tabTemplate (name,color,feature.GetBranchId(),feature.GetColor()); try { //- Prepare data cv::Mat img = feature.GetImage(); cv::Mat f = feature.GetFeature(); std::vector<cv::KeyPoint> ps = feature.GetKeyPoints(); //- Image. wxMemoryBuffer buf1; buf1.AppendData(&img.rows,4); int type1 = img.type(); buf1.AppendData(&img.cols,4); buf1.AppendData(&type1,4); buf1.AppendData(img.data,img.cols * img.rows * img.elemSize()); //- Feature. wxMemoryBuffer buf2; buf2.AppendData(&f.rows,4); int type2 = img.type(); buf2.AppendData(&f.cols,4); buf2.AppendData(&type2,4); buf2.AppendData(f.data,f.cols * f.rows * f.eleSize()); //- keyPoints. wxMemoryBuffer buf3; int eleSize = sizeof(cv::KeyPoint); for (auto point : ps) { buf3.AppendData(&point,eleSize); } wxsqlite3Statement stmt = archive->PrepareStatement(sql); stmt.Bind(1,buf2); stmt.Bind(2,buf1); stmt.Bind(3,buf3); if (stmt.ExecuteUpdate() > 0) return archive->ExecuteScalar("select MAX(id) from tabTemplate"); } catch (wxsqlite3Exception& e) { throw MException(e.GetMessage()); } catch (cv::Exception& e) { throw MException(e.msg); } } return 0; }<span style="font-weight: bold;"> </span>
2. 读取:
MTemplateArray MTemplateFactory::List() { MTemplateArray arr; MDatabaseArchive* archive = MDatabaseManager::getSingleton().GetArchive(); if (archive) { try { wxsqlite3ResultSet res = archive->ExecuteQuery("select * from tabTemplate"); while (res.NextRow()) { int id = res.GetInt(0); MString name = res.GetString(1); int branch_id = res.GetInt(2); int colorId = res.GetInt(3); wxMemoryBuffer f1,f2,f3; res.GetBlob("image",f1); res.GetBlob("feature",f2); res.GetBlob("keypoints",f3); if (f1.IsEmpty() == true || f2.IsEmpty() == true || f3.IsEmpty() == true) { throw MException(_("特征模版保存数据为空,无法创建对象!")); } cv::Mat&& img = MakeMat(f1); cv::Mat&& feature = MakeMat(f2); //- Parse keypoints data. uchar* data = (uchar*)f3.GetData(); int len = f3.GetDataLen(); std::vector<cv::KeyPoint> ps; int eleSize = sizeof(cv::KeyPoint); int cnt = len / eleSize; for (int i = 0; i < cnt; i++) { cv::KeyPoint* p = (cv::KeyPoint*)(data + i * eleSize); ps.push_back(cv::KeyPoint(*p)); } //- Construct template feature object... arr.push_back(MTemplateFeature(id,name,colorId,img.clone(),feature.clone(),ps)); } } catch (cv::Exception &e) { throw MException(e.msg); } catch (wxsqlite3Exception& e) { throw MException(e.GetMessage()); } } return arr; } cv::Mat MTemplateFactory::MakeMat(const wxMemoryBuffer& buf ) { assert(buf.IsEmpty() == false); void* data = buf.GetData(); int len = buf.GetDataLen(); int* p = (int*)data; int h = p[0]; int w = p[1]; <span style="white-space:pre"> </span>int _type = p[2]; int offset = sizeof(int) * 3; return cv::Mat(h,w,_type,(uchar*)data + offset); }
说明几点注意的地方:
sqlite3 存取二进制的文章很多,不记录了。wxsqlite3对blob的操作在wxsqlite3Statement类中,有类似绑定数据(Bind...)和提交操作(ExecuteUpdate...)的函数,具体查看相应的文档或者看源码有注释的。关于KeyPoint的保存,由于cv::KeyPoint这个类的所有数据成员全部都是实体数据,比如int,float 这样的数据,那么直接可以把每个cv::KeyPoint对象的地址加上长度( int len = sizeof(cv::KeyPoint) )存入即可,读数据库还原的时候只需 获取当该片内存,强制转换成 cv::KeyPoint* 就行了,但是如果某些情况,比如对象的成员变量里面有指针指向了另外的地址上的数据,那么这样行不通,强转后的这些指针因为环境变化而无效,如果操作程序会崩溃掉的。
原文链接:https://www.f2er.com/sqlite/199670.html