SQlite3 二進位制資料(BLOB)的存取【基於wxSqlite3的封裝的操作】
阿新 • • 發佈:2019-01-22
問題描述:
由於需要,把一張圖片載入到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, branch_id, color, feature, image, keypoints) values ('%s', %d, %d, ?, ?, ?)", feature.GetName(), 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, branch_id, 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* 就行了,但是如果某些情況,比如物件的成員變數裡面有指標指向了另外的地址上的資料,那麼這樣行不通,強轉後的這些指標因為環境變化而無效,如果操作程式會崩潰掉的。