PRAgMaTIc  master
jpeg2mesh.cpp
Go to the documentation of this file.
1 /* Copyright (C) 2010 Imperial College London and others.
2  *
3  * Please see the AUTHORS file in the main source directory for a
4  * full list of copyright holders.
5  *
6  * Gerard Gorman
7  * Applied Modelling and Computation Group
8  * Department of Earth Science and Engineering
9  * Imperial College London
10  *
11  * g.gorman@imperial.ac.uk
12  *
13  * Redistribution and use in source and binary forms, with or without
14  * modification, are permitted provided that the following conditions
15  * are met:
16  * 1. Redistributions of source code must retain the above copyright
17  * notice, this list of conditions and the following disclaimer.
18  * 2. Redistributions in binary form must reproduce the above
19  * copyright notice, this list of conditions and the following
20  * disclaimer in the documentation and/or other materials provided
21  * with the distribution.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
24  * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
25  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
26  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
27  * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS
28  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
29  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
30  * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
31  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
32  * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
33  * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
34  * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35  * SUCH DAMAGE.
36  */
37 
38 #include <iostream>
39 
40 #include <getopt.h>
41 
42 #include "Mesh.h"
43 #include "VTKTools.h"
44 #include "MetricField.h"
45 
46 #include "Coarsen.h"
47 #include "Smooth.h"
48 #include "Swapping.h"
49 #include "ticker.h"
50 
51 #include <mpi.h>
52 
53 void usage(char *cmd){
54  std::cout<<"Usage: "<<cmd<<" [options] infile\n"
55  <<"\nOptions:\n"
56  <<" -h, --help\n\tHelp! Prints this message.\n"
57  <<" -v, --verbose\n\tVerbose output.\n"
58  <<" -c factor, --compression factor\n\tCurrently an art...\n";
59  return;
60 }
61 
62 int parse_arguments(int argc, char **argv, std::string &infilename, bool &verbose, double &factor){
63 
64  // Set defaults
65  verbose = false;
66  factor = 1.0;
67 
68  if(argc==1){
69  usage(argv[0]);
70  exit(0);
71  }
72 
73  struct option longOptions[] = {
74  {"help", 0, 0, 'h'},
75  {"verbose", 0, 0, 'v'},
76  {"compression", optional_argument, 0, 'c'},
77  {0, 0, 0, 0}
78  };
79 
80  int optionIndex = 0;
81  int c;
82  const char *shortopts = "hvc:";
83 
84  // Set opterr to nonzero to make getopt print error messages
85  opterr=1;
86  while (true){
87  c = getopt_long(argc, argv, shortopts, longOptions, &optionIndex);
88 
89  if (c == -1) break;
90 
91  switch (c){
92  case 'h':
93  usage(argv[0]);
94  break;
95  case 'v':
96  verbose = true;
97  break;
98  case 'c':
99  factor = atof(optarg);
100  break;
101  case '?':
102  // missing argument only returns ':' if the option string starts with ':'
103  // but this seems to stop the printing of error messages by getopt?
104  std::cerr<<"ERROR: unknown option or missing argument\n";
105  usage(argv[0]);
106  exit(-1);
107  case ':':
108  std::cerr<<"ERROR: missing argument\n";
109  usage(argv[0]);
110  exit(-1);
111  default:
112  // unexpected:
113  std::cerr<<"ERROR: getopt returned unrecognized character code\n";
114  exit(-1);
115  }
116  }
117 
118  infilename = std::string(argv[argc-1]);
119 
120  return 0;
121 }
122 
123 void cout_quality(const Mesh<double> *mesh, std::string operation){
124  double qmean = mesh->get_qmean();
125  double qmin = mesh->get_qmin();
126 
127  int rank;
128  MPI_Comm_rank(MPI_COMM_WORLD, &rank);
129 
130  if(rank==0)
131  std::cout<<operation<<": step in quality (mean, min): ("<<qmean<<", "<<qmin<<")"<<std::endl;
132 }
133 
134 int main(int argc, char **argv){
135  int required_thread_support=MPI_THREAD_SINGLE;
136  int provided_thread_support;
137  MPI_Init_thread(&argc, &argv, required_thread_support, &provided_thread_support);
138  assert(required_thread_support==provided_thread_support);
139 
140  int rank;
141  MPI_Comm_rank(MPI_COMM_WORLD, &rank);
142 
143  if(argc==1){
144  usage(argv[0]);
145  exit(-1);
146  }
147 
148  std::string infilename, outfilename;
149  bool verbose=false;
150  double factor=1.0;
151 
152  parse_arguments(argc, argv, infilename, verbose, factor);
153 
154  // Read in image
155  vtkSmartPointer<vtkJPEGReader> reader = vtkSmartPointer<vtkJPEGReader>::New();
156  reader->SetFileName(infilename.c_str());
157  vtkSmartPointer<vtkImageGaussianSmooth> gsmooth = vtkSmartPointer<vtkImageGaussianSmooth>::New();
158  gsmooth->SetStandardDeviation(9);
159  gsmooth->SetDimensionality(2);
160 #if VTK_MAJOR_VERSION < 6
161  gsmooth->SetInput(reader->GetOutput());
162 #else
163  gsmooth->SetInputConnection(reader->GetOutputPort());
164 #endif
165  gsmooth->Update();
166 
167  // Convert image to triangulated unstructured grid
168  vtkSmartPointer<vtkDataSetTriangleFilter> image2ug = vtkSmartPointer<vtkDataSetTriangleFilter>::New();
169 #if VTK_MAJOR_VERSION < 6
170  image2ug->SetInput(gsmooth->GetOutput());
171 #else
172  image2ug->SetInputConnection(gsmooth->GetOutputPort());
173 #endif
174 
175  vtkUnstructuredGrid *ug = image2ug->GetOutput();
176 #if VTK_MAJOR_VERSION < 6
177  ug->Update();
178 #endif
179 
180  size_t NNodes = ug->GetNumberOfPoints();
181  std::vector<double> x(NNodes),y(NNodes), imageR(NNodes), imageG(NNodes), imageB(NNodes);
182  for(size_t i=0;i<NNodes;i++){
183  double r[3];
184  ug->GetPoints()->GetPoint(i, r);
185  x[i] = r[0];
186  y[i] = r[1];
187 
188  const double *tuple = ug->GetPointData()->GetArray(0)->GetTuple(i);
189 
190  imageR[i] = tuple[0];
191  imageG[i] = tuple[1];
192  imageB[i] = tuple[2];
193  }
194 
195  int cell_type = ug->GetCell(0)->GetCellType();
196  assert(cell_type==VTK_TRIANGLE);
197 
198  int nloc = 3;
199  int ndims = 2;
200 
201  size_t NElements = ug->GetNumberOfCells();
202 
203  std::vector<int> ENList(NElements*nloc);
204  for(size_t i=0;i<NElements;i++){
205  vtkCell *cell = ug->GetCell(i);
206  assert(cell->GetCellType()==cell_type);
207 
208  for(int j=0;j<nloc;j++){
209  ENList[i*nloc+j] = cell->GetPointId(j);
210  }
211  }
212 
213  int nparts=1;
214  Mesh<double> *mesh=NULL;
215 
216  // Handle mpi parallel run.
217  MPI_Comm_size(MPI_COMM_WORLD, &nparts);
218 
219  if(nparts>1){
220  std::vector<index_t> owner_range;
221  std::vector<index_t> lnn2gnn;
222  std::vector<int> node_owner;
223 
224  std::vector<int> epart(NElements, 0), npart(NNodes, 0);
225  int rank;
226  MPI_Comm_rank(MPI_COMM_WORLD, &rank);
227 
228  if(rank==0){
229  int edgecut;
230 
231  std::vector<int> eind(NElements*nloc);
232  eind[0] = 0;
233  for(size_t i=0;i<NElements*nloc;i++)
234  eind[i] = ENList[i];
235 
236  int intNElements = NElements;
237  int intNNodes = NNodes;
238 
239 #ifdef METIS_VER_MAJOR
240  int vsize = nloc - 1;
241  std::vector<int> eptr(NElements+1);
242  for(size_t i=0;i<NElements;i++)
243  eptr[i+1] = eptr[i]+nloc;
244  METIS_PartMeshNodal(&intNElements,
245  &intNNodes,
246  &(eptr[0]),
247  &(eind[0]),
248  NULL,
249  &vsize,
250  &nparts,
251  NULL,
252  NULL,
253  &edgecut,
254  &(epart[0]),
255  &(npart[0]));
256 #else
257  std::vector<int> etype(NElements);
258  for(size_t i=0;i<NElements;i++)
259  etype[i] = ndims-1;
260  int numflag = 0;
261  METIS_PartMeshNodal(&intNElements,
262  &intNNodes,
263  &(eind[0]),
264  &(etype[0]),
265  &numflag,
266  &nparts,
267  &edgecut,
268  &(epart[0]),
269  &(npart[0]));
270 #endif
271  }
272 
273  mpi_type_wrapper<index_t> mpi_index_t_wrapper;
274  MPI_Datatype MPI_INDEX_T = mpi_index_t_wrapper.mpi_type;
275 
276  MPI_Bcast(&(epart[0]), NElements, MPI_INDEX_T, 0, MPI_COMM_WORLD);
277  MPI_Bcast(&(npart[0]), NNodes, MPI_INDEX_T, 0, MPI_COMM_WORLD);
278 
279  // Separate out owned nodes.
280  std::vector< std::vector<index_t> > node_partition(nparts);
281  for(size_t i=0;i<NNodes;i++)
282  node_partition[npart[i]].push_back(i);
283 
284 #ifdef HAVE_BOOST_UNORDERED_MAP_HPP
285  boost::unordered_map<index_t, index_t> renumber;
286 #else
287  std::map<index_t, index_t> renumber;
288 #endif
289  {
290  index_t pos=0;
291  owner_range.push_back(0);
292  for(int i=0;i<nparts;i++){
293  int pNNodes = node_partition[i].size();
294  owner_range.push_back(owner_range[i]+pNNodes);
295  for(int j=0;j<pNNodes;j++)
296  renumber[node_partition[i][j]] = pos++;
297  }
298  }
299  std::vector<index_t> element_partition;
300  std::set<index_t> halo_nodes;
301  for(size_t i=0;i<NElements;i++){
302  std::set<index_t> residency;
303  for(int j=0;j<nloc;j++)
304  residency.insert(npart[ENList[i*nloc+j]]);
305 
306  if(residency.count(rank)){
307  element_partition.push_back(i);
308 
309  for(int j=0;j<nloc;j++){
310  index_t nid = ENList[i*nloc+j];
311  if(npart[nid]!=rank)
312  halo_nodes.insert(nid);
313  }
314  }
315  }
316 
317  // Append halo nodes to local node partition.
318  for(typename std::set<index_t>::const_iterator it=halo_nodes.begin();it!=halo_nodes.end();++it){
319  node_partition[rank].push_back(*it);
320  }
321 
322  // Global numbering to partition numbering look up table.
323  NNodes = node_partition[rank].size();
324  lnn2gnn.resize(NNodes);
325  node_owner.resize(NNodes);
326  for(size_t i=0;i<NNodes;i++){
327  index_t nid = node_partition[rank][i];
328  index_t gnn = renumber[nid];
329  lnn2gnn[i] = gnn;
330  node_owner[i] = npart[nid];
331  }
332 
333  // Construct local mesh.
334  std::vector<double> lx(NNodes), ly(NNodes), lz(NNodes), limageR(NNodes), limageG(NNodes), limageB(NNodes);
335  for(size_t i=0;i<NNodes;i++){
336  lx[i] = x[node_partition[rank][i]];
337  ly[i] = y[node_partition[rank][i]];
338 
339  limageR[i] = imageR[node_partition[rank][i]];
340  limageG[i] = imageG[node_partition[rank][i]];
341  limageB[i] = imageB[node_partition[rank][i]];
342  }
343 
344  NElements = element_partition.size();
345  std::vector<index_t> lENList(NElements*nloc);
346  for(size_t i=0;i<NElements;i++){
347  for(int j=0;j<nloc;j++){
348  index_t nid = renumber[ENList[element_partition[i]*nloc+j]];
349  lENList[i*nloc+j] = nid;
350  }
351  }
352 
353  // Swap
354  x.swap(lx);
355  y.swap(ly);
356  imageR.swap(limageR);
357  imageG.swap(limageG);
358  imageB.swap(limageB);
359  ENList.swap(lENList);
360 
361  MPI_Comm comm = MPI_COMM_WORLD;
362 
363  mesh = new Mesh<double>(NNodes, NElements, &(ENList[0]), &(x[0]), &(y[0]), &(lnn2gnn[0]), &(owner_range[0]), comm);
364  }else{
365  mesh = new Mesh<double>(NNodes, NElements, &(ENList[0]), &(x[0]), &(y[0]));
366  }
367 
368  mesh->create_boundary();
369 
370  MetricField<double, 2> metric_field(*mesh);
371 
372  double time_metric = get_wtime();
373  metric_field.add_field(imageR.data(), 5.0, 1);
374  std::cout<<"Predicted number of elementsR: "<<metric_field.predict_nelements()<<std::endl;
375  // metric_field.add_field(imageG.data(), 5.0, 3);
376  // std::cout<<"Predicted number of elementsG: "<<metric_field.predict_nelements()<<std::endl;
377  // metric_field.add_field(imageB.data(), 1.0, 3);
378  // std::cout<<"Predicted number of elementsB: "<<metric_field.predict_nelements()<<std::endl;
379 
380  metric_field.apply_min_edge_length(0.5);
381  std::cout<<"Predicted number of elements1: "<<metric_field.predict_nelements()<<std::endl;
382 
383  // metric_field.apply_max_aspect_ratio(10.0);
384  // std::cout<<"Predicted number of elements1: "<<metric_field.predict_nelements()<<std::endl;
385 
386  // metric_field.apply_max_nelements(std::max(2, (int)NElements/2));
387  // std::cout<<"Predicted number of elements2: "<<metric_field.predict_nelements()<<std::endl;
388  // metric_field.apply_max_edge_length(10.0);
389  // std::cout<<"Predicted number of elements3: "<<metric_field.predict_nelements()<<std::endl;
390 
391  time_metric = (get_wtime() - time_metric);
392 
393  metric_field.update_mesh();
394 
395  if(verbose){
396  cout_quality(mesh, "Initial quality");
397  VTKTools<double>::export_vtu("initial_mesh_3d", mesh);
398  }
399 
400  double L_up = sqrt(2.0);
401 
402  Coarsen<double, 2> coarsen(*mesh);
403  Smooth<double, 2> smooth(*mesh);
404  Swapping<double, 2> swapping(*mesh);
405 
406  double time_coarsen=0, time_swapping=0;
407  for(size_t i=0;i<5;i++){
408  if(verbose)
409  std::cout<<"INFO: Sweep "<<i<<std::endl;
410 
411  double tic = get_wtime();
412  coarsen.coarsen(L_up, L_up, true);
413  time_coarsen += (get_wtime()-tic);
414  if(verbose)
415  cout_quality(mesh, "Quality after coarsening");
416 
417  tic = get_wtime();
418  swapping.swap(0.1);
419  time_swapping += (get_wtime()-tic);
420  if(verbose)
421  cout_quality(mesh, "Quality after swapping");
422  }
423 
424  mesh->defragment();
425 
426  double time_smooth=get_wtime();
427  smooth.smart_laplacian(20);
428  smooth.optimisation_linf(20);
429  time_smooth = (get_wtime() - time_smooth);
430  if(verbose)
431  cout_quality(mesh, "Quality after final smoothening");
432 
433  if(verbose)
434  std::cout<<"Times for metric, coarsen, swapping, smoothing = "<<time_metric<<", "<<time_coarsen<<", "<<time_swapping<<", "<<time_smooth<<std::endl;
435 
436  if(outfilename.size()==0)
437  VTKTools<double>::export_vtu("scaled_mesh_3d", mesh);
438  else
439  VTKTools<double>::export_vtu(outfilename.c_str(), mesh);
440 
441  delete mesh;
442 
443  MPI_Finalize();
444 
445  return 0;
446 }
void smart_laplacian(int max_iterations=10, double quality_tol=-1.0)
Definition: Smooth.h:111
void update_mesh()
Update the metric field on the mesh.
Definition: MetricField.h:394
void create_boundary()
Definition: Mesh.h:204
void cout_quality(const Mesh< double > *mesh, std::string operation)
Definition: jpeg2mesh.cpp:123
Performs 2D mesh coarsening.
Definition: Coarsen.h:59
int index_t
double get_qmean() const
Get the element mean quality in metric space.
Definition: Mesh.h:764
Performs edge/face swapping.
Definition: Swapping.h:56
Manages mesh data.
Definition: Mesh.h:70
double get_qmin() const
Get the element minimum quality in metric space.
Definition: Mesh.h:825
static void export_vtu(const char *basename, const Mesh< real_t > *mesh, const real_t *psi=NULL)
Definition: VTKTools.h:356
void coarsen(real_t L_low, real_t L_max, bool enable_sliver_deletion=false)
Definition: Coarsen.h:126
Constructs metric tensor field which encodes anisotropic edge size information.
Definition: MetricField.h:73
void add_field(const real_t *psi, const real_t target_error, int p_norm=-1)
Definition: MetricField.h:454
int parse_arguments(int argc, char **argv, std::string &infilename, bool &verbose, double &factor)
Definition: jpeg2mesh.cpp:62
real_t predict_nelements()
Definition: MetricField.h:756
double get_wtime()
Definition: ticker.cpp:34
void usage(char *cmd)
Definition: jpeg2mesh.cpp:53
void apply_min_edge_length(real_t min_len)
Definition: MetricField.h:565
void swap(real_t quality_tolerance)
Definition: Swapping.h:124
const MPI_Datatype mpi_type
Definition: mpi_tools.h:46
int main(int argc, char **argv)
Definition: jpeg2mesh.cpp:134
Toolkit for importing and exporting VTK files. This is mostly used as part of the internal unit testi...
Definition: VTKTools.h:95
Applies Laplacian smoothen in metric space.
Definition: Smooth.h:67
void defragment()
Definition: Mesh.h:958
void optimisation_linf(int max_iterations=10, double quality_tol=-1.0)
Definition: Smooth.h:240