www.mooseframework.org
ImageMesh.C
Go to the documentation of this file.
1 //* This file is part of the MOOSE framework
2 //* https://www.mooseframework.org
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 "ImageMesh.h"
11 #include "pcrecpp.h"
12 #include "MooseApp.h"
13 
14 #include <cstdlib> // std::system, mkstemp
15 #include <fstream>
16 
17 #include "libmesh/mesh_generation.h"
18 #include "libmesh/unstructured_mesh.h"
19 
20 registerMooseObject("MooseApp", ImageMesh);
21 
22 template <>
25 {
28  params.addClassDescription("Generated mesh with the aspect ratio of a given image stack.");
29 
30  // Add ImageMesh-specific params
31  params.addParam<bool>(
32  "scale_to_one", true, "Whether or not to scale the image so its max dimension is 1");
33  params.addRangeCheckedParam<Real>("cells_per_pixel",
34  1.0,
35  "cells_per_pixel<=1.0",
36  "The number of mesh cells per pixel, must be <=1 ");
37 
38  return params;
39 }
40 
42  : GeneratedMesh(parameters),
43  FileRangeBuilder(parameters),
44  _scale_to_one(getParam<bool>("scale_to_one")),
45  _cells_per_pixel(getParam<Real>("cells_per_pixel"))
46 {
47  // Failure is not an option
48  errorCheck();
49 }
50 
51 ImageMesh::ImageMesh(const ImageMesh & other_mesh)
52  : GeneratedMesh(other_mesh),
53  FileRangeBuilder(other_mesh.parameters()),
54  _scale_to_one(getParam<bool>("scale_to_one")),
55  _cells_per_pixel(getParam<Real>("cells_per_pixel"))
56 {
57 }
58 
59 std::unique_ptr<MooseMesh>
61 {
62  return libmesh_make_unique<ImageMesh>(*this);
63 }
64 
65 void
67 {
68  // A list of filenames of length 1 means we are building a 2D mesh
69  if (_filenames.size() == 1)
71 
72  else
74 }
75 
76 void
77 ImageMesh::buildMesh3D(const std::vector<std::string> & filenames)
78 {
79  // If the user gave us a "stack" with 0 or 1 files in it, we can't
80  // really create a 3D Mesh from that
81  if (filenames.size() <= 1)
82  mooseError("ImageMesh error: Cannot create a 3D ImageMesh from an image stack with ",
83  filenames.size(),
84  " images.");
85 
86  // For each file in the stack, process it using the 'file' command.
87  // We want to be sure that all the images in the stack are the same
88  // size, for example...
89  int xpixels = 0, ypixels = 0, zpixels = filenames.size();
90 
91  // Take pixel info from the first image in the stack to determine the aspect ratio
92  GetPixelInfo(filenames[0], xpixels, ypixels);
93 
94  // TODO: Check that all images are the same aspect ratio and have
95  // the same number of pixels? ImageFunction does not currently do
96  // this...
97  // for (const auto & filename : filenames)
98  // {
99  // // Extract the number of pixels from the image using the file command
100  // GetPixelInfo(filename, xpixels, ypixels);
101  //
102  // // Moose::out << "Image " << filename << " has size: " << xpixels << " by " << ypixels <<
103  // std::endl;
104  // }
105 
106  // Use the number of x and y pixels and the number of images to
107  // determine the the x, y, and z dimensions of the mesh. We assume
108  // that there is 1 pixel in the z-direction for each image in the
109  // stack.
110 
111  // Set the maximum dimension to 1.0 while scaling the other
112  // directions to maintain the aspect ratio.
113  _xmax = xpixels;
114  _ymax = ypixels;
115  _zmax = zpixels;
116 
117  if (_scale_to_one)
118  {
119  Real max = std::max(std::max(_xmax, _ymax), _zmax);
120  _xmax /= max;
121  _ymax /= max;
122  _zmax /= max;
123  }
124 
125  // Compute the number of cells in the x and y direction based on
126  // the user's cells_per_pixel parameter. Note: we use ints here
127  // because the GeneratedMesh params object uses ints for these...
128  _nx = static_cast<int>(_cells_per_pixel * xpixels);
129  _ny = static_cast<int>(_cells_per_pixel * ypixels);
130  _nz = static_cast<int>(_cells_per_pixel * zpixels);
131 
132  // Actually build the Mesh
133  MeshTools::Generation::build_cube(dynamic_cast<UnstructuredMesh &>(getMesh()),
134  _nx,
135  _ny,
136  _nz,
137  /*xmin=*/0.,
138  /*xmax=*/_xmax,
139  /*ymin=*/0.,
140  /*ymax=*/_ymax,
141  /*zmin=*/0.,
142  /*zmax=*/_zmax,
143  HEX8);
144 }
145 
146 void
147 ImageMesh::buildMesh2D(const std::string & filename)
148 {
149  int xpixels = 0, ypixels = 0;
150 
151  // Extract the number of pixels from the image using the file command
152  GetPixelInfo(filename, xpixels, ypixels);
153 
154  // Set the maximum dimension to 1.0 while scaling the other
155  // direction to maintain the aspect ratio.
156  _xmax = xpixels;
157  _ymax = ypixels;
158 
159  if (_scale_to_one)
160  {
161  Real max = std::max(_xmax, _ymax);
162  _xmax /= max;
163  _ymax /= max;
164  }
165 
166  // Compute the number of cells in the x and y direction based on
167  // the user's cells_per_pixel parameter. Note: we use ints here
168  // because the GeneratedMesh params object uses ints for these...
169  _nx = static_cast<int>(_cells_per_pixel * xpixels);
170  _ny = static_cast<int>(_cells_per_pixel * ypixels);
171 
172  // Actually build the Mesh
173  MeshTools::Generation::build_square(dynamic_cast<UnstructuredMesh &>(getMesh()),
174  _nx,
175  _ny,
176  /*xmin=*/0.,
177  /*xmax=*/_xmax,
178  /*ymin=*/0.,
179  /*ymax=*/_ymax,
180  QUAD4);
181 }
182 
183 void
184 ImageMesh::GetPixelInfo(std::string filename, int & xpixels, int & ypixels)
185 {
186  // For reporting possible error messages
187  std::string error_message = "";
188 
189  // A template for creating a temporary file.
190  char temp_file[] = "file_command_output.XXXXXX";
191 
192  // Use a do-loop so we can break out under various error conditions
193  // while still cleaning up temporary files. Basically use goto
194  // statements without actually using them.
195  do
196  {
197  // mkstemp is not in namespace std for whatever reason...
198  int fd = mkstemp(temp_file);
199 
200  // If mkstemp fails, we failed.
201  if (fd == -1)
202  {
203  error_message = "Error creating temporary file in ImageMesh::buildMesh()";
204  break;
205  }
206 
207  // Construct the command string
208  std::ostringstream command;
209  command << "file " << filename << " 2>/dev/null 1>" << temp_file;
210 
211  // Make the system call, catch the return code
212  int exit_status = std::system(command.str().c_str());
213 
214  // If the system command returned a non-zero status, we failed.
215  if (exit_status != 0)
216  {
217  error_message = "Error calling 'file' command in ImageMesh::buildMesh()";
218  break;
219  }
220 
221  // Open the file which contains the result of the system command
222  std::ifstream fin(temp_file);
223 
224  // Read the contents of the output file into a string
225  std::string command_result;
226  std::getline(fin, command_result);
227 
228  // A regular expression which matches "NNN x NNN" , i.e. any number
229  // of digits, a space, an 'x', a space, and any number of digits.
230  // The parentheses define capture groups which are stored into the
231  // xsize and ysize integers.
232  // Here's an example string:
233  // sixteenth_image001_cropped3_closing_298.png: PNG image data, 115 x 99, 16-bit/color RGB,
234  // non-interlaced
235  xpixels = 0, ypixels = 0;
236  pcrecpp::RE re("(\\d+) x (\\d+)");
237  re.PartialMatch(command_result, &xpixels, &ypixels);
238 
239  // Detect failure of the regex
240  if ((xpixels == 0) || (ypixels == 0))
241  {
242  error_message = "Regex failed to find a match in " + command_result;
243  break;
244  }
245  } while (false);
246 
247  // Remove the temporary file. This will still work even if the file was never created...
248  std::remove(temp_file);
249 
250  // Report and exit if there was an error
251  if (error_message != "")
252  mooseError(error_message);
253 }
void build_cube(UnstructuredMesh &mesh, const unsigned int nx, unsigned int ny, unsigned int nz, const Real xmin, const Real xmax, const Real ymin, const Real ymax, const Real zmin, const Real zmax, const ElemType type, bool verbose)
void buildMesh3D(const std::vector< std::string > &filenames)
buildMesh() calls this helper function to build 3D ImageMeshes from stacks of images.
Definition: ImageMesh.C:77
ImageMesh(const InputParameters &parameters)
Definition: ImageMesh.C:41
virtual std::unique_ptr< MooseMesh > safeClone() const override
A safer version of the clone() method that hands back an allocated object wrapped in a smart pointer...
Definition: ImageMesh.C:60
const std::vector< std::string > & filenames()
The main MOOSE class responsible for handling user-defined parameters in almost every MOOSE system...
void mooseError(Args &&... args) const
Definition: MooseObject.h:147
void buildMesh2D(const std::string &filename)
buildMesh() calls this helper function to build 2D ImageMeshes.
Definition: ImageMesh.C:147
nl system()
Mesh generated from parameters.
Definition: GeneratedMesh.h:22
InputParameters validParams< GeneratedMesh >()
Definition: GeneratedMesh.C:27
MeshBase & getMesh()
Accessor for the underlying libMesh Mesh object.
Definition: MooseMesh.C:2567
virtual void buildMesh() override
Must be overridden by child classes.
Definition: ImageMesh.C:66
unsigned int _ny
Definition: GeneratedMesh.h:43
InputParameters validParams< FileRangeBuilder >()
To be called in the validParams functions of classes that need to operate on ranges of files...
Augments an InputParameters object with file range information.
unsigned int _nz
Definition: GeneratedMesh.h:43
registerMooseObject("MooseApp", ImageMesh)
const Real & _cells_per_pixel
A number <= 1.0 which determines the number of cells in the mesh per pixel in each direction...
Definition: ImageMesh.h:64
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...
unsigned int _nx
Number of elements in x, y, z direction.
Definition: GeneratedMesh.h:43
const bool _scale_to_one
If true, forces the maximum width (height) of the mesh to be 1.0 while retaining the original aspect ...
Definition: ImageMesh.h:54
InputParameters validParams< ImageMesh >()
Definition: ImageMesh.C:24
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...
Definition: ImageMesh.C:184
A 2D GeneratedMesh where xmin, xmax, etc.
Definition: ImageMesh.h:23
std::vector< std::string > _filenames