zoukankan      html  css  js  c++  java
  • 【OpenCV学习笔记】之六 手写图像旋转函数---万丈高楼平地起

    话说,平凡之处显真格,这一点也没错!  比如,对旋转图像进行双线性插值,很简单吧?  可,对我,折腾了大半天,也没有达到预期效果!  尤其是三个误区让我抓瞎好久:

    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



  • 相关阅读:
    某个牛人做WINDOWS系统文件详解
    常用ASP脚本程序集锦
    LINUX基础:文件安全与权限
    proftpd+mysql+quota
    apache2.0.49tomcat5.0.19jk2建立virtualHost
    URL Redirection(转) Anny
    顶级域名后缀列表(转) Anny
    \u4E00\u9FA5意义 Anny
    How to POST Form Data Using Ruby(转) Anny
    How to get rid of 'Enter password to unlock your login keyring' in Ubuntu(转) Anny
  • 原文地址:https://www.cnblogs.com/keanuyaoo/p/3266737.html
Copyright © 2011-2022 走看看