我的编程空间,编程开发者的网络收藏夹
学习永远不晚

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

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

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

原理

我们通过相机拍摄的图片存在各种畸变,其中投影畸变使得原本平行的直线不再平行,就会产生照片中近大远小的效果,要校正这一畸变,书中给了很多方法,这里是其中的一种。

我们可以将投影变换拆分成相似变换、仿射变换和投影变换三部分, 如下图,

其中相似变换和仿射变换不会改变infinite line,只有投影变换会改变。因此只要找到畸变图像中的这条线,就能够恢复图像的仿射特性(相当于逆转投影变换)。而要确定这条线的位置,就得至少知道线上的两个点。我们知道,所有平行线的交点都在infinite line上面,因此,我们只需要找到图像上的两对平行线(原本是平行,图像上不再平行),求出对应的两个交点,就能找到infinite line了,如下图

进而可以图像的恢复仿射特性。

实现思路

首先我们的畸变图像如下图,

利用公式:


l = x1 × x2

可以通过x1、x2的齐次坐标求出两点连线l的齐次坐标。在图中我们找到两对平行线l1、l2和l3、l4,如下图

利用公式:


x = l1 × l2

可以通过l1、l2以及l3、l4的齐次坐标分别求出两对平行线的交点A12、A34,直线A12A34就是我们要找的infinite line。假设该直线的齐次坐标为(l1,l2,l3),那么通过矩阵:


H = ((1,0,0),(0,1,0),(l1,l2,l3))

就能够将直线(l1,l2,l3)变换成(0,0,1),即将该直线还原成为infinite line。同理我们也可以利用H矩阵,通过公式:


x = Hx'

还原投影畸变。

主要代码

代码一共需要运行两次

第一次运行的主函数:


int main()
{
	Mat class="lazy" data-src = imread("distortion.jpg", IMREAD_GRAYSCALE);
	IplImage *class="lazy" data-src1 = cvLoadImage("distortion.jpg");
	//第一步,通过鼠标获取图片中某个点的坐标,运行第一步时注释掉Rectify(points_3d, class="lazy" data-src, class="lazy" data-src1);,将获取到的八个点写入
	//points_3d[8]坐标数组中,因为是齐次坐标,x3 = 1
	GetMouse(class="lazy" data-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(class="lazy" data-src1);,解除注释Rectify(points_3d, class="lazy" data-src, class="lazy" data-src1);
	//Rectify(points_3d, class="lazy" data-src, class="lazy" data-src1);
	imshow("yuantu", class="lazy" data-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(class="lazy" data-src, temp, pt, &font, cvScalar(255, 255, 255, 0));
        cvCircle(class="lazy" data-src, pt, 2, cvScalar(255, 0, 0, 0), CV_FILLED, CV_AA, 0);
        cvShowImage("class="lazy" data-src", class="lazy" data-src);
    }
}

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

    cvShowImage("class="lazy" data-src", class="lazy" data-src);
    waitKey(0);
}

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

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


ab // cd		  ac // bd

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

第二次运行的主函数:


int main()
{
	Mat class="lazy" data-src = imread("distortion.jpg", IMREAD_GRAYSCALE);
	IplImage *class="lazy" data-src1 = cvLoadImage("distortion.jpg");
	//第一步,通过鼠标获取图片中某个点的坐标,运行第一步时注释掉Rectify(points_3d, class="lazy" data-src, class="lazy" data-src1);,将获取到的八个点写入
	//points_3d[8]矩阵中,因为是齐次坐标,x3 = 1
	//GetMouse(class="lazy" data-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(class="lazy" data-src1);,解除注释Rectify(points_3d, class="lazy" data-src, class="lazy" data-src1);
	Rectify(points_3d, class="lazy" data-src, class="lazy" data-src1);
	imshow("yuantu", class="lazy" data-src);
	waitKey(0);	
}

校正函数:


void Rectify(Point3d* points, Mat class="lazy" data-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(class="lazy" data-src.rows, class="lazy" data-src.cols, CV_8UC1);
    GetRectifingImage(vanishing_line[0], class="lazy" data-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 class="lazy" data-src, Mat dst)
{
    Size size_class="lazy" data-src = class="lazy" data-src.size();
    for (int i = 0; i < size_class="lazy" data-src.height; i++)
    {
        for (int j = 0; j < size_class="lazy" data-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_class="lazy" data-src.width && x1 >= 0 && x2 < size_class="lazy" data-src.height && x2 >= 0)
            {
                dst.at<uint8_t>(x2, x1) = class="lazy" data-src.at<uint8_t>(i, j);
            }
        }
    }
    imshow("class="lazy" data-src", class="lazy" data-src);
    imshow("dst", dst);
    waitKey(0);
}

运行结果如下图:

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

完整代码链接  提取码:qltt 

到此这篇关于利用C++ OpenCV 实现从投影图像恢复仿射特性的文章就介绍到这了,更多相关C++ OpenCV 投影图像恢复仿射特性内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

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

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

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

如何利用C++ OpenCV 实现从投影图像恢复仿射特性,针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。原理我们通过相机拍摄的图片存在各种畸变,其中投影畸变使得原本平行的直线
2023-06-21

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录