geopro/src/data/store/ChunkedVolumeStore.hpp

160 lines
6.4 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#ifndef GEOPRO_DATA_STORE_CHUNKEDVOLUMESTORE_HPP
#define GEOPRO_DATA_STORE_CHUNKEDVOLUMESTORE_HPP
#include <array>
#include <cstdint>
#include <string>
#include <utility>
#include <vector>
#include "model/ScalarVolumeI16.hpp" // geopro::core::Quant
namespace geopro::core {
struct BuiltI16; // src/core/algo/GprVolumeBuilder.hpp
}
namespace geopro::data {
// 分块存储的 sidecar 元数据meta.json 反序列化结果,不含逐块索引)。
struct StoreMeta {
int nx = 0, ny = 0, nz = 0;
int brick = 64;
std::array<double, 3> origin{{0, 0, 0}};
std::array<double, 3> spacing{{0, 0, 0}};
geopro::core::Quant quant; // scale/offset
double vminPhys = 0, vmaxPhys = 0;
};
// GPR 三维体的分块压缩落盘B/C 共用基座)。
// 格式dir/meta.json几何 + 量化 + 逐块索引)+ dir/data.bin逐块 qCompress 流)。
// 块布局与体一致(块内 i 最快、k 最慢);边缘块尺寸 < brick。
// 偏移/长度全程 64 位(块偏移可能 > 2GB
class ChunkedVolumeStore {
public:
// 落盘dir/meta.json + dir/data.bin。逐块 int16 → qCompress 压缩流,
// 块索引/偏移/压缩长度记入 meta.json。dir 不存在则创建。
static void write(const std::string& dir, const geopro::core::BuiltI16& b,
int brick = 64);
// 只读 meta.json不打开 data.bin
static StoreMeta readMeta(const std::string& dir);
// 读 meta + 打开 data.bin。
explicit ChunkedVolumeStore(const std::string& dir);
const StoreMeta& meta() const { return meta_; }
// --- level 0 兼容接口(语义不变,= 全分辨率级)---
int bricksX() const { return bricksX_; }
int bricksY() const { return bricksY_; }
int bricksZ() const { return bricksZ_; }
// 读单块 → 还原 int16 vector。返回长度 = bw*bh*bd内部块 = brick³边缘块更小
// == readBrick(0, bx, by, bz)。
std::vector<std::int16_t> readBrick(int bx, int by, int bz) const;
// --- 金字塔(多分辨率 LOD---
// 在已 write 的 store 目录上构建金字塔level 0=全分辨率(已存),
// level 1..levels 逐级 2× 降采样(维度 ceil(n/2)),故总级数 = levels+1。
// 同时为所有 level含 0计算并存每块 (min,max)(跳过 kBlank。结果写回
// meta.json + 各级数据文件level 0 复用现有 data.binlevel L 写
// data_L<level>.bin。levels<=0 视为无金字塔(仅 level 0
void buildPyramid(int levels);
// 总层数(含 level 0未建金字塔时 = 1。
int levels() const { return levelCount_; }
// 各级块数bricksX() == bricksX(0) 保持兼容。
int bricksX(int level) const;
int bricksY(int level) const;
int bricksZ(int level) const;
// 各级体素维度。
void dims(int level, int& nx, int& ny, int& nz) const;
// 读某级单块 → 还原 int16 vector。level 0 与兼容重载等价。
std::vector<std::int16_t> readBrick(int level, int bx, int by, int bz) const;
// 每块 (min,max),跳过 kBlank全 blank 块返回 (kBlank,kBlank)。
// 对未建金字塔的 level 0惰性读块计算。
std::pair<std::int16_t, std::int16_t> brickRange(int level, int bx, int by,
int bz) const;
private:
// 单块在所属级数据文件中的位置、未压缩尺寸与值域。
struct BrickEntry {
std::int64_t offset = 0;
std::int64_t compressedLen = 0;
int bw = 0, bh = 0, bd = 0;
std::int16_t vmin = 0, vmax = 0; // 块内 (min,max),跳过 kBlank全 blank=(kBlank,kBlank)
// 显式「值域已算」标志:替代 (0,0) 哨兵。(0,0) 是合法值域,不能当未计算用。
// false → brickRange 惰性读块计算并缓存true → 直接返回 (vmin,vmax)。
bool hasRange = false;
};
// 一个分辨率级:维度 + 块数 + 逐块索引 + 数据文件名。
struct Level {
int nx = 0, ny = 0, nz = 0;
int bx = 0, by = 0, bz = 0; // 块数
std::string dataFile;
std::vector<BrickEntry> bricks;
};
int brickIndexAt(const Level& lv, int bx, int by, int bz) const {
return (bz * lv.by + by) * lv.bx + bx;
}
const Level& levelAt(int level) const;
std::vector<std::int16_t> readBrickFrom(const Level& lv, int bx, int by,
int bz) const;
std::string dir_;
StoreMeta meta_;
int bricksX_ = 0, bricksY_ = 0, bricksZ_ = 0; // = level 0 块数(兼容字段)
std::vector<BrickEntry> bricks_; // = level 0 索引兼容字段readBrick(bx,by,bz) 用)
int levelCount_ = 1;
// mutablebrickRange 为 const但惰性算出值域后需就地缓存置 hasRange=true
mutable std::vector<Level> levels_; // levels_[0] 即 level 0与 bricks_ 同源)
friend class StreamingVolumeWriter;
};
// 逐块增量写 level0 store不持整卷。块写入顺序任意但每块只写一次。
// 产出与 ChunkedVolumeStore::write(整卷) 逐 brick + meta 完全一致data.bin 为
// 逐块 qCompress 流(按 bz 最慢、bx 最快的固定顺序排布meta.json 结构同 write
// 故 ChunkedVolumeStore(dir)/readBrick 能照常读。偏移/长度全程 64 位。
class StreamingVolumeWriter {
public:
// 用 StoreMeta 定 dims/brick/origin/spacing/quant/vminmax与 write 一致的元信息)。
StreamingVolumeWriter(const std::string& dir, const StoreMeta& meta);
// 写一块voxels 为该块体素(大小=bw*bh*bd块内 i 最快,与 write 同布局)。
// bx/by/bz 为块索引;体素数不符或同块重复写 → 抛 std::runtime_error。
void writeBrick(int bx, int by, int bz,
const std::vector<std::int16_t>& voxels);
// 收尾:写 meta.json含所有已写块的索引。有缺块 → 抛 std::runtime_error。
void finalize();
private:
// 单块在 data.bin 中的索引(与 ChunkedVolumeStore::BrickEntry 子集对应)。
struct Entry {
std::int64_t offset = 0;
std::int64_t compressedLen = 0;
int bw = 0, bh = 0, bd = 0;
bool written = false;
};
std::string dir_;
StoreMeta meta_;
int bricksX_ = 0, bricksY_ = 0, bricksZ_ = 0;
std::vector<Entry> entries_; // 固定顺序索引bz 最慢、bx 最快)
std::int64_t offset_ = 0; // data.bin 当前追加偏移64 位)
std::int64_t written_ = 0; // 已写块计数
bool finalized_ = false;
};
} // namespace geopro::data
#endif // GEOPRO_DATA_STORE_CHUNKEDVOLUMESTORE_HPP