备战第16届智能车-图像处理进阶-逆透视算法

备战第16届智能车-图像处理进阶-逆透视算法

  • 一、前言
  • 二、代码说明
    • 1、定义部分
    • 2、模型计算部分
    • 3、图像变换部分
  • 三、总结

一、前言

逆透视的原理在网上有许多的说明,但是实际可以使用的代码却并不多。
代码并非完全原创,但是这种形式对于初学者会更友好

二、代码说明

1、定义部分

定义输入输出图像尺寸及数组

//输入图片尺寸
#define CAMERA_H  60
#define CAMERA_W  94

//输出图片尺寸
#define OUT_H  60
#define OUT_W  94

uint8 image[CAMERA_H][CAMERA_W];      //原图像
uint8 image_final[OUT_H][OUT_W];      //逆变换图像
double map_square[CAMERA_H][CAMERA_W][2];//现实映射
int map_int[OUT_H][OUT_W][2];//图像映射

2、模型计算部分

此部分用于计算现实与图像之间的映射关系
同一摄像头角度和高度对应的模型完全相同,只需要在主程序开始前计算一次即可
具体的参数调节详见注释

void Calculate(void)//计算逆变换模型 一般放在最早只运算一次 比较费时间 同一摄像头角度以及高度模型完全一样
{
	double angle = 0.8;//摄像头俯仰角 必须小于1 摄像头越平值越大
    double dep = 3.8;//视点到投影面距离


    double prop_j = 1;//上下宽度矫正,大于1
    double prop_i = 0;//密度修正系数,大于-1,小于1


    double j_large = 1.6;//横向放大倍数
    uint8 i_abodon = 7;//上方舍弃的行距离 舍弃行数越多图像前瞻越少,图像也越宽

    double hight = 50;//摄像头高度(调节也没有效果,只改变参数坐标)


    uint8 i;//图像从上到下行数
    uint8 j;//图像从左到右行数
    uint8 ii;
    //图片坐标
	double xg;
	double yg;
	//原始坐标
    double x0;
	double y0;

	double zt;

    double sin_a;
    double cos_a;
    sin_a = sin(angle);
    cos_a = cos(angle);

    //初始化摄像头坐标系
	for (i=0; i<CAMERA_H; i++)
	{
		for (j=0; j<CAMERA_W; j++)
		{
		    map_square[i][j][0] = ((float)CAMERA_H/2  - (float)i + 0.5)/10;
		    map_square[i][j][1] = ((float)j - (float)CAMERA_W/2 + 0.5)/10;
		}
	}

    //横向拉伸(微调)
    for (i=0; i<CAMERA_H; i++)
	{
		for (j=0; j<CAMERA_W; j++)
		{
            map_square[i][j][1] = map_square[i][j][1] * (1*(CAMERA_H-1-i) + (1/prop_j)*i)/(CAMERA_H-1);
		}
	}

	//逆透视变换……直接套公式
    for (i=0; i<CAMERA_H; i++)
	{
		for (j=0; j<CAMERA_W; j++)
		{
		    xg = map_square[i][j][1];
		    yg = map_square[i][j][0];
            y0 = (yg*dep + hight*cos_a*yg + hight*dep*sin_a)/(dep*cos_a-yg*sin_a);
            zt = -y0*sin_a-hight*cos_a;
            x0 = xg * (dep-zt) /dep;
		    map_square[i][j][1] = x0;
            map_square[i][j][0] = y0;
		}
	}

    double prop_x;//横坐标缩放比例
    prop_x = (OUT_W-1)/(map_square[i_abodon][CAMERA_W-1][1] - map_square[i_abodon][0][1]);
    for (i=0; i<CAMERA_H; i++)
	{
		for (j=0; j<CAMERA_W; j++)
		{
		    map_square[i][j][1] *= prop_x;
		    map_square[i][j][1] *= j_large;
		    map_square[i][j][1] = map_square[i][j][1] + OUT_W/2 -0.5*OUT_W/CAMERA_W;
		}
	}



	//前后方向
	double move_y;
	double prop_y;
	move_y = map_square[CAMERA_H-1][0][0];
	for (i=0; i<CAMERA_H; i++)
	{
		for (j=0; j<CAMERA_W; j++)
		{
		    map_square[i][j][0] -=  move_y;
		}
	}
	prop_y = (OUT_H-1)/map_square[i_abodon][0][0];
	for (i=0; i<CAMERA_H; i++)
	{
		for (j=0; j<CAMERA_W; j++)
		{
		    map_square[i][j][0] *=  prop_y;
		    map_square[i][j][0] =  OUT_H - OUT_H/CAMERA_H - map_square[i][j][0];
		}
	}

    //前后拉伸
    double dis_ever[CAMERA_H];
    double dis_add[CAMERA_H];
    double adjust_y[CAMERA_H];//每一行调整的值

    //计算每行代表的宽度(原为1)
    for (i=0; i<CAMERA_H; i++)
	{
	    dis_ever[i] = ((1+prop_i)*(CAMERA_H-1-i) + (1-prop_i)*i)/(CAMERA_H-1);
	}
	dis_add[0] = 0;
    for (i=0; i<CAMERA_H; i++)
	{
	    if(i==0)
        {
            dis_add[i] = 0;
        }
        else
        {
            dis_add[i] = dis_add[i-1] + dis_ever[i-1];
        }
	}
	adjust_y[0] = 1;
	for (i=1; i<CAMERA_H; i++)
	{
        adjust_y[i] = dis_add[i]/i;
	}

	for (i=0; i<CAMERA_H; i++)
	{
		for (j=0; j<CAMERA_W; j++)
		{
		    map_square[i][j][0] *=  adjust_y[i];
		}
	}
	double y_fix;
	y_fix = (OUT_H-1)/map_square[CAMERA_H-1][0][0];
	for (i=0; i<CAMERA_H; i++)
	{
		for (j=0; j<CAMERA_W; j++)
		{
		    map_square[i][j][0] *=  y_fix;
		}
	}
    //逆映射,开始投入新尺寸的图像
    //前后方向
    double far;
    double far_min;
    int near;

    for (i=0; i<OUT_H; i++)
	{
	    far_min = OUT_H;
	    for (ii=0; ii<CAMERA_H; ii++)
        {
            far = (double)i - (double)(map_square[ii][CAMERA_H/2][0]);
            if(far<0)
            {
                far = -far;
            }
            if(far<far_min)
            {
                far_min = far;
                near = ii;
            }
        }
		for (j=0; j<OUT_W; j++)
		{
		    map_int[i][j][0] = near;
		}

	}
	//左右方向
	int jj;
	double left_lim;
	double right_lim;
	for (i=0; i<OUT_H; i++)
	{
	    //计算每一行要按照哪一行来取
	    ii = map_int[i][OUT_W/2][0];
	    left_lim = map_square[ii][0][1];
	    right_lim = map_square[ii][CAMERA_W-1][1];
		for(j=0; j<OUT_W; j++)
		{
		    if(j<left_lim-1 || j>right_lim+1)
            {
                map_int[i][j][1] = 255;
            }
            else
            {
                far_min = CAMERA_W;
                for(jj=0; jj<CAMERA_W; jj++)
                {
                    far = (double)j - (double)(map_square[ii][jj][1]);
                    if(far<0)
                    {
                        far = -far;
                    }

                    if(far<far_min)
                    {
                        far_min = far;
                        near = jj;
                    }

                }
                map_int[i][j][1] = near;
            }

		}

	}
}

3、图像变换部分

基础的图像变换,没有什么难度

void Change(void) //图像变换函数
{
	//修改图像
	for (i=0; i<OUT_H; i++)
    {
        for(j=0; j<OUT_W; j++)
        {
            if(map_int[i][j][1] == 255)
            {
                image_final[i][j] = 0x77; //灰色
            }
            else
            {
                image_final[i][j] = image[map_int[i][j][0]][map_int[i][j][1]];
            }
        }
    }
}

三、总结

逆透视的方法在处理许多特殊元素的时候可以起到意想不到的结果,但是其本身也具有一定的局限性,需要使用者自行抉择。

你可能感兴趣的:(图像处理)