/***************************************************************************** * VCGLib o o * * Visual and Computer Graphics Library o o * * _ O _ * * Copyright(C) 2004-2022 \/)\/ * * Visual Computing Lab /\/| * * ISTI - Italian National Research Council | * * \ * * All rights reserved. * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 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 General Public License (http://www.gnu.org/licenses/gpl.txt) * * for more details. * * * ****************************************************************************/ #include "shot.h" namespace vcg { template Shot::Shot() : Intrinsics(), Extrinsics() { Extrinsics.SetIdentity(); } template Shot::Shot(const Camera& i, const ReferenceFrame& e) : Intrinsics(), Extrinsics() { Intrinsics = i; Extrinsics = e; } template Shot::Shot(const Camera& c) : Intrinsics(), Extrinsics() { Intrinsics = c; Extrinsics.SetIdentity(); } template template Shot Shot::Construct(const Shot& b) { ReferenceFrame r; r.SetRot(Matrix44::Construct(b.Extrinsics.Rot())); r.SetTra(Point3::Construct(b.Extrinsics.Tra())); return Shot(Camera::Construct(b.Intrinsics), r); } /** * @brief get the i-th axis of the coordinate system of the camera */ template vcg::Point3 Shot::Axis(const int& i) const { vcg::Matrix44 m; Extrinsics.rot.ToMatrix(m); vcg::Point3 aa = m.GetRow3(i); return aa; } /** * @brief Get the viewdir */ template const vcg::Point3 Shot::GetViewDir() const { return Extrinsics.Rot().GetRow3(2); } /** * @brief Get the viewpoint */ template const vcg::Point3 Shot::GetViewPoint() const { return Extrinsics.tra; } /** * @brief set the viewpoint */ template void Shot::SetViewPoint(const vcg::Point3& viewpoint) { Extrinsics.SetTra(viewpoint); } /** * @brief get fov from focal */ template float Shot::GetFovFromFocal() const { double viewportYMm = Intrinsics.PixelSizeMm[1] * Intrinsics.ViewportPx[1]; return 2 * (vcg::math::ToDeg(atanf(viewportYMm / (2 * Intrinsics.FocalMm)))); } /** * @brief look at (point+up) */ template void Shot::LookAt(const vcg::Point3& z_dir, const vcg::Point3& up) { LookTowards(z_dir - GetViewPoint(), up); } /** * @brief look at (opengl-like) */ template void Shot::LookAt( const S& eye_x, const S& eye_y, const S& eye_z, const S& at_x, const S& at_y, const S& at_z, const S& up_x, const S& up_y, const S& up_z) { SetViewPoint(Point3(eye_x, eye_y, eye_z)); LookAt(Point3(at_x, at_y, at_z), Point3(up_x, up_y, up_z)); } /** * @brief look towards (dir+up) */ template void Shot::LookTowards(const vcg::Point3& z_dir, const vcg::Point3& up) { vcg::Point3 x_dir = up ^ -z_dir; vcg::Point3 y_dir = -z_dir ^ x_dir; Matrix44 m; m.SetIdentity(); *(vcg::Point3*) &m[0][0] = x_dir / x_dir.Norm(); *(vcg::Point3*) &m[1][0] = y_dir / y_dir.Norm(); *(vcg::Point3*) &m[2][0] = -z_dir / z_dir.Norm(); Extrinsics.rot.FromMatrix(m); } /** * @brief Sometimes the focal is given in pixels. In this case, this function can be used to convert * it in millimiters given the CCD width (in mm). This method should be moved in vcg::Camera(). * Equivalent focal length is obtained by setting the ccd width to 35 mm. */ template void Shot::ConvertFocalToMM(S ccdwidth) { double ccd_width = ccdwidth; // ccd is assumed conventionally to be 35mm double ccd_height = (ccd_width * Intrinsics.ViewportPx[1]) / Intrinsics.ViewportPx[0]; Intrinsics.PixelSizeMm[0] = (ccd_width / Intrinsics.ViewportPx[0]); Intrinsics.PixelSizeMm[1] = (ccd_height / Intrinsics.ViewportPx[1]); Intrinsics.FocalMm = (ccd_width * Intrinsics.FocalMm) / Intrinsics.ViewportPx[0]; // NOW FOCAL IS IN MM } /** * @brief Sometimes the 3D World coordinates are known up to a scale factor. This method adjust the * camera/shot parameters to account for the re-scaling of the World. If the intrisic parameters are * just reasonable values the cameras need only a re-positioning. */ template void Shot::RescalingWorld(S scalefactor, bool adjustIntrinsics) { // adjust INTRINSICS (if required) if (adjustIntrinsics) { Intrinsics.FocalMm = Intrinsics.FocalMm * scalefactor; double ccdwidth = static_cast(Intrinsics.ViewportPx[0] * Intrinsics.PixelSizeMm[0]); double ccdheight = static_cast(Intrinsics.ViewportPx[1] * Intrinsics.PixelSizeMm[1]); Intrinsics.PixelSizeMm[0] = (ccdwidth * scalefactor) / Intrinsics.ViewportPx[0]; Intrinsics.PixelSizeMm[1] = (ccdheight * scalefactor) / Intrinsics.ViewportPx[1]; } // adjust EXTRINSICS // rotation remains the same (!) // nothing to do.. // the viewpoint should be modified according to the scale factor Extrinsics.tra *= scalefactor; } /** * @brief Given a pure roto-translation matrix (4-by-4) modify the reference frame accordingly. */ template void Shot::ApplyRigidTransformation(const Matrix44& M) { Matrix44 rotM; Extrinsics.rot.ToMatrix(rotM); // roto-translate the viewpoint Extrinsics.tra = M * Extrinsics.tra; Matrix44 newRot = rotM * M.transpose(); newRot[3][0] = newRot[3][1] = newRot[3][2] = 0.0; Extrinsics.SetRot(newRot); } /** * @brief Given a similarity transformation modifies the reference frame accordingly. */ template void Shot::ApplySimilarity(Matrix44 M) { Matrix44 rotM; Extrinsics.rot.ToMatrix(rotM); // normalize M = M * (1 / M.ElementAt(3, 3)); M[3][3] = 1; // just for numeric precision // compute scale factor ScalarType scalefactor = 1.0 / pow(ScalarType(M.Determinant()), 1 / ScalarType(3.0)); // roto-translate the viewpoint Extrinsics.tra = M * Extrinsics.tra; vcg::Matrix44 M2 = M; M2 = M2 * scalefactor; // remove the scaling M2[3][3] = 1.0; M2[0][3] = M2[1][3] = M2[2][3] = 0; // remove the translation rotM = rotM * M2.transpose(); Extrinsics.SetRot(rotM); } /** * @brief Given a similarity transformation modifies the reference frame accordingly. */ template void Shot::ApplySimilarity(const Similarity& Sm) { Matrix44 rotM; Extrinsics.rot.ToMatrix(rotM); // similarity decomposition vcg::Matrix44 R; Sm.rot.ToMatrix(R); vcg::Matrix44 T; T.SetIdentity(); T.ElementAt(0, 3) = Sm.tra[0]; T.ElementAt(1, 3) = Sm.tra[1]; T.ElementAt(2, 3) = Sm.tra[2]; vcg::Matrix44d S44; S44.SetIdentity(); S44 *= Sm.sca; S44.ElementAt(3, 3) = 1.0; vcg::Matrix44 M = T * R * S44; // roto-translate the viewpoint Extrinsics.tra = M * Extrinsics.tra; vcg::Matrix44 M2 = M; M2 = M2 * (1.0 / Sm.sca); Extrinsics.rot = rotM * M2.transpose(); Extrinsics.rot.ElementAt(3, 0) = 0; Extrinsics.rot.ElementAt(3, 1) = 0; Extrinsics.rot.ElementAt(3, 2) = 0; Extrinsics.rot.ElementAt(3, 3) = 1; } /** * @brief Convert a 3d point from world to camera coordinates (do not confuse with the Shot * reference frame) */ template vcg::Point3 Shot::ConvertWorldToCameraCoordinates(const vcg::Point3& p) const { Matrix44 rotM; Extrinsics.rot.ToMatrix(rotM); vcg::Point3 cp = rotM * (p - GetViewPoint()); cp[2] = -cp[2]; return cp; } /** * @brief Convert a 3d point from camera coordinates (do not confuse with the Shot reference frame) * to world coordinates */ template vcg::Point3 Shot::ConvertCameraToWorldCoordinates(const vcg::Point3& p) const { Matrix44 rotM; vcg::Point3 cp = p; cp[2] = -cp[2]; Extrinsics.rot.ToMatrix(rotM); cp = rotM.transpose() * cp + GetViewPoint(); return cp; } /** * @brief Convert a 3d point from camera to world coordinates, uses inverse instead of trranspose * for non-exactly-rigid rotation matrices (such as calculated by tsai and garcia) */ template vcg::Point3 Shot::ConvertCameraToWorldCoordinates_Substitute(const vcg::Point3& p) const { Matrix44 rotM; vcg::Point3 cp = p; cp[2] = -cp[2]; Extrinsics.rot.ToMatrix(rotM); cp = Inverse(rotM) * cp + GetViewPoint(); return cp; } /** * @brief Project a 3d point from world coordinates to 2d camera viewport (the value returned is in * pixel) */ template vcg::Point2 Shot::Project(const vcg::Point3& p) const { Point3 cp = ConvertWorldToCameraCoordinates(p); Point2 pp = Intrinsics.Project(cp); Point2 vp = Intrinsics.LocalToViewportPx(pp); return vp; } /** * @brief Inverse projection from 2d camera viewport (in pixels) to 3d world coordinates (it * requires the original depth of the point to unproject) */ template vcg::Point3 Shot::UnProject(const vcg::Point2& p, const S& d) const { Point2 lp = Intrinsics.ViewportPxToLocal(p); Point3 cp = Intrinsics.UnProject(lp, d); Point3 wp = ConvertCameraToWorldCoordinates(cp); return wp; } /** * @brief Inverse projection from 2d camera viewport (in pixels) to 3d world coordinates (it * requires the original depth of the projected point) uses inverse instead of trranspose for * non-exactly-rigid rotation matrices (such as calculated by tsai and garcia) */ template vcg::Point3 Shot::UnProject_Substitute(const vcg::Point2& p, const S& d) const { Point2 lp = Intrinsics.ViewportPxToLocal(p); Point3 cp = Intrinsics.UnProject(lp, d); Point3 wp = ConvertCameraToWorldCoordinates_Substitute(cp); return wp; } /** * @brief Returns the distance of point p from camera plane (z depth), required for unprojection * operation */ template S Shot::Depth(const vcg::Point3& p) const { return ConvertWorldToCameraCoordinates(p).Z(); } /** * @brief Returns the (4-by-4) matrix M such that 3dpoint_in_world_coordinates = M * * 3dpoint_in_local_coordinates */ template Matrix44 Shot::GetExtrinsicsToWorldMatrix() const { Matrix44 rotM; Extrinsics.rot.ToMatrix(rotM); return Matrix44().SetTranslate(Extrinsics.tra) * rotM.transpose(); } /** * @brief Returns the (4-by-4) matrix M such that 3dpoint_in_local_coordinates = M * * 3dpoint_in_world_coordinates */ template Matrix44 Shot::GetWorldToExtrinsicsMatrix() const { Matrix44 rotM; Extrinsics.rot.ToMatrix(rotM); return rotM * Matrix44().SetTranslate(-Extrinsics.tra); } /** * @brief multiply the current reference frame for the matrix passed * note: it is up to the caller to check the the matrix passed is a pure rototranslation */ template void Shot::MultMatrix(vcg::Matrix44 m44) { Extrinsics.tra = m44 * Extrinsics.tra; m44[0][3] = m44[1][3] = m44[2][3] = 0.0; // set no translation const S k = m44.GetRow3(0).Norm(); // compute scaling (assumed uniform) Extrinsics.rot = Extrinsics.rot * m44.transpose() * (1 / k); } /** * @brief Multiply the current reference frame for the similarity passed * note: it is up to the caller to check the the matrix passed is a pure rototranslation */ template void Shot::MultSimilarity(const Similarity& s) { MultMatrix(s.Matrix()); } template bool Shot::IsValid() const { return Intrinsics.PixelSizeMm[0] > 0 && Intrinsics.PixelSizeMm[1] > 0; } template bool Shot::operator==(const Shot &oth) const { return Intrinsics == oth.Intrinsics && Extrinsics == oth.Extrinsics; } template bool Shot::operator!=(const Shot &oth) const { return !(*this == oth); } template Shot::ReferenceFrame::ReferenceFrame() : rot(), tra() { } template void Shot::ReferenceFrame::SetIdentity() { rot.SetIdentity(); tra = Point3(0.0, 0.0, 0.0); } template void vcg::Shot::ReferenceFrame::SetTra(const Point3 &tr) { tra = tr; } template void vcg::Shot::ReferenceFrame::SetRot(const RotationType &rt) { rot = rt; } template Point3 vcg::Shot::ReferenceFrame::Tra() const { return tra; } template RotationType vcg::Shot::ReferenceFrame::Rot() const { return rot; } template bool vcg::Shot::ReferenceFrame::operator==( const Shot::ReferenceFrame& oth) const { return rot == oth.rot && tra == oth.tra; } template bool vcg::Shot::ReferenceFrame::operator!=( const Shot::ReferenceFrame& oth) const { return !(*this == oth); } } // namespace vcg