Population
Image processing library in C++
Grain partition

You do as in this page CMake (cross-platform) but this time you quote the BUILD_ARTICLE before to generate it with cmake_gui.

In this article, we explore three technics of grain segmentations.

C++ code

#include"Population.h"
using namespace pop;
template<int DIM>
MatN<DIM,UI32> poreDecompositionMixedMethod(MatN<DIM,UI8> m,F32 alpha=1 ){
//###SEGMENTATION###
int value;
MatN<DIM,UI8> m_grain_binary = Processing::thresholdOtsuMethod(m_filter,value);
//###GRAIN PARTITION###
//euclidean distunce map
MatN<DIM,UI8> m_pore = m_grain_binary.opposite();
//normalization in [0,1]
m_dist =Processing::greylevelRange(m_dist,0,1);
MatN<DIM,F32> m_filter_normalization =Processing::greylevelRange(MatN<DIM,F32>(m_filter),0,1);
//linear combination
m_dist = m_dist + alpha*m_filter_normalization;
//convert float to integer needed by the watershed and dynamic algorithms
MatN<DIM,UI8> m_dist_int = Processing::greylevelRange(m_dist,0,255);
//set at 0 the points outside the grain space
while(it.next()){
if(m_grain_binary(it.x())==0)
m_dist_int(it.x())=0;
}
//opposite to have the watershed surface on the grain boundaries
m_dist_int = m_dist_int.opposite();
//filter the over-seeding with vertical filter
m_dist_int = Processing::dynamic(m_dist_int,20,0);
//regional minima with with the norm-0 (the norm is here important with norm-1 ->over-partition)
//watershed ytansformation with the seeds as minima of the distunce function, with the topographic surface as the distunce function, and the growing region is restricted by a mask function as the granular phase
MatN<DIM,UI32> m_grain_labelled = Processing::watershed(m_seed,m_dist_int,m_grain_binary,0);
return m_grain_labelled;
}
template<int DIM>
MatN<DIM,UI32> poreDecompositionGrainBoundarySharpVariationGreyLevel(MatN<DIM,UI8> m ){
//GRAIN PARTITION OF THE BINARY IMAGE
m = m.opposite();
//horizontal filter
filter = Processing::dynamic(filter,12);
MatN<DIM,UI32> markers_inside_grains = Processing::minimaRegional(filter,0);
MatN<DIM,UI32> marker_outside_grains = Processing::watershedBoundary(markers_inside_grains,filter,0);
marker_outside_grains = Processing::threshold(marker_outside_grains,0,0);
MatN<DIM,UI32> markers = Processing::labelMerge(marker_outside_grains,markers_inside_grains);
MatN<DIM,UI32> watershed =Processing::watershed(markers,gradient);
watershed = watershed -1;
return watershed;
}
template<int DIM>
MatN<DIM,UI32> poreDecompositionGrainContactNarrowContact(MatN<DIM,UI8> m ){
//SEGMENTATION
int value;
MatN<DIM,UI8> m_grain_binary = Processing::thresholdOtsuMethod(m_filter,value);
//GRAIN PARTITION OF THE BINARY IMAGE
//create the distunce function
MatN<DIM,UI8> m_pore = m_grain_binary.opposite();
m_dist = m_dist.opposite();
//dynamic filter to avoid over-partition
m_dist = Processing::dynamic(m_dist,20,0);
//regional minima with with the norm-0 (the norm is here important with norm-1 ->over-partition)
//watershed ytansformation with the seeds as minima of the distunce function, with the topographic surface as the distunce function, and the growing region is restricted by a mask function as the granular phase
MatN<DIM,UI32> m_grain_labelled = Processing::watershed(m_seed,m_dist,m_grain_binary,0);
return m_grain_labelled;
}
int main(){
m.load(POP_PROJECT_SOURCE_DIR+std::string("/image/sand.pgm"));
m = m(Vec3I32(0,0,0),Vec3I32(128,128,128));
//## select a method by uncommented
//## FIRST METHOD
//Mat3UI32 grain3d = poreDecompositionGrainBoundarySharpVariationGreyLevel(m);
//## SECOND METHOD
//Mat3UI32 grain3d = poreDecompositionGrainContactNarrowContact(m);
//## THIRD METHOD
Mat3UI32 grain3d = poreDecompositionMixedMethod(m,2);
//Visualization::labelForegroundBoundary(grain3d,m,1).display();
//grain_color.display();
//3d
Scene3d scene;
Visualization::marchingCubeSmooth(scene,grain3d,5,grain_color);//add the marching cube of the grain to the scene
Visualization::lineCube(scene,grain_color);//add the border red lines to the scene to the scene
scene.display();
return 1;
}