DGtalTools 2.0.0
Loading...
Searching...
No Matches
heightfield2shading.cpp
1
30#include <iostream>
31#include <fstream>
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"
42
43#include "DGtal/io/colormaps/GrayscaleColorMap.h"
44
45#include "CLI11.hpp"
46
47
48using namespace std;
49using namespace DGtal;
50
122template<typename TImage, typename TImageVector>
123void
124computerBasicNormalsFromHeightField(const TImage &anHeightMap, TImageVector &vectorField,
125 bool invertN = false)
126{
127 for(typename TImage::Domain::ConstIterator it = anHeightMap.domain().begin();
128 it != anHeightMap.domain().end(); it++){
129 if(anHeightMap.domain().isInside(*it+Z2i::Point::diagonal(1))&&
130 anHeightMap.domain().isInside(*it-Z2i::Point::diagonal(1))){
131 double dx = (anHeightMap(*it-Z2i::Point(1,0))-anHeightMap(*it+Z2i::Point(1,0)))/2.0;
132 double dy = (anHeightMap(*it-Z2i::Point(0,1))-anHeightMap(*it+Z2i::Point(0,1)))/2.0;
133 Z3i::RealPoint n (dx, dy, 1);
134 n /= n.norm();
135 vectorField.setValue(*it,invertN? -n : n);
136 }
137 }
138}
139
140
141
142template<typename TImageVector>
143void
144importNormals(std::string file, TImageVector &vectorField,
145 bool invertN = false)
146{
147 std::vector<Z3i::RealPoint> vp = PointListReader<Z3i::RealPoint>::getPointsFromFile(file);
148 trace.info() << "import done: " << vp.size() << std::endl;
149 for(unsigned int i = 0; i< vp.size()-1; i=i+2){
150 Z3i::RealPoint p = vp.at(i);
151 Z3i::RealPoint q = vp.at(i+1);
152 Z3i::RealPoint n = (q-p)/(p-q).norm();
153 vectorField.setValue(Z2i::Point(p[0], p[1]),invertN? -n : n);
154 }
155 trace.info() <<endl;
156}
157
158
159template<typename TImageVector>
160void
161importNormalsOrdDir(std::string file, TImageVector &vectorField,
162 unsigned int width, unsigned int height, bool invertN = false)
163{
164 std::vector<Z3i::RealPoint> vp = PointListReader<Z3i::RealPoint>::getPointsFromFile(file);
165
166 for(unsigned int i = 0; i< vp.size()-1; i++){
167 Z2i::Point p ( i-(width*floor((i/width))), i/width);
168 Z3i::RealPoint n = vp[i];
169 vectorField.setValue(p,invertN? -n : n);
170 }
171 trace.info() <<endl;
172}
173
174
175// Basic Lambertian reflectance model only based on light source direction.
176template<typename TImage2D, typename TPoint3D >
177struct LambertianShadindFunctor{
178 LambertianShadindFunctor(const TPoint3D &aLightSourceDir ):
179 myLightSourceDirection(aLightSourceDir/aLightSourceDir.norm()){}
180
181 inline
182 unsigned int operator()(const TPoint3D &aNormal) const
183 {
184 int intensity = aNormal.dot(myLightSourceDirection)*std::numeric_limits<typename TImage2D::Value>::max();
185 return intensity>0? intensity:0;
186 }
187 TPoint3D myLightSourceDirection;
188};
189
190
191// Basic Lambertian reflectance model from one light source positiion emeting in all direction.
192template<typename TImage2D, typename TPoint3D >
193struct LambertianShadindFunctorAllDirections{
194 LambertianShadindFunctorAllDirections(const TPoint3D &aLightSourcePosition ):
195 myLightSourcePosition(aLightSourcePosition){}
196
197 inline
198 unsigned int operator()(const TPoint3D &aNormal, const Z2i::Point &aPoint, const double h) const
199 {
200 TPoint3D l;
201
202 Z3i::RealPoint posL (aPoint[0], aPoint[1], h);
203 l = -posL+myLightSourcePosition;
204 l /= l.norm();
205 int intensity = aNormal.dot(l)*std::numeric_limits<typename TImage2D::Value>::max();
206 return intensity>0? intensity:0;
207 }
208 TPoint3D myLightSourcePosition;
209};
210
211
212
213
214
215// Specular reflectance from Nayar model.
216template<typename TImage2D, typename TPoint3D >
217struct SpecularNayarShadindFunctor{
218 SpecularNayarShadindFunctor(const TPoint3D &lightSourceDirection, const double kld,
219 const double kls, const double sigma ):
220 myLightSourceDirection(lightSourceDirection/lightSourceDirection.norm()),
221 myKld(kld), myKls(kls), mySigma(sigma){}
222
223 inline
224 unsigned int operator()(const TPoint3D &aNormal) const {
225 double lambertianIntensity = std::max(aNormal.dot(myLightSourceDirection), 0.0);
226 double alpha = acos(((myLightSourceDirection+Z3i::RealPoint(0,0,1.0))/2.0).dot(aNormal/aNormal.norm()));
227 double specularIntensity = exp(-alpha*alpha/(2.0*mySigma));
228 double resu = myKld*lambertianIntensity+myKls*specularIntensity;
229
230 resu = std::max(resu, 0.0);
231 resu = std::min(resu, 1.0);
232 return resu*std::numeric_limits<typename TImage2D::Value>::max();
233 }
234
235 TPoint3D myLightSourceDirection;
236 double myKld, myKls, mySigma;
237};
238
239
240
241// Specular reflectance from Nayar model.
242template<typename TImage2D, typename TPoint3D >
243struct SpecularNayarShadindFunctorAllDirections{
244 SpecularNayarShadindFunctorAllDirections(const TPoint3D &lightSourcePosition, const double kld,
245 const double kls, const double sigma ):
246 myLightSourcePosition(lightSourcePosition),
247 myKld(kld), myKls(kls), mySigma(sigma){}
248
249 inline
250 unsigned int operator()(const TPoint3D &aNormal, const Z2i::Point &aPoint, const double h) const {
251 TPoint3D l;
252 Z3i::RealPoint posL (aPoint[0], aPoint[1], h);
253 l = -posL+myLightSourcePosition;
254 l /= l.norm();
255
256 double lambertianIntensity = std::max(aNormal.dot(l), 0.0);
257 double alpha = acos(((l+Z3i::RealPoint(0,0,1.0))/2.0).dot(aNormal/aNormal.norm()));
258 double specularIntensity = exp(-alpha*alpha/(2.0*mySigma));
259 double resu = myKld*lambertianIntensity+myKls*specularIntensity;
260
261 resu = std::max(resu, 0.0);
262 resu = std::min(resu, 1.0);
263 return resu*std::numeric_limits<typename TImage2D::Value>::max();
264 }
265
266 TPoint3D myLightSourcePosition;
267 double myKld, myKls, mySigma;
268};
269
270
271
272
273// Basic Lambertian reflectance model from one light source positiion emeting in all direction.
274template<typename TImage2D, typename TPoint3D >
275struct ImageMapReflectance{
276 ImageMapReflectance(const std::string &filename): myImageMap (PPMReader<TImage2D>::importPPM(filename))
277 {
278
279 myCenterPoint = (myImageMap.domain().upperBound()-myImageMap.domain().lowerBound())/2;
280 myImageRadius = min((myImageMap.domain().upperBound()-myImageMap.domain().lowerBound())[1], (myImageMap.domain().upperBound()-myImageMap.domain().lowerBound())[0])/2;
281 }
282
283 inline
284 unsigned int operator()(const TPoint3D &aNormal) const
285 {
286 Z2i::Point p(aNormal[0]*myImageRadius,aNormal[1]*myImageRadius ) ;
287 p += myCenterPoint;
288 if(myImageMap.domain().isInside(p)){
289 return myImageMap(p);
290 }else{
291 return myImageMap(Z2i::Point(0,0,0));
292 }
293 }
294 TImage2D myImageMap;
295 Z2i::Point myCenterPoint;
296 unsigned int myImageRadius;
297};
298
299
300
301struct IdColor{
302 Color operator()( const unsigned int & aValue ) const{
303 return DGtal::Color(aValue);
304 }
305};
306
307
308
309DGtal::Color
310colorFromHSB(double h, double saturation, double value){
311 double r, g, b;
312 DGtal::Color::HSVtoRGB(r, g, b, h,saturation, value);
313 return DGtal::Color(r*255.0,g*255.0,b*255.0);
314
315}
316
317
318int main( int argc, char** argv )
319{
320 typedef ImageContainerBySTLVector < Z2i::Domain, unsigned int> Image2DC;
321 typedef ImageSelector < Z2i::Domain, unsigned char>::Type Image2D;
322 typedef ImageContainerBySTLVector < Z2i::Domain, Z3i::RealPoint> Image2DNormals;
323
324
325
326 // parse command line using CLI ----------------------------------------------
327 CLI::App app;
328 std::string inputFileName;
329 std::string outputFileName {"result.pgm"};
330 std::string normalFileName {""};
331 double lx, ly, lz, px, py, pz;
332 bool usingAllDirectionLightSource = false;
333 bool useOrderedImportNormal = false;
334 bool hsvShading = false;
335 bool normalMap = false;
336 bool invertNormals = false;
337 std::vector<double> specularModel;
338 std::string reflectanceMap;
339 std::vector<double> lDir = {0, 0, 1};
340 std::vector<double> lPos;
341 std::vector<unsigned int> domain;
342
343 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"
344 "Other example: heightfield2shading ${DGtal}/examples/samples/bunnyHeightField.pgm shading.ppm --importNormal ${DGtal}/examples/samples/bunnyHeightField_normals.sdp --hsvShading\n");
345 auto opt1 = app.add_option("-i,--input,1", inputFileName, "input heightfield file (2D image).")
346 ->check(CLI::ExistingFile);
347 auto domOpt = app.add_option("--domain,-d", domain , "specify the domain (required when normal are imported and if --inout is not given).")
348 -> expected(2);
349 app.add_option("-o,--output,2", outputFileName,"output image.");
350 auto impNOpt = app.add_option("--importNormal", normalFileName, "import normals from file.");
351 app.add_flag("--orderedNormalsImport",useOrderedImportNormal, "Use ordered normals." );
352 app.add_option("--lightDir,--lDir,--ld", lDir, "light source direction: lx ly lz.")
353 ->expected(3);
354 app.add_option("--lightPos,--lPos,--lp", lPos, "light source position: px py pz.")
355 ->expected(3);
356 app.add_option("-s,--specularModel", specularModel, "use specular Nayar model with 3 param Kdiff, Kspec, sigma." )
357 ->expected(3);
358 app.add_option("-r,--reflectanceMap",reflectanceMap, "specify a image as reflectance map.")
359 ->check(CLI::ExistingFile);
360 app.add_flag("--hsvShading", hsvShading, "use shading with HSV shading (given from the normal vector)");
361 app.add_flag("--normalMap", normalMap, "generates normal map.");
362 app.add_flag("--invertNormals,-v", invertNormals, "invert normal orientations.");
363 app.get_formatter()->column_width(40);
364 CLI11_PARSE(app, argc, argv);
365 // END parse command line using CLI ----------------------------------------------
366
367 if(! *opt1 && !(*domOpt && *impNOpt ) ){
368 trace.error() << "You need either set input file (--input) or use a domain (--domain) with the --importNormal option." << std::endl;
369 exit(0);
370 }
371
372
373 if(lDir.size() == 3)
374 {
375 lx = lDir[0];
376 ly = lDir[1];
377 lz = lDir[2];
378 }
379 else if(lPos.size() == 3)
380 {
381 px = lPos[0];
382 py = lPos[1];
383 pz = lPos[2];
384 usingAllDirectionLightSource = true;
385 }
386 else if (reflectanceMap == "" && ! hsvShading && !normalMap)
387 {
388 trace.error() << "You need to specify either the light source direction or position (if you use a all directions model)." << std::endl;
389 exit(0);
390 }
391
392 LambertianShadindFunctor<Image2D, Z3i::RealPoint> lShade (Z3i::RealPoint(lx,ly,lz));
393 LambertianShadindFunctorAllDirections<Image2D, Z3i::RealPoint> lShadePosD (Z3i::RealPoint(px ,py, pz));
394 SpecularNayarShadindFunctor<Image2D, Z3i::RealPoint> lSpecular (Z3i::RealPoint(lx,ly,lz), 0, 0, 0);
395 SpecularNayarShadindFunctorAllDirections<Image2D, Z3i::RealPoint> lSpecularPosD (Z3i::RealPoint(px,py,pz), 0, 0, 0);
396
397
398 bool useSpecular = false;
399 if(specularModel.size() == 3){
400 useSpecular = true;
401 lSpecular.myKld = specularModel[0];
402 lSpecular.myKls = specularModel[1];
403 lSpecular.mySigma = specularModel[2];
404 lSpecularPosD.myKld = specularModel[0];
405 lSpecularPosD.myKls = specularModel[1];
406 lSpecularPosD.mySigma = specularModel[2];
407 if(specularModel[2]==0.0)
408 {
409 trace.error()<< "a 0 value for sigma is not possible in the Nayar model, please change it. "<< std::endl;
410 exit(1);
411 }
412
413 }
414
415
416 Image2D inputImage(Z2i::Domain(Z2i::Point(0,0), Z2i::Point(0,0) ));
417 if (inputFileName != "") {
418 trace.info() << "Reading input file " << inputFileName ;
419 inputImage = DGtal::GenericReader<Image2D>::import(inputFileName);
420 trace.info() << "[done]" << std::endl;
421 }
422 else {
423 inputImage = Image2D(Z2i::Domain(Z2i::Domain(Z2i::Point(0,0),
424 Z2i::Point(domain[0],domain[1]) )));
425 }
426
427 Image2DNormals vectNormals (inputImage.domain());
428 Image2D result (inputImage.domain());
429 Image2DC resultC (inputImage.domain());
430
431 if(normalFileName != ""){
432 trace.info() << "Import normal file " << inputFileName << vectNormals.domain();
433 if (useOrderedImportNormal)
434 {
435 importNormalsOrdDir(normalFileName, vectNormals,
436 inputImage.domain().upperBound()[0]+1,
437 inputImage.domain().upperBound()[1]+1, invertNormals);
438 }
439 else
440 {
441 importNormals(normalFileName, vectNormals, invertNormals);
442 }
443 trace.info() << "[done]" << std::endl;
444 }
445 else
446 {
447 computerBasicNormalsFromHeightField(inputImage, vectNormals, invertNormals);
448 }
449 if (hsvShading)
450 {
451 for(typename Image2D::Domain::ConstIterator it = inputImage.domain().begin();
452 it != inputImage.domain().end(); it++){
453 auto n = vectNormals(*it);
454 double sat = 1.0*( sin(acos(Z3i::RealPoint(0.0,0.0,1.0).dot(n))));
455 double value = 1.0;
456 double hue = ((int)(((2.0*M_PI+atan2(Z3i::RealPoint(0.0,1.0,0.0).dot(n),
457 Z3i::RealPoint(1.0,0.0,0.0).dot(n)))/(2.0*M_PI))*360.0+100))%360;
458 DGtal::uint32_t colCode = colorFromHSB(hue, sat, value).getRGB();
459 resultC.setValue(*it, colCode);
460 }
461 IdColor id;
462 PPMWriter<Image2DC, IdColor >::exportPPM(outputFileName, resultC, id);
463 }
464 else if (normalMap)
465 {
466 DGtal::functors::Rescaling<double, unsigned int> rgRescale (-1.0, 1.0, 0, 255);
467 DGtal::functors::Rescaling<double, unsigned int> bRescale (0.0, -1.0, 128, 255);
468 for(typename Image2D::Domain::ConstIterator it = inputImage.domain().begin();
469 it != inputImage.domain().end(); it++){
470 auto n = vectNormals(*it);
471 DGtal::Color c (rgRescale(n[0]), rgRescale(n[1]), bRescale(n[2]) );
472 resultC.setValue(*it, c.getRGB());
473 }
474 IdColor id;
475 PPMWriter<Image2DC, IdColor >::exportPPM(outputFileName, resultC, id);
476 }
477 else if(reflectanceMap != "")
478 {
479 ImageMapReflectance<Image2D, Z3i::RealPoint> lMap(reflectanceMap);
480 for(typename Image2D::Domain::ConstIterator it = inputImage.domain().begin();
481 it != inputImage.domain().end(); it++){
482 result.setValue(*it, lMap(vectNormals(*it)));
483 }
484 IdColor id;
485 PPMWriter<Image2D, IdColor >::exportPPM(outputFileName, result, id);
486 }
487
488 else
489 {
490 for(typename Image2D::Domain::ConstIterator it = inputImage.domain().begin();
491 it != inputImage.domain().end(); it++){
492 if(usingAllDirectionLightSource)
493 {
494 result.setValue(*it, useSpecular? lSpecularPosD(vectNormals(*it), *it, inputImage(*it)):
495 lShadePosD(vectNormals(*it), *it, inputImage(*it)));
496 }
497 else
498 {
499 result.setValue(*it, useSpecular? lSpecular(vectNormals(*it)):lShade(vectNormals(*it)));
500 }
501
502 }
503 result >> outputFileName;
504 }
505
506 return 0;
507}
Definition ATu0v1.h:57