利用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 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 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),如下图:

我选取了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);
}

运行结果如下图:

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

完整代码链接  提取码:qltt 

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

(0)

相关推荐

  • OpenCV和C++实现图像的翻转(镜像)、平移、旋转、仿射与透视变换

    目录 一.翻转(镜像) 二.仿射扭曲 获取变换矩阵 仿射扭曲函数 warpAffine 旋转 平移 三.仿射变换 四.透视变换 综合示例 总结 官网教程 一.翻转(镜像) 头文件 quick_opencv.h:声明类与公共函数 #pragma once #include <opencv2\opencv.hpp> using namespace cv; class QuickDemo { public: ... void flip_Demo(Mat& image); void rotat

  • C++之OpenCV图像高光调整具体流程

    实现原理 PS中的高光命令是一种校正由于太接近相机闪光灯而有些发白的焦点的方法.在用其他方式采光的图像中,这种调整也可用于使高光区域变暗.要实现图像的高光调整,首先要识别出高光区:再通过对高光区的色彩进行一定变换,使其达到提光或者暗化效果:最后也是最重要的,就是对高光区和非高光区的边缘作平滑处理. 下方介绍具体流程. 具体流程 1)读取识别图像的原图,并转灰度图,再归一化. // 生成灰度图 cv::Mat gray = cv::Mat::zeros(input.size(), CV_32FC1

  • 使用c++实现OpenCV图像横向&纵向拼接

    功能函数 // 图像拼接 cv::Mat ImageSplicing(vector<cv::Mat> images,int type) { if (type != 0 && type != 1) type = 0; int num = images.size(); int newrow = 0; int newcol = 0; cv::Mat result; // 横向拼接 if (type == 0) { int minrow = 10000; for (int i = 0;

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

    目录 原理 实现思路 主要代码 原理 我们通过相机拍摄的图片存在各种畸变,其中投影畸变使得原本平行的直线不再平行,就会产生照片中近大远小的效果,要校正这一畸变,书中给了很多方法,这里是其中的一种. 我们可以将投影变换拆分成相似变换.仿射变换和投影变换三部分, 如下图, 其中相似变换和仿射变换不会改变infinite line,只有投影变换会改变.因此只要找到畸变图像中的这条线,就能够恢复图像的仿射特性(相当于逆转投影变换).而要确定这条线的位置,就得至少知道线上的两个点.我们知道,所有平行线的交

  • Python利用OpenCV和skimage实现图像边缘检测

    目录 一.简介 二.opencv 实践 三.skimage 实践 一.简介 提取图片的边缘信息是底层数字图像处理的基本任务之一.边缘信息对进一步提取高层语义信息有很大的影响.大部分边缘检测算法都是上个世纪的了,OpenCV 的使用的算法是 Canny 边缘检测算法,大概是在 1986 年由 John F. Canny 提出了,似乎说明边缘检测算法的研究已经到达了瓶颈期.跟人眼系统相比,边缘检测算法仍然逊色不少. Canny 边缘检测算法是比较出色的算法,也是一种多步算法,可用于检测任何输入图像的

  • python opencv 直方图反向投影的方法

    本文介绍了python opencv 直方图反向投影的方法,分享给大家,具体如下: 目标: 直方图反向投影 原理: 反向投影可以用来做图像分割,寻找感兴趣区间.它会输出与输入图像大小相同的图像,每一个像素值代表了输入图像上对应点属于目标对象的概率,简言之,输出图像中像素值越高的点越可能代表想要查找的目标.直方图投影经常与camshift(追踪算法)算法一起使用. 算法实现的方法,首先要为包含我们感兴趣区域的图像建立直方图(样例要找一片草坪,其他的不要).被查找的对象最好是占据整个图像(图像里全是

  • Python摸鱼神器之利用树莓派opencv人脸识别自动控制电脑显示桌面

    前言 老早就看到新闻员工通过人脸识别监控老板来摸鱼. 有时候摸鱼太入迷了,经常在上班时间玩其他的东西被老板看到.自从在咸鱼上淘了一个树莓派3b,尝试做了一下内网穿透,搭建网站就吃灰了,接下来突发奇想就买了一个摄像头和延长线 接下来就是敲代码了 环境 树莓派3+ python3.7 win7 python3.6 过程 首先树莓派和电脑要在一个内网下面,就是一个路由器下面吧.要在树莓派设置里面开启摄像头,然后安装cv2,cv2有很多依赖库需要手动安装,很是费脑筋.原理介绍一下,人脸识别主要是依赖op

  • OpenCV实现低对比度图像脏污区域检测

    目录 1. 低对比度图像脏污区域检测 2. 实现方法介绍 3. C++源码实现 4.结果 总结 参考 1. 低对比度图像脏污区域检测 先上图: 第一张图如果不是标注结果,我都没有发现脏污区域在哪里,第二张图还清晰一些,基本可以看出来图像靠近左边缘的位置有偏暗的区域,这就是我们所说的脏污区域了,也是我们要检测的区域. 标注结果图: 2. 实现方法介绍 这里介绍两种实现方法, 第一种是用C++实现参考博文的方法,即利用梯度方法来检测,具体步骤如下: 对图像进行高斯模糊去噪,梯度计算对噪声很敏感: 调

  • C语言 OpenCV实现柱面投影

    目录 前言 实现代码 针对彩色图像 针对灰度图像 前言 在做全景拼接的时候,为了保持图片中的空间约束与视觉的一致性,需要进行柱面投影,否则离中心图像距离越远的图像拼接后变形越大. 柱面投影公式为 实现代码 针对彩色图像 int main() { cv::Mat image1 = cv::imread("images/1.jpg", 1); if (!image1.data) return 0; imshow("image1", image1); Mat imgOut

  • 利用python OpenCV去除视频水印

    上面的图片展示的是视频中的某一帧,需要将图片中的,实体拓展和退出全屏以及右上角的两个圆圈按钮给删除掉. 解决思路一个很简单的想法就是,从图片上其它两块背景相同的地方,拷贝两个块替换掉按钮位置的块 用下面的代码先从视频中导出一帧图片,然后统计需要删除按钮的坐标位置 实现代码如下: import cv2 # 创建读取视频的类 capture = cv2.VideoCapture("watermark.mp4") # 得到视频的高度 height = capture.get(cv2.CAP_

  • 详解在OpenCV中实现的图像标注技术

    目录 目录 图像注解 对图片注释的需求 图像注解的类型 分类法 物体检测 语义分割 用OpenCV实现图像注解 包围盒方法 KNN方法进行分割 结论 参考文献 图像标注在计算机视觉中很重要,计算机视觉是一种技术,它允许计算机从数字图像或视频中获得高水平的理解力,并以人类的方式观察和解释视觉信息.注释,通常被称为图片标签,是大多数计算机视觉模型发展中的一个关键阶段.本文将重点讨论在OpenCV的帮助下创建这些注释.以下是将要涉及的主题. 目录 图像注解 对图像注释的需求 图像注解的类型 用Open

  • python 利用for循环 保存多个图像或者文件的实例

    在实际应用中,会遇到保存多个文件或者图像的操作,利用for循环可以实现基本要求: for i in range(50): plt.savefig("%d.jpg"%(i+1)) 就解决了,出去50组图片. 和print格式化输出有异曲同工之妙: print ("she is %d years old"%(18)) 以上这篇python 利用for循环 保存多个图像或者文件的实例就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持我们.

  • opencv python如何实现图像二值化

    这篇文章主要介绍了opencv python如何实现图像二值化,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 代码如下 import cv2 as cv import numpy as np import matplotlib.pyplot as plt # 二值图像就是将灰度图转化成黑白图,没有灰,在一个值之前为黑,之后为白 # 有全局和局部两种 # 在使用全局阈值时,我们就是随便给了一个数来做阈值,那我们怎么知道我们选取的这个数的好坏呢?答

随机推荐