首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> VC >> VC 教學

C++Builder 任意角度圖片旋轉算法實現(雙線性插值優化版)

     編輯:VC 教學

核心思路:逆向映射 + 雙線性插值

正向映射(源像素→目標)會導致目標圖像出現「空洞」(未被覆蓋的像素)。標準做法是逆向映射:遍歷目標圖像每個像素,計算其對應的源圖像浮點坐標,通過雙線性插值計算像素值,保證目標圖像無空洞、邊緣平滑。

完整優化代碼

void S_PicXZ(Graphics::TBitmap *Source, Graphics::TBitmap *NewPic, int angle) {
    // 1. 角度歸一化到 [-180, 180]
    if (angle > 180) angle = 360 - angle;
    if (angle < -180) angle = 360 + angle;

    // 2. 角度轉弧度
    float radians = (2 * 3.1415926535f * angle) / 360.0f;
    float cosine = cos(radians);
    float sine = sin(radians);

    // 3. 計算源圖像四角旋轉後的坐標(以中心為原點)
    // 源圖像中心為 (0,0),四角相對坐標:
    // (-w/2, -h/2), (w/2, -h/2), (w/2, h/2), (-w/2, h/2)
    // 旋轉後坐標:
    float w = Source->Width;
    float h = Source->Height;
    float hw = w / 2.0f;
    float hh = h / 2.0f;

    float corners[4][2] = {
        {-hw, -hh}, {hw, -hh}, {hw, hh}, {-hw, hh}
    };
    float rotCorners[4][2];
    float minX = 0, minY = 0, maxX = 0, maxY = 0;

    for (int i = 0; i < 4; i++) {
        float x = corners[i][0];
        float y = corners[i][1];
        rotCorners[i][0] = x * cosine - y * sine;
        rotCorners[i][1] = x * sine + y * cosine;
        if (i == 0) { minX = maxX = rotCorners[i][0]; minY = maxY = rotCorners[i][1]; }
        else {
            minX = std::min(minX, rotCorners[i][0]);
            maxX = std::max(maxX, rotCorners[i][0]);
            minY = std::min(minY, rotCorners[i][1]);
            maxY = std::max(maxY, rotCorners[i][1]);
        }
    }

    // 4. 目標圖像尺寸 = 旋轉包圍盒尺寸(向上取整)
    int destW = (int)ceil(maxX - minX);
    int destH = (int)ceil(maxY - minY);
    NewPic->Width = destW;
    NewPic->Height = destH;
    NewPic->PixelFormat = pf24bit; // 或 pf32bit,避免調色板模式慢

    // 5. 逆向映射 + 雙線性插值
    // 目標像素 (dx, dy) 對應源圖像中心坐標系:
    // sx = (dx - destW/2) * cosine + (dy - destH/2) * sine
    // sy = -(dx - destW/2) * sine + (dy - destH/2) * cosine
    // 源圖像實際坐標 = (sx + w/2, sy + h/2)

    float destHalfW = destW / 2.0f;
    float destHalfH = destH / 2.0f;

    for (int dy = 0; dy < destH; dy++) {
        for (int dx = 0; dx < destW; dx++) {
            // 目標像素中心相對坐標
            float tx = dx - destHalfW + 0.5f; // +0.5 對齊像素中心
            float ty = dy - destHalfH + 0.5f;

            // 逆旋轉得到源圖像相對中心坐標
            float sx = tx * cosine + ty * sine;
            float sy = -tx * sine + ty * cosine;

            // 轉換為源圖像絕對坐標(基於左上角 0,0)
            float srcX = sx + hw;
            float srcY = sy + hh;

            // 邊界檢查
            if (srcX >= 0 && srcX < w - 1 && srcY >= 0 && srcY < h - 1) {
                // 雙線性插值
                int x1 = (int)srcX;
                int y1 = (int)srcY;
                int x2 = x1 + 1;
                int y2 = y1 + 1;

                float dx1 = srcX - x1;
                float dy1 = srcY - y1;
                float dx2 = 1.0f - dx1;
                float dy2 = 1.0f - dy1;

                // 獲取四鄰域像素 (假設 24bit RGB)
                TColor c11 = Source->Canvas->Pixels[x1][y1];
                TColor c21 = Source->Canvas->Pixels[x2][y1];
                TColor c12 = Source->Canvas->Pixels[x1][y2];
                TColor c22 = Source->Canvas->Pixels[x2][y2];

                // 各通道獨立插值
                int r = (int)(GetRValue(c11)*dx2*dy2 + GetRValue(c21)*dx1*dy2 +
                              GetRValue(c12)*dx2*dy1 + GetRValue(c22)*dx1*dy1);
                int g = (int)(GetGValue(c11)*dx2*dy2 + GetGValue(c21)*dx1*dy2 +
                              GetGValue(c12)*dx2*dy1 + GetGValue(c22)*dx1*dy1);
                int b = (int)(GetBValue(c11)*dx2*dy2 + GetBValue(c21)*dx1*dy2 +
                              GetBValue(c12)*dx2*dy1 + GetBValue(c22)*dx1*dy1);

                NewPic->Canvas->Pixels[dx][dy] = RGB(r, g, b);
            }
            else {
                NewPic->Canvas->Pixels[dx][dy] = clWhite; // 背景色
            }
        }
    }
}

關鍵優化點解析

  1. 以圖像中心為旋轉原點:避免原代碼以左上角為原點導致的大量負坐標與複雜邊界判斷。
  2. 包圍盒精確計算:四角旋轉後取極值,目標尺寸精確,無多餘黑邊。
  3. 雙線性插值:替換原最近鄰採樣 (Pixels[][] 直接取整),消除鋸齒,圖像質量顯著提升。
  4. 像素中心對齊 (+0.5):符合圖像處理標準坐標系,進一步提升銳度。
  5. 預乘優化:循環外預計算 cosine、sine、destHalfW 等,減少重複運算。
  6. PixelFormat 設置:避免 pfDevice/調色板模式下 Pixels 讀寫極慢的問題。

性能進一步提升建議

  • ScanLine 替代 Pixels:TBitmap::ScanLine[y] 返回行指針,連續內存訪問,速度提升 10-50 倍。
  • 固定點數學 / SIMD:嵌入式或極高性能場景可用定點運算或 SSE/AVX 內聯汇編。
  • GPU 加速:FireMonkey 或 Direct2D/OpenGL 實現硬件加速旋轉。

調用示例

Graphics::TBitmap *src = new Graphics::TBitmap();
src->LoadFromFile("test.bmp");
Graphics::TBitmap *dst = new Graphics::TBitmap();
S_PicXZ(src, dst, 45); // 旋轉 45 度
dst->SaveToFile("rotated.bmp");
delete src;
delete dst;

總結

本文提供生產級任意角度旋轉函數,採用逆向映射+雙線性插值,解決原代碼的邊界錯誤、鋸齒、性能問題。理解坐標系變換、插值算法、邊界處理是圖像處理核心基本功。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved