メインコンテンツまでスキップ
バージョン: Next

非GenDCバイナリデータの解析

このチュートリアルでは、バイナリ形式の画像データを解析する方法について学びます。

前提条件

  • Modern json (このチュートリアルに含まれています)

チュートリアル

前のチュートリアル では、画像データをバイナリファイルに保存する方法を学びました。今回は、データをロードし、コンテナを解析し、ディスクリプターからセンサーに関する情報を取得します。

バイナリファイルの構造

前のチュートリアルで保存されたバイナリデータの構造は次の通りです:

バイト数内容
4フレーム数
w * h * d * c画像
4フレーム数
w * h * d * c4
4フレーム数
......
w * h * d * c画像

フレーム数は4バイトの長さであり、画像データのサイズは幅 高さ バイト深度 * チャンネル数です。

幅と高さの値は、前のチュートリアルでパイプラインによって保存されたバイナリファイル内の prefix-config.json に記載されています。

バイト深度とチャンネル数は、 prefix-config.json で指定された pfnc_pixelformat から計算できます。

バイナリファイルとともに設定ファイルは tutorial_save_image_bin_XXXXXXXXXXXXXXXXXX の下に保存され、プレフィックスは image0- です。

std::string directory_name = "tutorial_save_image_bin_XXXXXXXXXXXXXXXXXX";

std::ifstream f(std::filesystem::path(directory_name) / std::filesystem::path(prefix+"config.json"));
nlohmann::json config = nlohmann::json::parse(f);

int32_t w = config["width"];
int32_t h = config["height"];
int32_t d = <PixelFormatで計算されたバイト深度 config["pfnc_pixelformat"]>; 例: Mono12の場合は2
int32_t c = <PixelFormatで計算されたカラーチャンネル config["pfnc_pixelformat"]>; 例: Mono12の場合は1
int32_t framesize = w * h * d * c;

このチュートリアルの最後に紹介されたソースコードには、 config["pfnc_pixelformat"] の値を取り、バイト深度とカラーチャンネルを返す getByteDepth および getNumChannel 関数が提供されています。

バイナリファイルの検索

前のチュートリアルで保存されたバイナリファイルを使用する場合、ディレクトリの名前は tutorial_save_image_bin_XXXXXXXXXXXXXXXXXX であり、バイナリファイルのプレフィックスは image0- です。

std::string directory_name = "tutorial_save_image_bin_XXXXXXXXXXXXXXXXXX";
std::string prefix = "image0-";

次のスニペットでは、指定されたプレフィックスで始まるすべてのバイナリファイルをディレクトリから取得し、記録された順序に並べ替えます。

std::vector<std::string> bin_files;
for (const auto& entry : std::filesystem::directory_iterator(directory_name)) {
if (entry.path().filename().string().find(prefix) == 0 && entry.is_regular_file() && entry.path().extension() == ".bin") {
bin_files.push_back(entry.path().filename().string());
}
}

// バイナリファイルを sensor0-0.bin, sensor0-1.bin, sensor0-2.bin... のように並べ替えます
std::sort(bin_files.begin(), bin_files.end(), [](const std::string& a, const std::string& b) {
return extractNumber(a) < extractNumber(b);
});

これで、bin_files 内のすべての順序付けされたバイナリファイルをループで処理します。

for (const auto& filename : bin_files){

}

バイナリファイルのオープンと読み込み

ループ内で、対象の(単一の)バイナリファイルは filename です。このバイナリファイルを ifstream で開きます。

std::filesystem::path jth_bin= std::filesystem::path(directory_name) / std::filesystem::path(filename);
std::ifstream ifs(jth_bin, std::ios::binary);

バイナリファイル全体のサイズを取得するために、 ifstream をファイルの末尾に設定して、現在の位置とファイルの先頭との距離がファイルサイズに等しくなるようにします。

ifs.seekg(0, std::ios::end);
std::streampos filesize = ifs.tellg();

ファイルの内容を読み込むために ifstream をファイルの先頭に戻すことを忘れないでください。

ifs.seekg(0, std::ios::beg);
char* filecontent = new char[filesize];

バイナリファイルの解析

最後に、各バイナリファイルを解析します。

フレーム数のサイズが4バイトであるため、ファイルの内容から4バイトのデータをframecountにコピーします。

int cursor = 0;
while(cursor < static_cast<int>(filesize)){
int framecount = *reinterpret_cast<int*>(filecontent cursor);
std::cout << framecount << std::endl;
...
}

画像データを保存するための cv::Mat オブジェクトを準備します。これには高さ、幅、および cv::Mat::type が必要です。cv::Mat::type はまた、 PixelFormat から取得することができます。

PixelFormatcv::Mat::type
Mono8CV_8UC1
Mono12CV_16UC1
RGB8CV_8UC3
cv::Mat img(h, w, getOpenCVMatType(d, c));

画像データはフレーム数の後に続きますので、オフセットは +4 で、データサイズは 幅 * 高さ * バイト深度 * 色チャンネル数 です。

while(cursor < static_cast<int>(filesize)){
...
cv::Mat img(h, w, getOpenCVMatType(d, c));
std::memcpy(img.ptr(), filecontent cursor 4, framesize);
  ...
cv::imshow("First available image component", img);

cv::waitKeyEx(1);
}

OpenCVのimshowを使って、読み取った画像データを表示することができます。

次のフレーム数と画像データに移動するために、カーソルをシフトすることを忘れないでください。

cursor += 4 framesize;

完全なコード

このチュートリアルで使用される完全なコードはこちらです。