赞
踩
去畸变请参考:图像处理去畸变教程_LoseHu的博客-CSDN博客
去畸变+逆透视请参考:智能车去畸变+逆透视教程_LoseHu的博客-CSDN博客
逆透视:如下
对于初做车的同学,看见摄像头图像神奇的侧视角难免会有些烦躁,我刚开始也是如此,所以在此,想详细的分享一下我们的透视变换方案。(实战教学)
先上各种效果图。
使用指针映射透视变换数组,只需要初始化映射一次,后续不需要时间
通用性强,可移植性高
还可以已知原图坐标情况下求透视后坐标
硬件:总钻风摄像头(由于多车组使用沁恒芯片,修改过硬件适应dvp)
镜头:140度镜头,畸变小于5%
摄像头分辨率:120*188(长*宽)
透视图分辨率:100*114(长*宽)
摄像头高度:30cm左右
总钻风是很常见的智能车摄像头,之所以选用140度镜头,是因为其角度大,且畸变率非常小,完全不影响图像处理,可以省去去畸变的烦恼,摄像头120*188是因为其比例可以使视野最大。
众所周知,对于十字这个元素,标准赛道(不算路肩、黑胶带),它是一个宽度为45cm的标准正方形。但是因为摄像头角度问题,十字的正方形在图片中是一个梯形。
那嚒我们所要做的,就是根据图片中的这一块梯形(实则是正方形),这一个信息,将图片进行拉伸变换,使之成为标准的俯视图。
w与w’在二维图中为1
已知结果图中坐标,通过上述公式就能在原图中找到对应的坐标。
那么这个3*3矩阵就是我们需要使用上位机求取的。
拍摄的十字图像(或正方形图像),灰度与二值化图均可,要能完全展示出正方形部分,如:
图像中可以显示出车头,因为在后续的透视过程中可以通过图像修改参数平移去除。
那么,根据图中的假正方形,就可以进行后续操作。
结果图宽高:得到的透视图像的宽高。
方形中心距顶部像素:在结果图中,正方形的中心,距离图像最上端多少个像素行(值越大,图像越向下平移)。
方形像素边长:在结果图中,正方形的边长相当于多少个像素(值越大,图片就会被放大更多,如果是45,就是一像素对应1cm距离)。
四个XY值:图中方形四个顶点的坐标,通过鼠标右键在图中点击就可以自动输入(点击顺序:左下---右下---左上---右上)。
逆透视上位机操作
最终矩阵会复制到您的剪切板中,如:
- {{-10.603578,4.261626,-292.576700},
- {0.665623,6.708527,-1508.674551},
- {0.000570,0.016935,-2.741442}};
1.图片经过逆透视操作后得到的结果图,因为对图片进行了拉伸,底边两个角落可能会出现无内容部分(如下图),使用时应当避免.可通过修改结果图大小,或者增大方形中心距顶部像素来对图形进行平移去除。
2.鼠标点击顺序要符合左下---右下---左上---右上。
有些图片打开失败,请检查图片后缀,有的是jpg格式,后缀却是bmp。
求得矩阵后,就可根据矩阵,和结果图的坐标,计算出结果图中的某个点,在原图中的坐标。
但如果每获取到一帧图像,都进行一次映射,非常耗费时间。所以我们使用指针。
在初始化时只需要对指针地址进行一次映射,以后只需要调用指针数组,就可以获取到透视后的图。(需要注意的是,和图像一样的大的指针数组可能会导致您的内存溢出,所以建议将透视图尺寸缩小,即减小RESULT_ROW与RESULT_COL,也可以避免黑边出现)
change_un_Mat[3][3] 是你通过上位机求取的矩阵,在您的剪切板中。
ImageUsed[0][0]代表图像左上角的值
PER_IMG 为用来透视变换的图片,如果使用灰度图,那么ImageUsed就是灰度图的逆透视图,
如果使用二值化图,那么ImageUsed就是二值化的逆透视图
BlackColor的值为没有内容部分的灰度值。
只需要初始化时调用一次ImagePerspective_Init()函数,只需要初始化时调用一次!!!!一次就行!!!!!!!
- //
- // Created by RUPC on 2022/9/20.
- //
- #define RESULT_ROW 100//结果图行列
- #define RESULT_COL 114
- #define USED_ROW 120 //用于透视图的行列
- #define USED_COL 188
- #define PER_IMG SimBinImage//SimBinImage:用于透视变换的图像
- #define ImageUsed *PerImg_ip//*PerImg_ip定义使用的图像,ImageUsed为用于巡线和识别的图像
- typedef unsigned char uint8_t; // 无符号 8 bits
- uint8_t *PerImg_ip[RESULT_ROW][RESULT_COL];
-
- void ImagePerspective_Init(void) {
-
- static uint8_t BlackColor = 0;
- double change_un_Mat[3][3] = { //114w*100h
- { -0.01609759704190238, 0.01932561893613478, -2.040617594981866 }, {
- 0.0004352209945470896, -0.000367865364438621,
- -0.7035606436969671 }, { 1.115951268069474e-005,
- 0.0001970185393508392, -0.03104642853440032 }, };
- for (int i = 0; i < RESULT_COL ;i++) {
- for (int j = 0; j < RESULT_ROW ;j++) {
- int local_x = (int) ((change_un_Mat[0][0] * i
- + change_un_Mat[0][1] * j + change_un_Mat[0][2])
- / (change_un_Mat[2][0] * i + change_un_Mat[2][1] * j
- + change_un_Mat[2][2]));
- int local_y = (int) ((change_un_Mat[1][0] * i
- + change_un_Mat[1][1] * j + change_un_Mat[1][2])
- / (change_un_Mat[2][0] * i + change_un_Mat[2][1] * j
- + change_un_Mat[2][2]));
- if (local_x
- >= 0&& local_y >= 0 && local_y < USED_ROW && local_x < USED_COL){
- PerImg_ip[j][i] = &PER_IMG[local_y][local_x];
- }
- else {
- PerImg_ip[j][i] = &BlackColor; //&PER_IMG[0][0];
- }
-
- }
- }
-
- }
-
-
- /*完成摄像头初始化后,调用一次ImagePerspective_Init,此后,直接调用ImageUsed 即为透视结果*/
- int main(void)
- {
-
- All_Init();//屏幕、摄像头、以及其他外设初始化
- ImagePerspective_Init();
- while(1)
- {
- if (mt9v03x_finish_flag_dvp == 1) {
- uint8_t show[RESULT_ROW][RESULT_COL];
- for(int i=0;i<RESULT_ROW;i++)
- {
- for(int j=0;j<RESULT_COL;j++)
- {
- show[i][j]=ImageUsed[i][j];
- }
- }
- ips114_show_gray_image(0,0,show[0],RESULT_COL,RESULT_ROW,RESULT_COL,RESULT_ROW,0);
- mt9v03x_finish_flag_dvp = 0;
-
- }
- }
- }
(看注释的sample,用不来就用上面)
- /**
- *@Name :ips114_show_gray_image_vec
- *@Description :ips114_show_gray_image_vec 显示透视变换指针所指的图像
- *@Param :
- *@Return :
- *@Sample :ips114_show_gray_image_vec(0,0,PerImg_ip,TRFED_COL,TRFED_ROW,TRFED_COL,TRFED_ROW,0);
- **/
- void ips114_show_gray_image_vec (uint16_t x, uint16_t y, uint8_t *p[][TRFED_COL], uint16_t width, uint16_t height, uint16_t dis_width, uint16_t dis_height, uint8_t threshold)
- {
- zf_assert(x < ips114_x_max);
- zf_assert(y < ips114_y_max);
-
- uint32_t i = 0, j = 0;
- uint16_t color = 0,temp = 0;
- uint32_t width_index = 0, height_index = 0;
-
- ips114_set_region(x, y, x+dis_width-1, y+dis_height-1); // 设置显示区域
-
- for(j=0;j<dis_height;j++)
- {
- height_index = j*height/dis_height;
- for(i=0;i<dis_width;i++)
- {
- width_index = i*width/dis_width;
- temp = *p[height_index][width_index]; // 读取像素点
- if(threshold == 0)
- {
- color=(0x001f&((temp)>>3))<<11;
- color=color|(((0x003f)&((temp)>>2))<<5);
- color=color|(0x001f&((temp)>>3));
- ips114_write_16bit_data(color);
- }
- else if(temp < threshold)
- ips114_write_16bit_data(BLACK);
- else
- ips114_write_16bit_data(WHITE);
- }
- }
- }
其中包含了测试图包
CSDN:https://download.csdn.net/download/wu58430/86399773
推荐github:https://github.com/wu58430/RUBO-IPM
如果只使用用途,下载github中Release即可。
现在已经完成了逆透视、去畸变、逆透视+去畸变的操作,除非程序有重大bug,后续不会考虑更新。此三种只是图片处理方法,如此软件只适用于简化和降低操作的门槛,以便大家共同进步,图像处理方法无好坏之分,但确实有精妙与粗糙之别。
此软件仅用于竞赛、学习交流,禁止任何商业用途,包括有营利性的、商业的教学指导活动。
2022.8.23 修复了不同版本windows兼容问题
2022.8.27 修复中文路径、视图大小异常问题,缩小程序体积
2022.9.23 增加了彩色图像显示,输出格式改为数组
2022.10.7 代码迁移至QT6.3.1环境,加入去畸变,加入保存图片功能
2022.10.29 融合两种方法,修复保存图像色彩错误问题
2023.1.12 修复去畸变+逆透视下位机代码中未考虑的可能
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。