BlobClustering.cxx
Go to the documentation of this file.
5 
6 #include <boost/graph/graphviz.hpp>
7 
10 
11 using namespace WireCell;
12 
13 
15  : m_spans(1.0)
16  , m_last_bs(nullptr)
17  , l(Log::logger("img"))
18 {
19 }
20 Img::BlobClustering::~BlobClustering()
21 {
22 }
23 
25 {
26  m_spans = get(cfg, "spans", m_spans);
27 }
28 
29 WireCell::Configuration Img::BlobClustering::default_configuration() const
30 {
32  // A number multiplied to the span of the current slice when
33  // determining it a gap exists between the next slice. Default is
34  // 1.0. Eg, if set to 2.0 then a single missing slice won't be
35  // grounds for considering a gap in the cluster. A number less
36  // than 1.0 will cause each "cluster" to consist of only one blob.
37  cfg["spans"] = m_spans;
38  return cfg;
39 }
40 
42 {
43  clusters.push_back(std::make_shared<SimpleCluster>(m_grind.graph()));
44  m_grind.clear();
45  m_last_bs = nullptr;
46 }
47 
48 void Img::BlobClustering::intern(const input_pointer& newbs)
49 {
50  m_last_bs = newbs;
51 }
52 
53 bool Img::BlobClustering::judge_gap(const input_pointer& newbs)
54 {
55  const double epsilon = 1*units::ns;
56 
57  if (m_spans <= epsilon) {
58  return false; // never break on gap.
59  }
60 
61  auto nslice = newbs->slice();
62  auto oslice = m_last_bs->slice();
63 
64  const double dt = nslice->start() - oslice->start();
65  return std::abs(dt - m_spans*oslice->span()) > epsilon;
66 }
67 
68 
69 void Img::BlobClustering::add_slice(const ISlice::pointer& islice)
70 {
71  if (m_grind.has(islice)) {
72  return;
73  }
74 
75  for (const auto& ichv : islice->activity()) {
76  const IChannel::pointer ich = ichv.first;
77  if (m_grind.has(ich)) {
78  continue;
79  }
80  for (const auto& iwire : ich->wires()) {
81  m_grind.edge(ich, iwire);
82  }
83  }
84 }
85 
86 void Img::BlobClustering::add_blobs(const input_pointer& newbs)
87 {
88  for (const auto& iblob : newbs->blobs()) {
89  auto islice = iblob->slice();
90  add_slice(islice);
91  m_grind.edge(islice, iblob);
92 
93  auto iface = iblob->face();
94  auto wire_planes = iface->planes();
95 
96  const auto& shape = iblob->shape();
97  for (const auto& strip : shape.strips()) {
98  // FIXME: need a way to encode this convention!
99  // For now, it requires collusion. Don't impeach me.
100  const int num_nonplane_layers = 2;
101  int iplane = strip.layer - num_nonplane_layers;
102  if (iplane < 0) {
103  continue;
104  }
105  const auto& wires = wire_planes[iplane]->wires();
106  for (int wip=strip.bounds.first; wip < strip.bounds.second and wip < int(wires.size()); ++wip) {
107  auto iwire = wires[wip];
108  m_grind.edge(iblob, iwire);
109  }
110  }
111  }
112 }
113 
114 bool Img::BlobClustering::graph_bs(const input_pointer& newbs)
115 {
116  add_blobs(newbs);
117 
118  if (!m_last_bs) {
119  // need to wait for next one to do anything.
120  // note, caller interns.
121  return false;
122  }
123  if (judge_gap(newbs)) {
124  // nothing to do, but pass on that we hit a gap
125  return true;
126  }
127 
128  // handle each face separately faces
129  IBlob::vector iblobs1 = newbs->blobs();
130  IBlob::vector iblobs2 = m_last_bs->blobs();
131 
132  RayGrid::blobs_t blobs1 = newbs->shapes();
133  RayGrid::blobs_t blobs2 = m_last_bs->shapes();
134 
135  const auto beg1 = blobs1.begin();
136  const auto beg2 = blobs2.begin();
137 
138  auto assoc = [&](RayGrid::blobref_t& a, RayGrid::blobref_t& b) {
139  int an = a - beg1;
140  int bn = b - beg2;
141  m_grind.edge(iblobs1[an], iblobs2[bn]);
142  };
143  RayGrid::associate(blobs1, blobs2, assoc);
144 
145 
146 
147  return false;
148 }
149 
150 bool Img::BlobClustering::operator()(const input_pointer& blobset, output_queue& clusters)
151 {
152  if (!blobset) { // eos
153  l->debug("BlobClustering: EOS");
154  flush(clusters);
155  clusters.push_back(nullptr); // forward eos
156  return true;
157  }
158 
159  SPDLOG_LOGGER_TRACE(l,"BlobClustering: got {} blobs", blobset->blobs().size());
160 
161  bool gap = graph_bs(blobset);
162  if (gap) {
163  flush(clusters);
164  l->debug("BlobClustering: sending {} clusters", clusters.size());
165  // note: flush fast to keep memory usage in this component
166  // down and because in an MT job, downstream components might
167  // benefit to start consuming clusters ASAP. We do NOT want
168  // to intern() the new blob set BEFORE a flush if there is a
169  // gap because newbs is needed for next time and fush clears
170  // the cache.
171  }
172 
173  intern(blobset);
174 
175  SPDLOG_LOGGER_TRACE(l,"BlobClustering: holding {}", boost::num_vertices(m_grind.graph()));
176 
177  return true;
178 }
179 
std::shared_ptr< const ISlice > pointer
Definition: IData.h:19
cfg
Definition: dbjson.py:29
std::vector< pointer > vector
Definition: IData.h:21
static QStrList * l
Definition: config.cpp:1044
std::deque< output_pointer > output_queue
T abs(T value)
blobs_t::const_iterator blobref_t
Definition: RayClustering.h:13
def configure(cfg)
Definition: cuda.py:34
QTextStream & flush(QTextStream &s)
logptr_t logger(std::string name)
Definition: Logging.cxx:71
const GenericPointer< typename T::ValueType > T2 T::AllocatorType & a
Definition: pointer.h:1124
Definition: Main.h:22
std::vector< Blob > blobs_t
Definition: RayTiling.h:134
#define SPDLOG_LOGGER_TRACE(logger,...)
Definition: spdlog.h:319
Json::Value Configuration
Definition: Configuration.h:50
static bool * b
Definition: config.cpp:1043
std::shared_ptr< const IBlobSet > input_pointer
bool associate(const std::string &classname, WireCell::INamedFactory *factory)
Associate a factory with the type it makes.
Definition: NamedFactory.h:210
QAsciiDict< Entry > ns
WIRECELL_FACTORY(BlobClustering, WireCell::Img::BlobClustering, WireCell::IClustering, WireCell::IConfigurable) using namespace WireCell