2017年11月20日 星期一

[原始碼] C / C++ 旋轉 任意角度的圖片

[原始碼] C / C++ 如何旋轉圖片

首先會用到線性插補可以參考這一篇
https://charlottehong.blogspot.tw/2017/11/bilinear.html

旋轉公式

跟上一篇差不多意思線性插補指的是for迴圈去跑新圖,然後縮放對應回去原圖的座標,因為縮放得之後的點可能比較多或比較少,對應回去就會在非整數整數位置,這時候就看比例給數值。
旋轉也是一樣意思,旋轉過後的的點會正好不在點上,旋轉有兩個公式一般看到的就是給輸入原圖得出新圖的座標,這個不是我們要的,因為如果是這樣就不能插補了。
需要的是for去跑新圖座標,輸入新圖座標得到舊圖的座標(非整數),然後再利用線性插補補出來。
公式如下:
float r_rot = (j)*sin_t + (i)*cos_t; // 原圖X座標
float c_rot = (j)*cos_t - (i)*sin_t; // 原圖Y座標
其中 (j, i) 就是新圖的 (y, x) 座標,sin_t 與 cos_t 則是預先算好的數值,因為計算三角函數很費時,建議提出到for迴圈之外先算好,如果再for回圈內就要不斷的重複運算同一個角度浪費效能。
// 預算
float cos_t = cosf(sita*(float)(M_PI/180));
float sin_t = sinf(sita*(float)(M_PI/180));

原點在哪裡

需要注意的是上圖的公式會以圖的左上角 (0, 0) 作為定點轉換,如果需要從中心點旋轉或是任點轉需要轉換一下座標
假如現在要以 (10, 10) 為中心旋轉,那麼在旋轉前你需要先把for迴圈扣調這個範圍,也就是從 (j, i) 要從負號 -10 開始跑,經過旋轉公式計算出來之後還需要將原本的 10 加回來,這樣就可以獲得正確的位置了。
此外獲得正確位置之後還有一個問題就是旋轉後的圖比旋轉前的圖還要大,勢必會計算出超出原圖範圍的點,跑一個if過濾負號與超出原圖長寬的點即可。

範例代碼

只有給主要函式,有部分是已經寫好的函式庫,我有空再補上完整能跑的
(懶懶的~還沒補上又真的有需要然後看不懂可以直接留言問吧~我會看到的)
ImgRaw rotateImg(const ImgRaw& sou, size_t x, size_t y, float radius, float sita) {
    ImgRaw test = sou;
    //test.at2d(x, y) = 1;
    Draw::draw_arrow(test, y, x, radius, sita, 1);
    test.bmp("rotate_test.bmp");
    // 新圖長寬半徑
    float maxRadius = radius;
    int rotH = floor(maxRadius);
    int rotW = floor(maxRadius);
    ImgRaw rotate(rotW*2, rotH*2, 8);
    // 預算
    sita *= -1; // 把新圖轉回0度
    float cos_t = cosf(sita*(float)(M_PI/180));  
    float sin_t = sinf(sita*(float)(M_PI/180));
    // 跑新圖
    for (int j = -rotH; j < rotH; j++) {
        for (int i = -rotW; i < rotW; i++) {
            // 輸入新圖座標返回舊圖座標(已0, 0為圓心旋轉)
            float r_rot = (j)*sin_t + (i)*cos_t; // 原圖X座標
            float c_rot = (j)*cos_t - (i)*sin_t; // 原圖Y座標
            // 矯正回指定位置
            float rbin = r_rot + x; 
            float cbin = c_rot + y;
            // 去除原圖外的點
            if (cbin < sou.height - 1 and cbin > 0) {
                if (rbin < sou.width - 1 and rbin > 0) {
                    // 雙線姓插補
                    rotate.at2d(j+rotH, i+rotW) = test.atBilinear(cbin, rbin); 
                }
            }
        }
    }
    return rotate;
}
ImgRaw 帶有圖像的
  1. 一維陣列
  2. 長寬
  3. 位元數(彩圖灰圖)
  4. 一些方法
線性插補建議寫成一個讀取的,這樣規劃結構還不錯用 atBilinear(y, x) 要是你輸入的點不正好在點上,比如說 (0.5, 0.5) 就算出這個位置的插補值回傳。
這個函式的功能主要為輸入
  1. 點(y, x)
  2. 角度
  3. 半徑
輸出為
  1. 畫出點與半徑的箭頭
  2. 把點與角度轉正向右
  3. 擷取點為中心半徑的距離
其實就是SIFT算法的一小部分,轉正主角度時用到到的

沒有留言:

張貼留言