https://mooseframework.inl.gov
Loading...
Searching...
No Matches
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
53std::unique_ptr<MeshBase>
55{
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
68void
69ImageMeshGenerator::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 {
111 Real max = std::max(std::max(_xmax, _ymax), _zmax);
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
138void
139ImageMeshGenerator::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 {
153 Real max = std::max(_xmax, _ymax);
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
175void
176ImageMeshGenerator::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}
registerMooseObject("MooseApp", ImageMeshGenerator)
void mooseError(Args &&... args)
Emit an error message with the given stringified, concatenated args and terminate the application.
Definition MooseError.h:311
To be called in the validParams functions of classes that need to operate on ranges of files.
const std::vector< std::string > & filenames()
static InputParameters validParams()
std::vector< std::string > _filenames
Generates a line, square, or cube mesh with uniformly spaced or biased elements.
static InputParameters validParams()
unsigned int & _nx
Number of elements in x, y, z direction.
A 2D GeneratedMesh where xmin, xmax, etc.
void buildMesh2D(const std::string &filename, MeshBase &mesh)
generate() calls this helper function to build 2D ImageMeshes.
void GetPixelInfo(std::string filename, int &xpixels, int &ypixels)
Process a single image with the 'file' command to find out the number of pixels in the x and y direct...
ImageMeshGenerator(const InputParameters &parameters)
void buildMesh3D(const std::vector< std::string > &filenames, MeshBase &mesh)
generate() calls this helper function to build 3D ImageMeshes from stacks of images.
const bool _scale_to_one
If true, forces the maximum width (height) of the mesh to be 1.0 while retaining the original aspect ...
std::unique_ptr< MeshBase > generate() override
Generate / modify the mesh.
const Real & _cells_per_pixel
A number <= 1.0 which determines the number of cells in the mesh per pixel in each direction.
static InputParameters validParams()
The main MOOSE class responsible for handling user-defined parameters in almost every MOOSE system.
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 addClassDescription(const std::string &doc_string)
This method adds a description of the class that will be displayed in the input file syntax dump.
void addRangeCheckedParam(const std::string &name, const T &value, const std::string &parsed_function, const std::string &doc_string)
std::unique_ptr< ReplicatedMesh > buildReplicatedMesh(unsigned int dim=libMesh::invalid_uint)
Build a replicated mesh.
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...
MeshBase & mesh
int mkstemp(char *tmpl)