https://mooseframework.inl.gov
ImageMeshGenerator.C
Go to the documentation of this file.
1 //* This file is part of the MOOSE framework
2 //* https://mooseframework.inl.gov
3 //*
4 //* All rights reserved, see COPYRIGHT for full restrictions
5 //* https://github.com/idaholab/moose/blob/master/COPYRIGHT
6 //*
7 //* Licensed under LGPL 2.1, please see LICENSE for details
8 //* https://www.gnu.org/licenses/lgpl-2.1.html
9 
10 #include "ImageMeshGenerator.h"
11 #include "pcrecpp.h"
12 #include "MooseApp.h"
13 #include "CastUniquePointer.h"
14 
15 #include "libmesh/unstructured_mesh.h"
16 #include "libmesh/replicated_mesh.h"
17 #include "libmesh/mesh_generation.h"
18 #include "libmesh/face_quad4.h"
19 #include "libmesh/cell_hex8.h"
20 
21 #include <cstdlib> // std::system, mkstemp
22 #include <fstream>
23 
25 
28 {
31  params.addClassDescription("Generated mesh with the aspect ratio of a given image stack.");
32 
33  // Add ImageMeshGenerator-specific params
34  params.addParam<bool>(
35  "scale_to_one", true, "Whether or not to scale the image so its max dimension is 1");
36  params.addRangeCheckedParam<Real>("cells_per_pixel",
37  1.0,
38  "cells_per_pixel<=1.0",
39  "The number of mesh cells per pixel, must be <=1 ");
40 
41  return params;
42 }
43 
45  : GeneratedMeshGenerator(parameters),
46  FileRangeBuilder(parameters),
47  _scale_to_one(getParam<bool>("scale_to_one")),
48  _cells_per_pixel(getParam<Real>("cells_per_pixel"))
49 {
50  declareMeshProperty("use_distributed_mesh", false);
51 }
52 
53 std::unique_ptr<MeshBase>
55 {
56  auto mesh = buildReplicatedMesh();
57 
58  // A list of filenames of length 1 means we are building a 2D mesh
59  if (_filenames.size() == 1)
61 
62  else
64 
65  return dynamic_pointer_cast<MeshBase>(mesh);
66 }
67 
68 void
69 ImageMeshGenerator::buildMesh3D(const std::vector<std::string> & filenames, MeshBase & mesh)
70 {
71  // If the user gave us a "stack" with 0 or 1 files in it, we can't
72  // really create a 3D Mesh from that
73  if (filenames.size() <= 1)
74  mooseError("ImageMesh error: Cannot create a 3D ImageMesh from an image stack with ",
75  filenames.size(),
76  " images.");
77 
78  // For each file in the stack, process it using the 'file' command.
79  // We want to be sure that all the images in the stack are the same
80  // size, for example...
81  int xpixels = 0, ypixels = 0, zpixels = filenames.size();
82 
83  // Take pixel info from the first image in the stack to determine the aspect ratio
84  GetPixelInfo(filenames[0], xpixels, ypixels);
85 
86  // TODO: Check that all images are the same aspect ratio and have
87  // the same number of pixels? ImageFunction does not currently do
88  // this...
89  // for (const auto & filename : filenames)
90  // {
91  // // Extract the number of pixels from the image using the file command
92  // GetPixelInfo(filename, xpixels, ypixels);
93  //
94  // // Moose::out << "Image " << filename << " has size: " << xpixels << " by " << ypixels <<
95  // std::endl;
96  // }
97 
98  // Use the number of x and y pixels and the number of images to
99  // determine the the x, y, and z dimensions of the mesh. We assume
100  // that there is 1 pixel in the z-direction for each image in the
101  // stack.
102 
103  // Set the maximum dimension to 1.0 while scaling the other
104  // directions to maintain the aspect ratio.
105  _xmax = xpixels;
106  _ymax = ypixels;
107  _zmax = zpixels;
108 
109  if (_scale_to_one)
110  {
112  _xmax /= max;
113  _ymax /= max;
114  _zmax /= max;
115  }
116 
117  // Compute the number of cells in the x and y direction based on
118  // the user's cells_per_pixel parameter. Note: we use ints here
119  // because the GeneratedMesh params object uses ints for these...
120  _nx = static_cast<int>(_cells_per_pixel * xpixels);
121  _ny = static_cast<int>(_cells_per_pixel * ypixels);
122  _nz = static_cast<int>(_cells_per_pixel * zpixels);
123 
124  // Actually build the Mesh
125  MeshTools::Generation::build_cube(dynamic_cast<UnstructuredMesh &>(mesh),
126  _nx,
127  _ny,
128  _nz,
129  /*xmin=*/0.,
130  /*xmax=*/_xmax,
131  /*ymin=*/0.,
132  /*ymax=*/_ymax,
133  /*zmin=*/0.,
134  /*zmax=*/_zmax,
135  HEX8);
136 }
137 
138 void
139 ImageMeshGenerator::buildMesh2D(const std::string & filename, MeshBase & mesh)
140 {
141  int xpixels = 0, ypixels = 0;
142 
143  // Extract the number of pixels from the image using the file command
144  GetPixelInfo(filename, xpixels, ypixels);
145 
146  // Set the maximum dimension to 1.0 while scaling the other
147  // direction to maintain the aspect ratio.
148  _xmax = xpixels;
149  _ymax = ypixels;
150 
151  if (_scale_to_one)
152  {
154  _xmax /= max;
155  _ymax /= max;
156  }
157 
158  // Compute the number of cells in the x and y direction based on
159  // the user's cells_per_pixel parameter. Note: we use ints here
160  // because the GeneratedMesh params object uses ints for these...
161  _nx = static_cast<int>(_cells_per_pixel * xpixels);
162  _ny = static_cast<int>(_cells_per_pixel * ypixels);
163 
164  // Actually build the Mesh
165  MeshTools::Generation::build_square(dynamic_cast<UnstructuredMesh &>(mesh),
166  _nx,
167  _ny,
168  /*xmin=*/0.,
169  /*xmax=*/_xmax,
170  /*ymin=*/0.,
171  /*ymax=*/_ymax,
172  QUAD4);
173 }
174 
175 void
176 ImageMeshGenerator::GetPixelInfo(std::string filename, int & xpixels, int & ypixels)
177 {
178  // For reporting possible error messages
179  std::string error_message = "";
180 
181  // A template for creating a temporary file.
182  char temp_file[] = "file_command_output.XXXXXX";
183 
184  // Use a do-loop so we can break out under various error conditions
185  // while still cleaning up temporary files. Basically use goto
186  // statements without actually using them.
187  do
188  {
189  // mkstemp is not in namespace std for whatever reason...
190  int fd = mkstemp(temp_file);
191 
192  // If mkstemp fails, we failed.
193  if (fd == -1)
194  {
195  error_message = "Error creating temporary file in ImageMesh::buildMesh()";
196  break;
197  }
198 
199  // Construct the command string
200  std::ostringstream command;
201  command << "file " << filename << " 2>/dev/null 1>" << temp_file;
202 
203  // Make the system call, catch the return code
204  int exit_status = std::system(command.str().c_str());
205 
206  // If the system command returned a non-zero status, we failed.
207  if (exit_status != 0)
208  {
209  error_message = "Error calling 'file' command in ImageMesh::buildMesh()";
210  break;
211  }
212 
213  // Open the file which contains the result of the system command
214  std::ifstream fin(temp_file);
215 
216  // Read the contents of the output file into a string
217  std::string command_result;
218  std::getline(fin, command_result);
219 
220  // A regular expression which matches "NNN x NNN" , i.e. any number
221  // of digits, a space, an 'x', a space, and any number of digits.
222  // The parentheses define capture groups which are stored into the
223  // xsize and ysize integers.
224  // Here's an example string:
225  // sixteenth_image001_cropped3_closing_298.png: PNG image data, 115 x 99, 16-bit/color RGB,
226  // non-interlaced
227  xpixels = 0, ypixels = 0;
228  pcrecpp::RE re("(\\d+) x (\\d+)");
229  re.PartialMatch(command_result, &xpixels, &ypixels);
230 
231  // Detect failure of the regex
232  if ((xpixels == 0) || (ypixels == 0))
233  {
234  error_message = "Regex failed to find a match in " + command_result;
235  break;
236  }
237  } while (false);
238 
239  // Remove the temporary file. This will still work even if the file was never created...
240  std::remove(temp_file);
241 
242  // Report and exit if there was an error
243  if (error_message != "")
244  mooseError(error_message);
245 }
static InputParameters validParams()
const bool _scale_to_one
If true, forces the maximum width (height) of the mesh to be 1.0 while retaining the original aspect ...
void GetPixelInfo(std::string filename, int &xpixels, int &ypixels)
Process a single image with the &#39;file&#39; command to find out the number of pixels in the x and y direct...
HEX8
std::unique_ptr< ReplicatedMesh > buildReplicatedMesh(unsigned int dim=libMesh::invalid_uint)
Build a replicated mesh.
const std::vector< std::string > & filenames()
MeshBase & mesh
Generates a line, square, or cube mesh with uniformly spaced or biased elements.
The main MOOSE class responsible for handling user-defined parameters in almost every MOOSE system...
std::unique_ptr< T_DEST, T_DELETER > dynamic_pointer_cast(std::unique_ptr< T_SRC, T_DELETER > &src)
These are reworked from https://stackoverflow.com/a/11003103.
void buildMesh3D(const std::vector< std::string > &filenames, MeshBase &mesh)
generate() calls this helper function to build 3D ImageMeshes from stacks of images.
static InputParameters validParams()
auto max(const L &left, const R &right)
QUAD4
static InputParameters validParams()
int mkstemp(char *tmpl)
std::unique_ptr< MeshBase > generate() override
Generate / modify the mesh.
ImageMeshGenerator(const InputParameters &parameters)
unsigned int & _nx
Number of elements in x, y, z direction.
void buildMesh2D(const std::string &filename, MeshBase &mesh)
generate() calls this helper function to build 2D ImageMeshes.
To be called in the validParams functions of classes that need to operate on ranges of files...
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
const Real & _cells_per_pixel
A number <= 1.0 which determines the number of cells in the mesh per pixel in each direction...
registerMooseObject("MooseApp", ImageMeshGenerator)
void mooseError(Args &&... args) const
Emits an error prefixed with object name and type.
void addClassDescription(const std::string &doc_string)
This method adds a description of the class that will be displayed in the input file syntax dump...
T & declareMeshProperty(const std::string &data_name, Args &&... args)
Methods for writing out attributes to the mesh meta-data store, which can be retrieved from most othe...
void addParam(const std::string &name, const S &value, const std::string &doc_string)
These methods add an optional parameter and a documentation string to the InputParameters object...
void addRangeCheckedParam(const std::string &name, const T &value, const std::string &parsed_function, const std::string &doc_string)
A 2D GeneratedMesh where xmin, xmax, etc.
std::vector< std::string > _filenames