.. _program_listing_file__tmp_ws_src_vitis_common_include_imgproc_xf_houghlines.hpp: Program Listing for File xf_houghlines.hpp ========================================== |exhale_lsh| :ref:`Return to documentation for file ` (``/tmp/ws/src/vitis_common/include/imgproc/xf_houghlines.hpp``) .. |exhale_lsh| unicode:: U+021B0 .. UPWARDS ARROW WITH TIP LEFTWARDS .. code-block:: cpp /* * Copyright 2019 Xilinx, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef _XF_HOUGHLINES_HPP_ #define _XF_HOUGHLINES_HPP_ #ifndef __cplusplus #error C++ is needed to include this header #endif typedef unsigned short uint16_t; #include "hls_stream.h" #include "../common/xf_common.hpp" #include "../common/xf_utility.hpp" #define pai 3.1415926 #define pai_by_360 0.008726 #define pai_by_180 0.017444 //-------------------------------------------------------------------------------------------- // Sin & Cos Tables for angles [0,180) or [0,179.5] in the steps of 0.5 degrees // Sin(90) and Cos(90) are made close 1 (but NOT 1) to avoid using 2 bits for integer part // The values are stored in 1.15 format // Also, the vlaues 1.000000 and 0.500000 are made 0.999980 and 0.499980 // to make sure the cvRound does not create mismatch with orgin shift // Example: // When theta=90, W=240 H=135 // rho = (x - (W/2)Cos(theta) + (y - (H/2))Sin(Theta) // = 0 + (-67.5)Sin(90) (for the first row i.e y=0) // We see .5 as the fractional part which makes cvRound to kick in and round the value // cvRound is an irreverible computation, we can not get original value. So, to avoid cvRound // getting kicked in because of H/2 or W/2, we chose 0.999980 and 0.499980 instead 1 & 0.5 //-------------------------------------------------------------------------------------------- static ap_fixed<16, 1, AP_RND> sinval[360] = { 0.000000, 0.008727, 0.017452, 0.026177, 0.034899, 0.043619, 0.052336, 0.061049, 0.069756, 0.078459, 0.087156, 0.095846, 0.104528, 0.113203, 0.121869, 0.130526, 0.139173, 0.147809, 0.156434, 0.165048, 0.173648, 0.182236, 0.190809, 0.199368, 0.207912, 0.216440, 0.224951, 0.233445, 0.241922, 0.250380, 0.258819, 0.267238, 0.275637, 0.284015, 0.292372, 0.300706, 0.309017, 0.317305, 0.325568, 0.333807, 0.342020, 0.350207, 0.358368, 0.366501, 0.374607, 0.382684, 0.390731, 0.398749, 0.406737, 0.414693, 0.422618, 0.430511, 0.438371, 0.446198, 0.453991, 0.461749, 0.469472, 0.477159, 0.484810, 0.492424, 0.499980, 0.507539, 0.515038, 0.522499, 0.529920, 0.537300, 0.544639, 0.551937, 0.559193, 0.566407, 0.573577, 0.580703, 0.587786, 0.594823, 0.601815, 0.608762, 0.615662, 0.622515, 0.629321, 0.636079, 0.642788, 0.649448, 0.656059, 0.662620, 0.669131, 0.675591, 0.681999, 0.688355, 0.694659, 0.700910, 0.707107, 0.713251, 0.719340, 0.725375, 0.731354, 0.737278, 0.743145, 0.748956, 0.754710, 0.760406, 0.766045, 0.771625, 0.777146, 0.782609, 0.788011, 0.793354, 0.798636, 0.803857, 0.809017, 0.814116, 0.819152, 0.824127, 0.829038, 0.833886, 0.838671, 0.843392, 0.848048, 0.852641, 0.857168, 0.861629, 0.866026, 0.870356, 0.874620, 0.878817, 0.882948, 0.887011, 0.891007, 0.894934, 0.898794, 0.902585, 0.906308, 0.909961, 0.913545, 0.917060, 0.920505, 0.923879, 0.927184, 0.930417, 0.933580, 0.936672, 0.939692, 0.942641, 0.945518, 0.948323, 0.951056, 0.953717, 0.956305, 0.958820, 0.961261, 0.963630, 0.965926, 0.968147, 0.970295, 0.972370, 0.974370, 0.976296, 0.978147, 0.979924, 0.981627, 0.983255, 0.984807, 0.986285, 0.987688, 0.989016, 0.990268, 0.991445, 0.992546, 0.993572, 0.994522, 0.995396, 0.996195, 0.996917, 0.997564, 0.998135, 0.998629, 0.999048, 0.999391, 0.999657, 0.999848, 0.999962, 0.999980, 0.999962, 0.999848, 0.999657, 0.999391, 0.999048, 0.998629, 0.998135, 0.997564, 0.996917, 0.996195, 0.995396, 0.994522, 0.993572, 0.992546, 0.991445, 0.990268, 0.989016, 0.987688, 0.986285, 0.984807, 0.983255, 0.981627, 0.979924, 0.978147, 0.976296, 0.97437, 0.97237, 0.970295, 0.968147, 0.965926, 0.96363, 0.961261, 0.95882, 0.956305, 0.953717, 0.951056, 0.948323, 0.945518, 0.942641, 0.939692, 0.936672, 0.93358, 0.930417, 0.927184, 0.923879, 0.920505, 0.91706, 0.913545, 0.909961, 0.906308, 0.902585, 0.898794, 0.894934, 0.891007, 0.887011, 0.882948, 0.878817, 0.87462, 0.870356, 0.866026, 0.861629, 0.857168, 0.852641, 0.848048, 0.843392, 0.838671, 0.833886, 0.829038, 0.824127, 0.819152, 0.814116, 0.809017, 0.803857, 0.798636, 0.793354, 0.788011, 0.782609, 0.777146, 0.771625, 0.766045, 0.760406, 0.75471, 0.748956, 0.743145, 0.737278, 0.731354, 0.725375, 0.71934, 0.713251, 0.707107, 0.70091, 0.694659, 0.688355, 0.681999, 0.675591, 0.669131, 0.66262, 0.656059, 0.649448, 0.642788, 0.636079, 0.629321, 0.622515, 0.615662, 0.608762, 0.601815, 0.594823, 0.587786, 0.580703, 0.573577, 0.566407, 0.559193, 0.551937, 0.544639, 0.5373, 0.52992, 0.522499, 0.515038, 0.507539, 0.5, 0.492424, 0.48481, 0.477159, 0.469472, 0.461749, 0.453991, 0.446198, 0.438371, 0.430511, 0.422618, 0.414693, 0.406737, 0.398749, 0.390731, 0.382684, 0.374607, 0.366501, 0.358368, 0.350207, 0.34202, 0.333807, 0.325568, 0.317305, 0.309017, 0.300706, 0.292372, 0.284015, 0.275637, 0.267238, 0.258819, 0.25038, 0.241922, 0.233445, 0.224951, 0.21644, 0.207912, 0.199368, 0.190809, 0.182236, 0.173648, 0.165048, 0.156434, 0.147809, 0.139173, 0.130526, 0.121869, 0.113203, 0.104528, 0.095846, 0.087156, 0.078459, 0.069756, 0.061049, 0.052336, 0.043619, 0.034899, 0.026177, 0.017452, 0.008727}; static ap_fixed<16, 1, AP_RND> cosval[360] = { 0.999980, 0.999962, 0.999848, 0.999657, 0.999391, 0.999048, 0.998629, 0.998135, 0.997564, 0.996917, 0.996195, 0.995396, 0.994522, 0.993572, 0.992546, 0.991445, 0.990268, 0.989016, 0.987688, 0.986285, 0.984807, 0.983255, 0.981627, 0.979924, 0.978147, 0.976296, 0.97437, 0.97237, 0.970295, 0.968147, 0.965926, 0.96363, 0.961261, 0.95882, 0.956305, 0.953717, 0.951056, 0.948323, 0.945518, 0.942641, 0.939692, 0.936672, 0.93358, 0.930417, 0.927184, 0.923879, 0.920505, 0.91706, 0.913545, 0.909961, 0.906308, 0.902585, 0.898794, 0.894934, 0.891007, 0.887011, 0.882948, 0.878817, 0.87462, 0.870356, 0.866026, 0.861629, 0.857168, 0.852641, 0.848048, 0.843392, 0.838671, 0.833886, 0.829038, 0.824127, 0.819152, 0.814116, 0.809017, 0.803857, 0.798636, 0.793354, 0.788011, 0.782609, 0.777146, 0.771625, 0.766045, 0.760406, 0.75471, 0.748956, 0.743145, 0.737278, 0.731354, 0.725375, 0.71934, 0.713251, 0.707107, 0.70091, 0.694659, 0.688355, 0.681999, 0.675591, 0.669131, 0.66262, 0.656059, 0.649448, 0.642788, 0.636079, 0.629321, 0.622515, 0.615662, 0.608762, 0.601815, 0.594823, 0.587786, 0.580703, 0.573577, 0.566407, 0.559193, 0.551937, 0.544639, 0.5373, 0.52992, 0.522499, 0.515038, 0.507539, 0.5, 0.492424, 0.48481, 0.477159, 0.469472, 0.461749, 0.453991, 0.446198, 0.438371, 0.430511, 0.422618, 0.414693, 0.406737, 0.398749, 0.390731, 0.382684, 0.374607, 0.366501, 0.358368, 0.350207, 0.34202, 0.333807, 0.325568, 0.317305, 0.309017, 0.300706, 0.292372, 0.284015, 0.275637, 0.267238, 0.258819, 0.25038, 0.241922, 0.233445, 0.224951, 0.21644, 0.207912, 0.199368, 0.190809, 0.182236, 0.173648, 0.165048, 0.156434, 0.147809, 0.139173, 0.130526, 0.121869, 0.113203, 0.104528, 0.095846, 0.087156, 0.078459, 0.069756, 0.061049, 0.052336, 0.043619, 0.034899, 0.026177, 0.017452, 0.008727, 0.000000, -0.008727, -0.017452, -0.026177, -0.034899, -0.043619, -0.052336, -0.061049, -0.069756, -0.078459, -0.087156, -0.095846, -0.104528, -0.113203, -0.121869, -0.130526, -0.139173, -0.147809, -0.156434, -0.165048, -0.173648, -0.182236, -0.190809, -0.199368, -0.207912, -0.216440, -0.224951, -0.233445, -0.241922, -0.250380, -0.258819, -0.267238, -0.275637, -0.284015, -0.292372, -0.300706, -0.309017, -0.317305, -0.325568, -0.333807, -0.342020, -0.350207, -0.358368, -0.366501, -0.374607, -0.382684, -0.390731, -0.398749, -0.406737, -0.414693, -0.422618, -0.430511, -0.438371, -0.446198, -0.453991, -0.461749, -0.469472, -0.477159, -0.484810, -0.492424, -0.499980, -0.507539, -0.515038, -0.522499, -0.529920, -0.537300, -0.544639, -0.551937, -0.559193, -0.566407, -0.573577, -0.580703, -0.587786, -0.594823, -0.601815, -0.608762, -0.615662, -0.622515, -0.629321, -0.636079, -0.642788, -0.649448, -0.656059, -0.662620, -0.669131, -0.675591, -0.681999, -0.688355, -0.694659, -0.700910, -0.707107, -0.713251, -0.719340, -0.725375, -0.731354, -0.737278, -0.743145, -0.748956, -0.754710, -0.760406, -0.766045, -0.771625, -0.777146, -0.782609, -0.788011, -0.793354, -0.798636, -0.803857, -0.809017, -0.814116, -0.819152, -0.824127, -0.829038, -0.833886, -0.838671, -0.843392, -0.848048, -0.852641, -0.857168, -0.861629, -0.866026, -0.870356, -0.874620, -0.878817, -0.882948, -0.887011, -0.891007, -0.894934, -0.898794, -0.902585, -0.906308, -0.909961, -0.913545, -0.917060, -0.920505, -0.923879, -0.927184, -0.930417, -0.933580, -0.936672, -0.939692, -0.942641, -0.945518, -0.948323, -0.951056, -0.953717, -0.956305, -0.958820, -0.961261, -0.963630, -0.965926, -0.968147, -0.970295, -0.972370, -0.974370, -0.976296, -0.978147, -0.979924, -0.981627, -0.983255, -0.984807, -0.986285, -0.987688, -0.989016, -0.990268, -0.991445, -0.992546, -0.993572, -0.994522, -0.995396, -0.996195, -0.996917, -0.997564, -0.998135, -0.998629, -0.999048, -0.999391, -0.999657, -0.999848, -0.999962}; namespace xf { namespace cv { /***************************************************************** * Function for Voting process *****************************************************************/ static void register_rho_stg1_sin(ap_fixed<28, 13, AP_RND>& rho_stg1_sino, ap_fixed<28, 13, AP_RND> rho_stg1_sini, ap_fixed<16, 1, AP_RND> sinvals, bool j_eq_width) { // clang-format off #pragma HLS INLINE OFF // clang-format on if (j_eq_width) // Update when it is the last pixel of row { rho_stg1_sino = rho_stg1_sini + sinvals; } else { rho_stg1_sino = rho_stg1_sini; } } template void xfVoting(xf::cv::Mat& _src_mat, ap_uint<12> accum[AngleN + 1][rhoN + 1], ap_uint<12> height, ap_uint<12> width) { // clang-format off #pragma HLS INLINE OFF // clang-format on // accumulator matrix initialization // Make all votes as "0" loop_init_r: for (ap_uint<13> r = 0; r < rhoN + 1; r++) { // clang-format off #pragma HLS LOOP_TRIPCOUNT min=1 max=rhoN #pragma HLS PIPELINE // clang-format on loop_init_n: for (ap_uint<13> n = 0; n < AngleN + 1; n++) { // clang-format off #pragma HLS LOOP_TRIPCOUNT min=1 max=AngleN // clang-format on accum[n][r] = 0; } } ap_fixed<16, 1, AP_RND> sinvals[AngleN]; // To store fixed point sin angle values 1.15 ap_fixed<16, 1, AP_RND> cosvals[AngleN]; // To store fixed point cos angle values 1.15 // Angle values scaling based on rho step and theta step // Top level arguments are required to be integers. // But we consider those values to be in 6.1 format // i.e if input is 5 that means theta = 5>>1 = 5/2 = 2.5 unsigned char rhoval = rho; loop_init: for (ap_uint<10> n = 0, ang = (MINTHETA * 2); n < AngleN; ang = ang + (theta), n++) // Assumtion is theta is in 6.1 format { // clang-format off #pragma HLS PIPELINE #pragma HLS LOOP_TRIPCOUNT min=1 max=AngleN // clang-format on sinvals[n] = sinval[ang] / rhoval; cosvals[n] = cosval[ang] / rhoval; } ap_uint<13> rho_prev_set1[(AngleN)]; ap_uint<13> rho_stg3, rho_stg3_reg[AngleN], rho_set2_reg; ap_fixed<14, 13> rho_stg3_apfixedp1; ap_fixed<13, 13> rho_stg3_apfixedp0; ap_fixed<28, 13, AP_RND> rho_stg1_sin[(AngleN)], rho_stg1_cos[(AngleN)], rho_stg1_sin_wire[AngleN], rho_stg2[(AngleN)]; // sin and cos value register (13.15) ap_uint<12> accval_reg_set1[AngleN]; // [MINTHETA, MAXTHETA) angles // clang-format off #pragma HLS ARRAY_PARTITION variable=accval_reg_set1 complete dim=0 #pragma HLS ARRAY_PARTITION variable=rho_stg1_cos complete dim=0 #pragma HLS ARRAY_PARTITION variable=rho_stg1_sin complete dim=0 #pragma HLS ARRAY_PARTITION variable=rho_stg1_sin_wire complete dim=0 #pragma HLS ARRAY_PARTITION variable=rho_stg2 complete dim=0 #pragma HLS ARRAY_PARTITION variable=sinvals complete dim=0 #pragma HLS ARRAY_PARTITION variable=cosvals complete dim=0 #pragma HLS ARRAY_PARTITION variable=rho_stg3_reg complete dim=0 #pragma HLS ARRAY_PARTITION variable=rho_prev_set1 complete dim=0 // clang-format on // Our computation (with origin shifted to center) generates rho= -rM/2 to (rM/2)-1 // we need to add (rM/2) to make inexing/addressing of accumulator matrix easy // with offest added, the range will be 0 to (rM)-1 ap_fixed<14, 13, AP_RND> diag_offset = (rhoN) / 2; ap_fixed<14, 13, AP_RND> roundval = 0.5; ap_fixed<14, 13, AP_RND> rnd_Const_m0p5 = diag_offset - roundval; ap_fixed<14, 13, AP_RND> rnd_Const_p0p5 = diag_offset + roundval; ap_uint<13> hei = (height / 2); ap_uint<13> wdt = (width / 2); ap_fixed<28, 13, AP_RND> rho_stg1_sin_sin, rho_stg1_sin_cos; // Truncate height/2 & width/2 so that center falls on a pixel (not inbetween pixels) // When image size WxH, the co-ordinates ranges are // Origin Co-ordinates range // Top left(0,0) (0,0) to (W-1 x H-1) // Center (W/2, H/2) (-W/2,-H/2) to (H/2 -1 , W/2 - 1) // // Example: // For WxH = 6x4 // Top left - (0,0) to (5,3) // Center (3,2) - (-3,-2) to (2,1) // // For WxH = 5x4 // Top left - (0,0) to (4,3) // Center (2,2) - (-2,-2) to (2,1) // FILE *fpang = fopen("hlsang.txt","w"); // Initialization of all registers for (ap_uint<10> ki = 0; ki < (AngleN); ki++) { // clang-format off #pragma HLS pipeline // clang-format on rho_stg1_sin[ki] = (-wdt * cosvals[ki]) + (-hei * sinvals[ki]); // 13.15 // fprintf(fpang,"sin:%f cos%f\n",(float)(-wdt * cosvals[ki]),(float)(-hei * sinvals[ki])); // Splitting the computation seems to be helping in timing // rho_stg1_sin_sin = (-hei * sinvals[ki]); // 13.15 // rho_stg1_sin_cos = (-wdt * cosvals[ki]); // 13.15 // rho_stg1_sin[ki] = rho_stg1_sin_sin + rho_stg1_sin_cos; rho_prev_set1[ki] = 0; accval_reg_set1[ki] = 0; rho_stg3_reg[ki] = 0; } // fclose(fpang); ap_fixed<14, 13, AP_RND> rho_offset; ap_fixed<14, 13, AP_RND> rho_stg2_rsh; ap_uint<12> acc_val_set1, upd_accval_set1 = 0; ap_uint<23> row_index = 0; bool rho_stg2_lsbs; bool j_eq_width, j_eq_0, delay_1edge = 0; XF_PTNAME(DEPTH) img_pixel_val_reg = 0; // Row loop LOOPI: for (ap_uint<13> i = 0; i < height; i++) { // clang-format off #pragma HLS LOOP_TRIPCOUNT min=1 max=ROWS // clang-format on XF_PTNAME(DEPTH) img_pixel_val; // Column loop LOOPJ: for (ap_uint<12> j = 0; j < width; j++) { // clang-format off #pragma HLS LOOP_TRIPCOUNT min=1 max=COLS #pragma HLS PIPELINE #pragma HLS DEPENDENCE array inter false #pragma HLS LOOP_FLATTEN off // clang-format on img_pixel_val = _src_mat.read(row_index); // Reading one pixel at a time (address auto incremented) in raster scan order j_eq_width = (j == (width - 1)) ? 1 : 0; j_eq_0 = (j == 0) ? 1 : 0; LOOPN1: for (ap_uint<10> n = 0; n < (AngleN); n++) // angle loop { // clang-format off #pragma HLS UNROLL // clang-format on // rho_stg1_sin_wire[n] = rho_stg1_sin[n]; // Check if it is a new row (by checking if col=0) if (j_eq_0) { rho_stg2[n] = rho_stg1_sin[n]; } else { rho_stg2[n] = rho_stg1_cos[n]; } //------------------------------------------------- // Prepare value for the next pixel rho_stg1_cos[n] = rho_stg2[n] + cosvals[n]; if (j_eq_width) // Update when it is the last pixel of row rho_stg1_sin[n] = rho_stg1_sin[n] + sinvals[n]; // register_rho_stg1_sin(rho_stg1_sin[n], rho_stg1_sin_wire[n], sinvals[n], j_eq_width); //------------------------------------------------- //---------------------------------------------------------------- // Logic to select rounding consant to implement cvRound function rho_stg2_lsbs = (rho_stg2[n].range(15, 0) == 16384) ? 1 : 0; rho_offset = (rho_stg2_lsbs) ? rnd_Const_m0p5 : rnd_Const_p0p5; //---------------------------------------------------------------- // Prepare rho in 13.1 format to avoid big adder // as cvRound operation needs addition/subtration of 0.5 // and diag offset is added after cvRound operation rho_stg2_rsh.range(13, 0) = rho_stg2[n].range(27, 14); // cvRound rho and add diag_offset to shift rho values from [-rM/2,(rM/2)-1) to [0,rM-1) // So that we can address/index the accumulator cells easily rho_stg3_apfixedp1 = rho_stg2_rsh + rho_offset; rho_stg3_apfixedp0 = rho_stg3_apfixedp1.range(13, 1); rho_stg3 = rho_stg3_apfixedp0; //--------------------------------------------------------------------------------------------- // This logic ensures the voting increment happend even if the previous pixel is also // an edge pixel and has same rho. This logic will ensure that accumulator matrix is not read // but to use the recently incremeneted vote value if ((img_pixel_val_reg != 0)) // If pixel is a edge pixel, then update the vote { acc_val_set1 = accum[n][rho_stg3_reg[n]]; upd_accval_set1 = accval_reg_set1[n] + 1; if (rho_stg3_reg[n] == rho_prev_set1[n]) { accval_reg_set1[n] = upd_accval_set1; } else { accval_reg_set1[n] = acc_val_set1; } // Writing is pipelined. So, we start writing from the 2nd edge pixel if (delay_1edge) { accum[n][rho_prev_set1[n]] = upd_accval_set1; // Writing set 1 } rho_prev_set1[n] = rho_stg3_reg[n]; } //--------------------------------------------------------------------------------------------- rho_stg3_reg[n] = rho_stg3; } // Angle Loop if (img_pixel_val_reg != 0) { delay_1edge = 1; } row_index = row_index + 1; img_pixel_val_reg = img_pixel_val; } // Column loop } // Row Loop /* Left over pixel computation. Flushing the pipeline*/ LOOPN2: for (ap_uint<10> n = 0; n < (AngleN); n++) { // clang-format off #pragma HLS UNROLL // clang-format on upd_accval_set1 = accval_reg_set1[n] + 1; accum[n][rho_prev_set1[n]] = upd_accval_set1; // Writing set 1 } } /***************************************************************** * For Thinning process *****************************************************************/ template void thinningCompare(ap_uint<12> vote_at_rho_theta[AngleN + 1], ap_uint<12> vote_at_rho_theta_reg[AngleN + 1], bool cond1, bool cond2[AngleN], bool cond3, bool cond4, bool four_conds[AngleN], short threshold) { // clang-format off #pragma HLS INLINE OFF // clang-format on CONDLOOP: for (ap_uint<10> ang2 = 0; ang2 < AngleN; ang2++) { // clang-format off #pragma HLS PIPELINE // clang-format on cond1 = (vote_at_rho_theta[ang2 + 1] > vote_at_rho_theta[ang2]) ? 1 : 0; cond2[ang2 + 1] = (vote_at_rho_theta[ang2 + 1] > vote_at_rho_theta_reg[ang2 + 1]) ? 1 : 0; cond3 = (vote_at_rho_theta[ang2 + 1] > threshold) ? 1 : 0; cond4 = (vote_at_rho_theta[ang2 + 2] > vote_at_rho_theta[ang2 + 1]) ? 0 : 1; four_conds[ang2 + 1] = (cond1 && cond2[ang2 + 1] && cond3 && cond4) ? 1 : 0; } } /***************************************************************** * Thinning process ***************************************************************** * xfThinning * * | -- | x01 | -- | * * | x10 | x11 | x12 | * * | -- | x21 | -- | * * Comparing x11 with x01,x10,x12 and x21 * ((x11 > x01) && (x11 > x10) && (x11 >= x12) && (x11 >= x21) && x11>threshold) * *****************************************************************/ template void xfThinning(ap_uint<12> accumulator[AngleN + 1][rhoN + 1], short threshold) { ap_uint<12> vote_at_rho_theta[AngleN + 2], vote_at_rho_theta_reg[AngleN + 2]; bool cond1, cond2[AngleN + 1], cond3, cond4, four_conds[AngleN + 1], four_conds_reg[AngleN + 1], four_conds_reg_2[AngleN + 1]; // clang-format off #pragma HLS ARRAY_PARTITION variable=cond2 complete dim=0 #pragma HLS ARRAY_PARTITION variable=vote_at_rho_theta complete dim=0 #pragma HLS ARRAY_PARTITION variable=vote_at_rho_theta_reg complete dim=0 #pragma HLS ARRAY_PARTITION variable=four_conds complete dim=0 #pragma HLS ARRAY_PARTITION variable=four_conds_reg complete dim=0 #pragma HLS ARRAY_PARTITION variable=four_conds_reg_2 complete dim=0 // clang-format on // Initialization for (ap_uint<10> ang1 = 0; ang1 < AngleN + 1; ang1++) { // clang-format off #pragma HLS UNROLL // clang-format on vote_at_rho_theta_reg[ang1] = 0; four_conds_reg[ang1] = 0; four_conds_reg_2[ang1] = 1; } vote_at_rho_theta[0] = 0; vote_at_rho_theta[AngleN + 1] = 0; RHOLOOPTHINNING: for (ap_uint<13> r = 0; r < rhoN + 1; r++) { // clang-format off #pragma HLS LOOP_FLATTEN off // clang-format on //#pragma HLS PIPELINE THINNINGINIT: for (ap_uint<10> ang1 = 0; ang1 < AngleN; ang1++) { // clang-format off #pragma HLS UNROLL // clang-format on vote_at_rho_theta[ang1 + 1] = accumulator[ang1][r]; // use ang1+1 for assigning so that we can avoid the pad data bram (ang1=0) } // Compare flags for all Angles thinningCompare(vote_at_rho_theta, vote_at_rho_theta_reg, cond1, cond2, cond3, cond4, four_conds, threshold); THINWRITELOOP: for (ap_uint<10> ang3 = 0; ang3 < AngleN; ang3++) { // clang-format off #pragma HLS UNROLL // clang-format on if (four_conds_reg_2[ang3 + 1] && (r > 1)) accumulator[ang3][r - 2] = 0; vote_at_rho_theta_reg[ang3 + 1] = vote_at_rho_theta[ang3 + 1]; four_conds_reg_2[ang3 + 1] = (!four_conds_reg[ang3 + 1] || cond2[ang3 + 1]) ? 1 : 0; four_conds_reg[ang3 + 1] = four_conds[ang3 + 1]; } } } /***************************************************************** * For Sorting process * This function outputs the max Vote value and its rho location // in a given Angle memory *****************************************************************/ template void get_maxval_index(ap_uint<12> input_array[rhoN + 1], ap_uint<12>& maxval, ap_uint<12>& max_index) { // clang-format off #pragma HLS INLINE OFF // clang-format on ap_uint<12> local_max = 0; ap_uint<12> input_array_reg = 0; RHOLOOP: for (ap_uint<13> r = 0; r < rhoN; r++) // diagonal loop; Value @rhoN+1 is 0 anyway { // clang-format off #pragma HLS PIPELINE // clang-format on input_array_reg = input_array[r]; // Regsister BRAM output if (input_array_reg > local_max) // comparing rho values to find max rho value { local_max = input_array_reg; max_index = r; } } maxval = local_max; } /***************************************************************** * Sorting process ***************************************************************** * xfSorting * * * Finding the Top linesMax values in the accumulator buffer. * Inside linesMax loop,for all the theta values we are doing rho comparision in parallel. * and the rho comparisions are in pipeline loop. * * Once the final rho and theta values are computed, Returning theta in radians and rho values as float ****************************************************************/ template void xfSorting(ap_uint<12> accumulator[AngleN + 1][rhoN + 1], float linesrho[linesMax], float linestheta[linesMax], short linesmax) { ap_uint<12> local_max[AngleN]; ap_uint<12> local_max_rho[AngleN]; ap_uint<12> maxrho = 0; ap_uint<10> maxangle = 0; // clang-format off #pragma HLS ARRAY_PARTITION variable=local_max complete #pragma HLS ARRAY_PARTITION variable=local_max_rho complete // clang-format on // Initialize local max (make max vote vale as 0 for each angle) for (ap_uint<10> i = 0; i < AngleN; i++) { // clang-format off #pragma HLS UNROLL // clang-format on local_max[i] = 0; } ap_fixed<14, 13, AP_RND> diag_offset = (rhoN) / 2.0; MAINL: for (ap_uint<12> li = 0; li < linesmax; li++) // linesMax loop { // clang-format off #pragma HLS LOOP_TRIPCOUNT min=1 max=linesMax // clang-format on for (ap_uint<10> n = 0; n < AngleN; n++) // Theta loop { // clang-format off #pragma HLS UNROLL // clang-format on get_maxval_index(accumulator[n], local_max[n], local_max_rho[n]); } // Find the global maxima (of vote value) of the local maxima ap_uint<12> maxfinal = 0; THETAL: for (ap_uint<10> n = 0; n < AngleN; n++) { // clang-format off #pragma HLS PIPELINE // clang-format on if (local_max[n] > maxfinal) // Vote comparision { maxfinal = local_max[n]; maxangle = n; maxrho = local_max_rho[n]; } } // Make the location of that localMaxima 0 if that localMaxima becomes globalMaxima accumulator[maxangle][maxrho] = 0; float mintheta_radn = (MINTHETA * pai_by_180); float ang_temp = pai_by_360 * (theta); float angle_radn = (maxangle * ang_temp); float _rho = (maxrho - diag_offset) * rho; // rho computation float _angle = angle_radn + mintheta_radn; // Theta computation linesrho[li] = _rho; // updating rho value linestheta[li] = _angle; // updating theta value } } template void xfHoughLines(xf::cv::Mat& _src_mat, float outputrho[linesMax], float outputtheta[linesMax], short _threshold, ap_uint<12> height, ap_uint<12> width, short linesmax) { // clang-format off #pragma HLS INLINE OFF // clang-format on // Accumulator buffer declaration // we need theta=[0,AngleN-1] and rho=[0,rhoN-1]; // Also, we need theta=AngleN & r=rhoN for padding // But, BRAM for AngleN is avoided by handling it in design // using vote_at_rho_theta[AngleN] =0 in Thinning // // Example : MAXTHETA = 5; MINTHETA = 2; THETA=1 i.e ThetaStep=0.5 // (5-2)/0.5 = 6 ; {2, 2.5, 3, 3.5, 4, 4.5} // i.e MAXTHETA is not included // // If width=4 and Height=3, then Diag=5 // so, if rhoStep=1, then rho takes value 0,1,2,3,4 // i.e., {0,1,, ...Diag-1} // // rho=0 and theta=0 will have actual values // Padding on the left side (zero side) of rho and theta // are handled in the design. But we need memory for pad data on right ap_uint<12> accum[((2 * (MAXTHETA - MINTHETA)) / theta) + 1][(DIAG) + 1]; // clang-format off #pragma HLS ARRAY_PARTITION variable=accum complete dim=1 #pragma HLS RESOURCE variable=accum core=RAM_T2P_BRAM // clang-format on xfVoting(_src_mat, accum, height, width); // votes updation xfThinning( accum, _threshold); // thinning -->NMS xfSorting(accum, outputrho, outputtheta, linesmax); // finding the linesmax lines from the complete accum buffer } /************************************************************************** * HoughLines : Wrapper function which calls the kernel function * depending upon the configurations. * * RHO = RHOSTEP * THETA=THETASTEP * MAXLINES=MAXIMUM NUMBER OF TOP LINES * DIAG=DIAGONAL OF THE IMAGE * MINTHETA = MINIMUM THETA * MAXTHETA = MAXIMUM THETA * SRC_T = SOURCE TYPE * ROWS = HEIGHT OF THE IMAGE * COLS = WIDTH OF THE IMAGE * NPC = NUMBER OF PIXELS PROCESSED PER CYCLE * * _src_mat: input image * outputrho:output rho values * outputtheta:output theta values * threshold:threshold value to supress votes in the kernel * **************************************************************************/ template void HoughLines(xf::cv::Mat& _src_mat, float outputrho[MAXLINES], float outputtheta[MAXLINES], short threshold, short linesmax) { // clang-format off #pragma HLS INLINE OFF // clang-format on #ifndef __SYNTHESIS__ assert(((_src_mat.rows <= ROWS) && (_src_mat.cols <= COLS)) && "ROWS and COLS should be greater than input image"); assert((NPC == XF_NPPC1) && "NPC must be XF_NPPC1"); assert((((MAXTHETA - MINTHETA) > 0)) && "MINTHETA must be less than MAXTHETA"); assert(((MINTHETA >= 0) && (MINTHETA < 180)) && "MINTHETA must be between 0 to 180"); assert(((MAXTHETA > 0) && (MAXTHETA <= 180)) && "MAXTHETA must be between 0 to 180"); #endif // Main function calling xfHoughLines(_src_mat, outputrho, outputtheta, threshold, _src_mat.rows, _src_mat.cols, linesmax); } } // namespace cv } // namespace xf #endif //_XF_HOUGHLINES_HPP_