32 #include "DGtal/base/Common.h"
33 #include "DGtal/helpers/StdDefs.h"
34 #include "DGtal/images/ImageContainerBySTLVector.h"
35 #include "DGtal/io/readers/GenericReader.h"
36 #include "DGtal/io/writers/GenericWriter.h"
37 #include "DGtal/io/writers/PPMWriter.h"
38 #include "DGtal/images/ImageSelector.h"
39 #include "DGtal/io/readers/PointListReader.h"
40 #include "DGtal/images/ConstImageAdapter.h"
41 #include "DGtal/kernel/BasicPointFunctors.h"
43 #include "DGtal/io/colormaps/GrayscaleColorMap.h"
49 using namespace DGtal;
119 template<
typename TImage,
typename TImageVector>
121 computerBasicNormalsFromHeightField(
const TImage &anHeightMap, TImageVector &vectorField,
122 bool invertN =
false)
124 for(
typename TImage::Domain::ConstIterator it = anHeightMap.domain().begin();
125 it != anHeightMap.domain().end(); it++){
126 if(anHeightMap.domain().isInside(*it+Z2i::Point::diagonal(1))&&
127 anHeightMap.domain().isInside(*it-Z2i::Point::diagonal(1))){
132 vectorField.setValue(*it,invertN? -n : n);
139 template<
typename TImageVector>
141 importNormals(std::string file, TImageVector &vectorField,
142 bool invertN =
false)
145 trace.
info() <<
"import done: " << vp.size() << std::endl;
146 for(
unsigned int i = 0; i< vp.size()-1; i=i+2){
150 vectorField.setValue(
Z2i::Point(p[0], p[1]),invertN? -n : n);
156 template<
typename TImageVector>
158 importNormalsOrdDir(std::string file, TImageVector &vectorField,
159 unsigned int width,
unsigned int height,
bool invertN =
false)
163 for(
unsigned int i = 0; i< vp.size()-1; i++){
164 Z2i::Point p ( i-(width*floor((i/width))), i/width);
166 vectorField.setValue(p,invertN? -n : n);
173 template<
typename TImage2D,
typename TPo
int3D >
174 struct LambertianShadindFunctor{
175 LambertianShadindFunctor(
const TPoint3D &aLightSourceDir ):
176 myLightSourceDirection(aLightSourceDir/aLightSourceDir.norm()){}
179 unsigned int operator()(
const TPoint3D &aNormal)
const
181 int intensity = aNormal.dot(myLightSourceDirection)*std::numeric_limits<typename TImage2D::Value>::max();
182 return intensity>0? intensity:0;
184 TPoint3D myLightSourceDirection;
189 template<
typename TImage2D,
typename TPo
int3D >
190 struct LambertianShadindFunctorAllDirections{
191 LambertianShadindFunctorAllDirections(
const TPoint3D &aLightSourcePosition ):
192 myLightSourcePosition(aLightSourcePosition){}
195 unsigned int operator()(
const TPoint3D &aNormal,
const Z2i::Point &aPoint,
const double h)
const
200 l = -posL+myLightSourcePosition;
202 int intensity = aNormal.dot(l)*std::numeric_limits<typename TImage2D::Value>::max();
203 return intensity>0? intensity:0;
205 TPoint3D myLightSourcePosition;
213 template<
typename TImage2D,
typename TPo
int3D >
214 struct SpecularNayarShadindFunctor{
215 SpecularNayarShadindFunctor(
const TPoint3D &lightSourceDirection,
const double kld,
216 const double kls,
const double sigma ):
217 myLightSourceDirection(lightSourceDirection/lightSourceDirection.norm()),
218 myKld(kld), myKls(kls), mySigma(sigma){}
221 unsigned int operator()(
const TPoint3D &aNormal)
const {
222 double lambertianIntensity = std::max(aNormal.dot(myLightSourceDirection), 0.0);
223 double alpha = acos(((myLightSourceDirection+
Z3i::RealPoint(0,0,1.0))/2.0).dot(aNormal/aNormal.norm()));
224 double specularIntensity = exp(-alpha*alpha/(2.0*mySigma));
225 double resu = myKld*lambertianIntensity+myKls*specularIntensity;
227 resu = std::max(resu, 0.0);
228 resu = std::min(resu, 1.0);
229 return resu*std::numeric_limits<typename TImage2D::Value>::max();
232 TPoint3D myLightSourceDirection;
233 double myKld, myKls, mySigma;
239 template<
typename TImage2D,
typename TPo
int3D >
240 struct SpecularNayarShadindFunctorAllDirections{
241 SpecularNayarShadindFunctorAllDirections(
const TPoint3D &lightSourcePosition,
const double kld,
242 const double kls,
const double sigma ):
243 myLightSourcePosition(lightSourcePosition),
244 myKld(kld), myKls(kls), mySigma(sigma){}
247 unsigned int operator()(
const TPoint3D &aNormal,
const Z2i::Point &aPoint,
const double h)
const {
250 l = -posL+myLightSourcePosition;
253 double lambertianIntensity = std::max(aNormal.dot(l), 0.0);
254 double alpha = acos(((l+
Z3i::RealPoint(0,0,1.0))/2.0).dot(aNormal/aNormal.norm()));
255 double specularIntensity = exp(-alpha*alpha/(2.0*mySigma));
256 double resu = myKld*lambertianIntensity+myKls*specularIntensity;
258 resu = std::max(resu, 0.0);
259 resu = std::min(resu, 1.0);
260 return resu*std::numeric_limits<typename TImage2D::Value>::max();
263 TPoint3D myLightSourcePosition;
264 double myKld, myKls, mySigma;
271 template<
typename TImage2D,
typename TPo
int3D >
272 struct ImageMapReflectance{
273 ImageMapReflectance(
const std::string &filename): myImageMap (
PPMReader<TImage2D>::importPPM(filename))
276 myCenterPoint = (myImageMap.domain().upperBound()-myImageMap.domain().lowerBound())/2;
277 myImageRadius = min((myImageMap.domain().upperBound()-myImageMap.domain().lowerBound())[1], (myImageMap.domain().upperBound()-myImageMap.domain().lowerBound())[0])/2;
281 unsigned int operator()(
const TPoint3D &aNormal)
const
283 Z2i::Point p(aNormal[0]*myImageRadius,aNormal[1]*myImageRadius ) ;
285 if(myImageMap.domain().isInside(p)){
286 return myImageMap(p);
293 unsigned int myImageRadius;
299 Color operator()(
const unsigned int & aValue )
const{
307 colorFromHSB(
double h,
double saturation,
double value){
309 DGtal::Color::HSVtoRGB(r, g, b, h,saturation, value);
315 int main(
int argc,
char** argv )
325 std::string inputFileName;
326 std::string outputFileName {
"result.pgm"};
327 std::string normalFileName {
""};
328 double lx, ly, lz, px, py, pz;
329 bool usingAllDirectionLightSource =
false;
330 bool useOrderedImportNormal =
false;
331 bool hsvShading =
false;
332 bool normalMap =
false;
333 bool invertNormals =
false;
334 std::vector<double> specularModel;
335 std::string reflectanceMap;
336 std::vector<double> lDir = {0, 0, 1};
337 std::vector<double> lPos;
338 std::vector<unsigned int> domain;
340 app.description(
"Render a 2D heightfield image into a shading image. You can choose between lambertian model (diffuse reflectance) and specular model (Nayar reflectance model). You can also choose between a single directional light source (using --lightDir option) or use light source which emits in all direction (by specifying the light source position with --lightPos} option). Another rendering mode is given from a bitmap reflectance map which represents the rendering for a normal vector value (mapped according the x/y coordinates).\nExample:\n heightfield2shading ${DGtal}/examples/samples/bunnyHeightField.pgm shading.pgm --lPos 10.0 -120.0 550.0 --importNormal ${DGtal}/examples/samples/bunnyHeightField_normals.sdp -s 1.0 0.2 0.8 \n"
341 "Other example: heightfield2shading ${DGtal}/examples/samples/bunnyHeightField.pgm shading.ppm --importNormal ${DGtal}/examples/samples/bunnyHeightField_normals.sdp --hsvShading\n");
342 auto opt1 = app.add_option(
"-i,--input,1", inputFileName,
"input heightfield file (2D image).")
343 ->check(CLI::ExistingFile);
344 auto domOpt = app.add_option(
"--domain,-d", domain ,
"specify the domain (required when normal are imported and if --inout is not given).")
346 app.add_option(
"-o,--output,2", outputFileName,
"output image.");
347 auto impNOpt = app.add_option(
"--importNormal", normalFileName,
"import normals from file.");
348 app.add_flag(
"--orderedNormalsImport",useOrderedImportNormal,
"Use ordered normals." );
349 app.add_option(
"--lightDir,--lDir,--ld", lDir,
"light source direction: lx ly lz.")
351 app.add_option(
"--lightPos,--lPos,--lp", lPos,
"light source position: px py pz.")
353 app.add_option(
"-s,--specularModel", specularModel,
"use specular Nayar model with 3 param Kdiff, Kspec, sigma." )
355 app.add_option(
"-r,--reflectanceMap",reflectanceMap,
"specify a image as reflectance map.")
356 ->check(CLI::ExistingFile);
357 app.add_flag(
"--hsvShading", hsvShading,
"use shading with HSV shading (given from the normal vector)");
358 app.add_flag(
"--normalMap", normalMap,
"generates normal map.");
359 app.add_flag(
"--invertNormals,-v", invertNormals,
"invert normal orientations.");
360 app.get_formatter()->column_width(40);
361 CLI11_PARSE(app, argc, argv);
364 if(! *opt1 && !(*domOpt && *impNOpt ) ){
365 trace.
error() <<
"You need either set input file (--input) or use a domain (--domain) with the --importNormal option." << std::endl;
376 else if(lPos.size() == 3)
381 usingAllDirectionLightSource =
true;
383 else if (reflectanceMap ==
"" && ! hsvShading && !normalMap)
385 trace.
error() <<
"You need to specify either the light source direction or position (if you use a all directions model)." << std::endl;
389 LambertianShadindFunctor<Image2D, Z3i::RealPoint> lShade (
Z3i::RealPoint(lx,ly,lz));
390 LambertianShadindFunctorAllDirections<Image2D, Z3i::RealPoint> lShadePosD (
Z3i::RealPoint(px ,py, pz));
391 SpecularNayarShadindFunctor<Image2D, Z3i::RealPoint> lSpecular (
Z3i::RealPoint(lx,ly,lz), 0, 0, 0);
392 SpecularNayarShadindFunctorAllDirections<Image2D, Z3i::RealPoint> lSpecularPosD (
Z3i::RealPoint(px,py,pz), 0, 0, 0);
395 bool useSpecular =
false;
396 if(specularModel.size() == 3){
398 lSpecular.myKld = specularModel[0];
399 lSpecular.myKls = specularModel[1];
400 lSpecular.mySigma = specularModel[2];
401 lSpecularPosD.myKld = specularModel[0];
402 lSpecularPosD.myKls = specularModel[1];
403 lSpecularPosD.mySigma = specularModel[2];
404 if(specularModel[2]==0.0)
406 trace.
error()<<
"a 0 value for sigma is not possible in the Nayar model, please change it. "<< std::endl;
414 if (inputFileName !=
"") {
415 trace.
info() <<
"Reading input file " << inputFileName ;
424 Image2DNormals vectNormals (inputImage.domain());
425 Image2D result (inputImage.domain());
426 Image2DC resultC (inputImage.domain());
428 if(normalFileName !=
""){
429 trace.
info() <<
"Import normal file " << inputFileName << vectNormals.domain();
430 if (useOrderedImportNormal)
432 importNormalsOrdDir(normalFileName, vectNormals,
433 inputImage.domain().upperBound()[0]+1,
434 inputImage.domain().upperBound()[1]+1, invertNormals);
438 importNormals(normalFileName, vectNormals, invertNormals);
444 computerBasicNormalsFromHeightField(inputImage, vectNormals, invertNormals);
449 it != inputImage.domain().end(); it++){
450 auto n = vectNormals(*it);
451 double sat = 1.0*( sin(acos(
Z3i::RealPoint(0.0,0.0,1.0).dot(n))));
453 double hue = ((int)(((2.0*M_PI+atan2(
Z3i::RealPoint(0.0,1.0,0.0).dot(n),
456 resultC.setValue(*it, colCode);
466 it != inputImage.domain().end(); it++){
467 auto n = vectNormals(*it);
468 DGtal::Color c (rgRescale(n[0]), rgRescale(n[1]), bRescale(n[2]) );
469 resultC.setValue(*it, c.getRGB());
474 else if(reflectanceMap !=
"")
476 ImageMapReflectance<Image2D, Z3i::RealPoint> lMap(reflectanceMap);
478 it != inputImage.domain().end(); it++){
479 result.setValue(*it, lMap(vectNormals(*it)));
488 it != inputImage.domain().end(); it++){
489 if(usingAllDirectionLightSource)
491 result.setValue(*it, useSpecular? lSpecularPosD(vectNormals(*it), *it, inputImage(*it)):
492 lShadePosD(vectNormals(*it), *it, inputImage(*it)));
496 result.setValue(*it, useSpecular? lSpecular(vectNormals(*it)):lShade(vectNormals(*it)));
500 result >> outputFileName;
int main(int argc, char **argv)
DGtal::uint32_t getRGB() const
std::vector< Value >::const_iterator ConstIterator
double norm(const NormType type=L_2) const
Trace trace(traceWriterTerm)
static TContainer import(const std::string &filename, std::vector< unsigned int > dimSpace=std::vector< unsigned int >())