Draw line primitives

Draw line primitives

在栅格图中找到两点间的栅格(栅格图坐标), 这些点是两个点构成的线段经过的栅格, 使用算法:
Bresenham's_line_algorithm.

See also:


Implementation:

NOTE: Algorithm and implementation for integer arithmetic.

/**
 * Draw the segment with a Brush instance.
 * Implements in Bresenham's line algorithm: is a line drawing algorithm
 * that determines the points of an n-dimensional raster that should be
 * selected in order to form a close approximation to a straight line
 * between two points.
 * It is commonly used to draw line primitives in a bitmap image
 * (e.g. on a computer screen), as it uses only integer addition,
 * subtraction and bit shifting, all of which are very cheap operations
 * in standard computer architectures.
 * @tparam Brush the brush to draw, operator()(x, y) requried.
 * @param brush the brush instance.
 * @note
 * - Scalar should be integer!
 * - n should >= 2, this method for 2D, only use x and y.
 * - operator()(x, y) for Brush requried.
 * @return drawed count.
 * @sa
 * - https://en.wikipedia.org/wiki/Bresenham's_line_algorithm
 * - https://en.wikipedia.org/wiki/Bresenham%27s_line_algorithm#All_cases
 * - costmap_2d/include/costmap_2d/costmap_2d.h
 */
template<typename Brush>
uint32_t draw2D(const Brush& brush) const noexcept
{
    // Tparams check
    static_assert(2 == n || 3 == n, "n should be 2 or 3");
    static_assert(
        std::is_integral<typename std::decay<Scalar>::type>::value,
        "Scalar should be integer");
    // Draw
    uint32_t ndrawed = 0;
    // Cover gradients between 0 and -1 by checking whether y needs to
    // increase or decrease (i.e. dy < 0).
    const auto draw2DLow = [&brush, &ndrawed](const P& a, const P& b) {
        Scalar dx = b(0) - a(0);
        Scalar dy = b(1) - a(1);
        Scalar yi;
        if (dy < 0) {
            yi = -1;
            dy = -dy;
        } else {
            yi = 1;
        }
        const Scalar ddx = 2 * dx;
        const Scalar ddy = 2 * dy;
        Scalar D = ddy  - dx;
        Scalar y = a(1);
        for (const Scalar& x : Range<Scalar>{ a(0), b(0) }) {
            brush(x, y);
            ++ndrawed;
            if (D > 0) {
                y += yi;
                D -= ddx;
            }
            D += ddy;
        }
    };
    // By switching the x and y axis an implementation for positive or negative
    // steep gradients can be written as:
    const auto draw2DHigh = [&brush, &ndrawed](const P& a, const P& b) {
        Scalar dx = b(0) - a(0);
        Scalar dy = b(1) - a(1);
        Scalar xi;
        if (dx < 0) {
            xi = -1;
            dx = -dx;
        } else {
            xi = 1;
        }
        const Scalar ddx = 2 * dx;
        const Scalar ddy = 2 * dy;
        Scalar D = ddx  - dy;
        Scalar x = a(0);
        for (const Scalar& y : Range<Scalar>{ a(1), b(1) }) {
            brush(x, y);
            ++ndrawed;
            if (D > 0) {
                x += xi;
                D -= ddy;
            }
            D += ddx;
        }
    };
    // Need to detect whether x1 > x0 or y1 > y0 and reverse the input
    // coordinates before drawing
    if (std::abs(this->b(1) - this->a(1)) < std::abs(this->b(0) - this->a(0))) {
        if (this->a(0) > this->b(0)) {
            draw2DLow(this->b, this->a);
        } else {
            draw2DLow(this->a, this->b);
        }
    } else {
        if (this->a(1) > this->b(1)) {
            draw2DHigh(this->b, this->a);
        } else {
            draw2DHigh(this->a, this->b);
        }
    }
    return ndrawed;
}

Test and example:

TEST(Segment, draw2D)
{
    Segment<int32_t, 2> segment{ Eigen::Vector2i{ 3, 1 }, Eigen::Vector2i{ -6, -4 } };
    const std::vector<Eigen::Vector2i> expect{
        Eigen::Vector2i{ -6, -4 },
        Eigen::Vector2i{ -5, -3 },
        Eigen::Vector2i{ -4, -3 },
        Eigen::Vector2i{ -3, -2 },
        Eigen::Vector2i{ -2, -2 },
        Eigen::Vector2i{ -1, -1 },
        Eigen::Vector2i{ 0,  -1 },
        Eigen::Vector2i{ 1,  0 },
        Eigen::Vector2i{ 2,  0 },
        Eigen::Vector2i{ 3,  1 } };
    std::vector<Eigen::Vector2i> ps;
    const auto brush = [&ps](const int32_t x, const int32_t y) {
        std::cout << x << ' ' << y << '\n';
        ps.emplace_back(Eigen::Vector2i{ x, y });
    };
    ps.clear();
    EXPECT_EQ(expect.size(), segment.draw2D(brush));
    EXPECT_EQ(expect.size(), ps.size());
    if (expect.size() == ps.size()) {
        for (uint32_t i = 0; i < ps.size(); ++i) {
            EXPECT_EQ(expect[i], ps[i]);
        }
    }
    //
    Segment<int32_t, 3> s3;
    ps.clear();
    s3.draw2D(brush);
    EXPECT_EQ(1u, ps.size());
    if (1u == ps.size()) {
        EXPECT_EQ((Eigen::Vector2i{ 0, 0 }), ps[0]);
    }
    // BAD test:
    //Segment<double, 3> badS3;
    //badS3.draw2D(brush);
}

draw2D-example.png

Project Point Cloud to depth

Project Point Cloud to depth

要投影点云到深度图首先输入的点云是 “organized” or “organizable(先处理 cloud 的 width 和 height!)”, 然后需要确定 以那个轴作为深度值或者投影到那个平面.

例如 cloud z to depth:

template<typename Point = pcl::PointXYZ, typename DT = float>
bool cloudZToDepth(
    const pcl::PointCloud<Point>& cloud,
    const bool invalidToNew,
    const DT newValue,
    cv::Mat& depth) noexcept
{
    if (!cloud.isOrganized()) {
        return false;
    }
    int32_t type;
    if (std::is_same<float, typename std::decay<DT>::type>::value) {
        type = CV_32F;
    } else if (std::is_same<double, typename std::decay<DT>::type>::value) {
        type = CV_64F;
    } else if (std::is_same<int32_t, typename std::decay<DT>::type>::value) {
        type = CV_32S;
    } else {
        return false;
    }
    const int32_t width = cloud.width;
    const int32_t height = cloud.height;
    if (std::is_floating_point<typename std::decay<DT>::type>::value) {
        depth.create(height, width, CV_MAKETYPE(type, 1));
    } else {
        depth.create(height, width, CV_MAKETYPE(type, 1));
    }
    if (invalidToNew) {
        for (int32_t y = 0; y < height; ++y) {
            for (int32_t x = 0; x < width; ++x) {
                // NOTE: cv: (row, column), BUT PCL (column, row)
                const auto dv = cloud(x, y).z;
                if (std::isfinite(dv)) {
                    depth.at<DT>(y, x) = DT(dv);
                } else {
                    depth.at<DT>(y, x) = newValue;
                }
            }
        }
    } else {
        for (int32_t y = 0; y < height; ++y) {
            for (int32_t x = 0; x < width; ++x) {
                // NOTE: cv: (row, column), BUT PCL (column, row)
                depth.at<DT>(y, x) = cloud(x, y).z;
            }
        }
    }
    return true;
}

再比如 cloud x, y, z to depth (3 channels):

template<typename Point = pcl::PointXYZ, typename CDT = float>
bool CvHelper::cloudXYZToDepth(
    const pcl::PointCloud<Point>& cloud,
    const bool invalidToNew,
    const CDT newValue,
    cv::Mat& depth,
    const CDT xFactor,
    const CDT yFactor,
    const CDT zFactor) noexcept
{
    if (!cloud.isOrganized()) {
        return false;
    }
    int32_t type;
    if (std::is_same<float, typename std::decay<CDT>::type>::value) {
        type = CV_32F;
    } else if (std::is_same<double, typename std::decay<CDT>::type>::value) {
        type = CV_64F;
    } else if (std::is_same<int32_t, typename std::decay<CDT>::type>::value) {
        type = CV_32S;
    } else {
        return false;
    }
    const int32_t width = cloud.width;
    const int32_t height = cloud.height;
    if (std::is_floating_point<typename std::decay<CDT>::type>::value) {
        depth.create(height, width, CV_MAKETYPE(type, 3));
    } else {
        depth.create(height, width, CV_MAKETYPE(type, 3));
    }
    if (invalidToNew) {
        for (int32_t y = 0; y < height; ++y) {
            for (int32_t x = 0; x < width; ++x) {
                // NOTE: cv: (row, column), BUT PCL (column, row)
                auto dv = cloud(x, y).x;
                if (std::isfinite(dv)) {
                    depth.at<cv::Vec<CDT, 3> >(y, x)[0] = CDT(dv * xFactor);
                } else {
                    depth.at<cv::Vec<CDT, 3> >(y, x)[0] = newValue;
                }
                // NOTE: cv: (row, column), BUT PCL (column, row)
                dv = cloud(x, y).y;
                if (std::isfinite(dv)) {
                    depth.at<cv::Vec<CDT, 3> >(y, x)[1] = CDT(dv * yFactor);
                } else {
                    depth.at<cv::Vec<CDT, 3> >(y, x)[1] = newValue;
                }
                // NOTE: cv: (row, column), BUT PCL (column, row)
                dv = cloud(x, y).z;
                if (std::isfinite(dv)) {
                    depth.at<cv::Vec<CDT, 3> >(y, x)[2] = CDT(dv * zFactor);
                } else {
                    depth.at<cv::Vec<CDT, 3> >(y, x)[2] = newValue;
                }
            }
        }
    } else {
        for (int32_t y = 0; y < height; ++y) {
            for (int32_t x = 0; x < width; ++x) {
                // NOTE: cv: (row, column), BUT PCL (column, row)
                depth.at<cv::Vec<CDT, 3> >(y, x)[0] = cloud(x, y).x * xFactor;
                depth.at<cv::Vec<CDT, 3> >(y, x)[1] = cloud(x, y).y * yFactor;
                depth.at<cv::Vec<CDT, 3> >(y, x)[2] = cloud(x, y).z * zFactor;
            }
        }
    }
    return true;
}

Setup core dump (Linux)

Setup core dump (Linux)

【原文】

1. sudo vim /etc/security/limits.conf

#*               soft    core            0

To

# 1GB
*               soft    core             1048576

2. Prepare coredump files path

sudo mkdir -p /corefiles && sudo chmod 1777 /corefiles

3. Set coredump enable config and file name pattern

Format of core file name

  • %% – 符号%
  • %e – 程序文件名
  • %t – 生成core文件的时间戳(seconds since 0:00h, 1 Jan 1970)
  • %p – 进程号
  • %s – 生成 core 文件时收到的信号
  • %h – 主机名
  • %u – 进程用户 id
  • %g – 进程用户组 id

Config

sudo vim /etc/sysctl.conf

kernel.core_uses_pid = 1
kernel.core_pattern = /corefiles/%e-%t-%p.coredump

Fix for ubuntu

If system is ubuntu, remove apport:

sudo apt-get purge apport -y

4. Apply system config

sudo sysctl -p

reboot

5. 查看是否启用了 core dump

ulimit -c

# Or
ulimit -a

临时启用

ulimit -c SIZE

# Or:
ulimit -c unlimited