Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Mesh vertex coloring as an alternative to mesh texturing #424

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
5 changes: 4 additions & 1 deletion apps/TextureMesh/TextureMesh.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ float fOutlierThreshold;
float fRatioDataSmoothness;
bool bGlobalSeamLeveling;
bool bLocalSeamLeveling;
bool bColorVertices;
unsigned nTextureSizeMultiple;
unsigned nRectPackingHeuristic;
uint32_t nColEmpty;
Expand Down Expand Up @@ -108,6 +109,8 @@ bool Initialize(size_t argc, LPCTSTR* argv)
("patch-packing-heuristic", boost::program_options::value<unsigned>(&OPT::nRectPackingHeuristic)->default_value(3), "specify the heuristic used when deciding where to place a new patch (0 - best fit, 3 - good speed, 100 - best speed)")
("empty-color", boost::program_options::value<uint32_t>(&OPT::nColEmpty)->default_value(0x00FF7F27), "color used for faces not covered by any image")
("orthographic-image-resolution", boost::program_options::value<unsigned>(&OPT::nOrthoMapResolution)->default_value(0), "orthographic image resolution to be generated from the textured mesh - the mesh is expected to be already geo-referenced or at least properly oriented (0 - disabled)")
("color-vertices", boost::program_options::value<bool>(&OPT::bColorVertices)->default_value(false), "set color for mesh vertices and do not generate a texture")

;

// hidden options, allowed both on command line and
Expand Down Expand Up @@ -226,7 +229,7 @@ int main(int argc, LPCTSTR* argv)
{
// compute mesh texture
TD_TIMER_START();
if (!scene.TextureMesh(OPT::nResolutionLevel, OPT::nMinResolution, OPT::fOutlierThreshold, OPT::fRatioDataSmoothness, OPT::bGlobalSeamLeveling, OPT::bLocalSeamLeveling, OPT::nTextureSizeMultiple, OPT::nRectPackingHeuristic, Pixel8U(OPT::nColEmpty)))
if (!scene.TextureMesh(OPT::nResolutionLevel, OPT::nMinResolution, OPT::fOutlierThreshold, OPT::fRatioDataSmoothness, OPT::bGlobalSeamLeveling, OPT::bLocalSeamLeveling, OPT::bColorVertices, OPT::nTextureSizeMultiple, OPT::nRectPackingHeuristic, Pixel8U(OPT::nColEmpty)))
return EXIT_FAILURE;
VERBOSE("Mesh texturing completed: %u vertices, %u faces (%s)", scene.mesh.vertices.GetSize(), scene.mesh.faces.GetSize(), TD_TIMER_GET_FMT().c_str());

Expand Down
76 changes: 57 additions & 19 deletions libs/MVS/Mesh.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ void Mesh::ReleaseExtra()
vertexVertices.Release();
vertexFaces.Release();
vertexBoundary.Release();
vertexColors.Release();
faceNormals.Release();
faceTexcoords.Release();
textureDiffuse.release();
Expand All @@ -89,6 +90,7 @@ void Mesh::EmptyExtra()
vertexVertices.Empty();
vertexFaces.Empty();
vertexBoundary.Empty();
vertexColors.Empty();
faceNormals.Empty();
faceTexcoords.Empty();
textureDiffuse.release();
Expand Down Expand Up @@ -1217,22 +1219,36 @@ void Mesh::Clean(float fDecimate, float fSpurious, bool bRemoveSpikes, unsigned
// define a PLY file format composed only of vertices and triangles
namespace BasicPLY {
// list of property information for a vertex

struct VertexColor {
Mesh::Vertex v;
Mesh::Color c;
};

static const PLY::PlyProperty vert_props[] = {
{"x", PLY::Float32, PLY::Float32, offsetof(Mesh::Vertex,x), 0, 0, 0, 0},
{"y", PLY::Float32, PLY::Float32, offsetof(Mesh::Vertex,y), 0, 0, 0, 0},
{"z", PLY::Float32, PLY::Float32, offsetof(Mesh::Vertex,z), 0, 0, 0, 0}
{"x", PLY::Float32, PLY::Float32, offsetof(VertexColor,v.x), 0, 0, 0, 0},
{"y", PLY::Float32, PLY::Float32, offsetof(VertexColor,v.y), 0, 0, 0, 0},
{"z", PLY::Float32, PLY::Float32, offsetof(VertexColor,v.z), 0, 0, 0, 0},
{"red", PLY::Uint8, PLY::Uint8, offsetof(VertexColor,c.r), 0, 0, 0, 0},
{"green", PLY::Uint8, PLY::Uint8, offsetof(VertexColor,c.g), 0, 0, 0, 0},
{"blue", PLY::Uint8, PLY::Uint8, offsetof(VertexColor,c.b), 0, 0, 0, 0},
};
struct VertexNormal {

struct VertexNormalColor {
Mesh::Vertex v;
Mesh::Normal n;
Mesh::Color c;
};
static const PLY::PlyProperty vert_normal_props[] = {
{ "x", PLY::Float32, PLY::Float32, offsetof(VertexNormal,v.x), 0, 0, 0, 0},
{ "y", PLY::Float32, PLY::Float32, offsetof(VertexNormal,v.y), 0, 0, 0, 0},
{ "z", PLY::Float32, PLY::Float32, offsetof(VertexNormal,v.z), 0, 0, 0, 0},
{"nx", PLY::Float32, PLY::Float32, offsetof(VertexNormal,n.x), 0, 0, 0, 0},
{"ny", PLY::Float32, PLY::Float32, offsetof(VertexNormal,n.y), 0, 0, 0, 0},
{"nz", PLY::Float32, PLY::Float32, offsetof(VertexNormal,n.z), 0, 0, 0, 0}
{ "x", PLY::Float32, PLY::Float32, offsetof(VertexNormalColor,v.x), 0, 0, 0, 0},
{ "y", PLY::Float32, PLY::Float32, offsetof(VertexNormalColor,v.y), 0, 0, 0, 0},
{ "z", PLY::Float32, PLY::Float32, offsetof(VertexNormalColor,v.z), 0, 0, 0, 0},
{"nx", PLY::Float32, PLY::Float32, offsetof(VertexNormalColor,n.x), 0, 0, 0, 0},
{"ny", PLY::Float32, PLY::Float32, offsetof(VertexNormalColor,n.y), 0, 0, 0, 0},
{"nz", PLY::Float32, PLY::Float32, offsetof(VertexNormalColor,n.z), 0, 0, 0, 0},
{"red", PLY::Uint8, PLY::Uint8, offsetof(VertexNormalColor,c.r), 0, 0, 0, 0},
{"green", PLY::Uint8, PLY::Uint8, offsetof(VertexNormalColor,c.g), 0, 0, 0, 0},
{"blue", PLY::Uint8, PLY::Uint8, offsetof(VertexNormalColor,c.b), 0, 0, 0, 0},
};
// list of property information for a face
struct Face {
Expand Down Expand Up @@ -1463,25 +1479,47 @@ bool Mesh::SavePLY(const String& fileName, const cList<String>& comments, bool b
ply.append_comment((_T("TextureFile ")+Util::getFileNameExt(textureFileName)).c_str());
}

bool hasColors = !vertexColors.IsEmpty();
if (vertexNormals.IsEmpty()) {
// describe what properties go into the vertex elements
ply.describe_property(BasicPLY::elem_names[0], 3, BasicPLY::vert_props);
ply.describe_property(BasicPLY::elem_names[0], hasColors ? 6 : 3, BasicPLY::vert_props);

// export the array of vertices
FOREACHPTR(pVert, vertices)
ply.put_element(pVert);
BasicPLY::VertexColor vc;
if(hasColors) {
FOREACH(i, vertices) {
vc.v = vertices[i];
vc.c = vertexColors[i];
ply.put_element(&vc);
}
} else {
FOREACH(i, vertices) {
vc.v = vertices[i];
ply.put_element(&vc);
}
}
} else {
bool hasColors = vertexColors.IsEmpty();
ASSERT(vertices.GetSize() == vertexNormals.GetSize());

// describe what properties go into the vertex elements
ply.describe_property(BasicPLY::elem_names[0], 6, BasicPLY::vert_normal_props);
ply.describe_property(BasicPLY::elem_names[0], hasColors ? 9 : 6, BasicPLY::vert_normal_props);

// export the array of vertices
BasicPLY::VertexNormal vn;
FOREACH(i, vertices) {
vn.v = vertices[i];
vn.n = vertexNormals[i];
ply.put_element(&vn);
BasicPLY::VertexNormalColor vn;
if(hasColors) {
FOREACH(i, vertices) {
vn.v = vertices[i];
vn.n = vertexNormals[i];
vn.c = vertexColors[i];
ply.put_element(&vn);
}
} else {
FOREACH(i, vertices) {
vn.v = vertices[i];
vn.n = vertexNormals[i];
ply.put_element(&vn);
}
}
}
if (ply.get_current_element_count() == 0)
Expand Down
5 changes: 5 additions & 0 deletions libs/MVS/Mesh.h
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,9 @@ class MVS_API Mesh
typedef cList<VertexIdxArr> VertexVerticesArr;
typedef cList<FaceIdxArr> VertexFacesArr;

typedef Pixel8U Color;
typedef CLISTDEF0(Color) VertexColorArr;

typedef TPoint3<Type> Normal;
typedef cList<Normal,const Normal&,0,8192,FIndex> NormalArr;

Expand All @@ -90,6 +93,8 @@ class MVS_API Mesh
VertexVerticesArr vertexVertices; // for each vertex, the list of adjacent vertices (optional)
VertexFacesArr vertexFaces; // for each vertex, the list of faces containing it (optional)
BoolArr vertexBoundary; // for each vertex, stores if it is at the boundary or not (optional)
VertexColorArr vertexColors; // for each vertex, stores its assigned color (optional)


NormalArr faceNormals; // for each face, the normal to it (optional)
TexCoordArr faceTexcoords; // for each face, the texture-coordinates corresponding to the contained vertices (optional)
Expand Down
2 changes: 1 addition & 1 deletion libs/MVS/Scene.h
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ class MVS_API Scene
#endif

// Mesh texturing
bool TextureMesh(unsigned nResolutionLevel, unsigned nMinResolution, float fOutlierThreshold=0.f, float fRatioDataSmoothness=0.3f, bool bGlobalSeamLeveling=true, bool bLocalSeamLeveling=true, unsigned nTextureSizeMultiple=0, unsigned nRectPackingHeuristic=3, Pixel8U colEmpty=Pixel8U(255,127,39));
bool TextureMesh(unsigned nResolutionLevel, unsigned nMinResolution, float fOutlierThreshold=0.f, float fRatioDataSmoothness=0.3f, bool bGlobalSeamLeveling=true, bool bLocalSeamLeveling=true, bool bColorVertices=false, unsigned nTextureSizeMultiple=0, unsigned nRectPackingHeuristic=3, Pixel8U colEmpty=Pixel8U(255,127,39));

#ifdef _USE_BOOST
// implement BOOST serialization
Expand Down
42 changes: 33 additions & 9 deletions libs/MVS/SceneTexture.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -389,7 +389,7 @@ struct MeshTexture {
void CreateSeamVertices();
void GlobalSeamLeveling();
void LocalSeamLeveling();
void GenerateTexture(bool bGlobalSeamLeveling, bool bLocalSeamLeveling, unsigned nTextureSizeMultiple, unsigned nRectPackingHeuristic, Pixel8U colEmpty);
void GenerateTexture(bool bGlobalSeamLeveling, bool bLocalSeamLeveling, bool bColorVertices, unsigned nTextureSizeMultiple, unsigned nRectPackingHeuristic, Pixel8U colEmpty);

template <typename PIXEL>
static inline PIXEL RGB2YCBCR(const PIXEL& v) {
Expand Down Expand Up @@ -433,6 +433,7 @@ struct MeshTexture {

// valid the entire time
Mesh::VertexFacesArr& vertexFaces; // for each vertex, the list of faces containing it
Mesh::VertexColorArr& vertexColors; // for each vertex, assigned vertex color (optional)
BoolArr& vertexBoundary; // for each vertex, stores if it is at the boundary or not
Mesh::TexCoordArr& faceTexcoords; // for each face, the texture-coordinates of the vertices
Image8U3& textureDiffuse; // texture containing the diffuse color
Expand All @@ -450,6 +451,7 @@ MeshTexture::MeshTexture(Scene& _scene, unsigned _nResolutionLevel, unsigned _nM
nResolutionLevel(_nResolutionLevel),
nMinResolution(_nMinResolution),
vertexFaces(_scene.mesh.vertexFaces),
vertexColors(_scene.mesh.vertexColors),
vertexBoundary(_scene.mesh.vertexBoundary),
faceTexcoords(_scene.mesh.faceTexcoords),
textureDiffuse(_scene.mesh.textureDiffuse),
Expand Down Expand Up @@ -1265,14 +1267,14 @@ void MeshTexture::GlobalSeamLeveling()

// fill the matrix A and the coefficients for the Vector b of the linear equation system
IndexArr indices;
Colors vertexColors;
Colors vertColors;
Colors coeffB;
FOREACHPTR(pSeamVertex, seamVertices) {
const SeamVertex& seamVertex = *pSeamVertex;
if (seamVertex.patches.GetSize() < 2)
continue;
seamVertex.SortByPatchIndex(indices);
vertexColors.Resize(indices.GetSize());
vertColors.Resize(indices.GetSize());
FOREACH(i, indices) {
const SeamVertex::Patch& patch0 = seamVertex.patches[indices[i]];
ASSERT(patch0.idxPatch < numPatches);
Expand All @@ -1284,16 +1286,16 @@ void MeshTexture::GlobalSeamLeveling()
const SeamVertex::Patch& patch1 = seamVertex1.patches[idxPatch1];
sampler.AddEdge(patch0.proj, patch1.proj);
}
vertexColors[i] = sampler.GetColor();
vertColors[i] = sampler.GetColor();
}
const VertexPatch2RowMap& vertpatch2row = vertpatch2rows[seamVertex.idxVertex];
for (IDX i=0; i<indices.GetSize()-1; ++i) {
const uint32_t idxPatch0(seamVertex.patches[indices[i]].idxPatch);
const Color& color0 = vertexColors[i];
const Color& color0 = vertColors[i];
const MatIdx col0(vertpatch2row.at(idxPatch0));
for (IDX j=i+1; j<indices.GetSize(); ++j) {
const uint32_t idxPatch1(seamVertex.patches[indices[j]].idxPatch);
const Color& color1 = vertexColors[j];
const Color& color1 = vertColors[j];
const MatIdx col1(vertpatch2row.at(idxPatch1));
ASSERT(idxPatch0 < idxPatch1);
const MatIdx rowA((MatIdx)coeffB.GetSize());
Expand Down Expand Up @@ -1784,7 +1786,7 @@ void MeshTexture::LocalSeamLeveling()
}
}

void MeshTexture::GenerateTexture(bool bGlobalSeamLeveling, bool bLocalSeamLeveling, unsigned nTextureSizeMultiple, unsigned nRectPackingHeuristic, Pixel8U colEmpty)
void MeshTexture::GenerateTexture(bool bGlobalSeamLeveling, bool bLocalSeamLeveling, bool bColorVertices, unsigned nTextureSizeMultiple, unsigned nRectPackingHeuristic, Pixel8U colEmpty)
{
// project patches in the corresponding view and compute texture-coordinates and bounding-box
const int border(2);
Expand Down Expand Up @@ -1861,6 +1863,28 @@ void MeshTexture::GenerateTexture(bool bGlobalSeamLeveling, bool bLocalSeamLevel
}
}

if(bColorVertices) {
/* Only assign color to mesh vertices and quit; do not generate a PNG texture */
vertexColors.Resize(vertices.GetSize());
vertexColors.Memset(0);
for (int_t idx = 0; idx < (int_t) numPatches; ++idx) {
TexturePatch &texturePatch = texturePatches[(uint32_t) idx];
const Image &imageData = images[texturePatch.label];
FOREACHPTR(pIdxFace, texturePatch.faces) {
const FIndex idxFace(*pIdxFace);
const Face &face = faces[idxFace];
TexCoord *texcoords = faceTexcoords.Begin() + idxFace * 3;
const TexCoord offset(texturePatch.rect.tl());
for (int i = 0; i < 3; ++i) {
TexCoord q = texcoords[i] + offset;
vertexColors[face[i]] = imageData.image(q.y, q.x);
}
}
}
faceTexcoords.Empty();
return;
}

// merge texture patches with overlapping rectangles
for (unsigned i=0; i<texturePatches.GetSize()-2; ++i) {
TexturePatch& texturePatchBig = texturePatches[i];
Expand Down Expand Up @@ -1969,7 +1993,7 @@ void MeshTexture::GenerateTexture(bool bGlobalSeamLeveling, bool bLocalSeamLevel
}

// texture mesh
bool Scene::TextureMesh(unsigned nResolutionLevel, unsigned nMinResolution, float fOutlierThreshold, float fRatioDataSmoothness, bool bGlobalSeamLeveling, bool bLocalSeamLeveling, unsigned nTextureSizeMultiple, unsigned nRectPackingHeuristic, Pixel8U colEmpty)
bool Scene::TextureMesh(unsigned nResolutionLevel, unsigned nMinResolution, float fOutlierThreshold, float fRatioDataSmoothness, bool bGlobalSeamLeveling, bool bLocalSeamLeveling, bool bColorVertices, unsigned nTextureSizeMultiple, unsigned nRectPackingHeuristic, Pixel8U colEmpty)
{
MeshTexture texture(*this, nResolutionLevel, nMinResolution);

Expand All @@ -1984,7 +2008,7 @@ bool Scene::TextureMesh(unsigned nResolutionLevel, unsigned nMinResolution, floa
// generate the texture image and atlas
{
TD_TIMER_STARTD();
texture.GenerateTexture(bGlobalSeamLeveling, bLocalSeamLeveling, nTextureSizeMultiple, nRectPackingHeuristic, colEmpty);
texture.GenerateTexture(bGlobalSeamLeveling, bLocalSeamLeveling, bColorVertices, nTextureSizeMultiple, nRectPackingHeuristic, colEmpty);
DEBUG_EXTRA("Generating texture atlas and image completed: %u patches, %u image size (%s)", texture.texturePatches.GetSize(), mesh.textureDiffuse.width(), TD_TIMER_GET_FMT().c_str());
}

Expand Down