【OpenCV學習筆記】之六 手寫映像旋轉函數—萬丈高樓平地起

來源:互聯網
上載者:User

話說,平凡之處顯真格,這一點也沒錯!  比如,對旋轉映像進行雙線性插值,很簡單吧?  可,對我,折騰了大半天,也沒有達到預期效果!  尤其是三個誤區讓我抓瞎好久:

1,座標旋轉公式。   這東西,要用的時候查資料,抄過來,從不記清,猛地一下讓人寫正確,確實不容易,雖然只是正餘弦的排列問題。畫圖推導的方法也是知道,但是,奈何又記不得三角形的和角展開公式。沒辦法,只好逐一測實驗證了,心血經驗,45、90,135,180這幾個角度最好都驗證一下。

2,雙插的資料來源。 一開始,思維上習慣地資料來源認定應該是旋轉之後的,為此施展多種手段都不能較好克服資料有效性、配對性等異常。搞個帶掩模的3*3濾波吧,卻使映像變模糊了。  絕境反思,資料來源取自源圖資料,該是多好的事呀。 仿射變換 warpAffine() 函數中的仿射矩陣就是預設為逆向。

3,雙插的方法。 一直來,都知道X、Y方向要各插值一次,但卻不明確它們的相互關係是 串列,而非並行!


以下貼出我後來完善出的旋轉部分代碼,有路過的高手請幫忙指點最佳化一下:


//旋轉 平移 點座標,依據旋轉矩陣而來void rotatePoint(const Point2d& src, Point2d& dst, const double angle, const Point2d& offset=Point2f(0,0)){    const double cosAngle =cos(angle);    const double sinAngle =sin(angle);    dst.x = src.x * cosAngle + src.y * sinAngle + offset.x;    dst.y = src.y * cosAngle - src.x * sinAngle + offset.y;}//旋轉 平移 點座標 angle中的x值為 cos(angle)  y為sin(angle)inline void rotatePoint(const Point2d& src, Point2d& dst, const Point2d& angle, const Point2d& offset=Point2f(0,0)){    //dst.x = src.dot(angle) + offset.x;    dst.x = src.x * angle.x + src.y * angle.y + offset.x;    dst.y = src.y * angle.x - src.x * angle.y + offset.y;}//雙線性插值  a為左上點 b右上 c左下 d右下 權重因子Sx Sy 的取值範圍為(0 , 1)由小座標指向大座標距離比template<typename T> inline double insertDLine(const T a, const T b, const T c, const T d, const double Sx, const double Sy){    const double Sx1 = 1 -Sx;    const double Sy1 = 1 -Sy;    return (a *Sx1 *Sy1 + b *Sx *Sy1 + c *Sx1 *Sy + d *Sx *Sy);}//雙線性插值  dst為data映像中的2*2子塊 權重因子Sx Sy 的取值範圍為(0 , 1)由小座標指向大座標距離比void insertDLine(const Mat& src, Scalar& dst,const double Sx, const double Sy){    const int channels =src.channels();    const uchar *pU = src.ptr(0);    const uchar *pD = src.ptr(1);    const int depth =min(4, channels);    for (int i = 0; i < depth; i++)    {        dst[i] = insertDLine(pU[i], pU[i + channels], pD[i], pD[i + channels], Sx, Sy);    }}//將源映像旋轉一定的角度 int rotateImage(const Mat& src, Mat& dst, double angle, const bool isDegree){    const int channels =src.channels();    if(channels > 4)    {        //dst=src;  //         return -1;    }    //將角度化為弧度     if(isDegree)    {        angle *=CV_PI/180;    }    //參數初始化    const double cosAngle =cos(angle);    const double sinAngle =sin(angle);    const int srcRows = src.rows;    const int srcCols = src.cols;    const int  dstCols =srcRows *abs(sinAngle) + srcCols *abs(cosAngle);    const int  dstRows =srcRows *abs(cosAngle) + srcCols *abs(sinAngle);    const int srcRowsLess2 = srcRows -2;    const int srcColsLess2 = srcCols -2;    const Point2d centerA(srcCols/2 +0.5, srcRows/2 +0.5);    const Point2d centerB(dstCols/2 +0.5, dstRows/2 +0.5);    const Point2d rotateAngle(cosAngle, -sinAngle);  //用於從靶心圖表迴旋轉到初始圖,角度取反    Point2d hitPoint;    int xL, yL;    Rect insertROI(0, 0, 2, 2);    Scalar insertVaule;    //申請記憶體空間,並設定為 0    dst.create(dstRows, dstCols, CV_8UC(channels));    dst.setTo(Scalar(0,0,0,0));    for (int i = 0; i < dstRows; i++)    {        uchar *pDst=dst.ptr(i);        for (int j = 0; j  < dstCols; j ++)        {            rotatePoint(Point2d(j, i)-centerB, hitPoint, rotateAngle, centerA);            xL =floor(hitPoint.x);            yL =floor(hitPoint.y);                        //從靶心圖表中迴轉至源映像,不在地區內的直接跳過            if (xL < 0 || xL > srcColsLess2 || yL < 0 || yL > srcRowsLess2)            {                continue;            }            insertROI.x =xL;            insertROI.y =yL;            insertDLine(src(insertROI), insertVaule, hitPoint.x -xL, hitPoint.y -yL);                    int base =j *channels;            for (int z = 0; z < channels; z++)            {                pDst[base +z] = insertVaule[z];            }        }    }    return 0;}


為了展示我的手寫旋轉函數rotateImage() 與仿射變換 warpAffine() 函數的效果比較,有如下程式碼片段:

    double angle =90.0 * CV_PI/180; //將角度化為弧度 30    Mat rotateImg;    rotateImage(colorImg, rotateImg, angle);    const double cosAngle =cos(angle);    const double sinAngle =sin(angle);    Mat rrM =(Mat_<double>(2,3) << cosAngle, sinAngle, cosAngle *colorImg.rows,  -sinAngle, cosAngle, sinAngle *colorImg.cols);    Mat rotatewarpAffine;     warpAffine(colorImg, rotatewarpAffine, rrM, colorImg.size()*2);

旋轉30度時:

rotateImage30



warpAffine 30

旋轉90度時:

rotateImage90.jpg



warpAffine 90


聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.