From 6699ca1a408af83f713ea36b1f7304fcc8a48061 Mon Sep 17 00:00:00 2001 From: Rostislav Vasilikhin <savuor@gmail.com> Date: Tue, 16 Apr 2024 09:20:44 +0200 Subject: [PATCH] Merge pull request #25382 from savuor:rv/fix_mesh_load Fix mesh loading for texture coordinates and face indices #25382 ### This PR changes * Texture coordinates were stored incorrectly (3-channel array is read as if there were 2 channels), fixed * Faces were pushed back to the output array instead of indexed writing which produced a lot of empty faces, fixed * A set of ground truth tests were added to cover these issues * `std::vector<cv::Mat>` support added for `saveMesh()` which is required for Python bindings * More command line args were added to rasterization test data generator ### Pull Request Readiness Checklist See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request - [x] I agree to contribute to the project under Apache 2 License. - [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV - [x] The PR is proposed to the proper branch - [x] There is a reference to the original bug report and related work - [x] There is accuracy test, performance test and test data in opencv_extra repository, if applicable Patch to opencv_extra has the same branch name. - [x] The feature is well documented and sample code can be built with the project CMake --- modules/3d/misc/python/test/test_iomesh.py | 2 +- modules/3d/perf/perf_rendering.cpp | 6 +- .../3d/src/pointcloud/load_point_cloud.cpp | 55 ++--- modules/3d/test/test_pointcloud.cpp | 181 ++++++++++----- modules/3d/test/test_rendering.cpp | 8 +- samples/opengl/opengl_testdata_generator.cpp | 219 ++++++++++++------ 6 files changed, 314 insertions(+), 157 deletions(-) diff --git a/modules/3d/misc/python/test/test_iomesh.py b/modules/3d/misc/python/test/test_iomesh.py index 20f301e8d2..647eb06337 100644 --- a/modules/3d/misc/python/test/test_iomesh.py +++ b/modules/3d/misc/python/test/test_iomesh.py @@ -32,7 +32,7 @@ class raster_test(NewOpenCVTests): if a.shape not in goodShapes: self.fail(errorMsg % s) - if texCoords.shape not in [(1, 18, 2), (18, 1, 2)]: + if texCoords.shape not in [(1, 18, 2), (18, 1, 2), (18, 2)]: self.fail('texture coordinates array should be 1x18x2 or 18x1x2') if isinstance(indices, numpy.ndarray): if indices.shape not in [(1, 6, 3), (6, 1, 3)]: diff --git a/modules/3d/perf/perf_rendering.cpp b/modules/3d/perf/perf_rendering.cpp index 48e6bb5739..c565709300 100644 --- a/modules/3d/perf/perf_rendering.cpp +++ b/modules/3d/perf/perf_rendering.cpp @@ -285,10 +285,8 @@ PERF_TEST_P(RenderingTest, rasterizeTriangles, ::testing::Combine( if (shadingType != RASTERIZE_SHADING_WHITE) { // let vertices be in BGR format to avoid later color conversions - // cvtColor does not work with 1d Mats - std::vector<Mat> xyz; - cv::split(colors, xyz); - cv::merge(std::vector<Mat>{xyz[2], xyz[1], xyz[0]}, colors); + // mixChannels does not support in-place operation + cv::mixChannels(Mat(colors).clone(), colors, {0, 2, 1, 1, 2, 0}); } double zNear = 0.1, zFar = 50.0; diff --git a/modules/3d/src/pointcloud/load_point_cloud.cpp b/modules/3d/src/pointcloud/load_point_cloud.cpp index c99f4d2c24..63f6f828f6 100644 --- a/modules/3d/src/pointcloud/load_point_cloud.cpp +++ b/modules/3d/src/pointcloud/load_point_cloud.cpp @@ -140,7 +140,7 @@ void loadMesh(const String &filename, OutputArray vertices, OutputArrayOfArrays std::vector<std::vector<int32_t>> vec_indices; std::vector<Point3f> vec_texCoords; - int nTexCoords; + int nTexCoords = 0; decoder->readData(vec_vertices, vec_normals, vec_rgb, vec_texCoords, nTexCoords, vec_indices, 0); @@ -210,32 +210,30 @@ void loadMesh(const String &filename, OutputArray vertices, OutputArrayOfArrays if (texCoords.needed()) { - int ch = texCoords.empty() ? 0 : texCoords.channels(); - Mat texMat = Mat(1, static_cast<int>(vec_texCoords.size()), CV_MAKETYPE(CV_32F, nTexCoords), vec_texCoords.data()); - if (ch == nTexCoords) - { - texMat.copyTo(texCoords); - } - else + if (nTexCoords) { - Mat newTexMat; - std::vector<Mat> varr; - cv::split(texMat, varr); - if (ch == 2 && nTexCoords == 3) - { - std::vector<Mat> marr = { varr[0], varr[1] }; - cv::merge(marr, newTexMat); - } - else if (ch == 3 && nTexCoords == 2) + CV_Assert(!texCoords.fixedType() || (texCoords.type() == CV_MAKE_TYPE(CV_32F, nTexCoords))); + + Mat tex3(vec_texCoords); + + if (nTexCoords == 3) { - std::vector<Mat> marr = { varr[0], varr[1], Mat::zeros(varr[0].size(), CV_32F) }; - cv::merge(marr, newTexMat); + tex3.copyTo(texCoords); } - else + else if (nTexCoords == 2) { - newTexMat = texMat; + // if texCoords is empty then channels() can be any number + bool has3ch = texCoords.channels() == 3; + int ch = has3ch ? 3 : 2; + std::vector<int> permut = has3ch ? std::vector<int>{ 0, 0, 1, 1, -1, 2 } : std::vector<int>{ 0, 0, 1, 1 }; + texCoords.createSameSize(vec_texCoords, CV_MAKE_TYPE(CV_32F, ch)); + Mat out = texCoords.getMat(); + cv::mixChannels(tex3, out, permut); } - newTexMat.copyTo(texCoords); + } + else + { + texCoords.clear(); } } @@ -280,7 +278,8 @@ void saveMesh(const String &filename, InputArray vertices, InputArrayOfArrays in std::vector<std::vector<int32_t>> vec_indices; CV_Assert(indices.depth() == CV_32S); - if (indices.kind() == _InputArray::KindFlag::STD_VECTOR_VECTOR) + if (indices.kind() == _InputArray::KindFlag::STD_VECTOR_VECTOR || + indices.kind() == _InputArray::KindFlag::STD_VECTOR_MAT) { std::vector<Mat> mat_indices; indices.getMatVector(mat_indices); @@ -312,13 +311,9 @@ void saveMesh(const String &filename, InputArray vertices, InputArrayOfArrays in } if (nTexCoords == 2) { - std::vector<Point2f> vec2_texCoords; - texCoords.copyTo(vec2_texCoords); - for (size_t i = 0; i < vec2_texCoords.size(); i++) - { - Point2f p = vec2_texCoords[i]; - vec_texCoords.push_back({p.x, p.y, 0}); - } + // extend by 3rd zero channel + vec_texCoords.resize(texCoords.total()); + cv::mixChannels(texCoords, vec_texCoords, {0, 0, 1, 1, -1, 2}); } if (nTexCoords == 3) { diff --git a/modules/3d/test/test_pointcloud.cpp b/modules/3d/test/test_pointcloud.cpp index ba5a34e385..e18c54d66e 100644 --- a/modules/3d/test/test_pointcloud.cpp +++ b/modules/3d/test/test_pointcloud.cpp @@ -11,67 +11,135 @@ namespace opencv_test { namespace { -TEST(PointCloud, LoadPointCloudObj) +struct OriginalObjGoldValues { - std::vector<cv::Point3f> points_gold = { - {-5.93915f, -0.13257f, 2.55837f}, - {-5.93915f, 1.86743f, 2.55837f}, - {-5.93915f, -0.13257f, -1.16339f}, - {-5.93915f, 1.86743f, -1.16339f}, - {0.399941f, -0.13257f, 2.55837f}, - {0.399941f, 1.86743f, 2.55837f}, - {0.399941f, -0.13257f, -1.16339f}, - {0.399941f, 1.86743f, -1.16339f} - }; - - std::vector<cv::Point3f> normals_gold = { - {-1.0000f, 0.0000f, 0.0000f}, - { 0.0000f, 0.0000f, -1.0000f}, - { 1.0000f, 0.0000f, 0.0000f}, - { 0.0000f, 0.0000f, 1.0000f}, - { 0.0000f, -1.0000f, 0.0000f}, - { 0.0000f, 1.0000f, 0.0000f} - }; - - std::vector<cv::Point3f> rgb_gold = { - {0.0756f, 0.5651f, 0.5829f}, - {0.8596f, 0.1105f, 0.8455f}, - {0.8534f, 0.6143f, 0.3950f}, - {0.0438f, 0.6308f, 0.3065f}, - {0.9716f, 0.7170f, 0.8378f}, - {0.2472f, 0.7701f, 0.0234f}, - {0.6472f, 0.7467f, 0.5981f}, - {0.3502f, 0.7954f, 0.0443f} - }; + OriginalObjGoldValues() + { + std::array<float, 6> vals = { -5.93915f, -0.13257f, 2.55837f, 1.86743f, -1.16339f, 0.399941f }; + points = + { + { vals[0], vals[1], vals[2] }, + { vals[0], vals[3], vals[2] }, + { vals[0], vals[1], vals[4] }, + { vals[0], vals[3], vals[4] }, + { vals[5], vals[1], vals[2] }, + { vals[5], vals[3], vals[2] }, + { vals[5], vals[1], vals[4] }, + { vals[5], vals[3], vals[4] }, + }; + + normals = + { + {-1.0f, 0.0f, 0.0f}, + { 0.0f, 0.0f, -1.0f}, + { 1.0f, 0.0f, 0.0f}, + { 0.0f, 0.0f, 1.0f}, + { 0.0f, -1.0f, 0.0f}, + { 0.0f, 1.0f, 0.0f} + }; + + rgb = + { + {0.0756f, 0.5651f, 0.5829f}, + {0.8596f, 0.1105f, 0.8455f}, + {0.8534f, 0.6143f, 0.3950f}, + {0.0438f, 0.6308f, 0.3065f}, + {0.9716f, 0.7170f, 0.8378f}, + {0.2472f, 0.7701f, 0.0234f}, + {0.6472f, 0.7467f, 0.5981f}, + {0.3502f, 0.7954f, 0.0443f} + }; + + std::vector<std::pair<int, int>> tcvals = + { + { 3, 0 }, + { 5, 0 }, + { 5, 2 }, + { 3, 2 }, + { 5, 4 }, + { 3, 4 }, + { 5, 6 }, + { 3, 6 }, + { 5, 8 }, + { 3, 8 }, + { 1, 4 }, + { 1, 6 }, + { 7, 4 }, + { 7, 6 }, + }; + + for (const auto& p : tcvals) + { + texCoords.push_back({p.first * 0.125f, p.second * 0.125f}); + } + + // mesh data is duplicated for each face + std::vector<std::array<int, 9>> fileIndices = + { + { 1, 1, 1, /**/ 2, 2, 1, /**/ 4, 3, 1 }, + { 3, 4, 2, /**/ 4, 3, 2, /**/ 8, 5, 2 }, + { 7, 6, 3, /**/ 8, 5, 3, /**/ 6, 7, 3 }, + { 5, 8, 4, /**/ 6, 7, 4, /**/ 2, 9, 4 }, + { 3, 11, 5, /**/ 7, 6, 5, /**/ 5, 8, 5 }, + { 8, 5, 6, /**/ 4, 13, 6, /**/ 2, 14, 6 }, + }; + + for (const auto& fi : fileIndices) + { + pointsMesh.push_back(points.at(fi[0] - 1)); + pointsMesh.push_back(points.at(fi[3] - 1)); + pointsMesh.push_back(points.at(fi[6] - 1)); + rgbMesh.push_back(rgb.at(fi[0] - 1)); + rgbMesh.push_back(rgb.at(fi[3] - 1)); + rgbMesh.push_back(rgb.at(fi[6] - 1)); + + texCoordsMesh.push_back(texCoords.at(fi[1] - 1)); + texCoordsMesh.push_back(texCoords.at(fi[4] - 1)); + texCoordsMesh.push_back(texCoords.at(fi[7] - 1)); + + normalsMesh.push_back(normals.at(fi[2] - 1)); + normalsMesh.push_back(normals.at(fi[5] - 1)); + normalsMesh.push_back(normals.at(fi[8] - 1)); + } + + indices = + { + { 0, 1, 2}, + { 3, 4, 5}, + { 6, 7, 8}, + { 9, 10, 11}, + {12, 13, 14}, + {15, 16, 17}, + }; + } + + std::vector<Point3f> points, pointsMesh, normals, normalsMesh, rgb, rgbMesh; + std::vector<Point2f> texCoords, texCoordsMesh; + std::vector<std::vector<int32_t>> indices; +}; +OriginalObjGoldValues origGold; + +TEST(PointCloud, LoadPointCloudObj) +{ std::vector<cv::Point3f> points, normals, rgb; auto folder = cvtest::TS::ptr()->get_data_path(); cv::loadPointCloud(folder + "pointcloudio/orig.obj", points, normals, rgb); - EXPECT_EQ(points_gold, points); - EXPECT_EQ(rgb_gold, rgb); - EXPECT_EQ(normals_gold, normals); + EXPECT_EQ(origGold.points, points); + EXPECT_EQ(origGold.rgb, rgb); + EXPECT_EQ(origGold.normals, normals); } TEST(PointCloud, LoadObjNoNormals) { - std::vector<cv::Point3f> points_gold = { - {-5.93915f, -0.13257f, 2.55837f}, - {-5.93915f, 1.86743f, 2.55837f}, - {-5.93915f, -0.13257f, -1.16339f}, - {-5.93915f, 1.86743f, -1.16339f}, - {0.399941f, -0.13257f, 2.55837f}, - {0.399941f, 1.86743f, 2.55837f}, - {0.399941f, -0.13257f, -1.16339f}, - {0.399941f, 1.86743f, -1.16339f}}; - std::vector<cv::Point3f> points, normals; auto folder = cvtest::TS::ptr()->get_data_path(); cv::loadPointCloud(folder + "pointcloudio/orig_no_norms.obj", points, normals); - EXPECT_EQ(points_gold, points); + EXPECT_EQ(origGold.points, points); EXPECT_TRUE(normals.empty()); } @@ -125,11 +193,11 @@ TEST(PointCloud, LoadSaveMeshObj) std::string new_path = tempfile("new_mesh.obj"); cv::loadMesh(folder + "pointcloudio/orig.obj", points, indices, normals, colors, texCoords); - EXPECT_FALSE(points.empty()); - EXPECT_FALSE(indices.empty()); - EXPECT_FALSE(normals.empty()); - EXPECT_FALSE(colors.empty()); - EXPECT_FALSE(texCoords.empty()); + EXPECT_EQ(origGold.pointsMesh, points); + EXPECT_EQ(origGold.indices, indices); + EXPECT_EQ(origGold.normalsMesh, normals); + EXPECT_EQ(origGold.rgbMesh, colors); + EXPECT_EQ(origGold.texCoordsMesh, texCoords); cv::saveMesh(new_path, points, indices, normals, colors, texCoords); std::vector<cv::Point3f> points_gold, normals_gold, colors_gold; @@ -164,8 +232,17 @@ TEST_P(PlyTest, LoadSaveMesh) std::string new_path = tempfile("new_mesh.ply"); cv::loadMesh(folder + fname, points_gold, indices_gold, normals_gold, colors_gold); - EXPECT_FALSE(points_gold.empty()); - EXPECT_FALSE(indices_gold.empty()); + size_t truePts, trueFaces; + if (fname.find("/dragon.ply") != fname.npos) + { + truePts = 50000; trueFaces = 100000; + } + else + { + truePts = 8; trueFaces = 12; + } + EXPECT_EQ(points_gold.size(), truePts); + EXPECT_EQ(indices_gold.size(), trueFaces); cv::saveMesh(new_path, points_gold, indices_gold, normals_gold, colors_gold); diff --git a/modules/3d/test/test_rendering.cpp b/modules/3d/test/test_rendering.cpp index da1be90cac..02e73fee26 100644 --- a/modules/3d/test/test_rendering.cpp +++ b/modules/3d/test/test_rendering.cpp @@ -516,13 +516,11 @@ protected: if (shadingType != RASTERIZE_SHADING_WHITE) { + // let vertices be in BGR format to avoid later color conversions + // mixChannels() does not support in-place operation colors = Mat(modelData.colors); colors.convertTo(colors, ftype); - // let vertices be in BGR format to avoid later color conversions - // cvtColor does not work with 1d Mats - std::vector<Mat> xyz; - cv::split(colors, xyz); - cv::merge(std::vector<Mat>{xyz[2], xyz[1], xyz[0]}, colors); + cv::mixChannels(colors.clone(), colors, {0, 2, 1, 1, 2, 0}); } indices = Mat(modelData.indices); diff --git a/samples/opengl/opengl_testdata_generator.cpp b/samples/opengl/opengl_testdata_generator.cpp index 0f6bf6e42b..22e8bb19fa 100644 --- a/samples/opengl/opengl_testdata_generator.cpp +++ b/samples/opengl/opengl_testdata_generator.cpp @@ -1,4 +1,5 @@ #include <iostream> +#include <map> #ifdef _WIN32 #define WIN32_LEAN_AND_MEAN 1 @@ -78,6 +79,9 @@ public: upVector = Vec3d(0.0, 1.0, 0.0); fovy = 45.0; + zNear = 0.1; + zFar = 50; + scaleCoeff = 1000.0; vertices = std::vector<Vec3f>(4, {2.0f, 0, -2.0f}); colors = std::vector<Vec3f>(4, {0, 0, 1.0f}); @@ -91,6 +95,9 @@ public: upVector = Vec3d( 0.0, 1.0, 0.0); fovy = 45.0; + zNear = 0.1; + zFar = 50; + scaleCoeff = 1000.0; objectPath = objPath; std::vector<vector<int>> indvec; @@ -119,6 +126,9 @@ public: upVector = Vec3d(0.0, 1.0, 0.0); fovy = 45.0; + zNear = 0.1; + zFar = 50; + scaleCoeff = 1000.0; vertices = { @@ -152,6 +162,9 @@ public: upVector = Vec3d(0.0, 1.0, 0.0); fovy = 45.0; + zNear = 0.1; + zFar = 50; + scaleCoeff = 1000.0; vertices = { @@ -181,6 +194,9 @@ public: upVector = Vec3d(0.0, 1.0, 0.0); fovy = 60.0; + zNear = 0.1; + zFar = 50; + scaleCoeff = 1000.0; vertices = { @@ -208,11 +224,35 @@ public: } } + ModelData(std::string modelPath, double fov, double near, double far, double scale, Vec3d pos, Vec3d center, Vec3d up) + { + objectPath = modelPath; + position = pos; + lookat = center; + upVector = up; + fovy = fov; + zNear = near; + zFar = far; + scaleCoeff = scale; + + std::vector<vector<int>> indvec; + + loadMesh(objectPath, vertices, indvec, noArray(), colors); + if (vertices.size() != colors.size()) + { + std::runtime_error("Model should contain normals for each vertex"); + } + for (const auto &vec : indvec) + { + indices.push_back({vec[0], vec[1], vec[2]}); + } + } + Vec3d position; Vec3d lookat; Vec3d upVector; - double fovy; + double fovy, zNear, zFar, scaleCoeff; std::vector<Vec3f> vertices; std::vector<Vec3i> indices; @@ -239,13 +279,11 @@ void draw(void* userdata) } static void generateImage(cv::Size imgSz, TriangleShadingType shadingType, TriangleCullingMode cullingMode, - ModelType modelType, std::string modelPath, cv::Mat& colorImage, cv::Mat& depthImage) + const ModelData& modelData, cv::Mat& colorImage, cv::Mat& depthImage) { namedWindow("OpenGL", WINDOW_OPENGL); resizeWindow("OpenGL", imgSz.width, imgSz.height); - ModelData modelData(modelType, modelPath); - DrawData data; std::vector<Vec3f> vertices; @@ -293,10 +331,9 @@ static void generateImage(cv::Size imgSz, TriangleShadingType shadingType, Trian data.arr.setColorArray(colors4f); data.indices.copyFrom(idxLinear); - double zNear = 0.1, zFar = 50; glMatrixMode(GL_PROJECTION); glLoadIdentity(); - gluPerspective(modelData.fovy, (double)imgSz.width / imgSz.height, zNear, zFar); + gluPerspective(modelData.fovy, (double)imgSz.width / imgSz.height, modelData.zNear, modelData.zFar); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); @@ -343,10 +380,10 @@ static void generateImage(cv::Size imgSz, TriangleShadingType shadingType, Trian // map from [0, 1] to [zNear, zFar] for (auto it = depthImage.begin<float>(); it != depthImage.end<float>(); ++it) { - *it = (float)(zNear * zFar / (double(*it) * (zNear - zFar) + zFar)); + *it = (float)(modelData.zNear * modelData.zFar / (double(*it) * (modelData.zNear - modelData.zFar) + modelData.zFar)); } cv::flip(depthImage, depthImage, 0); - depthImage.convertTo(depthImage, CV_16U, 1000.0); + depthImage.convertTo(depthImage, CV_16U, modelData.scaleCoeff); char key = (char)waitKey(40); if (key == 27) @@ -364,6 +401,26 @@ int main(int argc, char* argv[]) "{ help h usage ? | | show this message }" "{ outPath | | output path for generated images }" "{ modelPath | | path to 3d model to render }" + "{ custom | | pass it to use custom camera parameters instead of iterating through test parameters }" + "{ fov | 45.0 | (if custom parameters are used) field of view }" + "{ posx | 1.0 | (if custom parameters are used) camera position x }" + "{ posy | 1.0 | (if custom parameters are used) camera position y }" + "{ posz | 1.0 | (if custom parameters are used) camera position z }" + "{ lookatx | 0.0 | (if custom parameters are used) lookup camera direction x }" + "{ lookaty | 0.0 | (if custom parameters are used) lookup camera direction y }" + "{ lookatz | 0.0 | (if custom parameters are used) lookup camera direction z }" + "{ upx | 0.0 | (if custom parameters are used) up camera direction x }" + "{ upy | 1.0 | (if custom parameters are used) up camera direction y }" + "{ upz | 0.0 | (if custom parameters are used) up camera direction z }" + "{ resx | 640 | (if custom parameters are used) camera resolution x }" + "{ resy | 480 | (if custom parameters are used) camera resolution y }" + "{ zNear | 0.1 | (if custom parameters are used) near z clipping plane }" + "{ zFar | 50 | (if custom parameters are used) far z clipping plane }" + "{ scaleCoeff | 1000 | (if custom parameters are used) scale coefficient for saving depth }" + "{ shading | | (if custom parameters are used) shading type: white/flat/shaded }" + "{ culling | | (if custom parameters are used) culling type: none/cw/ccw }" + "{ colorPath | | (if custom parameters are used) output path for color image }" + "{ depthPath | | (if custom parameters are used) output path for depth image }" ); parser.about("This app is used to generate test data for triangleRasterize() function"); @@ -380,77 +437,109 @@ int main(int argc, char* argv[]) return -1; } - std::string outPath = parser.get<std::string>("outPath"); - if (outPath.empty()) + if (parser.has("custom")) { - std::cout << "No output path given" << std::endl; - return -1; + double fov = parser.get<double>("fov"); + Vec3d position, lookat, upVector; + position[0] = parser.get<double>("posx"); + position[1] = parser.get<double>("posy"); + position[2] = parser.get<double>("posz"); + lookat[0] = parser.get<double>("lookatx"); + lookat[1] = parser.get<double>("lookaty"); + lookat[2] = parser.get<double>("lookatz"); + upVector[0] = parser.get<double>("upx"); + upVector[1] = parser.get<double>("upy"); + upVector[2] = parser.get<double>("upz"); + Size res; + res.width = parser.get<int>("resx"); + res.height = parser.get<int>("resy"); + double zNear = parser.get<double>("zNear"); + double zFar = parser.get<double>("zFar"); + double scaleCoeff = parser.get<double>("scaleCoeff"); + + std::map<std::string, cv::TriangleShadingType> shadingTxt = { + { "white", RASTERIZE_SHADING_WHITE }, + { "flat", RASTERIZE_SHADING_FLAT }, + { "shaded", RASTERIZE_SHADING_SHADED }, + }; + cv::TriangleShadingType shadingType = shadingTxt.at(parser.get<std::string>("shading")); + + std::map<std::string, cv::TriangleCullingMode> cullingTxt = { + { "none", RASTERIZE_CULLING_NONE }, + { "cw", RASTERIZE_CULLING_CW }, + { "ccw", RASTERIZE_CULLING_CCW }, + }; + cv::TriangleCullingMode cullingMode = cullingTxt.at(parser.get<std::string>("culling")); + + std::string colorPath = parser.get<std::string>("colorPath"); + std::string depthPath = parser.get<std::string>("depthPath"); + + Mat colorImage, depthImage; + ModelData modelData(modelPath, fov, zNear, zFar, scaleCoeff, position, lookat, upVector); + generateImage(res, shadingType, cullingMode, modelData, colorImage, depthImage); + + cv::imwrite(colorPath, colorImage); + cv::imwrite(depthPath, depthImage); } - - std::array<cv::Size, 4> resolutions = { cv::Size {700, 700}, cv::Size {640, 480}, cv::Size(256, 256), cv::Size(320, 240) }; - for (const auto& res : resolutions) + else { - for (const auto shadingType : { - RASTERIZE_SHADING_WHITE, - RASTERIZE_SHADING_FLAT, - RASTERIZE_SHADING_SHADED - }) + std::string outPath = parser.get<std::string>("outPath"); + if (outPath.empty()) { - std::string shadingName; - switch (shadingType) - { - case RASTERIZE_SHADING_WHITE: shadingName = "White"; break; - case RASTERIZE_SHADING_FLAT: shadingName = "Flat"; break; - case RASTERIZE_SHADING_SHADED: shadingName = "Shaded"; break; - default: - break; - } + std::cout << "No output path given" << std::endl; + return -1; + } - for (const auto cullingMode : { - RASTERIZE_CULLING_NONE, - RASTERIZE_CULLING_CW, - RASTERIZE_CULLING_CCW - }) + std::array<cv::Size, 4> resolutions = {cv::Size{700, 700}, cv::Size{640, 480}, cv::Size(256, 256), cv::Size(320, 240)}; + std::vector<std::pair<cv::TriangleShadingType, std::string>> shadingTxt = { + {RASTERIZE_SHADING_WHITE, "White"}, + {RASTERIZE_SHADING_FLAT, "Flat"}, + {RASTERIZE_SHADING_SHADED, "Shaded"}, + }; + std::vector<std::pair<cv::TriangleCullingMode, std::string>> cullingTxt = { + {RASTERIZE_CULLING_NONE, "None"}, + {RASTERIZE_CULLING_CW, "CW"}, + {RASTERIZE_CULLING_CCW, "CCW"}, + }; + std::vector<std::pair<ModelType, std::string>> modelTxt = { + {ModelType::File, "File"}, + {ModelType::Clipping, "Clipping"}, + {ModelType::Color, "Color"}, + {ModelType::Centered, "Centered"}, + }; + + for (const auto& res : resolutions) + { + for (const auto shadingPair : shadingTxt) { - std::string cullingName; - switch (cullingMode) - { - case RASTERIZE_CULLING_NONE: cullingName = "None"; break; - case RASTERIZE_CULLING_CW: cullingName = "CW"; break; - case RASTERIZE_CULLING_CCW: cullingName = "CCW"; break; - default: break; - } + cv::TriangleShadingType shadingType = shadingPair.first; + std::string shadingName = shadingPair.second; - for (const auto modelType : { - ModelType::File, - ModelType::Clipping, - ModelType::Color, - ModelType::Centered, - }) + for (const auto cullingPair : cullingTxt) { - std::string modelName; - switch (modelType) + cv::TriangleCullingMode cullingMode = cullingPair.first; + std::string cullingName = cullingPair.second; + + for (const auto modelPair : modelTxt) { - case ModelType::File: modelName = "File"; break; - case ModelType::Clipping: modelName = "Clipping"; break; - case ModelType::Color: modelName = "Color"; break; - case ModelType::Centered: modelName = "Centered"; break; - default: - break; - } + ModelType modelType = modelPair.first; + std::string modelName = modelPair.second; - std::string suffix = cv::format("%s_%dx%d_Cull%s", modelName.c_str(), res.width, res.height, cullingName.c_str()); + std::string suffix = cv::format("%s_%dx%d_Cull%s", modelName.c_str(), res.width, res.height, cullingName.c_str()); - std::cout << suffix + "_" + shadingName << "..." << std::endl; + std::cout << suffix + "_" + shadingName << "..." << std::endl; - cv::Mat colorImage, depthImage; - generateImage(res, shadingType, cullingMode, modelType, modelPath, colorImage, depthImage); + cv::Mat colorImage, depthImage; - std::string gtPathColor = outPath + "/example_image_" + suffix + "_" + shadingName + ".png"; - std::string gtPathDepth = outPath + "/depth_image_" + suffix + ".png"; + ModelData modelData(modelType, modelPath); + generateImage(res, shadingType, cullingMode, modelData, colorImage, depthImage); - cv::imwrite(gtPathColor, colorImage); - cv::imwrite(gtPathDepth, depthImage); + std::string gtPathColor = outPath + "/example_image_" + suffix + "_" + shadingName + ".png"; + std::string gtPathDepth = outPath + "/depth_image_" + suffix + ".png"; + + cv::imwrite(gtPathColor, colorImage); + cv::imwrite(gtPathDepth, depthImage); + } } } }