關于球面投影SphericalProjector的介紹以及代碼開發

球面投影的幾何背景

什么是球面投影?

球面投影將 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];? ??// 相機內參矩陣K(3x3)

? ? 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?1R * K?1K * R?1)以提高后續圖像投影效率。結構體中的變量 scale 表示圖像投影縮放比例,而 krinvr_kinvk_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 的 KR,3x1 或 1x3 的 T,數據類型為 CV_32F)。隨后將矩陣數據以逐元素的形式復制到結構體的 float 數組中(如 krinv 等)。此外,它計算了 R * K?1K * R?1,分別存儲在 r_kinvk_rinv 中,為后續圖像投影變換(如前向和反向映射)提供高效的線性變換支持。這種設計可在圖像縫合或投影過程中大幅降低重復矩陣運算的開銷。

這段函數的核心目的是:

  • ?解析相機的內外參數

  • ?預計算常用的變換矩陣(K?1、R?1、R×K?1、K×R?1)

  • ?將結果緩存到 float[] 數組中,加快后續幾何投影計算速度

這也是 OpenCV 中 RotationWarperBaseSphericalWarper 等投影器運行時的前提配置步驟。

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_cast<float>(CV_PI) - acosf(w == w ? w : 0));

}

這段代碼是 SphericalProjector 類中的 mapForward 函數,用于將二維圖像坐標 (x, y) 映射到球面投影坐標 (u, v)。首先通過預計算的矩陣 r_kinv = R * K?1 將圖像坐標變換到相機坐標系下的三維方向向量 (x_, y_, z_)。然后,使用球面坐標變換:u 表示水平方向的角度(由 atan2f(x_, z_) 得到),v 表示垂直方向的角度(通過向量與 y 軸夾角的反余弦得到),并結合縮放因子 scale 轉換為像素單位。這種前向映射常用于將圖像像素投影到球面上,例如圖像拼接或全景圖生成中的幾何校正步驟。

?步驟

  1. 通過矩陣 r_kinv 將輸入的平面點 (x, y) 轉換到相機坐標系中的3D點 (x_, y_, z_)。這個矩陣是旋轉矩陣的逆和內參矩陣的逆的組合。
  2. 計算經度角(u):
    • 使用 atan2f(x_, z_) 計算經度(方位角),并乘以縮放因子 scale
  3. 計算緯度角(v):
    • 首先計算點相對于球心的仰角。公式中,w = y_ / ||P||(即點在相機坐標系中的 y 分量除以模長),這相當于 sin(φ),其中 φ 是與 y 軸相關的角度。
    • 使用 acos(w) 得到緯度角,然后調整為 v = scale * (π - acos(w)),使得 v 從0到π(通常球面投影緯度范圍是[-π/2, π/2],這里轉換為[0, π]以符合圖像坐標習慣)。
  4. 處理 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_cast<float>(CV_PI) - v);

? ? float x_ = sinv * sinf(u);

? ? float y_ = cosf(static_cast<float>(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?1 將該方向向量投影回圖像平面,得到 (x, y, z)。最后執行透視除法(x/z, y/z)得到標準圖像坐標。如果 z ≤ 0,說明方向指向相機背后,不可見,函數將 (x, y) 設為 -1。該函數常用于生成圖像投影反向映射表,在圖像拼接、全景重建等應用中至關重要。

    ?步驟

    1. 將輸入的 u, v 還原為弧度值(除以縮放因子 scale)。
    2. 從球面坐標重建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(θ)
          其中 φ 是緯度角,θ 是經度角。
    3. 使用矩陣 k_rinv(旋轉矩陣和內參矩陣的組合的逆)將3D點變換回平面點。
    4. 進行透視除法(若點在相機前方,z>0),得到歸一化平面坐標 (x/z, y/z)
    5. 若點位于相機后方(z<=0),則返回 (-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 圖像拼接模塊中的核心接口之一,主要用于處理圖像在不同相機姿態下的旋轉投影變換。它為各種具體的投影方式(如球面投影、柱面投影)提供統一的接口封裝,包括點的前向/反向投影(warpPointwarpPointBackward)、圖像投影與反投影(warpwarpBackward)、構建映射表(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. It's 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 isn't guaranteed.

    ? ? void detectResultRoiByBorder(Size src_size, Point &dst_tl, Point &dst_br);

    ? ? P projector_;

    };

    這段代碼定義了一個模板基類 RotationWarperBase<P>,是 RotationWarper 接口的具體實現框架,適用于基于旋轉變換的圖像投影操作。它以模板參數 P 作為具體的投影器(如 SphericalProjectorCylindricalProjector)實例,通過封裝和調用 projector_ 中定義的投影邏輯(如 mapForwardmapBackward),實現圖像點的正向/反向映射、構建映射表、執行圖像投影、計算變換后的圖像區域等功能。該類為各種具體投影器提供統一的實現基礎,既具備通用性,也便于在 OpenCV 圖像拼接中擴展不同的投影模型。它將相機內參 K、旋轉矩陣 R 封裝為投影器的參數,使圖像在全景拼接、視角變換等任務中能實現精準的幾何變換。

    template <class P>

    Point2f RotationWarperBase<P>::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 RotationWarperBase<P>::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;

    }

    這段代碼是模板類 RotationWarperBase<P>warpPointBackward 函數的實現,它用于執行圖像坐標的反向投影操作。函數接受一個圖像點 pt(通常是投影圖像中的坐標),以及相機的內參矩陣 K 和旋轉矩陣 R。它首先調用 projector_ 設置相機參數,然后通過 projector_mapBackward 方法將該點 (u, v) 映射回原始圖像中的點 (x, y),最終返回這個反投影后的點。該函數常用于圖像反向映射(如反變形或從輸出圖像推回源圖像坐標),在圖像拼接和圖像重建中尤為重要。

    template <class P>

    Rect RotationWarperBase<P>::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_cast<float>(u), static_cast<float>(v), x, y);

    ? ? ? ? ? ? xmap.at<float>(v - dst_tl.y, u - dst_tl.x) = x;

    ? ? ? ? ? ? ymap.at<float>(v - dst_tl.y, u - dst_tl.x) = y;

    ? ? ? ? }

    ? ? }

    ? ? return Rect(dst_tl, dst_br);

    }

    這段代碼實現了一個模板函數 buildMaps,它是圖像旋轉投影類 RotationWarperBase<P> 的核心方法之一,主要作用是根據輸入圖像的尺寸 src_size,相機的內參矩陣 K 和旋轉矩陣 R,構建兩個映射表 xmapymap,分別對應每個目標圖像像素在源圖像上的橫縱坐標。該函數首先設置投影器參數,然后通過 detectResultRoi 計算投影后圖像的邊界區域,接著為映射表分配內存,并遍歷該區域的每個像素,使用投影器的 mapBackward 方法將目標像素反投影回源圖像坐標系,并記錄結果到 xmapymap 中。最終返回投影圖像的邊界矩形 Rect(dst_tl, dst_br),供后續拼接或重采樣使用。這個函數在圖像縫合、投影轉換等視覺任務中非常關鍵。

    template <class P>

    Point RotationWarperBase<P>::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();

    }

    這段模板函數 warpRotationWarperBase<P> 類中的圖像正向投影實現,它根據給定的源圖像 src、相機內參 K、旋轉矩陣 R 以及插值方式 interp_mode 和邊界處理方式 border_mode,對輸入圖像進行仿射或旋轉投影變換。函數內部首先通過 buildMaps 構建反向映射表 xmapymap,確定目標圖像的像素在源圖像中的對應位置,然后創建目標圖像 dst 的空間,并調用 remap 函數按照映射關系將源圖像重新采樣到目標圖像中。最終返回的是目標圖像左上角的坐標 dst_roi.tl(),常用于圖像拼接時確定偏移。整個流程適用于基于旋轉的圖像投影變換,如全景拼接或視圖重映射。

    template <class P>

    void RotationWarperBase<P>::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_cast<float>(x), static_cast<float>(y), u, v);

    ? ? ? ? ? ? xmap.at<float>(y, x) = u - src_tl.x;

    ? ? ? ? ? ? ymap.at<float>(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 推斷源圖像的邊界,并構造反向映射表 xmapymap:對于目標圖中每個像素 (x, y),通過 mapForward 得到它在源圖中的位置 (u, v),并記錄偏移。最后調用 remap 進行插值采樣,生成重建后的目標圖像 dst。此方法常用于圖像去畸變、全景圖像還原等場景。

    template <class P>

    Rect RotationWarperBase<P>::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 RotationWarperBase<P>::detectResultRoi(Size src_size, Point &dst_tl, Point &dst_br)

    {

    ? ? float tl_uf = (std::numeric_limits<float>::max)();

    ? ? float tl_vf = (std::numeric_limits<float>::max)();

    ? ? float br_uf = -(std::numeric_limits<float>::max)();

    ? ? float br_vf = -(std::numeric_limits<float>::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_cast<float>(x), static_cast<float>(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_cast<int>(tl_uf);

    ? ? dst_tl.y = static_cast<int>(tl_vf);

    ? ? dst_br.x = static_cast<int>(br_uf);

    ? ? dst_br.y = static_cast<int>(br_vf);

    }

    這段模板函數 detectResultRoi 的作用是計算經過投影變換后圖像的最小包圍矩形(ROI)。函數通過遍歷原始圖像 src_size 中的每一個像素點 (x, y),使用 projector_.mapForward 將其投影到目標圖像坐標系 (u, v),并記錄所有投影點中的最小和最大坐標,以此確定變換后圖像的左上角 dst_tl 和右下角 dst_br。這些點組成的矩形就是變換后圖像的邊界,用于后續圖像重映射(remap)或圖像拼接等操作。這是計算投影范圍的基礎步驟之一。

    template <class P>

    void RotationWarperBase<P>::detectResultRoiByBorder(Size src_size, Point &dst_tl, Point &dst_br)

    {

    ? ? float tl_uf = (std::numeric_limits<float>::max)();

    ? ? float tl_vf = (std::numeric_limits<float>::max)();

    ? ? float br_uf = -(std::numeric_limits<float>::max)();

    ? ? float br_vf = -(std::numeric_limits<float>::max)();

    ? ? float u, v;

    ? ? for (float x = 0; x < src_size.width; ++x)

    ? ? {

    ? ? ? ? projector_.mapForward(static_cast<float>(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_cast<float>(x), static_cast<float>(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_cast<float>(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_cast<float>(src_size.width - 1), static_cast<float>(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_cast<int>(tl_uf);

    ? ? dst_tl.y = static_cast<int>(tl_vf);

    ? ? dst_br.x = static_cast<int>(br_uf);

    ? ? dst_br.y = static_cast<int>(br_vf);

    }

    這段代碼 detectResultRoiByBorderRotationWarperBase 模板類的一個成員函數,用于估算投影變換后的目標圖像區域的最小包圍矩形(ROI),但它只考慮了圖像邊界上的像素點,因此精度可能略低于完全遍歷圖像像素的方法 detectResultRoi。函數通過對源圖像的四條邊(頂邊、底邊、左邊、右邊)進行 mapForward 投影變換,計算變換后所有邊緣點的最小(左上)和最大(右下)坐標,并據此構造 ROI 區域 dst_tldst_br。這種方法計算量較小,適合對 ROI 精度要求不是很高的場景。

    class CV_EXPORTS SphericalWarper : public RotationWarperBase<SphericalProjector>

    {

    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 類,它繼承自 RotationWarperBase<SphericalProjector>,用于實現球面圖像投影變換。構造函數中通過 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_cast<float>(dst_tl.x);

    ? ? float tl_vf = static_cast<float>(dst_tl.y);

    ? ? float br_uf = static_cast<float>(dst_br.x);

    ? ? float br_vf = static_cast<float>(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_cast<float>(CV_PI * projector_.scale));

    ? ? ? ? ? ? br_uf = std::max(br_uf, 0.f); br_vf = std::max(br_vf, static_cast<float>(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_cast<float>(0));

    ? ? ? ? ? ? br_uf = std::max(br_uf, 0.f); br_vf = std::max(br_vf, static_cast<float>(0));

    ? ? ? ? }

    ? ? }

    ? ? dst_tl.x = static_cast<int>(tl_uf);

    ? ? dst_tl.y = static_cast<int>(tl_vf);

    ? ? dst_br.x = static_cast<int>(br_uf);

    ? ? dst_br.y = static_cast<int>(br_vf);

    }

    這段代碼是 SphericalWarper::detectResultRoi 的實現,用于精確計算球面投影后圖像的目標區域(ROI)。它首先調用 detectResultRoiByBorder() 獲取一個初始邊界框,然后進一步考慮圖像在球面投影下的可視范圍,并根據投影中心方向是否朝上/下修正邊界框。

    關鍵步驟如下:

    1. 使用 detectResultRoiByBorder 得到初步的左上 (dst_tl) 和右下 (dst_br) 投影邊界點。

    2. 提取旋轉矩陣的逆矩陣中與 Y 軸方向相關的向量,判斷相機朝向。

    3. 根據相機是否朝上 (y > 0) 或朝下 (-y > 0),計算相機視角對應圖像坐標,判斷這些方向是否在圖像有效區域內。

    4. 若在圖像范圍內,則更新投影區域上下邊界 tl_vfbr_vf,使其完整包含可能的投影角度范圍,如 [0, π*scale]

    5. 最終將浮點結果轉換為整數坐標,輸出目標矩形區域。

    這段邏輯主要用于在球面投影中修正 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 RotationWarperBase<SphericalProjector>::buildMaps(src_size, K, R, xmap, ymap);

    }

    這段代碼實現了 SphericalWarper::buildMaps 方法,用于為球面投影生成圖像的重映射(remap)表,即 xmapymap。其核心目的是:計算球面投影后的目標圖像坐標映射表,以便后續使用 OpenCV 的 remap() 函數進行圖像變換。

    其邏輯可分為兩種路徑:

    1. OpenCL 加速路徑(條件編譯啟用 HAVE_OPENCL):

      • 檢查是否啟用 OpenCL 加速(ocl::isOpenCLActivated())。

      • 創建 OpenCL kernel "buildWarpSphericalMaps"(定義在 OpenCV 的 warpers_oclsrc 源中)。

      • 若 kernel 成功加載:

        • 設置相機內參 K 和旋轉矩陣 R

        • 調用 detectResultRoi() 獲取投影圖像的邊界框。

        • 分配 xmapymap,并將投影參數打包為 OpenCL 參數傳給 kernel。

        • 運行 kernel,根據線程模型自動生成 warp map。

        • 若成功執行,返回投影矩形區域。

    2. 回退到 CPU 路徑(當沒有 OpenCL 或 kernel 加載失敗):

      • 調用基類 RotationWarperBase<SphericalProjector>::buildMaps() 使用 CPU 實現生成 xmapymap

    該方法優先嘗試使用 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 構建重映射表 uxmapuymap,然后使用 OpenCV 的 remap 函數將圖像投影到球面坐標系下,生成輸出圖像 dst。該方法的核心作用是:基于球面模型將圖像從透視投影變換到球面投影,并輸出映射后的圖像及其左上角在全局投影圖像中的位置

    本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
    如若轉載,請注明出處:http://www.pswp.cn/web/84625.shtml
    繁體地址,請注明出處:http://hk.pswp.cn/web/84625.shtml
    英文地址,請注明出處:http://en.pswp.cn/web/84625.shtml

    如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

    相關文章

    wordpress外貿獨立站常用留言表單插件 contact form 7

    Contact Form 7 介紹 Contact Form 7 是一款非常流行的 WordPress 聯系表單插件&#xff0c;廣泛應用于外貿獨立站。以下是其主要特點&#xff1a; 功能強大且免費&#xff1a;Contact Form 7 是完全免費的&#xff0c;支持創建和管理多個聯系表單。 簡單易用&#xff1a;用…

    佰力博科技與您探討油浴極化的優點及工藝流程

    一、油浴極化的優點 溫度范圍寬&#xff1a;油浴極化適用于較寬的溫度范圍&#xff0c;適合不同材料的極化需求。 絕緣強度高&#xff1a;油浴介質具有良好的絕緣性能&#xff0c;能夠承受較高的極化電場。 防潮性好&#xff1a;油浴極化在潮濕環境中仍能保持良好的絕緣性能。 …

    從0開始學習R語言--Day28--高維回歸

    我們一般處理的數據&#xff0c;都是樣本數量遠大于其特征數量&#xff0c;可用很多種回歸方法&#xff1b;但當特征數量遠大于樣本量時&#xff0c;可能會因為出現無數多個完美解導致過擬合現象&#xff0c;也使得在計算時搜索最有特征子集的方法不再可行&#xff08;因為計算…

    響應式數據的判斷:Vue3中的方法

    &#x1f90d; 前端開發工程師、技術日更博主、已過CET6 &#x1f368; 阿珊和她的貓_CSDN博客專家、23年度博客之星前端領域TOP1 &#x1f560; 牛客高級專題作者、打造專欄《前端面試必備》 、《2024面試高頻手撕題》、《前端求職突破計劃》 &#x1f35a; 藍橋云課簽約作者、…

    [論文閱讀] 人工智能+軟件工程 | 用大語言模型架起軟件需求形式化的橋梁

    用大語言模型架起軟件需求形式化的橋梁&#xff1a;一篇ACM調查草案的深度解讀 論文信息 arXiv:2506.14627 ACM Survey Draft on Formalising Software Requirements with Large Language Models Arshad Beg, Diarmuid O’Donoghue, Rosemary Monahan Comments: 22 pages. 6 s…

    DM8故障分析工具-AWR報告

    在數據庫運維過程中&#xff0c;大家都會利用數據庫提供的各種工具來找到數據庫存在的問題&#xff0c;以便對癥實施配置優化&#xff0c;我是因工作需要&#xff0c;最近開始了解達夢數據庫DM8的故障分析工具&#xff0c;這里發現AWR報告是一款不錯的自帶工具&#xff0c;故而…

    《企業司法風險監控系統架構設計:從數據采集到T+1實時預警的完整解決方案》

    本文深入探討了天遠大數據在構建企業級司法風險監控平臺和風險報告查詢系統方面的技術實現與業務應用。平臺依托權威、合法的司法數據源&#xff0c;通過實時數據處理與智能分析&#xff0c;為金融、供應鏈、人力資源等領域提供精準、及時的司法預警和決策支持。它通過靈活的多…

    使用ccs生成bin

    CCS12.6 編譯生成BIN文件正確方法_ccs生成bin文件-CSDN博客

    Kafka網絡模塊全鏈路源碼深度剖析與設計哲學解讀

    在分布式消息系統的競技場上&#xff0c;Kafka憑借卓越的高性能與高吞吐量脫穎而出&#xff0c;而其網絡模塊正是支撐這一卓越表現的核心引擎。從生產者將消息送入消息隊列&#xff0c;到消費者從中拉取消息&#xff0c;Kafka網絡模塊貫穿消息流轉的每個環節。本文不僅深入Kafk…

    華為開發者大會6月20日舉行

    華為開發者大會2025&#xff08;HDC 2025&#xff09;將于6月20日至22日在深圳松山湖舉辦。 目前&#xff0c;華為開發者大會2025的詳細日程已經公布&#xff0c;華為終端BG董事長余承東、華為終端BG首席執行官何剛、華為終端BG軟件部總裁龔體等華為高管將出席并發表主題演講&a…

    `provide` 和 `inject` 組件通訊:實現跨組件層級通訊

    &#x1f90d; 前端開發工程師、技術日更博主、已過CET6 &#x1f368; 阿珊和她的貓_CSDN博客專家、23年度博客之星前端領域TOP1 &#x1f560; 牛客高級專題作者、打造專欄《前端面試必備》 、《2024面試高頻手撕題》、《前端求職突破計劃》 &#x1f35a; 藍橋云課簽約作者、…

    MCP入門實戰(Python版)

    MCP介紹 MCP入門介紹 MCP 簡介 - MCP 中文文檔 MCP&#xff0c;全稱是Model Context Protocol&#xff0c;模型上下文協議&#xff0c;由Claude母公司Anthropic于2024年11月正式提出。 從本質上來說&#xff0c;MCP是一種技術協議&#xff0c;一種智能體Agent開發過程中共同…

    1、自然語言處理任務全流程

    自然語言處理黃金九步法&#xff0c;葵花寶典&#xff0c;請珍藏心間 目錄 需求分析&#xff1a;問題定義 1.文本分類任務 2.序列標注任務 3.文本生成任務 4.文本理解任務 5.信息抽取任務 6.文本匹配任務 7.多模態任務 一、數據獲取 1、發現可用數據集 2、常用的數…

    可編程密碼學(Part 1)

    1. 引言 當前密碼學正處于一次代際轉變之中&#xff0c;從special-purpose cryptography專用密碼學過渡到programmable cryptography可編程密碼學。 1&#xff09;所謂“專用密碼學”&#xff0c;指的是那些只能執行單個操作且具有密碼學安全保證的協議。 公鑰加密和簽名方案…

    Linux運維新人自用筆記(Ubuntu磁盤命名規則、新磁盤分區、主流文件系統類型、mkfs命令格式化文件系統、臨時和永久掛載、掛載報錯、dd指令)

    內容全為個人理解和自查資料梳理&#xff0c;歡迎各位大神指點&#xff01; 每天學習較為零散。 day21 一、磁盤維護流程 新硬盤&#xff08;虛擬機可添加&#xff09; 新硬盤需要做lvm管理 數據庫遷移&#xff08;夜間網站停機維護&#xff09;&#xff1a; 停止數據庫監…

    騰訊云輕量級服務器Ubuntu系統與可視化界面

    以云服務器的方式搭建Linux workstation對比在電腦本地安裝虛擬機的優勢在于&#xff0c;不需要占用本地電腦資源空間&#xff0c;網絡環境等相對穩定&#xff0c;可以用手機等輕量移動設備連接管理等。本文主要介紹使用騰訊云服務器&#xff0c;搭建Ubuntu Linux系統以及可視化…

    如何在MacOS系統和Windows系統安裝節點小寶遠程工具

    如何在MacOS系統和Windows系統安裝節點小寶遠程工具 摘要 本文講述如何在MacOS系統和Windows系統安裝節點小寶遠程工具&#xff0c;并詳細介紹了配置和使用遠程控制的步驟。無論是在個人電腦還是手機、平板設備之間的遠程連接&#xff0c;您都可以通過本教程輕松實現。 文章…

    60天python訓練營打卡day38

    學習目標&#xff1a; 60天python訓練營打卡 學習內容&#xff1a; DAY 38 Dataset和Dataloader類 知識點回顧&#xff1a; 1.Dataset類的__getitem__和__len__方法&#xff08;本質是python的特殊方法&#xff09; 2.Dataloader類 3.minist手寫數據集的了解 作業&#xff1a…

    Python 鄰接表詳細實現指南

    鄰接表是圖數據結構的一種高效表示方法&#xff0c;特別適合表示稀疏圖。下面我將用 Python 詳細講解鄰接表的多種實現方式、操作方法和實際應用。 一、鄰接表基礎概念 鄰接表的核心思想是為圖中的每個頂點維護一個列表&#xff0c;存儲與該頂點直接相連的所有鄰接頂點。 鄰…

    Nginx反向代理解決跨域問題詳解

    Nginx反向代理解決跨域問題詳解 核心原理 Nginx反向代理解決跨域的核心思路是讓客戶端請求同域名下的接口&#xff0c;由Nginx將請求轉發到目標服務器&#xff0c;從而規避瀏覽器的同源策略限制。 客戶端&#xff08;同源&#xff1a;www.domain.com&#xff09;↓Nginx&…