Skip to content

Commit 77ab7aa

Browse files
committed
initial work on post processor that highlights cracks forming in materials
1 parent c17c292 commit 77ab7aa

File tree

4 files changed

+281
-18
lines changed

4 files changed

+281
-18
lines changed

src/core/DICe_PostProcessor.cpp

Lines changed: 222 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,11 @@
4444
#include <Teuchos_LAPACK.hpp>
4545
#include <Teuchos_SerialDenseMatrix.hpp>
4646

47+
#include <opencv2/core/core.hpp>
48+
#include <opencv2/highgui/highgui.hpp>
49+
#include <opencv2/imgcodecs.hpp>
50+
#include <opencv2/imgproc.hpp>
51+
4752
#include <fstream>
4853

4954
namespace DICe {
@@ -230,6 +235,216 @@ Post_Processor::initialize_neighborhood(const scalar_t & neighborhood_radius){
230235
DEBUG_MSG("Post_Processor::initialize_neighborhood(): end");
231236
}
232237

238+
Crack_Locator_Post_Processor::Crack_Locator_Post_Processor(const Teuchos::RCP<Teuchos::ParameterList> & params) :
239+
Post_Processor(post_process_crack_locator){
240+
window_size_ = 25;
241+
ref_img_ = Teuchos::null;
242+
// no new fields added since the post processor writes output files instead
243+
DEBUG_MSG("Enabling post processor Crack_Locator_Post_Processor with no new associated fields:");
244+
set_params(params);
245+
}
246+
247+
void
248+
Crack_Locator_Post_Processor::set_params(const Teuchos::RCP<Teuchos::ParameterList> & params){
249+
window_size_ = params->get<int_t>(crack_locator_window_size,25);
250+
DEBUG_MSG("Crack_Locator_Post_Processor set_params(): using window size " << window_size_);
251+
}
252+
253+
void
254+
Crack_Locator_Post_Processor::execute(Teuchos::RCP<Image> ref_img, Teuchos::RCP<Image> def_img){
255+
DEBUG_MSG("Crack_Locator_Post_Processor execute() begin");
256+
assert(ref_img!=Teuchos::null);
257+
assert(def_img!=Teuchos::null);
258+
if(ref_img_==Teuchos::null) ref_img_ = Teuchos::rcp(new DICe::Image(ref_img)); // deep copy the reference image in case it gets modified or swapped later
259+
assert(ref_img_!=Teuchos::null);
260+
// get the image width and height:
261+
const int_t width = ref_img_->width();
262+
const int_t height = ref_img_->height();
263+
264+
// create opencv mat images, start with 16 bit then equalize the histogram
265+
cv::Mat ref = cv::Mat(height,width,CV_32FC1,cv::Scalar(0));
266+
cv::Mat def = cv::Mat(height,width,CV_32FC1,cv::Scalar(0));
267+
cv::Mat map = cv::Mat(height,width,CV_32FC1,cv::Scalar(0));
268+
// for(int_t y=0;y<height;++y){
269+
// for(int_t x=0;x<width;++x){
270+
// ref.at<float>(y,x) = 0.0f;
271+
// def.at<float>(y,x) = 0.0f;
272+
// def.at<float>(y,x) = 0.0f;
273+
// }
274+
// }
275+
276+
277+
// ensure only in serial for now TODO enable parallel post processor for this
278+
const int_t num_procs = mesh_->get_comm()->get_size();
279+
TEUCHOS_TEST_FOR_EXCEPTION(num_procs!=1,std::runtime_error,"Plotly_Contour_Post_Processor only works in serial");
280+
281+
// gather the current coordinates of the subsets with sigma>=0 or all valid points
282+
Teuchos::RCP<MultiField> coords_x = mesh_->get_field(DICe::field_enums::SUBSET_COORDINATES_X_FS);
283+
Teuchos::RCP<MultiField> coords_y = mesh_->get_field(DICe::field_enums::SUBSET_COORDINATES_Y_FS);
284+
Teuchos::RCP<MultiField> disp_x = mesh_->get_field(DICe::field_enums::SUBSET_DISPLACEMENT_X_FS);
285+
Teuchos::RCP<MultiField> disp_y = mesh_->get_field(DICe::field_enums::SUBSET_DISPLACEMENT_Y_FS);
286+
Teuchos::RCP<MultiField> sigma = mesh_->get_field(DICe::field_enums::SIGMA_FS);
287+
288+
// count the number of valid points
289+
int_t num_valid_pts = 0;
290+
for(int_t i=0;i<local_num_points_;++i)
291+
if(sigma->local_value(i)>=0.0)
292+
num_valid_pts++;
293+
std::vector<int_t> local_ids(num_valid_pts,0);
294+
295+
// create neighborhood lists using nanoflann:
296+
DEBUG_MSG("creating the point cloud using nanoflann");
297+
point_cloud_ = Teuchos::rcp(new Point_Cloud_2D<scalar_t>());
298+
point_cloud_->pts.resize(num_valid_pts);
299+
int_t current_pt = 0;
300+
for(int_t i=0;i<local_num_points_;++i){
301+
if(sigma->local_value(i)<0.0) continue;
302+
point_cloud_->pts[current_pt].x = coords_x->local_value(i) + disp_x->local_value(i);
303+
point_cloud_->pts[current_pt].y = coords_y->local_value(i) + disp_y->local_value(i);
304+
local_ids[current_pt++] = i;
305+
}
306+
DEBUG_MSG("building the kd-tree");
307+
Teuchos::RCP<kd_tree_2d_t> kd_tree = Teuchos::rcp(new kd_tree_2d_t(2 /*dim*/, *point_cloud_.get(), nanoflann::KDTreeSingleIndexAdaptorParams(10 /* max leaf */) ) );
308+
kd_tree->buildIndex();
309+
DEBUG_MSG("kd-tree completed");
310+
311+
const int_t N = 3;
312+
int *IPIV = new int[N+1];
313+
int LWORK = N*N;
314+
int INFO = 0;
315+
double *WORK = new double[LWORK];
316+
double *GWORK = new double[10*N];
317+
int *IWORK = new int[LWORK];
318+
// Note, LAPACK does not allow templating on long int or scalar_t...must use int and double
319+
Teuchos::LAPACK<int,double> lapack;
320+
std::vector<std::pair<size_t,scalar_t> > ret_matches;
321+
nanoflann::SearchParams params;
322+
params.sorted = true; // sort by distance in ascending order
323+
const double neigh_rad_sq = window_size_*window_size_;
324+
scalar_t query_pt[2];
325+
// initialize value storage vector of vectors
326+
// iterate each pixel and find the closest displacement using least squares
327+
for(int_t y=0;y<height;++y){
328+
for(int_t x=0;x<width;++x){
329+
query_pt[0] = x;
330+
query_pt[1] = y;
331+
kd_tree->radiusSearch(&query_pt[0],neigh_rad_sq,ret_matches,params);
332+
const int_t num_neigh = ret_matches.size();
333+
if(num_neigh<=3) continue;
334+
// set up the X_t matrices
335+
Teuchos::SerialDenseMatrix<int_t,double> X_t(N,num_neigh, true);
336+
Teuchos::SerialDenseMatrix<int_t,double> X_t_X(N,N,true);
337+
for(int_t i=0;i<num_neigh;++i){
338+
X_t(0,i) = 1.0;
339+
X_t(1,i) = point_cloud_->pts[ret_matches[i].first].x - x;//coords_x->local_value(local_ids[ret_matches[i].first]) - x;
340+
X_t(2,i) = point_cloud_->pts[ret_matches[i].first].y - y;//coords_y->local_value(local_ids[ret_matches[i].first]) - y;
341+
}
342+
// set up X^T*X
343+
for(int_t k=0;k<N;++k){
344+
for(int_t m=0;m<N;++m){
345+
for(int_t j=0;j<num_neigh;++j){
346+
X_t_X(k,m) += X_t(k,j)*X_t(m,j);
347+
}
348+
}
349+
}
350+
// Invert X^T*X
351+
// TODO: remove for performance?
352+
// compute the 1-norm of H:
353+
std::vector<double> colTotals(X_t_X.numCols(),0.0);
354+
for(int_t i=0;i<X_t_X.numCols();++i){
355+
for(int_t j=0;j<X_t_X.numRows();++j){
356+
colTotals[i]+=std::abs(X_t_X(j,i));
357+
}
358+
}
359+
double anorm = 0.0;
360+
for(int_t i=0;i<X_t_X.numCols();++i){
361+
if(colTotals[i] > anorm) anorm = colTotals[i];
362+
}
363+
lapack.GETRF(X_t_X.numRows(),X_t_X.numCols(),X_t_X.values(),X_t_X.numRows(),IPIV,&INFO);
364+
double rcond=0.0; // reciporical condition number
365+
lapack.GECON('1',X_t_X.numRows(),X_t_X.values(),X_t_X.numRows(),anorm,&rcond,GWORK,IWORK,&INFO);
366+
if(rcond < 1.0E-12) continue;
367+
lapack.GETRI(X_t_X.numRows(),X_t_X.values(),X_t_X.numRows(),IPIV,WORK,LWORK,&INFO);
368+
369+
Teuchos::ArrayRCP<double> u(num_neigh,0.0);
370+
Teuchos::ArrayRCP<double> v(num_neigh,0.0);
371+
for(int_t j=0;j<num_neigh;++j){
372+
u[j] = -1.0*disp_x->local_value(local_ids[ret_matches[j].first]);
373+
v[j] = -1.0*disp_y->local_value(local_ids[ret_matches[j].first]);
374+
}
375+
// compute X^T*u
376+
Teuchos::ArrayRCP<double> X_t_u(N,0.0);
377+
Teuchos::ArrayRCP<double> X_t_v(N,0.0);
378+
for(int_t k=0;k<N;++k)
379+
for(int_t j=0;j<num_neigh;++j){
380+
X_t_u[k] += X_t(k,j)*u[j];
381+
X_t_v[k] += X_t(k,j)*v[j];
382+
}
383+
384+
// compute the coeffs
385+
Teuchos::ArrayRCP<double> coeffs_u(N,0.0);
386+
Teuchos::ArrayRCP<double> coeffs_v(N,0.0);
387+
for(int_t k=0;k<N;++k)
388+
for(int_t j=0;j<N;++j){
389+
coeffs_u[k] += X_t_X(k,j)*X_t_u[j];
390+
coeffs_v[k] += X_t_X(k,j)*X_t_v[j];
391+
}
392+
393+
const scalar_t x_loc = x + coeffs_u[0];
394+
const scalar_t y_loc = y + coeffs_v[0];
395+
if(x_loc>=0&&x_loc<width&&y_loc>=0&&y_loc<height){
396+
ref.at<float>(y,x) = (*ref_img_)(x,y);
397+
def.at<float>(y,x) = (*def_img)(x,y);
398+
map.at<float>(y,x) = ref_img_->interpolate_keys_fourth(x_loc,y_loc);
399+
}
400+
} // end y loop
401+
} // end x loop
402+
403+
// TODO write the output json file
404+
delete [] WORK;
405+
delete [] GWORK;
406+
delete [] IWORK;
407+
delete [] IPIV;
408+
409+
ref.convertTo(ref,CV_8UC1);
410+
def.convertTo(def,CV_8UC1);
411+
map.convertTo(map,CV_8UC1);
412+
cv::equalizeHist(ref,ref);
413+
cv::equalizeHist(def,def);
414+
cv::equalizeHist(map,map);
415+
std::stringstream filenameR;
416+
filenameR << "ref_" << current_frame_id_ << ".png";
417+
cv::imwrite(filenameR.str(),ref);
418+
std::stringstream filenameD;
419+
filenameD << "def_" << current_frame_id_ << ".png";
420+
cv::imwrite(filenameD.str(),def);
421+
std::stringstream filenameM;
422+
filenameM << "map_" << current_frame_id_ << ".png";
423+
cv::imwrite(filenameM.str(),map);
424+
425+
for(int_t y=0;y<height;++y){
426+
for(int_t x=0;x<width;++x){
427+
if(def.at<uchar>(y,x)<map.at<uchar>(y,x)){
428+
def.at<uchar>(y,x) = map.at<uchar>(y,x) - def.at<uchar>(y,x);
429+
}else{
430+
def.at<uchar>(y,x) = 0.0f;
431+
}
432+
}
433+
}
434+
// cv::absdiff(def,map,def);
435+
std::stringstream filenameDiff;
436+
filenameDiff << "diff_" << current_frame_id_ << ".png";
437+
cv::imwrite(filenameDiff.str(),def);
438+
439+
// set a threshold on how far away the nearest subset can be
440+
// do a diff using the displacement value
441+
// threshold the diff
442+
// output a json file with a heatmap?
443+
444+
445+
DEBUG_MSG("Crack_Locator_Post_Processor execute() end");
446+
}
447+
233448
Plotly_Contour_Post_Processor::Plotly_Contour_Post_Processor(const Teuchos::RCP<Teuchos::ParameterList> & params) :
234449
Post_Processor(post_process_plotly_contour){
235450
// no new fields added since the post processor writes output files instead
@@ -244,7 +459,7 @@ Plotly_Contour_Post_Processor::set_params(const Teuchos::RCP<Teuchos::ParameterL
244459
}
245460

246461
void
247-
Plotly_Contour_Post_Processor::execute(){
462+
Plotly_Contour_Post_Processor::execute(Teuchos::RCP<Image> ref_img, Teuchos::RCP<Image> def_img){
248463
DEBUG_MSG("Plotly_Contour_Post_Processor execute() begin");
249464

250465
// ensure only in serial for now TODO enable parallel post processor for this
@@ -493,7 +708,7 @@ Plotly_Contour_Post_Processor::execute(){
493708
} // end gy loop
494709
} // end gx loop
495710

496-
// TODO write the output json file
711+
// write the output json file
497712
delete [] WORK;
498713
delete [] GWORK;
499714
delete [] IWORK;
@@ -658,7 +873,7 @@ VSG_Strain_Post_Processor::pre_execution_tasks(){
658873
}
659874

660875
void
661-
VSG_Strain_Post_Processor::execute(){
876+
VSG_Strain_Post_Processor::execute(Teuchos::RCP<Image> ref_img, Teuchos::RCP<Image> def_img){
662877
DEBUG_MSG("VSG_Strain_Post_Processor execute() begin");
663878
if(!neighborhood_initialized_) pre_execution_tasks();
664879

@@ -934,7 +1149,7 @@ NLVC_Strain_Post_Processor::compute_kernel(const scalar_t & dx,
9341149

9351150

9361151
void
937-
NLVC_Strain_Post_Processor::execute(){
1152+
NLVC_Strain_Post_Processor::execute(Teuchos::RCP<Image> ref_img, Teuchos::RCP<Image> def_img){
9381153
DEBUG_MSG("NLVC_Strain_Post_Processor execute() begin");
9391154
if(!neighborhood_initialized_) pre_execution_tasks();
9401155

@@ -1074,7 +1289,7 @@ Altitude_Post_Processor::Altitude_Post_Processor(const Teuchos::RCP<Teuchos::Par
10741289
}
10751290

10761291
void
1077-
Altitude_Post_Processor::execute(){
1292+
Altitude_Post_Processor::execute(Teuchos::RCP<Image> ref_img, Teuchos::RCP<Image> def_img){
10781293
DEBUG_MSG("Altitude_Post_Processor::execute(): begin");
10791294

10801295
// Teuchos::RCP<DICe::MultiField> ground_level_rcp = mesh_->get_field(DICe::field_enums::GROUND_LEVEL_FS);
@@ -1234,7 +1449,7 @@ Uncertainty_Post_Processor::Uncertainty_Post_Processor(const Teuchos::RCP<Teucho
12341449
}
12351450

12361451
void
1237-
Uncertainty_Post_Processor::execute(){
1452+
Uncertainty_Post_Processor::execute(Teuchos::RCP<Image> ref_img, Teuchos::RCP<Image> def_img){
12381453
DEBUG_MSG("Uncertainty_Post_Processor::execute(): begin");
12391454

12401455
Teuchos::RCP<DICe::MultiField> sigma_rcp = mesh_->get_field(DICe::field_enums::SIGMA_FS);
@@ -1586,7 +1801,7 @@ Live_Plot_Post_Processor::pre_execution_tasks(){
15861801
}
15871802

15881803
void
1589-
Live_Plot_Post_Processor::execute(){
1804+
Live_Plot_Post_Processor::execute(Teuchos::RCP<Image> ref_img, Teuchos::RCP<Image> def_img){
15901805
DEBUG_MSG("Live_Plot_Post_Processor execute() begin");
15911806
if(!neighborhood_initialized_) pre_execution_tasks();
15921807

0 commit comments

Comments
 (0)