Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions modules/perception/camera_location_refinement/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ apollo_cc_library(
"//modules/perception/common/camera:apollo_perception_common_camera",
"//modules/perception/common/lib:apollo_perception_common_lib",
"//modules/perception/common/onboard:apollo_perception_common_onboard",
"@com_google_googletest//:gtest",
],
)

Expand All @@ -64,6 +65,18 @@ apollo_component(
],
)

apollo_cc_test(
name = "location_refiner_postprocessor_test",
size = "small",
srcs = [
"location_refiner/location_refiner_postprocessor_test.cc",
],
deps = [
":apollo_perception_camera_location_refinement",
"@com_google_googletest//:gtest_main",
],
)

apollo_package()

cpplint()
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,19 @@ LocationRefinerPostprocessor::LocationRefinerPostprocessor() {
postprocessor_.reset(new ObjPostProcessor);
}

ObjPostProcessorOptions LocationRefinerPostprocessor::BuildPostprocessorOptions(
const float bbox2d[4], const float dimension_hwl[3], float rotation_y,
const float query_plane[4]) {
ObjPostProcessorOptions options;
memcpy(options.bbox, bbox2d, sizeof(options.bbox));
options.check_lowerbound = true;
options.line_segs.emplace_back(bbox2d[0], bbox2d[3], bbox2d[2], bbox2d[3]);
memcpy(options.hwl, dimension_hwl, sizeof(options.hwl));
options.ry = rotation_y;
memcpy(options.plane, query_plane, sizeof(options.plane));
return options;
}

bool LocationRefinerPostprocessor::Init(
const PostprocessorInitOptions &options) {
std::string config_file =
Expand All @@ -56,6 +69,9 @@ bool LocationRefinerPostprocessor::Process(const PostprocessorOptions &options,
Eigen::Vector4d plane;
if (!calibration_service_->QueryGroundPlaneInCameraFrame(&plane)) {
AINFO << "No valid ground plane in the service.";
// Refinement is defined on a calibrated ground plane, so skip cleanly when
// the service cannot provide one.
return true;
}

float query_plane[4] = {
Expand All @@ -78,7 +94,6 @@ bool LocationRefinerPostprocessor::Process(const PostprocessorOptions &options,
const int height_image = frame->data_provider->src_height();
postprocessor_->Init(k_mat, width_image, height_image);

ObjPostProcessorOptions obj_postprocessor_options;
int nr_valid_obj = 0;

for (auto &obj : frame->detected_objects) {
Expand Down Expand Up @@ -124,17 +139,9 @@ bool LocationRefinerPostprocessor::Process(const PostprocessorOptions &options,
rotation_y -= 2 * PI;
}

// process
memcpy(obj_postprocessor_options.bbox, bbox2d, sizeof(float) * 4);
obj_postprocessor_options.check_lowerbound = true;
camera::LineSegment2D<float> line_seg(bbox2d[0], bbox2d[3], bbox2d[2],
bbox2d[3]);
obj_postprocessor_options.line_segs.push_back(line_seg);
memcpy(obj_postprocessor_options.hwl, dimension_hwl, sizeof(float) * 3);
obj_postprocessor_options.ry = rotation_y;
// refine with calibration service, support ground plane model currently
// {0.0f, cos(tilt), -sin(tilt), -camera_ground_height}
memcpy(obj_postprocessor_options.plane, query_plane, sizeof(float) * 4);
ObjPostProcessorOptions obj_postprocessor_options =
BuildPostprocessorOptions(bbox2d, dimension_hwl, rotation_y,
query_plane);

// changed to touching-ground center
object_center[1] += dimension_hwl[0] / 2;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
#include <memory>
#include <string>

#include "gtest/gtest_prod.h"

#include "modules/perception/camera_location_refinement/location_refiner/proto/location_refiner.pb.h"

#include "modules/perception/camera_location_refinement/interface/base_postprocessor.h"
Expand Down Expand Up @@ -72,7 +74,16 @@ class LocationRefinerPostprocessor : public BasePostprocessor {
return x > left && x < right;
}

static ObjPostProcessorOptions BuildPostprocessorOptions(
const float bbox2d[4], const float dimension_hwl[3], float rotation_y,
const float query_plane[4]);

private:
FRIEND_TEST(LocationRefinerPostprocessorTest,
build_postprocessor_options_test);
FRIEND_TEST(LocationRefinerPostprocessorTest,
process_skips_when_ground_plane_unavailable_test);

std::unique_ptr<ObjPostProcessor> postprocessor_;
std::shared_ptr<BaseCalibrationService> calibration_service_;
location_refiner::LocationRefinerParam location_refiner_param_;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
/******************************************************************************
* Copyright 2026 The Apollo Authors. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the License);
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an AS IS BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*****************************************************************************/

#include "modules/perception/camera_location_refinement/location_refiner/location_refiner_postprocessor.h"

#include "gtest/gtest.h"

#include "modules/perception/common/base/object.h"

namespace apollo {
namespace perception {
namespace camera {

class FakeCalibrationService : public BaseCalibrationService {
public:
bool Init(const CalibrationServiceInitOptions &options =
CalibrationServiceInitOptions()) override {
return true;
}

bool BuildIndex() override { return true; }

bool QueryGroundPlaneInCameraFrame(
Eigen::Vector4d *plane_param) const override {
if (plane_param != nullptr) {
*plane_param = Eigen::Vector4d::Zero();
}
return false;
}

std::string Name() const override { return "FakeCalibrationService"; }
};

class LocationRefinerPostprocessorTest : public ::testing::Test {};

TEST_F(LocationRefinerPostprocessorTest, build_postprocessor_options_test) {
const float bbox2d[4] = {10.0f, 20.0f, 30.0f, 40.0f};
const float dimension_hwl[3] = {1.5f, 2.5f, 3.5f};
const float rotation_y = 0.25f;
const float query_plane[4] = {0.0f, 1.0f, 0.0f, -1.6f};

auto first = LocationRefinerPostprocessor::BuildPostprocessorOptions(
bbox2d, dimension_hwl, rotation_y, query_plane);
auto second = LocationRefinerPostprocessor::BuildPostprocessorOptions(
bbox2d, dimension_hwl, rotation_y, query_plane);

ASSERT_EQ(first.line_segs.size(), 1u);
ASSERT_EQ(second.line_segs.size(), 1u);

EXPECT_FLOAT_EQ(first.bbox[0], bbox2d[0]);
EXPECT_FLOAT_EQ(first.bbox[1], bbox2d[1]);
EXPECT_FLOAT_EQ(first.bbox[2], bbox2d[2]);
EXPECT_FLOAT_EQ(first.bbox[3], bbox2d[3]);
EXPECT_FLOAT_EQ(first.hwl[0], dimension_hwl[0]);
EXPECT_FLOAT_EQ(first.hwl[1], dimension_hwl[1]);
EXPECT_FLOAT_EQ(first.hwl[2], dimension_hwl[2]);
EXPECT_FLOAT_EQ(first.ry, rotation_y);
EXPECT_FLOAT_EQ(first.plane[0], query_plane[0]);
EXPECT_FLOAT_EQ(first.plane[1], query_plane[1]);
EXPECT_FLOAT_EQ(first.plane[2], query_plane[2]);
EXPECT_FLOAT_EQ(first.plane[3], query_plane[3]);
EXPECT_FLOAT_EQ(first.line_segs[0].pt_start[0], bbox2d[0]);
EXPECT_FLOAT_EQ(first.line_segs[0].pt_start[1], bbox2d[3]);
EXPECT_FLOAT_EQ(first.line_segs[0].pt_end[0], bbox2d[2]);
EXPECT_FLOAT_EQ(first.line_segs[0].pt_end[1], bbox2d[3]);

first.line_segs.emplace_back(0.0f, 0.0f, 1.0f, 1.0f);

EXPECT_EQ(first.line_segs.size(), 2u);
EXPECT_EQ(second.line_segs.size(), 1u);
}

TEST_F(LocationRefinerPostprocessorTest,
process_skips_when_ground_plane_unavailable_test) {
LocationRefinerPostprocessor postprocessor;
postprocessor.calibration_service_ =
std::make_shared<FakeCalibrationService>();
postprocessor.location_refiner_param_.set_min_dist_to_camera(30.0f);
postprocessor.location_refiner_param_.set_roi_h2bottom_scale(0.5f);

onboard::CameraFrame frame;
auto obj = std::make_shared<base::Object>();
obj->camera_supplement.local_center = Eigen::Vector3f(3.0f, 0.0f, 5.0f);
obj->camera_supplement.box.xmin = 10.0f;
obj->camera_supplement.box.ymin = 20.0f;
obj->camera_supplement.box.xmax = 30.0f;
obj->camera_supplement.box.ymax = 40.0f;
obj->size = Eigen::Vector3f(4.0f, 2.0f, 1.5f);
frame.detected_objects.push_back(obj);

PostprocessorOptions options;
options.do_refinement_with_calibration_service = true;

EXPECT_TRUE(postprocessor.Process(options, &frame));
EXPECT_EQ(frame.data_provider, nullptr);
EXPECT_FLOAT_EQ(frame.detected_objects[0]->camera_supplement.local_center(0),
3.0f);
EXPECT_FLOAT_EQ(frame.detected_objects[0]->camera_supplement.local_center(1),
0.0f);
EXPECT_FLOAT_EQ(frame.detected_objects[0]->camera_supplement.local_center(2),
5.0f);
}

} // namespace camera
} // namespace perception
} // namespace apollo