Калибровка OpenCV "рыбий глаз" обрезает слишком много полученного изображения
Я использую OpenCV для калибровки изображений, полученных с помощью камер с объективом типа "рыбий глаз".
Функции, которые я использую:
findChessboardCorners(...);
чтобы найти углы калибровочного шаблона.cornerSubPix(...);
уточнять найденные углы.fisheye::calibrate(...);
калибровать матрицу камеры и коэффициенты искажения.fisheye::undistortImage(...);
чтобы не искажать изображения, используя информацию камеры, полученную при калибровке.
Хотя получающееся изображение выглядит хорошо (прямые линии и т. Д.), Моя проблема заключается в том, что функция обрезает слишком большую часть изображения.
Это реальная проблема, так как я использую четыре камеры с углом 90 градусов между ними, и когда большая часть сторон обрезана, между ними нет области перекрытия, которая необходима, поскольку я собираюсь сшивать изображения.
Я смотрел на использование fisheye::estimateNewCameraMatrixForUndistortRectify(...)
но я не мог получить его, чтобы дать хорошие результаты, так как я не знаю, что я должен положить в качестве R
вход, как выход вектора вращения fisheye::calibrate
3xN (где N - количество калибровочных изображений) и fisheye::estimateNewCameraMatrixForUndistortRectify
требуется 1x3 или 3x3.
Изображения ниже показывают изображение моего результата искажения и пример такого результата, который я бы идеально хотел.
Undistortion:
Пример желаемого результата:
4 ответа
Я думаю, что столкнулся с подобной проблемой, ища "альфа" узел в getOptimalNewCameraMatrix для рыбий глаз.
Я калибровал его cv2.fisheye.calibrate, получил параметры K и D
K = [[ 329.75951163 0. 422.36510555]
[ 0. 329.84897388 266.45855056]
[ 0. 0. 1. ]]
D = [[ 0.04004325]
[ 0.00112638]
[ 0.01004722]
[-0.00593285]]
Это то, что я получаю
map1, map2 = cv2.fisheye.initUndistortRectifyMap(K, d, np.eye(3), k, (800,600), cv2.CV_16SC2)
nemImg = cv2.remap( img, map1, map2, interpolation=cv2.INTER_LINEAR, borderMode=cv2.BORDER_CONSTANT)
И я думаю, что это слишком много. Я хочу увидеть весь кубик Рубика
Я чиню это с
nk = k.copy()
nk[0,0]=k[0,0]/2
nk[1,1]=k[1,1]/2
# Just by scaling the matrix coefficients!
map1, map2 = cv2.fisheye.initUndistortRectifyMap(k, d, np.eye(3), nk, (800,600), cv2.CV_16SC2) # Pass k in 1st parameter, nk in 4th parameter
nemImg = cv2.remap( img, map1, map2, interpolation=cv2.INTER_LINEAR, borderMode=cv2.BORDER_CONSTANT)
TADA!
Как упомянуто Полом Бурком здесь:
проекция "рыбий глаз" не является "искаженным" изображением, и процесс не является "искажением". Рыбий глаз, как и другие проекции, является одним из многих способов отображения 3D-мира на 2D-плоскость, он не более или менее "искажен", чем другие проекции, включая прямоугольную перспективную проекцию.
Чтобы получить проекцию без обрезки изображения (и ваша камера имеет угол обзора около180 градусов), вы можете проецировать изображение " рыбий глаз" в квадрат, используя что-то вроде этого:
Исходный код:
#include <iostream>
#include <sstream>
#include <time.h>
#include <stdio.h>
#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/calib3d/calib3d.hpp>
#include <opencv2/highgui/highgui.hpp>
// - compile with:
// g++ -ggdb `pkg-config --cflags --libs opencv` fist2rect.cpp -o fist2rect
// - execute:
// fist2rect input.jpg output.jpg
using namespace std;
using namespace cv;
#define PI 3.1415926536
Point2f getInputPoint(int x, int y,int srcwidth, int srcheight)
{
Point2f pfish;
float theta,phi,r, r2;
Point3f psph;
float FOV =(float)PI/180 * 180;
float FOV2 = (float)PI/180 * 180;
float width = srcwidth;
float height = srcheight;
// Polar angles
theta = PI * (x / width - 0.5); // -pi/2 to pi/2
phi = PI * (y / height - 0.5); // -pi/2 to pi/2
// Vector in 3D space
psph.x = cos(phi) * sin(theta);
psph.y = cos(phi) * cos(theta);
psph.z = sin(phi) * cos(theta);
// Calculate fisheye angle and radius
theta = atan2(psph.z,psph.x);
phi = atan2(sqrt(psph.x*psph.x+psph.z*psph.z),psph.y);
r = width * phi / FOV;
r2 = height * phi / FOV2;
// Pixel in fisheye space
pfish.x = 0.5 * width + r * cos(theta);
pfish.y = 0.5 * height + r2 * sin(theta);
return pfish;
}
int main(int argc, char **argv)
{
if(argc< 3)
return 0;
Mat orignalImage = imread(argv[1]);
if(orignalImage.empty())
{
cout<<"Empty image\n";
return 0;
}
Mat outImage(orignalImage.rows,orignalImage.cols,CV_8UC3);
namedWindow("result",CV_WINDOW_NORMAL);
for(int i=0; i<outImage.cols; i++)
{
for(int j=0; j<outImage.rows; j++)
{
Point2f inP = getInputPoint(i,j,orignalImage.cols,orignalImage.rows);
Point inP2((int)inP.x,(int)inP.y);
if(inP2.x >= orignalImage.cols || inP2.y >= orignalImage.rows)
continue;
if(inP2.x < 0 || inP2.y < 0)
continue;
Vec3b color = orignalImage.at<Vec3b>(inP2);
outImage.at<Vec3b>(Point(i,j)) = color;
}
}
imwrite(argv[2],outImage);
}
Вам нужно использовать fisheye::estimateNewCameraMatrixForUndistortRectify
с R=np.eye(3)
(единичная матрица) и balance=1
чтобы получить все пиксели:
new_K = cv2.fisheye.estimateNewCameraMatrixForUndistortRectify(K, D, dim, np.eye(3), balance=balance)
map1, map2 = cv2.fisheye.initUndistortRectifyMap(scaled_K, D, np.eye(3), new_K, dim, cv2.CV_32FC1)
# and then remap:
undistorted_img = cv2.remap(img, map1, map2, interpolation=cv2.INTER_LINEAR, borderMode=cv2.BORDER_CONSTANT)
У вас все хорошо, вам просто нужно использовать getOptimalNewCameraMatrix()
установить newCameraMatrix
в undistort()
, Для того, чтобы все пиксели были видны, вы должны установить alpha
до 1 в getOptimalNewCameraMatrix()
,
Я сложил ту же проблему. И если угол обзора вашей камеры ~ 180 градусов, я думаю, вы не сможете деформировать 100% поверхности исходного изображения. Более подробное объяснение я разместил здесь