open3D源码分析第十一篇

2021SC@SDUSC

open3D Image和RGBDImage

本篇对open3D中的image、RGBDImage和ImageIO的源码进行分析。

Image

image类存储具有可自定义的宽度、高度、通道数和每个通道的字节数的图像。

  • 注意点:

    1. 由于需要解析的代码较多,为使代码解读更加清晰,我将代码分析的详细过程写在代码段的注释中。
    2. 中文部分是源码解读,包含代码分析和遇到的问题。
  • 类中部分关键函数和相关知识:

    1. FloatValueAt:用于访问一个(单通道)浮点图像的双线性插值浮点值的函数。
    2. CreateDepthToCameraDistanceMultiplierFloatImage:函数,创建一个浮点型图像组成的乘数,将深度值转换为相机距离。
      乘数函数 M ( u , v ) M(u,v) M(u,v)定义为: M ( u , v ) = 1 + ( ( u − c x ) / f x ) 2 + ( ( v − c y ) / f y ) 2 M(u, v) = \sqrt{1 + ((u - cx) / fx) ^ 2 + ((v - cy) / fy) ^ 2} M(u,v)=1+((ucx)/fx)2+((vcy)/fy)2
#pragma once

#include <Eigen/Core>
#include <memory>
#include <vector>

#include "open3d/geometry/Geometry2D.h"
#include "open3d/utility/Logging.h"

namespace open3d {

namespace camera {
class PinholeCameraIntrinsic;
}

namespace geometry {

class Image;

/// Typedef and functions for ImagePyramid.
typedef std::vector<std::shared_ptr<Image>> ImagePyramid;

/// Image类存储的图像具有可定制的宽度,高度,通道数量和每个通道的字节数。
class Image : public Geometry2D {
public:
    /// \enum ColorToIntensityConversionType
    /// 指定R、G、B通道转换为强度时权重是否相同。只用于图像与3通道。
    /// 当使用“加权”时,R、G、B通道根据ITU BT.601数字标准加权:I = 0.299 * R + 0.587 * G + 0.114 * B。
    enum class ColorToIntensityConversionType {
        ///R, G, B信道的权值相等。
        Equal,
        ///加权R, G, B通道:I = 0.299 * R + 0.587 * G + 0.114 * B。
        Weighted,
    };

    /// \enum FilterType
    ///
    /// 指定图像过滤器类型。
    enum class FilterType {
        ///高斯滤波器的大小为3 x 3。
        Gaussian3,
        ///高斯滤波器的大小为5 x 5。
        Gaussian5,
        ///高斯滤波器的大小为7 x 7。
        Gaussian7,
        ///沿x轴的Sobel滤波器。
        Sobel3Dx,
        ///沿y轴的Sobel滤波器。
        Sobel3Dy
    };

public:
    ///默认构造函数。
    Image() : Geometry2D(Geometry::GeometryType::Image) {}
    ~Image() override {}

public:
    Image &Clear() override;
    bool IsEmpty() const override;
    Eigen::Vector2d GetMinBound() const override;
    Eigen::Vector2d GetMaxBound() const override;

    /// 测试坐标(u, v)是否位于图像的inner_marge中。
    ///
    /// 参数u 沿宽度维度的坐标。
    /// 参数v 沿着高度维度坐标。
    /// 参数inner_margin inner_margin图像边界的内缘。
    /// 如果坐标(u, v)位于图像的inner_marge中,返回true。
    bool TestImageBoundary(double u, double v, double inner_margin = 0.0) const;

public:
    ///如果图像有有效数据,返回true。
    virtual bool HasData() const {
        return width_ > 0 && height_ > 0 &&
               data_.size() == size_t(height_ * BytesPerLine());
    }

    ///预设图像属性并分配图像缓冲区。
    Image &Prepare(int width,
                   int height,
                   int num_of_channels,
                   int bytes_per_channel) {
        width_ = width;
        height_ = height;
        num_of_channels_ = num_of_channels;
        bytes_per_channel_ = bytes_per_channel;
        AllocateDataBuffer();
        return *this;
    }

    ///返回每行(行或宽度)的数据大小(以字节为单位)。
    int BytesPerLine() const {
        return width_ * num_of_channels_ * bytes_per_channel_;
    }

    ///函数,用于访问一个(单通道)浮点图像的双线性插值浮点值。
	///返回一个元组,其中第一个bool值表示u,v坐标是否在图像尺寸内,第二个double值是插值的像素值。
    std::pair<bool, double> FloatValueAt(double u, double v) const;

    ///工厂函数,创建一个浮动图像组成的乘数,将深度值转换为相机距离。
    ///乘数函数M(u,v)定义为:M(u, v) = sqrt(1 + ((u - cx) / fx) ^ 2 + ((v - cy) / fy) ^ 2)。
    ///这个函数被用作容量集成中性能优化的优化函数(详见Core/ integration /TSDFVolume.h)。
    static std::shared_ptr<Image>
    CreateDepthToCameraDistanceMultiplierFloatImage(
            const camera::PinholeCameraIntrinsic &intrinsic);

    ///返回一个灰度缩放浮点类型的图像。
    std::shared_ptr<Image> CreateFloatImage(
            Image::ColorToIntensityConversionType type =
                    Image::ColorToIntensityConversionType::Weighted) const;

    ///函数,访问单通道图像的原始数据。
    template <typename T>
    T *PointerAt(int u, int v) const;

    ///函数,访问多通道图像的原始数据。
    template <typename T>
    T *PointerAt(int u, int v, int ch) const;

    ///重新解释内部数据缓冲区。结果类型的大小必须与bytes_per_channel_相同。这类似于PointerAt<T>(0, 0)。
    template <class T>
    T *PointerAs() const {
        if (sizeof(T) != bytes_per_channel_) {
            utility::LogError("sizeof(T) != byte_per_channel_: {} != {}.",
                              sizeof(T), bytes_per_channel_);
        }
        return (T *)(data_.data());
    }

    std::shared_ptr<Image> ConvertDepthToFloatImage(
            double depth_scale = 1000.0, double depth_trunc = 3.0) const;

    std::shared_ptr<Image> Transpose() const;

    ///函数,水平翻转图像(从左到右)。
    std::shared_ptr<Image> FlipHorizontal() const;
	///函数,垂直翻转图像。
    std::shared_ptr<Image> FlipVertical() const;

    ///函数,使用预定义的滤波类型对图像进行滤波操作。
    std::shared_ptr<Image> Filter(Image::FilterType type) const;

    ///函数,滤波图像与任意dx, dy可分离滤波器。
    std::shared_ptr<Image> Filter(const std::vector<double> &dx,
                                  const std::vector<double> &dy) const;

    std::shared_ptr<Image> FilterHorizontal(
            const std::vector<double> &kernel) const;

    ///函数,对2x图像使用简单的2x2平均进行downsample(下采样)。
    std::shared_ptr<Image> Downsample() const;

    ///函数,扩展8位掩码映射。
    std::shared_ptr<Image> Dilate(int half_kernel_size = 1) const;

    ///函数,线性变换像素强度。
    ///算法是:image_new = scale * image + offset.
    Image &LinearTransform(double scale = 1.0, double offset = 0.0);

    ///剪切像素强度的函数。
    ///
    ///参数min是下限。
    ///参数max是上限。
    Image &ClipIntensity(double min = 0.0, double max = 1.0);

    ///更改为特定用途制作的图像的数据类型的函数,如单通道浮动图像->8位RGB或16位深度图像。
    template <typename T>
    std::shared_ptr<Image> CreateImageFromFloatImage() const;

    ///函数,过滤图像金字塔(image pyramid)。
    static ImagePyramid FilterPyramid(const ImagePyramid &input,
                                      Image::FilterType type);

    ///函数,创建图像金字塔。
    ImagePyramid CreatePyramid(size_t num_of_levels,
                               bool with_gaussian_filter = true) const;

    ///函数,从深度图像创建深度地图边界掩码。
    std::shared_ptr<Image> CreateDepthBoundaryMask(
            double depth_threshold_for_discontinuity_check = 0.1,
            int half_dilation_kernel_size_for_discontinuity_map = 3) const;

protected:
    void AllocateDataBuffer() {
        data_.resize(width_ * height_ * num_of_channels_ * bytes_per_channel_);
    }

public:
    ///图像的宽度。
    int width_ = 0;
    ///图像的高度。
    int height_ = 0;
    ///图像中的通道数量。
    int num_of_channels_ = 0;
    ///每个通道字节数。
    int bytes_per_channel_ = 0;
    ///图像存储缓冲区。
    std::vector<uint8_t> data_;
};

}  // namespace geometry
}  // namespace open3d

RGBDImage

RGBDImage用于从同一视角观察具有相同分辨率的一对配准颜色和深度图像。如果有其他格式,需要先转换它。

#pragma once

#include "open3d/geometry/Geometry2D.h"
#include "open3d/geometry/Image.h"

namespace open3d {
namespace geometry {

class RGBDImage;

/// Typedef and functions for RGBDImagePyramid
typedef std::vector<std::shared_ptr<RGBDImage>> RGBDImagePyramid;

/// \class RGBDImage
///RGBDImage类用于一对配准的颜色和深度图像。
///
///从相同的视角看,具有相同的分辨率。
///如果有其他格式,需要先转换它。
class RGBDImage : public Geometry2D {
public:
    ///默认构造函数。
    RGBDImage() : Geometry2D(Geometry::GeometryType::RGBDImage) {}
	///参数化构造函数。
	///参数color 彩色图像。
	///参数depth 深度图像。
    RGBDImage(const Image &color, const Image &depth)
        : Geometry2D(Geometry::GeometryType::RGBDImage),
          color_(color),
          depth_(depth) {}

    ~RGBDImage() override {
        color_.Clear();
        depth_.Clear();
    };

    RGBDImage &Clear() override;
    bool IsEmpty() const override;
    Eigen::Vector2d GetMinBound() const override;
    Eigen::Vector2d GetMaxBound() const override;

    ///工厂函数,由颜色和深度图像创建一个RGBD图像。
    ///
    ///参数color 颜色图像。
    ///参数depth 深度图像。
    ///参数depth_scale 缩放深度值的比例。深度将首先被缩放,然后被截断。
    ///参数depth_trunc 大于depth_trunc的深度值被截断为0。深度值将首先被缩放,然后被截断。
    ///参数convert_rgb_to_intensity 判断是否将RGB图像转换为强度图像。
    static std::shared_ptr<RGBDImage> CreateFromColorAndDepth(
            const Image &color,
            const Image &depth,
            double depth_scale = 1000.0,
            double depth_trunc = 3.0,
            bool convert_rgb_to_intensity = true);

    ///从Redwood数据集创建一个RGBD图像的工厂函数。
    ///参数color 颜色图像。
    ///参数depth 深度图像。
	///参数convert_rgb_to_intensity 判断是否将RGB图像转换为强度图像。
    static std::shared_ptr<RGBDImage> CreateFromRedwoodFormat(
            const Image &color,
            const Image &depth,
            bool convert_rgb_to_intensity = true);

    ///工厂函数,根据TUM数据集创建一个RGBD图像。
    ///参数color 颜色图像。
    ///参数depth 深度图像。
	///参数convert_rgb_to_intensity 判断是否将RGB图像转换为强度图像。
    static std::shared_ptr<RGBDImage> CreateFromTUMFormat(
            const Image &color,
            const Image &depth,
            bool convert_rgb_to_intensity = true);

    ///根据SUN3D数据集创建一个RGBD图像的工厂函数。
    ///参数color 颜色图像。
    ///参数depth 深度图像。
	///参数convert_rgb_to_intensity 判断是否将RGB图像转换为强度图像。
    static std::shared_ptr<RGBDImage> CreateFromTUMFormat(
    static std::shared_ptr<RGBDImage> CreateFromSUNFormat(
            const Image &color,
            const Image &depth,
            bool convert_rgb_to_intensity = true);

    ///工厂函数,根据NYU数据集创建一个RGBD图像。
    ///参数color 颜色图像。
    ///参数depth 深度图像。
	///参数convert_rgb_to_intensity 判断是否将RGB图像转换为强度图像。
    static std::shared_ptr<RGBDImage> CreateFromNYUFormat(
            const Image &color,
            const Image &depth,
            bool convert_rgb_to_intensity = true);

    static RGBDImagePyramid FilterPyramid(
            const RGBDImagePyramid &rgbd_image_pyramid, Image::FilterType type);

    RGBDImagePyramid CreatePyramid(
            size_t num_of_levels,
            bool with_gaussian_filter_for_color = true,
            bool with_gaussian_filter_for_depth = false) const;

public:
    ///color 颜色图像。
    Image color_;
    ///depth 深度图像。
    Image depth_;
};

}  // namespace geometry
}  // namespace open3d

ImageIO

#pragma once

#include <string>

#include "open3d/geometry/Image.h"

namespace open3d {
namespace io {

///从一个文件创建一个映像的工厂函数。
///如果读取失败,返回一个空图像。
std::shared_ptr<geometry::Image> CreateImageFromFile(
        const std::string &filename);

///从文件中读取Image的一般入口。
///该函数基于扩展名filename调用read函数。
///如果读取成功,返回true,否则返回false。
bool ReadImage(const std::string &filename, geometry::Image &image);

constexpr int kOpen3DImageIODefaultQuality = -1;

///将Image写入文件的一般入口。
///函数调用基于扩展名filename的write函数。
///如果write函数支持质量,则使用参数。否则将被忽略。
///参数quality: PNG: [0-9] <=2 快速写入,用于存储中间数据
///                        >=3 (默认)正常写入平衡速度和文件大小
///                 JPEG: [0-100]
///						  通常在[70,95]。
///                       90是默认值(高质量)。
///如果写入成功,返回true,否则返回false。
bool WriteImage(const std::string &filename,
                const geometry::Image &image,
                int quality = kOpen3DImageIODefaultQuality);

bool ReadImageFromPNG(const std::string &filename, geometry::Image &image);

bool WriteImageToPNG(const std::string &filename,
                     const geometry::Image &image,
                     int quality = kOpen3DImageIODefaultQuality);

bool ReadImageFromJPG(const std::string &filename, geometry::Image &image);

bool WriteImageToJPG(const std::string &filename,
                     const geometry::Image &image,
                     int quality = kOpen3DImageIODefaultQuality);

}  // namespace io
}  // namespace open3d

小结

本篇对Image和RGBDImage的两个基类,以及Image输入输出类进行了源码分析,使我了解了open3D对图像的处理方法,接下来是对open3D中关于BoundingVolume(包围盒)和HalfEdge(半边)相关知识的源码分析,以及对Mesh类和RGBD图像类相关知识的应用。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值