Esempio n. 1
0
// Performs the proposed Hough transform voting scheme.
void
voting(accumulator_t &accumulator, section_list_t &sections, const clusters_list_t &clusters, const double kernel_min_height, const double n_sigmas)
{
	/* Leandro A. F. Fernandes, Manuel M. Oliveira
	 * Real-time line detection through an improved Hough transform voting scheme
	 * Pattern Recognition (PR), Elsevier, 41:1, 2008, 299-314.
	 *
	 * Algorithm 2
	 */
	static kernels_list_t kernels;
	static pkernels_list_t used_kernels;

	kernels.resize( clusters.size() );
	used_kernels.resize( clusters.size() );

	matrix_t M, V, S;
	point_t mean, u, v;
	double x, y, Sxx, Syy, Sxy, aux;

	static const double rad_to_deg = 180.0 / pi;
	const double delta = accumulator.delta();
	const double one_div_delta = 1.0 / delta;
	const double n_sigmas2 = n_sigmas * n_sigmas;
	const double rho_max = accumulator.rho_bounds().upper;

	for (size_t k=0, end=clusters.size(); k!=end; ++k)
	{
		const cluster_t &cluster = clusters[k];
		kernel_t &kernel = kernels[k];

		kernel.pcluster = &cluster;
		
		// Alternative reference system definition.
		mean.x = mean.y = 0.0;
		for (size_t i=0; i!=cluster.size; ++i)
		{
			mean.x += cluster.pixels[i].x;
			mean.y += cluster.pixels[i].y;
		}
		mean.x /= cluster.size;
		mean.y /= cluster.size;
		
		Sxx = Syy = Sxy = 0.0;
		for (size_t i=0; i!=cluster.size; ++i)
		{
			x = cluster.pixels[i].x - mean.x;
			y = cluster.pixels[i].y - mean.y;
		
			Sxx += (x * x);
			Syy += (y * y);
			Sxy += (x * y);
		}
		
		M[0] = Sxx;
		M[3] = Syy;
		M[1] = M[2] = Sxy;
		eigen( V, S, M );

		u.x = V[0];
		u.y = V[2];
		
		v.x = V[1];
		v.y = V[3];
		
		// y_v >= 0 condition verification.
		if (v.y < 0.0)
		{
			v.x *= -1.0;
			v.y *= -1.0;
		}

		// Normal equation parameters computation (Eq. 3).
		kernel.rho = (v.x * mean.x) + (v.y * mean.y);
		kernel.theta = acos( v.x ) * rad_to_deg;

		kernel.rho_index = static_cast<size_t>( std::abs( (kernel.rho + rho_max) * one_div_delta ) ) + 1;
		kernel.theta_index = static_cast<size_t>( std::abs( kernel.theta * one_div_delta ) ) + 1;

		// sigma^2_m' and sigma^2_b' computation, substituting Eq. 5 in Eq. 10.
		aux = sqrt( 1.0 - (v.x * v.x) );
		matrix_t nabla = {
				             -((u.x * mean.x) + (u.y * mean.y)), 1.0,
				(aux != 0.0) ? ((u.x / aux) * rad_to_deg) : 0.0, 0.0
			};

		aux = 0.0;
		for (size_t i=0; i!=cluster.size; ++i)
		{
			x = (u.x * (cluster.pixels[i].x - mean.x)) + (u.y * (cluster.pixels[i].y - mean.y));
			aux += (x * x);
		}

		matrix_t lambda = {
				1.0 / aux,                0.0,
					  0.0, 1.0 / cluster.size
			};

		// Uncertainty from sigma^2_m' and sigma^2_b' to sigma^2_rho,  sigma^2_theta and sigma_rho_theta.
		solve( kernel.lambda, nabla, lambda );

		if (kernel.lambda[3] == 0.0)
		{
			kernel.lambda[3] = 0.1;
		}

		kernel.lambda[0] *= n_sigmas2;
		kernel.lambda[3] *= n_sigmas2;

		// Compute the height of the kernel.
		kernel.height = gauss( 0.0, 0.0, kernel.lambda[0], kernel.lambda[3], kernel.lambda[1] );
	}

	/* Leandro A. F. Fernandes, Manuel M. Oliveira
	 * Real-time line detection through an improved Hough transform voting scheme
	 * Pattern Recognition (PR), Elsevier, 41:1, 2008, 299-314.
	 *
	 * Algorithm 3
	 */

	// Discard groups with very short kernels.
	double norm = std::numeric_limits<double>::min();

	for (size_t k=0, end=kernels.size(); k!=end; ++k)
	{
		kernel_t &kernel = kernels[k];

		if (norm < kernel.height)
		{
			norm = kernel.height;
		}
		used_kernels[k] = &kernel;
	}
	norm = 1.0 / norm;

	size_t i = 0;
	for (size_t k=0, end=used_kernels.size(); k!=end; ++k)
	{
		if ((used_kernels[k]->height * norm) >= kernel_min_height)
		{
			if (i != k)
			{
				kernel_t *temp = used_kernels[i];
				used_kernels[i] = used_kernels[k];
				used_kernels[k] = temp;
			}
			i++;
		}
	}
	used_kernels.resize( i );

	// Find the g_min threshold and compute the scale factor for integer votes.
	double radius, scale;
	double kernels_scale = std::numeric_limits<double>::min();

	for (size_t k=0, end=used_kernels.size(); k!=end; ++k)
	{
		kernel_t &kernel = *used_kernels[k];

		eigen( V, S, kernel.lambda );
		radius = sqrt( S[3] );

		scale = gauss( V[1] * radius, V[3] * radius, kernel.lambda[0], kernel.lambda[3], kernel.lambda[1] );
		scale = (scale < 1.0) ? (1.0 / scale) : 1.0;

		if (kernels_scale < scale)
		{
			kernels_scale = scale;
		}
	}

	// Vote for each selected kernel.
	for (size_t k=0, end=used_kernels.size(); k!=end; ++k)
	{
		kernel_t &kernel = *used_kernels[k];

		vote( accumulator, kernel.rho_index,     kernel.theta_index,        0.0,    0.0,  1,  1, kernel.lambda[0], kernel.lambda[3], kernel.lambda[1], kernels_scale );
		vote( accumulator, kernel.rho_index,     kernel.theta_index - 1,    0.0, -delta,  1, -1, kernel.lambda[0], kernel.lambda[3], kernel.lambda[1], kernels_scale );
		vote( accumulator, kernel.rho_index - 1, kernel.theta_index,     -delta,    0.0, -1,  1, kernel.lambda[0], kernel.lambda[3], kernel.lambda[1], kernels_scale );
		vote( accumulator, kernel.rho_index - 1, kernel.theta_index - 1, -delta, -delta, -1, -1, kernel.lambda[0], kernel.lambda[3], kernel.lambda[1], kernels_scale );
	}

	sections.resize( used_kernels.size() );
	for (int i=0; i< used_kernels.size(); i++) {
		section_t &section = sections[i];
		kernel_t &kernel = *used_kernels[i];

		section.rho = kernel.rho;
		section.theta = kernel.theta;
		section.size = kernel.pcluster->size;
		section.pixels = kernel.pcluster->pixels;
	}
}
// Identify the peaks of votes (most significant straight lines) in the accmulator.
void
peak_detection(lines_list_t &lines, const accumulator_t &accumulator)
{
	/* Leandro A. F. Fernandes, Manuel M. Oliveira
	 * Real-time line detection through an improved Hough transform voting scheme
	 * Pattern Recognition (PR), Elsevier, 41:1, 2008, 299-314.
	 *
	 * Section 3.4
	 */

	const int **bins = accumulator.bins();
	const double *rho = accumulator.rho();
	const double *theta = accumulator.theta();

	// Create a list with all cells that receive at least one vote.
	static bins_list_t used_bins;
	
	size_t used_bins_count = 0;
	for (size_t theta_index=1, theta_end=accumulator.height()+1; theta_index!=theta_end; ++theta_index)
	{
		for (size_t rho_index=1, rho_end=accumulator.width()+1; rho_index!=rho_end; ++rho_index)
		{
			if (bins[theta_index][rho_index])
			{
				used_bins_count++;
			}
		}
	}
	used_bins.resize( used_bins_count );
		
	for (size_t theta_index=1, i=0, theta_end=accumulator.height()+1; theta_index!=theta_end; ++theta_index)
	{
		for (size_t rho_index=1, rho_end=accumulator.width()+1; rho_index!=rho_end; ++rho_index)
		{
			if (bins[theta_index][rho_index])
			{
				bin_t &bin = used_bins[i];

				bin.rho_index = rho_index;
				bin.theta_index = theta_index;
				bin.votes = convolution( bins, rho_index, theta_index ); // Convolution of the cells with a 3x3 Gaussian kernel

				i++;
			}
		}
	}
	
	// Sort the list in descending order according to the result of the convolution.
	std::qsort( used_bins.items(), used_bins_count, sizeof( bin_t ), (int(*)(const void*, const void*))compare_bins );
	
	// Use a sweep plane that visits each cell of the list.
	static visited_map_t visited;
	visited.init( accumulator.width(), accumulator.height() );

	lines.clear();
	lines.reserve( used_bins_count );

	for (size_t i=0; i!=used_bins_count; ++i)
	{
		bin_t &bin = used_bins[i];

		if (!visited.visited_neighbour( bin.rho_index, bin.theta_index ))
		{
			line_t &line = lines.push_back();
			
			line.rho = rho[bin.rho_index];
			line.theta = theta[bin.theta_index];
		}
		visited.set_visited( bin.rho_index, bin.theta_index );
	}
}
Esempio n. 3
0
// This function complements the proposed voting process.
inline
void
vote(accumulator_t &accumulator, size_t rho_start_index, const size_t theta_start_index, const double rho_start, const double theta_start, int inc_rho_index, const int inc_theta_index, const double sigma2_rho, const double sigma2_theta, const double sigma_rho_theta, const double scale)
{
	/* Leandro A. F. Fernandes, Manuel M. Oliveira
	 * Real-time line detection through an improved Hough transform voting scheme
	 * Pattern Recognition (PR), Elsevier, 41:1, 2008, 299-314.
	 *
	 * Algorithm 4
	 */

	int **bins = accumulator.bins();
	
	const size_t rho_size = accumulator.width(), theta_size = accumulator.height();
	const double delta = accumulator.delta();
	const double inc_rho = delta * inc_rho_index, inc_theta = delta * inc_theta_index;
		
	const double sigma_rho_sigma_theta = sqrt( sigma2_rho ) * sqrt( sigma2_theta );
	const double r = (sigma_rho_theta / sigma_rho_sigma_theta), two_r = 2.0 * r;
	const double a = 1.0 / (2.0 * pi * sigma_rho_sigma_theta * sqrt( 1.0 - (r * r) ));
	const double b = 1.0 / (2.0 * (1.0 - (r * r)));

	bool theta_voted;
	double rho, theta;
	int votes, theta_not_voted = 0;
	size_t rho_index, theta_index, theta_count = 0;
	
	// Loop for the theta coordinates of the parameter space.
	theta_index = theta_start_index;
	theta = theta_start;
	do
	{
		// Test if the kernel exceeds the parameter space limits.
		if ((theta_index == 0) || (theta_index == (theta_size + 1)))
		{
			rho_start_index = rho_size - rho_start_index + 1;
			theta_index = (theta_index == 0) ? theta_size : 1;
			inc_rho_index = -inc_rho_index;
		}

		// Loop for the rho coordinates of the parameter space.
		theta_voted = false;

		rho_index = rho_start_index;
		rho = rho_start;
		while (((votes = static_cast<int>( (gauss( rho, theta, sigma2_rho, sigma2_theta, sigma_rho_sigma_theta, two_r, a, b ) * scale) + 0.5 )) > 0) && (rho_index >= 1) && (rho_index <= rho_size))
		{
			bins[theta_index][rho_index] += votes;
			theta_voted = true;

			rho_index += inc_rho_index;
			rho += inc_rho;
		}

		if (!theta_voted)
		{
			theta_not_voted++;
		}

		theta_index += inc_theta_index;
		theta += inc_theta;
		theta_count++;
	}
	while ((theta_not_voted != 2) && (theta_count < theta_size));
}