我为中国第一张火星照片做鱼眼矫正
点击上方“小白学视觉”,选择加"星标"或“置顶”
重磅干货,第一时间送达
本文转自|CVer
以下是昨日发布的内容。今天把代码整理了一下,放到GitHub。包含注释和空行,C++代码一共70行,欢迎测试及指正!代码链接:
https://github.com/ShiqiYu/mars-fisheye-correct
2021年5月19日18点多,中国火星探测器拍摄的第一张图片在互联网上发布。图片是火星车的前避障相机拍摄,为了追求广角,所以拍到的照片有鱼眼变形。火星地面成了曲面,而非平面,如下图。
作为一名计算机视觉的从业人员,我觉得应该用自己的知识做点什么。晚上陪孩子游泳回家就9点多了,马上动手!
为了表达敬意,不能用现成的程序来做这个事,我选择了C++和OpenCV库,纯手工制作,逐个像素计算和搬运!大约花了30分钟,矫正结果出来了,如下图。可以看到火星地平面变平了!
鱼眼矫正的原理图需要理解摄像机的小孔成像模型,以及了解一点立体几何知识,高中数学足够。成像原理示意图如下,然后再动手写个程序就可以了。
上图来自论文:Chan, Sixian & Zhou, Xiaolong & Huang, Chengbin & Chen, Shengyong & Li, Youfu. (2016). An improved method for fisheye camera calibration and distortion correction. 579-584. 10.1109/ICARM.2016.7606985.
为了更让大家更容易地理解鱼眼镜头成像,我手绘了如下示意图,时间仓促有点简陋,望谅解。
70行C++代码如下:
using namespace cv;
using namespace std;
// map the fisheye image position to the rectilinear image position
// input: src_x, src_y, center_x, center_y, R,
// output: dst_x, dst_y
int rectxy2fisheyexy(double src_x, double src_y,
double *dst_x, double *dst_y,
double center_x, double center_y,
int image_width,
double R)
{
double phi;
double theta;
double D = sqrt( R * R - image_width*image_width/4);
src_x -= center_x;
src_y -= center_y;
phi = atan( sqrt( double(src_x*src_x+src_y*src_y))/ D );
theta = atan2(src_y, src_x);
*dst_x = R * sin(phi) * cos(theta) + center_x;
*dst_y = R * sin(phi) * sin(theta) + center_y;
return 0;
}
int main(int argc, char ** argv)
{
if(argc != 2)
{
cout << "Usage: " << argv[0] << " filename.jpg" << endl;
return -1;
}
// read a fisheye image
Mat input = imread(argv[1]);
if(input.empty())
{
cerr << "Cannot read input image file " << argv[1] << endl;
return -1;
}
double fisheye_radius = 1500; //you can adjust the parameter in range [1500, +INF]
int input_width = input.cols;
int input_height = input.rows;
// the output image is 1.25x large
int output_width = cvRound(input_width*1.25);
int output_height = cvRound(input_height*1.25);
Mat output(output_height, output_width, input.type(), Scalar(0,0,0));
// copy each pixel from the fisheye image
// the current implementation is using NN
// bilinear interpolation can make the result more smooth
for ( int r = 0; r < output.rows; r++)
for ( int c = 0; c < output.cols; c++)
{
double src_r = 0;
double src_c = 0;
rectxy2fisheyexy(c-(output_width-input_width)/2 , r-(output_height-input_width)/2, &src_c, &src_r,
input_width/2.0, input_height/2.0,
input_width, fisheye_radius);
// copy the current pixel if it's in the range
if ( src_r > 0 && src_r < input_height-1 && src_c > 0 && src_c < input_width-1)
//using pointer nor at() functioin can gain better performance
output.at<Vec3b>(r, c) = input.at<Vec3b>( cvRound(src_r), cvRound(src_c));
}
// save the result and show it in a window
imwrite("result.jpg", output);
imshow("result", output);
waitKey(0);
return 0;
}
交流群
欢迎加入公众号读者群一起和同行交流,目前有SLAM、三维视觉、传感器、自动驾驶、计算摄影、检测、分割、识别、医学影像、GAN、算法竞赛等微信群(以后会逐渐细分),请扫描下面微信号加群,备注:”昵称+学校/公司+研究方向“,例如:”张三 + 上海交大 + 视觉SLAM“。请按照格式备注,否则不予通过。添加成功后会根据研究方向邀请进入相关微信群。请勿在群内发送广告,否则会请出群,谢谢理解~