圖像處理--圖像的幾何變換--旋轉變換

原博客:http://blog.csdn.net/linshanxian/article/details/68944748


旋轉有一個繞着什麼轉的問題。通常的做法是以圖像的中心爲圓心旋轉,將圖像上的所有像素都旋轉一個相同的角度。圖像的旋轉變換是圖像的位置變換,但旋轉後圖像的大小一般會改變。和平移變換一樣,既可以把轉出顯示區域的圖像截去,也可以擴大顯示區域以顯示完整的圖像,如下圖所示。


我們先討論不裁剪轉出部分,擴大顯示區域的情況。在下圖所示的平面座標系中,A0逆時針旋轉θ變成A1r是該點到原點的距離,則旋轉前:


旋轉後A1的座標爲


寫成矩陣的形式爲:


其逆變換矩陣如下:


上面公式是旋轉變換的基本公式,座標系是以圖像的中心爲原點,向右爲x軸正方向,向上爲y軸正方向。上述旋轉是繞座標原點進行的,如果是繞指定點(a,b)旋轉,那麼應該先將座標系平移至改點,再旋轉,然後平移至新的座標原點。

下面推導座標系平移的變換公式。座標系是圖像的座標系,座標系是旋轉座標系,座標系的原點在座標系中爲(a,b),如下圖所示。


兩種座標系之間的轉換爲:


逆變換爲:


有了上面的公式,就可以很方便的推導圖像旋轉變換的表達式。假設圖像未旋轉時候旋轉中心的座標是a,b,旋轉後中心點的座標爲c,d(在新的座標系下,以旋轉後圖像的左上角爲原點),則可以把變換分爲3步:

第一步,將座標系Ⅰ變成Ⅱ;

第二步,旋轉θ(逆時針爲正,順時針爲負);

第三步,將座標系Ⅱ變換回Ⅰ。這樣就得到了總的變換矩陣。

設原圖像某像素點的座標爲(x0y0),旋轉後在目標圖像的座標爲(x1y1),則旋轉變換的矩陣表達式爲:


逆變換爲:


有了上面的轉換公式,就可以很方便的編寫出實現圖像旋轉的程序。首先需要計算出公式中需要的幾個參數:abcd和旋轉後圖像的尺寸。已知原是圖像的寬度爲w0,高度爲h0,以圖像的中心爲座標原點。則原圖像四個角的座標分別是:


按照旋轉公式,旋轉後這四個點的座標分別是:


則新圖像的高度和寬度分別爲:



圖像旋轉的主要代碼如下:

void RotIamge(const Mat &srcImage, Mat &dstImage, double angle)  
{  
    //弧度  
    double sita = angle * CV_PI / 180;  
    double a = (srcImage.cols - 1) / 2.0;  
    double b = (srcImage.rows - 1) / 2.0;  
  
    int srcRow = srcImage.rows;  
    int srcCol = srcImage.cols;  
  
    double x1 = -a * cos(sita) - b * sin(sita);  
    double y1 = -a * sin(sita) + b * cos(sita);  
  
    double x2 = a * cos(sita) - b * sin(sita);  
    double y2 = a * sin(sita) + b * cos(sita);  
  
    double x3 = a * cos(sita) + b * sin(sita);  
    double y3 = a * sin(sita) - b * cos(sita);  
  
    double x4 = -a * cos(sita) + b * sin(sita);  
    double y4 = -a * sin(sita) - b * cos(sita);  
  
    int w1 = cvRound(max(abs(x1 - x3), abs(x4 - x2)));  
    int h1 = cvRound(max(abs(y1 - y3), abs(y4 - y2)));  
    dstImage.create(h1, w1, srcImage.type());  
  
    double c = (w1 - 1) / 2.0;  
    double d = (h1 - 1) / 2.0;  
  
    double f1 = -c * cos(sita) + d * sin(sita) + a;  
    double f2 = -c * sin(sita) - d * sin(sita) + b;  
    int nRowNum = dstImage.rows;  
    int nColNum = dstImage.cols;  
    for (int i = 0; i < nRowNum; i++)  
    {  
  
        for (int j = 0; j < nColNum; j++)  
        {  
            int x = cvRound(j * cos(sita) - i * sin(sita) + f1);  
            int y = cvRound(j * sin(sita) + i * cos(sita) + f2);  
            if (x > 0 && x < srcCol && y > 0 && y < srcRow)  
            {  
                dstImage.at<Vec3b>(i, j) = srcImage.at<Vec3b>(y, x);  
            }  
        }  
    }  
}  


對於旋轉以後圖像大小不變的情況,旋轉前後圖像的中心點座標都是a,b,那麼旋轉的變換矩陣就是:


逆變換爲:


公式中,


主要代碼如下:

void RotIamge2(const Mat &srcImage, Mat &dstImage, double angle)  
{  
    //弧度  
    double sita = angle * CV_PI / 180;  
    double a = (srcImage.cols - 1) / 2.0 + 0.5;  
    double b = (srcImage.rows - 1) / 2.0 + 0.5;  
  
    int nRowNum = srcImage.rows;  
    int nColNum = srcImage.cols;  
    dstImage.create(nRowNum, nColNum, srcImage.type());  
  
    double f1 = -a * cos(sita) + b * sin(sita) + a;  
    double f2 = -a * sin(sita) - b * cos(sita) + b;  
  
    for (int i = 0; i < nRowNum; i++)  
    {  
        for (int j = 0; j < nColNum; j++)  
        {  
            int x = cvRound(j * cos(sita) - i * sin(sita) + f1);  
            int y = cvRound(j * sin(sita) + i * cos(sita) + f2);  
            if (x > 0 && x < nColNum && y > 0 && y < nRowNum)  
            {  
                dstImage.at<Vec3b>(i, j) = srcImage.at<Vec3b>(y, x);  
            }  
        }  
    }  
}  

要注意的是,由於有浮點運算,計算出來的座標可能不是整數,需要採取取整處理,使用cvRound()函數尋找最接近的點,這樣會帶來一些誤差,圖像可能會出現鋸齒,更好的方式是採用插值,後續將會具體介紹。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章