Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Save the divided tiles from whole scequence with overlapping scanes #40

Open
Hao-HUST opened this issue Apr 27, 2021 · 1 comment
Open

Comments

@Hao-HUST
Copy link

Hi, I noticed that the sequence is divided into different tiles on your labeling tool. Is that possible to separately save each tile with point cloud and labels?

@jbehley
Copy link
Owner

jbehley commented Jun 15, 2021

There is currently no option available for this. You can look in our code, how we achieve this using the given trajectory:

/** \brief set axis fixed (x = 1, y = 2, z = 3) **/
void Viewport::setFixedAxis(AXIS axis) { mAxis = axis; }
void Viewport::setMaximumScans(uint32_t numScans) {
maxScans_ = numScans;
uint32_t max_size = maxScans_ * maxPointsPerScan_;
bufPoses_.resize(maxScans_);
bufPoints_.resize(max_size);
bufVisible_.resize(max_size);
bufLabels_.resize(max_size);
bufScanIndexes_.resize(max_size);
uint32_t memTile = 0; // = bufPoses_.memorySize();
memTile += bufPoints_.memorySize();
memTile += bufVisible_.memorySize();
memTile += bufLabels_.memorySize();
memTile += bufScanIndexes_.memorySize();
std::cout << "mem size: " << float(memTile) / (1000 * 1000) << " MB" << std::endl;
}
void Viewport::setPoints(const std::vector<PointcloudPtr>& p, std::vector<LabelsPtr>& l) {
std::cout << "Setting points..." << std::flush;
glow::_CheckGlError(__FILE__, __LINE__);
points_ = p;
labels_ = l;
// Stopwatch::tic();
{
ScopedBinder<GlVertexArray> vaoBinder(vao_temp_points_);
ScopedBinder<GlProgram> programBinder(prgFillTilePoints_);
ScopedBinder<GlTransformFeedback> feedbackBinder(tfFillTilePoints_);
glow::_CheckGlError(__FILE__, __LINE__);
prgFillTilePoints_.setUniform(GlUniform<float>("maxRange", maxRange_));
prgFillTilePoints_.setUniform(GlUniform<float>("minRange", minRange_));
prgFillTilePoints_.setUniform(GlUniform<vec2>("tilePos", tilePos_));
prgFillTilePoints_.setUniform(GlUniform<float>("tileSize", tileSize_));
prgFillTilePoints_.setUniform(GlUniform<float>("tileBoundary", tileBoundary_));
prgFillTilePoints_.setUniform(GlUniform<bool>("addCarPoints", drawingOption_["add car points"]));
glEnable(GL_RASTERIZER_DISCARD);
tfFillTilePoints_.begin(TransformFeedbackMode::POINTS);
for (uint32_t t = 0; t < points_.size(); ++t) {
prgFillTilePoints_.setUniform(GlUniform<float>("maxRange", maxRange_));
prgFillTilePoints_.setUniform(GlUniform<Eigen::Matrix4f>("pose", points_[t]->pose));
uint32_t num_points = points_[t]->size();
std::vector<uint32_t> visible(num_points, 1);
for (uint32_t i = 0; i < num_points; ++i) {
if (std::find(mFilteredLabels.begin(), mFilteredLabels.end(), (*labels_[t])[i]) != mFilteredLabels.end()) {
visible[i] = 0;
}
}
// copy data from CPU -> GPU.
bufTempPoints_.assign(points_[t]->points);
if (points_[t]->hasRemissions())
bufTempRemissions_.assign(points_[t]->remissions);
else
bufTempRemissions_.assign(std::vector<float>(points_[t]->size(), 1.0f));
bufTempLabels_.assign(*(labels_[t]));
bufTempVisible_.assign(visible);
prgFillTilePoints_.setUniform(GlUniform<uint32_t>("scan", t));
// extract tile points.
glDrawArrays(GL_POINTS, 0, points_[t]->size());
}
uint32_t numCopiedPoints = tfFillTilePoints_.end();
glDisable(GL_RASTERIZER_DISCARD);
bufPoints_.resize(numCopiedPoints);
bufLabels_.resize(numCopiedPoints);
bufVisible_.resize(numCopiedPoints);
bufScanIndexes_.resize(numCopiedPoints);
// get per scan information from scan indexes.
scanInfos_.clear();
scanInfos_.resize(points_.size());
for (auto& info : scanInfos_) {
info.start = 0;
info.size = 0;
}
glow::_CheckGlError(__FILE__, __LINE__);
std::vector<glow::vec2> scanIndexes;
glow::GlBuffer<glow::vec2> bufReadBuffer{glow::BufferTarget::ARRAY_BUFFER, glow::BufferUsage::STREAM_READ};
bufReadBuffer.resize(maxPointsPerScan_);
ScanInfo current;
int32_t currentScanIndex{-1};
uint32_t count = 0;
while (count * maxPointsPerScan_ < bufScanIndexes_.size()) {
uint32_t read_size = std::min<uint32_t>(maxPointsPerScan_, bufScanIndexes_.size() - count * maxPointsPerScan_);
bufScanIndexes_.copyTo(count * maxPointsPerScan_, read_size, bufReadBuffer, 0);
bufReadBuffer.get(scanIndexes, 0, read_size);
for (uint32_t i = 0; i < read_size; ++i) {
if (currentScanIndex != int32_t(scanIndexes[i].x)) {
if (currentScanIndex > -1) scanInfos_[currentScanIndex] = current;
current.start = count * maxPointsPerScan_ + i;
current.size = 0;
currentScanIndex = uint32_t(scanIndexes[i].x);
}
current.size += 1;
}
++count;
}
// write last entry.
if (currentScanIndex > -1) scanInfos_[currentScanIndex] = current;
// ensure that empty indexes get the beginning from before.
for (uint32_t i = 0; i < scanInfos_.size(); ++i) {
if (scanInfos_[i].size == 0 && i > 0) scanInfos_[i].start = scanInfos_[i - 1].start + scanInfos_[i - 1].size;
}
std::cout << "copied " << scanInfos_.size() << " scans with " << numCopiedPoints << " points" << std::endl;
if (numCopiedPoints == bufPoints_.capacity()) {
QMessageBox::warning(this, "Increase number of scans.",
"Its possible that not all scans could be loaded. Please increase the 'max scans' in "
"the settings.cfg, but ensure that enough GPU memory is available.");
}
}
glow::_CheckGlError(__FILE__, __LINE__);
updateLabels();
if (labelInstances_) {
updateBoundingBoxes();
fillBoundingBoxBuffers();
updateInstanceSelectionMap(); // re-render selection map.
}
updateGL();
}

But the process is quite simple (even though we use OpenGL to speed it up, you can replicate it in Python):

  1. Determine Tile centers and corresponding indexes of point clouds based on the trajectory. We use a simple heuristic to determine all point clouds that overlap:
    // assumes that (0,0,0) is always the start.
    Eigen::Vector2f min = Eigen::Vector2f::Zero();
    Eigen::Vector2f max = Eigen::Vector2f::Zero();
    for (uint32_t i = 0; i < poses_.size(); ++i) {
    Eigen::Vector4f t = poses_[i].col(3);
    min.x() = std::min(t.x() - maxDistance_, min.x());
    min.y() = std::min(t.y() - maxDistance_, min.y());
    max.x() = std::max(t.x() + maxDistance_, max.x());
    max.y() = std::max(t.y() + maxDistance_, max.y());
    }
    // std::cout << "tileSize = " << tileSize_ << std::endl;
    // std::cout << "min = " << min << ", max = " << max << std::endl;
    offset_.x() = std::ceil((std::abs(min.x()) - 0.5 * tileSize_) / tileSize_) * tileSize_ + 0.5 * tileSize_;
    offset_.y() = std::ceil((std::abs(min.y()) - 0.5 * tileSize_) / tileSize_) * tileSize_ + 0.5 * tileSize_;
    // std::cout << "offset = " << offset_ << std::endl;
    numTiles_.x() = std::ceil((std::abs(min.x()) - 0.5 * tileSize_) / tileSize_) +
    std::ceil((max.x() - 0.5 * tileSize_) / tileSize_) + 1;
    numTiles_.y() = std::ceil((std::abs(min.y()) - 0.5 * tileSize_) / tileSize_) +
    std::ceil((max.y() - 0.5 * tileSize_) / tileSize_) + 1;
    // std::cout << "numTiles = " << numTiles_ << std::endl;
    tiles_.clear();
    tiles_.resize(numTiles_.x() * numTiles_.y());
    Eigen::Vector2f idxRadius(maxDistance_ / tileSize_, maxDistance_ / tileSize_);
    for (uint32_t i = 0; i < uint32_t(numTiles_.x()); ++i) {
    for (uint32_t j = 0; j < uint32_t(numTiles_.y()); ++j) {
    auto& tile = tiles_[tileIdxToOffset(i, j)];
    tile.i = i;
    tile.j = j;
    tile.x = i * tileSize_ - offset_.x() + 0.5 * tileSize_;
    tile.y = j * tileSize_ - offset_.y() + 0.5 * tileSize_;
    tile.size = tileSize_;
    }
    }
    trajectory_.clear();
    Eigen::Vector2f e(0.5 * tileSize_, 0.5 * tileSize_);
    for (uint32_t i = 0; i < poses_.size(); ++i) {
    Eigen::Vector2f t = poses_[i].col(3).head(2);
    Eigen::Vector2f idx((t.x() + offset_.x()) / tileSize_, (t.y() + offset_.y()) / tileSize_);
    trajectory_.push_back(Eigen::Vector2f((t.x() + offset_.x()) / tileSize_, (t.y() + offset_.y()) / tileSize_));
    // tiles_[tileIdxToOffset(uint32_t(idx.x()), uint32_t(idx.y()))].indexes.push_back(i);
    // uint32_t u_min = std::max(int32_t(idx.x() - idxRadius.x()), 0);
    // uint32_t u_max = std::min(int32_t(std::ceil(idx.x() + idxRadius.x())), numTiles_.x());
    // uint32_t v_min = std::max(int32_t(idx.y() - idxRadius.y()), 0);
    // uint32_t v_max = std::min(int32_t(std::ceil(idx.y() + idxRadius.y())), numTiles_.y());
    // FIXME: workaround check all tiles.
    for (uint32_t u = 0; u < uint32_t(numTiles_.x()); ++u) {
    for (uint32_t v = 0; v < uint32_t(numTiles_.y()); ++v) {
    auto& tile = tiles_[tileIdxToOffset(u, v)];
    Eigen::Vector2f q = t - Eigen::Vector2f(tile.x, tile.y);
    q[0] = std::abs(q[0]);
    q[1] = std::abs(q[1]);
    // check for exact overlap (see Behley et al., ICRA, 2015)
    if (std::max(q[0], q[1]) > e[0] + maxDistance_) continue; // definitely outside.
    if (std::min(q[0], q[1]) < e[0] || (q - e).norm() < maxDistance_) {
    tile.indexes.push_back(i);
    }
    }
    }
    }
    // sanity check:
    for (auto& t : tiles_) {
    std::sort(t.indexes.begin(), t.indexes.end());
    // std::cout << "Tile has " << t.indexes.size() << " tiles associated." << std::endl;
    for (uint32_t i = 1; i < t.indexes.size(); ++i) {
    if (t.indexes[i - 1] == t.indexes[i]) {
    std::cout << "found duplicate!" << std::endl;
    }
    }
    }
    uint32_t tileCount = 0;
    for (uint32_t i = 0; i < uint32_t(numTiles_.x()); ++i) {
    for (uint32_t j = 0; j < uint32_t(numTiles_.y()); ++j) {
    auto& tile = tiles_[tileIdxToOffset(i, j)];
    std::sort(tile.indexes.begin(), tile.indexes.end());
    if (tile.indexes.size() > 0) tileCount += 1;
    }
    }
    std::cout << "#tiles = " << tileCount << std::endl;
  2. Using the indexes you can now open the point cloud files and copy the points accordingly. Depending on the center and the extent of the tile, it is possible to determine which points should go in.

I hope this helps (even though quite late now. Sorry again...)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants