2014-02-06 2 views
6

У меня есть cameraMatrix и distCoeff Нужно, чтобы изображаться или вектор точек. Теперь я хотел бы их исказить.Opencv: искажение назад

Возможно ли с помощью Opencv? Я помню, я прочитал что-то об этом в stackoverflow, но не могу найти его сейчас.

EDIT: Я нашел способ сделать это в этом answer. Он также находится в зоне разработки opencv (в этом issue)

Но мои результаты неверны. Есть некоторая ошибка в 2-4 пикселя больше или меньше. Возможно, что-то не так в моем коде, потому что в ответе, который я связывал, все кажется хорошим в модульном тесте. Может быть, тип casting от float до double или что-то еще, что я не вижу.

вот мой тест:

#include <opencv2/core/core.hpp> 
#include <opencv2/imgproc/imgproc.hpp> 

#include <iostream> 

using namespace cv; 
using namespace std; 

void distortPoints(const std::vector<cv::Point2d> & src, std::vector<cv::Point2d> & dst, 
         const cv::Mat & cameraMatrix, const cv::Mat & distorsionMatrix) 
{ 

    dst.clear(); 
    double fx = cameraMatrix.at<double>(0,0); 
    double fy = cameraMatrix.at<double>(1,1); 
    double ux = cameraMatrix.at<double>(0,2); 
    double uy = cameraMatrix.at<double>(1,2); 

    double k1 = distorsionMatrix.at<double>(0, 0); 
    double k2 = distorsionMatrix.at<double>(0, 1); 
    double p1 = distorsionMatrix.at<double>(0, 2); 
    double p2 = distorsionMatrix.at<double>(0, 3); 
    double k3 = distorsionMatrix.at<double>(0, 4); 

    for (unsigned int i = 0; i < src.size(); i++) 
    { 
    const cv::Point2d & p = src[i]; 
    double x = p.x; 
    double y = p.y; 
    double xCorrected, yCorrected; 
    //Step 1 : correct distorsion 
    { 
     double r2 = x*x + y*y; 
     //radial distorsion 
     xCorrected = x * (1. + k1 * r2 + k2 * r2 * r2 + k3 * r2 * r2); 
     yCorrected = y * (1. + k1 * r2 + k2 * r2 * r2 + k3 * r2 * r2); 

     //tangential distorsion 
     //The "Learning OpenCV" book is wrong here !!! 
     //False equations from the "Learning OpenCv" book below : 
     //xCorrected = xCorrected + (2. * p1 * y + p2 * (r2 + 2. * x * x)); 
     //yCorrected = yCorrected + (p1 * (r2 + 2. * y * y) + 2. * p2 * x); 
     //Correct formulae found at : http://www.vision.caltech.edu/bouguetj/calib_doc/htmls/parameters.html 
     xCorrected = xCorrected + (2. * p1 * x * y + p2 * (r2 + 2. * x * x)); 
     yCorrected = yCorrected + (p1 * (r2 + 2. * y * y) + 2. * p2 * x * y); 
    } 
    //Step 2 : ideal coordinates => actual coordinates 
    { 
     xCorrected = xCorrected * fx + ux; 
     yCorrected = yCorrected * fy + uy; 
    } 
    dst.push_back(cv::Point2d(xCorrected, yCorrected)); 
    } 

} 

int main(int /*argc*/, char** /*argv*/) { 

    cout << "OpenCV version: " << CV_MAJOR_VERSION << " " << CV_MINOR_VERSION << endl; // 2 4 

    Mat cameraMatrix = (Mat_<double>(3,3) << 1600, 0, 789, 0, 1600, 650, 0, 0, 1); 
    Mat distorsion = (Mat_<double>(5,1) << -0.48, 0, 0, 0, 0); 

    cout << "camera matrix: " << cameraMatrix << endl; 
    cout << "distorsion coefficent: " << distorsion << endl; 

    // the starting points 
    std::vector<Point2f> original_pts; 
    original_pts.push_back(Point2f(23, 358)); 
    original_pts.push_back(Point2f(8, 357)); 
    original_pts.push_back(Point2f(12, 342)); 
    original_pts.push_back(Point2f(27, 343)); 
    original_pts.push_back(Point2f(7, 350)); 
    original_pts.push_back(Point2f(-8, 349)); 
    original_pts.push_back(Point2f(-4, 333)); 
    original_pts.push_back(Point2f(12, 334)); 
    Mat original_m = Mat(original_pts); 

    // undistort 
    Mat undistorted_m; 
    undistortPoints(original_m, undistorted_m, 
        cameraMatrix, distorsion); 

    cout << "undistort points" << undistorted_m << endl; 

    // back to array 
    vector<cv::Point2d> undistorted_points; 
    for(int i=0; i<original_pts.size(); ++i) { 
     Point2d p; 
     p.x = undistorted_m.at<float>(i, 0); 
     p.y = undistorted_m.at<float>(i, 1); 
     undistorted_points.push_back(p); 

     // NOTE THAT HERE THERE IS AN APPROXIMATION 
     // WHAT IS IT? STD::COUT? CASTING TO FLOAT? 
     cout << undistorted_points[i] << endl; 
    } 

    vector<cv::Point2d> redistorted_points; 
    distortPoints(undistorted_points, redistorted_points, cameraMatrix, distorsion); 

    cout << redistorted_points << endl; 

    for(int i=0; i<original_pts.size(); ++i) { 
     cout << original_pts[i] << endl; 
     cout << redistorted_points[i] << endl; 

     Point2d o; 
     o.x = original_pts[i].x; 
     o.y = original_pts[i].y; 
     Point2d dist = redistorted_points[i] - o; 

     double norm = sqrt(dist.dot(dist)); 
     std::cout << "distance = " << norm << std::endl; 

     cout << endl; 
    } 

    return 0; 
} 

А вот мой выход:

OpenCV version: 2 4 
camera matrix: [1600, 0, 789; 
    0, 1600, 650; 
    0, 0, 1] 
distorsion coefficent: [-0.48; 0; 0; 0; 0] 
undistort points[-0.59175861, -0.22557901; -0.61276215, -0.22988389; -0.61078846, -0.24211435; -0.58972651, -0.23759322; -0.61597037, -0.23630577; -0.63910204, -0.24136727; -0.63765121, -0.25489968; -0.61291695, -0.24926868] 
[-0.591759, -0.225579] 
[-0.612762, -0.229884] 
[-0.610788, -0.242114] 
[-0.589727, -0.237593] 
[-0.61597, -0.236306] 
[-0.639102, -0.241367] 
[-0.637651, -0.2549] 
[-0.612917, -0.249269] 
[24.45809095301274, 358.5558144841519; 10.15042938413364, 357.806737955385; 14.23419751024494, 342.8856229036298; 28.51642501095819, 343.610956960508; 9.353743900129871, 350.9029663678638; -4.488033489615646, 350.326357275197; -0.3050714463695385, 334.477016554487; 14.41516474594289, 334.9822130217053] 
[23, 358] 
[24.4581, 358.556] 
distance = 1.56044 

[8, 357] 
[10.1504, 357.807] 
distance = 2.29677 

[12, 342] 
[14.2342, 342.886] 
distance = 2.40332 

[27, 343] 
[28.5164, 343.611] 
distance = 1.63487 

[7, 350] 
[9.35374, 350.903] 
distance = 2.521 

[-8, 349] 
[-4.48803, 350.326] 
distance = 3.75408 

[-4, 333] 
[-0.305071, 334.477] 
distance = 3.97921 

[12, 334] 
[14.4152, 334.982] 
distance = 2.60725 

ответ

6

initUndistortRectifyMap связаны в одном из ответов на вопрос, который вы упоминаете делает действительно то, что вы хотите. Так как он используется в Remap для создания полного неискаженного изображения, он дает для каждого места в целевом изображении (неискаженное), где можно найти соответствующий пиксель в искаженном изображении, чтобы он мог использовать его цвет. Так что это действительно карта f(undistorted) = distorted.

Однако использование этой карты разрешает только позиции ввода, которые являются целыми и внутри прямоугольника изображения. К счастью, документация дает full equations.

Это в основном то, что у вас есть, за исключением того, что есть предварительный шаг, который вам не хватает. Вот моя версия (это C#, но должна быть такой же):

public PointF Distort(PointF point) 
{ 
    // To relative coordinates <- this is the step you are missing. 
    double x = (point.X - cx)/fx; 
    double y = (point.Y - cy)/fy; 

    double r2 = x*x + y*y; 

    // Radial distorsion 
    double xDistort = x * (1 + k1 * r2 + k2 * r2 * r2 + k3 * r2 * r2 * r2); 
    double yDistort = y * (1 + k1 * r2 + k2 * r2 * r2 + k3 * r2 * r2 * r2); 

    // Tangential distorsion 
    xDistort = xDistort + (2 * p1 * x * y + p2 * (r2 + 2 * x * x)); 
    yDistort = yDistort + (p1 * (r2 + 2 * y * y) + 2 * p2 * x * y); 

    // Back to absolute coordinates. 
    xDistort = xDistort * fx + cx; 
    yDistort = yDistort * fy + cy; 

    return new PointF((float)xDistort, (float)yDistort); 
} 
+0

Я думаю, что это решение исказило бы точки, а не искажало бы их назад, как запрошено OP. Я вхожу в аналогичную ситуацию с ОП, и умножение коэффициентов на -1 дает мне ожидаемый результат. –

+0

@ FrançoisPilote: ваши коэффициенты исходят из OpenCV? Существуют и другие модели искажений, которые используют обратное отображение и производят коэффициенты, которые идут другим путем. Я успешно использовал это в своих программах. –

+0

Да, коэффициенты, которые я использую, исходят из opencv, наиболее точно из функции calibrateCamera. –

2

Вы можете легко искажают обратно свои очки, используя ProjectPoints.

cv::Mat rVec(3, 1, cv::DataType<double>::type); // Rotation vector 
rVec.at<double>(0) = 0; 
rVec.at<double>(1) = 0; 
rVec.at<double>(2) =0; 
cv::Mat tVec(3, 1, cv::DataType<double>::type); // Translation vector 
tVec.at<double>(0) =0; 
tVec.at<double>(1) = 0; 
tVec.at<double>(2) = 0; 

cv::projectPoints(points,rVec,tVec, cameraMatrix, distCoeffs,result); 

PS: в opencv 3 они добавили функцию искажаться.

2

Если вы умножаете все коэффициенты искажения на -1, вы можете передать их неискаженным или неиспользуемым точкам, и в основном вы примените обратное искажение, которое приведет к искажению.

0

Модель камеры OCV (см. http://docs.opencv.org/2.4/modules/calib3d/doc/camera_calibration_and_3d_reconstruction.html) описывает, как трехмерная точка сначала отображает координату неподвижной идеальной камеры-обскуры, а затем «искажает» координату, чтобы она смоделировала изображение реальной камеры реального мира.

Используя коэффициенты искажения OpenCV (= Brown коэффициенты искажения), следующие 2 операции являются простыми для расчета:

  • Вычислить пиксель-координаты в исходном изображении камеры от заданного пикселя координаты в искажении (без искажения изображения). AFAIK для этого нет явной функции OpenCV. Но код в ответе Джоан Шармант делает именно это.
  • Рассчитайте изображение без искажений с исходного изображения камеры.Это можно сделать, используя cv::undistort(....) или, альтернативно, комбинацию cv::initUndistortRectifyMap(....) и cv::remap(....).

Однако следующие 2 операции computionally намного сложнее:

  • Вычислить пиксель координат в каких-либо искажений изображения от пикселя координат в исходном изображении камеры. Это можно сделать, используя cv::undistortPoints(....).
  • Рассчитайте исходное изображение камеры с изображения без искажений.

Это может звучать контрастно интуитивным. Более подробное объяснение:

Для данной координаты пикселя в изображении без искажений легко вычислить соответствующую координату в исходном изображении (т. Е. «Исказить» координату).

x = (u - cx)/fx; // u and v are distortion free 
y = (v - cy)/fy; 

rr = x*x + y*y 
distortion = 1 + rr * (k1 + rr * (k2 + rr * k3)) 
# I ommit the tangential parameters for clarity 

u_ = fx * distortion * x + cx 
v_ = fy * distortion * y + cy 
// u_ and v_ are coordinates in the original camera image 

Выполнение этого в обратном направлении намного сложнее; в принципе, нужно было бы объединить все приведенные выше коды кода в одно большое векторное уравнение и решить его для u и v. Я думаю, что для общего случая, когда используются все 5 коэффициентов искажения, это можно сделать только численно. Который (не глядя на код), вероятно, то, что делает cv::undistortPoints(....).

Однако, используя коэффициенты искажения, мы можем вычислить карту искажения (cv::initUndistortRectifyMap(....)), которая отображает координаты изображения без искажений в исходные координаты изображения камеры. Каждая запись в карте искажения содержит положение пикселя (с плавающей запятой) в исходном изображении камеры. Другими словами, карта искажения указывает на изображение без искажения на исходное изображение камеры. Таким образом, карта вычисляется по точно указанной формуле.

Затем карту можно применить для получения нового изображения без искажений от оригинала (cv::remap(....)). cv::undistort() делает это без явного расчета карты неустановления.

Смежные вопросы