提交scripts
This commit is contained in:
1 parent
82caf72036
commit
07dabbd186
224 files changed
+130146
No files matched your search
@@ -0,0 +1,437 @@
|
||||
/*
|
||||
* Camera.cpp
|
||||
*
|
||||
* Copyright (c) 2014-2015 SEACAVE
|
||||
*
|
||||
* Author(s):
|
||||
*
|
||||
* cDc <cdc.seacave@gmail.com>
|
||||
*
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*
|
||||
* Additional Terms:
|
||||
*
|
||||
* You are required to preserve legal notices and author attributions in
|
||||
* that material or in the Appropriate Legal Notices displayed by works
|
||||
* containing it.
|
||||
*/
|
||||
|
||||
#include "Common.h"
|
||||
#include "Camera.h"
|
||||
|
||||
using namespace MVS;
|
||||
|
||||
|
||||
// D E F I N E S ///////////////////////////////////////////////////
|
||||
|
||||
|
||||
// S T R U C T S ///////////////////////////////////////////////////
|
||||
|
||||
const Camera Camera::IDENTITY(Matrix3x3::eye(), Point3(REAL(0), REAL(0), REAL(0)));
|
||||
|
||||
Camera::Camera(const Matrix3x4& _P, bool bUpdate/*=true*/)
|
||||
:
|
||||
P(_P)
|
||||
{
|
||||
if (bUpdate)
|
||||
DecomposeP();
|
||||
} // Camera
|
||||
Camera::Camera(const Matrix3x3& _R, const Point3& _C, bool bUpdate/*=true*/)
|
||||
:
|
||||
CameraIntern(_R, _C)
|
||||
{
|
||||
if (bUpdate)
|
||||
ComposeP_RC();
|
||||
} // Camera
|
||||
Camera::Camera(const Matrix3x3& _K, const Matrix3x3& _R, const Point3& _C, bool bUpdate/*=true*/)
|
||||
:
|
||||
CameraIntern(_K, _R, _C)
|
||||
{
|
||||
if (bUpdate)
|
||||
ComposeP();
|
||||
} // Camera
|
||||
/*----------------------------------------------------------------*/
|
||||
|
||||
|
||||
Camera& Camera::operator= (const CameraIntern& camera)
|
||||
{
|
||||
K = camera.K;
|
||||
R = camera.R;
|
||||
C = camera.C;
|
||||
return *this;
|
||||
}
|
||||
/*----------------------------------------------------------------*/
|
||||
|
||||
|
||||
void Camera::ComposeP_RC()
|
||||
{
|
||||
AssembleProjectionMatrix(R, C, P);
|
||||
} // ComposeP_RC
|
||||
/*----------------------------------------------------------------*/
|
||||
|
||||
void Camera::ComposeP()
|
||||
{
|
||||
AssembleProjectionMatrix(K, R, C, P);
|
||||
} // ComposeP
|
||||
/*----------------------------------------------------------------*/
|
||||
|
||||
void Camera::DecomposeP_RC()
|
||||
{
|
||||
DecomposeProjectionMatrix(P, R, C);
|
||||
} // DecomposeP_RC
|
||||
/*----------------------------------------------------------------*/
|
||||
|
||||
void Camera::DecomposeP()
|
||||
{
|
||||
DecomposeProjectionMatrix(P, K, R, C);
|
||||
} // DecomposeP
|
||||
/*----------------------------------------------------------------*/
|
||||
|
||||
void Camera::Transform(const Matrix3x3& _R, const Point3& _t, const REAL& _s)
|
||||
{
|
||||
R = R * _R.t();
|
||||
C = _R*C*_s+_t;
|
||||
ComposeP();
|
||||
} // Transform
|
||||
/*----------------------------------------------------------------*/
|
||||
|
||||
|
||||
REAL Camera::PointDepth(const Point3& X) const
|
||||
{
|
||||
return P(2,0)*X.x + P(2,1)*X.y + P(2,2)*X.z + P(2,3);
|
||||
} // PointDepth
|
||||
/*----------------------------------------------------------------*/
|
||||
|
||||
bool Camera::IsInFront(const Point3& X) const
|
||||
{
|
||||
return (PointDepth(X)>0);
|
||||
} // IsInFront
|
||||
/*----------------------------------------------------------------*/
|
||||
|
||||
REAL Camera::DistanceSq(const Point3& X) const
|
||||
{
|
||||
const Point3 ray = X-C;
|
||||
return (SQUARE(ray.x)+SQUARE(ray.y)+SQUARE(ray.z));
|
||||
} // Distance
|
||||
/*----------------------------------------------------------------*/
|
||||
|
||||
|
||||
// decomposition of projection matrix into KR[I|-C]: internal calibration ([3,3]), rotation ([3,3]) and translation ([3,1])
|
||||
// (comparable with OpenCV: normalized cv::decomposeProjectionMatrix)
|
||||
void MVS::DecomposeProjectionMatrix(const PMatrix& P, KMatrix& K, RMatrix& R, CMatrix& C)
|
||||
{
|
||||
// extract camera center as the right null vector of P
|
||||
const Vec4 hC(P.RightNullVector());
|
||||
C = CMatrix(hC[0],hC[1],hC[2]) * INVERT(hC[3]);
|
||||
// perform RQ decomposition
|
||||
RQDecomp3x3<REAL>(cv::Mat(3,4,cv::DataType<REAL>::type,const_cast<REAL*>(P.val))(cv::Rect(0,0, 3,3)), K, R);
|
||||
// normalize calibration matrix
|
||||
K *= INVERT(K(2,2));
|
||||
// ensure positive focal length
|
||||
if (K(0,0) < 0) {
|
||||
ASSERT(K(1,1) < 0);
|
||||
NEGATE(K(0,0));
|
||||
NEGATE(K(1,1));
|
||||
NEGATE(K(0,1));
|
||||
NEGATE(K(0,2));
|
||||
NEGATE(K(1,2));
|
||||
(TMatrix<REAL,2,3>&)R *= REAL(-1);
|
||||
}
|
||||
ASSERT(R.IsValid());
|
||||
} // DecomposeProjectionMatrix
|
||||
void MVS::DecomposeProjectionMatrix(const PMatrix& P, RMatrix& R, CMatrix& C)
|
||||
{
|
||||
#ifndef _RELEASE
|
||||
KMatrix K;
|
||||
DecomposeProjectionMatrix(P, K, R, C);
|
||||
ASSERT(K.IsEqual(Matrix3x3::IDENTITY, 1e-5));
|
||||
#endif
|
||||
// extract camera center as the right null vector of P
|
||||
const Vec4 hC(P.RightNullVector());
|
||||
C = CMatrix(hC[0],hC[1],hC[2]) * INVERT(hC[3]);
|
||||
// get rotation
|
||||
const cv::Mat mP(3,4,cv::DataType<REAL>::type,const_cast<REAL*>(P.val));
|
||||
mP(cv::Rect(0,0, 3,3)).copyTo(R);
|
||||
ASSERT(R.IsValid());
|
||||
} // DecomposeProjectionMatrix
|
||||
/*----------------------------------------------------------------*/
|
||||
|
||||
// assemble projection matrix: P=KR[I|-C]
|
||||
void MVS::AssembleProjectionMatrix(const KMatrix& K, const RMatrix& R, const CMatrix& C, PMatrix& P)
|
||||
{
|
||||
// compute temporary matrices
|
||||
cv::Mat mP(3,4,cv::DataType<REAL>::type,const_cast<REAL*>(P.val));
|
||||
cv::Mat M(mP, cv::Rect(0,0, 3,3));
|
||||
cv::Mat(K * R).copyTo(M); //3x3
|
||||
mP.col(3) = M * cv::Mat(-C); //3x1
|
||||
} // AssembleProjectionMatrix
|
||||
void MVS::AssembleProjectionMatrix(const RMatrix& R, const CMatrix& C, PMatrix& P)
|
||||
{
|
||||
Eigen::Map<Matrix3x3::EMat,0,Eigen::Stride<4,0> >(P.val) = (const Matrix3x3::EMat)R;
|
||||
Eigen::Map<Point3::EVec,0,Eigen::Stride<0,4> >(P.val+3) = ((const Matrix3x3::EMat)R) * (-((const Point3::EVec)C));
|
||||
} // AssembleProjectionMatrix
|
||||
/*----------------------------------------------------------------*/
|
||||
|
||||
// compute the focus of attention of a set of cameras; only cameras
|
||||
// that have the focus of attention in front of them are considered
|
||||
Point3 MVS::ComputeCamerasFocusPoint(const CameraArr& cameras, const Point3* pInitialFocus)
|
||||
{
|
||||
// set initial focus point
|
||||
Point3 focus;
|
||||
if (pInitialFocus == NULL) {
|
||||
// initialize focus point to the average camera center
|
||||
focus = Point3::ZERO;
|
||||
for (const Camera& camera: cameras)
|
||||
focus += camera.C;
|
||||
focus /= cameras.size();
|
||||
// compute average distance to the cameras
|
||||
REAL avgDist = 0;
|
||||
for (const Camera& camera: cameras)
|
||||
avgDist += SQRT(camera.DistanceSq(focus));
|
||||
avgDist /= cameras.size();
|
||||
// move focus point to the average distance in front of first camera
|
||||
focus = cameras.front().C + cameras.front().Direction()*avgDist;
|
||||
} else
|
||||
focus = *pInitialFocus;
|
||||
// compute focus point as the point in front of the active cameras,
|
||||
// closest to the view direction of each camera
|
||||
uint32_t numActive = 0;
|
||||
Point3::EVec smp(Point3::EVec::Zero());
|
||||
Matrix3x3::EMat smTm(Matrix3x3::EMat::Zero());
|
||||
for (const Camera& camera: cameras) {
|
||||
if (!camera.IsInFront(focus))
|
||||
continue;
|
||||
// https://en.wikipedia.org/wiki/Line–line_intersection#In_more_than_two_dimensions
|
||||
const Point3::EVec dir = camera.Direction();
|
||||
const Matrix3x3::EMat m(Matrix3x3::EMat::Identity() - dir * dir.transpose());
|
||||
const Matrix3x3::EMat mTm(m.transpose() * m);
|
||||
const Point3::EVec s = mTm * Point3::EVec(camera.C);
|
||||
smp += s;
|
||||
smTm += mTm;
|
||||
++numActive;
|
||||
}
|
||||
if (numActive >= 2)
|
||||
focus = smTm.inverse() * smp;
|
||||
return focus;
|
||||
} // ComputeCamerasFocusPoint
|
||||
/*----------------------------------------------------------------*/
|
||||
|
||||
|
||||
|
||||
namespace MVS {
|
||||
|
||||
namespace RECTIFY {
|
||||
|
||||
// compute the ROIs for the two images based on the corresponding points
|
||||
void GetImagePairROI(const Point3fArr& points1, const Point3fArr& points2, const Matrix3x3& K1, const Matrix3x3& K2, const Matrix3x3& R1, const Matrix3x3& R2, const Matrix3x3& invK1, const Matrix3x3& invK2, AABB2f& roi1h, AABB2f& roi2h)
|
||||
{
|
||||
ASSERT(!points1.empty() && points1.size() && points2.size());
|
||||
|
||||
// compute rectification homography (from original to rectified image)
|
||||
const Matrix3x3 H1(K1 * R1 * invK1);
|
||||
const Matrix3x3 H2(K2 * R2 * invK2);
|
||||
|
||||
// determine the ROIs in rectified images
|
||||
roi1h.Reset(); roi2h.Reset();
|
||||
FOREACH(i, points1) {
|
||||
Point2f xh;
|
||||
const Point3f& x1 = points1[i];
|
||||
ProjectVertex_3x3_2_2(H1.val, x1.ptr(), xh.ptr());
|
||||
roi1h.InsertFull(xh);
|
||||
const Point3f& x2 = points2[i];
|
||||
ProjectVertex_3x3_2_2(H2.val, x2.ptr(), xh.ptr());
|
||||
roi2h.InsertFull(xh);
|
||||
}
|
||||
}
|
||||
|
||||
void SetCameraMatricesROI(const AABB2f& roi1h, const AABB2f& roi2h, cv::Size& size1, cv::Size& size2, Matrix3x3& K1, Matrix3x3& K2)
|
||||
{
|
||||
// set the new image sizes such that they are equal and contain the entire ROI
|
||||
const Point2f size1h(roi1h.GetSize());
|
||||
const Point2f size2h(roi2h.GetSize());
|
||||
const int maxSize(MAXF(size1.width+size2.width, size1.height+size2.height)/2);
|
||||
size1.width = size2.width = MINF(ROUND2INT(MAXF(size1h.x,size2h.x)), maxSize);
|
||||
size1.height = size2.height = MINF(ROUND2INT(MAXF(size1h.y,size2h.y)), maxSize);
|
||||
|
||||
// set the new camera matrices such that the ROI is centered
|
||||
const Point2f center1h(roi1h.GetCenter());
|
||||
const Point2f center2h(roi2h.GetCenter());
|
||||
K1(0,2) += size1.width /2-center1h[0];
|
||||
K1(1,2) += size1.height/2-center1h[1];
|
||||
K2(0,2) += size2.width /2-center2h[0];
|
||||
K2(1,2) += size2.height/2-center2h[1];
|
||||
}
|
||||
|
||||
} // namespace RECTIFY
|
||||
|
||||
} // namespace MVS
|
||||
|
||||
// Compute stereo rectification homographies that transform two images,
|
||||
// such that corresponding pixels in one image lie on the same scan-line in the other image;
|
||||
// note: this function assumes that the two cameras are already undistorted
|
||||
REAL Camera::StereoRectify(const cv::Size& size1, const Camera& camera1, const cv::Size& size2, const Camera& camera2, Matrix3x3& R1, Matrix3x3& R2, Matrix3x3& K1, Matrix3x3& K2)
|
||||
{
|
||||
// compute relative pose
|
||||
RMatrix poseR;
|
||||
CMatrix poseC;
|
||||
ComputeRelativePose(camera1.R, camera1.C, camera2.R, camera2.C, poseR, poseC);
|
||||
|
||||
// compute the average rotation between the first and the second camera
|
||||
const Point3 r(poseR.GetRotationAxisAngle());
|
||||
reinterpret_cast<RMatrix&>(R2).SetFromAxisAngle(r*REAL(-0.5));
|
||||
R1 = R2.t();
|
||||
|
||||
// compute the translation, such that it coincides with the X-axis
|
||||
int idx(0);
|
||||
Point3 t(R2 * (poseR*(-poseC)));
|
||||
#if 0
|
||||
const Point3 unitX(t.x<0?-1:1, 0, 0);
|
||||
const Point3 axis(t.cross(unitX));
|
||||
const REAL normAxis(norm(axis));
|
||||
if (normAxis > EPSILONTOLERANCE<REAL>()) {
|
||||
ASSERT(t.dot(unitX) >= 0);
|
||||
const REAL angle(ACOS(ComputeAngle(t.ptr(), unitX.ptr())));
|
||||
const RMatrix Rx(axis*angle/normAxis);
|
||||
#else
|
||||
const REAL nt(norm(t));
|
||||
//idx = (ABS(t.x) > ABS(t.y) ? 0 : 1);
|
||||
Point3 axis(Point3(0,0,1).cross(t));
|
||||
const REAL na(norm(axis));
|
||||
if (na < EPSILONTOLERANCE<REAL>())
|
||||
return 0;
|
||||
{
|
||||
const Point3 w(normalized(t.cross(axis)));
|
||||
RMatrix Rx;
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
Rx(idx,i) = -t[i]/nt;
|
||||
Rx(idx^1,i) = -axis[i]/na;
|
||||
Rx(2,i) = w[i]*(1-2*idx); // if idx == 1 -> opposite direction
|
||||
}
|
||||
#endif
|
||||
R1 = Rx * R1;
|
||||
R2 = Rx * R2;
|
||||
t = R2 * (poseR*(-poseC));
|
||||
}
|
||||
ASSERT(ISEQUAL(-t.x, norm(camera2.C-camera1.C)) && ISZERO(t.y) && ISZERO(t.z));
|
||||
|
||||
// determine the intrinsic calibration matrix;
|
||||
// vertical focal length must be the same for both images to keep the epipolar constraint
|
||||
K1 = Matrix3x3::IDENTITY;
|
||||
K1(0,0) = (camera1.K(0,0)+camera2.K(0,0))/2;
|
||||
K1(1,1) = (camera1.K(1,1)+camera2.K(1,1))/2;
|
||||
K2 = K1;
|
||||
// set ROI to contain the whole transformed image
|
||||
for (int k = 0; k < 2; k++) {
|
||||
const Matrix3x3 H(k == 0 ? K1*R1*camera1.GetInvK() : K2*R2*camera2.GetInvK());
|
||||
Point2f ptMin(std::numeric_limits<float>::max());
|
||||
for (int i = 0; i < 4; i++) {
|
||||
const Point2f pt(
|
||||
(float)((i%2)*size1.width),
|
||||
(float)((i<2?0:1)*size1.height)
|
||||
);
|
||||
Point2f ptH;
|
||||
ProjectVertex_3x3_2_2(H.val, pt.ptr(), ptH.ptr());
|
||||
ptMin.x = MINF(ptMin.x,ptH.x);
|
||||
ptMin.y = MINF(ptMin.y,ptH.y);
|
||||
}
|
||||
Matrix3x3& KNew(k == 0 ? K1 : K2);
|
||||
KNew(0,2) = -ptMin.x;
|
||||
KNew(1,2) = -ptMin.y;
|
||||
}
|
||||
K1(idx^1,2) = K2(idx^1,2) = MAXF(K1(idx^1,2), K2(idx^1,2));
|
||||
return t.x;
|
||||
} // StereoRectify
|
||||
|
||||
// see: "A compact algorithm for rectification of stereo pairs", A. Fusiello, E. Trucco, and A. Verri, 2000
|
||||
REAL Camera::StereoRectifyFusiello(const cv::Size& size1, const Camera& camera1, const cv::Size& size2, const Camera& camera2, Matrix3x3& R1, Matrix3x3& R2, Matrix3x3& K1, Matrix3x3& K2)
|
||||
{
|
||||
// compute relative pose
|
||||
RMatrix poseR;
|
||||
CMatrix poseC;
|
||||
ComputeRelativePose(camera1.R, camera1.C, camera2.R, camera2.C, poseR, poseC);
|
||||
|
||||
// new x axis (baseline, from C1 to C2)
|
||||
const Point3 v1(camera2.C-camera1.C);
|
||||
// new y axes (orthogonal to old z and new x)
|
||||
const Point3 v2(camera1.Direction().cross(v1));
|
||||
// new z axes (no choice, orthogonal to baseline and y)
|
||||
const Point3 v3(v1.cross(v2));
|
||||
|
||||
// new extrinsic (translation unchanged)
|
||||
RMatrix R;
|
||||
R.SetFromRowVectors(normalized(v1), normalized(v2), normalized(v3));
|
||||
|
||||
// new intrinsic (arbitrary)
|
||||
K1 = camera1.K; K1(0,1) = 0;
|
||||
K2 = camera2.K; K2(0,1) = 0;
|
||||
K1(1,1) = K2(1,1) = (camera1.K(1,1)+camera2.K(1,1))/2;
|
||||
|
||||
// new rotations
|
||||
R1 = R*camera1.R.t();
|
||||
R2 = R*camera2.R.t();
|
||||
|
||||
#if 0
|
||||
// new projection matrices
|
||||
PMatrix P1, P2;
|
||||
AssembleProjectionMatrix(K1, R, camera1.C, P1);
|
||||
AssembleProjectionMatrix(K2, R, camera2.C, P2);
|
||||
|
||||
// rectifying image transformation
|
||||
#if 0
|
||||
const Matrix3x3 H1((PMatrix::EMat(P1).leftCols<3>()*(PMatrix::EMat(camera1.P).leftCols<3>().inverse())).eval());
|
||||
const Matrix3x3 H2((PMatrix::EMat(P2).leftCols<3>()*(PMatrix::EMat(camera2.P).leftCols<3>().inverse())).eval());
|
||||
#else
|
||||
const Matrix3x3 H1(K1*R*camera1.R.t()*camera1.GetInvK());
|
||||
const Matrix3x3 H2(K2*R*camera2.R.t()*camera2.GetInvK());
|
||||
#endif
|
||||
#endif
|
||||
|
||||
const Point3 t(R2 * (poseR*(-poseC)));
|
||||
ASSERT(ISEQUAL(-t.x, norm(v1)) && ISZERO(t.y) && ISZERO(t.z));
|
||||
return t.x;
|
||||
} // StereoRectifyFusiello
|
||||
|
||||
// adjust rectified camera matrices such that the entire area common to both source images is contained in the rectified images;
|
||||
// as long as the focal-length on x and the skewness are equal in both cameras
|
||||
// the point-scale is also equal in both images;
|
||||
// note however that the surface-scale can still be different (most likely is)
|
||||
// as it depends on its (unknown) normal too;
|
||||
// - points1 and points2: contain the pairs of corresponding pairs of image projections and their depth
|
||||
// - size1 and size2: input the size of the source images, output the size of the rectified images (rectified image sizes are equal)
|
||||
void Camera::SetStereoRectificationROI(const Point3fArr& points1, cv::Size& size1, const Camera& camera1, const Point3fArr& points2, cv::Size& size2, const Camera& camera2, const Matrix3x3& R1, const Matrix3x3& R2, Matrix3x3& K1, Matrix3x3& K2)
|
||||
{
|
||||
ASSERT(!points1.empty() && points1.size() && points2.size());
|
||||
|
||||
#if 1
|
||||
// ignore skewness
|
||||
K1(0,1) = K2(0,1) = 0;
|
||||
#else
|
||||
// set same skewness
|
||||
K1(0,1) = K2(0,1) = (K1(0,1)+K2(0,1))/2;
|
||||
#endif
|
||||
|
||||
// set same focal-length on x too
|
||||
K1(0,0) = K2(0,0) = (K1(0,0)+K2(0,0))/2;
|
||||
ASSERT(ISEQUAL(K1(1,1), K2(1,1)));
|
||||
|
||||
// determine the ROIs in rectified images
|
||||
AABB2f roi1h, roi2h;
|
||||
RECTIFY::GetImagePairROI(points1, points2, K1, K2, R1, R2, camera1.GetInvK(), camera2.GetInvK(), roi1h, roi2h);
|
||||
|
||||
// set the new camera matrices such that the ROI is centered
|
||||
RECTIFY::SetCameraMatricesROI(roi1h, roi2h, size1, size2, K1, K2);
|
||||
} // SetStereoRectificationROI
|
||||
/*----------------------------------------------------------------*/
|
||||
Reference in new issue
Block a user