利用视差图合成新视点

利用视差图合成新视点,视差图一般通过图像匹配获取,以middlebury上的一张图为例,左边为原图(左图像),右边为对应视差图(灰度图)。

    

1. 正向映射:

简单的利用左视点原图和视差图进行视点合成,取每一个像素点处的视差值,然后计算新图像中像素点位置,然后赋值。前向映射,单点赋值代码如下。配置完Opencv可以直接跑,如下图:

 

#include <iostream>
#include <string>
#include <opencv2/opencv.hpp>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>

using namespace std;
using namespace cv;

void main()
{
	string imgPath = "data/source_images/teddy/";
	Mat srcImgL = imread("1.png");
	Mat dispL = imread("2.png", 0);
	dispL = dispL / 4;

	int imgHeight = srcImgL.rows;
	int imgWidth = srcImgL.cols;
	int channels = srcImgL.channels();

	Mat dstImgL = Mat::zeros(imgHeight, imgWidth, CV_8UC3);

	uchar* pImgDataL = (uchar*)srcImgL.data;
	uchar* pDispDataL = (uchar*)dispL.data;
	uchar* pDstDataL = (uchar*)dstImgL.data;

	cv::VideoWriter writer;
	writer.open("video.avi", VideoWriter::fourcc('D', 'I', 'V', 'X'), 30, Size(imgWidth, imgHeight));
	int cnt = 0;
	int viewCnt = 50;
	while (cnt != 4)
	{
		for (int k = 0; k < viewCnt; k++)
		{
			dstImgL.setTo(0);
			float interp;
			if (cnt % 2 == 0)    interp = (float)k / viewCnt;
			else  interp = float(viewCnt - k) / viewCnt;
			for (int j = 0; j < imgHeight; j++)
			{
				for (int i = 0; i < imgWidth; i++)
				{
					uchar dispL = pDispDataL[j*imgWidth + i];
					float offsetL = dispL * interp;
					int idL = (int)(offsetL + 0.5);  //计算视差值

					if (idL + i >= imgWidth) continue;
					//插值结果
					int idxResult = (j*imgWidth + i)*channels;
					int idx = (j*imgWidth + i + idL)*channels;
					for (int chan = 0; chan < channels; chan++)
					{
						pDstDataL[idxResult + chan] = pImgDataL[idx + chan];
					}
				}
			}
			namedWindow("show");
			imshow("show", dstImgL);
			waitKey(10);
			writer << dstImgL;
		}
		cnt++;
	}
	writer.release();
}

 边缘有锯齿,随后找时间加上反向映射以及双线性插值的版本。

2. 反向映射

先根据左视点视差图生成虚拟视点的视差图,然后反向映射得到每一个像素点在原图像中的浮点位置,利用线性插值获取最终颜色值。(虚拟视点位置视差图没有填充空洞版本),可见有很多裂纹。

 

#include <iostream>
#include <string>
#include <opencv.hpp>

using namespace std;
using namespace cv;

int index(int m, int n)
{
    if (m>=0 && m<n)
        return m;
    else if (m<0)
        return 0;
    else if (m>=n)
        return n-1;
}

void obtainNewDispMap(const Mat &refDisp, Mat &dstDisp, float value)
{
    int height=refDisp.rows;
    int width=refDisp.cols;
    uchar* pSrcDispData=(uchar*) refDisp.data;
    float* pDstDispData=(float*) dstDisp.data;
    for (int j=0; j<height; j++)
    {
        for (int i=0; i<width; i++)
        {
            int disp=(int)pSrcDispData[j*width+i];
            float newDisp=disp*(value);
            int inew=(int)(i-newDisp);
            inew=index(inew, width);
            pDstDispData[j*width+inew]=newDisp;
        } 
    }
}

void main(void)
{
    string imgPath="data/source_images/teddy/";
    Mat srcImgL=imread(imgPath+"imgL.png");
    Mat dispL=imread(imgPath+"dispL.png",0);
    dispL=dispL/4;

    int imgHeight=srcImgL.rows;
    int imgWidth=srcImgL.cols;

    Mat dstImgL=Mat::zeros(imgHeight,imgWidth, CV_8UC3);
    Mat dstImg=Mat::zeros(imgHeight,imgWidth, CV_8UC3);
    Mat dstNewDispImg=Mat::zeros(imgHeight,imgWidth, CV_32FC1);

    uchar* pImgDataL=(uchar*)srcImgL.data;
    uchar* pDispDataL=(uchar*)dispL.data;
    uchar* pDstDataL=(uchar*)dstImgL.data;

    VideoWriter writer("video.avi", CV_FOURCC('D','I','V','X'), 30, Size(imgWidth, imgHeight), 1); 
    int cnt=0;
    int viewCnt=50;
    while (cnt!=4)
    {
        float interp;
        for (int k=0; k<viewCnt; k++)
        {
            dstNewDispImg.setTo(255);
            dstImgL.setTo(0);
            if (cnt%2==0)   interp=(float)k/viewCnt;
            else interp=(float)(viewCnt-k)/viewCnt;

            obtainNewDispMap(dispL, dstNewDispImg, interp);

            float* pNewDispData=(float*)dstNewDispImg.data;
            for (int j=0; j<imgHeight; j++)
            {
                for (int i=0; i<imgWidth; i++)
                {
                    float disp=pNewDispData[j*imgWidth+i];
                    float id=i+disp;

                    int id0=floor(id);
                    int id1=floor(id+1);
                
                    float weight1=1-(id-id0);
                    float weight2=id-id0;

                    id0=index(id0, imgWidth);
                    id1=index(id1, imgWidth);

                    //插值结果
                    pDstDataL[j*imgWidth*3+i*3+0]=weight1*pImgDataL[j*imgWidth*3+id0*3+0]+weight2*pImgDataL[j*imgWidth*3+id1*3+0];
                    pDstDataL[j*imgWidth*3+i*3+1]=weight1*pImgDataL[j*imgWidth*3+id0*3+1]+weight2*pImgDataL[j*imgWidth*3+id1*3+1];
                    pDstDataL[j*imgWidth*3+i*3+2]=weight1*pImgDataL[j*imgWidth*3+id0*3+2]+weight2*pImgDataL[j*imgWidth*3+id1*3+2];
                }
            }
            namedWindow("virImg");
            imshow("virImg", dstImgL);
            waitKey(10);
            writer<<dstImgL;
        }
        cnt++;
    }
   writer.release();
}

 3.反向映射+空洞填充+双线性插值

上面生成虚拟视点位置的视差图时没有填充空洞,生成的虚拟视点会有很多裂纹存在。加上空洞填充能够有效消除裂纹。如下:

   

填充空洞后生的虚拟视点图如下,可见空洞裂纹得到有效消除:

--------------------------------- 业精于勤而荒于嬉 行成于思而毁于随 ---------------------------------

转自:利用视差图合成新视点

  • 3
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 7
    评论
利用视差图进行三维重建是一种常见的方法,下面是一个简单的步骤: 1. 获取视差图:首先,使用双目摄像头或其他立体视觉系统获取左右两个像,并通过立体匹配算法计算得到视差图视差图中的每个像素值表示了对应位置的深度或距离。 2. 点云生成:将视差图转换为点云数据。可以使用视差值与相机参数进行三角测量,将每个像素位置转换为三维空间中的点坐标。这样就得到了一组点云数据,表示了场景中物体表面的三维坐标。 3. 点云滤波:对生成的点云进行滤波处理,以去除噪声和无效的点。常用的滤波方法包括基于距离、法线、曲率等的滤波算法。 4. 点云配准:如果有多个视角的点云数据,需要将它们进行配准,以获得一个整体的三维模型。配准可以通过特征匹配、ICP(最近点迭代)算法等方法来实现。 5. 三维重建:根据生成的点云数据,可以使用三维重建软件(例如MeshLab、AutoCAD等)进行进一步的处理和编辑。可以进行表面重建、纹理映射、模型修复等操作,以得到更完整和真实的三维模型。 需要注意的是,利用视差图进行三维重建需要准确的摄像机参数和视差计算算法。因此,在进行实际操作之前,建议先对相机进行标定,并选择适合的视差计算方法。此外,还需要注意处理过程中的数据精度和噪声处理,以获得更高质量的三维重建结果。
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值