Program Listing for File PrepFlowMatchData.cxx¶
↰ Return to documentation for file (larflow/PrepFlowMatchData/arxiv/PrepFlowMatchData.cxx
)
#include "PrepFlowMatchData.hh"
#include "core/LArUtil/Geometry.h"
#include "larcv/core/DataFormat/EventSparseImage.h"
#include "larcv/core/DataFormat/SparseImage.h"
#include "larcv/core/DataFormat/EventImage2D.h"
#include "larcv/core/DataFormat/EventChStatus.h"
#include "ublarcvapp/UBImageMod/EmptyChannelAlgo.h"
#include <sstream>
#include <ctime>
namespace larflow {
// ---------------------------------------------------------
// PREP FLOW MATCH MAP CLASS FACTORY
static PrepFlowMatchDataFactory __global_PrepFlowMatchDataFactory__;
// ---------------------------------------------------------
// PREP FLOW MATCH MAP CLASS
PrepFlowMatchData::PrepFlowMatchData( std::string instance_name )
: larcv::ProcessBase(instance_name),
_input_adc_producername(""),
_input_trueflow_producername(""),
_has_mctruth(false),
_use_ana_tree(false),
_use_soft_truth(false),
_positive_example_distance(0),
_use_3plane_constraint(true),
_debug_detailed_output(false),
_use_gapch(false),
_source_plane(-1),
_matchdata_v(nullptr),
_ana_tree(nullptr)
{}
void PrepFlowMatchData::configure( const larcv::PSet& pset ) {
// the source plane index
_source_plane = pset.get<int>("SourcePlane");
// the tree holding the ADC images
_input_adc_producername = pset.get<std::string>("InputADC", "wire");
// the tree holding larcv::ChStatus
_input_chstatus_producername = pset.get<std::string>("InputChStatus", "wire");
// the tree holding the true flow images
_input_trueflow_producername = pset.get<std::string>("InputTrueFlow", "larflow" );
// [not implemented]: truth score is vector with intermediate scores, not a one-hot vector
_use_soft_truth = pset.get<bool>("UseSoftTruthVector",false);
// has MC truth information
_has_mctruth = pset.get<bool>("HasMCTruth",false);
// distance witin which labeled as truth
_positive_example_distance = pset.get<int>("PositiveExampleDistance",5);
// only keep two-plane candidate matches where corresponding pixel in other wire has charge or is in dead region
_use_3plane_constraint = pset.get<bool>("Use3PlaneConstraint",true);
_debug_detailed_output = pset.get<bool>("DetailedDebugOutput",false);
// use gap channels instead of dead channels, if we dont believe the dead channels
_use_gapch = pset.get<bool>("UseGapChannels",false);
useAnaTree(true);
}
void PrepFlowMatchData::initialize() {
_setup_ana_tree();
_extract_wire_overlap_bounds();
_pbadch_v.clear();
}
const std::vector<FlowMatchMap>& PrepFlowMatchData::getMatchData() const {
if ( _matchdata_v==nullptr ) {
LARCV_CRITICAL() << "Need to initialize class first." << std::endl;
throw std::runtime_error("Need to initialize first");
}
return *_matchdata_v;
}
std::string PrepFlowMatchData::getFlowDirName( FlowDir_t flowdir ) {
switch( flowdir ) {
case kU2V:
return "u2v";
break;
case kU2Y:
return "u2y";
break;
case kV2U:
return "v2u";
break;
case kV2Y:
return "v2y";
break;
case kY2U:
return "y2u";
break;
case kY2V:
return "y2v";
break;
case kNumFlows:
return "allflows";
break;
default:
throw std::runtime_error("invalid flow direction");
break;
}
return "oh-oh";
}
bool PrepFlowMatchData::process( larcv::IOManager& mgr ) {
// first we sparsify data,
// then for each flow direction,
// we loop through each source pixel
// and make a list of target image indices that the source pixel could possibly flow to.
// we then provide a {0,1} label for each possbile flow, with 1 reserved for the correct match
// we also need to provide a weight for each event for bad and good matches, to balance that choice.
larcv::EventImage2D* ev_adc = (larcv::EventImage2D*)mgr.get_data(larcv::kProductImage2D,_input_adc_producername);
LARCV_DEBUG() << "get adc and flow images. len(adc)=" << ev_adc->Image2DArray().size() << std::endl;
larcv::EventImage2D* ev_flow = nullptr;
if ( _has_mctruth ) {
ev_flow = (larcv::EventImage2D*)mgr.get_data(larcv::kProductImage2D,_input_trueflow_producername);
LARCV_DEBUG() << " len(flow)=" << ev_flow->Image2DArray().size() << std::endl;
}
larcv::EventChStatus* ev_chstatus = nullptr;
if ( _use_3plane_constraint ) {
LARCV_NORMAL() << "Using Three Plane Constraint. Retrieving Channel Status..." << std::endl;
ev_chstatus = (larcv::EventChStatus*)mgr.get_data(larcv::kProductChStatus, _input_chstatus_producername );
}
// make sparse image for the source ADC + 2 x (true flow + matachabilitity)+weights+nchoice,
// + 2 x sparse target images
// Define source plane and target planes. Get associated ADC images.
int srcindex = _source_plane;
int target_index[2] = { _target_planes[_flowdirs[0]],
_target_planes[_flowdirs[1]] };
int flow_index[2] = { (int)_flowdirs[0], (int)_flowdirs[1] }; // Y->U, Y->V
auto const& srcimg = ev_adc->Image2DArray().at(srcindex);
std::vector<const larcv::Image2D*> tarimg = { &ev_adc->Image2DArray().at(target_index[0]),
&ev_adc->Image2DArray().at(target_index[1]) };
std::vector<const larcv::Image2D*> flowimg_v(2,nullptr);
if ( _has_mctruth ) {
for (int i=0; i<2; i++ )
flowimg_v[i] = &(ev_flow->Image2DArray().at(flow_index[i]));
}
// Make both bad channel and gap channel images
ublarcvapp::EmptyChannelAlgo empty_algo;
std::vector<larcv::Image2D> badch_v;
std::vector<larcv::Image2D> gapch_v;
if ( ev_chstatus && _pbadch_v.size()==0 ) {
std::clock_t begin_badch = std::clock();
badch_v = empty_algo.makeBadChImage( 4, 3, 2400, 6*1008, 3456, 6, 1, *ev_chstatus );
LARCV_INFO() << "Made Bad Channel Image: " << badch_v.front().meta().dump() << std::endl;
if ( _use_gapch ) {
gapch_v = empty_algo.findMissingBadChs( ev_adc->Image2DArray(), badch_v, 10.0, 100 );
for ( size_t p=0; p<badch_v.size(); p++ ) {
for ( size_t c=0; c<badch_v[p].meta().cols(); c++ ) {
//std::cout << "plane[" << p << "] badch=" << c << " status=" << badch_v[p].pixel(0,c) << std::endl;
if ( gapch_v[p].pixel(0,c)>0 ) {
//std::cout << "plane[" << p << "] gapch=" << c << std::endl;
badch_v[p].paint_col(c,255);
}
}
}
LARCV_INFO() << "Made Gap Channel Image: " << gapch_v.front().meta().dump() << std::endl;
}
// save the prepare bad/gap channel image to the manager
larcv::EventImage2D* ev_badchout = (larcv::EventImage2D*)mgr.get_data( larcv::kProductImage2D, "prepflowbadch" );
if ( ev_badchout->Image2DArray().size()==0 ) {
for ( auto& badch : badch_v )
ev_badchout->Append( badch );
}
std::clock_t end_badch = std::clock();
LARCV_INFO() << "Time elapsed to make and store bad channel image: " << float(end_badch-begin_badch)/float(CLOCKS_PER_SEC) << std::endl;
provideBadChannelImages( badch_v );
}
else {
if ( _pbadch_v.size()>0 )
LARCV_INFO() << "Badch images already provided." << std::endl;
else
LARCV_INFO() << "Not making badch or gapch images" << std::endl;
}
// create matchability image
std::vector<larcv::Image2D> matchability_v;
if ( _has_mctruth ) {
std::clock_t begin_match = std::clock();
_makeMatchabilityImage( srcimg, tarimg, flowimg_v, matchability_v );
std::clock_t end_match = std::clock();
LARCV_INFO() << "Time to make matchability images: " << float(end_match-begin_match)/float(CLOCKS_PER_SEC);
}
// Make sparse images
// Source Image: combined with features [adc,trueflow1,trueflow2,matchable1,matchable2]
std::vector< const larcv::Image2D* > img_v;
std::vector< float > threshold_v;
std::vector< int > cuton_v;
if ( _has_mctruth ) {
threshold_v = { 10.0, -3999.0, -3999.0, -1, -1 }; // threshold value to include pixel
cuton_v = { 1, 0, 0, 0, 0 }; // feature to make cut on (COMBINED USING OR)
}
else {
threshold_v = { 10.0 };
cuton_v = { 1 };
}
LARCV_DEBUG() << "make source sparse image" << std::endl;
img_v.push_back( &srcimg );
if ( _has_mctruth ) {
img_v.push_back( &ev_flow->Image2DArray().at( flow_index[0] ) );
img_v.push_back( &ev_flow->Image2DArray().at( flow_index[1] ) );
img_v.push_back( &matchability_v[0] );
img_v.push_back( &matchability_v[1] );
}
larcv::SparseImage spsrc( img_v, threshold_v, cuton_v ); //make the sparse image
LARCV_DEBUG() << "Number of source image pixels: " << spsrc.len() << std::endl;
LARCV_DEBUG() << "Occupancy of source image: "
<< float(spsrc.len())/float(spsrc.meta(0).cols()*spsrc.meta(0).rows())
<< std::endl;
std::vector< larcv::SparseImage > spimg_v;
spimg_v.emplace_back( std::move(spsrc) );
// sparse image for the target images. we just need the adc values
for (int i=0; i<2; i++ ) {
LARCV_DEBUG() << "make target sparse image: flowdir=" << i << std::endl;
std::vector< const larcv::Image2D* > imgtar_v;
imgtar_v.push_back( tarimg[i] );
std::vector<float> tar_threshold_v(1,10.0);
std::vector<int> tar_cuton_v(1,1);
larcv::SparseImage sptar( imgtar_v, tar_threshold_v, tar_cuton_v );
LARCV_DEBUG() << "target (flowdir=" << i << ") image pixels: " << sptar.len() << std::endl;
spimg_v.emplace_back( std::move(sptar) );
}
// now we make the flowmatch map for the source image, for both flows.
_matchdata_v->clear();
int n3plane = 0;
int n2plane = 0;
// loop over flow directions
for (int i=0; i<2; i++ ) {
LARCV_DEBUG() << "make match map: flowdir=" << i << std::endl;
std::clock_t begin_matchmap = std::clock();
auto& sparsesrc = spimg_v[0];
auto& sptar = spimg_v[1+i];
int source_plane = _source_plane;
int target_plane = target_index[i];
int other_plane = _other_planes[ _flowdirs[i] ];
LARCV_DEBUG() << "source plane=" << source_plane
<< " target plane=" << target_plane
<< " other plane=" << other_plane
<< std::endl;
FlowMatchMap matchmap( source_plane, target_plane );
// first, we scan the target sparse image, making a map of row to vector of column indices
// essentially, giving us the columns with charge for each target
std::clock_t start_targetmap = std::clock();
std::map< int, std::vector<int> > targetmap;
for ( size_t ipt=0; ipt<sptar.len(); ipt++ ) {
int col = (int)sptar.getfeature(ipt,1);
int row = (int)sptar.getfeature(ipt,0);
float adc = sptar.getfeature(ipt,2);
auto it=targetmap.find( row );
if ( it==targetmap.end() ) {
targetmap[row] = std::vector<int>();
targetmap[row].reserve(10);
it = targetmap.find(row);
}
it->second.push_back( ipt ); // add in the index of this point
}
std::clock_t end_targetmap = std::clock();
float elapsed_targetmap = float(end_targetmap-start_targetmap)/float(CLOCKS_PER_SEC);
LARCV_DEBUG() << "target[row] -> {target indices} map define (flowdir=" << i << ")."
<< " elements=" << targetmap.size()
<< std::endl;
LARCV_INFO() << "Prep Target Map: " << elapsed_targetmap << " sec" << std::endl;
int npix_w_matches = 0;
int npix_wo_matches = 0;
_ntrue_pairs[i] = 0;
_nfalse_pairs[i] = 0;
// now we can make the src pixel to target pixel choices map
for ( int ipt=0; ipt<sparsesrc.len(); ipt++ ) {
// source coordinates
int col = (int)sparsesrc.getfeature(ipt,1);
int row = (int)sparsesrc.getfeature(ipt,0);
int matchable = -1;
if ( _has_mctruth )
matchable = (int)sparsesrc.getfeature(ipt,5+i);
float umin = _wire_bounds[i][col][0];
float umax = _wire_bounds[i][col][1];
std::vector<int> matchable_target_cols;
auto it=targetmap.find(row);
if ( it!=targetmap.end() ) {
auto const& target_index_v = targetmap[row];
std::vector< int > truth_v;
std::vector< int > within_bounds_target_v;
// if MC, find the true column match
float flow = 0;
int target_col = 0;
if ( _has_mctruth ) {
flow = sparsesrc.getfeature(ipt,3+i); // the true flow
target_col = col + (int)flow;
}
int nmatches = 0;
std::vector<int> tar_col_v;
std::vector<float> other_wire_adc_v;
for ( size_t idx=0; idx<target_index_v.size(); idx++ ) {
int tarcol = sptar.getfeature( target_index_v[idx], 1 );
if ( tarcol<(int)umin || tarcol>(int)umax ) continue;
// if we enforce 3-plane consistency, we need to check for charge in the other plane
bool indead = false;
if ( _use_3plane_constraint ) {
double y, z;
larutil::Geometry::GetME()->IntersectionPoint( col, tarcol, (UChar_t)source_plane, (UChar_t)target_plane, y, z );
Double_t pos[3] = { 0, y, z };
float other_wire = larutil::Geometry::GetME()->WireCoordinate( pos, other_plane );
int i_other_wire = (int)other_wire;
if ( other_wire-(float)i_other_wire >0.5 )
i_other_wire += 1;
if ( other_wire<0 || (int)other_wire>=(int)larutil::Geometry::GetME()->Nwires(other_plane) )
continue;
float other_adc = 0.;
for (int dc=-2; dc<=2; dc++ ) {
int c = i_other_wire+dc;
if ( c<0 || c>=(int)larutil::Geometry::GetME()->Nwires(other_plane) ) continue;
float test_c = ev_adc->Image2DArray()[other_plane].pixel( row, c, __FILE__,__LINE__ );
if ( test_c>other_adc )
other_adc = test_c;
if ( other_adc>10.0 )
break;
}
if ( other_adc<10.0 ) {
// not using dead channels, other plane is below threshold. skip this combination
if ( ev_chstatus==nullptr )
continue;
// if event chstatus pointer is not null, we check the ch status
// int chstatus = ev_chstatus->Status( other_plane ).Status( other_wire );
// if ( chstatus==4 ) {
// // good channel so ignore this match
// continue;
// }
if ( _pbadch_v[other_plane]->pixel( row, (int)i_other_wire)<1 ) {
// good channel, so ignore
continue;
}
// otherwise pass, but in dead region
indead = true;
}
other_wire_adc_v.push_back( other_adc );
}// if use three-plane constraint
if ( indead )
n2plane++;
else
n3plane++;
// moving on, we store this combination
tar_col_v.push_back( tarcol );
within_bounds_target_v.push_back( target_index_v[idx] );
// === [ MC ] =========================
if ( _has_mctruth && abs(tarcol-target_col)<=_positive_example_distance ) {
// within positive example distance
if ( !_use_soft_truth || tarcol==target_col ) {
// if positive example, set to truth vector to 1
truth_v.push_back(1.0);
}
else {
// soft truth
truth_v.push_back( 2.0/float(tarcol-target_col) ); // arbitrary function
}
_ntrue_pairs[i]++;
if ( tarcol==target_col) nmatches++;
}
else {
truth_v.push_back(0.0);
_nfalse_pairs[i]++;
}
}//end of loop over target columns for this row
if ( matchable==1 ) {
if ( _has_mctruth && nmatches!=1 ) {
//LARCV_CRITICAL() << "did not find matchable column" << std::endl;
}
if ( nmatches==1 )
npix_w_matches++;
else
npix_wo_matches++;
}
std::stringstream sstarcol;
if ( logger().debug() ) {
sstarcol << "{ ";
for ( size_t ii=0; ii<tar_col_v.size(); ii++ ) {
sstarcol << tar_col_v[ii];
if ( _use_3plane_constraint )
sstarcol << "(" << other_wire_adc_v[ii] << ")";
sstarcol << " ";
}
sstarcol << "}";
}
if ( _debug_detailed_output ) {
LARCV_DEBUG() << "srcpixel[" << ipt << ", (" << row << "," << col << ")] "
<< " flow (" << flow << ") to targetcol=" << target_col
<< " matchable=" << matchable
<< " bounds=[" << umin << "," << umax << "]"
<< std::endl;
LARCV_DEBUG() << " has " << target_index_v.size() << " potential matches and " << tar_col_v.size() << " saved matches with " << sstarcol.str()
<< " and " << nmatches << " correct match" << std::endl;
}
matchmap.add_matchdata( ipt, within_bounds_target_v, truth_v );
}
else {
matchmap.add_matchdata( ipt, std::vector<int>(), std::vector<int>() );
}
}//loop over points
_matchdata_v->emplace_back( std::move(matchmap) );
std::clock_t end_matchmap = std::clock();
LARCV_INFO() << "matched map flowdir=" << i << ": "
<< " matchable hasmatch=" << npix_w_matches
<< " hasnomatch=" << npix_wo_matches
<< " elapsed=" << float(end_matchmap-begin_matchmap)/float(CLOCKS_PER_SEC)
<< std::endl;
}//end of loop over flow directions
LARCV_INFO() << "number of true matches: flow[0]=" << _ntrue_pairs[0] << " flow[1]=" << _ntrue_pairs[1] << std::endl;
LARCV_INFO() << "number of false matches: flow[0]=" << _nfalse_pairs[0] << " flow[1]=" << _nfalse_pairs[1] << std::endl;
LARCV_INFO() << "number of three-plane matches: " << n3plane << std::endl;
LARCV_INFO() << "number of two-plane+dead region matches: " << n2plane << std::endl;
LARCV_DEBUG() << "pass sparse images to iomanager" << std::endl;
std::stringstream sparseout_name;
sparseout_name << "larflow_plane" << _source_plane;
larcv::EventSparseImage* ev_out = (larcv::EventSparseImage*)mgr.get_data(larcv::kProductSparseImage, sparseout_name.str() );
ev_out->Emplace( std::move( spimg_v ) );
if ( _use_ana_tree ) {
std::clock_t begin_saveana = std::clock();
LARCV_DEBUG() << "save flow match maps to ana tree" << std::endl;
_ana_tree->Fill();
std::clock_t end_saveana = std::clock();
LARCV_INFO() << "time to save ana tree = " << float(end_saveana-begin_saveana)/float(CLOCKS_PER_SEC) << std::endl;
}
// clear out badch image pointers, so don't accidently use ones from previous events
_pbadch_v.clear();
LARCV_DEBUG() << "done" << std::endl;
return true;
}
void PrepFlowMatchData::finalize() {
if ( _use_ana_tree )
_ana_tree->Write();
}
void PrepFlowMatchData::_setup_ana_tree() {
if ( _use_ana_tree )
LARCV_DEBUG() << "create tree, make container, set branch" << std::endl;
else
LARCV_DEBUG() << "not storing data in ana tree" << std::endl;
_matchdata_v = new std::vector< FlowMatchMap >();
if ( !_use_ana_tree )
return;
char treename[50];
sprintf(treename,"flowmatchdata_plane%d",_source_plane);
_ana_tree = new TTree(treename,"Provides map from source to target pixels to match");
_ana_tree->Branch( "matchmap", _matchdata_v );
_ana_tree->Branch( "nfalsepairs", _nfalse_pairs, "nfalsepairs[2]/I" );
_ana_tree->Branch( "ntruepairs", _ntrue_pairs, "ntruepairs[2]/I" );
}
void PrepFlowMatchData::_extract_wire_overlap_bounds() {
const larutil::Geometry* geo = larutil::Geometry::GetME();
// set the flow directions we will evaluate
std::string src_plane_name;
switch ( _source_plane ) {
case 2:
_flowdirs[0] = kY2U;
_flowdirs[1] = kY2V;
src_plane_name = "Y";
break;
case 0:
_flowdirs[0] = kU2V;
_flowdirs[1] = kU2Y;
src_plane_name = "U";
break;
case 1:
_flowdirs[0] = kV2U;
_flowdirs[1] = kV2Y;
src_plane_name = "V";
break;
default:
throw std::runtime_error("PrepFlowMatchData::_extract_wire_overlap_bounds: bad source plane");
break;
}
// loop over flow directions
for (int i=0; i<2; i++ ) {
int target_plane = _target_planes[_flowdirs[i]];
LARCV_DEBUG() << "defined overlapping wire bounds for " << src_plane_name << "[" << _source_plane << "]"
<< "->plane[" << target_plane << "]" << std::endl;
_wire_bounds[i].clear();
for (int isrc=0; isrc<(int)geo->Nwires(_source_plane); isrc++ ) {
Double_t xyzstart[3];
Double_t xyzend[3];
geo->WireEndPoints( (UChar_t)_source_plane, (UInt_t)isrc, xyzstart, xyzend );
float u1 = geo->WireCoordinate( xyzstart, (UInt_t)target_plane );
float u2 = geo->WireCoordinate( xyzend, (UInt_t)target_plane );
float umin = (u1<u2) ? u1 : u2;
float umax = (u1>u2) ? u1 : u2;
umin -= 5.0;
umax += 5.0;
if ( umin<0 ) umin = 0;
if ( umax<0 ) umax = 0;
if ( (int)umin>=geo->Nwires(target_plane) ) umin = (float)geo->Nwires(target_plane)-1;
if ( (int)umax>=geo->Nwires(target_plane) ) umax = (float)geo->Nwires(target_plane)-1;
_wire_bounds[i][isrc] = std::vector<int>{ (int)umin, (int)umax };
//LARCV_DEBUG() << " src[" << isrc << "] -> plane[" << i << "]: (" << umin << "," << umax << ")" << std::endl;
}
}
LARCV_DEBUG() << "defined overlapping wire bounds" << std::endl;
//std::cin.get();
}
void PrepFlowMatchData::_makeMatchabilityImage( const larcv::Image2D& srcimg,
const std::vector<const larcv::Image2D*>& tarimg_v,
const std::vector<const larcv::Image2D*>& flowimg_v,
std::vector<larcv::Image2D>& matchability_v ) {
matchability_v.clear();
// loop over flow directions (2 in MicroBooNE)
for ( size_t i=0; i<2; i++ ) {
LARCV_DEBUG() << "make matchability image for flowdir=" << i << std::endl;
larcv::Image2D matchability( srcimg.meta() );
matchability.paint(0.0);
auto const& tar = *tarimg_v[i];
//auto const& flowimg = flowimg_v.at( flow_index[i] );
auto const& flowimg = *flowimg_v[i];
// we check matchability of flow. does flow go into a dead region?
// we try to setup the loop to be vectorize (and use open MP?)
const std::vector<float>& srcdata = srcimg.as_vector();
const std::vector<float>& tardata = tar.as_vector();
const std::vector<float>& flowdata = flowimg.as_vector();
std::vector<float>& matchdata = matchability.as_mod_vector();
size_t ncols = srcimg.meta().cols();
size_t nrows = srcimg.meta().rows();
int tar_ncols = (int)tar.meta().cols();
int tar_nrows = (int)tar.meta().rows();
for ( size_t c=0; c<ncols; c++ ) {
for ( size_t r=0; r<nrows; r++ ) {
float adc = srcdata[ c*nrows + r ];
float flow = flowdata[ c*nrows + r ];
if ( adc<10.0 ) continue;
if ( flow<=-4000) continue;
int target_col = (int)c + (int)flow;
if ( target_col>=0 && target_col<tar_ncols && tardata[ target_col*tar_nrows+(int)r ]<10.0 ) {
matchdata[ c*nrows + r ] = 1.0;
}
}
}//end of col loop
matchability_v.emplace_back( std::move(matchability) );
}//end of flow loop
}
// /**
// * make sparse matrix
// *
// */
// std::vector<larcv::SparseImage> PrepFlowMatchData::_makeFlowSparseImage( const std::vector<larcv::Image2D>& ) {
// }
}