diff --git a/modules/odm_orthophoto/src/OdmOrthoPhoto.cpp b/modules/odm_orthophoto/src/OdmOrthoPhoto.cpp index b4d6d814..d5147fd2 100644 --- a/modules/odm_orthophoto/src/OdmOrthoPhoto.cpp +++ b/modules/odm_orthophoto/src/OdmOrthoPhoto.cpp @@ -13,11 +13,6 @@ OdmOrthoPhoto::OdmOrthoPhoto() resolution_ = 0.0f; - boundaryPoint1_[0] = 0.0f; boundaryPoint1_[1] = 0.0f; - boundaryPoint2_[0] = 0.0f; boundaryPoint2_[1] = 0.0f; - boundaryPoint3_[0] = 0.0f; boundaryPoint3_[1] = 0.0f; - boundaryPoint4_[0] = 0.0f; boundaryPoint4_[1] = 0.0f; - alphaBand = nullptr; currentBandIndex = 0; } @@ -217,6 +212,15 @@ void OdmOrthoPhoto::saveTIFF(const std::string &filename, GDALDataType dataType) } } + // Alpha + if (dataType == GDT_UInt16){ + finalizeAlphaBand(); + }else if (dataType == GDT_Byte){ + finalizeAlphaBand(); + }else{ + throw OdmOrthoPhotoException("Invalid data type"); + } + // Alpha hBand = GDALGetRasterBand( hDstDS, static_cast(i) + 1 ); @@ -264,6 +268,20 @@ void OdmOrthoPhoto::initAlphaBand(){ } } +template +void OdmOrthoPhoto::finalizeAlphaBand(){ + // Adjust alpha band values, only pixels that have + // values on all bands should be visible + + size_t pixelCount = static_cast(width * height); + int channels = bands.size(); + + T *arr = reinterpret_cast(alphaBand); + for (size_t j = 0; j < pixelCount; j++){ + arr[j] = arr[j] >= channels ? 255.0 : 0.0; + } +} + void OdmOrthoPhoto::createOrthoPhoto() { @@ -273,14 +291,15 @@ void OdmOrthoPhoto::createOrthoPhoto() } int textureDepth = -1; - float xMax, xMin, yMax, yMin; bool primary = true; + Bounds bounds; for (auto &inputFile : inputFiles){ log_ << "Reading mesh file... " << inputFile << "\n"; + std::vector companions; /**< Materials (used by loadOBJFile). **/ pcl::TextureMesh mesh; - loadObjFile(inputFile, mesh); + loadObjFile(inputFile, mesh, companions); log_ << "Mesh file read.\n\n"; // Does the model have more than one material? @@ -296,31 +315,25 @@ void OdmOrthoPhoto::createOrthoPhoto() } } - // Set boundaries according to the first model - // subsequent models use the boundaries of the first - if (primary){ - adjustBoundsForEntireModel(mesh); - } + bounds = computeBoundsForModel(mesh); + // TODO: check these haven't changed for other models - // The minimum and maximum boundary values. - xMin = std::min(std::min(boundaryPoint1_[0], boundaryPoint2_[0]), std::min(boundaryPoint3_[0], boundaryPoint4_[0])); - xMax = std::max(std::max(boundaryPoint1_[0], boundaryPoint2_[0]), std::max(boundaryPoint3_[0], boundaryPoint4_[0])); - yMin = std::min(std::min(boundaryPoint1_[1], boundaryPoint2_[1]), std::min(boundaryPoint3_[1], boundaryPoint4_[1])); - yMax = std::max(std::max(boundaryPoint1_[1], boundaryPoint2_[1]), std::max(boundaryPoint3_[1], boundaryPoint4_[1])); - - log_ << "Ortho photo bounds x : " << xMin << " -> " << xMax << '\n'; - log_ << "Ortho photo bounds y : " << yMin << " -> " << yMax << '\n'; + log_ << "Model bounds x : " << bounds.xMin << " -> " << bounds.xMax << '\n'; + log_ << "Model bounds y : " << bounds.yMin << " -> " << bounds.yMax << '\n'; // The size of the area. - float xDiff = xMax - xMin; - float yDiff = yMax - yMin; - log_ << "Ortho photo area : " << xDiff*yDiff << "m2\n"; + float xDiff = bounds.xMax - bounds.xMin; + float yDiff = bounds.yMax - bounds.yMin; + log_ << "Model area : " << xDiff*yDiff << "m2\n"; // The resolution necessary to fit the area with the given resolution. height = static_cast(std::ceil(resolution_*yDiff)); width = static_cast(std::ceil(resolution_*xDiff)); + + // TODO: check these haven't changed for other models + depth_ = cv::Mat::zeros(height, width, CV_32F) - std::numeric_limits::infinity(); - log_ << "Ortho photo resolution, width x height : " << width << "x" << height << '\n'; + log_ << "Model resolution, width x height : " << width << "x" << height << '\n'; // Check size of photo. if(0 >= height*width) @@ -397,8 +410,7 @@ void OdmOrthoPhoto::createOrthoPhoto() } // Creates a transformation which aligns the area for the ortho photo. - Eigen::Transform transform = getROITransform(xMin, -yMax); - + Eigen::Transform transform = getROITransform(bounds.xMin, -bounds.yMax); log_ << "Translating and scaling mesh...\n"; // Move the mesh into position. @@ -511,24 +523,24 @@ void OdmOrthoPhoto::createOrthoPhoto() } cornerStream.setf(std::ios::scientific, std::ios::floatfield); cornerStream.precision(17); - cornerStream << xMin << " " << yMin << " " << xMax << " " << yMax; + cornerStream << bounds.xMin << " " << bounds.yMin << " " << bounds.xMax << " " << bounds.yMax; cornerStream.close(); } log_ << "Orthophoto generation done.\n"; } -void OdmOrthoPhoto::adjustBoundsForEntireModel(const pcl::TextureMesh &mesh) +Bounds OdmOrthoPhoto::computeBoundsForModel(const pcl::TextureMesh &mesh) { log_ << "Set boundary to contain entire model.\n"; // The boundary of the model. - float xMin, xMax, yMin, yMax; + Bounds r; - xMin = std::numeric_limits::infinity(); - xMax = -std::numeric_limits::infinity(); - yMin = std::numeric_limits::infinity(); - yMax = -std::numeric_limits::infinity(); + r.xMin = std::numeric_limits::infinity(); + r.xMax = -std::numeric_limits::infinity(); + r.yMin = std::numeric_limits::infinity(); + r.yMax = -std::numeric_limits::infinity(); // Contains the vertices of the mesh. pcl::PointCloud::Ptr meshCloud (new pcl::PointCloud); @@ -555,25 +567,21 @@ void OdmOrthoPhoto::adjustBoundsForEntireModel(const pcl::TextureMesh &mesh) pcl::PointXYZ v2 = meshCloud->points[v2i]; pcl::PointXYZ v3 = meshCloud->points[v3i]; - xMin = std::min(std::min(xMin, v1.x), std::min(v2.x, v3.x)); - xMax = std::max(std::max(xMax, v1.x), std::max(v2.x, v3.x)); - yMin = std::min(std::min(yMin, v1.y), std::min(v2.y, v3.y)); - yMax = std::max(std::max(yMax, v1.y), std::max(v2.y, v3.y)); + r.xMin = std::min(std::min(r.xMin, v1.x), std::min(v2.x, v3.x)); + r.xMax = std::max(std::max(r.xMax, v1.x), std::max(v2.x, v3.x)); + r.yMin = std::min(std::min(r.yMin, v1.y), std::min(v2.y, v3.y)); + r.yMax = std::max(std::max(r.yMax, v1.y), std::max(v2.y, v3.y)); } } - // Create dummy boundary points. - boundaryPoint1_[0] = xMin; boundaryPoint1_[1] = yMin; - boundaryPoint2_[0] = xMin; boundaryPoint2_[1] = yMax; - boundaryPoint3_[0] = xMax; boundaryPoint3_[1] = yMax; - boundaryPoint4_[0] = xMax; boundaryPoint4_[1] = yMin; - - log_ << "Local boundary points:\n"; - log_ << "Point 1: " << boundaryPoint1_[0] << " " << boundaryPoint1_[1] << "\n"; - log_ << "Point 2: " << boundaryPoint2_[0] << " " << boundaryPoint2_[1] << "\n"; - log_ << "Point 3: " << boundaryPoint3_[0] << " " << boundaryPoint3_[1] << "\n"; - log_ << "Point 4: " << boundaryPoint4_[0] << " " << boundaryPoint4_[1] << "\n"; + log_ << "Boundary points:\n"; + log_ << "Point 1: " << r.xMin << " " << r.yMin << "\n"; + log_ << "Point 2: " << r.xMin << " " << r.yMax << "\n"; + log_ << "Point 3: " << r.xMax << " " << r.yMax << "\n"; + log_ << "Point 4: " << r.xMax << " " << r.yMin << "\n"; log_ << "\n"; + + return r; } Eigen::Transform OdmOrthoPhoto::getROITransform(float xMin, float yMin) const @@ -910,10 +918,10 @@ void OdmOrthoPhoto::renderPixel(int row, int col, float s, float t, const cv::Ma static_cast(bands[currentBandIndex + i])[idx] = static_cast(value); } - // The first model dictates the alpha band - if (currentBandIndex == 0){ - static_cast(alphaBand)[idx] = static_cast(255); // Alpha should always be in the 255 range - } + // Add 1 to the alpha band if the pixel was visible for this band + // the final alpha band will be set to 255 if alpha == num channels + // (all bands have information at this pixel) + static_cast(alphaBand)[idx] += static_cast(1); } void OdmOrthoPhoto::getBarycentricCoordinates(pcl::PointXYZ v1, pcl::PointXYZ v2, pcl::PointXYZ v3, float x, float y, float &l1, float &l2, float &l3) const @@ -971,7 +979,7 @@ bool OdmOrthoPhoto::isModelOk(const pcl::TextureMesh &mesh) } -bool OdmOrthoPhoto::loadObjFile(std::string inputFile, pcl::TextureMesh &mesh) +bool OdmOrthoPhoto::loadObjFile(std::string inputFile, pcl::TextureMesh &mesh, std::vector &companions) { int data_type; unsigned int data_idx; @@ -980,7 +988,7 @@ bool OdmOrthoPhoto::loadObjFile(std::string inputFile, pcl::TextureMesh &mesh) Eigen::Vector4f origin; Eigen::Quaternionf orientation; - if (!readHeader(inputFile, mesh.cloud, origin, orientation, file_version, data_type, data_idx, offset)) + if (!readHeader(inputFile, mesh.cloud, origin, orientation, file_version, data_type, data_idx, offset, companions)) { throw OdmOrthoPhotoException("Problem reading header in modelfile!\n"); } @@ -1112,10 +1120,10 @@ bool OdmOrthoPhoto::loadObjFile(std::string inputFile, pcl::TextureMesh &mesh) { mesh.tex_polygons.push_back (std::vector ()); mesh.tex_materials.push_back (pcl::TexMaterial ()); - for (std::size_t i = 0; i < companions_.size (); ++i) + for (std::size_t i = 0; i < companions.size (); ++i) { - std::vector::const_iterator mat_it = companions_[i].getMaterial (st[1]); - if (mat_it != companions_[i].materials_.end ()) + std::vector::const_iterator mat_it = companions[i].getMaterial (st[1]); + if (mat_it != companions[i].materials_.end ()) { mesh.tex_materials.back () = *mat_it; break; @@ -1182,7 +1190,8 @@ bool OdmOrthoPhoto::loadObjFile(std::string inputFile, pcl::TextureMesh &mesh) bool OdmOrthoPhoto::readHeader (const std::string &file_name, pcl::PCLPointCloud2 &cloud, Eigen::Vector4f &origin, Eigen::Quaternionf &orientation, int &file_version, int &data_type, unsigned int &data_idx, - const int offset) + const int offset, + std::vector &companions) { origin = Eigen::Vector4f::Zero (); orientation = Eigen::Quaternionf::Identity (); @@ -1319,7 +1328,7 @@ bool OdmOrthoPhoto::readHeader (const std::string &file_name, pcl::PCLPointCloud log_<<"Problem reading material file."; } - companions_.push_back (companion); + companions.push_back (companion); } } diff --git a/modules/odm_orthophoto/src/OdmOrthoPhoto.hpp b/modules/odm_orthophoto/src/OdmOrthoPhoto.hpp index 172366aa..b902b32f 100644 --- a/modules/odm_orthophoto/src/OdmOrthoPhoto.hpp +++ b/modules/odm_orthophoto/src/OdmOrthoPhoto.hpp @@ -27,6 +27,23 @@ // Logger #include "Logger.hpp" +struct Bounds{ + float xMin; + float xMax; + float yMin; + float yMax; + + Bounds() : xMin(0), xMax(0), yMin(0), yMax(0) {} + Bounds(float xMin, float xMax, float yMin, float yMax) : + xMin(xMin), xMax(xMax), yMin(yMin), yMax(yMax){} + Bounds(const Bounds &b) { + xMin = b.xMin; + xMax = b.xMax; + yMin = b.yMin; + yMax = b.yMax; + } +}; + /*! * \brief The OdmOrthoPhoto class is used to create an orthographic photo over a given area. * The class reads an oriented textured mesh from an OBJ-file. @@ -51,18 +68,17 @@ public: private: int width, height; - void parseArguments(int argc, char* argv[]); void printHelp(); void createOrthoPhoto(); /*! - * \brief Adjusts the boundary points so that the entire model fits inside the photo. + * \brief Compute the boundary points so that the entire model fits inside the photo. * * \param mesh The model which decides the boundary. */ - void adjustBoundsForEntireModel(const pcl::TextureMesh &mesh); + Bounds computeBoundsForModel(const pcl::TextureMesh &mesh); /*! * \brief Creates a transformation which aligns the area for the orthophoto. @@ -75,6 +91,9 @@ private: template void initAlphaBand(); + template + void finalizeAlphaBand(); + void saveTIFF(const std::string &filename, GDALDataType dataType); /*! @@ -141,7 +160,7 @@ private: * \param mesh The model. * \return True if model was loaded successfully. */ - bool loadObjFile(std::string inputFile, pcl::TextureMesh &mesh); + bool loadObjFile(std::string inputFile, pcl::TextureMesh &mesh, std::vector &companions); /*! * \brief Function is compied straight from the function in the pcl::io module. @@ -149,7 +168,8 @@ private: bool readHeader (const std::string &file_name, pcl::PCLPointCloud2 &cloud, Eigen::Vector4f &origin, Eigen::Quaternionf &orientation, int &file_version, int &data_type, unsigned int &data_idx, - const int offset); + const int offset, + std::vector &companions); Logger log_; /**< Logging object. */ @@ -160,18 +180,11 @@ private: float resolution_; /**< The number of pixels per meter in the ortho photo. */ - Eigen::Vector2f boundaryPoint1_; /**< The first boundary point for the ortho photo, in local coordinates. */ - Eigen::Vector2f boundaryPoint2_; /**< The second boundary point for the ortho photo, in local coordinates. */ - Eigen::Vector2f boundaryPoint3_; /**< The third boundary point for the ortho photo, in local coordinates. */ - Eigen::Vector2f boundaryPoint4_; /**< The fourth boundary point for the ortho photo, in local coordinates. */ - std::vector bands; void *alphaBand; // Keep alpha band separate int currentBandIndex; cv::Mat depth_; /**< The depth of the ortho photo as an OpenCV matrix, CV_32F. */ - - std::vector companions_; /**< Materials (used by loadOBJFile). **/ }; /*!