图像处理-投影图像恢复仿射特性2

前言

在我的上一篇笔记中记录了有关投影图像恢复仿射特性,去除投影畸变的方法,主要是通过找平行线的交点来完成的。这里将使用另外一种方法来找vanishing point,进而确定infinite line。这种方法更加适合类似例子中的这种比较整齐有规律的图像。

原理

对于一维空间(一条直线上)的射影几何,点的齐次坐标为(x1,x2),点的投影变换公式为:
在这里插入图片描述

投影变换矩阵H有三个自由度,每对点映射可以提供一个约束条件,即一个方程,因此只要找到三对点映射,建立三元一次方程组,就能求解出H,进而使用上面的投影变换公式就可以得到原直线上的无穷远点(1,0)对应投影图像上的一维齐次坐标。然后将一维齐次坐标变换为图像平面上的二维齐次坐标(x1,x2,x3)即可,接下来的方法原理和上一篇笔记就完全一样了。

思路

主要是怎样找到一条直线上的三对点映射,如下图所示:
请添加图片描述

a’、b’、c’是投影图像中共线的三点,对应原平面上的a、b、c三点,不难看出,在原平面上:

			    ab:bc = 1:1

然后通过图中的坐标,我们可以求出:

			 a'b':b'c' = 1.104:1

在原平面中,令a、b、c三点的一维齐次坐标分别为
(0,1),(1,1),(2,1)。类似的,在投影平面中,令a’、b’、c’三点的一维齐次坐标分别为*(0,1),(1.104,1),(2.104,1)*。于是,我们得到了三对点映射,正如原理中所说,我们可以通过这三对点映射求出投影变换矩阵H,因为点取得比较特殊,该三元一次方程组求解比较简单,我们解出了:

		 H = ((1.161,0),(0.052,1))

通过这个矩阵H,我们使用一维空间点的投影变换公式求出原平面中直线abc上的无穷远点(1,0)对应投影平面上直线a’b’c’的一维齐次坐标为(22.4,1),我们利用相似三角形(其他方法也行)求出该点二维齐次坐标为(1340,-544,1)。同理,我们利用直线def也能求出另外一点(-522,-391,1),通过这两个点即可求出连线的齐次坐标为(0.00019,0.0023,1)。然后就可以使用上一篇笔记中的方法校正图像了。

代码

同样是先使用鼠标获取图像上点的坐标:

void on_mouse(int event, int x, int y, int flags, void* ustc)
{
    CvFont font;
    cvInitFont(&font, CV_FONT_HERSHEY_SIMPLEX, 0.5, 0.5, 0, 1, CV_AA);

    if (event == CV_EVENT_LBUTTONDOWN)
    {
        CvPoint pt = cvPoint(x, y);
        char temp[16];
        sprintf(temp, "(%d,%d)", pt.x, pt.y);
        cvPutText(src, temp, pt, &font, cvScalar(255, 255, 255, 0));
        cvCircle(src, pt, 2, cvScalar(255, 0, 0, 0), CV_FILLED, CV_AA, 0);
        cvShowImage("src", src);
    }
}

void GetMouse(IplImage *img)
{
    src = img;
    cvNamedWindow("src", 1);
    cvSetMouseCallback("src", on_mouse, 0);

    cvShowImage("src", src);
    waitKey(0);
}

下面是校正相关的函数:

void RectifyByCrossRatio(Point2d points_3d_6[6], Mat src)
{
    vector<Point3f> vanishing_points;
    for (int i = 0; i < 2; i++)
    {
        //1、测量ad:bc
       float length_ratio =  MeasureLengthRatio(points_3d_6[i * 3], points_3d_6[i * 3 + 1], points_3d_6[i * 3 + 2]);
       //2、定义abc直线上的齐次坐标,变换前和变换后
       Point2f point_a = { 0, 1 };
       Point2f point_b = { 1, 1 };
       Point2f point_c = { 2, 1 };
       Point2f point_a1 = { 0, 1 };
       Point2f point_b1 = { length_ratio, 1 };
       Point2f point_c1 = { length_ratio + 1, 1 };
       vector<Point2f> points;
       vector<Point2f> points1;
       points.push_back(point_a);
       points.push_back(point_b);
       points.push_back(point_c);
       points1.push_back(point_a1);
       points1.push_back(point_b1);
       points1.push_back(point_c1);
       //计算变换矩阵H(2×2) H =((a,b),(c,1)),使用三组点映射
       float H[2][2] = { {1, 0},
                      {0, 1} };
       ComputeMappingMatrix(H, points, points1);
       //计算vanishing point的直线坐标
       Point2f point1_line = { H[0][0] * 1, H[1][0] * 1 };
       //对该坐标归一化
       point1_line.x = point1_line.x / point1_line.y;
       point1_line.y = 1;
       float vanishing_ratio = point1_line.x;
       //利用相似三角形,计算vanishing point的齐次坐标
       float x0 = points_3d_6[i * 3].x;
       float y0 = points_3d_6[i * 3].y;
       float x1 = points_3d_6[i * 3 + 1].x;
       float y1 = points_3d_6[i * 3 + 1].y;
       float x_vanishing = 0;
       float x = (x1 - x0) * vanishing_ratio / length_ratio + x0;
       float y = (y1 - y0) * vanishing_ratio / length_ratio + y0;
       Point3f vanishing_point = { x, y, 1 };
       vanishing_points.push_back(vanishing_point);
    }
    vector<vector<float>> vanishing_line;
    GetLineFromPoints(vanishing_points[0], vanishing_points[1], vanishing_line);
    Mat image = Mat::zeros(src.rows, src.cols, CV_8UC1);
    GetRectifingImage(vanishing_line[0], src, image);
}

//返回ad:bc的长度比
float MeasureLengthRatio(Point2d a, Point2d b, Point2d c)
{
    //首先计算ab和bc的长度
    float length_ab = sqrt((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y));
    float length_bc = sqrt((b.x - c.x) * (b.x - c.x) + (b.y - c.y) * (b.y - c.y));
    float ratio = 0;
    if (length_bc != 0)
        ratio = length_ab / length_bc;
    else
        cout << "error!" << endl;
    return ratio;
}

//利用三对点映射计算变换矩阵(2×2),这里用了更复杂的方法,其实第一个方程就能解出b = 0,
//进而转换完二元一次方程组
void ComputeMappingMatrix(float H[2][2], vector<Point2f> points, vector<Point2f> points1)
{
    //获得三元一次方程组的系数矩阵 (3×4)
    float coefficient_matrix[3][4] = { 0 };
    for (int i = 0; i < 3; i++)
    {
        coefficient_matrix[i][0] = points[i].x;
        coefficient_matrix[i][1] = 1;
        coefficient_matrix[i][2] = -points[i].x * points1[i].x;
        coefficient_matrix[i][3] = points1[i].x;
    }
    float delta[3][3] = { 0 };
    for (int i = 0; i < 3; i++)
    {
        for (int j = 0; j < 3; j++)
        {
            delta[i][j] = coefficient_matrix[i][j];
        }
    }
    float delta_x[3][3] = { 0 };
    for (int i = 0; i < 3; i++)
    {
        for (int j = 0; j < 3; j++)
        {
            if (j == 0)
                delta_x[i][j] = coefficient_matrix[i][3];
            else
                delta_x[i][j] = coefficient_matrix[i][j];
        }
    }
    float delta_y[3][3] = { 0 };
    for (int i = 0; i < 3; i++)
    {
        for (int j = 0; j < 3; j++)
        {
            if (j == 1)
                delta_y[i][j] = coefficient_matrix[i][3];
            else
                delta_y[i][j] = coefficient_matrix[i][j];
        }
    }
    float delta_z[3][3] = { 0 };
    for (int i = 0; i < 3; i++)
    {
        for (int j = 0; j < 3; j++)
        {
            if (j == 2)
                delta_z[i][j] = coefficient_matrix[i][3];
            else
                delta_z[i][j] = coefficient_matrix[i][j];
        }
    }
    //计算4个delta矩阵的行列式
    float det_delta = ComputeMatrixDet(delta);
    float det_delta_x = ComputeMatrixDet(delta_x);
    float det_delta_y = ComputeMatrixDet(delta_y);
    float det_delta_z = ComputeMatrixDet(delta_z);
    //求解三元一次方程组
    float a = det_delta_x / det_delta;
    float b = det_delta_y / det_delta;
    float c = det_delta_z / det_delta;
    H[0][0] = a;
    H[0][1] = b;
    H[1][0] = c;
    H[1][1] = 1;
}

//计算3×3矩阵的行列式
float ComputeMatrixDet(float matrix[3][3])
{
    float a1 = matrix[0][0];
    float a2 = matrix[1][0];
    float a3 = matrix[2][0];
    float b1 = matrix[0][1];
    float b2 = matrix[1][1];
    float b3 = matrix[2][1];
    float c1 = matrix[0][2];
    float c2 = matrix[1][2];
    float c3 = matrix[2][2];
    return a1 * b2 * c3 + a2 * b3 * c1 + a3 * b1 * c2 - a3 * b2 * c1 - a2 * b1 * c3 - a1 * b3 * c2;
}
  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值