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

一、前言

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

二、代码说明

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

三、总结

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

  • 26
    点赞
  • 271
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
透视算法(perspective transform)是指将一个有透视效果的图像进行向变换,使其恢复为原本的平面图像。智能车摄像头透视算法的主要作用是对车辆行驶过程中捕捉的视频进行处理,使得图像中的车道线、交通标志等物体在图像中呈现出平面视图,方便车辆的跟踪和分析。 以下是一个简单的透视算法的C语言代码: ```c #include <stdio.h> #include <stdlib.h> #include <math.h> #define PI 3.141592653589 typedef struct { float x, y; } point; int main(int argc, char *argv[]) { // 定义原始图像和变换后的图像的宽和高 int width = 640, height = 480; // 定义变换矩阵 float mat[3][3] = { {1, 0, 0}, {0, 1, 0}, {0, 0, 1} }; // 定义变换前和变换后的四个点 point src[4] = { {0, 0}, {0, height}, {width, height}, {width, 0} }; point dst[4] = { {0, 0}, {0, height}, {width, height}, {width, 0} }; // 定义变换后的图像缓存 unsigned char *out = (unsigned char*)malloc(sizeof(unsigned char) * width * height * 3); // 计算变换矩阵 mat[0][0] = (dst[1].y - dst[0].y) / (src[1].y - src[0].y); mat[1][1] = (dst[3].x - dst[0].x) / (src[3].x - src[0].x); mat[1][0] = (dst[1].x - dst[0].x) / (src[1].y - src[0].y); mat[0][1] = (dst[3].y - dst[0].y) / (src[3].x - src[0].x); mat[2][0] = -mat[0][0] * src[0].x - mat[1][0] * src[0].y; mat[2][1] = -mat[0][1] * src[0].x - mat[1][1] * src[0].y; // 遍历变换后的图像像素 for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { // 计算变换前的坐标 float nx = mat[0][0] * x + mat[1][0] * y + mat[2][0]; float ny = mat[0][1] * x + mat[1][1] * y + mat[2][1]; // 判断是否越界 if (nx >= 0 && nx < width && ny >= 0 && ny < height) { // 将变换前的像素值赋给变换后的像素 out[(y * width + x) * 3 + 0] = in[(int)(ny * width + nx) * 3 + 0]; out[(y * width + x) * 3 + 1] = in[(int)(ny * width + nx) * 3 + 1]; out[(y * width + x) * 3 + 2] = in[(int)(ny * width + nx) * 3 + 2]; } } } // 释放变换后的图像缓存 free(out); return 0; } ``` 这段代码实现了一个简单的透视算法,其中主要的计算逻辑在计算变换矩阵的代码段中。透视变换矩阵的计算过程比较复杂,需要根据变换前后的点坐标来确定。在这段代码中,我们先定义了变换前后的四个点,然后根据这些点的坐标计算出变换矩阵。接着,我们遍历变换后的图像像素,计算出变换前的坐标,并根据坐标值将变换前的像素值赋给变换后的像素。最后,我们释放变换后的图像缓存即可。 这段代码只是一个简单的透视算法实现,对于实际的智能车摄像头应用还需要根据实际情况进行调整和优化。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值