Skip to content

Commit 1f8475f

Browse files
committed
fix(benchmark): support legacy monocular geometry
Signed-off-by: chaofengw <chaofengw@nvidia.com>
1 parent 0968758 commit 1f8475f

6 files changed

Lines changed: 131 additions & 13 deletions

File tree

apps/benchmark/native/benchmark_worker.cpp

Lines changed: 70 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -465,6 +465,75 @@ Json run_transcribe(trtmc::ITask& task, const Json& request, const Timing& timin
465465
});
466466
}
467467

468+
template <class T>
469+
void write_binary_artifact(const std::string& path, const T* values, std::uint64_t count) {
470+
if (count > static_cast<std::uint64_t>(std::numeric_limits<std::streamsize>::max()) / sizeof(T))
471+
throw std::runtime_error("binary artifact is too large");
472+
std::ofstream output(path, std::ios::binary);
473+
output.exceptions(std::ios::badbit | std::ios::failbit);
474+
output.write(reinterpret_cast<const char*>(values),
475+
static_cast<std::streamsize>(count * sizeof(T)));
476+
output.close();
477+
}
478+
479+
Json run_geometry(trtmc::ITask& task, const Json& request, const Timing& timing) {
480+
auto& interface = require_interface<trtmc::IMonocularGeometry>(task, "IMonocularGeometry");
481+
const auto path = request.at("image_path").get<std::string>();
482+
std::optional<Image> cached;
483+
if (!timing.asset_loading_included)
484+
cached = read_image(path);
485+
auto invoke = [&]() {
486+
std::optional<Image> loaded;
487+
if (!cached)
488+
loaded = read_image(path);
489+
const auto& image = cached ? *cached : *loaded;
490+
return interface.estimate_geometry(image.pixels.data(), image.height, image.width);
491+
};
492+
std::optional<trtmc::GeometryResult> last;
493+
for (int index = 0; index < timing.warmup; ++index)
494+
last = invoke();
495+
Json observations = Json::array();
496+
for (int index = 0; index < timing.iterations; ++index) {
497+
last.reset();
498+
const auto started = Clock::now();
499+
auto result = invoke();
500+
const auto wall_ms = elapsed_ms(started);
501+
last.emplace(std::move(result));
502+
observations.push_back({{"runtime_e2e_wall_ms", wall_ms},
503+
{"geometry_images", 1},
504+
{"geometry_pixels", last->depth.size()}});
505+
}
506+
if (last->height <= 0 || last->width <= 0)
507+
throw std::runtime_error("monocular geometry returned invalid dimensions");
508+
const auto pixels = static_cast<std::size_t>(last->height) * last->width;
509+
if (last->points.size() != pixels * 3 || last->depth.size() != pixels ||
510+
last->mask.size() != pixels)
511+
throw std::runtime_error("monocular geometry returned inconsistent output shapes");
512+
const auto prefix = request.at("_artifact_prefix").get<std::string>();
513+
write_binary_artifact(prefix + ".points.f32", last->points.data(), last->points.size());
514+
write_binary_artifact(prefix + ".depth.f32", last->depth.data(), last->depth.size());
515+
write_binary_artifact(prefix + ".mask.u8", last->mask.data(), last->mask.size());
516+
Json intrinsics = Json::array();
517+
for (int row = 0; row < 3; ++row)
518+
intrinsics.push_back({last->intrinsics[row * 3], last->intrinsics[row * 3 + 1],
519+
last->intrinsics[row * 3 + 2]});
520+
return {{"observations", std::move(observations)},
521+
{"output_summary",
522+
{{"geometry_images", 1},
523+
{"geometry_pixels", pixels},
524+
{"height", last->height},
525+
{"width", last->width},
526+
{"point_shape", {last->height, last->width, 3}},
527+
{"valid_pixels", std::count(last->mask.begin(), last->mask.end(), std::uint8_t{1})},
528+
{"normalized_intrinsics", std::move(intrinsics)},
529+
{"units", "meters"},
530+
{"camera_axes", {"right", "down", "forward"}},
531+
{"intrinsics_coordinates", "normalized_uv"},
532+
{"points_artifact", prefix + ".points.f32"},
533+
{"depth_artifact", prefix + ".depth.f32"},
534+
{"valid_mask_artifact", prefix + ".mask.u8"}}}};
535+
}
536+
468537
Json run_segment(trtmc::ITask& task, const Json& request, const Timing& timing) {
469538
auto& interface = require_interface<trtmc::ISegmentation>(task, "ISegmentation");
470539
const Image image = read_image(request.at("image_path").get<std::string>());
@@ -2894,17 +2963,6 @@ Json run_track_pose(const trtmc::Model& model, const Json& request, const Timing
28942963
});
28952964
}
28962965

2897-
template <class T>
2898-
void write_binary_artifact(const std::string& path, const T* values, std::uint64_t count) {
2899-
if (count > static_cast<std::uint64_t>(std::numeric_limits<std::streamsize>::max()) / sizeof(T))
2900-
throw std::runtime_error("binary artifact is too large");
2901-
std::ofstream output(path, std::ios::binary);
2902-
output.exceptions(std::ios::badbit | std::ios::failbit);
2903-
output.write(reinterpret_cast<const char*>(values),
2904-
static_cast<std::streamsize>(count * sizeof(T)));
2905-
output.close();
2906-
}
2907-
29082966
Json run_geometry(const trtmc::Model& model, const Json& request, const Timing& timing,
29092967
const std::string& task_id) {
29102968
const auto task = task_for_operation<trtmc::ImageToMetricGeometry>(model, task_id);
@@ -3796,6 +3854,7 @@ Json execute(const Json& request, const std::string& output_path) {
37963854
{"classify", run_classify},
37973855
{"detect", run_detect},
37983856
{"extract_features", run_extract_features},
3857+
{"geometry", run_geometry},
37993858
{"disparity", run_disparity},
38003859
{"rerank", run_rerank},
38013860
{"encode", run_encode},

apps/benchmark/tests/native/test_benchmark_worker_e2e.cpp

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,41 @@ int main(int argc, char** argv) {
230230
prompted.at("box_coordinates") == "original_image_pixels_xyxy",
231231
"legacy prompted segmentation retains masks, scores and boxes for comparison");
232232
std::filesystem::remove(prompted_bundle);
233+
234+
const auto geometry_bundle = runtime_root / "benchmark_fake_geometry.bundle";
235+
write_bundle(geometry_bundle, "monocular_geometry");
236+
Json geometry_request = request;
237+
geometry_request["case_name"] = "fake-monocular-geometry";
238+
geometry_request["bundle"] = geometry_bundle.string();
239+
geometry_request["operation"] = "geometry";
240+
geometry_request["request"] = {{"image_path", legacy_image.string()}};
241+
{
242+
std::ofstream request_file(request_path);
243+
request_file << geometry_request << '\n';
244+
}
245+
check(std::system(command.c_str()) == 0, "monocular geometry worker process completed");
246+
std::ifstream geometry_output_file(output_path);
247+
Json geometry_result;
248+
geometry_output_file >> geometry_result;
249+
const auto& geometry = geometry_result.at("output_summary");
250+
check(geometry_result.at("task") == "monocular_geometry" &&
251+
geometry_result.at("selected_task") == "monocular_geometry" &&
252+
geometry.at("geometry_pixels") == 2 &&
253+
geometry.at("point_shape") == Json::array({1, 2, 3}) &&
254+
geometry.at("valid_pixels") == 1 &&
255+
geometry.at("normalized_intrinsics") ==
256+
Json::array({{1.0, 0.0, 0.5}, {0.0, 1.0, 0.5}, {0.0, 0.0, 1.0}}) &&
257+
std::filesystem::file_size(geometry.at("points_artifact").get<std::string>()) ==
258+
6 * sizeof(float) &&
259+
std::filesystem::file_size(geometry.at("depth_artifact").get<std::string>()) ==
260+
2 * sizeof(float) &&
261+
std::filesystem::file_size(
262+
geometry.at("valid_mask_artifact").get<std::string>()) == 2,
263+
"legacy monocular geometry retains complete metric artifacts");
264+
std::filesystem::remove(geometry.at("points_artifact").get<std::string>());
265+
std::filesystem::remove(geometry.at("depth_artifact").get<std::string>());
266+
std::filesystem::remove(geometry.at("valid_mask_artifact").get<std::string>());
267+
std::filesystem::remove(geometry_bundle);
233268
std::filesystem::remove(legacy_image);
234269

235270
Json sdk = request;

apps/benchmark/trtmc_benchmark/task_adapters.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ class CaseResolution:
5858
"image_text_to_instance_masks": ("segment_prompted", 10, 100),
5959
"stereo_images_to_disparity": ("disparity", 3, 100),
6060
"image_to_metric_geometry": ("geometry", 3, 100),
61+
"monocular_geometry": ("geometry", 3, 100),
6162
"text_to_pooled_features": ("encode", 50, 500),
6263
"text_to_head_scores": ("head_scores", 50, 500),
6364
"text_to_token_features": ("encode", 50, 500),
@@ -312,7 +313,7 @@ def _request(task: str, case: Mapping[str, Any], root: Path) -> dict[str, Any]:
312313
return _text_controls(case, include_defaults=False)
313314
if task in _LATENT_TEXT | _LATENT_STEPS:
314315
return _latent_request(task, case, root)
315-
if task == "image_to_metric_geometry":
316+
if task in {"image_to_metric_geometry", "monocular_geometry"}:
316317
request = {"image_path": _semantic_asset(case, root, "image_path", "image", "test_image")}
317318
_copy_explicit(request, case, "fov_x")
318319
return request

apps/benchmark/trtmc_benchmark/tests/test_benchmark.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -340,6 +340,13 @@ def test_metric_geometry_has_an_image_input_and_preserves_optional_config(sdk_as
340340
}
341341

342342

343+
def test_legacy_monocular_geometry_uses_the_same_public_request(sdk_assets: Path) -> None:
344+
result = resolve_task_case("monocular_geometry", {"image": "image.ppm"}, sdk_assets)
345+
346+
assert result.operation == "geometry"
347+
assert result.request == {"image_path": str(sdk_assets / "image.ppm")}
348+
349+
343350
def test_image_only_detection_has_no_text_or_postprocessing_defaults(sdk_assets: Path) -> None:
344351
result = resolve_task_case("image_to_boxes", {"image": "image.ppm", "config": {"score": 0.0}}, sdk_assets)
345352
assert result.operation == "detect"

core/runtime/tests/fake_family.cpp

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,21 @@ class FakeObjectDetection final : public trtmc::IObjectDetection {
7676
}
7777
};
7878

79+
class FakeMonocularGeometry final : public trtmc::IMonocularGeometry {
80+
public:
81+
trtmc::GeometryResult estimate_geometry(const float*, std::int32_t height,
82+
std::int32_t width) override {
83+
trtmc::GeometryResult result;
84+
result.points = {1.0F, 2.0F, 3.0F, 4.0F, 5.0F, 6.0F};
85+
result.depth = {3.0F, 6.0F};
86+
result.mask = {1, 0};
87+
result.intrinsics = {1.0F, 0.0F, 0.5F, 0.0F, 1.0F, 0.5F, 0.0F, 0.0F, 1.0F};
88+
result.height = height;
89+
result.width = width;
90+
return result;
91+
}
92+
};
93+
7994
class FakePointPromptedSegmentation final : public trtmc::IPointPromptedSegmentation {
8095
public:
8196
trtmc::PromptedSegmentationResult segment_prompted(const float*, std::int32_t height,
@@ -97,6 +112,8 @@ trtmc::ITask* create_fake_perception_task(const trtmc::FamilyContext& context) {
97112
return new FakeSegmentation();
98113
if (context.reader.info().task == trtmc::IObjectDetection::kTask)
99114
return new FakeObjectDetection();
115+
if (context.reader.info().task == trtmc::IMonocularGeometry::kTask)
116+
return new FakeMonocularGeometry();
100117
if (context.reader.info().task == trtmc::IPointPromptedSegmentation::kTask)
101118
return new FakePointPromptedSegmentation();
102119
return nullptr;

families/moge/tests/benchmark/moge-2-vitl.yaml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ candidate:
88
checkpoint: Ruicheng/moge-2-vitl
99
revision: 39c4d5e957afe587e04eec59dc2bcc3be5ecd968
1010
task: monocular_geometry
11-
selected_task: image_to_metric_geometry
1211
precision: fp32
1312
bundle: moge-2-vitl.bundle
1413
build:

0 commit comments

Comments
 (0)