如何利用C++ OpenCV 实现从投影图像恢复仿射特性

技术如何利用C++ OpenCV 实现从投影图像恢复仿射特性如何利用C++ OpenCV 实现从投影图像恢复仿射特性,针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易

如何利用C OpenCV从投影图像中还原仿射特征,针对这个问题,本文详细介绍了相应的分析和解决方法,希望能帮助更多想要解决这个问题的朋友找到一个更简单易行的方法。

原理

我们相机拍的照片有各种失真。其中投影失真使原本平行的直线不再平行,会产生画面近大远小的效果。为了纠正这种扭曲,书中有很多方法,这里是其中之一。

我们可以将投影变换分为三个部分:相似变换、仿射变换和投影变换,如下图所示。

如何利用C++OpenCV实现从投影图像恢复仿射特性

相似变换和仿射变换不会改变无限线,只会改变投影变换。因此,只要找到失真图像中的这条线,就可以恢复图像的仿射特征(相当于逆投影变换)。要确定这条线的位置,你必须知道这条线上至少有两点。我们知道所有平行线的交点都在无限线上,所以我们只需要在图像上找到两对平行线(原本平行,但在图像上不再平行),找出对应的两个交点,然后就可以找到无限线,如下图所示。

如何利用C++OpenCV实现从投影图像恢复仿射特性

从而可以恢复图像的仿射特征。

00-1010首先,我们扭曲的图像如下,

如何利用C++OpenCV实现从投影图像恢复仿射特性

使用公式:

l=x1x2如何利用C++OpenCV实现从投影图像恢复仿射特性

两点连线L的齐次坐标可以由x1和x2的齐次坐标得到。在图中,我们找到两对平行线l1、l2、l3和l4,如下所示。

如何利用C++OpenCV实现从投影图像恢复仿射特性

使用公式:

X=l1l2。两对平行线的交点A12和A34可以分别由l1、l2、l3和l4的齐次坐标来计算。直线A12A34就是我们要找的无限线。假设直线的齐次坐标是(l1,l2,l3),那么通过矩阵:

H=((1,0,0),(0,1,0),(l1,l2,l3))可以把直线(l1,l2,l3)变换成(0,0,1),即把直线化简为无限长的直线。同样,我们也可以使用H矩阵,通过公式:

X=Hx '恢复投影失真。

实现思路

代码需要运行两次。

第一次运行的主要功能:

intmain()

{

Matsrc=imread('distortion.jpg ',IMREAD _ GRADE);

IPL image * Sr C1=cvLoadImage(' distortion . jpg ');

//第一步,用鼠标获取图片中某一点的坐标,运行第一步时注释掉procure(points _ 3d,src,Sr C1);写出获得的八分。

//points_3d[8]坐标数组,x3=1因为是齐次坐标。

GetMouse(Sr C1);

//在变形图上输入8个关键点。

point 3d points _ 3d[8]={ point 3d(99,147,1),Point3d(210,93,1),Point3d(144,184,1),Point3d(261,122,1),

点3d(144,184,1),点3d(99,147,1),Poi

nt3d(261, 122, 1), Point3d(210, 93, 1) };
//第二步,校正图像,运行此步骤时注释掉GetMouse(src1);,解除注释Rectify(points_3d, src, src1);
//Rectify(points_3d, src, src1);
imshow("yuantu", src);
waitKey(0);
}

其他函数:

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);
}

在弹出来的图片中点击任意地方可获得改点的图像坐标(x1,x2),如下图:

如何利用C++ OpenCV 实现从投影图像恢复仿射特性

我选取了a、b、c、d四个点,其中:

ab // cd		  ac // bd

将这四个点的坐标按照a、b、c、d、c、a、d、b的顺序填入points_3d[8]坐标数组中,第一次运行结束。

第二次运行的主函数:

int main()
{
	Mat src = imread("distortion.jpg", IMREAD_GRAYSCALE);
	IplImage *src1 = cvLoadImage("distortion.jpg");
	//第一步,通过鼠标获取图片中某个点的坐标,运行第一步时注释掉Rectify(points_3d, src, src1);,将获取到的八个点写入
	//points_3d[8]矩阵中,因为是齐次坐标,x3 = 1
	//GetMouse(src1);
	//输入畸变图上的8个关键点
	Point3d points_3d[8] = { Point3d(99, 147, 1), Point3d(210, 93, 1), Point3d(144, 184, 1), Point3d(261, 122, 1),
						Point3d(144, 184, 1), Point3d(99, 147, 1), Point3d(261, 122, 1), Point3d(210, 93, 1) };
	//第二步,校正图像,运行此步骤时注释掉GetMouse(src1);,解除注释Rectify(points_3d, src, src1);
	Rectify(points_3d, src, src1);
	imshow("yuantu", src);
	waitKey(0);	
}

校正函数:

void Rectify(Point3d* points, Mat src, IplImage* img)
{
    //通过输入的8个点得到4条连线
    vector<vector<float>> lines;
    int num_lines = 4;
    for(int i = 0; i < num_lines; i++)
    {
        //获取两点连线
        GetLineFromPoints(points[2 * i], points[2 * i + 1], lines);
    }
    //分别求取两个交点
    vector<Point3f> intersect_points;
    int num_intersect_points = 2;
    for (int i = 0; i < num_intersect_points; i++)
    {
        //计算交点
        GetIntersectPoint(lines[2 * i], lines[2 * i + 1], intersect_points);
    }
    //通过两个交点连线求消失线
    vector<vector<float>> vanishing_line;
    GetLineFromPoints(intersect_points[0], intersect_points[1], vanishing_line);
    //恢复矩阵
    float H[3][3] = {{1, 0, 0},
                     {0, 1, 0},
                     {vanishing_line[0][0], vanishing_line[0][1], vanishing_line[0][2]}};
    Mat image = Mat::zeros(src.rows, src.cols, CV_8UC1);
    GetRectifingImage(vanishing_line[0], src, image);
    int i = 0;
}
void GetLineFromPoints(Point3d point1, Point3d point2, vector<vector<float>> &lines)
{
    vector<float> line;
    //定义直线的三个齐次坐标
    float l1 = 0;
    float l2 = 0;
    float l3 = 0;
    l1 = (point1.y * point2.z - point1.z * point2.y);
    l2 = (point1.z * point2.x - point1.x * point2.z);
    l3 = (point1.x * point2.y - point1.y * point2.x);
    //归一化
    l1 = l1 / l3;
    l2 = l2 / l3;
    l3 = 1;
    line.push_back(l1);
    line.push_back(l2);
    line.push_back(l3); 
    lines.push_back(line);
}
void GetIntersectPoint(vector<float> line1, vector<float> line2, vector<Point3f> &intersect_points)
{
    Point3f intersect_point;
    //定义交点的三个齐次坐标
    float x1 = 0;
    float x2 = 0;
    float x3 = 0;
    x1 = (line1[1] * line2[2] - line1[2] * line2[1]);
    x2 = (line1[2] * line2[0] - line1[0] * line2[2]);
    x3 = (line1[0] * line2[1] - line1[1] * line2[0]);
    //归一化
    x1 = x1 / x3;
    x2 = x2 / x3;
    x3 = 1;
    intersect_point.x = x1;
    intersect_point.y = x2;
    intersect_point.z = x3;
    intersect_points.push_back(intersect_point);
}
int Round(float x)
{
    return (x > 0.0) ? floor(x + 0.5) : ceil(x - 0.5);
}
void GetRectifingImage(vector<float> line, Mat src, Mat dst)
{
    Size size_src = src.size();
    for (int i = 0; i < size_src.height; i++)
    {
        for (int j = 0; j < size_src.width; j++)
        {
            float x3 = line[0] * j + line[1] * i + line[2] * 1;
            int x1 = Round(j / x3);
            int x2 = Round(i / x3);
            if (x1 < size_src.width && x1 >= 0 && x2 < size_src.height && x2 >= 0)
            {
                dst.at<uint8_t>(x2, x1) = src.at<uint8_t>(i, j);
            }
        }
    }
    imshow("src", src);
    imshow("dst", dst);
    waitKey(0);
}

运行结果如下图:

如何利用C++ OpenCV 实现从投影图像恢复仿射特性

校正效果和点的选取有关,因为鼠标点击的那个点不一定是我们真正想要的点,建议一条直线的的两个点间距尽量大一些。

关于如何利用C++ OpenCV 实现从投影图像恢复仿射特性问题的解答就分享到这里了,希望

内容来源网络,如有侵权,联系删除,本文地址:https://www.230890.com/zhan/124801.html

(0)

相关推荐

  • 怎么解决oracle12.2 adg中的ORA-46952问题

    技术怎么解决oracle12.2 adg中的ORA-46952问题这篇文章主要讲解了“怎么解决oracle12.2 adg中的ORA-46952问题”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢

    攻略 2021年11月5日
  • Vue中怎么使用计算属性

    技术Vue中怎么使用计算属性这期内容当中小编将会给大家带来有关Vue中怎么使用计算属性,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。下面带大家了解一下Vue计算属性,介绍一下Vue计算

    攻略 2021年12月3日
  • 怎么用EthMon.php监听以太坊代币转账交易

    技术怎么用EthMon.php监听以太坊代币转账交易怎么用EthMon.php监听以太坊代币转账交易,相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。EthMo

    攻略 2021年10月23日
  • Java Servlet时遇到的小问题是怎样的

    技术Java Servlet时遇到的小问题是怎样的今天就跟大家聊聊有关Java Servlet时遇到的小问题是怎样的,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。

    攻略 2021年11月20日
  • 如何使用Spring Session 与 Spring security 完成网站登录改造

    技术如何使用Spring Session 与 Spring security 完成网站登录改造如何使用Spring Session 与 Spring security 完成网站登录改造,相信很多没有经验的人对此束手无策,

    攻略 2021年11月9日
  • node.js有什么指令

    技术node.js有什么指令小编给大家分享一下node.js有什么指令,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!

    攻略 2021年12月13日