mirror of
https://github.com/opencv/opencv.git
synced 2026-07-30 07:43:03 +04:00
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
This commit is contained in:
committed by
GitHub
parent
3e561d8353
commit
6699ca1a40
@@ -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)]:
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
if (nTexCoords)
|
||||
{
|
||||
texMat.copyTo(texCoords);
|
||||
CV_Assert(!texCoords.fixedType() || (texCoords.type() == CV_MAKE_TYPE(CV_32F, nTexCoords)));
|
||||
|
||||
Mat tex3(vec_texCoords);
|
||||
|
||||
if (nTexCoords == 3)
|
||||
{
|
||||
tex3.copyTo(texCoords);
|
||||
}
|
||||
else if (nTexCoords == 2)
|
||||
{
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
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)
|
||||
{
|
||||
std::vector<Mat> marr = { varr[0], varr[1], Mat::zeros(varr[0].size(), CV_32F) };
|
||||
cv::merge(marr, newTexMat);
|
||||
}
|
||||
else
|
||||
{
|
||||
newTexMat = texMat;
|
||||
}
|
||||
newTexMat.copyTo(texCoords);
|
||||
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)
|
||||
{
|
||||
|
||||
@@ -11,67 +11,135 @@
|
||||
|
||||
namespace opencv_test { namespace {
|
||||
|
||||
struct OriginalObjGoldValues
|
||||
{
|
||||
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_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}
|
||||
};
|
||||
|
||||
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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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::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;
|
||||
}
|
||||
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 modelType : {
|
||||
ModelType::File,
|
||||
ModelType::Clipping,
|
||||
ModelType::Color,
|
||||
ModelType::Centered,
|
||||
})
|
||||
for (const auto& res : resolutions)
|
||||
{
|
||||
for (const auto shadingPair : shadingTxt)
|
||||
{
|
||||
cv::TriangleShadingType shadingType = shadingPair.first;
|
||||
std::string shadingName = shadingPair.second;
|
||||
|
||||
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::cout << suffix + "_" + shadingName << "..." << std::endl;
|
||||
|
||||
cv::Mat colorImage, depthImage;
|
||||
|
||||
ModelData modelData(modelType, modelPath);
|
||||
generateImage(res, shadingType, cullingMode, modelData, colorImage, 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);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
cv::Mat colorImage, depthImage;
|
||||
generateImage(res, shadingType, cullingMode, modelType, modelPath, colorImage, 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user