diff --git a/BGL/include/CGAL/boost/parameter.h b/BGL/include/CGAL/boost/parameter.h
index 9c28ac96b7a2..14ab09b04748 100644
--- a/BGL/include/CGAL/boost/parameter.h
+++ b/BGL/include/CGAL/boost/parameter.h
@@ -110,6 +110,7 @@ BOOST_PARAMETER_NAME( (pointer_to_stop_atomic_boolean, tag ) pointer_to_stop_ato
BOOST_PARAMETER_NAME( (function, tag ) function_)
BOOST_PARAMETER_NAME( (bounding_object, tag ) bounding_object_)
BOOST_PARAMETER_NAME( (relative_error_bound, tag ) relative_error_bound_)
+BOOST_PARAMETER_NAME( (weights, tag) weights_)
BOOST_PARAMETER_NAME( (p_rng, tag ) p_rng_)
BOOST_PARAMETER_NAME( (null_subdomain_index, tag ) null_subdomain_index_)
BOOST_PARAMETER_NAME( (construct_surface_patch_index, tag ) construct_surface_patch_index_)
diff --git a/CGAL_ImageIO/include/CGAL/Image_3.h b/CGAL_ImageIO/include/CGAL/Image_3.h
index 8ede8eefec57..e0ecd7ce77b9 100644
--- a/CGAL_ImageIO/include/CGAL/Image_3.h
+++ b/CGAL_ImageIO/include/CGAL/Image_3.h
@@ -102,7 +102,7 @@ class CGAL_IMAGEIO_EXPORT Image_3
// std::cerr << "Image_3::copy_constructor\n";
}
- Image_3(_image* im, Own own_the_data = OWN_THE_DATA)
+ explicit Image_3(_image* im, Own own_the_data = OWN_THE_DATA)
{
private_read(im, own_the_data);
}
@@ -146,9 +146,9 @@ class CGAL_IMAGEIO_EXPORT Image_3
double vy() const { return image_ptr->vy; }
double vz() const { return image_ptr->vz; }
- double tx() const { return image_ptr->tx; }
- double ty() const { return image_ptr->ty; }
- double tz() const { return image_ptr->tz; }
+ float tx() const { return image_ptr->tx; }
+ float ty() const { return image_ptr->ty; }
+ float tz() const { return image_ptr->tz; }
float value(const std::size_t i,
const std::size_t j,
@@ -157,6 +157,11 @@ class CGAL_IMAGEIO_EXPORT Image_3
return ::evaluate(image(),i,j,k);
}
+ bool is_valid() const
+ {
+ return image_ptr.get() != nullptr;
+ }
+
public:
bool read(const char* file)
diff --git a/Documentation/doc/biblio/cgal_manual.bib b/Documentation/doc/biblio/cgal_manual.bib
index 9ac6cdc2a186..1c49fe6ffd27 100644
--- a/Documentation/doc/biblio/cgal_manual.bib
+++ b/Documentation/doc/biblio/cgal_manual.bib
@@ -3256,6 +3256,12 @@ @article{cgal:Wwshap-eepec-20
publisher = {ACM}
}
+@article{stalling1998weighted,
+ title={Weighted labels for 3D image segmentation},
+ author={Stalling, Detlev and Z{\"o}ckler, Malte and Sander, Oliver and Hege, Hans-Christian},
+ year={1998}
+}
+
% ----------------------------------------------------------------------------
% END OF BIBFILE
% ----------------------------------------------------------------------------
diff --git a/Installation/cmake/modules/CGAL_ITK_support.cmake b/Installation/cmake/modules/CGAL_ITK_support.cmake
new file mode 100644
index 000000000000..61ed67e2f2ba
--- /dev/null
+++ b/Installation/cmake/modules/CGAL_ITK_support.cmake
@@ -0,0 +1,7 @@
+if(ITK_FOUND AND NOT TARGET CGAL::ITK_support)
+ add_library(CGAL::ITK_support INTERFACE IMPORTED)
+ set_target_properties(CGAL::ITK_support PROPERTIES
+ INTERFACE_COMPILE_DEFINITIONS "CGAL_USE_ITK"
+ INTERFACE_INCLUDE_DIRECTORIES "${ITK_INCLUDE_DIRS}"
+ INTERFACE_LINK_LIBRARIES "${ITK_LIBRARIES}")
+endif()
diff --git a/Installation/cmake/modules/list_of_whitelisted_headers.cmake b/Installation/cmake/modules/list_of_whitelisted_headers.cmake
index 5aa71d9560b5..9cbef5d8a577 100644
--- a/Installation/cmake/modules/list_of_whitelisted_headers.cmake
+++ b/Installation/cmake/modules/list_of_whitelisted_headers.cmake
@@ -41,7 +41,7 @@ set(list_of_whitelisted_headers_txt [=[
CGAL/IO/Triangulation_geomview_ostream_3.h
CGAL/IO/Triangulation_geomview_ostream_2.h
CGAL/IO/Polyhedron_geomview_ostream.h
-
+ CGAL/Mesh_3/generate_label_weights.h
]=])
separate_arguments(list_of_whitelisted_headers UNIX_COMMAND ${list_of_whitelisted_headers_txt})
diff --git a/Mesh_3/doc/Mesh_3/CGAL/Labeled_mesh_domain_3.h b/Mesh_3/doc/Mesh_3/CGAL/Labeled_mesh_domain_3.h
index 1dbef3e1efb8..59808d448996 100644
--- a/Mesh_3/doc/Mesh_3/CGAL/Labeled_mesh_domain_3.h
+++ b/Mesh_3/doc/Mesh_3/CGAL/Labeled_mesh_domain_3.h
@@ -233,6 +233,12 @@ The parameters are optional unless otherwise specified.
`parameters::image` (mandatory) the input 3D image. Must
be a `CGAL::Image_3` object.
+ `parameters::weights` an input 3D image that provides
+weights associated to each voxel (the word type is `unsigned char`,
+and the voxels values are integers between 0 and 255).
+The weights image can be generated with `CGAL::Mesh_3::generate_label_weights()`.
+Its dimensions must be the same as the dimensions of `parameters::image`.
+
`parameter::value_outside` the value attached to voxels
outside of the domain to be meshed. Its default value is `0`.
@@ -246,6 +252,11 @@ From the example (\ref Mesh_3/mesh_3D_image.cpp):
\snippet Mesh_3/mesh_3D_image.cpp Domain creation
+From the example (\ref Mesh_3/mesh_3D_weighted_image.cpp),
+where the labeled image is used with a precomputed 3D image of weights :
+
+\snippet Mesh_3/mesh_3D_weighted_image.cpp Domain creation
+
*/
template
static
diff --git a/Mesh_3/doc/Mesh_3/Doxyfile.in b/Mesh_3/doc/Mesh_3/Doxyfile.in
index 5c8e03ccafd0..62ea73403e60 100644
--- a/Mesh_3/doc/Mesh_3/Doxyfile.in
+++ b/Mesh_3/doc/Mesh_3/Doxyfile.in
@@ -5,7 +5,8 @@ ALIASES += "cgalDescribePolylineType=A polyline is defined as a sequence of poin
INPUT += \
${CGAL_PACKAGE_INCLUDE_DIR}/CGAL/Polyhedral_complex_mesh_domain_3.h \
- ${CGAL_PACKAGE_INCLUDE_DIR}/CGAL/Mesh_domain_with_polyline_features_3.h
+ ${CGAL_PACKAGE_INCLUDE_DIR}/CGAL/Mesh_domain_with_polyline_features_3.h \
+ ${CGAL_PACKAGE_INCLUDE_DIR}/CGAL/Mesh_3/generate_label_weights.h
PROJECT_NAME = "CGAL ${CGAL_DOC_VERSION} - 3D Mesh Generation"
HTML_EXTRA_FILES = ${CGAL_PACKAGE_DOC_DIR}/fig/implicit_domain_3.jpg \
diff --git a/Mesh_3/doc/Mesh_3/Mesh_3.txt b/Mesh_3/doc/Mesh_3/Mesh_3.txt
index a8b806d783c7..08e82efd875c 100644
--- a/Mesh_3/doc/Mesh_3/Mesh_3.txt
+++ b/Mesh_3/doc/Mesh_3/Mesh_3.txt
@@ -706,6 +706,24 @@ The resulting mesh is shown in \cgalFigureRef{figureliver_3d_image_mesh}.
Cut view of a 3D mesh produced from a segmented liver image. Code from subsection \ref Mesh_3_subsection_examples_3d_image generates this file.
\cgalFigureEnd
+\subsubsection Mesh_3DomainsFrom3DImagesWithWeights Domains From Segmented 3D Images, with Weights
+
+When a segmented image is given as input, the generated mesh surface sometimes sticks too closely
+to the voxels surface, causing an aliasing effect.
+A solution to generate a smooth and accurate output surface was described by Stalling et al in
+\cgalCite{stalling1998weighted}. It consists in generating a second input image, made
+of integer coefficients called *weights*, and use those weights to define smoother domain boundaries.
+The 3D image of weights can be generated using `CGAL::Mesh_3::generate_weights()` as shown in
+the following example.
+
+\cgalExample{Mesh_3/mesh_3D_weighted_image.cpp}
+
+\cgalFigureBegin{figure_weightedImage, weighted_images.jpg}
+Surface of the output mesh generated with a very small `facet_distance`
+without the weights (left, 25563 vertices) and with the weights (right, 19936 vertices).
+\cgalFigureEnd
+
+
\subsubsection Mesh_3DomainsFrom3DImagesWithCustomInitialization Domains From 3D Images, with a Custom Initialization
The example \ref Mesh_3/mesh_3D_image_with_custom_initialization.cpp is a modification
diff --git a/Mesh_3/doc/Mesh_3/PackageDescription.txt b/Mesh_3/doc/Mesh_3/PackageDescription.txt
index f7a09d8f4738..240ca4bf2941 100644
--- a/Mesh_3/doc/Mesh_3/PackageDescription.txt
+++ b/Mesh_3/doc/Mesh_3/PackageDescription.txt
@@ -115,6 +115,7 @@ and their associated classes:
- `CGAL::lloyd_optimize_mesh_3()`
- `CGAL::odt_optimize_mesh_3()`
- `CGAL::facets_in_complex_3_to_triangle_mesh()`
+- `CGAL::Mesh_3::generate_label_weights()`
\cgalCRPSection{CGAL::parameters Functions}
diff --git a/Mesh_3/doc/Mesh_3/examples.txt b/Mesh_3/doc/Mesh_3/examples.txt
index c1c5e4de1a81..aa44908504c1 100644
--- a/Mesh_3/doc/Mesh_3/examples.txt
+++ b/Mesh_3/doc/Mesh_3/examples.txt
@@ -4,6 +4,7 @@
\example Mesh_3/mesh_3D_gray_image_with_custom_initialization.cpp
\example Mesh_3/mesh_3D_image_with_features.cpp
\example Mesh_3/mesh_3D_image_with_custom_initialization.cpp
+\example Mesh_3/mesh_3D_weighted_image.cpp
\example Mesh_3/random_labeled_image.h
\example CGAL/Mesh_3/initialize_triangulation_from_gray_image.h
\example CGAL/Mesh_3/initialize_triangulation_from_labeled_image.h
diff --git a/Mesh_3/doc/Mesh_3/fig/weighted_images.jpg b/Mesh_3/doc/Mesh_3/fig/weighted_images.jpg
new file mode 100644
index 000000000000..39a3dca2f9cf
Binary files /dev/null and b/Mesh_3/doc/Mesh_3/fig/weighted_images.jpg differ
diff --git a/Mesh_3/examples/Mesh_3/CMakeLists.txt b/Mesh_3/examples/Mesh_3/CMakeLists.txt
index 3c1602a2e267..c1230c541dee 100644
--- a/Mesh_3/examples/Mesh_3/CMakeLists.txt
+++ b/Mesh_3/examples/Mesh_3/CMakeLists.txt
@@ -161,10 +161,21 @@ if(TARGET CGAL::CGAL_ImageIO)
create_single_source_cgal_program("mesh_3D_image_variable_size.cpp")
target_link_libraries(mesh_3D_image_variable_size
PUBLIC CGAL::Eigen3_support)
+
+ find_package(ITK QUIET COMPONENTS ITKCommon ITKThresholding ITKSmoothing ITKImageIntensity)
+ if(ITK_FOUND)
+ include(CGAL_ITK_support)
+ create_single_source_cgal_program("mesh_3D_weighted_image.cpp")
+ target_link_libraries(mesh_3D_weighted_image
+ PUBLIC CGAL::Eigen3_support CGAL::ITK_support)
+ else(ITK_FOUND)
+ message(STATUS "NOTICE: The examples that need ITK will not be compiled.")
+ endif(ITK_FOUND)
+
else()
message(
STATUS
- "NOTICE: The examples mesh_3D_image.cpp, mesh_3D_image_variable_size.cpp, mesh_optimization_example.cpp and mesh_optimization_lloyd_example.cpp need CGAL_ImageIO to be configured with ZLIB support, and will not be compiled."
+ "NOTICE: The examples mesh_3D_image.cpp, mesh_3D_weighted_image.cpp, mesh_3D_image_variable_size.cpp, mesh_optimization_example.cpp and mesh_optimization_lloyd_example.cpp need CGAL_ImageIO to be configured with ZLIB support, and will not be compiled."
)
endif()
@@ -179,6 +190,7 @@ if(CGAL_ACTIVATE_CONCURRENT_MESH_3 AND TARGET CGAL::TBB_support)
foreach(
target
mesh_3D_image
+ mesh_3D_weighted_image
mesh_3D_image_variable_size
mesh_3D_image_with_custom_initialization
mesh_3D_gray_image_with_custom_initialization
diff --git a/Mesh_3/examples/Mesh_3/mesh_3D_weighted_image.cpp b/Mesh_3/examples/Mesh_3/mesh_3D_weighted_image.cpp
new file mode 100644
index 000000000000..0dc3e8e8be2e
--- /dev/null
+++ b/Mesh_3/examples/Mesh_3/mesh_3D_weighted_image.cpp
@@ -0,0 +1,67 @@
+#include
+
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+// Domain
+using K = CGAL::Exact_predicates_inexact_constructions_kernel;
+using Mesh_domain = CGAL::Labeled_mesh_domain_3;
+
+// Triangulation
+using Tr = CGAL::Mesh_triangulation_3::type;
+using C3t3 = CGAL::Mesh_complex_3_in_triangulation_3;
+
+// Criteria
+using Mesh_criteria = CGAL::Mesh_criteria_3
;
+
+// To avoid verbose function and named parameters call
+using namespace CGAL::parameters;
+
+int main(int argc, char* argv[])
+{
+ /// [Loads image]
+ const char* fname = (argc > 1) ? argv[1] : "data/liver.inr.gz";
+ CGAL::Image_3 image;
+ if(!image.read(fname)){
+ std::cerr << "Error: Cannot read file " << fname << std::endl;
+ return EXIT_FAILURE;
+ }
+ /// [Loads image]
+
+ /// [Domain creation]
+ const float sigma = 10.f;
+ CGAL::Image_3 img_weights =
+ CGAL::Mesh_3::generate_label_weights(image, sigma);
+
+ Mesh_domain domain
+ = Mesh_domain::create_labeled_image_mesh_domain(image,
+ weights = img_weights,
+ relative_error_bound = 1e-6);
+ /// [Domain creation]
+
+ // Mesh criteria
+ Mesh_criteria criteria(facet_angle=30, facet_size=6, facet_distance=0.5,
+ cell_radius_edge_ratio=3, cell_size=8);
+
+ /// [Meshing]
+ C3t3 c3t3 = CGAL::make_mesh_3(domain, criteria);
+ /// [Meshing]
+
+ // Output
+ std::ofstream medit_file("out.mesh");
+ c3t3.output_to_medit(medit_file);
+ std::ofstream bin_file("out.binary.cgal", std::ios_base::binary);
+ CGAL::IO::save_binary_file(bin_file, c3t3);
+
+ return 0;
+}
diff --git a/Mesh_3/include/CGAL/Labeled_mesh_domain_3.h b/Mesh_3/include/CGAL/Labeled_mesh_domain_3.h
index e2b1af1e8994..1e18a4dc124c 100644
--- a/Mesh_3/include/CGAL/Labeled_mesh_domain_3.h
+++ b/Mesh_3/include/CGAL/Labeled_mesh_domain_3.h
@@ -39,6 +39,7 @@
// support for `CGAL::Image_3`
#include
#include
+#include
// support for implicit functions
#include
@@ -418,6 +419,7 @@ class Labeled_mesh_domain_3 :
(optional
(relative_error_bound_, (const FT&),
FT(1e-3))
+ (weights_, (const CGAL::Image_3&), CGAL::Image_3())
(value_outside_, *, 0)
(p_rng_, (CGAL::Random*), (CGAL::Random*)(0))
(image_values_to_subdomain_indices_, *,
@@ -429,18 +431,37 @@ class Labeled_mesh_domain_3 :
)
{
namespace p = CGAL::parameters;
- return Labeled_mesh_domain_3
- (create_labeled_image_wrapper
+ if (weights_.is_valid())
+ {
+ return Labeled_mesh_domain_3
+ (create_weighted_labeled_image_wrapper
(image_,
+ weights_,
image_values_to_subdomain_indices_,
value_outside_),
- Mesh_3::internal::compute_bounding_box(image_),
- p::relative_error_bound = relative_error_bound_,
- p::p_rng = p_rng_,
- p::null_subdomain_index =
- create_null_subdomain_index(null_subdomain_index_),
- p::construct_surface_patch_index =
- create_construct_surface_patch_index(construct_surface_patch_index_));
+ Mesh_3::internal::compute_bounding_box(image_),
+ p::relative_error_bound = relative_error_bound_,
+ p::p_rng = p_rng_,
+ p::null_subdomain_index =
+ create_null_subdomain_index(null_subdomain_index_),
+ p::construct_surface_patch_index =
+ create_construct_surface_patch_index(construct_surface_patch_index_));
+ }
+ else
+ {
+ return Labeled_mesh_domain_3
+ (create_labeled_image_wrapper
+ (image_,
+ image_values_to_subdomain_indices_,
+ value_outside_),
+ Mesh_3::internal::compute_bounding_box(image_),
+ p::relative_error_bound = relative_error_bound_,
+ p::p_rng = p_rng_,
+ p::null_subdomain_index =
+ create_null_subdomain_index(null_subdomain_index_),
+ p::construct_surface_patch_index =
+ create_construct_surface_patch_index(construct_surface_patch_index_));
+ }
}
BOOST_PARAMETER_MEMBER_FUNCTION(
@@ -866,6 +887,33 @@ class Labeled_mesh_domain_3 :
transform_fct(value_outside));
}
+ template
+ static
+ Function
+ create_weighted_labeled_image_wrapper_with_know_word_type
+ (const CGAL::Image_3& image,
+ const CGAL::Image_3& weights,
+ const Functor& image_values_to_subdomain_indices,
+ const FT& value_outside)
+ {
+ using Mesh_3::internal::Create_labeled_image_values_to_subdomain_indices;
+ typedef Create_labeled_image_values_to_subdomain_indices C_i_v_t_s_i;
+ typedef typename C_i_v_t_s_i::type Image_values_to_subdomain_indices;
+ Image_values_to_subdomain_indices transform_fct =
+ C_i_v_t_s_i()(image_values_to_subdomain_indices);
+
+ typedef Mesh_3::Image_plus_weights_to_labeled_function_wrapper<
+ Image_word_type,
+ int, //interpolation type
+ unsigned char, // Weights_type,
+ Subdomain_index> Wrapper;
+ return Wrapper(image,
+ weights,
+ transform_fct,
+ transform_fct(value_outside));
+ }
+
template
static
Function
@@ -885,6 +933,27 @@ class Labeled_mesh_domain_3 :
return Function();
}
+ template
+ static
+ Function
+ create_weighted_labeled_image_wrapper(const CGAL::Image_3& image,
+ const CGAL::Image_3& weights,
+ const Functor& image_values_to_subdomain_indices,
+ const FT& value_outside)
+ {
+ CGAL_IMAGE_IO_CASE(image.image(),
+ return create_weighted_labeled_image_wrapper_with_know_word_type
+ (image,
+ weights,
+ image_values_to_subdomain_indices,
+ value_outside);
+ );
+ CGAL_error_msg("This place should never be reached, because it would mean "
+ "the image word type is a type that is not handled by "
+ "CGAL_ImageIO.");
+ return Function();
+ }
+
static
Construct_surface_patch_index
create_construct_surface_patch_index(const Null_functor&) {
diff --git a/Mesh_3/include/CGAL/Mesh_3/Image_plus_weights_to_labeled_function_wrapper.h b/Mesh_3/include/CGAL/Mesh_3/Image_plus_weights_to_labeled_function_wrapper.h
new file mode 100644
index 000000000000..e1d54932eb69
--- /dev/null
+++ b/Mesh_3/include/CGAL/Mesh_3/Image_plus_weights_to_labeled_function_wrapper.h
@@ -0,0 +1,141 @@
+// Copyright (c) 2016,2021 GeometryFactory Sarl (France).
+// All rights reserved.
+//
+// This file is part of CGAL (www.cgal.org).
+//
+// $URL$
+// $Id$
+// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-Commercial
+//
+//
+// Author(s) : Laurent Rineau
+//
+
+#ifndef CGAL_MESH_3_IMAGE_PLUS_WEIGHTS_TO_LABELED_FUNCTION_WRAPPER_H
+#define CGAL_MESH_3_IMAGE_PLUS_WEIGHTS_TO_LABELED_FUNCTION_WRAPPER_H
+
+#include
+
+#include
+#include
+
+namespace CGAL {
+
+namespace ImageIO {
+
+template
+class Weighted_indicator_factory
+{
+ const Image_3& image;
+ const Image_3& weights;
+
+public:
+
+ Weighted_indicator_factory(const Image_3& image,
+ const Image_3& weights)
+ : image(image)
+ , weights(weights)
+ {}
+
+ class Indicator
+ {
+ const Weighted_indicator_factory& f;
+ const Image_word_type label;
+ public:
+ Indicator(const Weighted_indicator_factory& f,
+ const Image_word_type label) : f(f), label(label) {}
+
+ double operator()(const Image_word_type& x) const
+ {
+ const std::ptrdiff_t offset = &x - (Image_word_type *)f.image.data();
+ const Weights_type w = (std::max)(
+ Weights_type(128), ((Weights_type *)f.weights.data())[offset]);
+ return (x == label) ? w : (255 - w);
+ }
+ }; // end nested class Indicator
+
+ Indicator indicator(const Image_word_type label) const
+ {
+ return Indicator(*this, label);
+ }
+}; // end template class ImageIO::Weighted_indicator_factory
+
+} // end namespace CGAL::ImageIO
+
+namespace Mesh_3 {
+
+
+/**
+ * @class Image_plus_weights_to_labeled_function_wrapper
+ *
+ * Wraps a pair of images into a labeled function which takes his values into
+ * N. Uses weighted trilinear interpolation.
+ */
+template
+class Image_plus_weights_to_labeled_function_wrapper
+{
+public:
+ typedef std::function
+ Image_values_to_labels;
+
+ // Types
+ typedef Return_type return_type;
+ typedef Image_word_type word_type;
+ typedef CGAL::Image_3 Image_;
+
+ /// Constructor
+ Image_plus_weights_to_labeled_function_wrapper
+ (const Image_& image,
+ const Image_& weights_image,
+ Image_values_to_labels transform = Identity(),
+ const Interpolation_type value_outside = Interpolation_type())
+ : r_im_(image)
+ , r_weights_im_(weights_image)
+ , transform(transform)
+ , value_outside(value_outside)
+ , indicator_factory(image, weights_image)
+ {
+ }
+
+ // Default copy constructor and assignment operator are ok
+
+ /// Destructor
+ ~Image_plus_weights_to_labeled_function_wrapper() {}
+
+ /**
+ * Returns an int corresponding to the label at point \c p
+ * @param p the input point
+ * @return the label at point \c p
+ */
+ template
+ return_type operator()(const Point_3& p) const
+ {
+ return static_cast(transform(
+ r_im_.template labellized_trilinear_interpolation(
+ CGAL::to_double(p.x()),
+ CGAL::to_double(p.y()),
+ CGAL::to_double(p.z()),
+ value_outside,
+ indicator_factory)));
+ }
+
+private:
+ /// Labeled image to wrap
+ const Image_& r_im_;
+ const Image_& r_weights_im_;
+ const Image_values_to_labels transform;
+ const Interpolation_type value_outside;
+ CGAL::ImageIO::Weighted_indicator_factory indicator_factory;
+}; // end class Image_plus_weights_to_labeled_function_wrapper
+
+
+} // end namespace Mesh_3
+
+} // end namespace CGAL
+
+#endif // CGAL_MESH_3_IMAGE_PLUS_WEIGHTS_TO_LABELED_FUNCTION_WRAPPER_H
diff --git a/Mesh_3/include/CGAL/Mesh_3/generate_label_weights.h b/Mesh_3/include/CGAL/Mesh_3/generate_label_weights.h
new file mode 100644
index 000000000000..6f3d9d77babf
--- /dev/null
+++ b/Mesh_3/include/CGAL/Mesh_3/generate_label_weights.h
@@ -0,0 +1,259 @@
+// Copyright (c) 2021 GeometryFactory
+// All rights reserved.
+//
+// This file is part of CGAL (www.cgal.org).
+//
+// $URL$
+// $Id$
+// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-Commercial
+//
+//
+// Author(s) : Laurent Rineau, Jane Tournois
+
+#ifndef CGAL_MESH_3_GENERATE_LABEL_WEIGHTS_H
+#define CGAL_MESH_3_GENERATE_LABEL_WEIGHTS_H
+
+#include
+
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+
+namespace CGAL {
+namespace Mesh_3 {
+namespace internal {
+
+template
+void convert_image_3_to_itk(const CGAL::Image_3& image,
+ itk::Image* const itk_img,
+ LabelsSet& labels)
+{
+ const double spacing[3] = {image.vx(), image.vy(), image.vz()};
+ itk_img->SetSpacing(spacing);
+
+ const double origin[3] = {image.tx(), image.ty(), image.tz()};
+ itk_img->SetOrigin(origin);
+
+ using ImageType = itk::Image;
+ typename ImageType::IndexType corner = {{0, 0, 0 }};
+ typename ImageType::SizeType size = {{image.xdim(), image.ydim(), image.zdim()}};
+ typename ImageType::RegionType region(corner, size);
+ itk_img->SetRegions(region);
+
+ itk_img->Allocate();
+
+ const Image_word_type* img_begin = static_cast(image.data());
+ std::copy(img_begin, img_begin + image.size(), itk_img->GetBufferPointer());
+
+ labels.insert(img_begin, img_begin + image.size());
+}
+
+#ifdef CGAL_MESH_3_WEIGHTED_IMAGES_DEBUG
+template
+int count_non_white_pixels(const CGAL::Image_3& image)
+{
+ auto diff255 = [&](const Image_word_type p)
+ {
+ return p != 255;
+ };
+ const Image_word_type* img_begin = static_cast(image.data());
+ return std::count_if(img_begin,
+ img_begin + image.size(),
+ diff255);
+}
+
+template
+int count_non_white_pixels(itk::Image* itk_img)
+{
+ auto diff255 = [&](const Image_word_type p)
+ {
+ return p != 255;
+ };
+ auto size = itk_img->GetLargestPossibleRegion().GetSize();
+ return std::count_if(itk_img->GetBufferPointer(),
+ itk_img->GetBufferPointer() + size[0]*size[1]*size[2],
+ diff255);
+}
+#endif //CGAL_MESH_3_WEIGHTED_IMAGES_DEBUG
+
+template
+WORD_KIND get_wordkind()
+{
+ if (std::is_floating_point::value)
+ return WK_FLOAT;
+ else
+ return WK_FIXED;
+/** unknown (uninitialized) */
+// WK_UNKNOWN
+}
+
+template
+SIGN get_sign()
+{
+ if (std::is_signed::value)
+ return SGN_SIGNED;
+ else
+ return SGN_UNSIGNED;
+/** unknown (uninitialized or floating point words) */
+// SGN_UNKNOWN
+}
+
+}//namespace internal
+
+/// @cond INTERNAL
+template
+CGAL::Image_3 generate_label_weights_with_known_word_type(const CGAL::Image_3& image,
+ const float& sigma)
+{
+ typedef unsigned char Weights_type; //from 0 t 255
+ const std::size_t img_size = image.size();
+
+ //create weights image
+ _image* weights
+ = _createImage(image.xdim(), image.ydim(), image.zdim(),
+ 1, //vectorial dimension
+ image.vx(), image.vy(), image.vz(),
+ sizeof(Weights_type), //image word size in bytes
+ internal::get_wordkind(), //image word kind WK_FIXED, WK_FLOAT, WK_UNKNOWN
+ internal::get_sign()); //image word sign
+ Weights_type* weights_ptr = (Weights_type*)(weights->data);
+ std::fill(weights_ptr,
+ weights_ptr + img_size,
+ Weights_type(0));
+ weights->tx = image.tx();
+ weights->ty = image.ty();
+ weights->tz = image.tz();
+
+ //convert image to itkImage
+ using ImageType = itk::Image;
+ using WeightsType = itk::Image;
+ typename ImageType::Pointer itk_img = ImageType::New();
+ std::set labels;
+ internal::convert_image_3_to_itk(image, itk_img.GetPointer(), labels);
+
+ using DuplicatorType = itk::ImageDuplicator;
+ using IndicatorFilter = itk::BinaryThresholdImageFilter;
+ using GaussianFilterType = itk::RecursiveGaussianImageFilter;
+ using MaximumImageFilterType = itk::MaximumImageFilter;
+
+ std::vector indicators(labels.size());
+ typename DuplicatorType::Pointer duplicator = DuplicatorType::New();
+ duplicator->SetInputImage(itk_img);
+ duplicator->Update();
+
+ for (std::size_t id = 0; id < labels.size(); ++id)
+ {
+ if (id > 0)
+ {
+ duplicator->SetInputImage(indicators[id - 1]);
+ duplicator->Update();
+ }
+ indicators[id] = duplicator->GetOutput();
+ }
+
+ int id = 0;
+ typename WeightsType::Pointer blured_max = WeightsType::New();
+ for (Image_word_type label : labels)
+ {
+#ifdef CGAL_MESH_3_WEIGHTED_IMAGES_DEBUG
+ std::cout << "\nLABEL = " << label << std::endl;
+#endif
+
+ //compute "indicator image" for "label"
+ typename IndicatorFilter::Pointer indicator = IndicatorFilter::New();
+ indicator->SetInput(indicators[id]);
+ indicator->SetOutsideValue(0);
+ indicator->SetInsideValue(255);
+ indicator->SetLowerThreshold(label);
+ indicator->SetUpperThreshold(label);
+ indicator->Update();
+
+ //perform gaussian smoothing
+ typename GaussianFilterType::Pointer smoother = GaussianFilterType::New();
+ smoother->SetInput(indicator->GetOutput());
+ smoother->SetSigma(sigma);
+ smoother->Update();
+
+ //take the max of smoothed indicator functions
+ if (id == 0)
+ blured_max = smoother->GetOutput();
+ else
+ {
+ typename MaximumImageFilterType::Pointer maximumImageFilter = MaximumImageFilterType::New();
+ maximumImageFilter->SetInput(0, blured_max);
+ maximumImageFilter->SetInput(1, smoother->GetOutput());
+ maximumImageFilter->Update();
+ blured_max = maximumImageFilter->GetOutput();
+ }
+
+ id++;
+
+#ifdef CGAL_MESH_3_WEIGHTED_IMAGES_DEBUG
+ std::cout << "AFTER MAX (label = " << label << ") : " << std::endl;
+ std::cout << "\tnon zero in max ("
+ << label << ")\t= " << internal::count_non_white_pixels(blured_max.GetPointer()) << std::endl;
+#endif
+ }
+
+ //copy pixels to weights
+ std::copy(blured_max->GetBufferPointer(),
+ blured_max->GetBufferPointer() + img_size,
+ weights_ptr);
+
+ CGAL::Image_3 weights_img(weights);
+
+#ifdef CGAL_MESH_3_WEIGHTED_IMAGES_DEBUG
+ std::cout << "non white in image \t= "
+ << internal::count_non_white_pixels(image) << std::endl;
+ std::cout << "non white in weights \t= "
+ << internal::count_non_white_pixels(weights_img) << std::endl;
+ std::cout << "non white in itkWeights \t= "
+ << internal::count_non_white_pixels(blured_max.GetPointer()) << std::endl;
+ _writeImage(weights, "weights-image.inr.gz");
+#endif
+
+ return weights_img;
+}
+/// @endcond
+
+/*!
+* Free function that generates a `CGAL::Image_3` of weights associated to each
+* voxel of `image`, to make the output mesh surfaces smoother.
+* The weights image is generated using the algorithm described by Stalling et al
+* in \cgalCite{stalling1998weighted}.
+* The [Insight toolkit](https://itk.org/) is needed to compile this function.
+*
+* @param image the input labeled image from which the weights image is computed.
+* Both will then be used to construct a `Labeled_mesh_domain_3`.
+* @param sigma the standard deviation parameter of the internal Gaussian filter
+*
+* @returns a `CGAL::Image_3` of weights used to build a quality `Labeled_mesh_domain_3`,
+* with the same dimensions as `image`
+*/
+
+CGAL::Image_3 generate_label_weights(const CGAL::Image_3& image,
+ const float& sigma)
+{
+ CGAL_IMAGE_IO_CASE(image.image(),
+ return generate_label_weights_with_known_word_type(image, sigma);
+ );
+ CGAL_error_msg("This place should never be reached, because it would mean "
+ "the image word type is a type that is not handled by "
+ "CGAL_ImageIO.");
+ return CGAL::Image_3();
+}
+
+}//namespace Mesh_3
+}//namespace CGAL
+
+#endif // CGAL_MESH_3_GENERATE_LABEL_WEIGHTS_H
diff --git a/Polyhedron/demo/Polyhedron/CMakeLists.txt b/Polyhedron/demo/Polyhedron/CMakeLists.txt
index ade09e4d8369..a5da27ffd13b 100644
--- a/Polyhedron/demo/Polyhedron/CMakeLists.txt
+++ b/Polyhedron/demo/Polyhedron/CMakeLists.txt
@@ -268,6 +268,7 @@ if(CGAL_Qt5_FOUND AND Qt5_FOUND)
endif()
add_item(scene_tetrahedra_item Scene_tetrahedra_item.cpp)
target_link_libraries(scene_tetrahedra_item PUBLIC scene_c3t3_item)
+
add_item(scene_transform_item Plugins/PCA/Scene_facegraph_transform_item.cpp)
add_item(scene_edit_box_item Plugins/PCA/Scene_edit_box_item.cpp)
add_item(scene_image_item Scene_image_item.cpp)
diff --git a/Polyhedron/demo/Polyhedron/Plugins/Mesh_3/CMakeLists.txt b/Polyhedron/demo/Polyhedron/Plugins/Mesh_3/CMakeLists.txt
index ac143f6dec03..89d05476c462 100644
--- a/Polyhedron/demo/Polyhedron/Plugins/Mesh_3/CMakeLists.txt
+++ b/Polyhedron/demo/Polyhedron/Plugins/Mesh_3/CMakeLists.txt
@@ -28,6 +28,12 @@ target_link_libraries(
${OPENGL_gl_LIBRARY})
target_include_directories(mesh_3_plugin PRIVATE include)
+find_package(ITK QUIET COMPONENTS ITKCommon ITKThresholding ITKSmoothing ITKImageIntensity)
+if(ITK_FOUND)
+ include(CGAL_ITK_support)
+ target_link_libraries(mesh_3_plugin PUBLIC CGAL::ITK_support)
+endif(ITK_FOUND)
+
find_package(VTK QUIET COMPONENTS vtkImagingGeneral vtkIOImage NO_MODULE)
if(VTK_FOUND)
if(VTK_USE_FILE)
diff --git a/Polyhedron/demo/Polyhedron/Plugins/Mesh_3/Mesh_3_plugin.cpp b/Polyhedron/demo/Polyhedron/Plugins/Mesh_3/Mesh_3_plugin.cpp
index 5dcf518f0ec2..e2139eb1b14a 100644
--- a/Polyhedron/demo/Polyhedron/Plugins/Mesh_3/Mesh_3_plugin.cpp
+++ b/Polyhedron/demo/Polyhedron/Plugins/Mesh_3/Mesh_3_plugin.cpp
@@ -36,6 +36,10 @@ auto make_not_null(T&& t) {
#ifdef CGAL_MESH_3_DEMO_ACTIVATE_SEGMENTED_IMAGES
#include "Scene_image_item.h"
#include "Image_type.h"
+#ifdef CGAL_USE_ITK
+#include
+#endif
+
#endif
#include "Meshing_thread.h"
@@ -590,6 +594,29 @@ void Mesh_3_plugin::mesh_3(const Mesh_type mesh_type,
}
}
}
+
+ //Labeled (weighted) image
+ connect(ui.useWeights_checkbox, SIGNAL(toggled(bool)),
+ ui.weightsSigma, SLOT(setEnabled(bool)));
+ connect(ui.useWeights_checkbox, SIGNAL(toggled(bool)),
+ ui.weightsSigma_label, SLOT(setEnabled(bool)));
+ ui.weightsSigma->setValue(1.);
+ bool input_is_labeled_img = (image_item != nullptr && !image_item->isGray());
+ ui.labeledImgGroup->setVisible(input_is_labeled_img);
+
+#ifndef CGAL_USE_ITK
+ if (input_is_labeled_img)
+ {
+ ui.labeledImgGroup->setDisabled(true);
+ ui.labeledImgGroup->setToolTip(
+ QString("The use of weighted images is disabled "
+ "because the Insight Toolkit (ITK) is not available."));
+ ui.useWeights_checkbox->setDisabled(true);
+ ui.weightsSigma_label->setDisabled(true);
+ ui.weightsSigma->setDisabled(true);
+ }
+#endif
+
// -----------------------------------
// Get values
// -----------------------------------
@@ -621,6 +648,9 @@ void Mesh_3_plugin::mesh_3(const Mesh_type mesh_type,
const float iso_value = float(ui.iso_value_spinBox->value());
const float value_outside = float(ui.value_outside_spinBox->value());
const bool inside_is_less = ui.inside_is_less_checkBox->isChecked();
+ const float sigma_weights = ui.useWeights_checkbox->isChecked()
+ ? ui.weightsSigma->value() : 0.f;
+
as_facegraph = (mesh_type == Mesh_type::SURFACE_ONLY)
? ui.facegraphCheckBox->isChecked()
: false;
@@ -692,6 +722,18 @@ void Mesh_3_plugin::mesh_3(const Mesh_type mesh_type,
QMessageBox::critical(mw, tr(""), tr("ERROR: no data in selected item"));
return;
}
+#ifdef CGAL_USE_ITK
+ if ( sigma_weights > 0
+ && sigma_weights != image_item->sigma_weights())
+ {
+ image_item->set_image_weights(
+ CGAL::Mesh_3::generate_label_weights(*pImage, sigma_weights),
+ sigma_weights);
+ }
+#endif
+ const Image* pWeights = sigma_weights > 0
+ ? image_item->image_weights()
+ : nullptr;
Scene_polylines_item::Polylines_container plc;
@@ -711,7 +753,8 @@ void Mesh_3_plugin::mesh_3(const Mesh_type mesh_type,
image_item->isGray(),
iso_value,
value_outside,
- inside_is_less);
+ inside_is_less,
+ pWeights);
break;
}
default:
diff --git a/Polyhedron/demo/Polyhedron/Plugins/Mesh_3/Mesh_3_plugin_cgal_code.cpp b/Polyhedron/demo/Polyhedron/Plugins/Mesh_3/Mesh_3_plugin_cgal_code.cpp
index 4eee5f1aa9ff..f51c46022ed4 100644
--- a/Polyhedron/demo/Polyhedron/Plugins/Mesh_3/Mesh_3_plugin_cgal_code.cpp
+++ b/Polyhedron/demo/Polyhedron/Plugins/Mesh_3/Mesh_3_plugin_cgal_code.cpp
@@ -203,7 +203,8 @@ Meshing_thread* cgal_code_mesh_3(const Image* pImage,
bool is_gray,
float iso_value,
float value_outside,
- bool inside_is_less)
+ bool inside_is_less,
+ const Image* pWeights)
{
if (nullptr == pImage) { return nullptr; }
@@ -221,12 +222,50 @@ Meshing_thread* cgal_code_mesh_3(const Image* pImage,
param.tet_shape = tet_shape;
param.manifold = manifold;
param.image_3_ptr = pImage;
+ param.weights_ptr = pWeights;
Scene_c3t3_item* p_new_item = new Scene_c3t3_item(surface_only);
+
+ QString tooltip = QString("\" With the following mesh parameters"
+ ""
+ "- Angle: %1
"
+ "- Edge size bound: %2
"
+ "- Facets size bound: %3
"
+ "- Approximation bound: %4
")
+ .arg(facet_angle)
+ .arg(edge_size)
+ .arg(facet_sizing)
+ .arg(facet_approx);
+ if (!surface_only)
+ tooltip += QString("- Tetrahedra size bound: %1
")
+ .arg(tet_sizing);
+ tooltip += QString("- Use Weighted Image: %1
")
+ .arg(pWeights == nullptr ? "No" : "Yes");
+ tooltip += "
";
+
+ p_new_item->setProperty("toolTip", tooltip);
+
if(!is_gray)
{
namespace p = CGAL::parameters;
- Image_mesh_domain* p_domain = new Image_mesh_domain
+ Image_mesh_domain* p_domain;
+#ifdef CGAL_USE_ITK
+ if(nullptr != pWeights)
+ {
+ p_domain = new Image_mesh_domain
+ (Image_mesh_domain::create_labeled_image_mesh_domain
+ (p::image = *pImage,
+ p::weights = *pWeights,
+ p::relative_error_bound = 1e-6,
+ p::construct_surface_patch_index =
+ [](int i, int j) { return (i * 1000 + j); }
+ )
+ );
+ }
+ else
+#endif
+ {
+ p_domain = new Image_mesh_domain
(Image_mesh_domain::create_labeled_image_mesh_domain
(p::image = *pImage,
p::relative_error_bound = 1e-6,
@@ -234,6 +273,7 @@ Meshing_thread* cgal_code_mesh_3(const Image* pImage,
[](int i, int j) { return (i * 1000 + j); }
)
);
+ }
if(protect_features && polylines.empty()){
std::vector > polylines_on_bbox;
diff --git a/Polyhedron/demo/Polyhedron/Plugins/Mesh_3/Mesh_3_plugin_cgal_code.h b/Polyhedron/demo/Polyhedron/Plugins/Mesh_3/Mesh_3_plugin_cgal_code.h
index 2e72edebc9c8..5a0e5cc0a1d1 100644
--- a/Polyhedron/demo/Polyhedron/Plugins/Mesh_3/Mesh_3_plugin_cgal_code.h
+++ b/Polyhedron/demo/Polyhedron/Plugins/Mesh_3/Mesh_3_plugin_cgal_code.h
@@ -63,5 +63,6 @@ Meshing_thread* cgal_code_mesh_3(const CGAL::Image_3* pImage,
bool is_gray = false,
float iso_value = 3.f,
float value_outside = 0.f,
- bool inside_is_less = true);
+ bool inside_is_less = true,
+ const CGAL::Image_3* pWeights = nullptr);
#endif
diff --git a/Polyhedron/demo/Polyhedron/Plugins/Mesh_3/Mesh_function.h b/Polyhedron/demo/Polyhedron/Plugins/Mesh_3/Mesh_function.h
index addbc6b8ea7f..d5249e256b91 100644
--- a/Polyhedron/demo/Polyhedron/Plugins/Mesh_3/Mesh_function.h
+++ b/Polyhedron/demo/Polyhedron/Plugins/Mesh_3/Mesh_function.h
@@ -53,6 +53,7 @@ struct Mesh_parameters
bool detect_connected_components;
int manifold;
const CGAL::Image_3* image_3_ptr;
+ const CGAL::Image_3* weights_ptr;
bool use_sizing_field_with_aabb_tree;
inline QStringList log() const;
diff --git a/Polyhedron/demo/Polyhedron/Plugins/Mesh_3/Meshing_dialog.ui b/Polyhedron/demo/Polyhedron/Plugins/Mesh_3/Meshing_dialog.ui
index cc9a9b224bdf..776adbc87661 100644
--- a/Polyhedron/demo/Polyhedron/Plugins/Mesh_3/Meshing_dialog.ui
+++ b/Polyhedron/demo/Polyhedron/Plugins/Mesh_3/Meshing_dialog.ui
@@ -9,8 +9,8 @@
0
0
- 576
- 928
+ 568
+ 1049
@@ -342,10 +342,10 @@
-
- 25,00
+
- 25,00
+ 25.00
@@ -516,27 +516,69 @@
-
-
-
- Create as FaceGraph
+
+
+ Labeled Images
+
+
-
+
+
+ Qt::RightToLeft
+
+
+ Use Weighted Image
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+
+ -
+
+
+ Qt::LeftToRight
+
+
+
+
+
+ false
+
+
+
+ -
+
+
+ false
+
+
+ standard deviation parameter of the internal Gaussian filter
+
+
+ Sigma
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+
+ -
+
+
+ false
+
+
+
+
-
-
-
- Qt::Vertical
-
-
- QSizePolicy::MinimumExpanding
-
-
-
- 20
- 0
-
+
+
+ Create as FaceGraph
-
+
-
@@ -558,13 +600,31 @@
+ detectComponents
+ protectEdges
+ protect
+ sharpEdgesAngle
+ edgeSizing
+ noEdgeSizing
+ approx
noApprox
+ facetSizing
noFacetSizing
+ facetAngle
noAngle
+ tetSizing
noTetSizing
+ tetShape
noTetShape
+ value_outside_spinBox
+ iso_value_spinBox
+ inside_is_less_checkBox
+ useWeights_checkbox
+ weightsSigma
+ facegraphCheckBox
checkBox
manifoldCheckBox
+ facetTopology
diff --git a/Polyhedron/demo/Polyhedron/Scene_image_item.cpp b/Polyhedron/demo/Polyhedron/Scene_image_item.cpp
index 64f756237176..229aff8a7ffe 100644
--- a/Polyhedron/demo/Polyhedron/Scene_image_item.cpp
+++ b/Polyhedron/demo/Polyhedron/Scene_image_item.cpp
@@ -429,6 +429,8 @@ struct Scene_image_item_priv
Scene_image_item_priv(int display_scale, bool hidden, Scene_image_item* parent)
: m_initialized(false)
, m_voxel_scale(display_scale)
+ , m_image_weights()
+ , m_sigma_weights(0.f)
{
item = parent;
is_ogl_4_3 = Three::mainViewer()->isOpenGL_4_3();
@@ -450,6 +452,8 @@ struct Scene_image_item_priv
bool is_ogl_4_3;
Scene_image_item* item;
internal::Vertex_buffer_helper* helper;
+ Image m_image_weights;
+ float m_sigma_weights;
//#endif // SCENE_SEGMENTED_IMAGE_GL_BUFFERS_AVAILABLE
};
@@ -496,6 +500,24 @@ Scene_image_item::compute_bbox() const
m_image->image()->tz+(m_image->zdim()-1) * m_image->vz()));
}
+const Image*
+Scene_image_item::image_weights() const
+{
+ return &d->m_image_weights;
+}
+void
+Scene_image_item::set_image_weights(const Image& img, const float sigma)
+{
+ d->m_image_weights = img;
+ d->m_sigma_weights = sigma;
+}
+float
+Scene_image_item::sigma_weights() const
+{
+ return d->m_sigma_weights;
+}
+
+
void
Scene_image_item::draw(Viewer_interface* viewer) const
{
diff --git a/Polyhedron/demo/Polyhedron/Scene_image_item.h b/Polyhedron/demo/Polyhedron/Scene_image_item.h
index 39630ae6d39a..f1470860c0b4 100644
--- a/Polyhedron/demo/Polyhedron/Scene_image_item.h
+++ b/Polyhedron/demo/Polyhedron/Scene_image_item.h
@@ -44,6 +44,11 @@ class SCENE_IMAGE_ITEM_EXPORT Scene_image_item
const Image* image() const { return m_image; }
bool isGray();
Image* m_image;
+
+ const Image* image_weights() const;
+ void set_image_weights(const Image& img, const float sigma);
+ float sigma_weights() const;
+
void invalidateOpenGLBuffers();
void initializeBuffers(Viewer_interface *) const;
void computeElements() const;
diff --git a/Testsuite/test/collect_cgal_testresults_from_cmake b/Testsuite/test/collect_cgal_testresults_from_cmake
index 54b5d502ce0b..f6aca34a87d1 100755
--- a/Testsuite/test/collect_cgal_testresults_from_cmake
+++ b/Testsuite/test/collect_cgal_testresults_from_cmake
@@ -60,7 +60,7 @@ print_testresult()
RESULT="t"
fi
else
- if grep -E -q 'NOTICE: .*(need|require|incompatible).*and.*will not be' CompilerOutput_$1
+ if grep -E -q 'NOTICE: .*(need|require|incompatible).*will not be' CompilerOutput_$1
then
RESULT="r"
else