zoukankan      html  css  js  c++  java
  • Opencv 几何变换


    python API

    计算仿射矩阵

    1、方程法

    如果(0,0)、(200,0)、(0,200)三个坐标通过某仿射变换矩阵A转换为(0,0)、(100,0)、(0,100),通过三组坐标构造出六个方程,求解A

    import cv2
    import numpy as np
    src = np.array([[0,0],[200,0],[0,200]],np.float32)#数据类型必须为浮点型,否则出错
    dst = np.array([[0,0],[100,0],[0,100]],np.float32)
    A = cv2.getAffineTransform(src,dst)
    print(A)
    
    [[0.5 0.  0. ]
     [0.  0.5 0. ]]
    

    2、知道基本变换步骤后可以使用矩阵法计算仿射矩阵

    例如空间坐标先等比例缩放两倍,然后水平方向平移100,垂直方向平移200

    s = np.array([[0.5,0,0],[0,0.5,0],[0,0,1]])#缩放矩阵
    t = np.array([[1,0,100],[0,1,200],[0,0,1]])#平移矩阵
    A = np.dot(t,s)#矩阵相乘
    print(A)
    
    [[  0.5   0.  100. ]
     [  0.    0.5 200. ]
     [  0.    0.    1. ]]
    

    3、等比例缩放的仿射运算Opencv提供了cv2.getRotationMatrix2D(center, angle, scale)

    例子:计算坐标点(40, 50)为中心逆时针旋转30度的仿射变换矩阵。

    A = cv2.getRotationMatrix2D((40,50),30,0.5)
    print(A)
    
    [[ 0.4330127   0.25       10.17949192]
     [-0.25        0.4330127  38.34936491]]
    

    插值算法

    最近邻插值:采用离(x,y)点最近的整数坐标点的值来代替该点的数值

    双线性插值:通过(x,y)附件4个点的值来估计该点的数值

    实现仿射变换

    opencv提供了cv2.warpAffine函数

    import numpy as np
    import cv2
    import sys
    import math
    if __name__ == "__main__":
        image = cv2.imread("c:/users/76973/desktop/output_image1.jpg",cv2.IMREAD_GRAYSCALE)
        cv2.imwrite("img.jpg",image)
        #原图的高、宽
        h,w = image.shape[:2]
        # 仿射变换矩阵,缩小两倍
        A1 = np.array([[0.5,0,0],[0,0.5,0]],np.float32)
        d1 = cv2.warpAffine(image,A1,(w,h),borderValue = 125)
        #先缩小两倍,再平移
        A2 = np.array([[0.5,0,w/4],[0,0.5,h/4]],np.float32)
        d2 = cv2.warpAffine(image,A2,(w,h),borderValue = 125)
        #在d2的基础上,绕图像的中心点旋转
        A3 = cv2.getRotationMatrix2D((w/2.0,h/2.0),30,1)
        d3 = cv2.warpAffine(d2,A3,(w,h),borderValue = 125)
        cv2.imshow("image",image)
        cv2.imshow("d1",d1)
        cv2.imshow("d2",d2)
        cv2.imshow("d3",d3)
        cv2.waitKey(0)
        cv2.destroyAllWindows()
    

    旋转函数rotate

    opencv3.X的新特性

    这个函数不是通过仿射变换来完成图像的旋转,而是通过行列的互换,类似于矩阵的转置操作

    import cv2
    image = cv2.imread("c:/users/76973/desktop/output_image1.jpg",cv2.IMREAD_GRAYSCALE)
    #显示原图
    cv2.imshow("image",image)
    #图像旋转:
    rImg = cv2.rotate(image,cv2.ROTATE_90_CLOCKWISE)
    #显示旋转结果
    cv2.imshow("rImg",rImg)
    cv2.waitKey(0)
    cv2.destroyAllWindows()
    

    投影变换

    使用方程法计算投影变换的投影变换的仿射矩阵需要4个点对

    假设(0,0)(200,0)(0,200)(200,200)是原坐标,通过投影变换转换为(100,20)(200,20)(50,70)(250,70)

    import cv2
    import numpy as np
    src = np.array([[0,0],[200,0],[0,200],[200,200]],np.float32)
    dst = np.array([[100,20],[200,20],[50,70],[250,70]],np.float32)
    p = cv2.getPerspectiveTransform(src,dst)#返回投影矩阵p的类型是float64
    print(p)
    
    [[ 5.00e-01 -3.75e-01  1.00e+02]
     [ 0.00e+00  7.50e-02  2.00e+01]
     [-0.00e+00 -2.50e-03  1.00e+00]]
    

    实现投影变换

    import numpy as np
    import cv2
    image = cv2.imread("c:/users/76973/desktop/output_image1.jpg",cv2.IMREAD_GRAYSCALE)
    # 原图的宽和高
    h,w = image.shape
    src = np.array([[0,0],[w-1,0],[0,h-1],[w-1,h-1]],np.float32)
    dst = np.array([[50,50],[w/3,50],[50,h-1],[w-1,h-1]],np.float32)
    #计算投影变换矩阵
    p = cv2.getPerspectiveTransform(src,dst)
    #利用计算出的投影变换矩阵进行投影变化
    r = cv2.warpPerspective(image,p,(w,h),borderValue = 125)
    #显示
    cv2.imshow("image",image)
    cv2.imshow("warpperspective",r)
    cv2.waitKey(0)
    cv2.destroyAllWindows()
    

    极坐标变换

    笛卡尔坐标系转化为极坐标

    以变换中心为圆心的同一个圆上的点,在极坐标系中显示为一条直线

    (11,13)以(3,5)为中心进行极坐标变换

    import math
    r = math.sqrt(math.pow(11-3,2)+math.pow(13-5,2));
    theta = math.atan2(13-5,11-3)/math.pi*180#转换为角度
    r,theta
    
    (11.313708498984761, 45.0)
    

    opencv提供了函数:cartToPolar

    举例:(0,0)(1,0)(2,0)(0,1)(1,1)(2,1)(0,2)(1,2)(2,2)这九 个点以(1,1)为中心进行极坐标变换

    import cv2
    import numpy as np
    x = np.array([[0,1,2],[0,1,2],[0,1,2]],np.float64)-1
    y = np.array([[0,0,0],[1,1,1],[2,2,2]],np.float64)-1
    r,theta = cv2.cartToPolar(x,y,angleInDegrees = True)
    r,theta
    
    (array([[1.41421356, 1.        , 1.41421356],
            [1.        , 0.        , 1.        ],
            [1.41421356, 1.        , 1.41421356]]),
     array([[224.990448  , 270.        , 315.009552  ],
            [180.        ,   0.        ,   0.        ],
            [135.009552  ,  90.        ,  44.99045563]]))
    

    极坐标转化为笛卡尔坐标

    Opencv提供了函数:cv2.polarToCart

    举例:已知极坐标系θor中的(30,10)(31,10)(30,11)(31,11),其中θ是用角度表示的,问笛卡尔坐标系中哪四个坐标以(-12,15)为中心经过极坐标变换后得到这四个坐标。

    import cv2
    import numpy as np
    angle = np.array([[30,31],[30,31]],np.float32)
    r = np.array([[10,10],[11,11]],np.float32)
    x,y = cv2.polarToCart(r,angle,angleInDegrees = True)
    print(x,y)#这里得到的(x,y)以(0,0)为变换中心
    x+=-12
    y+=15
    x,y#以(-12,15)为变换中心
    
    [[8.660255 8.571674]
     [9.52628  9.428843]] [[5.0000005 5.150382 ]
     [5.5000005 5.66542  ]]
    
    
    
    
    
    (array([[-3.3397446, -3.4283257],
            [-2.4737196, -2.5711575]], dtype=float32),
     array([[20.      , 20.150383],
            [20.5     , 20.66542 ]], dtype=float32))
    

    实现极坐标变换

    Numpy中的tile(a,(m,n))函数

    该函数返回的矩阵是由m*n个a平铺而成的与MATLAB中的remat函数功能相同

    import numpy as np
    a = np.array([[1,2],[3,4]])
    print(a)
    b = np.tile(a,(2,3))#将a分别在垂直方向和水平方向复制2次和3次
    b
    
    [[1 2]
     [3 4]]
    
    
    
    
    
    array([[1, 2, 1, 2, 1, 2],
           [3, 4, 3, 4, 3, 4],
           [1, 2, 1, 2, 1, 2],
           [3, 4, 3, 4, 3, 4]])
    
    def polar(I,center,r,theta=(0,360),rstep=1.0,thetastep=360.0/(180*8)):
        #得到距离的最小、最大范围
        minr,maxr = r;
        #角度的最小返回
        mintheta,maxtheta = theta
        #输出图像的高宽
        H = int((maxr - minr) / rstep) + 1
        W = int((maxtheta - mintheta)/thetastep) + 1
        O = 125 * np.ones((H,W),I.dtype)
        #极坐标变换
        r = np.linspace(minr, maxr,H)
        r = np.tile(r,(W,1))
        r = np.transpose(r)
        theta = np.linspace(mintheta, maxtheta, W)
        theta = np.tile(theta,(H,1))
        x,y = cv2.polarToCart(r,theta,angleInDegrees = True)
        #最邻近插值
        for i in range(H):
            for j in range(W):
                px = int(round(x[i][j])+cx)
                py = int(round(y[i][j])+cy)
                if((px >= 0 and px <= w-1) and (py >= 0 and py <= h-1)):
                    O[i][j] = I[py][px]
        return 0
    import cv2
    import numpy as np
    I = cv2.imread("c:/users/76973/desktop/output_image1.jpg",cv2.IMREAD_GRAYSCALE)
    h,w = I.shape[:2]
    # 极坐标变换中心
    cx, cy = 508,503
    cv2.circle(I,(int(cx),int(cy)),10,(255.0,0,0),3)
    # 距离的最小和最大半径 # 200 550 270 340
    O = polar(I,(cx,cy),(200,550))
    # 旋转
    O = cv2.flip(0,0)
    # 显示原图和输出图像
    cv2.imshow("I",I)
    cv2.imshow("O",O)
    cv2.waitKey(0)
    cv2.destroyAllWindows()
    

    线性极坐标函数LinearPolar

    import cv2
    import sys
    src = cv2.imread("c:/users/76973/desktop/output_image1.jpg",cv2.IMREAD_GRAYSCALE)
    #显示原图
    cv2.imshow("src",src)
    # 图像的极坐标变换
    dst = cv2.linearPolar(src,(508,503),550,cv2.INTER_LINEAR)
    # 显示结果
    cv2.imshow("dst",dst)
    cv2.waitKey(0)
    cv2.destroyAllWindows()
    

    对数极坐标函数logPolar

    import cv2
    src = cv2.imread("c:/users/76973/desktop/output_image1.jpg",cv2.IMREAD_GRAYSCALE)
    #显示原图
    cv2.imshow("src",src)
    # 图像的极坐标变换
    M = 100
    dst = cv2.logPolar(src,(508,503),M,cv2.WARP_FILL_OUTLIERS)
    # 显示结果
    cv2.imshow("dst",dst)
    cv2.waitKey(0)
    cv2.destroyAllWindows()
    

    C++ API

    计算仿射矩阵

    # include <opencv2corecore.hpp>
    #include <opencv2highguihighgui.hpp>
    #include<opencv2/imgproc/imgproc.hpp>
    #include <iostream>
    using namespace std;
    using namespace cv;
    template<class T>
    void printMat(Mat matrix)
    {
    	for (int r = 0; r < matrix.rows; r++)
    	{
    		for (int c = 0; c < matrix.cols; c++)
    		{
    			if (typeid(T) == typeid(uchar)) {
    				cout << (int)(matrix.at<T>(r, c)) << ",";
    			}
    			else
    			{
    				cout << (matrix.at<T>(r, c)) << ",";
    			}
    		}
    		cout << endl;
    	}
    	cout << endl;
    }
    template<class T>
    void printMatT(Mat matrix)
    {
    	for (int r = 0; r < matrix.rows; r++)
    	{
    		for (int c = 0; c < matrix.cols; c++)
    		{
    			cout << (matrix.at<T>(r, c)) << ",";
    		}
    		cout << endl;
    	}
    	cout << endl;
    }
    
    int main()
    {
    	//# 方程法计算仿射矩阵
    	//## 第一种方式:将原坐标点和经过仿射变换后的坐标点放在Point2f数组中
    	//原位置坐标
    	Point2f src[] = { Point2f(0,0),Point2f(200,0),Point2f(0,200) };
    	//经过某仿射变换后对应的坐标
    	Point2f dst[] = { Point2f(0,0),Point2f(100,0),Point2f(0,100) };
    	//计算仿射矩阵
    	Mat A = getAffineTransform(src, dst);//注意:计算结果返回的数据类型是CV_64F
    	printMat<double>(A);
    	//## 第二种方式:将原坐标点和经过仿射变换后的坐标点保存在Mat中;每一行代表一个坐标,数据类型必须为CV32F
    	Mat src1 = (Mat_<float>(3, 2) << 0, 0, 200, 0, 0, 200);
    	Mat dst1 = (Mat_<float>(3, 2) << 0, 0, 100, 0, 0, 100);
    	//计算仿射矩阵
    	Mat A1 = getAffineTransform(src1, dst1);//注意:计算结果返回的数据类型是CV_64F
    	printMat<double>(A1);
    	//# 矩阵法计算仿射矩阵
    	Mat s = (Mat_<float>(3, 3) << 0.5, 0, 0, 0, 0.5, 0, 0, 0, 1);
    	Mat t = (Mat_<float>(3,3) << 1, 0, 100, 0, 1, 200, 0, 0, 1);
    	Mat A2 = t * s;//使用操作符“*”
    	printMat<float>(A2);
    	Mat A3;
    	gemm(t, s, 1.0, Mat(), 0, A3, 0);//矩阵相乘
    	printMat<float>(A3);
    
    	//计算等比例缩放仿射变换矩阵
    	Mat A4 = getRotationMatrix2D(Point2f(40, 50), 30, 0.5);//返回的数据类型是CV_64F
    	printMat<double>(A4);
    
    	//# 实现仿射变换 为了方便对图像的缩放Opencv提供了resize函数
    	Mat I = imread("c:/users/76973/desktop/output_image1.jpg", IMREAD_GRAYSCALE);
    	if (!I.data)
    		return -1;
    	/*第一种方式:利用warpAffine进行缩放*/
    	//构造缩放仿射矩阵,等比例缩小2倍
    	Mat s1 = (Mat_<float>(2, 3) << 0.5, 0, 0, 0, 0.5, 0);
    	Mat dst2;
    	warpAffine(I, dst2, s1, Size(I.cols /1, I.rows / 2));//图像缩放
    	/*第二种方式:利用resize等比例缩小两倍*/
    	Mat dst3;
    	resize(I, dst3, Size(I.cols/2 , I.rows/2 ), 0.5, 0.5);
    	//显示效果
    	imshow("I", I);
    	imshow("warpAffine", dst2);
    	imshow("resize", dst3);
    	//# rotate 函数
    	Mat rImg;
    	rotate(I, rImg, ROTATE_90_CLOCKWISE);
    	imshow("旋转", rImg);
    	//waitKey(0);
    
    	//# 投影变换
    	//原坐标
    	Point2f sc1[] = { Point2f(0,0),Point2f(200.0,0.0),Point2f(0,200.0),Point2f(200,200) };
    	//经过某投影变换后的坐标
    	Point2f dt1[] = { Point2f(100,20),Point2f(200,20),Point2f(50,70),Point2f(250,70) };
    	//计算投影变换矩阵
    	Mat P = getPerspectiveTransform(sc1, dt1);//返回类型为CV_64F
    	printMatT<double>(P);
    	//构建Mat
    	Mat sc2 = (Mat_<float>(4, 2) << 0, 0, 200, 0, 0, 200, 200, 200);
    	Mat dt2 = (Mat_<float>(4, 2) << 100, 20, 200, 20, 50, 70, 250, 70);
    	Mat P1 = getPerspectiveTransform(sc2, dt2);//返回类型为CV_64F
    	printMatT<double>(P1);
    	// # 笛卡尔坐标系转化为极坐标
    	Mat x = (Mat_<float>(3, 3) << 0, 1, 2, 0, 1, 2, 0, 1, 2) - 1;
    	Mat y = (Mat_<float>(3, 3) << 0, 0, 0, 1, 1, 1, 2, 2, 2) - 1;
    	Mat r, theta;
    	cartToPolar(x, y, r, theta, true);
    	printMat<float>(r);
    	printMat<float>(theta);
    	// # 极坐标转化为笛卡尔坐标
    	Mat angle1 = (Mat_<float>(2, 2) << 30, 31, 30, 31);
    	Mat r1 = (Mat_<float>(2, 2) << 10, 10, 11, 11);
    	Mat x1, y1;
    	polarToCart(r1, angle1, x1, y1, true);
    	x1 += -12.0;
    	y1 += 15.0;
    	printMatT<float>(x1);
    	printMatT<float>(y1);
    	return 0;
    }
    

    实现极坐标变换

    # include <opencv2corecore.hpp>
    #include <opencv2highguihighgui.hpp>
    #include<opencv2/imgproc/imgproc.hpp>
    #include <iostream>
    using namespace std;
    using namespace cv;
    Mat polar(Mat I, Point2f center, Size size, float minr = 0, float mintheta = 0, float thetaStep = 1.0 / 4, float rStep = 1.0)
    {
    	//构建r
    	Mat ri = Mat::zeros(Size(1, size.height), CV_32FC1);
    	for (int i = 0; i < size.height; i++)
    	{
    		ri.at<float>(i, 0) = minr + i * rStep;
    	}
    	Mat r = repeat(ri, 1, size.width);
    	//构建theta
    	Mat thetaj = Mat::zeros(Size(size.width, 1), CV_32FC1);
    	for (int j = 0; j < size.width; j++)
    	{
    		thetaj.at<float>(0, j) = mintheta + j * thetaStep;
    	}
    	Mat theta = repeat(thetaj, size.height, 1);
    	//将极坐标转化为笛卡尔坐标
    	Mat x, y;
    	polarToCart(r, theta, x, y, true);
    	//将坐标原点移动到中心点
    	x += center.x;
    	y += center.y;
    	//最邻近插值
    	Mat dst = 125 * Mat::ones(size, CV_8UC1);
    	for (int i = 0; i < size.height;  i++)
    	{
    		for (int j = 0; j < size.width; j++)
    		{
    			float xij = x.at<float>(i, j);
    			float yij = y.at<float>(i, j);
    			int nearestx = int(round(xij));
    			int nearesty = int(round(yij));
    			if (0 <= nearestx && nearestx < I.cols && (0 <= nearesty && nearesty < I.rows))
    			{
    				dst.at<uchar>(i, j) = I.at<uchar>(nearesty, nearestx);
    			}
    		}
    	}
    	return dst;
    }
    
    int _main()
    {
    	Mat I = imread("c:/users/76973/desktop/output_image1.jpg");
    	if (!I.data)
    		return -1;
    	//图像的极坐标变换
    	float thetaStep = 1.0 / 4;
    	float minr = 270;
    	Size size(int(360 / thetaStep), 70);
    	Mat dst = polar(I, Point2f(508, 503), size, minr);
    	//沿水平方向的镜像处理
    	flip(dst, dst, 0);
    	//显示原图和变换后的结果
    	imshow("I", I);
    	imshow("极坐标变换", dst);
    	waitKey(0);
    	return 0;
    }
    

    线性极坐标函数

    # include <opencv2corecore.hpp>
    #include <opencv2highguihighgui.hpp>
    #include<opencv2/imgproc/imgproc.hpp>
    #include <iostream>
    using namespace std;
    using namespace cv;
    
    int main()
    {
    	//输入图像
    	Mat src = imread("c:/users/76973/desktop/output_image1.jpg", IMREAD_ANYCOLOR);
    	if (!src.data)
    		return -1;
    	//极坐标变换
    	Mat dst;
    	linearPolar(src, dst, Point2f(508, 503), 550, CV_INTER_LINEAR);
    	//显示原图和极坐标变换图
    	imshow("原图", src);
    	imshow("极坐标变换图", dst);
    	waitKey(0);
    	return 0;
    }
    

    对数极坐标函数

    # include <opencv2corecore.hpp>
    #include <opencv2highguihighgui.hpp>
    #include<opencv2/imgproc/imgproc.hpp>
    #include <iostream>
    using namespace std;
    using namespace cv;
    int main()
    {
    	//读入图像
    	Mat src = imread("c:/users/76973/desktop/output_image1.jpg", IMREAD_ANYCOLOR);
    	//对数极坐标变换
    	Mat dst;
    	Point2f center(508, 503);
    	float M = 100;
    	logPolar(src, dst, center, M, WARP_FILL_OUTLIERS);
    	//显示结果
    	imshow("对数极坐标变换", dst);
    	imshow("原图", src);
    	waitKey(0);
    	return 0;
    }
    

    Jupyder 笔记下载

    https://gitee.com/pythonFCGa/Opencv/tree/master/

  • 相关阅读:
    删除文件时,提示 "操作无法完成..." 怎么处理
    对象的理解
    TP5架构下链接SQL数据库的一种方法
    关于URL隐藏index.php方法
    非典型的千万用户后台之路
    就这样,再见2015
    理想的程序员
    4个小例子告诉你:如何成为一名数据极客
    馆中窥职:小公司没那么糟糕
    JAVA设计模式详解(六)----------状态模式
  • 原文地址:https://www.cnblogs.com/PythonFCG/p/13860128.html
Copyright © 2011-2022 走看看