• 【图像处理】基于OpenCV底层实现的图片旋转


    image processing 系列

    1. 【图像处理】直方图匹配
    2. 【图像处理】高斯滤波、中值滤波、均值滤波

    图片旋转,本质上是对旋转后的图片中每一个像素点计算在原图的位置。然后照搬过来就好。

    (多说一句。假设计算出来在原图中的位置不是整数而是小数,由于像素点个数都是整数,就须要小数到整数的转换。

    这个转换过程是有讲究的,须要用到插值:近期邻插值、双线性插值等等。这里我使用的是最简单的近期邻插值。即对小数四舍五入成整数。C/C++ 实现四舍五入见这里

    完整 github 代码:image-processing (里面同一时候包括OSTU / 大津算法、直方图均衡化、滤波器等算法。还包括两种測试图片)。

    图形图像课上通常会介绍旋转变换矩阵,当中 t 为须要旋转的角度,[x'; y']是变换后坐标(当中分号表示上下关系):


    即表示为:[x'; y'] = [cos(t) sin(t); -sin(t) cos(t)][x; y]

    由于我个人兴趣爱好(放P就是老师逼的。。。)。不同意使用 OpenCV 封装好的旋转函数。仅仅能自己实现,我開始的想法是:先求变换矩阵逆矩阵。然后将一张全黑图中每一个点一一相应插值到原图中。

    结果发现转换后图片全黑了……

    后来发现原点设置不正确。用OpenCV中的Mat格式存储(或二维数组)的图片。原点在左上角

    可是想要实现的旋转原点在图片中心

    同一时候。Mat格式存储(或二维数组)的坐标系中y轴正方向向下。这样人类视觉上的顺时针旋转,在二维数组的坐标系中是逆时针旋转。

    最重要的一点,也是二维数组操作中极易忽略的一点:数组操作的是数组下标,不是坐标系(数组的行数rows是矩形的宽width。列数cols是矩形的长length。比方坐标系(此时为了更贴近数组布局,我们如果 y 轴坐标系是向下的)中,矩形顶点是:


    可是在数组中。由于是行优先,所以四个点的下标取值为:


    有没有发现,两种坐标是相反的!


    总结下来,我们的图片旋转须要注意下面几点:

    1. 变换后图片中的每一个像素点(i; j),须要平移到相对旋转中心的新坐标,即(i - Mat.rows/2; j - Mat.cols/2)。计算完毕之后,须要再次还原到相对左上角原点的旧坐标。
    2. 本来须要 变换后图片 乘以 原图变换矩阵的逆矩阵 相应到原图中坐标。可是由于y轴方向向下,所以 变换后图片 乘以原图变换矩阵(无需逆矩阵) 就可以相应到原图中坐标(顺时针旋转50度,还原操作是逆时针旋转50度)。
    3. 矩阵下标与原图变换矩阵相乘之前。须要将矩阵下标两值互换。相乘之后,须要再次互换下标值还原成矩阵下标。

    因此对于一个经过旋转 t 度之后数组下标为[m‘, n’]的像素值,还原成原图中的数组下标[m; n]计算为:

    [cos(t) -sin(t); sin(t) cos(t)] ([m'; n'] - [Mat.rows/2; Mat.cols/2]) = [m; n] - [Mat.rows/2; Mat.cols/2]

    源码附上:

    Mat nearestNeighRotate(cv::Mat img, float angle)
    {
    	int len = (int)(sqrtf(pow(img.rows, 2) + pow(img.cols, 2)) + 0.5);
    
    	Mat retMat = Mat::zeros(len, len, CV_8UC3);
    	float anglePI = angle * CV_PI / 180;
    	int xSm, ySm;
    
    	for(int i = 0; i < retMat.rows; i++)
    		for(int j = 0; j < retMat.cols; j++)
    		{
    			xSm = (int)((i-retMat.rows/2)*cos(anglePI) - (j-retMat.cols/2)*sin(anglePI) + 0.5);
    			ySm = (int)((i-retMat.rows/2)*sin(anglePI) + (j-retMat.cols/2)*cos(anglePI) + 0.5);
    			xSm += img.rows / 2;
    			ySm += img.cols / 2;
    
    			if(xSm >= img.rows || ySm >= img.cols || xSm <= 0 || ySm <= 0){
    				retMat.at<Vec3b>(i, j) = Vec3b(0, 0);
    			}
    			else{
    				retMat.at<Vec3b>(i, j) = img.at<Vec3b>(xSm, ySm);
    			}
    		}
    
    	return retMat;
    }
    好,我们来測试看看:

    int main() 
    { 
        Mat img = imread("../HelloWorld.png");
        retImg = nearestNeighRotate(img, -20.f);
        namedWindow("nearNeigh", CV_WINDOW_AUTOSIZE);
        imshow("nearNeigh", retImg);
    
        waitKey(); 
        cvDestroyAllWindows();
        return 0; 
    } 
    结果(旋转了20度)为

  • 相关阅读:
    mybatis之衣服商城
    mybatis之增加操作
    There is no getter for property named 'answer' in 'class (N1)
    java.lang.ClassNotFoundException原因
    Openstack(Kilo)安装系列之环境准备(一)
    Python标识符
    Python命令行参数
    Python中文编码
    嵌入式数据库H2的安装与配置
    saltstack之nginx部署
  • 原文地址:https://www.cnblogs.com/slgkaifa/p/7143113.html
Copyright © 2020-2023  润新知