深圳h5网站公司,软文推广是什么意思,房山手机网站建设,哪些婚庆公司比较好球面投影的几何背景
什么是球面投影#xff1f;
球面投影将 2D 图像中的像素点#xff08;通常是平面#xff09;映射到一个虚拟的球面上#xff0c;再将球面上的角度#xff08;经度、纬度#xff09;展开到平面图上。它是广角图像拼接、全景图生成中常用的投影方法。…球面投影的几何背景
什么是球面投影
球面投影将 2D 图像中的像素点通常是平面映射到一个虚拟的球面上再将球面上的角度经度、纬度展开到平面图上。它是广角图像拼接、全景图生成中常用的投影方法。 与圆柱投影Cylinder Projection不同的是球面投影在水平与垂直两个方向都考虑了非线性映射适合处理超大视角的图像。 球面投影的示例代码
struct CV_EXPORTS_W_SIMPLE ProjectorBase
{ void setCameraParams(InputArray K Mat::eye(3, 3, CV_32F), InputArray R Mat::eye(3, 3, CV_32F), InputArray T Mat::zeros(3, 1, CV_32F)); float scale; // 缩放因子 float k[9]; // 相机内参矩阵K3x3 float rinv[9]; // 旋转矩阵R的逆R^{-1} float r_kinv[9]; // 矩阵乘积 R * K^{-1} float k_rinv[9]; // 矩阵乘积 K * R^{-1} float t[3]; // 平移向量T
};
这段代码定义了一个基础投影器结构体 ProjectorBase用于封装相机参数并提供统一的数据表示。它包含一个 setCameraParams 方法可用于设置相机的内参矩阵 K、旋转矩阵 R 和位移向量 T并预计算多个常用矩阵如 R⁻¹、R * K⁻¹、K * R⁻¹以提高后续图像投影效率。结构体中的变量 scale 表示图像投影缩放比例而 k、rinv、r_kinv 和 k_rinv 等数组是将矩阵展平成 float 数组以便在图像变换计算中快速访问。该结构通常作为球面、柱面等具体投影器的基类使用。
核心作用存储相机参数和预计算的变换矩阵为后续的投影变换如球面投影提供数学基础。矩阵存储方式所有 3x3 矩阵均以行优先方式展开为一维数组9 个float便于高效计算。
void ProjectorBase::setCameraParams(InputArray _K, InputArray _R, InputArray _T)
{ Mat K _K.getMat(), R _R.getMat(), T _T.getMat(); CV_Assert(K.size() Size(3, 3) K.type() CV_32F); CV_Assert(R.size() Size(3, 3) R.type() CV_32F); CV_Assert((T.size() Size(1, 3) || T.size() Size(3, 1)) T.type() CV_32F); Mat_float K_(K); k[0] K_(0,0); k[1] K_(0,1); k[2] K_(0,2); k[3] K_(1,0); k[4] K_(1,1); k[5] K_(1,2); k[6] K_(2,0); k[7] K_(2,1); k[8] K_(2,2); Mat_float Rinv R.t(); rinv[0] Rinv(0,0); rinv[1] Rinv(0,1); rinv[2] Rinv(0,2); rinv[3] Rinv(1,0); rinv[4] Rinv(1,1); rinv[5] Rinv(1,2); rinv[6] Rinv(2,0); rinv[7] Rinv(2,1); rinv[8] Rinv(2,2); Mat_float R_Kinv R * K.inv(); r_kinv[0] R_Kinv(0,0); r_kinv[1] R_Kinv(0,1); r_kinv[2] R_Kinv(0,2); r_kinv[3] R_Kinv(1,0); r_kinv[4] R_Kinv(1,1); r_kinv[5] R_Kinv(1,2); r_kinv[6] R_Kinv(2,0); r_kinv[7] R_Kinv(2,1); r_kinv[8] R_Kinv(2,2); Mat_float K_Rinv K * Rinv; k_rinv[0] K_Rinv(0,0); k_rinv[1] K_Rinv(0,1); k_rinv[2] K_Rinv(0,2); k_rinv[3] K_Rinv(1,0); k_rinv[4] K_Rinv(1,1); k_rinv[5] K_Rinv(1,2); k_rinv[6] K_Rinv(2,0); k_rinv[7] K_Rinv(2,1); k_rinv[8] K_Rinv(2,2); Mat_float T_(T.reshape(0, 3)); t[0] T_(0,0); t[1] T_(1,0); t[2] T_(2,0);
}
这段代码实现了 ProjectorBase 类中的 setCameraParams 函数用于初始化和预处理相机的内参矩阵 K、旋转矩阵 R 以及平移向量 T。函数首先检查输入矩阵的尺寸和类型是否符合要求3x3 的 K 和 R3x1 或 1x3 的 T数据类型为 CV_32F。随后将矩阵数据以逐元素的形式复制到结构体的 float 数组中如 k、rinv 等。此外它计算了 R * K⁻¹ 与 K * R⁻¹分别存储在 r_kinv 和 k_rinv 中为后续图像投影变换如前向和反向映射提供高效的线性变换支持。这种设计可在图像缝合或投影过程中大幅降低重复矩阵运算的开销。
这段函数的核心目的是 解析相机的内外参数 预计算常用的变换矩阵K⁻¹、R⁻¹、R×K⁻¹、K×R⁻¹ 将结果缓存到 float[] 数组中加快后续几何投影计算速度
这也是 OpenCV 中 RotationWarperBase 或 SphericalWarper 等投影器运行时的前提配置步骤。 struct CV_EXPORTS_W_SIMPLE SphericalProjector : ProjectorBase
{ CV_WRAP void mapForward(float x, float y, float u, float v); CV_WRAP void mapBackward(float u, float v, float x, float y);
};
SphericalProjector 继承自 ProjectorBase一个投影器基类。
它包含两个方法 mapForward: 将输入平面坐标 (x, y) 映射到球面坐标 (u, v)。mapBackward: 将球面坐标 (u, v) 映射回平面坐标 (x, y)。 CV_WRAP 是 OpenCV 的宏用于支持 Python 绑定如 Python 接口。 inline
void SphericalProjector::mapForward(float x, float y, float u, float v)
{ float x_ r_kinv[0] * x r_kinv[1] * y r_kinv[2]; float y_ r_kinv[3] * x r_kinv[4] * y r_kinv[5]; float z_ r_kinv[6] * x r_kinv[7] * y r_kinv[8]; u scale * atan2f(x_, z_); float w y_ / sqrtf(x_ * x_ y_ * y_ z_ * z_); v scale * (static_castfloat(CV_PI) - acosf(w w ? w : 0));
}
这段代码是 SphericalProjector 类中的 mapForward 函数用于将二维图像坐标 (x, y) 映射到球面投影坐标 (u, v)。首先通过预计算的矩阵 r_kinv R * K⁻¹ 将图像坐标变换到相机坐标系下的三维方向向量 (x_, y_, z_)。然后使用球面坐标变换u 表示水平方向的角度由 atan2f(x_, z_) 得到v 表示垂直方向的角度通过向量与 y 轴夹角的反余弦得到并结合缩放因子 scale 转换为像素单位。这种前向映射常用于将图像像素投影到球面上例如图像拼接或全景图生成中的几何校正步骤。
步骤
通过矩阵 r_kinv 将输入的平面点 (x, y) 转换到相机坐标系中的3D点 (x_, y_, z_)。这个矩阵是旋转矩阵的逆和内参矩阵的逆的组合。计算经度角u 使用 atan2f(x_, z_) 计算经度方位角并乘以缩放因子 scale。 计算纬度角v 首先计算点相对于球心的仰角。公式中w y_ / ||P||即点在相机坐标系中的 y 分量除以模长这相当于 sin(φ)其中 φ 是与 y 轴相关的角度。使用 acos(w) 得到纬度角然后调整为 v scale * (π - acos(w))使得 v 从0到π通常球面投影纬度范围是[-π/2, π/2]这里转换为[0, π]以符合图像坐标习惯。 处理 NaN当分母为零时 w 可能是 NaN使用 w w ? w : 0 进行判断如果 w 是 NaN则用 0 代替。 inline
void SphericalProjector::mapBackward(float u, float v, float x, float y)
{ u / scale; v / scale; float sinv sinf(static_castfloat(CV_PI) - v); float x_ sinv * sinf(u); float y_ cosf(static_castfloat(CV_PI) - v); float z_ sinv * cosf(u); float z; x k_rinv[0] * x_ k_rinv[1] * y_ k_rinv[2] * z_; y k_rinv[3] * x_ k_rinv[4] * y_ k_rinv[5] * z_; z k_rinv[6] * x_ k_rinv[7] * y_ k_rinv[8] * z_; if (z 0) { x / z; y / z; } else x y -1;
}
这段代码是 SphericalProjector 类中的 mapBackward 函数用于将球面投影坐标 (u, v) 反变换为图像平面坐标 (x, y)。首先将 (u, v) 除以缩放因子 scale还原为单位球坐标下的角度然后根据球面坐标公式将角度转换为三维向量 (x_, y_, z_) 表示空间方向。接着通过预计算的变换矩阵 k_rinv K * R⁻¹ 将该方向向量投影回图像平面得到 (x, y, z)。最后执行透视除法x/z, y/z得到标准图像坐标。如果 z ≤ 0说明方向指向相机背后不可见函数将 (x, y) 设为 -1。该函数常用于生成图像投影反向映射表在图像拼接、全景重建等应用中至关重要。 步骤
将输入的 u, v 还原为弧度值除以缩放因子 scale。从球面坐标重建3D点 sinv sin(π - v) sin(v)因为 v 是正向映射中计算为 π - φ所以这里 π - v 就是 φ。构建点(x_, y_, z_) (sinv * sin(u), cos(φ), sinv * cos(u))。注意这里 y_ 直接是 cos(π - v) -cos(v)但正向映射中 w y_ / ||P|| 相当于 sin(φ)。这里实际上是 x_ sin(φ) * sin(θ)y_ cos(φ) (因为 φ 是与 y 轴的夹角)z_ sin(φ) * cos(θ) 其中 φ 是纬度角θ 是经度角。 使用矩阵 k_rinv旋转矩阵和内参矩阵的组合的逆将3D点变换回平面点。进行透视除法若点在相机前方z0得到归一化平面坐标 (x/z, y/z)。若点位于相机后方z0则返回 (-1, -1) 表示无效点。 class CV_EXPORTS RotationWarper { public: virtual ~RotationWarper() {} /** brief 投影图像中的像素点 param pt 输入源图像中的点 param K 相机内参矩阵 param R 相机旋转矩阵 return 投影后的点例如球面、柱面上的位置 */ virtual Point2f warpPoint(const Point2f pt, InputArray K, InputArray R) 0; /** brief 将投影点反向映射回图像坐标 param pt 投影后的点 param K 相机内参矩阵 param R 相机旋转矩阵 return 反向映射回原图像的点 */ #if CV_VERSION_MAJOR 4 virtual Point2f warpPointBackward(const Point2f pt, InputArray K, InputArray R) { CV_UNUSED(pt); CV_UNUSED(K); CV_UNUSED(R); CV_Error(Error::StsNotImplemented, ); } #else virtual Point2f warpPointBackward(const Point2f pt, InputArray K, InputArray R) 0; #endif /** brief 构建投影映射表map用于图像重映射 param src_size 输入图像尺寸 param K 相机内参矩阵 param R 相机旋转矩阵 param xmap 输出的 x 方向映射表 param ymap 输出的 y 方向映射表 return 投影图像的最小外接矩形区域ROI */ virtual Rect buildMaps(Size src_size, InputArray K, InputArray R, OutputArray xmap, OutputArray ymap) 0; /** brief 对图像进行投影变换 param src 输入图像 param K 相机内参矩阵 param R 相机旋转矩阵 param interp_mode 插值方式如双线性、最近邻 param border_mode 边缘扩展模式如边缘复制、常量填充 param dst 输出变换后的图像 return 变换后图像的左上角坐标 */ virtual Point warp(InputArray src, InputArray K, InputArray R, int interp_mode, int border_mode, CV_OUT OutputArray dst) 0; /** brief 对图像进行反向投影变换 param src 已投影后的图像 param K 相机内参矩阵 param R 相机旋转矩阵 param interp_mode 插值方式 param border_mode 边缘扩展模式 param dst_size 反向投影图像的尺寸 param dst 输出的反向变换图像 */ virtual void warpBackward(InputArray src, InputArray K, InputArray R, int interp_mode, int border_mode, Size dst_size, CV_OUT OutputArray dst) 0; /** brief 获取投影图像的 ROI 区域外接矩形 param src_size 输入图像尺寸 param K 相机内参矩阵 param R 相机旋转矩阵 return 投影图像的最小外接矩形区域 */ virtual Rect warpRoi(Size src_size, InputArray K, InputArray R) 0; /// 获取球面/柱面投影缩放因子 virtual float getScale() const { return 1.f; } /// 设置缩放因子 virtual void setScale(float) {} };
这段代码定义了一个抽象类 RotationWarper是 OpenCV 图像拼接模块中的核心接口之一主要用于处理图像在不同相机姿态下的旋转投影变换。它为各种具体的投影方式如球面投影、柱面投影提供统一的接口封装包括点的前向/反向投影warpPoint 和 warpPointBackward、图像投影与反投影warp 和 warpBackward、构建映射表buildMaps、计算投影区域warpRoi以及设置缩放比例setScale。该类通过纯虚函数设计要求派生类根据具体投影模型实现对应功能是 OpenCV 中实现多视角图像配准和拼接如全景图的基础组件。 /** brief Base class for rotation-based warper using a detail::ProjectorBase_ derived class. */
template class P
class CV_EXPORTS_TEMPLATE RotationWarperBase : public RotationWarper
{
public: Point2f warpPoint(const Point2f pt, InputArray K, InputArray R) CV_OVERRIDE; Point2f warpPointBackward(const Point2f pt, InputArray K, InputArray R) CV_OVERRIDE; Rect buildMaps(Size src_size, InputArray K, InputArray R, OutputArray xmap, OutputArray ymap) CV_OVERRIDE; Point warp(InputArray src, InputArray K, InputArray R, int interp_mode, int border_mode, OutputArray dst) CV_OVERRIDE; void warpBackward(InputArray src, InputArray K, InputArray R, int interp_mode, int border_mode, Size dst_size, OutputArray dst) CV_OVERRIDE; Rect warpRoi(Size src_size, InputArray K, InputArray R) CV_OVERRIDE; float getScale() const CV_OVERRIDE{ return projector_.scale; } void setScale(float val) CV_OVERRIDE { projector_.scale val; } protected: // Detects ROI of the destination image. Its correct for any projection. virtual void detectResultRoi(Size src_size, Point dst_tl, Point dst_br); // Detects ROI of the destination image by walking over image border. // Correctness for any projection isnt guaranteed. void detectResultRoiByBorder(Size src_size, Point dst_tl, Point dst_br); P projector_;
};
这段代码定义了一个模板基类 RotationWarperBaseP是 RotationWarper 接口的具体实现框架适用于基于旋转变换的图像投影操作。它以模板参数 P 作为具体的投影器如 SphericalProjector、CylindricalProjector实例通过封装和调用 projector_ 中定义的投影逻辑如 mapForward、mapBackward实现图像点的正向/反向映射、构建映射表、执行图像投影、计算变换后的图像区域等功能。该类为各种具体投影器提供统一的实现基础既具备通用性也便于在 OpenCV 图像拼接中扩展不同的投影模型。它将相机内参 K、旋转矩阵 R 封装为投影器的参数使图像在全景拼接、视角变换等任务中能实现精准的几何变换。 template class P
Point2f RotationWarperBaseP::warpPoint(const Point2f pt, InputArray K, InputArray R)
{ projector_.setCameraParams(K, R); Point2f uv; projector_.mapForward(pt.x, pt.y, uv.x, uv.y); return uv;
}
这段代码是 RotationWarperBase 模板类中 warpPoint 方法的实现它接收一个图像点 pt相机内参矩阵 K 和旋转矩阵 R首先通过 projector_ 设置这些相机参数然后调用其 mapForward 方法将输入图像点 (x, y) 投影到目标图像坐标 (u, v)最终返回变换后的点 uv。该函数实现了图像点在相机旋转作用下的前向几何映射是图像配准和拼接中关键的投影步骤。 template class P
Point2f RotationWarperBaseP::warpPointBackward(const Point2f pt, InputArray K, InputArray R)
{ projector_.setCameraParams(K, R); Point2f xy; projector_.mapBackward(pt.x, pt.y, xy.x, xy.y); return xy;
}
这段代码是模板类 RotationWarperBaseP 中 warpPointBackward 函数的实现它用于执行图像坐标的反向投影操作。函数接受一个图像点 pt通常是投影图像中的坐标以及相机的内参矩阵 K 和旋转矩阵 R。它首先调用 projector_ 设置相机参数然后通过 projector_ 的 mapBackward 方法将该点 (u, v) 映射回原始图像中的点 (x, y)最终返回这个反投影后的点。该函数常用于图像反向映射如反变形或从输出图像推回源图像坐标在图像拼接和图像重建中尤为重要。 template class P
Rect RotationWarperBaseP::buildMaps(Size src_size, InputArray K, InputArray R, OutputArray _xmap, OutputArray _ymap)
{ projector_.setCameraParams(K, R); Point dst_tl, dst_br; detectResultRoi(src_size, dst_tl, dst_br); _xmap.create(dst_br.y - dst_tl.y 1, dst_br.x - dst_tl.x 1, CV_32F); _ymap.create(dst_br.y - dst_tl.y 1, dst_br.x - dst_tl.x 1, CV_32F); Mat xmap _xmap.getMat(), ymap _ymap.getMat(); float x, y; for (int v dst_tl.y; v dst_br.y; v) { for (int u dst_tl.x; u dst_br.x; u) { projector_.mapBackward(static_castfloat(u), static_castfloat(v), x, y); xmap.atfloat(v - dst_tl.y, u - dst_tl.x) x; ymap.atfloat(v - dst_tl.y, u - dst_tl.x) y; } } return Rect(dst_tl, dst_br);
}
这段代码实现了一个模板函数 buildMaps它是图像旋转投影类 RotationWarperBaseP 的核心方法之一主要作用是根据输入图像的尺寸 src_size相机的内参矩阵 K 和旋转矩阵 R构建两个映射表 xmap 和 ymap分别对应每个目标图像像素在源图像上的横纵坐标。该函数首先设置投影器参数然后通过 detectResultRoi 计算投影后图像的边界区域接着为映射表分配内存并遍历该区域的每个像素使用投影器的 mapBackward 方法将目标像素反投影回源图像坐标系并记录结果到 xmap 和 ymap 中。最终返回投影图像的边界矩形 Rect(dst_tl, dst_br)供后续拼接或重采样使用。这个函数在图像缝合、投影转换等视觉任务中非常关键。
template class P
Point RotationWarperBaseP::warp(InputArray src, InputArray K, InputArray R, int interp_mode, int border_mode, OutputArray dst)
{ UMat xmap, ymap; Rect dst_roi buildMaps(src.size(), K, R, xmap, ymap); dst.create(dst_roi.height 1, dst_roi.width 1, src.type()); remap(src, dst, xmap, ymap, interp_mode, border_mode); return dst_roi.tl();
}
这段模板函数 warp 是 RotationWarperBaseP 类中的图像正向投影实现它根据给定的源图像 src、相机内参 K、旋转矩阵 R 以及插值方式 interp_mode 和边界处理方式 border_mode对输入图像进行仿射或旋转投影变换。函数内部首先通过 buildMaps 构建反向映射表 xmap 和 ymap确定目标图像的像素在源图像中的对应位置然后创建目标图像 dst 的空间并调用 remap 函数按照映射关系将源图像重新采样到目标图像中。最终返回的是目标图像左上角的坐标 dst_roi.tl()常用于图像拼接时确定偏移。整个流程适用于基于旋转的图像投影变换如全景拼接或视图重映射。
template class P
void RotationWarperBaseP::warpBackward(InputArray src, InputArray K, InputArray R, int interp_mode, int border_mode, Size dst_size, OutputArray dst)
{ projector_.setCameraParams(K, R); Point src_tl, src_br; detectResultRoi(dst_size, src_tl, src_br); Size size src.size(); CV_Assert(src_br.x - src_tl.x 1 size.width src_br.y - src_tl.y 1 size.height); Mat xmap(dst_size, CV_32F); Mat ymap(dst_size, CV_32F); float u, v; for (int y 0; y dst_size.height; y) { for (int x 0; x dst_size.width; x) { projector_.mapForward(static_castfloat(x), static_castfloat(y), u, v); xmap.atfloat(y, x) u - src_tl.x; ymap.atfloat(y, x) v - src_tl.y; } } dst.create(dst_size, src.type()); remap(src, dst, xmap, ymap, interp_mode, border_mode);
}
这段模板函数 warpBackward 实现了图像的反向投影变换Backward Warping即将目标图像坐标反投影到原图像上从而实现视图重建。函数接受源图像 src相机内参 K旋转矩阵 R插值方式 interp_mode边界处理方式 border_mode 以及目标图像尺寸 dst_size。它首先设置投影参数然后通过 detectResultRoi 推断源图像的边界并构造反向映射表 xmap 和 ymap对于目标图中每个像素 (x, y)通过 mapForward 得到它在源图中的位置 (u, v)并记录偏移。最后调用 remap 进行插值采样生成重建后的目标图像 dst。此方法常用于图像去畸变、全景图像还原等场景。
template class P
Rect RotationWarperBaseP::warpRoi(Size src_size, InputArray K, InputArray R)
{ projector_.setCameraParams(K, R); Point dst_tl, dst_br; detectResultRoi(src_size, dst_tl, dst_br); return Rect(dst_tl, Point(dst_br.x 1, dst_br.y 1));
}
这段模板函数 warpRoi 用于计算输入图像经过旋转投影变换后在目标图像中的最小包围矩形区域ROI。它首先通过 setCameraParams 设置相机的内参矩阵 K 和旋转矩阵 R然后调用 detectResultRoi 函数计算变换后图像的左上角 dst_tl 和右下角 dst_br 坐标最后构造一个 Rect 对象返回表示该区域。注意这里右下角坐标加了 1以确保矩形包含所有变换后的像素。这在拼接图像或预分配内存时非常关键。
template class P
void RotationWarperBaseP::detectResultRoi(Size src_size, Point dst_tl, Point dst_br)
{ float tl_uf (std::numeric_limitsfloat::max)(); float tl_vf (std::numeric_limitsfloat::max)(); float br_uf -(std::numeric_limitsfloat::max)(); float br_vf -(std::numeric_limitsfloat::max)(); float u, v; for (int y 0; y src_size.height; y) { for (int x 0; x src_size.width; x) { projector_.mapForward(static_castfloat(x), static_castfloat(y), u, v); tl_uf (std::min)(tl_uf, u); tl_vf (std::min)(tl_vf, v); br_uf (std::max)(br_uf, u); br_vf (std::max)(br_vf, v); } } dst_tl.x static_castint(tl_uf); dst_tl.y static_castint(tl_vf); dst_br.x static_castint(br_uf); dst_br.y static_castint(br_vf);
}
这段模板函数 detectResultRoi 的作用是计算经过投影变换后图像的最小包围矩形ROI。函数通过遍历原始图像 src_size 中的每一个像素点 (x, y)使用 projector_.mapForward 将其投影到目标图像坐标系 (u, v)并记录所有投影点中的最小和最大坐标以此确定变换后图像的左上角 dst_tl 和右下角 dst_br。这些点组成的矩形就是变换后图像的边界用于后续图像重映射remap或图像拼接等操作。这是计算投影范围的基础步骤之一。
template class P
void RotationWarperBaseP::detectResultRoiByBorder(Size src_size, Point dst_tl, Point dst_br)
{ float tl_uf (std::numeric_limitsfloat::max)(); float tl_vf (std::numeric_limitsfloat::max)(); float br_uf -(std::numeric_limitsfloat::max)(); float br_vf -(std::numeric_limitsfloat::max)(); float u, v; for (float x 0; x src_size.width; x) { projector_.mapForward(static_castfloat(x), 0, u, v); tl_uf (std::min)(tl_uf, u); tl_vf (std::min)(tl_vf, v); br_uf (std::max)(br_uf, u); br_vf (std::max)(br_vf, v); projector_.mapForward(static_castfloat(x), static_castfloat(src_size.height - 1), u, v); tl_uf (std::min)(tl_uf, u); tl_vf (std::min)(tl_vf, v); br_uf (std::max)(br_uf, u); br_vf (std::max)(br_vf, v); } for (int y 0; y src_size.height; y) { projector_.mapForward(0, static_castfloat(y), u, v); tl_uf (std::min)(tl_uf, u); tl_vf (std::min)(tl_vf, v); br_uf (std::max)(br_uf, u); br_vf (std::max)(br_vf, v); projector_.mapForward(static_castfloat(src_size.width - 1), static_castfloat(y), u, v); tl_uf (std::min)(tl_uf, u); tl_vf (std::min)(tl_vf, v); br_uf (std::max)(br_uf, u); br_vf (std::max)(br_vf, v); } dst_tl.x static_castint(tl_uf); dst_tl.y static_castint(tl_vf); dst_br.x static_castint(br_uf); dst_br.y static_castint(br_vf);
}
这段代码 detectResultRoiByBorder 是 RotationWarperBase 模板类的一个成员函数用于估算投影变换后的目标图像区域的最小包围矩形ROI但它只考虑了图像边界上的像素点因此精度可能略低于完全遍历图像像素的方法 detectResultRoi。函数通过对源图像的四条边顶边、底边、左边、右边进行 mapForward 投影变换计算变换后所有边缘点的最小左上和最大右下坐标并据此构造 ROI 区域 dst_tl 到 dst_br。这种方法计算量较小适合对 ROI 精度要求不是很高的场景。 class CV_EXPORTS SphericalWarper : public RotationWarperBaseSphericalProjector
{
public: /** brief Construct an instance of the spherical warper class. param scale Radius of the projected sphere, in pixels. An image spanning the whole sphere will have a width of 2 * scale * PI pixels. */ SphericalWarper(float scale) { projector_.scale scale; } Rect buildMaps(Size src_size, InputArray K, InputArray R, OutputArray xmap, OutputArray ymap) CV_OVERRIDE; Point warp(InputArray src, InputArray K, InputArray R, int interp_mode, int border_mode, OutputArray dst) CV_OVERRIDE;
protected: void detectResultRoi(Size src_size, Point dst_tl, Point dst_br) CV_OVERRIDE;
};
这段代码定义了一个 SphericalWarper 类它继承自 RotationWarperBaseSphericalProjector用于实现球面图像投影变换。构造函数中通过 scale 参数设定球面投影的半径用于控制输出图像的分辨率。该类重载了 buildMaps() 和 warp() 方法分别用于生成投影映射图xmap/ymap和将源图像进行球面投影变换同时还重载了 detectResultRoi() 方法用于更精确地计算球面变换后图像的目标区域。这个类是 OpenCV 图像拼接模块中用于将图像映射到球面坐标系的关键组件常用于处理宽视角全景图像的对齐与合成。
void SphericalWarper::detectResultRoi(Size src_size, Point dst_tl, Point dst_br)
{ detectResultRoiByBorder(src_size, dst_tl, dst_br); float tl_uf static_castfloat(dst_tl.x); float tl_vf static_castfloat(dst_tl.y); float br_uf static_castfloat(dst_br.x); float br_vf static_castfloat(dst_br.y); float x projector_.rinv[1]; float y projector_.rinv[4]; float z projector_.rinv[7]; if (y 0.f) { float x_ (projector_.k[0] * x projector_.k[1] * y) / z projector_.k[2]; float y_ projector_.k[4] * y / z projector_.k[5]; if (x_ 0.f x_ src_size.width y_ 0.f y_ src_size.height) { tl_uf std::min(tl_uf, 0.f); tl_vf std::min(tl_vf, static_castfloat(CV_PI * projector_.scale)); br_uf std::max(br_uf, 0.f); br_vf std::max(br_vf, static_castfloat(CV_PI * projector_.scale)); } } x projector_.rinv[1]; y -projector_.rinv[4]; z projector_.rinv[7]; if (y 0.f) { float x_ (projector_.k[0] * x projector_.k[1] * y) / z projector_.k[2]; float y_ projector_.k[4] * y / z projector_.k[5]; if (x_ 0.f x_ src_size.width y_ 0.f y_ src_size.height) { tl_uf std::min(tl_uf, 0.f); tl_vf std::min(tl_vf, static_castfloat(0)); br_uf std::max(br_uf, 0.f); br_vf std::max(br_vf, static_castfloat(0)); } } dst_tl.x static_castint(tl_uf); dst_tl.y static_castint(tl_vf); dst_br.x static_castint(br_uf); dst_br.y static_castint(br_vf);
}
这段代码是 SphericalWarper::detectResultRoi 的实现用于精确计算球面投影后图像的目标区域ROI。它首先调用 detectResultRoiByBorder() 获取一个初始边界框然后进一步考虑图像在球面投影下的可视范围并根据投影中心方向是否朝上/下修正边界框。
关键步骤如下 使用 detectResultRoiByBorder 得到初步的左上 (dst_tl) 和右下 (dst_br) 投影边界点。 提取旋转矩阵的逆矩阵中与 Y 轴方向相关的向量判断相机朝向。 根据相机是否朝上 (y 0) 或朝下 (-y 0)计算相机视角对应图像坐标判断这些方向是否在图像有效区域内。 若在图像范围内则更新投影区域上下边界 tl_vf 和 br_vf使其完整包含可能的投影角度范围如 [0, π*scale]。 最终将浮点结果转换为整数坐标输出目标矩形区域。
这段逻辑主要用于在球面投影中修正 ROI避免丢失极端朝向下的可视区域从而保证拼接图像时视角完整、无裁剪。 Rect SphericalWarper::buildMaps(Size src_size, InputArray K, InputArray R, OutputArray xmap, OutputArray ymap)
{
#ifdef HAVE_OPENCL if (ocl::isOpenCLActivated()) { ocl::Kernel k(buildWarpSphericalMaps, ocl::stitching::warpers_oclsrc); if (!k.empty()) { int rowsPerWI ocl::Device::getDefault().isIntel() ? 4 : 1; projector_.setCameraParams(K, R); Point dst_tl, dst_br; detectResultRoi(src_size, dst_tl, dst_br); Size dsize(dst_br.x - dst_tl.x 1, dst_br.y - dst_tl.y 1); xmap.create(dsize, CV_32FC1); ymap.create(dsize, CV_32FC1); Mat k_rinv(1, 9, CV_32FC1, projector_.k_rinv); UMat uxmap xmap.getUMat(), uymap ymap.getUMat(), uk_rinv k_rinv.getUMat(ACCESS_READ); k.args(ocl::KernelArg::WriteOnlyNoSize(uxmap), ocl::KernelArg::WriteOnly(uymap), ocl::KernelArg::PtrReadOnly(uk_rinv), dst_tl.x, dst_tl.y, 1/projector_.scale, rowsPerWI); size_t globalsize[2] { (size_t)dsize.width, ((size_t)dsize.height rowsPerWI - 1) / rowsPerWI }; if (k.run(2, globalsize, NULL, true)) { CV_IMPL_ADD(CV_IMPL_OCL); return Rect(dst_tl, dst_br); } } }
#endif return RotationWarperBaseSphericalProjector::buildMaps(src_size, K, R, xmap, ymap);
}
这段代码实现了 SphericalWarper::buildMaps 方法用于为球面投影生成图像的重映射remap表即 xmap 和 ymap。其核心目的是计算球面投影后的目标图像坐标映射表以便后续使用 OpenCV 的 remap() 函数进行图像变换。
其逻辑可分为两种路径 OpenCL 加速路径条件编译启用 HAVE_OPENCL 检查是否启用 OpenCL 加速ocl::isOpenCLActivated()。 创建 OpenCL kernel buildWarpSphericalMaps定义在 OpenCV 的 warpers_oclsrc 源中。 若 kernel 成功加载 设置相机内参 K 和旋转矩阵 R。 调用 detectResultRoi() 获取投影图像的边界框。 分配 xmap 和 ymap并将投影参数打包为 OpenCL 参数传给 kernel。 运行 kernel根据线程模型自动生成 warp map。 若成功执行返回投影矩形区域。 回退到 CPU 路径当没有 OpenCL 或 kernel 加载失败 调用基类 RotationWarperBaseSphericalProjector::buildMaps() 使用 CPU 实现生成 xmap 和 ymap。
该方法优先尝试使用 OpenCL 并行计算提升投影映射生成效率若不可用则退回常规 CPU 实现。它是球面图像拼接中的关键步骤用于将图像正确投影到球面坐标系统上为后续图像缝合打下基础。
Point SphericalWarper::warp(InputArray src, InputArray K, InputArray R, int interp_mode, int border_mode, OutputArray dst)
{ UMat uxmap, uymap; Rect dst_roi buildMaps(src.size(), K, R, uxmap, uymap); dst.create(dst_roi.height 1, dst_roi.width 1, src.type()); remap(src, dst, uxmap, uymap, interp_mode, border_mode); return dst_roi.tl();
}
这段代码是 SphericalWarper::warp 方法的实现它完成了对输入图像 src 进行球面投影变换的全过程。首先调用 buildMaps 根据相机内参 K 和旋转矩阵 R 构建重映射表 uxmap 和 uymap然后使用 OpenCV 的 remap 函数将图像投影到球面坐标系下生成输出图像 dst。该方法的核心作用是基于球面模型将图像从透视投影变换到球面投影并输出映射后的图像及其左上角在全局投影图像中的位置。