void SVCandidateProcessor:: evaluateCandidates( const EdgeInfo& edge, const std::vector<SVMultiJunctionCandidate>& mjSVs, const SVCandidateSetData& svData) { const bool isIsolatedEdge(testIsolatedEdge(_cset,edge)); bool isFindLargeInsertions(isIsolatedEdge); if (isFindLargeInsertions) { for (const SVMultiJunctionCandidate& mjCandidateSV : mjSVs) { for (const SVCandidate& candidateSV : mjCandidateSV.junction) { if (! isComplexSV(candidateSV)) isFindLargeInsertions=false; } } } _svRefine.clearEdgeData(); for (const auto& cand : mjSVs) { evaluateCandidate(edge,cand,svData,isFindLargeInsertions); } }
GridFitter::candidate_set GridFitter::getInitialCandidates(const cv::Mat &binarizedROI, const cv::Mat& sobelXRoi, const cv::Mat& sobelYRoi, const Ellipse& ellipse_orig, const cv::Mat &roi) { static const auto initial_rotations = Util::linspace<double>(0, 2 * CV_PI - CV_PI / 32., 32); static const auto initial_position_offsets = Util::linspace<int>(-3, 3, 7); // initial search for gradient descent candidates in ellipse parameter space // note that the position offsets have to be evaluated in the inner loop // because shifting a known grid configuration is much faster than // recalculating all coordinates. candidate_set gridCandidates; for (const double rotation : initial_rotations) { // get the best candidates for the current rotation candidate_set candidatesForRotation; // estimate grid parameters from ellipse -> two possible candidates const std::array<Util::gridconfig_t, 2> configCandidates = Util::gridCandidatesFromEllipse(ellipse_orig, rotation); for (Util::gridconfig_t const& config : configCandidates) { PipelineGrid grid(config); for (const int pos_x_offset : initial_position_offsets) { for (const int pos_y_offset : initial_position_offsets) { grid.setCenter({config.center.x + pos_x_offset, config.center.y + pos_y_offset}); const double error = evaluateCandidate(grid, roi, binarizedROI, sobelXRoi, sobelYRoi, _settings_cache); candidatesForRotation.insert({error, grid.getConfig()}); } } // for each rotation and ellipse candidate, insert the best candiate into gridCandidates gridCandidates.insert(*candidatesForRotation.begin()); } } return gridCandidates; }
void GridFitter::GradientDescent::optimize() { const size_t num = std::min(_settings.gradient_num_initial, _initialCandidates.size()); candidate_set::iterator candidate_it = _initialCandidates.begin(); // iterate over the settings.numInitial best initial candidates for (size_t idx = 0; idx < num; ++idx) { candidate_t candidate = *candidate_it; const Util::gridconfig_t& initial_config = candidate.config; size_t iteration = 0; PipelineGrid grid(initial_config); Util::gridconfig_t config = initial_config; double error = evaluateCandidate(grid, _roi, _binarizedRoi, _edgeRoiX, _edgeRoiY, _settings); storeConfig(error, config); candidate_set bestGridsForCandidate; bestGridsForCandidate.insert(candidate); std::array<StepParameter, 6> parameters { SCALE, POSX, POSY, ANGLE_X, ANGLE_Y, ANGLE_Z }; // gradient descent size_t numWithoutImprovement = 0; while ((error > _settings.gradient_error_threshold) && (iteration < _settings.gradient_max_iterations)) { double const initerror = error; // shuffle order of parameters //std::shuffle(parameters.begin(), parameters.end(), _random_engine); for (const StepParameter param : parameters) { std::tie(error, config) = step(bestGridsForCandidate, config, error, param); } ++iteration; // abort gradient descent if error measurement did not improve by // a significant amount during the last six steps assert(!bestGridsForCandidate.empty()); if ((initerror - bestGridsForCandidate.begin()->error) < 0.0001) { ++numWithoutImprovement; if (numWithoutImprovement >= 5) break; } else { numWithoutImprovement = 0; } // instead of continuing the next iteration with the result of the // last step, use the best currently found config from now on. error = bestGridsForCandidate.begin()->error; config = bestGridsForCandidate.begin()->config; } assert(!bestGridsForCandidate.empty()); storeConfig(bestGridsForCandidate.begin()->error, bestGridsForCandidate.begin()->config); ++candidate_it; } }
std::pair<double, Util::gridconfig_t> GridFitter::GradientDescent::step(candidate_set &bestGrids, Util::gridconfig_t const& config, double error, const GridFitter::GradientDescent::StepParameter param) { // when adjusting one of the position parameters, we can not adjust the step // size based on the difference between of the errors and the learning rate // (because the coordinates are discrete values). // instead, in each step we only change the position by either 1 or -1. // furthermore, if the error does not improve after a position change, // instead of just applying the reverse direction, we check if the error // acutally improves when going in the reverse direction. static const std::array<int, 2> directions {-1, 1}; const double alpha = _settings.alpha; const double eps_scale = _settings.eps_scale; const int eps_pos = _settings.eps_pos; const double eps_angle = _settings.eps_angle; for (int direction : directions) { Util::gridconfig_t newConfig(config); // adjust parameter switch (param) { case SCALE: newConfig.radius = newConfig.radius + direction * eps_scale; break; case POSX: newConfig.center = newConfig.center + cv::Point2i(direction * eps_pos, 0); break; case POSY: newConfig.center = newConfig.center + cv::Point2i(0, direction * eps_pos); break; case ANGLE_X: newConfig.angle_x = newConfig.angle_x + direction * eps_angle; break; case ANGLE_Y: newConfig.angle_y = newConfig.angle_y + direction * eps_angle; break; case ANGLE_Z: newConfig.angle_z = newConfig.angle_z + direction * eps_angle; break; default: assert(false); break; } // TODO: don't construct new Grid in each step PipelineGrid newGrid(newConfig); double newError = evaluateCandidate(newGrid, _roi, _binarizedRoi, _edgeRoiX, _edgeRoiY, _settings); // adjust parameter based on learning rate and new error if (param != POSX && param != POSY) { switch (param) { case SCALE: newConfig.radius = config.radius + direction * alpha * (error - newError); break; case ANGLE_X: newConfig.angle_x = config.angle_x + direction * alpha * (error - newError); break; case ANGLE_Y: newConfig.angle_y = config.angle_y + direction * alpha * (error - newError); break; case ANGLE_Z: newConfig.angle_z = config.angle_z + direction * alpha * (error - newError); break; default: break; } PipelineGrid newGrid(newConfig); newError = evaluateCandidate(newGrid, _roi, _binarizedRoi, _edgeRoiX, _edgeRoiY, _settings); bestGrids.insert({newError, newConfig}); return {newError, newConfig}; } else if (newError < error) { bestGrids.insert({newError, newConfig}); return {newError, newConfig}; } } return {error, config}; }