Exemple #1
0
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};
}