fixing octave scaling when decimating image in Memory

This commit is contained in:
matlabbe
2026-09-19 16:54:13 -07:00
parent 0b1c93ef37
commit 4a8147b940
2 changed files with 56 additions and 2 deletions
+9 -2
View File
@@ -5660,7 +5660,11 @@ Signature * Memory::createSignature(const SensorData & inputData, const Transfor
if(_imagePreDecimation > 1 || useProvided3dPoints)
{
float decimationRatio = 1.0f / float(_imagePreDecimation);
double log2value = log(double(_imagePreDecimation))/log(2.0);
// The octave a feature was found at moves with the image it is
// expressed in, by the same ratio as its position: a decimated
// image is already that many pyramid levels down, so scaling the
// keypoints into it lowers their octave.
double log2value = log(double(decimationRatio))/log(2.0);
for(unsigned int i=0; i < keypoints.size(); ++i)
{
cv::KeyPoint & kpt = keypoints[i];
@@ -6246,7 +6250,10 @@ Signature * Memory::createSignature(const SensorData & inputData, const Transfor
UASSERT(keypoints3D.size() == 0 || keypoints3D.size() == wordIds.size());
unsigned int i=0;
float decimationRatio = float(preDecimation) / float(_imagePostDecimation);
double log2value = log(double(preDecimation))/log(2.0);
// Same ratio the positions are remapped by, which is what keeps a keypoint at
// the scale it was found at: log2(pre/post), and not log2(pre), those two
// agreeing only when the final image is not decimated at all.
double log2value = log(double(decimationRatio))/log(2.0);
for(std::list<int>::iterator iter=wordIds.begin(); iter!=wordIds.end() && i < keypoints.size(); ++iter, ++i)
{
cv::KeyPoint kpt = keypoints[i];
+47
View File
@@ -2844,6 +2844,53 @@ TEST_F(MemoryFixture, CreateSignatureAutoIncrementsIdWhenGenerateIdsOn)
EXPECT_EQ(memory_->getLastSignatureId(), id1 + 1);
}
TEST(MemoryTest, PreDecimationGivesBackProvidedKeypointsAsTheyCameIn)
{
// Keypoints provided with the frame are found in the full size image, so
// createSignature scales them into the pre-decimated one it describes them in, and
// scales them back to the final image size afterwards. With no post-decimation the
// two undo each other, which is the whole of what this test knows: what comes out is
// what went in, the octave included -- it moves down with the image and back up
// again, a decimated image being that many pyramid levels down already.
ParametersMap params = defaultMemoryParams();
params[Parameters::kKpMaxFeatures()] = "100"; // let descriptors be extracted
params[Parameters::kMemUseOdomFeatures()] = "true";
params[Parameters::kMemImagePreDecimation()] = "2";
params[Parameters::kMemImagePostDecimation()] = "1";
params[Parameters::kRtabmapImagesAlreadyRectified()] = "true"; // skip rectification
Memory memory(params);
// Big enough that the keypoint stays far from the border of the decimated image,
// where a descriptor cannot be computed and the keypoint would be dropped.
cv::Mat image(256, 256, CV_8UC1);
cv::RNG rng(7);
rng.fill(image, cv::RNG::UNIFORM, 0, 255);
const cv::Mat covariance = cv::Mat::eye(6, 6, CV_64FC1) * 0.01;
const CameraModel model(100.0, 100.0, 128.0, 128.0,
CameraModel::opticalRotation(), 0.0, cv::Size(256, 256));
SensorData data;
data.setRGBDImage(image, cv::Mat(), std::vector<CameraModel>{model});
data.setId(0);
// No descriptors: that is what sends createSignature down the branch where the
// provided keypoints are described from the image rather than taken wholesale.
cv::KeyPoint kpt(128.0f, 120.0f, 8.0f);
kpt.octave = 2;
data.setFeatures(std::vector<cv::KeyPoint>(1, kpt),
std::vector<cv::Point3f>(1, cv::Point3f(0.0f, 0.0f, 1.0f)),
cv::Mat());
ASSERT_TRUE(memory.update(data, Transform(0, 0, 0, 0, 0, 0), covariance));
const Signature * s = memory.getSignature(memory.getLastSignatureId());
ASSERT_NE(s, nullptr);
ASSERT_EQ(s->getWordsKpts().size(), 1u);
EXPECT_FLOAT_EQ(s->getWordsKpts()[0].pt.x, kpt.pt.x);
EXPECT_FLOAT_EQ(s->getWordsKpts()[0].pt.y, kpt.pt.y);
EXPECT_FLOAT_EQ(s->getWordsKpts()[0].size, kpt.size);
EXPECT_EQ(s->getWordsKpts()[0].octave, kpt.octave);
}
TEST(MemoryTest, CreateSignaturePostDecimatesImageWhenPostDecimationGreaterThanOne)
{
// kMemImagePostDecimation > 1 causes createSignature to downsample the RGB image