Program Listing for File ShowerRecoKeypoint.cxx¶
↰ Return to documentation for file (larflow/Reco/ShowerRecoKeypoint.cxx
)
#include "ShowerRecoKeypoint.h"
#include <ctime>
#include <fstream>
#include "larcv/core/DataFormat/EventImage2D.h"
#include "nlohmann/json.hpp"
#include <cilantro/principal_component_analysis.hpp>
#include "DataFormat/larflow3dhit.h"
#include "DataFormat/larflowcluster.h"
#include "DataFormat/pcaxis.h"
#include "larflow/LArFlowConstants/LArFlowConstants.h"
#include "geofuncs.h"
namespace larflow {
namespace reco {
void ShowerRecoKeypoint::process( larcv::IOManager& iolcv, larlite::storage_manager& ioll ) {
// clear member containers
_shower_cand_v.clear();
_recod_shower_v.clear();
// get shower larflow hits (use SplitHitsBySSNet)
larlite::event_larflow3dhit* shower_lfhit_v
= (larlite::event_larflow3dhit*)ioll.get_data( larlite::data::kLArFlow3DHit, _ssnet_lfhit_tree_name );
std::clock_t begin_process = std::clock();
LARCV_INFO() << "start" << std::endl;
LARCV_INFO() << "num larflow hits from [" << _ssnet_lfhit_tree_name << "]: " << shower_lfhit_v->size() << std::endl;
// filter out shower pixels by larmatch score
larlite::event_larflow3dhit shower_goodhit_v;
for (auto const& hit : *shower_lfhit_v ) {
if ( hit.track_score>_larmatch_score_threshold ) {
shower_goodhit_v.push_back(hit);
}
}
LARCV_INFO() << "number of above-threshold hits: " << shower_goodhit_v.size() << " of " << shower_lfhit_v->size() << std::endl;
// make shower clusters
float maxdist = 5.0;
int minsize = 20;
int maxkd = 20;
std::vector<cluster_t> cluster_v;
cluster_larflow3dhits( shower_goodhit_v, cluster_v, maxdist, minsize, maxkd );
LARCV_INFO() << "num shower clusters: " << cluster_v.size() << std::endl;
// now for each shower cluster, we find some trunk candidates.
// can have any number of such candidates per shower cluster
// we only analyze clusters with a first pc-axis length > 1.0 cm
std::vector< const cluster_t* > showerhit_cluster_v;
std::vector<int> cluster_used_v( cluster_v.size(), 0 );
int idx = -1;
for ( auto& showercluster : cluster_v ) {
idx++;
cluster_pca( showercluster );
// metrics to choose shower trunk candidates
// length
float len = showercluster.pca_len;
// pca eigenvalue [1]/[0] ratio -- to ensure straightness
float eigenval_ratio = showercluster.pca_eigenvalues[1]/showercluster.pca_eigenvalues[0];
LARCV_DEBUG() << "shower cluster[" << idx << "]"
<< " pca-len=" << len << " cm,"
<< " pca-eigenval-ratio=" << eigenval_ratio
<< std::endl;
if ( len<1.0 ) continue;
//if ( eigenval_ratio<0.1 ) continue;
cluster_used_v[idx] = 1;
showerhit_cluster_v.push_back( &showercluster );
}
// save shower cluster pca's
larlite::event_larflowcluster* evout_goodhit_cluster_v
= (larlite::event_larflowcluster*)ioll.get_data( larlite::data::kLArFlowCluster, "showergoodhit" );
larlite::event_pcaxis* evout_goodhit_pca_v
= (larlite::event_pcaxis*)ioll.get_data( larlite::data::kPCAxis, "showergoodhit" );
int pcidx = 0;
for ( auto const& cluster : cluster_v ) {
larlite::larflowcluster lfc;
for (auto const& idx : cluster.hitidx_v ) {
lfc.push_back( shower_goodhit_v[idx] );
}
larlite::pcaxis pca = cluster_make_pcaxis( cluster, pcidx );
pcidx++;
evout_goodhit_cluster_v->emplace_back( std::move(lfc) );
evout_goodhit_pca_v->emplace_back( std::move(pca) );
}
LARCV_INFO() << "num of trunk candidates: " << showerhit_cluster_v.size() << std::endl;
// GET KEYPOINT DATA
std::vector< const larlite::larflow3dhit* > keypoint_v;
larlite::event_larflow3dhit* evout_keypoint =
(larlite::event_larflow3dhit*)ioll.get_data(larlite::data::kLArFlow3DHit,"keypoint");
for ( auto const& kp : *evout_keypoint ) {
if (kp.at(3)==(int)larflow::kShowerStart ) {
keypoint_v.push_back( &kp );
}
}
LARCV_INFO() << "number of shower keypoints: " << keypoint_v.size() << " of " << evout_keypoint->size() << std::endl;
// MAKE TRUNK CANDIDATES FOR EACH SHOWER
_reconstructClusterTrunks( showerhit_cluster_v, keypoint_v );
// BUILD SHOWERS FROM CLUSTERS + TRUNK CANDIDATES
_buildShowers( showerhit_cluster_v );
std::clock_t end_process = std::clock();
LARCV_INFO() << "[ShowerRecoKeypoint::process] end; elapsed = "
<< float(end_process-begin_process)/CLOCKS_PER_SEC << " secs"
<< std::endl;
larlite::event_larflowcluster* evout_shower_cluster_v
= (larlite::event_larflowcluster*)ioll.get_data( larlite::data::kLArFlowCluster, "showerkp" );
larlite::event_pcaxis* evout_shower_pca_v
= (larlite::event_pcaxis*)ioll.get_data( larlite::data::kPCAxis, "showerkp" );
larlite::event_larflow3dhit* evout_shower_keypoint_v
= (larlite::event_larflow3dhit*)ioll.get_data( larlite::data::kLArFlow3DHit, "showerkp" );
std::vector<int> used_v( shower_goodhit_v.size(), 0 );
for ( size_t ireco=0; ireco<_recod_shower_v.size(); ireco++ ) {
auto const& recoshower = _recod_shower_v[ireco];
// make larflow3dhit cluster
larlite::larflowcluster lfcluster;
for ( auto const& idx : recoshower.hitidx_v ) {
lfcluster.push_back( shower_goodhit_v.at(idx) );
used_v[idx]++;
}
evout_shower_cluster_v->emplace_back( std::move(lfcluster) );
// we store the cluster's pca if we do not have trunk candidates, otherwise
larlite::pcaxis::EigenVectors e_v;
std::vector<double> axis_v(3,0);
for (int v=0; v<3; v++) axis_v[v] = recoshower.trunk.pcaxis_v[v];
e_v.push_back( axis_v );
e_v.push_back( axis_v );
e_v.push_back( axis_v );
std::vector<double> startpt(3,0);
std::vector<double> endpt(3,0);
for (int v=0; v<3; v++ ) {
startpt[v] = recoshower.trunk.start_v[v];
endpt[v] = startpt[v] + 50.0*axis_v[v];
}
e_v.push_back( startpt );
e_v.push_back( endpt );
double eigenval[3] = { 10, 0, 0 };
double centroid[3] = { (double)recoshower.trunk.center_v[0],
(double)recoshower.trunk.center_v[1],
(double)recoshower.trunk.center_v[2] };
larlite::pcaxis pca( true,
recoshower.trunk.npts,
eigenval,
e_v,
centroid,
0,
(int)ireco );
evout_shower_pca_v->emplace_back( std::move(pca) );
larlite::larflow3dhit shower_keypoint;
shower_keypoint.resize(3,0);
for (int v=0; v<3; v++) {
shower_keypoint[v] = recoshower.trunk.keypoint->at(v);
}
evout_shower_keypoint_v->push_back( shower_keypoint );
}//end of reco'd shower loop
// save unused shower points
larlite::event_larflow3dhit* evout_unused_hit_v
= (larlite::event_larflow3dhit*)ioll.get_data( larlite::data::kLArFlow3DHit, "showerkpunused" );
for (size_t idx=0; idx<shower_goodhit_v.size(); idx++ ) {
if ( used_v[idx]==0 )
evout_unused_hit_v->push_back( shower_goodhit_v.at(idx) );
}
LARCV_INFO() << "Unused hits: " << evout_unused_hit_v->size() << std::endl;
}
void ShowerRecoKeypoint::_reconstructClusterTrunks( const std::vector<const cluster_t*>& showercluster_v,
const std::vector<const larlite::larflow3dhit*>& keypoint_v )
{
const float radii[3] = { 10, 5, 3 };
for ( size_t ishower=0; ishower<showercluster_v.size(); ishower++ ) {
// get shower
const cluster_t* pshower = showercluster_v[ishower];
ShowerCandidate_t shower_cand;
shower_cand.cluster_idx = ishower;
shower_cand.cluster = pshower;
// loop over keypoints, finding shower pixels within 5,3,1
for ( size_t ikeypoint=0; ikeypoint<keypoint_v.size(); ikeypoint++ ) {
const larlite::larflow3dhit& keypoint = *(keypoint_v.at(ikeypoint));
std::vector< std::vector<float> > pcaxis_v(3);
std::vector< std::vector<float> > pca_center_v(3);
std::vector< float > pca_eigenval_ratio(3,0.0);
std::vector< float > impact_par_v(3,0.0);
bool inbbox = true;
for (int v=0; v<3; v++) {
if ( keypoint[v]<pshower->bbox_v[v][0]-10.0 || keypoint[v]>pshower->bbox_v[v][1]+10.0 )
inbbox = false;
if ( !inbbox ) break;
}
if ( !inbbox )
continue;
std::vector< cluster_t > trunk_v(3);
float mindist = 1e9;
for ( size_t ihit=0; ihit<pshower->points_v.size(); ihit++ ) {
float dist = 0.;
for (size_t v=0; v<3; v++)
dist += ( pshower->points_v[ihit][v]-keypoint[v] )*( pshower->points_v[ihit][v]-keypoint[v] );
dist = sqrt(dist);
for (size_t irad=0; irad<3; irad++) {
if ( dist<radii[irad] ) {
auto const& pt = pshower->points_v[ihit];
trunk_v[irad].points_v.push_back( std::vector<float>( {pt[0], pt[1], pt[2]} ) );
trunk_v[irad].hitidx_v.push_back( ihit );
}
}
if ( dist<mindist )
mindist = dist;
}//end of hit loop
// get pca for each length, must have >10 points
// we pick the best trunk by
// (1) trunk must have pca-ratio<0.15
// (2) rank by smallest impact parameter
LARCV_DEBUG() << "[ shower cluster[" << ishower << "] keypoint[" << ikeypoint << "] ]" << std::endl;
LARCV_DEBUG() << " keypoint pos: (" << keypoint[0] << "," << keypoint[1] << "," << keypoint[2] << ")" << std::endl;
LARCV_DEBUG() << " gap-dist: " << mindist << " cm" << std::endl;
if ( mindist>1.0 ) continue;
int best_trunk = -1;
float best_metric = -1;
float best_trunk_impactpar = 1e9;
float best_trunk_eratio = 1e9;
std::vector<int> trunk_best_pca_end_v(3,-1);
std::vector<int> trunk_best_shower_end_v(3,-1);
for (size_t irad=0; irad<3; irad++) {
cluster_t& cluster = trunk_v[irad];
if ( cluster.points_v.size()<10 ) continue;
cluster_pca( cluster );
//LARCV_DEBUG() << " radius[" << radii[irad] << "] num points: " << trunk_v[irad].size() << std::endl;
float eratio = cluster.pca_eigenvalues[1]/cluster.pca_eigenvalues[0];
std::vector<float> e_v = cluster.pca_axis_v[0];
std::vector<float> center_v = cluster.pca_center;
pcaxis_v[irad] = e_v;
pca_center_v[irad] = center_v;
pca_eigenval_ratio[irad] = eratio;
// find which trunk end is furthest from cluster center
float max_end_dist = 0;
int best_end = 0;
for (int iend=0; iend<2; iend++ ) {
float distend = 0.;
for (int v=0; v<3; v++ ){
distend += ( cluster.pca_ends_v[iend][v]-pshower->pca_center[v] )*( cluster.pca_ends_v[iend][v]-pshower->pca_center[v] );
}
if ( distend>max_end_dist ) {
best_end = iend;
max_end_dist = distend;
}
}
trunk_best_pca_end_v[irad] = best_end;
// we check if the trunk-pca and the cluster pca vary a lot
// we default to the shower pca if they do, treating the trunk pca as a refinement
// we find this is necessary as the KPS network's larmatch predictions are pretty noisy right now
// maybe if we get this to larmatch v1 performance (which is good), then we can go
// back to using the trunk pca.
// this is just a law of large numbers effect i think, i.e.
// since the shower cluster is more points, there are more right 3d point predictions
// still, shower clusters can have very weird shapes ...
float cluster_trunk_cos = 0.;
for (int v=0; v<3; v++) {
cluster_trunk_cos += pcaxis_v[irad][v]*pshower->pca_axis_v[0][v];
}
cluster_trunk_cos = fabs(cluster_trunk_cos);
// find closest shower cluster end
float min_shower_end = 1e9;
int best_shower_end = 0;
for (int iend=0; iend<2; iend++) {
float distend=0;
for (int v=0; v<3; v++) {
distend += ( pshower->pca_ends_v[iend][v]-keypoint[v] )*( pshower->pca_ends_v[iend][v]-keypoint[v] );
}
if (distend<min_shower_end ) {
min_shower_end = distend;
best_shower_end = iend;
}
}
trunk_best_shower_end_v[irad] = best_shower_end;
// distance from trunk axis to keypoint
std::vector<float> kp = { keypoint[0], keypoint[1], keypoint[2] };
std::vector<float> pt2(3,0);
for (int v=0; v<3; v++) pt2[v] = center_v[v] + e_v[v];
float impact = pointLineDistance( center_v, e_v, kp );
impact_par_v[irad] = impact;
// // closest cluster end
// int iend = 0;
// float enddist[2][3] = { 0 };
// for (int v=0; v<3; v++) {
// enddist[0][v] = ( kp[v]-
LARCV_DEBUG() << " radius["<< radii[irad] << " cm]: "
<< " pca ratio=" << pca_eigenval_ratio[irad]
<< " pca-0=(" << pcaxis_v[irad][0] << "," << pcaxis_v[irad][1] << "," << pcaxis_v[irad][2] << ")"
<< " impactpar=" << impact
<< " |trunk.shower|=" << cluster_trunk_cos
<< std::endl;
// [note] e-ratio an important parameter that needs configuration
if ( eratio<0.3 && impact<5.0 && ( cluster_trunk_cos>best_metric || best_trunk==-1 ) ) {
best_trunk = irad;
best_trunk_impactpar = impact;
best_trunk_eratio = eratio;
best_metric = cluster_trunk_cos;
}
}// loop over radius size, calculating radius
// define the best trunk candidate
if ( best_trunk!=-1 ) {
ShowerTrunk_t trunk;
trunk.idx_keypoint = ikeypoint;
trunk.keypoint = &keypoint;
trunk.pcaxis_v = pcaxis_v[best_trunk];
trunk.center_v = pca_center_v[best_trunk];
trunk.start_v = trunk_v[best_trunk].pca_ends_v[ trunk_best_pca_end_v[best_trunk] ];
//trunk.start_v = pshower->pca_ends_v[ trunk_best_shower_end_v[best_trunk] ];
trunk.pca_eigenval_ratio = pca_eigenval_ratio[best_trunk];
trunk.npts = (int)trunk_v[best_trunk].points_v.size();
trunk.gapdist = mindist;
trunk.impact_par = impact_par_v[best_trunk];
// we make sure the pca-axis is pointing to the rest of the cluster
// we use the distance of the trunk center to the whole shower cluster center
float coscenter = 0.;
for (size_t v=0; v<3; v++) {
coscenter += trunk.pcaxis_v[v]*( pshower->pca_center[v]-trunk.start_v[v] );
}
if ( coscenter<0 ) {
// flip the axis dir
for (size_t v=0; v<3; v++)
trunk.pcaxis_v[v] *= -1.0;
}
LARCV_DEBUG() << "define shower[" << ishower << "] keypoint[" << ikeypoint << "] trunk" << std::endl;
LARCV_DEBUG() << " gap-dist: " << trunk.gapdist << " cm" << std::endl;
LARCV_DEBUG() << " eigenval ratio: " << trunk.pca_eigenval_ratio << std::endl;
LARCV_DEBUG() << " npts: " << trunk.npts << std::endl;
LARCV_DEBUG() << " impact-par: " << trunk.impact_par << " cm" << std::endl;
shower_cand.trunk_candidates_v.emplace_back( std::move(trunk) );
}
}//end of keypoint
LARCV_INFO() << "Saving shower candidate with " << shower_cand.trunk_candidates_v.size() << " trunk candidates" << std::endl;
// we will pick the best trunk candidate later when we expand the shower candidates with nearby clusters
_shower_cand_v.emplace_back( std::move(shower_cand) );
}//end of shower cluster loop
}
void ShowerRecoKeypoint::_buildShowers( const std::vector< const cluster_t*>& showerhit_cluster_v )
{
int nbad_cands = 0;
for ( auto const& shower_cand : _shower_cand_v ) {
if ( shower_cand.trunk_candidates_v.size()==0 ) continue;
Shower_t shower = _buildShowerCandidate( shower_cand, showerhit_cluster_v );
if (shower.cluster_idx.size()>0 ) {
_recod_shower_v.emplace_back( std::move(shower) );
}
else
nbad_cands++;
}
LARCV_INFO() << "Number of reco'd shower candidates. "
<< "ngood=" << _recod_shower_v.size()
<< " nbad=" << nbad_cands
<< std::endl;
// now we deal with the clusters that were claimed by more than shower
// first we look for these by making a map from cluster index to a set with
// shower indices (following the _recod_shower_v container)
std::map< int, std::set<int> > claiming_shower_idx;
for ( int shwr_idx=0; shwr_idx<(int)_recod_shower_v.size(); shwr_idx++ ) {
auto const& shower = _recod_shower_v[shwr_idx];
for ( auto const& cluster_idx : shower.cluster_idx ) {
if ( claiming_shower_idx.find(cluster_idx)==claiming_shower_idx.end() ) {
// insert new index with empty set
claiming_shower_idx[cluster_idx] = std::set<int>();
}
claiming_shower_idx[cluster_idx].insert( shwr_idx );
}
}
// now we resolve the conflicts by pitting the showers together.
// the one with the lowest average least squares to the axis keeps the cluster.
for ( auto it=claiming_shower_idx.begin(); it!=claiming_shower_idx.end(); it++ ) {
int cluster_idx = it->first;
std::set<int>& shower_idx_v = it->second;
int best_shower_idx = _chooseBestShowerForCluster( *showerhit_cluster_v[cluster_idx],
shower_idx_v,
showerhit_cluster_v );
// remove cluster index from shower's list
for ( auto& shwr_idx : shower_idx_v ) {
if ( best_shower_idx==shwr_idx ) {
LARCV_DEBUG() << "shower[" << shwr_idx << "] keeps cluster[" << cluster_idx << "]" << std::endl;
continue; // let the best shower keep the cluster index
}
auto& shower = _recod_shower_v[shwr_idx];
LARCV_DEBUG() << "shower[" << shwr_idx << "] removes cluster[" << cluster_idx << "]" << std::endl;
auto it_remove = shower.cluster_idx.find( cluster_idx );
shower.cluster_idx.erase( it_remove );
}
}
// fill the shower objects with hits
int sidx=0;
for ( auto& shower : _recod_shower_v ) {
_fillShowerObject( shower, showerhit_cluster_v );
std::cout << "made shower[" << sidx << "] with " << shower.points_v.size() << std::endl;
sidx++;
}
}
ShowerRecoKeypoint::Shower_t
ShowerRecoKeypoint::_buildShowerCandidate( const ShowerCandidate_t& shower_cand,
const std::vector< const cluster_t*>& showerhit_cluster_v )
{
std::vector< std::set<int> > trunk_cluster_idxset_v;
LARCV_DEBUG() << "Build shower candidate. showercluster[" << shower_cand.cluster_idx << "]" << std::endl;
// for each trunk candidate in the shower candidate, we let it absorb hits
for (auto const& trunk : shower_cand.trunk_candidates_v ) {
//std::set<int> clusters = _buildoutShowerTrunkCandidate( trunk, showerhit_cluster_v );
// just
std::set<int> clusters;
clusters.insert( shower_cand.cluster_idx );
if ( clusters.size()>0 )
trunk_cluster_idxset_v.push_back(clusters);
}
if ( trunk_cluster_idxset_v.size()==0 ) {
LARCV_DEBUG() << "shower candidate returns empty shower" << std::endl;
return Shower_t(); //empty
}
if ( trunk_cluster_idxset_v.size()==1 ) {
// single trunk candidate case
// build the shower
LARCV_DEBUG() << "shower candidate with one trunk returns non-empty shower" << std::endl;
Shower_t shower;
shower.trunk = shower_cand.trunk_candidates_v[0];
shower.cluster_idx = _buildoutShowerTrunkCandidate( shower.trunk, showerhit_cluster_v );
return shower;
}
// now we have to evaluate which shower trunk is best
std::set<int> union_cluster_idx;
for ( auto& idx_set : trunk_cluster_idxset_v ) {
for (auto& idx : idx_set ) {
union_cluster_idx.insert(idx);
}
}
LARCV_DEBUG() << "showercluster[" << shower_cand.cluster_idx << "]"
<< "union cluster list size=" << union_cluster_idx.size() << std::endl;
// return least squares value for each shower over the entirety of the cluster set
// lowest value is considered the "best" cluster.
int best_trunk_idx = _chooseBestTrunk( shower_cand,
union_cluster_idx,
showerhit_cluster_v );
LARCV_DEBUG() << "returns best-fit trunk candidate" << std::endl;
Shower_t shower;
shower.trunk = shower_cand.trunk_candidates_v[best_trunk_idx];
// build out cluster after modifying direction to use pca-axis of cluster itself
shower.cluster_idx = _buildoutShowerTrunkCandidate( shower.trunk, showerhit_cluster_v );
return shower;
}
std::set<int> ShowerRecoKeypoint::_buildoutShowerTrunkCandidate( const ShowerTrunk_t& trunk_cand,
const std::vector< const cluster_t*>& showerhit_cluster_v )
{
const float ar_molier_rad_cm = 9.04;
std::set<int> clusteridx_v;
float pcalen = 0.;
for (int v=0; v<3; v++)
pcalen += trunk_cand.pcaxis_v[v]*trunk_cand.pcaxis_v[v];
pcalen = sqrt(pcalen);
for ( size_t idx=0; idx<showerhit_cluster_v.size(); idx++) {
auto const& cluster = *showerhit_cluster_v[idx];
// make two points along axis
std::vector<float> alongpca(3,0);
for (int v=0; v<3; v++)
alongpca[v] = trunk_cand.center_v[v] + 10.0*trunk_cand.pcaxis_v[v];
// first test bounding box before going in and checking this
bool testcluster = false;
// make bbox 3d points
// 2^3=8 bounding box vertex points
std::vector<float> bbox_pt(3,0);
// make permutation vector
int state[3] = { 0, 0, 0 };
for (int i=0; i<8; i++) {
// generate bounding box pt
//LARCV_DEBUG() << "permutation-vector[" << i << ": (" << state[0] << "," << state[1] << "," << state[2] << ")" << std::endl;
for (int v=0; v<3; v++)
bbox_pt[v] = cluster.bbox_v[v][state[v]];
// test
float dist = pointLineDistance( trunk_cand.center_v, alongpca, bbox_pt );
float proj = 0.;
for (int v=0; v<3; v++ ) {
proj += (bbox_pt[v]-trunk_cand.keypoint->at(v))*trunk_cand.pcaxis_v[v];
}
proj /= pcalen;
if ( dist<1.0*ar_molier_rad_cm && fabs(proj)<50.0 ) {
testcluster = true;
break;
}
// increment to next permutation
for (int v=0; v<3; v++) {
if ( state[v]!=1 ) {
state[v]++;
break;
}
else {
state[v]=0;
}
}
}//end of loop over bbox pt permutations
// nothing close
if ( !testcluster ) {
//LARCV_DEBUG() << "No bounding box points are close" << std::endl;
continue;
}
// if bbox pt close, we test to see if we absorb cluster
// defined as 10% of points inside molier radius (this needs tuning)
int npts_inside = 0;
int npts_outside = 0;
int npts_threshold = int( 0.1*cluster.points_v.size() );
if ( npts_threshold==0 )
npts_threshold = 1;
int npts_reject = (int)cluster.points_v.size()-npts_threshold;
bool accept = false;
for ( auto const& hit : cluster.points_v ) {
float dist = pointLineDistance( trunk_cand.center_v, alongpca, hit );
float proj = 0.;
for (int v=0; v<3;v++)
proj += ( hit[v]-trunk_cand.keypoint->at(v) )*trunk_cand.pcaxis_v[v];
proj /= pcalen;
if ( (proj>0.0 && dist<0.5*ar_molier_rad_cm && proj<50.0 )
|| (proj<=0.0 && proj>-3.0 && dist<3.0 ) ) {
npts_inside++;
}
else {
npts_outside++;
}
if ( npts_inside>=npts_threshold ) {
accept = true;
break;
}
else if ( npts_outside>npts_reject ) {
break;
}
}
LARCV_DEBUG() << "Eval cluster: npt_inside=" << npts_inside
<< " npts_outside=" << npts_outside
<< " npts_threshold=" << npts_threshold
<< std::endl;
if ( accept ) {
clusteridx_v.insert( idx );
}
}
return clusteridx_v;
}
ShowerRecoKeypoint::Shower_t
ShowerRecoKeypoint::_fillShowerObject( const ShowerCandidate_t& shower_cand,
const std::set<int>& cluster_idx_set,
const int trunk_idx,
const std::vector< const cluster_t* >& showerhit_cluster_v )
{
Shower_t recoshower;
recoshower.trunk = shower_cand.trunk_candidates_v[trunk_idx];
for ( auto const& idx : cluster_idx_set ) {
auto const& cluster = *showerhit_cluster_v[idx];
for (size_t ihit=0; ihit<cluster.points_v.size(); ihit++ ) {
recoshower.points_v.push_back( cluster.points_v[ihit] );
recoshower.hitidx_v.push_back( cluster.hitidx_v[ihit] );
}
}
return recoshower;
}
void ShowerRecoKeypoint::_fillShowerObject( Shower_t& shower,
const std::vector< const cluster_t* >& showerhit_cluster_v )
{
for ( auto const& idx : shower.cluster_idx ) {
auto const& cluster = *showerhit_cluster_v[idx];
for (size_t ihit=0; ihit<cluster.points_v.size(); ihit++ ) {
shower.points_v.push_back( cluster.points_v[ihit] );
shower.hitidx_v.push_back( cluster.hitidx_v[ihit] );
}
}
}
int ShowerRecoKeypoint::_chooseBestTrunk( const ShowerCandidate_t& shower_cand,
const std::set<int>& cluster_idx_v,
const std::vector< const cluster_t* >& showerhit_cluster_v )
{
float max_ll = -1e9;
int best_trunk_idx = -1;
for ( int trunk_idx=0; trunk_idx<(int)shower_cand.trunk_candidates_v.size(); trunk_idx++ ) {
auto const& trunk_cand = shower_cand.trunk_candidates_v[trunk_idx];
std::vector<float> alongpca(3,0);
for (int v=0; v<3; v++)
alongpca[v] = trunk_cand.center_v[v] + 10.0*trunk_cand.pcaxis_v[v];
// we pick by maximizing log-likelihood
// we build a likelihood via
// P(s,r) = exp(-r/r_M)*exp(-s/X_0)
// where r_M is the molier radius
// where X_0 is the radiation length
float ll = 0.;
int npoints = 0;
float w_tot = 0.;
for ( auto const& idx : cluster_idx_v ) {
for (auto const& hit : showerhit_cluster_v[idx]->points_v ) {
float dist = pointLineDistance( trunk_cand.center_v, alongpca, hit );
float proj = 0.;
for (int v=0; v<3; v++) {
proj += (hit[v]-trunk_cand.start_v[v])*trunk_cand.pcaxis_v[v];
}
if ( proj<0 )
proj = 0.;
ll += -sqrt(dist)/9.0;
if ( proj>0 )
ll += -proj/14.0;
else
ll += proj;
npoints++;
w_tot += 1.0;
}
}
if ( npoints>0 )
ll /= w_tot;
LARCV_DEBUG() << "trunk cand[" << trunk_idx << " of " << shower_cand.trunk_candidates_v.size() << "] "
<< " kp=(" << trunk_cand.keypoint->at(0) << "," << trunk_cand.keypoint->at(1) << "," << trunk_cand.keypoint->at(2) << ") "
<< " log(L)=" << ll << " for npoints=" << npoints
<< std::endl;
if ( best_trunk_idx==-1 || ll>max_ll ) {
max_ll = ll;
best_trunk_idx = trunk_idx;
}
}
LARCV_DEBUG() << "Choosing trunk index=" << best_trunk_idx << " with max_ll=" << max_ll << std::endl;
return best_trunk_idx;
}
int ShowerRecoKeypoint::_chooseBestShowerForCluster( const cluster_t& cluster,
const std::set<int>& shower_idx_v,
const std::vector< const cluster_t* >& showerhit_cluster_v )
{
float min_least_sq = -1;
int best_shower_idx = -1;
for ( auto const& shower_idx : shower_idx_v ) {
auto const& shower = _recod_shower_v.at(shower_idx);
float ls = 0.;
std::vector<float> alongpca(3,0);
for (int v=0; v<3; v++)
alongpca[v] = shower.trunk.center_v[v] + 10.0*shower.trunk.pcaxis_v[v];
int npoints = 0;
for ( auto const& hit : cluster.points_v ) {
float dist = pointLineDistance( shower.trunk.center_v, alongpca, hit );
ls += dist*dist;
npoints++;
}
if ( npoints>0 )
ls /= float(npoints);
LARCV_DEBUG() << "reco shower cand[" << shower_idx << "] "
<< " least-sq/npoints=" << ls << " for npoints=" << npoints
<< std::endl;
if ( min_least_sq<0 || min_least_sq>ls ) {
min_least_sq = ls;
best_shower_idx = shower_idx;
}
}
LARCV_DEBUG() << "Choosing shower with index=" << best_shower_idx << std::endl;
return best_shower_idx;
}
}
}