Program Listing for File PrepKeypointData.cxx¶
↰ Return to documentation for file (larflow/KeyPoints/PrepKeypointData.cxx
)
#include "PrepKeypointData.h"
#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION
#include <numpy/ndarrayobject.h>
#include <sstream>
#include <algorithm>
#include <queue>
#include "TH2D.h"
#include "ublarcvapp/MCTools/MCPixelPGraph.h"
#include "ublarcvapp/MCTools/crossingPointsAnaMethods.h"
#include "larcv/core/DataFormat/IOManager.h"
#include "larcv/core/DataFormat/Image2D.h"
#include "larcv/core/DataFormat/EventImage2D.h"
// larlite
#include "LArUtil/SpaceChargeMicroBooNE.h"
#include "LArUtil/LArProperties.h"
#include "LArUtil/Geometry.h"
#include "DataFormat/storage_manager.h"
#include "DataFormat/mctrack.h"
#include "DataFormat/mcshower.h"
#include "DataFormat/mctruth.h"
namespace larflow {
namespace keypoints {
bool PrepKeypointData::_setup_numpy = false;
PrepKeypointData::PrepKeypointData()
: _adc_image_treename("wire"),
_label_tree(nullptr)
{
_nclose = 0;
_nfar = 0;
hdist[0] = new TH1F("hdist_x","",2002,-500,500.0);
hdist[1] = new TH1F("hdist_y","",2002,-500,500.0);
hdist[2] = new TH1F("hdist_z","",2002,-500,500.0);
hdpix[0] = new TH1F("hdpix_dt","",1001,-500,500);
hdpix[1] = new TH1F("hdpix_du","",1001,-500,500);
hdpix[2] = new TH1F("hdpix_dv","",1001,-500,500);
hdpix[3] = new TH1F("hdpix_dy","",1001,-500,500);
}
PrepKeypointData::~PrepKeypointData()
{
for (int v=0; v<3; v++ )
if ( hdist[v] ) delete hdist[v];
for (int v=0; v<4; v++ )
if ( hdpix[v] ) delete hdpix[v];
if ( _label_tree ) delete _label_tree;
}
void PrepKeypointData::process( larcv::IOManager& iolcv, larlite::storage_manager& ioll )
{
auto ev_adc = (larcv::EventImage2D*)iolcv.get_data(larcv::kProductImage2D,_adc_image_treename);
auto ev_segment = (larcv::EventImage2D*)iolcv.get_data(larcv::kProductImage2D,"segment");
auto ev_instance = (larcv::EventImage2D*)iolcv.get_data(larcv::kProductImage2D,"instance");
auto ev_ancestor = (larcv::EventImage2D*)iolcv.get_data(larcv::kProductImage2D,"ancestor");
auto ev_mctrack = (larlite::event_mctrack*)ioll.get_data( larlite::data::kMCTrack, "mcreco" );
auto ev_mcshower = (larlite::event_mcshower*)ioll.get_data( larlite::data::kMCShower, "mcreco" );
auto ev_mctruth = (larlite::event_mctruth*)ioll.get_data( larlite::data::kMCTruth, "generator" );
std::vector<larcv::Image2D> badch_v;
for ( auto const& img : ev_adc->Image2DArray() ) {
larcv::Image2D blank(img.meta());
blank.paint(0.0);
badch_v.emplace_back( std::move(blank) );
}
std::cout << "[PrepKeypointData Inputs]" << std::endl;
std::cout << " adc images: " << ev_adc->Image2DArray().size() << std::endl;
std::cout << " badch images: " << badch_v.size() << std::endl;
std::cout << " segment images: " << ev_segment->Image2DArray().size() << std::endl;
std::cout << " instance images: " << ev_instance->Image2DArray().size() << std::endl;
std::cout << " ancestor images: " << ev_ancestor->Image2DArray().size() << std::endl;
std::cout << " mctracks: " << ev_mctrack->size() << std::endl;
std::cout << " mcshowers: " << ev_mcshower->size() << std::endl;
std::cout << " mctruths: " << ev_mctruth->size() << std::endl;
_run = iolcv.event_id().run();
_subrun = iolcv.event_id().subrun();
_event = iolcv.event_id().event();
process( ev_adc->Image2DArray(),
badch_v,
ev_segment->Image2DArray(),
ev_instance->Image2DArray(),
ev_ancestor->Image2DArray(),
*ev_mctrack,
*ev_mcshower,
*ev_mctruth );
}
void PrepKeypointData::process( const std::vector<larcv::Image2D>& adc_v,
const std::vector<larcv::Image2D>& badch_v,
const std::vector<larcv::Image2D>& segment_v,
const std::vector<larcv::Image2D>& instance_v,
const std::vector<larcv::Image2D>& ancestor_v,
const larlite::event_mctrack& mctrack_v,
const larlite::event_mcshower& mcshower_v,
const larlite::event_mctruth& mctruth_v ) {
// allocate space charge class
larutil::SpaceChargeMicroBooNE sce;
// make particle graph
ublarcvapp::mctools::MCPixelPGraph mcpg;
mcpg.buildgraph( adc_v, segment_v, instance_v, ancestor_v,
mcshower_v, mctrack_v, mctruth_v );
//mcpg.printGraph();
// build key-points
_kpd_v.clear();
for (int i=0; i<(int)larflow::kNumKeyPoints; i++)
_kppos_v[i].clear();
// build crossing points for muon track primaries
std::vector<KPdata> track_kpd
= getMuonEndpoints( mcpg, adc_v, mctrack_v, &sce );
std::cout << "[Track Endpoint Results]" << std::endl;
for ( auto const& kpd : track_kpd ) {
std::cout << " " << kpd.str() << std::endl;
_kpd_v.emplace_back( std::move(kpd) );
}
// add points for shower starts
std::vector<KPdata> shower_kpd
= getShowerStarts( mcpg, adc_v, mcshower_v, &sce );
std::cout << "[Shower Endpoint Results]" << std::endl;
for ( auto const& kpd : shower_kpd ) {
std::cout << " " << kpd.str() << std::endl;
_kpd_v.emplace_back( std::move(kpd) );
}
// we change the kptype to neutrino vertex for those on it
_label_nu_keypoints( mctruth_v, adc_v, &sce, _kpd_v );
// filter duplicates
//filter_duplicates();
// copy positions of keypoints into flat vector for storage
for ( auto const& kpd : _kpd_v ) {
if ( kpd.kptype>=0 && kpd.kptype<larflow::kNumKeyPoints ) {
_kppos_v[ kpd.kptype ].push_back( kpd.keypt );
}
else {
throw std::runtime_error("unrecognized keypoint type");
}
}
}
std::vector<KPdata>
PrepKeypointData::getMuonEndpoints( ublarcvapp::mctools::MCPixelPGraph& mcpg,
const std::vector<larcv::Image2D>& adc_v,
const larlite::event_mctrack& mctrack_v,
larutil::SpaceChargeMicroBooNE* psce )
{
bool verbose = false;
// get list of primaries
std::vector<ublarcvapp::mctools::MCPixelPGraph::Node_t*> primaries
= mcpg.getPrimaryParticles();
// output vector of keypoint data
std::vector<KPdata> kpd_v;
for ( auto const& pnode : primaries ) {
if ( abs(pnode->pid)!=13
&& abs(pnode->pid)!=2212
&& abs(pnode->pid)!=211 )
continue;
auto const& mctrk = mctrack_v.at( pnode->vidx );
int crossingtype =
ublarcvapp::mctools::CrossingPointsAnaMethods::
doesTrackCrossImageBoundary( mctrk,
adc_v.front().meta(),
4050.0,
psce );
if ( crossingtype>=0 ) {
if ( crossingtype>=0 && crossingtype<=2) {
KPdata kpd;
kpd.crossingtype = crossingtype;
kpd.trackid = pnode->tid;
kpd.pid = pnode->pid;
kpd.vid = pnode->vidx;
kpd.is_shower = 0;
kpd.origin = pnode->origin;
kpd.kptype = larflow::kTrackEnds;
kpd.imgcoord =
ublarcvapp::mctools::CrossingPointsAnaMethods::getFirstStepPosInsideImage( mctrk, adc_v.front().meta(),
4050.0, true, 0.3, 0.1,
kpd.keypt, psce, verbose );
if ( kpd.imgcoord.size()>0 ) {
kpd_v.emplace_back( std::move(kpd) );
}
}
if ( crossingtype>=0 && crossingtype<=2 ) {
KPdata kpd;
kpd.crossingtype = crossingtype;
kpd.trackid = pnode->tid;
kpd.pid = pnode->pid;
kpd.vid = pnode->vidx;
kpd.is_shower = 0;
kpd.origin = pnode->origin;
kpd.kptype = larflow::kTrackEnds;
kpd.imgcoord =
ublarcvapp::mctools::CrossingPointsAnaMethods::getFirstStepPosInsideImage( mctrk, adc_v.front().meta(),
4050.0, false, 0.3, 0.1,
kpd.keypt, psce, verbose );
if ( kpd.imgcoord.size()>0 ) {
kpd_v.emplace_back( std::move(kpd) );
}
}
}//if track in image
}//end of primary loop
return kpd_v;
}
std::vector<KPdata>
PrepKeypointData::getShowerStarts( ublarcvapp::mctools::MCPixelPGraph& mcpg,
const std::vector<larcv::Image2D>& adc_v,
const larlite::event_mcshower& mcshower_v,
larutil::SpaceChargeMicroBooNE* psce )
{
// output vector of keypoint data
std::vector<KPdata> kpd_v;
// loop over nodes, look for electron/gamma pixels
for ( auto const& pnode : mcpg.node_v ) {
if ( abs(pnode.pid)!=11
&& abs(pnode.pid)!=22 )
continue;
int max_plane_pixels = 0;
for (auto const& pix_v : pnode.pix_vv ) {
if ( max_plane_pixels<pix_v.size() )
max_plane_pixels = pix_v.size();
}
if ( max_plane_pixels<10 )
continue;
// start: pnode.start; //should be in apparent position already
KPdata kpd;
kpd.crossingtype = 2;
kpd.trackid = pnode.tid;
kpd.pid = pnode.pid;
kpd.vid = pnode.vidx;
kpd.origin = pnode.origin;
kpd.is_shower = 1;
kpd.kptype = larflow::kShowerStart;
kpd.keypt.resize(3,0);
for (int i=0; i<3; i++)
kpd.keypt[i] = pnode.imgpos4[i];
std::vector<double> dpos(3,0);
for (int i=0; i<3; i++ ) dpos[i] = pnode.imgpos4[i];
kpd.imgcoord.resize(4,0);
try {
for (int p=0; p<3; p++)
kpd.imgcoord[1+p] = (int)larutil::Geometry::GetME()->NearestWire( dpos, p );
}
catch (...) {
continue;
}
float tick = pnode.imgpos4[3];
if ( tick>adc_v[0].meta().min_y() && tick<adc_v[0].meta().max_y() ) {
kpd.imgcoord[0] = adc_v[0].meta().row( tick );
}
else {
continue;
}
kpd_v.emplace_back( std::move(kpd) );
}//end of node loop
return kpd_v;
}
void PrepKeypointData::_label_nu_keypoints( const larlite::event_mctruth& mctruth_v,
const std::vector<larcv::Image2D>& img_v,
larutil::SpaceChargeMicroBooNE* psce,
std::vector<KPdata>& kpdata_v )
{
// loop over all interactions
int inu = -1;
for ( auto const& mct : mctruth_v ) {
inu++;
auto const& nu = mct.GetNeutrino();
auto const& nutraj = nu.Nu().Trajectory();
if (nutraj.size()>0) {
// get the space-charge corrected neutrino vertex
std::vector<double> nupos(3,0);
for (int i=0; i<3; i++ )
nupos[i] = nutraj.front().Position()[i];
std::vector<double> offsets = psce->GetPosOffsets( nupos[0], nupos[1], nupos[2] );
nupos[0] = nupos[0] - offsets[0] + 0.7;
nupos[1] += offsets[1];
nupos[2] += offsets[2];
// make a neutrino keypoint
KPdata kpd;
kpd.crossingtype = 0;
kpd.trackid = 0;
kpd.pid = 12;
kpd.vid = inu;
kpd.is_shower = 0;
kpd.origin = 1;
kpd.kptype = larflow::kNuVertex;
kpd.keypt.resize(3,0);
for (int i=0; i<3; i++) kpd.keypt[i] = nupos[i];
kpd.imgcoord.resize(4,0);
try {
for (int p=0; p<3; p++)
kpd.imgcoord[1+p] = (int)larutil::Geometry::GetME()->NearestWire( nupos, p );
}
catch (...) {
continue;
}
float tick = 3200 + nupos[0]/larutil::LArProperties::GetME()->DriftVelocity()/0.5;
if ( tick>img_v[0].meta().min_y() && tick<img_v[0].meta().max_y() ) {
kpd.imgcoord[0] = img_v[0].meta().row( tick );
}
else {
continue;
}
kpdata_v.push_back( kpd );
}//end of if neutrino truth object has trajectory point for vertex
}//end of loop over mctruth elements
}
void PrepKeypointData::filter_duplicates()
{
// first count the number of unique points
std::set< std::vector<int> > unique_coords;
std::vector< std::vector<int> > kpd_index;
int npts = 0;
for ( size_t ikpd=0; ikpd<_kpd_v.size(); ikpd++ ) {
auto const& kpd = _kpd_v[ikpd];
if (kpd.imgcoord.size()>0) {
if ( unique_coords.find( kpd.imgcoord )==unique_coords.end() ) {
kpd_index.push_back( std::vector<int>{(int)ikpd,0} );
unique_coords.insert( kpd.imgcoord );
npts++;
}
}
}
std::vector<KPdata> kpd_v;
for ( auto const& kpdidx : kpd_index ) {
kpd_v.emplace_back( std::move( _kpd_v[kpdidx[0]] ) );
}
std::swap(kpd_v,_kpd_v);
}
PyObject* PrepKeypointData::get_keypoint_array( int iclass ) const
{
if ( !PrepKeypointData::_setup_numpy ) {
import_array1(0);
PrepKeypointData::_setup_numpy = true;
}
// first count the number of unique points
std::set< std::vector<int> > unique_coords;
std::vector< std::vector<int> > kpd_index;
int npts = 0;
for ( size_t ikpd=0; ikpd<_kpd_v.size(); ikpd++ ) {
auto const& kpd = _kpd_v[ikpd];
if ( kpd.kptype!=(larflow::KeyPoint_t)iclass ) continue;
if (kpd.imgcoord.size()>0) {
if ( unique_coords.find( kpd.imgcoord )==unique_coords.end() ) {
kpd_index.push_back( std::vector<int>{(int)ikpd,0} );
unique_coords.insert( kpd.imgcoord );
npts++;
}
}
}
int nd = 2;
npy_intp dims[] = { npts, 10 };
PyArrayObject* array = (PyArrayObject*)PyArray_SimpleNew( nd, dims, NPY_FLOAT );
size_t ipt = 0;
for ( auto& kpdidx : kpd_index ) {
auto const& kpd = _kpd_v[kpdidx[0]];
if ( kpdidx[1]==0 ) {
// start img coordinates
for ( size_t i=0; i<4; i++ )
*((float*)PyArray_GETPTR2(array,ipt,i)) = (float)kpd.imgcoord[i];
// 3D point
for ( size_t i=0; i<3; i++ )
*((float*)PyArray_GETPTR2(array,ipt,4+i)) = (float)kpd.keypt[i];
// is shower
*((float*)PyArray_GETPTR2(array,ipt,7)) = (float)kpd.is_shower;
// origin
*((float*)PyArray_GETPTR2(array,ipt,8)) = (float)kpd.origin;
// PID
*((float*)PyArray_GETPTR2(array,ipt,9)) = (float)kpd.pid;
ipt++;
}
}// end of loop over keypointdata structs
return (PyObject*)array;
}
PyObject* PrepKeypointData::get_triplet_score_array( float sig ) const
{
if ( !PrepKeypointData::_setup_numpy ) {
import_array1(0);
PrepKeypointData::_setup_numpy = true;
}
// get label info for each triplet proposal
int npts = (int)_match_proposal_labels_v[0].size();
for (size_t iclass=0; iclass<larflow::kNumKeyPoints; iclass++) {
if ( _match_proposal_labels_v[iclass].size()!=npts ) {
throw std::runtime_error("number of triplet labels/scores for each class does not match!");
}
}
int nd = 2;
npy_intp dims[] = { npts, larflow::kNumKeyPoints };
PyArrayObject* array = (PyArrayObject*)PyArray_SimpleNew( nd, dims, NPY_FLOAT );
size_t ipt = 0;
for ( size_t ipt=0; ipt<npts; ipt++ ) {
for (size_t iclass=0; iclass<larflow::kNumKeyPoints; iclass++) {
auto const& label_v = _match_proposal_labels_v[iclass][ipt];
if ( label_v[0]==0.0 ) {
*((float*)PyArray_GETPTR2(array,ipt,iclass)) = 0.0;
}
else {
float dist = 0.;
for (int i=0; i<3; i++) dist += label_v[1+i]*label_v[1+i];
*((float*)PyArray_GETPTR2(array,ipt,iclass)) = exp( -0.5*dist/(sig*sig) );
}
}
}// end of loop over keypointdata structs
return (PyObject*)array;
}
void PrepKeypointData::make_proposal_labels( const larflow::prep::PrepMatchTriplets& match_proposals )
{
for (int i=0; i<larflow::kNumKeyPoints; i++) {
_match_proposal_labels_v[i].clear();
_match_proposal_labels_v[i].reserve(match_proposals._triplet_v.size());
}
for (int imatch=0; imatch<match_proposals._triplet_v.size(); imatch++ ) {
// triplet index (index of the sparse matrix coordinates)
const std::vector<int>& triplet = match_proposals._triplet_v[imatch];
// 3D position formed by intersection of the wires
const std::vector<float>& pos = match_proposals._pos_v[imatch];
// std::cout << "[match " << imatch << "] "
// << "testpt=(" << pos[0] << "," << pos[1] << "," << pos[2] << ") "
// << std::endl;
// make a score for each class
for (int ikpclass=0; ikpclass<(int)larflow::kNumKeyPoints; ikpclass++) {
// store label values
// [0]: has a match to a true keypoint
// [1,2,3]: (dx,dy,dz) to closest keypoint
// [4,5,6,7]: (dr,du,dv,dy) shift in row and columns
std::vector<float> label_v(10,0);
float dist = 1.0e9;
// dumb assignment, loops over all truth keypoints
// seems fast enough
const KPdata* kpd = nullptr;
std::vector<float> leafpos(3,0);
for (auto const& testkpd : _kpd_v ) {
// ignore those not in class
if ( testkpd.kptype!=(larflow::KeyPoint_t)ikpclass )
continue;
float testdist = 0.;
for ( int v=0; v<3; v++ )
testdist += (testkpd.keypt[v]-pos[v])*(testkpd.keypt[v]-pos[v]);
testdist = sqrt(testdist);
if ( dist>testdist ) {
// update the leadpos and kpd pointer
dist = testdist;
for (int v=0; v<3; v++ )
leafpos[v] = testkpd.keypt[v];
kpd = &testkpd;
}
}//end of loop over true points
// make label vector
// within 50 pixels/15 cm
if ( dist<0.3*50 ) {
label_v[0] = 1.0;
_nclose++;
}
else {
label_v[0] = 0.0;
_nfar++;
}
// make shift in 3D label
if ( dist<0.3*50 ) {
for (int i=0; i<3; i++ ) {
label_v[1+i] = leafpos[i]-pos[i];
hdist[i]->Fill(label_v[1+i]);
}
// shift in imgcoords
std::vector<int> imgcoords(4,0);
imgcoords[0] = match_proposals._sparseimg_vv[0][triplet[0]].row;
for (int p=0; p<3; p++ ) {
imgcoords[1+p] = match_proposals._sparseimg_vv[p][triplet[p]].col;
}
for (int i=0; i<4; i++) {
label_v[4+i] = imgcoords[i]-kpd->imgcoord[i];
hdpix[i]->Fill( label_v[4+i] );
}
}
_match_proposal_labels_v[ikpclass].push_back(label_v);
}//end of keypoint class loop
}//end of match proposal loop
}
void PrepKeypointData::writeHists()
{
for (int i=0; i<3; i++ ) {
hdist[i]->Write();
}
for (int i=0; i<4; i++ ) {
hdpix[i]->Write();
}
}
void PrepKeypointData::defineAnaTree()
{
_label_tree = new TTree("keypointlabels","Key point Training Labels");
_label_tree->Branch("run",&_run,"run/I");
_label_tree->Branch("subrun",&_subrun,"subrun/I");
_label_tree->Branch("event",&_event,"event/I");
_label_tree->Branch("kplabel_nuvertex", &_match_proposal_labels_v[0]);
_label_tree->Branch("kplabel_trackends", &_match_proposal_labels_v[1]);
_label_tree->Branch("kplabel_showerstart", &_match_proposal_labels_v[2]);
_label_tree->Branch("kppos_nuvertex", &_kppos_v[0]);
_label_tree->Branch("kppos_trackends", &_kppos_v[1]);
_label_tree->Branch("kppos_showerstart", &_kppos_v[2]);
}
void PrepKeypointData::writeAnaTree()
{
if (_label_tree)
_label_tree->Write();
}
void PrepKeypointData::printKeypoints() const
{
std::cout << "[PrepKeypointData::printKeypoints] -----------------" << std::endl;
for ( size_t i=0; i<_kpd_v.size(); i++ ) {
auto const& kpd = _kpd_v[i];
std::cout << " [" << i << "] "
<< "(" << kpd.keypt[0] << "," << kpd.keypt[1] << "," << kpd.keypt[2] << ")"
<< std::endl;
}
std::cout << "----------------------------------------------------" << std::endl;
}
std::vector<TH2D> PrepKeypointData::makeScoreImage( const int ikpclass, const float sigma,
const std::string histname,
const larflow::prep::PrepMatchTriplets& tripmaker,
const std::vector<larcv::Image2D>& adc_v ) const
{
std::vector<TH2D> hist_v;
for ( size_t p=0; p<adc_v.size(); p++ ) {
std::stringstream ss;
ss << histname << "_p" << (int)p;
TH2D hist( ss.str().c_str(), ss.str().c_str(),
adc_v[p].meta().cols(), adc_v[p].meta().min_x(), adc_v[p].meta().max_x(),
adc_v[p].meta().rows(), adc_v[p].meta().min_y(), adc_v[p].meta().max_y() );
for (size_t ipt=0; ipt<tripmaker._triplet_v.size(); ipt++) {
int r = tripmaker._sparseimg_vv[p][ tripmaker._triplet_v[ipt][p] ].row;
int c = tripmaker._sparseimg_vv[p][ tripmaker._triplet_v[ipt][p] ].col;
auto const& label_v = _match_proposal_labels_v[ikpclass][ipt];
if ( label_v[0]==0.0 && hist.GetBinContent(c+1,r+1)<0.01 ) {
hist.SetBinContent( c+1, r+1, 0.01 );
}
else if (label_v[0]>0.0) {
float dist = 0.;
for (int i=0; i<3; i++) dist += label_v[1+i]*label_v[1+i];
float score = exp( -0.5*dist/(sigma*sigma) );
if ( hist.GetBinContent(c+1,r+1)<score )
hist.SetBinContent( c+1, r+1, score );
}
}
hist_v.emplace_back( std::move(hist) );
}
return hist_v;
}
}
}