openMVS icon indicating copy to clipboard operation
openMVS copied to clipboard

texture may be very blurred in some area after mesh decimation

Open CanCanZeng opened this issue 2 years ago • 25 comments

Hi, I use OpenMVS on ubuntu16.04 to create textured mesh. The original result is rather good, but I find that if I downsample the mesh using OpenMVS or other algorithm, some area may become very blurred. Is there any way to relieve this effect?

original: TextureMesh -i v2.0_mesh.mvs -o v2.0_mesh_texture.mvs --export-type ply and the result looks like below: Screenshot from 2022-06-14 14-19-22

decimated by openMVS: TextureMesh -i v2.0_mesh.mvs -o v2.0_mesh_texture_decimated.mvs --export-type ply --decimate 0.1 and the result looks like below: Screenshot from 2022-06-14 14-17-40

decimated by other algorithm: Screenshot from 2022-06-14 14-18-31

The texture of the generated mesh retains a lot of detail in some places and is very blurry in others, which is very confusing.

CanCanZeng avatar Jun 14 '22 06:06 CanCanZeng

indeed, maybe you decimate the mesh to much and the triangles become too big (hard to see without a wireframe rendering) you can also try to decimate the mesh after texturing, using for ex Meshlab which has decimation for textured meshes

cdcseacave avatar Jun 14 '22 07:06 cdcseacave

the mesh with wireframe rendering is showing below: original: Screenshot from 2022-06-14 15-31-41

decimated by openMVS: Screenshot from 2022-06-14 15-29-15

decimated by other algorithm: Screenshot from 2022-06-14 15-30-30

The reason I downsampled the mesh is that the density of original triangles is too high and the texture process is very slow. So I can't downsample after the texture is done.

CanCanZeng avatar Jun 14 '22 07:06 CanCanZeng

0.1 is too much, the trinagles are too big for texturing

cdcseacave avatar Jun 14 '22 07:06 cdcseacave

OK, I know decimate to 0.1 is too much.

But I still no understand why the mesh is only blurred in some area, is there any way to make the blur more evenly?

CanCanZeng avatar Jun 14 '22 08:06 CanCanZeng

I found the causer! It is caused by outlier rejection process. Because the patch is too large, the average color cannot be used for outlier rejection, so the above problem is solved if I turn off outlier rejection. But turn off outlier rejection brings other problems, the textures are very strange in places with reflections.

In fact we have a dense point cloud, the color of the point cloud is very close to the texture color of the mesh, can we use the color or visibility of dense point cloud as a clue?

CanCanZeng avatar Jun 15 '22 10:06 CanCanZeng

can you post some screenshots here?

cdcseacave avatar Jun 15 '22 11:06 cdcseacave

OK

close outlier rejection: Screenshot from 2022-06-15 21-15-48 the texture on the wall looks very good, but the texture on the floor is bad. Screenshot from 2022-06-15 21-13-01

open outlier rejection: the texture on the wall is what I have posted above(some place may blurred very much), but the texture on the floor is right. Screenshot from 2022-06-15 21-14-28

CanCanZeng avatar Jun 15 '22 13:06 CanCanZeng

I see indeed, we need to think at a new way of filtering the outlier views per face...

cdcseacave avatar Jun 15 '22 13:06 cdcseacave

What about color histogram or gradiant histogram? Say a gray-scale color histogram with 16 bins.

CanCanZeng avatar Jun 16 '22 08:06 CanCanZeng

yes, that should be much more robust, and probably not much slower I'd start though with an 8 bin histogram, each bin on 8 bin counter pls help with the implementation if you can

cdcseacave avatar Jun 16 '22 09:06 cdcseacave

OK, I'm also trying to implement it

CanCanZeng avatar Jun 16 '22 09:06 CanCanZeng

great, thank you, pls poke me if I can help with any advice/info

cdcseacave avatar Jun 16 '22 09:06 cdcseacave

Hi, I realized a simple one, the result is not perfect yet, may be you can help me enhance it. I made a pull request here https://github.com/cdcseacave/openMVS/pull/829

I use color histogram instead of intensity histogram right now, since I find that the preformance of using intensity histogram is rather poor. And the performance now will be better if the mesh is more sparse, using very dense mesh will decrease the quality of texture, since too few pixels in one patch, the histogram can not represent the patch very well.

And I use correlation coefficient to measure the similarity of two patch, there must be better ways, any suggestion is welcome!

CanCanZeng avatar Jun 17 '22 10:06 CanCanZeng

I think this is a good start. Some thoughts:

  • I think intensity histogram should work, maybe something else is the problem here
  • i'd replace the offset-like correlation with a pre histogram equalization: https://en.wikipedia.org/wiki/Histogram_equalization
  • some type of correlation is ok, you can use ZNCC for ex
  • indeed, small triangles will be a problem, take a look at this PR https://github.com/cdcseacave/openMVS/pull/807 which combine small adjacent faces into larger virtual faces before texturing, you should try your idea here

cdcseacave avatar Jun 18 '22 09:06 cdcseacave

i'd replace the offset-like correlation with a pre histogram equalization: https://en.wikipedia.org/wiki/Histogram_equalization

What does it mean? Do histogram equalization one the color image or the patch histogram? I find that the histogram of a patch tends to be very sparse, only one or two bins has data, so apply histogram equalization directly may not be possible.

some type of correlation is ok, you can use ZNCC for ex

I just replace NCC with ZNCC, the result does not have much difference.

indeed, small triangles will be a problem, take a look at this PR https://github.com/cdcseacave/openMVS/pull/807 which combine small adjacent faces into larger virtual faces before texturing, you should try your idea here

I'll try it later.

CanCanZeng avatar Jun 19 '22 06:06 CanCanZeng

histogram equalization on the patch intensities histogram

cdcseacave avatar Jun 19 '22 06:06 cdcseacave

I have a problem using MVS::PointCloud::Octree, the code is listed below:


// construct octree for point cloud
MVS::PointCloud::Octree octPoints(scene.pointcloud.points, [](MVS::PointCloud::Octree::IDX_TYPE size, MVS::PointCloud::Octree::Type /*radius*/) {
    return size > 512;
});

// search in a small sphere
PointCloud::Point center = (pts[0] + pts[1] + pts[2]) / 3;
float radius = std::max(std::max(cv::norm(pts[0] - center), cv::norm(pts[1] - center)), cv::norm(pts[2] - center));

// search dense points in this sphere
MVS::PointCloud::Octree::IDXARR_TYPE point_indices;
octPoints.Collect(point_indices, center, radius);

the last line will cause error when compilation,


openMVS/libs/Common/Octree.inl:380: error: no matching function for call to ‘SEACAVE::TOctree<SEACAVE::cList<SEACAVE::TPoint3<float>, const SEACAVE::TPoint3<float>&, 0, 8192>, float, 3>::_Collect(const SEACAVE::TOctree<SEACAVE::cList<SEACAVE::TPoint3<float>, const SEACAVE::TPoint3<float>&, 0, 8192>, float, 3>::CELL_TYPE&, SEACAVE::TOctree<SEACAVE::cList<SEACAVE::TPoint3<float>, const SEACAVE::TPoint3<float>&, 0, 8192>, float, 3>::AABB_TYPE, SEACAVE::TOctree<SEACAVE::cList<SEACAVE::TPoint3<float>, const SEACAVE::TPoint3<float>&, 0, 8192>, float, 3>::IndexInserter) const’
  380 |  _Collect(m_root, AABB_TYPE(center, radius), IndexInserter(indices));
      |  ^~~~~~~~

I did not find any example on using Collect function in that way, so I have no idea how to fix it , can you help?

CanCanZeng avatar Jun 19 '22 06:06 CanCanZeng

you are using it correctly, I've just tested your code on my OpenMVS project and it works

cdcseacave avatar Jun 19 '22 07:06 cdcseacave

That is so wired! I use it in SceneTexture.cpp, maybe I need to include some header files?

CanCanZeng avatar Jun 19 '22 07:06 CanCanZeng

I compile OpenMVS on ubuntu20.04, gcc=9.4.0, intel i7-8700k cpu. the full error log is:


[ 15%] Built target Common
[ 22%] Built target Math
[ 42%] Built target IO
Scanning dependencies of target MVS
[ 45%] Building CXX object libs/MVS/CMakeFiles/MVS.dir/PointCloud.cpp.o
[ 45%] Building CXX object libs/MVS/CMakeFiles/MVS.dir/SceneTexture.cpp.o
/media/zcc/data/Project/Work/DenseReconstruction/openMVS/libs/MVS/SceneTexture.cpp: In member function ‘bool MeshTexture::ListCameraFaces(MeshTexture::FaceDataViewArr&, const IIndexArr&) const’:
/media/zcc/data/Project/Work/DenseReconstruction/openMVS/libs/MVS/SceneTexture.cpp:835:14: warning: unused variable ‘points’ [-Wunused-variable]
  835 |  const auto& points = scene.pointcloud.points;
      |              ^~~~~~
In file included from /media/zcc/data/Project/Work/DenseReconstruction/openMVS/libs/Common/Octree.h:233,
                 from /media/zcc/data/Project/Work/DenseReconstruction/openMVS/libs/Common/Types.h:2796,
                 from /media/zcc/data/Project/Work/DenseReconstruction/openMVS/libs/Common/Common.h:176,
                 from /media/zcc/data/Project/Work/DenseReconstruction/openMVS/libs/MVS/Common.h:42,
                 from /media/zcc/data/Project/Work/DenseReconstruction/openMVS/build/Debug/libs/MVS/CMakeFiles/MVS.dir/cmake_pch.hxx:5,
                 from <command-line>:
/media/zcc/data/Project/Work/DenseReconstruction/openMVS/libs/Common/Octree.inl: In instantiation of ‘void SEACAVE::TOctree<ITEMARR_TYPE, TYPE, DIMS, DATA_TYPE>::Collect(SEACAVE::TOctree<ITEMARR_TYPE, TYPE, DIMS, DATA_TYPE>::IDXARR_TYPE&, const POINT_TYPE&, TYPE) const [with ITEMARR_TYPE = SEACAVE::cList<SEACAVE::TPoint3<float>, const SEACAVE::TPoint3<float>&, 0, 8192>; TYPE = float; int DIMS = 3; DATA_TYPE = unsigned int; SEACAVE::TOctree<ITEMARR_TYPE, TYPE, DIMS, DATA_TYPE>::IDXARR_TYPE = SEACAVE::cList<long unsigned int, long unsigned int, 0, 1024, long unsigned int>; typename ITEMARR_TYPE::IDX = long unsigned int; SEACAVE::TOctree<ITEMARR_TYPE, TYPE, DIMS, DATA_TYPE>::POINT_TYPE = Eigen::Matrix<float, 3, 1>]’:
/media/zcc/data/Project/Work/DenseReconstruction/openMVS/libs/MVS/SceneTexture.cpp:878:56:   required from here
/media/zcc/data/Project/Work/DenseReconstruction/openMVS/libs/Common/Octree.inl:380:2: error: no matching function for call to ‘SEACAVE::TOctree<SEACAVE::cList<SEACAVE::TPoint3<float>, const SEACAVE::TPoint3<float>&, 0, 8192>, float, 3>::_Collect(const SEACAVE::TOctree<SEACAVE::cList<SEACAVE::TPoint3<float>, const SEACAVE::TPoint3<float>&, 0, 8192>, float, 3>::CELL_TYPE&, SEACAVE::TOctree<SEACAVE::cList<SEACAVE::TPoint3<float>, const SEACAVE::TPoint3<float>&, 0, 8192>, float, 3>::AABB_TYPE, SEACAVE::TOctree<SEACAVE::cList<SEACAVE::TPoint3<float>, const SEACAVE::TPoint3<float>&, 0, 8192>, float, 3>::IndexInserter) const’
  380 |  _Collect(m_root, AABB_TYPE(center, radius), IndexInserter(indices));
      |  ^~~~~~~~
/media/zcc/data/Project/Work/DenseReconstruction/openMVS/libs/Common/Octree.inl:294:6: note: candidate: ‘void SEACAVE::TOctree<ITEMARR_TYPE, TYPE, DIMS, DATA_TYPE>::_Collect(const SEACAVE::TOctree<ITEMARR_TYPE, TYPE, DIMS, DATA_TYPE>::CELL_TYPE&, const AABB_TYPE&, INSERTER&) const [with INSERTER = SEACAVE::TOctree<SEACAVE::cList<SEACAVE::TPoint3<float>, const SEACAVE::TPoint3<float>&, 0, 8192>, float, 3>::IndexInserter; ITEMARR_TYPE = SEACAVE::cList<SEACAVE::TPoint3<float>, const SEACAVE::TPoint3<float>&, 0, 8192>; TYPE = float; int DIMS = 3; DATA_TYPE = unsigned int; SEACAVE::TOctree<ITEMARR_TYPE, TYPE, DIMS, DATA_TYPE>::AABB_TYPE = SEACAVE::TAABB<float, 3>]’ <near match>
  294 | void TOctree<ITEMARR_TYPE,TYPE,DIMS,DATA_TYPE>::_Collect(const CELL_TYPE& cell, const AABB_TYPE& aabb, INSERTER& inserter) const
      |      ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/media/zcc/data/Project/Work/DenseReconstruction/openMVS/libs/Common/Octree.inl:294:6: note:   conversion of argument 3 would be ill-formed:
/media/zcc/data/Project/Work/DenseReconstruction/openMVS/libs/Common/Octree.inl:380:2: error: cannot bind non-const lvalue reference of type ‘SEACAVE::TOctree<SEACAVE::cList<SEACAVE::TPoint3<float>, const SEACAVE::TPoint3<float>&, 0, 8192>, float, 3>::IndexInserter&’ to an rvalue of type ‘SEACAVE::TOctree<SEACAVE::cList<SEACAVE::TPoint3<float>, const SEACAVE::TPoint3<float>&, 0, 8192>, float, 3>::IndexInserter’
  380 |  _Collect(m_root, AABB_TYPE(center, radius), IndexInserter(indices));
      |  ^~~~~~~~
In file included from /media/zcc/data/Project/Work/DenseReconstruction/openMVS/libs/Common/Types.h:2796,
                 from /media/zcc/data/Project/Work/DenseReconstruction/openMVS/libs/Common/Common.h:176,
                 from /media/zcc/data/Project/Work/DenseReconstruction/openMVS/libs/MVS/Common.h:42,
                 from /media/zcc/data/Project/Work/DenseReconstruction/openMVS/build/Debug/libs/MVS/CMakeFiles/MVS.dir/cmake_pch.hxx:5,
                 from <command-line>:
/media/zcc/data/Project/Work/DenseReconstruction/openMVS/libs/Common/Octree.h:184:7: note: candidate: ‘template<class INSERTER, class COLLECTOR> void SEACAVE::TOctree<ITEMARR_TYPE, TYPE, DIMS, DATA_TYPE>::_Collect(const SEACAVE::TOctree<ITEMARR_TYPE, TYPE, DIMS, DATA_TYPE>::CELL_TYPE&, TYPE, const COLLECTOR&, INSERTER&) const [with INSERTER = INSERTER; COLLECTOR = COLLECTOR; ITEMARR_TYPE = SEACAVE::cList<SEACAVE::TPoint3<float>, const SEACAVE::TPoint3<float>&, 0, 8192>; TYPE = float; int DIMS = 3; DATA_TYPE = unsigned int]’
  184 |  void _Collect(const CELL_TYPE&, TYPE, const COLLECTOR&, INSERTER&) const;
      |       ^~~~~~~~
/media/zcc/data/Project/Work/DenseReconstruction/openMVS/libs/Common/Octree.h:184:7: note:   template argument deduction/substitution failed:
In file included from /media/zcc/data/Project/Work/DenseReconstruction/openMVS/libs/Common/Octree.h:233,
                 from /media/zcc/data/Project/Work/DenseReconstruction/openMVS/libs/Common/Types.h:2796,
                 from /media/zcc/data/Project/Work/DenseReconstruction/openMVS/libs/Common/Common.h:176,
                 from /media/zcc/data/Project/Work/DenseReconstruction/openMVS/libs/MVS/Common.h:42,
                 from /media/zcc/data/Project/Work/DenseReconstruction/openMVS/build/Debug/libs/MVS/CMakeFiles/MVS.dir/cmake_pch.hxx:5,
                 from <command-line>:
/media/zcc/data/Project/Work/DenseReconstruction/openMVS/libs/Common/Octree.inl:380:2: note:   candidate expects 4 arguments, 3 provided
  380 |  _Collect(m_root, AABB_TYPE(center, radius), IndexInserter(indices));
      |  ^~~~~~~~
make[2]: *** [libs/MVS/CMakeFiles/MVS.dir/build.make:289: libs/MVS/CMakeFiles/MVS.dir/SceneTexture.cpp.o] Error 1
make[1]: *** [CMakeFiles/Makefile2:493: libs/MVS/CMakeFiles/MVS.dir/all] Error 2
make: *** [Makefile:130: all] Error 2

CanCanZeng avatar Jun 19 '22 07:06 CanCanZeng

this looks like some crazy GCC problems with lvalues (I use Windows); try replacing:

 _Collect(m_root, AABB_TYPE(center, radius), IndexInserter(indices));

with

IndexInserter inserter(indices);
 _Collect(m_root, AABB_TYPE(center, radius), inserter);

cdcseacave avatar Jun 19 '22 07:06 cdcseacave

Wow, that fix the compilation problem!

CanCanZeng avatar Jun 19 '22 08:06 CanCanZeng

I got the best texture result ever on my data by using the covisibility information of dense point cloud! I will post the code after I clean it up.

CanCanZeng avatar Jun 19 '22 14:06 CanCanZeng

Hi @cdcseacave , can you try my code on windows? I find that my code runs well on ubuntu16.04 with gcc5, and the debug version also runs well on ubuntu20.04 with gcc9, but the release version will crash. I guess it also ascribes to the optimization of GCC9, can you help me check it?

CanCanZeng avatar Jun 20 '22 11:06 CanCanZeng

Sorry, I forgot one thing: to use covisibility of dense point cloud, there should be dense point cloud and views info in .mvs file, but the default scene_mesh.mvs file does not have these infos, so there are two ways to use my code.

  1. comment out this line https://github.com/cdcseacave/openMVS/blob/f34a258862af967456b7c4578aa2d8821875eb25/libs/MVS/SceneReconstruct.cpp#L876 to preserve point cloud info in output scene_mesh.mvs file
  2. use scene.mvs but provide mesh file from command line, like below: TextureMesh -i scene.mvs --mesh-file scene_mesh.ply

CanCanZeng avatar Jun 20 '22 12:06 CanCanZeng

@CanCanZeng Hello, I don't understand that why the point cloud visibility info would help to detect outliers. Cause it seems there is not so mush difference between mesh visibility and point cloud visibility. Could you explain that in detail? Thank you!

Yzhbuaa avatar Feb 21 '23 13:02 Yzhbuaa

The visibility of the point cloud is obtained in the MVS stage, and it is impossible for occluders to participate in the generation of the point cloud. The visibility of the mesh is obtained by projecting the mesh onto the image, so occluders cannot be excluded.

CanCanZeng avatar Feb 22 '23 10:02 CanCanZeng