diff --git a/Application/src/tracker/gui/IdentityHeatmap.cpp b/Application/src/tracker/gui/IdentityHeatmap.cpp index 7801669d514edc5236d8850ba2e88bae5fa3286a..96c8065574c19386eda887a82df13e05af07da11 100644 --- a/Application/src/tracker/gui/IdentityHeatmap.cpp +++ b/Application/src/tracker/gui/IdentityHeatmap.cpp @@ -173,6 +173,8 @@ void HeatmapController::sort_data_into_custom_grid() { std::vector<double> values; std::fill(_array_samples.begin(), _array_samples.end(), _normalization == normalization_t::cell ? 1 : 0); std::fill(_array_grid.begin(), _array_grid.end(), 0); + if(_normalization == normalization_t::variance) + std::fill(_array_sqsum.begin(), _array_sqsum.end(), 0); if(_normalization == normalization_t::cell && isPowerOfTwo(uniform_grid_cell_size)) @@ -194,7 +196,7 @@ void HeatmapController::sort_data_into_custom_grid() { }); } else if(isPowerOfTwo(uniform_grid_cell_size) - && (_normalization == normalization_t::none || _normalization == normalization_t::value)) + && (_normalization == normalization_t::none || _normalization == normalization_t::value || _normalization == normalization_t::variance)) { values.clear(); @@ -210,10 +212,15 @@ void HeatmapController::sort_data_into_custom_grid() { _array_grid.at(i) += r.value_sum(); _array_samples[i] += r.size(); - if(_normalization != normalization_t::none) { - values.push_back(r.value_range().start); - values.push_back(r.value_sum() / double(r.size())); - values.push_back(r.value_range().end); + if(_normalization == normalization_t::variance) { + _array_sqsum.at(i) += r.value_sqsum(); + + } else { + if(_normalization != normalization_t::none) { + values.push_back(r.value_range().start); + values.push_back(r.value_sum() / double(r.size())); + values.push_back(r.value_range().end); + } } //! do not traverse deeper @@ -221,7 +228,7 @@ void HeatmapController::sort_data_into_custom_grid() { }); } else { - if(_normalization == normalization_t::value) + if(_normalization != normalization_t::none) values.reserve(_grid.size()); _grid.apply<Leaf>([&](const Leaf& leaf) -> bool { @@ -246,6 +253,10 @@ void HeatmapController::sort_data_into_custom_grid() { _array_grid[i] += v; } else { + if(_normalization == normalization_t::variance) { + _array_sqsum.at(i) += leaf.value_sqsum(); + } + _array_grid[i] += leaf.data().size(); } @@ -253,6 +264,29 @@ void HeatmapController::sort_data_into_custom_grid() { }); } + // calculate standard deviation within each cell + if(_normalization == normalization_t::variance) { + minimum = maximum = infinity<double>(); + auto m = _grid.root()->value_sum() / double(_grid.size()); + + for (size_t i=0; i<_array_grid.size(); ++i) { + if(_array_samples[i] > 1) { + //auto m = _array_grid[i] / _array_samples[i]; + _array_grid[i] = sqrt((- 2 * m * _array_grid[i] + _array_sqsum[i] + N * SQR(m)) + / (_array_samples[i] - 1)); + + if(minimum > _array_grid[i] || minimum == infinity<double>()) { + minimum = _array_grid[i]; + } + if(maximum < _array_grid[i] || maximum == infinity<double>()) { + maximum = _array_grid[i]; + } + + } else + _array_grid[i] = _array_samples[i] = 0; + } + } + if(_normalization != normalization_t::none // none never does any normalization && !custom_heatmap_value_range.empty()) { @@ -261,6 +295,9 @@ void HeatmapController::sort_data_into_custom_grid() { } else { switch(_normalization) { + case normalization_t::variance: + break; + case normalization_t::cell: if(!_array_grid.empty()) { minimum = 0; @@ -291,7 +328,11 @@ void HeatmapController::sort_data_into_custom_grid() { auto mat = grid_image->get(); static auto empty = (cv::Scalar)Viridis::value(0).alpha(0); - mat.setTo(empty); + static auto empty_variance = (cv::Scalar)Viridis::value(1).alpha(200); + if(_normalization == normalization_t::variance) + mat.setTo(empty_variance); + else + mat.setTo(empty); double percentage; double ML = maximum - minimum; @@ -301,8 +342,10 @@ void HeatmapController::sort_data_into_custom_grid() { for (uint32_t x = 0; x < N; ++x) { for (uint32_t y = 0; y < N; ++y) { size_t i = y * N + x; - if(_array_grid[i] > 0) { + if(_array_samples[i] > 0) { percentage = (_array_grid[i] / _array_samples[i] - minimum) / ML; + if(_normalization == normalization_t::variance) + percentage = 1 - percentage; mat.at<cv::Vec4b>(y, x) = Viridis::value(percentage).alpha(percentage * 200); } } @@ -313,6 +356,12 @@ void HeatmapController::sort_data_into_custom_grid() { push_timing("sort_data_into_custom_grid", timer.elapsed()); } +void HeatmapController::frames_deleted_from(long_t frame) { + _iterators.clear(); + _capacities.clear(); + _grid.keep_only(Range<long_t>(0, max(0, frame-1))); +} + HeatmapController::UpdatedStats HeatmapController::update_data(long_t current_frame) { Timer timer; @@ -332,6 +381,7 @@ HeatmapController::UpdatedStats HeatmapController::update_data(long_t current_fr current_frame + frame_range + 1); //Debug("Clearing grid (%lu).", _grid.size()); _iterators.clear(); + _capacities.clear(); } else if(_frame_context != -1) { if(current_frame > _frame) { @@ -374,8 +424,13 @@ HeatmapController::UpdatedStats HeatmapController::update_data(long_t current_fr if(it == _iterators.end()) { kit = fish->iterator_for(frame); //Debug("Frame %d: fish%d, Reit", frame, fish->identity().ID()); - } else - kit = it->second; + } else { + if(_capacities[fish] != fish->frame_segments().capacity()) { + _capacities[fish] = fish->frame_segments().capacity(); + kit = fish->iterator_for(frame); + } else + kit = it->second; + } if(kit == fish->frame_segments().end() && range.end >= fish->start_frame()) { @@ -516,6 +571,7 @@ bool HeatmapController::update_variables() { if(_array_grid.size() != N * N) { _array_grid.resize(N * N); + _array_sqsum.resize(N * N); _array_samples.resize(N * N); } @@ -676,6 +732,7 @@ void Node::init(const Grid* grid, Node::Ptr parent, const Range<uint32_t>& x, co _frame_range.start = _frame_range.end = -1; _value_sum = 0; + _value_sqsum = 0; _value_range = Range<double>(infinity<double>(), infinity<double>()); _IDs.clear(); } @@ -859,6 +916,7 @@ size_t Leaf::keep_only(const Range<long_t> &frames) { void Leaf::update_ranges() { _value_range = Range<double>(infinity<double>(), infinity<double>()); _value_sum = 0; + _value_sqsum = 0; /*if(_IDs.size() != _grid->identities().size()) { _IDs.resize(_grid->identities().size()); @@ -877,6 +935,7 @@ void Leaf::update_ranges() { _value_range.end = d.value; _value_sum += d.value; + _value_sqsum += SQR(d.value); /*++_IDs[d.IDindex]; _values_per_id[d.IDindex] += d.value; @@ -1485,6 +1544,7 @@ void Region::insert(std::vector<DataPoint>::iterator start, std::vector<DataPoin void Region::update_ranges() { _value_sum = 0; + _value_sqsum = 0; _value_range.start = _value_range.end = infinity<double>(); /*if(_IDs.size() != _grid->identities().size()) { @@ -1508,6 +1568,7 @@ void Region::update_ranges() { _size += r->size(); _value_sum += r->value_sum(); + _value_sqsum += r->value_sqsum(); if(_value_range.start > r->value_range().start) _value_range.start = r->value_range().start; @@ -1620,6 +1681,7 @@ void Leaf::insert(std::vector<DataPoint>::iterator start, std::vector<DataPoint> for(auto it = start; it != end; ++it) { _value_sum += it->value; + _value_sqsum += SQR(it->value); if(_value_range.start > it->value) _value_range.start = it->value; diff --git a/Application/src/tracker/gui/IdentityHeatmap.h b/Application/src/tracker/gui/IdentityHeatmap.h index c39af38c2f9b2903d82b4837037d6c886f9d5a61..0b97479456c67820a2c6d4abaa4b3e304ee98496 100644 --- a/Application/src/tracker/gui/IdentityHeatmap.h +++ b/Application/src/tracker/gui/IdentityHeatmap.h @@ -66,6 +66,7 @@ protected: GETTER(Ptr, parent) const Grid* _grid; GETTER(double, value_sum) + GETTER(double, value_sqsum) GETTER(Range<double>, value_range) GETTER(std::vector<uint32_t>, IDs) @@ -332,10 +333,11 @@ protected: OptionsList<Output::Modifiers> _mods; std::shared_ptr<ExternalImage> _image; - std::vector<double> _array_grid, _array_samples; + std::vector<double> _array_grid, _array_sqsum, _array_samples; gpuMat _viridis, _gpuGrid; std::map<track::Individual*, track::Individual::segment_map::const_iterator> _iterators; + std::map<track::Individual*, size_t> _capacities; public: HeatmapController(); @@ -344,6 +346,8 @@ public: void update() override; void paint_heatmap(); void save(); + void frames_deleted_from(long_t frame); + private: struct UpdatedStats { size_t added; diff --git a/Application/src/tracker/gui/gui.cpp b/Application/src/tracker/gui/gui.cpp index 5b9a5605eff9e4b6c97931323e325964e08d2abd..f6c279e8ace487c5d03a909a1778a1157fb61cf5 100644 --- a/Application/src/tracker/gui/gui.cpp +++ b/Application/src/tracker/gui/gui.cpp @@ -119,6 +119,8 @@ public: } }; +static std::unique_ptr<gui::heatmap::HeatmapController> heatmapController; + void drawOptFlowMap (const cv::Mat& flow, cv::Mat& map) { assert(flow.isContinuous()); assert(map.isContinuous()); @@ -260,8 +262,11 @@ GUI::GUI(pv::File& video_source, const Image& average, Tracker& tracker) Tracker::LockGuard guard("setting_changed_"+name); if(Tracker::recognition()) Tracker::recognition()->clear_filter_cache(); - if(Tracker::recognition() && Tracker::recognition()->dataset_quality()) - Tracker::recognition()->dataset_quality()->remove_frames(Tracker::start_frame()); + if(Tracker::recognition() && Tracker::recognition()->dataset_quality()) { + auto start = Tracker::start_frame(); + Tracker::recognition()->dataset_quality()->remove_frames(start); + removed_frames(start); + } } std::lock_guard<std::recursive_mutex> lock_guard(this->gui().lock()); @@ -1193,6 +1198,12 @@ void GUI::draw_menu(gui::DrawStructure &base) { DrawMenu::draw(); } +void GUI::removed_frames(long_t including) { + std::lock_guard<std::recursive_mutex> gguard(gui().lock()); + if(heatmapController) + heatmapController->frames_deleted_from(including); +} + void GUI::reanalyse_from(long_t frame, bool in_thread) { if(!instance()) return; @@ -1209,6 +1220,7 @@ void GUI::reanalyse_from(long_t frame, bool in_thread) { if(frame <= Tracker::end_frame()) { Tracker::instance()->wait(); Tracker::instance()->_remove_frames(frame); + gui->removed_frames(frame); Output::Library::clear_cache(); gui->_timeline->reset_events(frame); @@ -1922,9 +1934,10 @@ void GUI::draw_tracking(DrawStructure& base, long_t frameNr, bool draw_graph) { s->set_pos(ptr_pos); } - static auto heatmap = std::make_unique<gui::heatmap::HeatmapController>(); - heatmap->set_frame(frame()); - base.wrap_object(*heatmap); + if(!heatmapController) + heatmapController = std::make_unique<gui::heatmap::HeatmapController>(); + heatmapController->set_frame(frame()); + base.wrap_object(*heatmapController); }); } @@ -4489,7 +4502,9 @@ void GUI::load_state(GUI::GUIType type, file::Path from) { }); Except("Cannot load results. Crashed with exception: %s", e.what()); - Tracker::instance()->_remove_frames(Tracker::start_frame()); + auto start = Tracker::start_frame(); + Tracker::instance()->_remove_frames(start); + removed_frames(start); } //_analysis->reset_cache(); diff --git a/Application/src/tracker/gui/gui.h b/Application/src/tracker/gui/gui.h index ec808513c8697c528974199887d2e1adca9307e5..3c19f22fa0006d0b11b08de7a55b8b4c7839352f 100644 --- a/Application/src/tracker/gui/gui.h +++ b/Application/src/tracker/gui/gui.h @@ -249,6 +249,8 @@ private: void draw_export_options(gui::DrawStructure& base); void draw_grid(gui::DrawStructure& base); + void removed_frames(long_t including); + void debug_binary(gui::DrawStructure& main_base, long_t frameIndex); void debug_optical_flow(gui::DrawStructure& base, long_t frameIndex); void redraw(); diff --git a/Application/src/tracker/misc/default_config.cpp b/Application/src/tracker/misc/default_config.cpp index ebaf812369520839b1743c4536bb181da9da4281..fb809bf71adf6cccbf413f867f3d8f653257afe7 100644 --- a/Application/src/tracker/misc/default_config.cpp +++ b/Application/src/tracker/misc/default_config.cpp @@ -44,7 +44,8 @@ namespace default_config { ENUM_CLASS_DOCS(heatmap_normalization_t, "No normalization at all. Values will only be averaged per cell.", "Normalization based in value-space. The average of each cell will be divided by the maximum value encountered.", - "The cell sum will be divided by the maximum cell value encountered." + "The cell sum will be divided by the maximum cell value encountered.", + "Displays the variation within each cell." ) ENUM_CLASS_DOCS(gui_recording_format_t, diff --git a/Application/src/tracker/misc/default_config.h b/Application/src/tracker/misc/default_config.h index 7e5d003b003e8a4ee1c13b83b4c7449d03c7788b..288d83ef753bf1381a63dfa7036de1dbfafdc585 100644 --- a/Application/src/tracker/misc/default_config.h +++ b/Application/src/tracker/misc/default_config.h @@ -32,7 +32,7 @@ namespace default_config { ENUM_CLASS(recognition_border_t, none, heatmap, outline, shapes, grid, circle); ENUM_CLASS_HAS_DOCS(recognition_border_t) - ENUM_CLASS(heatmap_normalization_t, none, value, cell); + ENUM_CLASS(heatmap_normalization_t, none, value, cell, variance); ENUM_CLASS_HAS_DOCS(heatmap_normalization_t) ENUM_CLASS(recognition_normalization_t, none, moments, posture, legacy); diff --git a/Application/src/tracker/tracking/PairingGraph.cpp b/Application/src/tracker/tracking/PairingGraph.cpp index 7d9ef93620810280ca2744edb2505194b35a359a..9949514fc613e4999248e5357a5608b2402ecbe3 100644 --- a/Application/src/tracker/tracking/PairingGraph.cpp +++ b/Application/src/tracker/tracking/PairingGraph.cpp @@ -873,10 +873,9 @@ PairingGraph::Stack* PairingGraph::work_single(queue_t& stack, Stack ¤t, c // Debug("%d: %d steps in %fms (%d)", frame(), objects, timer.elapsed()*1000, stack.size()); }*/ - const PairingGraph::Result& PairingGraph::get_optimal_pairing(bool debug, int mode) { + const PairingGraph::Result& PairingGraph::get_optimal_pairing(bool debug, default_config::matching_mode_t::Class match_mode) { static std::mutex _mutex; std::lock_guard<std::mutex> guard(_mutex); - const default_config::matching_mode_t::Class match_mode = mode == -1 ? FAST_SETTINGS(match_mode) : default_config::matching_mode_t::Class((default_config::matching_mode_t::data::values)mode); if(_optimal_pairing) delete _optimal_pairing; diff --git a/Application/src/tracker/tracking/PairingGraph.h b/Application/src/tracker/tracking/PairingGraph.h index 9abb5fb368f8ae588f13667827e331493cdb1687..a7ec29c8d332bdccadf9d1bb471693e3e6e14853 100644 --- a/Application/src/tracker/tracking/PairingGraph.h +++ b/Application/src/tracker/tracking/PairingGraph.h @@ -189,7 +189,7 @@ namespace Match //cv::Point2f pos(const Individual*) const; //cv::Point2f pos(const Blob*) const; - const Result& get_optimal_pairing(bool print = false, int mode = -1); + const Result& get_optimal_pairing(bool print = false, default_config::matching_mode_t::Class mode = default_config::matching_mode_t::accurate); public: //psets_t _psets; diff --git a/Application/src/tracker/tracking/Tracker.cpp b/Application/src/tracker/tracking/Tracker.cpp index 5591398b0c61546abdd3760abf1c6e0d946d7492..887724f44cf61ff61d62d8b584ea78f37e3815f5 100644 --- a/Application/src/tracker/tracking/Tracker.cpp +++ b/Application/src/tracker/tracking/Tracker.cpp @@ -2794,7 +2794,13 @@ void Tracker::clear_properties() { #endif try { - auto &optimal = graph.get_optimal_pairing(false, frame_uses_approximate ? (int)default_config::matching_mode_t::hungarian.value() : -1 ); + auto match_mode = frame_uses_approximate + ? default_config::matching_mode_t::hungarian + : FAST_SETTINGS(match_mode); + + //if(match_mode == default_config::matching_mode_t::accurate) + // U_EXCEPTION("Test %d", frameIndex); + auto &optimal = graph.get_optimal_pairing(false, match_mode); if(!frame_uses_approximate) { std::lock_guard<std::mutex> guard(_statistics_mutex); @@ -2808,7 +2814,7 @@ void Tracker::clear_properties() { _statistics[frameIndex].match_mean_edges_per_fish = mean_edges_per_fish; _statistics[frameIndex].match_improvements_made = optimal.improvements_made; _statistics[frameIndex].match_leafs_visited = optimal.leafs_visited; - _statistics[frameIndex].method_used = (int)FAST_SETTINGS(match_mode).value(); + _statistics[frameIndex].method_used = (int)match_mode.value(); } #if defined(PAIRING_PRINT_STATS) @@ -2829,7 +2835,6 @@ void Tracker::clear_properties() { graph.print_summary(); #endif - #if defined(PAIRING_PRINT_STATS) // matching did not work @@ -2846,7 +2851,7 @@ void Tracker::clear_properties() { Debug("gw ---"); #endif - auto &optimal = graph.get_optimal_pairing(false, (int)default_config::matching_mode_t::hungarian.value()); + auto &optimal = graph.get_optimal_pairing(false, default_config::matching_mode_t::hungarian); for (auto &p: optimal.pairings) { assign_blob_individual(frameIndex, frame, p.first, ptr2ptr.at(p.second)); active_individuals.insert(p.first); @@ -3501,6 +3506,9 @@ void Tracker::update_iterator_maps(long_t frame, const Tracker::set_of_individua _individual_add_iterator_map.clear(); _segment_map_known_capacity.clear(); + if(_approximative_enabled_in_frame >= frameIndex) + _approximative_enabled_in_frame = -1; + Debug("Removing frames after and including %ld", frameIndex); if (_endFrame < frameIndex || _startFrame > frameIndex)