introlab / rtabmap

RTAB-Map library and standalone application
https://introlab.github.io/rtabmap
Other
2.82k stars 787 forks source link

Question about table Word in Database #1027

Closed hellovuong closed 1 year ago

hellovuong commented 1 year ago

Hello, I spent a few days checking out what was saved in the rtabmap database. I realized that the word table is actually just a "copy" of rows in the Feature table with unique word_id. Taking into account that if not saving the compressed image/depth, the feature and word table are the ones that have the highest size. Moreover, if the descriptor size is 128 or even 256 then the duplicate data size in the database is even higher. Do you think that we can remove Word Table from the Database and modify the init process of the Memory class so that it can init without Word Table? Then I believe the database size is only half compared with now. Hope to hear your though about this idea.

matlabbe commented 1 year ago

Their meaning is slightly different.

The Word table contains visual word dictionary.

The Feature table contains for each feature in an image, a word_id reference to closest (quantized) descriptor in the visual word dictionary. The descriptor saved in Feature table can be different than the one pointed by word_id in Word table. The descriptor is the original one extracted from that image. Note that if that descriptor was not quantized to a different one in dictionary, it will be added to Word table, with new word_id pointing to a copy of that descriptor.

In original BOW version, we didn't keep the descriptors in Feature table. However later, we found that estimating transformation estimation between original descriptors of a loop closure resulted in better feature matching and more inliers. The parameter Mem/RawDescriptorsKept (default true) was introduced to keep the raw descriptors in Feature table. If Mem/RawDescriptorsKept=false, the Feature table won't keep any descriptors, so on loop closure, the quantized descriptors will be used for transformation estimation (which may result in less visual matches). To keep the same number of matches than Mem/RawDescriptorsKept=true without saving descriptors in Feature table, is to enable RGBD/LoopClosureReextractFeatures, at the cost of CPU usage as descriptors will be re-extracted from the loop closure images (this option assumes that we keep the images in database Mem/BinDataKept=true, otherwise we won't be able to re-extract features).

Note also that the number of descriptors in Word and Feature tables would be different, so not an exact 2X more memory usage. for example, default Kp/MaxFeatures=500 and Vis/MaxFeatures=1000, meaning at most 500 descriptors (if none of the descriptors could be quantized to ones in dictionary) will be added to Word table while 1000 will be added to Feature table per image. These parameters are based on default GFTT/BRIEF or ORB binary features. For larger descriptors like SIFT/SURF/SuperPoint, sometimes I use Kp/MaxFeatures=200 and Vis/MaxFeatures=400.

In summary, I think to remove any duplicates, while keeping same options as above, would be to add a new table Descriptor that would be referenced by Word and Feature tables.

Current: https://github.com/introlab/rtabmap/blob/2da448f4ee273dd848d0ee9d8da2e18e7d1c65b2/corelib/src/resources/DatabaseSchema.sql.in#L65-L88

Proposed:

CREATE TABLE Word (
    id INTEGER NOT NULL,
    descriptor_id INTEGER NOT NULL,
    time_enter DATE,
    PRIMARY KEY (id),
    FOREIGN KEY (descriptor_id) REFERENCES Descriptor(id)
);

CREATE TABLE Feature (
    node_id INTEGER NOT NULL,
    word_id INTEGER NOT NULL,
    pos_x FLOAT NOT NULL,
    pos_y FLOAT NOT NULL,
    size INTEGER NOT NULL,
    dir FLOAT NOT NULL,
    response FLOAT NOT NULL,
    octave INTEGER NOT NULL,
    depth_x FLOAT,
    depth_y FLOAT,
    depth_z FLOAT,
    descriptor_id INTEGER,
    FOREIGN KEY (node_id) REFERENCES Node(id)
    FOREIGN KEY (word_id) REFERENCES Word(id)
    FOREIGN KEY (descriptor_id) REFERENCES Descriptor(id)
);

CREATE TABLE Descriptor (
    id INTEGER NOT NULL,
    descriptor_size INTEGER,
    descriptor BLOB
);
hellovuong commented 1 year ago

@matlabbe Amazing, Thank you!

matlabbe commented 1 year ago

Setting BLOB in the database to null won't make the file smaller. I think sqlite3 could use that memory eventually to add new data instead of increasing the file size.

You may try VACUUM after setting BLOB to null: https://www.sqlite.org/lang_vacuum.html

To avoid unused nodes in database, you may set Mem/NotLinkedNodesKept=false when mapping.

hellovuong commented 1 year ago

@matlabbe, Hi thank you again. I can reduce the db's size by making a duplicate without sensor data.