// Performs the proposed Hough transform voting scheme. void voting(accumulator_t &accumulator, section_list_t §ions, 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 §ion = 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 ); } }
// 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)); }