├── 1.RTL ├── sim │ ├── canny_tb.sv │ ├── cordic_sqrt_tb.v │ ├── sim_cmos_tb.sv │ └── video_to_pic.sv └── source │ ├── VIP_RGB888_YCbCr444.v │ ├── canny_doubleThreshold.v │ ├── canny_edge_detect_top.v │ ├── canny_get_grandient.v │ ├── canny_nonLocalMaxValue.v │ ├── cordic_pipline.v │ ├── cordic_sqrt.v │ ├── fifo_ram.v │ ├── matrix_generate_3x3.v │ ├── one_column_ram.v │ └── vip_gaussian_filter.v ├── 2.C++_Model └── canny.cpp ├── 3.Vivado_Project ├── CannyEdgeDetection.xpr └── image_proces_tb_behav.wcfg ├── 4.QuestaSim └── readme.md ├── 5.Pic ├── monkey.bmp ├── outcom.bmp └── readme_pic │ └── result.png └── README.md /1.RTL/sim/canny_tb.sv: -------------------------------------------------------------------------------- 1 | `timescale 1ns / 1ps 2 | module canny_tb(); 3 | 4 | // `define Modelsim_Sim 5 | `define Vivado_Sim 6 | 7 | //-------------------------------------------------------------------------------- 8 | `ifdef Modelsim_Sim 9 | localparam PIC_INPUT_PATH = "..\\5.pic\\monkey.bmp" ; 10 | localparam PIC_OUTPUT_PATH = "..\\5.pic\\outcom.bmp" ; 11 | `endif 12 | //-------------------------------------------------------------------------------- 13 | `ifdef Vivado_Sim 14 | localparam PIC_INPUT_PATH = "../../../../../5.pic/monkey.bmp" ; 15 | localparam PIC_OUTPUT_PATH = "../../../../../5.pic/outcom.bmp" ; 16 | `endif 17 | 18 | localparam PIC_WIDTH = 640 ; 19 | localparam PIC_HEIGHT = 480 ; 20 | 21 | reg cmos_clk = 0; 22 | reg cmos_rst_n = 0; 23 | 24 | wire cmos_vsync ; 25 | wire cmos_href ; 26 | wire cmos_clken ; 27 | wire [23:0] cmos_data ; 28 | 29 | parameter cmos0_period = 6; 30 | 31 | always#(cmos0_period/2) cmos_clk = ~cmos_clk; 32 | initial #(20*cmos0_period) cmos_rst_n = 1; 33 | 34 | //-------------------------------------------------- 35 | //Camera Simulation 36 | sim_cmos #( 37 | .PIC_PATH (PIC_INPUT_PATH ) 38 | , .IMG_HDISP (PIC_WIDTH ) 39 | , .IMG_VDISP (PIC_HEIGHT ) 40 | )u_sim_cmos0( 41 | .clk (cmos_clk ) 42 | , .rst_n (cmos_rst_n ) 43 | , .CMOS_VSYNC (cmos_vsync ) 44 | , .CMOS_HREF (cmos_href ) 45 | , .CMOS_CLKEN (cmos_clken ) 46 | , .CMOS_DATA (cmos_data ) 47 | , .X_POS () 48 | , .Y_POS () 49 | ); 50 | 51 | //-------------------------------------------------- 52 | //Image Processing 53 | 54 | wire post0_vsync ; 55 | wire post0_href ; 56 | wire post0_clken ; 57 | wire [7:0] post0_img_Y ; 58 | wire [7:0] post0_img_Cb ; 59 | wire [7:0] post0_img_Cr ; 60 | 61 | wire gauss_vsync ; 62 | wire gauss_hsync ; 63 | wire gauss_de ; 64 | wire [7:0] img_gauss ; 65 | 66 | wire canny_vsync ; 67 | wire canny_hsync ; 68 | wire canny_de ; 69 | wire img_canny ; 70 | 71 | //RGB888 to YCbCr444 72 | VIP_RGB888_YCbCr444 u_VIP_RGB888_YCbCr444 73 | ( 74 | .clk (cmos_clk ) 75 | , .rst_n (cmos_rst_n ) 76 | 77 | , .per_frame_vsync (cmos_vsync ) 78 | , .per_frame_href (cmos_href ) 79 | , .per_frame_clken (cmos_clken ) 80 | , .per_img_red (cmos_data[16+:8] ) 81 | , .per_img_green (cmos_data[ 8+:8] ) 82 | , .per_img_blue (cmos_data[ 0+:8] ) 83 | 84 | , .post_frame_vsync (post0_vsync ) 85 | , .post_frame_href (post0_href ) 86 | , .post_frame_clken (post0_clken ) 87 | , .post_img_Y (post0_img_Y ) 88 | , .post_img_Cb (post0_img_Cb ) 89 | , .post_img_Cr (post0_img_Cr ) 90 | ); 91 | 92 | //Gaussian Filter 93 | image_gaussian_filter u_image_gaussian_filter 94 | ( 95 | .clk (cmos_clk ) 96 | , .rst_n (cmos_rst_n ) 97 | 98 | , .per_frame_vsync (post0_vsync ) 99 | , .per_frame_href (post0_href ) 100 | , .per_frame_clken (post0_clken ) 101 | , .per_img_gray (post0_img_Y ) 102 | 103 | , .post_frame_vsync (gauss_vsync ) 104 | , .post_frame_href (gauss_hsync ) 105 | , .post_frame_clken (gauss_de ) 106 | , .post_img_gray (img_gauss ) 107 | ); 108 | 109 | //Canny Edge Detection 110 | canny_edge_detect_top u_canny_edge_detect_top( 111 | .clk (cmos_clk ) 112 | , .rst_n (cmos_rst_n ) 113 | 114 | , .per_frame_vsync (gauss_vsync ) 115 | , .per_frame_href (gauss_hsync ) 116 | , .per_frame_clken (gauss_de ) 117 | , .per_img_y (img_gauss ) 118 | 119 | , .post_frame_vsync (canny_vsync ) 120 | , .post_frame_href (canny_hsync ) 121 | , .post_frame_clken (canny_de ) 122 | , .post_img_bit (img_canny ) 123 | ); 124 | 125 | 126 | //-------------------------------------------------- 127 | //Video saving 128 | video_to_pic #( 129 | .PIC_PATH (PIC_OUTPUT_PATH ) 130 | , .START_FRAME (1 ) 131 | , .IMG_HDISP (PIC_WIDTH ) 132 | , .IMG_VDISP (PIC_HEIGHT ) 133 | )u_video_to_pic0( 134 | .clk (cmos_clk ) 135 | , .rst_n (cmos_rst_n ) 136 | , .video_vsync (canny_vsync ) 137 | , .video_hsync (canny_hsync ) 138 | , .video_de (canny_de ) 139 | , .video_data ({24{img_canny}} ) 140 | ); 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | endmodule -------------------------------------------------------------------------------- /1.RTL/sim/cordic_sqrt_tb.v: -------------------------------------------------------------------------------- 1 | `timescale 1ns / 1ps 2 | module cordic_sqrt_tb(); 3 | 4 | reg clk; 5 | reg rst_n; 6 | 7 | wire [10 : 0] out; 8 | 9 | initial begin 10 | clk = 0; 11 | rst_n = 0; 12 | #2 13 | rst_n = 1; 14 | end 15 | 16 | always#1 begin 17 | clk = !clk; 18 | end 19 | 20 | 21 | 22 | 23 | cordic_sqrt u_cordic_sqrt( 24 | .clk (clk), 25 | .rst_n (rst_n), 26 | .sqrt_in_0 (21'd40), 27 | .sqrt_in_1 (21'd40), 28 | 29 | .sqrt_out (out) 30 | ); 31 | 32 | 33 | 34 | endmodule 35 | -------------------------------------------------------------------------------- /1.RTL/sim/sim_cmos_tb.sv: -------------------------------------------------------------------------------- 1 | module sim_cmos#( 2 | parameter PIC_PATH = "../../../../../../pic/duck_fog.bmp" 3 | , parameter IMG_HDISP = 640 4 | , parameter IMG_VDISP = 480 5 | )( 6 | input clk 7 | , input rst_n 8 | 9 | , output CMOS_VSYNC 10 | , output CMOS_HREF 11 | , output CMOS_CLKEN 12 | , output [23:0] CMOS_DATA 13 | , output [10:0] X_POS 14 | , output [10:0] Y_POS 15 | ); 16 | 17 | integer iBmpFileId; 18 | 19 | integer oTxtFileId; 20 | 21 | integer iIndex = 0; 22 | 23 | integer iCode; 24 | 25 | integer iBmpWidth; //输入BMP 宽度 26 | integer iBmpHight; //输入BMP 高度 27 | integer iBmpSize; //输入BMP 字节数 28 | integer iDataStartIndex; //输入BMP 像素数据偏移量 29 | 30 | localparam BMP_SIZE = 54 + IMG_HDISP * IMG_VDISP * 3 - 1; //输出BMP 字节数 31 | 32 | reg [ 7:0] rBmpData [0:BMP_SIZE]; 33 | 34 | integer i,j; 35 | 36 | 37 | //--------------------------------------------- 38 | initial begin 39 | iBmpFileId = $fopen(PIC_PATH,"rb"); 40 | 41 | iCode = $fread(rBmpData,iBmpFileId); 42 | 43 | //根据BMP图片文件头的格式,分别计算出图片的 宽度 /高度 /像素数据偏移量 /图片字节数 44 | iBmpWidth = {rBmpData[21],rBmpData[20],rBmpData[19],rBmpData[18]}; 45 | iBmpHight = {rBmpData[25],rBmpData[24],rBmpData[23],rBmpData[22]}; 46 | iBmpSize = {rBmpData[ 5],rBmpData[ 4],rBmpData[ 3],rBmpData[ 2]}; 47 | iDataStartIndex = {rBmpData[13],rBmpData[12],rBmpData[11],rBmpData[10]}; 48 | 49 | //关闭输入BMP图片 50 | $fclose(iBmpFileId); 51 | // if((iBmpWidth!= IMG_HDISP) | (iBmpHight!=IMG_VDISP)) begin 52 | // $display("Resolution mismatching.\n"); 53 | // $stop; 54 | // end 55 | end 56 | 57 | //--------------------------------------------- 58 | //产生摄像头时序 59 | 60 | wire cmos_vsync ; 61 | reg cmos_href; 62 | wire cmos_clken; 63 | reg [23:0] cmos_data; 64 | 65 | reg cmos_clken_r; 66 | 67 | reg [31:0] cmos_index; 68 | 69 | localparam H_SYNC = 11'd10; 70 | localparam H_BACK = 11'd10; 71 | localparam H_DISP = IMG_HDISP; 72 | localparam H_FRONT = 11'd10; 73 | localparam H_TOTAL = H_SYNC + H_BACK + H_DISP + H_FRONT; 74 | 75 | localparam V_SYNC = 11'd10; 76 | localparam V_BACK = 11'd10; 77 | localparam V_DISP = IMG_VDISP; 78 | localparam V_FRONT = 11'd10; 79 | localparam V_TOTAL = V_SYNC + V_BACK + V_DISP + V_FRONT; 80 | 81 | //--------------------------------------------- 82 | //模拟 OV7725/OV5640 驱动模块输出的时钟使能 83 | always@(posedge clk or negedge rst_n) begin 84 | if(!rst_n) 85 | cmos_clken_r <= 0; 86 | else 87 | cmos_clken_r <= ~cmos_clken_r; 88 | end 89 | 90 | //--------------------------------------------- 91 | //水平计数器 92 | reg [10:0] hcnt; 93 | always@(posedge clk or negedge rst_n) begin 94 | if(!rst_n) 95 | hcnt <= 11'd0; 96 | else if(cmos_clken_r) 97 | hcnt <= (hcnt < H_TOTAL - 1'b1) ? hcnt + 1'b1 : 11'd0; 98 | end 99 | 100 | //--------------------------------------------- 101 | //竖直计数器 102 | reg [10:0] vcnt; 103 | always@(posedge clk or negedge rst_n) begin 104 | if(!rst_n) 105 | vcnt <= 11'd0; 106 | else if(cmos_clken_r) begin 107 | if(hcnt == H_TOTAL - 1'b1) 108 | vcnt <= (vcnt < V_TOTAL - 1'b1) ? vcnt + 1'b1 : 11'd0; 109 | else 110 | vcnt <= vcnt; 111 | end 112 | end 113 | 114 | //--------------------------------------------- 115 | //场同步 116 | reg cmos_vsync_r; 117 | always@(posedge clk or negedge rst_n) begin 118 | if(!rst_n) 119 | cmos_vsync_r <= 1'b0; //H: Vaild, L: inVaild 120 | else begin 121 | if(vcnt <= V_SYNC - 1'b1) 122 | cmos_vsync_r <= 1'b0; //H: Vaild, L: inVaild 123 | else 124 | cmos_vsync_r <= 1'b1; //H: Vaild, L: inVaild 125 | end 126 | end 127 | assign cmos_vsync = cmos_vsync_r; 128 | 129 | //--------------------------------------------- 130 | //行有效 131 | wire frame_valid_ahead = ( vcnt >= V_SYNC + V_BACK && vcnt < V_SYNC + V_BACK + V_DISP 132 | && hcnt >= H_SYNC + H_BACK && hcnt < H_SYNC + H_BACK + H_DISP ) 133 | ? 1'b1 : 1'b0; 134 | 135 | reg cmos_href_r; 136 | always@(posedge clk or negedge rst_n) begin 137 | if(!rst_n) 138 | cmos_href_r <= 0; 139 | else begin 140 | if(frame_valid_ahead) 141 | cmos_href_r <= 1; 142 | else 143 | cmos_href_r <= 0; 144 | end 145 | end 146 | 147 | always@(posedge clk or negedge rst_n) begin 148 | if(!rst_n) 149 | cmos_href <= 0; 150 | else 151 | cmos_href <= cmos_href_r; 152 | end 153 | 154 | assign cmos_clken = cmos_href & cmos_clken_r; 155 | 156 | //------------------------------------- 157 | //从数组中以视频格式输出像素数据 158 | wire [10:0] x_pos; 159 | wire [10:0] y_pos; 160 | 161 | assign x_pos = frame_valid_ahead ? (hcnt - (H_SYNC + H_BACK )) : 0; 162 | assign y_pos = frame_valid_ahead ? (vcnt - (V_SYNC + V_BACK )) : 0; 163 | 164 | always@(posedge clk or negedge rst_n)begin 165 | if(!rst_n) begin 166 | cmos_index <= 0; 167 | cmos_data <= 24'd0; 168 | end 169 | else begin 170 | cmos_index <= y_pos * IMG_HDISP * 3 + x_pos * 3 + 54; // 3*(y*640 + x) + 54 171 | cmos_data <= {rBmpData[cmos_index], rBmpData[cmos_index+1] , rBmpData[cmos_index+2]}; 172 | end 173 | end 174 | 175 | reg [10:0] x_pos_d [0 : 10]; 176 | reg [10:0] y_pos_d [0 : 10]; 177 | 178 | always@(posedge clk or negedge rst_n)begin 179 | if(!rst_n)begin 180 | for(i = 0; i < 11; i = i + 1)begin 181 | x_pos_d[i] <= 0; 182 | y_pos_d[i] <= 0; 183 | end 184 | end 185 | else begin 186 | x_pos_d[0] <= x_pos; 187 | y_pos_d[0] <= y_pos; 188 | for(i = 1; i < 11; i = i + 1)begin 189 | x_pos_d[i] <= x_pos_d[i-1]; 190 | y_pos_d[i] <= y_pos_d[i-1]; 191 | end 192 | end 193 | end 194 | 195 | assign CMOS_VSYNC = cmos_vsync; 196 | assign CMOS_HREF = cmos_href; 197 | assign CMOS_CLKEN = cmos_clken; 198 | assign CMOS_DATA = cmos_data; 199 | assign X_POS = x_pos; 200 | assign Y_POS = y_pos; 201 | 202 | 203 | endmodule -------------------------------------------------------------------------------- /1.RTL/sim/video_to_pic.sv: -------------------------------------------------------------------------------- 1 | `timescale 1ns / 1ns 2 | module video_to_pic#( 3 | parameter PIC_PATH = "..\\pic\\outcom.bmp" 4 | , parameter START_FRAME = 1 5 | , parameter IMG_HDISP = 640 6 | , parameter IMG_VDISP = 480 7 | )( 8 | input clk 9 | , input rst_n 10 | , input video_vsync 11 | , input video_hsync 12 | , input video_de 13 | , input [23:0] video_data 14 | ); 15 | 16 | integer iCode; 17 | integer iBmpFileId; 18 | integer iBmpWidth; 19 | integer iBmpHight; 20 | integer iBmpSize; 21 | integer iDataStartIndex; 22 | integer iIndex = 0; 23 | 24 | localparam BMP_SIZE = 54 + IMG_HDISP * IMG_VDISP * 3 - 1; 25 | reg [ 7:0] BmpHead [0:53]; 26 | reg [ 7:0] Vip_BmpData [0:BMP_SIZE]; 27 | reg [ 7:0] vip_pixel_data [0:BMP_SIZE-54]; 28 | reg [31:0] rBmpWord; 29 | 30 | reg video_vsync_d1 = 0; 31 | reg [11:0] frame_cnt = 0; 32 | reg [31:0] PIC_cnt = 0; 33 | 34 | wire [7:0] PIC_img_R; 35 | wire [7:0] PIC_img_G; 36 | wire [7:0] PIC_img_B; 37 | assign PIC_img_R = video_data[16+:8]; 38 | assign PIC_img_G = video_data[ 8+:8]; 39 | assign PIC_img_B = video_data[ 0+:8]; 40 | 41 | 42 | always@(posedge clk or negedge rst_n)begin 43 | if(!rst_n)begin 44 | video_vsync_d1 <= 0; 45 | end 46 | else begin 47 | video_vsync_d1 <= video_vsync; 48 | end 49 | end 50 | 51 | always@(posedge clk or negedge rst_n)begin 52 | if(!rst_n)begin 53 | frame_cnt <= 0; 54 | end 55 | else if(video_vsync_d1 & !video_vsync)begin 56 | frame_cnt <= frame_cnt + 1; 57 | end 58 | end 59 | 60 | 61 | 62 | always@(posedge clk or negedge rst_n)begin 63 | if(!rst_n) begin 64 | PIC_cnt <= 32'd0; 65 | end 66 | else if(video_de) begin 67 | if(frame_cnt == START_FRAME - 1) begin 68 | PIC_cnt <= PIC_cnt + 3; 69 | vip_pixel_data[PIC_cnt+0] <= PIC_img_R; 70 | vip_pixel_data[PIC_cnt+1] <= PIC_img_G; 71 | vip_pixel_data[PIC_cnt+2] <= PIC_img_B; 72 | end 73 | end 74 | end 75 | 76 | 77 | //--------------------------------------------- 78 | //initial the BMP file header 79 | //The detail BMP file header is inpart from https://blog.csdn.net/Meteor_s/article/details/82414155 and inpart from my test. 80 | initial begin 81 | for(iIndex = 0; iIndex < 54; iIndex = iIndex + 1) begin 82 | BmpHead[iIndex] = 0; 83 | end 84 | #2 85 | {BmpHead[1],BmpHead[0]} = {8'h4D,8'h42}; 86 | {BmpHead[5],BmpHead[4],BmpHead[3],BmpHead[2]} = BMP_SIZE + 1;//File Size (Bytes) 87 | BmpHead[10] = 8'd54;//Bitmap Data Offset 88 | BmpHead[14] = 8'h28;//Bitmap Header Size 89 | {BmpHead[21],BmpHead[20],BmpHead[19],BmpHead[18]} = IMG_HDISP;//Width 90 | {BmpHead[25],BmpHead[24],BmpHead[23],BmpHead[22]} = IMG_VDISP;//Height 91 | BmpHead[26] = 8'd1;//Number of Color Planes 92 | BmpHead[28] = 8'd24;//Bits per Pixel 93 | end 94 | 95 | 96 | 97 | //--------------------------------------------- 98 | //write the data to the output bmp file 99 | initial begin 100 | //--------------------------------------------- 101 | //waiting for the start frame 102 | wait(frame_cnt == START_FRAME); 103 | 104 | iBmpFileId = $fopen(PIC_PATH,"wb+"); 105 | for (iIndex = 0; iIndex < BMP_SIZE + 1; iIndex = iIndex + 1) begin 106 | if(iIndex < 54) begin 107 | Vip_BmpData[iIndex] = BmpHead[iIndex]; 108 | end 109 | else begin 110 | Vip_BmpData[iIndex] = vip_pixel_data[iIndex-54]; 111 | end 112 | end 113 | for (iIndex = 0; iIndex < BMP_SIZE + 1; iIndex = iIndex + 1) begin 114 | $fwrite(iBmpFileId,"%c",Vip_BmpData[iIndex]); 115 | end 116 | $fclose(iBmpFileId); 117 | end 118 | 119 | 120 | 121 | 122 | endmodule -------------------------------------------------------------------------------- /1.RTL/source/VIP_RGB888_YCbCr444.v: -------------------------------------------------------------------------------- 1 | /*----------------------------------------------------------------------- 2 | \\\|/// 3 | \\ - - // 4 | ( @ @ ) 5 | +-----------------------------oOOo-(_)-oOOo-----------------------------+ 6 | CONFIDENTIAL IN CONFIDENCE 7 | This confidential and proprietary software may be only used as authorized 8 | by a licensing agreement from CrazyBingo (Thereturnofbingo). 9 | In the event of publication, the following notice is applicable: 10 | Copyright (C) 2011-20xx CrazyBingo Corporation 11 | The entire notice above must be reproduced on all authorized copies. 12 | Author : CrazyBingo 13 | Technology blogs : www.crazyfpga.com 14 | Email Address : crazyfpga@vip.qq.com 15 | Filename : VIP_Sobel_Edge_Detector.v 16 | Date : 2013-05-26 17 | Description : Convert the RGB888 format to YCbCr444 format. 18 | Modification History : 19 | Date By Version Change Description 20 | ========================================================================= 21 | 13/05/26 CrazyBingo 1.0 Original 22 | 14/03/16 CrazyBingo 2.0 Modification 23 | ------------------------------------------------------------------------- 24 | | Oooo | 25 | +-------------------------------oooO--( )-----------------------------+ 26 | ( ) ) / 27 | \ ( (_/ 28 | \_) 29 | -----------------------------------------------------------------------*/ 30 | 31 | `timescale 1ns/1ns 32 | module VIP_RGB888_YCbCr444 33 | ( 34 | //global clock 35 | input clk, //cmos video pixel clock 36 | input rst_n, //global reset 37 | 38 | //Image data prepred to be processd 39 | input per_frame_vsync, //Prepared Image data vsync valid signal 40 | input per_frame_href, //Prepared Image data href vaild signal 41 | input per_frame_clken, //Prepared Image data output/capture enable clock 42 | input [7:0] per_img_red, //Prepared Image red data to be processed 43 | input [7:0] per_img_green, //Prepared Image green data to be processed 44 | input [7:0] per_img_blue, //Prepared Image blue data to be processed 45 | 46 | //Image data has been processd 47 | output post_frame_vsync, //Processed Image data vsync valid signal 48 | output post_frame_href, //Processed Image data href vaild signal 49 | output post_frame_clken, //Processed Image data output/capture enable clock 50 | output [7:0] post_img_Y, //Processed Image brightness output 51 | output [7:0] post_img_Cb, //Processed Image blue shading output 52 | output [7:0] post_img_Cr //Processed Image red shading output 53 | ); 54 | 55 | //-------------------------------------------- 56 | /********************************************* 57 | //Refer to page 5 58 | Y = (77 *R + 150*G + 29 *B)>>8 59 | Cb = (-43*R - 85 *G + 128*B)>>8 + 128 60 | Cr = (128*R - 107*G - 21 *B)>>8 + 128 61 | ---> 62 | Y = (77 *R + 150*G + 29 *B)>>8 63 | Cb = (-43*R - 85 *G + 128*B + 32768)>>8 64 | Cr = (128*R - 107*G - 21 *B + 32768)>>8 65 | **********************************************/ 66 | //Step 1 67 | reg [15:0] img_red_r0, img_red_r1, img_red_r2; 68 | reg [15:0] img_green_r0, img_green_r1, img_green_r2; 69 | reg [15:0] img_blue_r0, img_blue_r1, img_blue_r2; 70 | always@(posedge clk or negedge rst_n) 71 | begin 72 | if(!rst_n) 73 | begin 74 | img_red_r0 <= 0; 75 | img_red_r1 <= 0; 76 | img_red_r2 <= 0; 77 | img_green_r0 <= 0; 78 | img_green_r1 <= 0; 79 | img_green_r2 <= 0; 80 | img_blue_r0 <= 0; 81 | img_blue_r1 <= 0; 82 | img_blue_r2 <= 0; 83 | end 84 | else 85 | begin 86 | img_red_r0 <= per_img_red * 8'd77; 87 | img_red_r1 <= per_img_red * 8'd43; 88 | img_red_r2 <= per_img_red * 8'd128; 89 | img_green_r0 <= per_img_green * 8'd150; 90 | img_green_r1 <= per_img_green * 8'd85; 91 | img_green_r2 <= per_img_green * 8'd107; 92 | img_blue_r0 <= per_img_blue * 8'd29; 93 | img_blue_r1 <= per_img_blue * 8'd128; 94 | img_blue_r2 <= per_img_blue * 8'd21; 95 | end 96 | end 97 | 98 | //-------------------------------------------------- 99 | //Step 2 100 | reg [15:0] img_Y_r0; 101 | reg [15:0] img_Cb_r0; 102 | reg [15:0] img_Cr_r0; 103 | always@(posedge clk or negedge rst_n) 104 | begin 105 | if(!rst_n) 106 | begin 107 | img_Y_r0 <= 0; 108 | img_Cb_r0 <= 0; 109 | img_Cr_r0 <= 0; 110 | end 111 | else 112 | begin 113 | img_Y_r0 <= img_red_r0 + img_green_r0 + img_blue_r0; 114 | img_Cb_r0 <= img_blue_r1 - img_red_r1 - img_green_r1 + 16'd32768; 115 | img_Cr_r0 <= img_red_r2 + img_green_r2 + img_blue_r2 + 16'd32768; 116 | end 117 | end 118 | 119 | //-------------------------------------------------- 120 | //Step 3 121 | reg [7:0] img_Y_r1; 122 | reg [7:0] img_Cb_r1; 123 | reg [7:0] img_Cr_r1; 124 | always@(posedge clk or negedge rst_n) 125 | begin 126 | if(!rst_n) 127 | begin 128 | img_Y_r1 <= 0; 129 | img_Cb_r1 <= 0; 130 | img_Cr_r1 <= 0; 131 | end 132 | else 133 | begin 134 | img_Y_r1 <= img_Y_r0[15:8]; 135 | img_Cb_r1 <= img_Cb_r0[15:8]; 136 | img_Cr_r1 <= img_Cr_r0[15:8]; 137 | end 138 | end 139 | 140 | 141 | 142 | //------------------------------------------ 143 | //lag 3 clocks signal sync 144 | reg [2:0] per_frame_vsync_r; 145 | reg [2:0] per_frame_href_r; 146 | reg [2:0] per_frame_clken_r; 147 | always@(posedge clk or negedge rst_n) 148 | begin 149 | if(!rst_n) 150 | begin 151 | per_frame_vsync_r <= 0; 152 | per_frame_href_r <= 0; 153 | per_frame_clken_r <= 0; 154 | end 155 | else 156 | begin 157 | per_frame_vsync_r <= {per_frame_vsync_r[1:0], per_frame_vsync }; 158 | per_frame_href_r <= {per_frame_href_r[1:0], per_frame_href }; 159 | per_frame_clken_r <= {per_frame_clken_r[1:0], per_frame_clken }; 160 | end 161 | end 162 | assign post_frame_vsync = per_frame_vsync_r[2]; 163 | assign post_frame_href = per_frame_href_r[2]; 164 | assign post_frame_clken = per_frame_clken_r[2]; 165 | assign post_img_Y = post_frame_href ? img_Y_r1 : 8'd0; 166 | assign post_img_Cb = post_frame_href ? img_Cb_r1: 8'd0; 167 | assign post_img_Cr = post_frame_href ? img_Cr_r1: 8'd0; 168 | endmodule 169 | -------------------------------------------------------------------------------- /1.RTL/source/canny_doubleThreshold.v: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DOUDIU/Hardware-Implementation-of-the-Canny-Edge-Detection-Algorithm/e792cb1b6a6244a7d930431b2a35aba6e0d4db8c/1.RTL/source/canny_doubleThreshold.v -------------------------------------------------------------------------------- /1.RTL/source/canny_edge_detect_top.v: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DOUDIU/Hardware-Implementation-of-the-Canny-Edge-Detection-Algorithm/e792cb1b6a6244a7d930431b2a35aba6e0d4db8c/1.RTL/source/canny_edge_detect_top.v -------------------------------------------------------------------------------- /1.RTL/source/canny_get_grandient.v: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DOUDIU/Hardware-Implementation-of-the-Canny-Edge-Detection-Algorithm/e792cb1b6a6244a7d930431b2a35aba6e0d4db8c/1.RTL/source/canny_get_grandient.v -------------------------------------------------------------------------------- /1.RTL/source/canny_nonLocalMaxValue.v: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DOUDIU/Hardware-Implementation-of-the-Canny-Edge-Detection-Algorithm/e792cb1b6a6244a7d930431b2a35aba6e0d4db8c/1.RTL/source/canny_nonLocalMaxValue.v -------------------------------------------------------------------------------- /1.RTL/source/cordic_pipline.v: -------------------------------------------------------------------------------- 1 | module cordic_pipline#( 2 | parameter DATA_WIDTH_IN = 11, 3 | parameter Pipeline = 16 4 | )( 5 | input clk, 6 | input rst_n, 7 | input signed [DATA_WIDTH_IN - 1 : 0] x_in, 8 | input signed [DATA_WIDTH_IN - 1 : 0] y_in, 9 | input polar_flag, 10 | input [5 : 0] pipline_level, 11 | input [31 : 0] rot, 12 | input [31 : 0] rot_in, 13 | 14 | output reg [31 : 0] rot_out, 15 | output reg signed [DATA_WIDTH_IN - 1 : 0] x_out, 16 | output reg signed [DATA_WIDTH_IN - 1 : 0] y_out 17 | ); 18 | 19 | 20 | always @ (posedge clk or negedge rst_n) 21 | begin 22 | if(!rst_n) begin 23 | x_out <= 0; 24 | y_out <= 0; 25 | end 26 | else begin 27 | if(polar_flag)begin 28 | x_out <= x_in + (y_in >>> pipline_level); 29 | y_out <= y_in - (x_in >>> pipline_level); 30 | rot_out <= rot_in + rot; 31 | end 32 | else begin 33 | x_out <= x_in - (y_in >>> pipline_level); 34 | y_out <= y_in + (x_in >>> pipline_level); 35 | rot_out <= rot_in - rot; 36 | end 37 | end 38 | end 39 | 40 | 41 | endmodule 42 | -------------------------------------------------------------------------------- /1.RTL/source/cordic_sqrt.v: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DOUDIU/Hardware-Implementation-of-the-Canny-Edge-Detection-Algorithm/e792cb1b6a6244a7d930431b2a35aba6e0d4db8c/1.RTL/source/cordic_sqrt.v -------------------------------------------------------------------------------- /1.RTL/source/fifo_ram.v: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DOUDIU/Hardware-Implementation-of-the-Canny-Edge-Detection-Algorithm/e792cb1b6a6244a7d930431b2a35aba6e0d4db8c/1.RTL/source/fifo_ram.v -------------------------------------------------------------------------------- /1.RTL/source/matrix_generate_3x3.v: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DOUDIU/Hardware-Implementation-of-the-Canny-Edge-Detection-Algorithm/e792cb1b6a6244a7d930431b2a35aba6e0d4db8c/1.RTL/source/matrix_generate_3x3.v -------------------------------------------------------------------------------- /1.RTL/source/one_column_ram.v: -------------------------------------------------------------------------------- 1 | module one_column_ram#( 2 | parameter DATA_WIDTH = 8, 3 | parameter DATA_DEPTH = 640 4 | )( 5 | input clock, 6 | input clken, 7 | input [DATA_WIDTH - 1 : 0] shiftin, 8 | 9 | output [DATA_WIDTH - 1 : 0] taps0x, 10 | output [DATA_WIDTH - 1 : 0] taps1x, 11 | output [DATA_WIDTH - 1 : 0] taps2x 12 | ); 13 | 14 | //***************************************************** 15 | //** main code 16 | //***************************************************** 17 | wire [DATA_WIDTH - 1 : 0] fifo_rd_data0; 18 | reg [DATA_WIDTH - 1 : 0] fifo_rd_data0_d1; 19 | wire [DATA_WIDTH - 1 : 0] fifo_rd_data1; 20 | reg clken_d1; 21 | reg clken_d2; 22 | reg [DATA_WIDTH - 1 : 0] shiftin_d1; 23 | reg [DATA_WIDTH - 1 : 0] shiftin_d2; 24 | 25 | always@(posedge clock)begin 26 | clken_d1 <= clken; 27 | clken_d2 <= clken_d1; 28 | shiftin_d1 <= shiftin; 29 | shiftin_d2 <= shiftin_d1; 30 | fifo_rd_data0_d1 <= fifo_rd_data0; 31 | end 32 | 33 | fifo_ram#( 34 | .DATA_WIDTH(DATA_WIDTH), 35 | .DATA_DEPTH(DATA_DEPTH) 36 | ) 37 | u_fifo_ram0( 38 | .clk (clock), 39 | //write port 40 | .wr_en (clken_d2), 41 | .wr_data (shiftin_d2), 42 | .wr_full (), 43 | //read port 44 | .rd_en (clken), 45 | .rd_data (fifo_rd_data0), 46 | .rd_empty () 47 | ); 48 | 49 | fifo_ram#( 50 | .DATA_WIDTH(DATA_WIDTH), 51 | .DATA_DEPTH(DATA_DEPTH) 52 | ) 53 | u_fifo_ram1( 54 | .clk (clock), 55 | //write port 56 | .wr_en (clken_d2), 57 | .wr_data (fifo_rd_data0_d1), 58 | .wr_full (), 59 | //read port 60 | .rd_en (clken), 61 | .rd_data (fifo_rd_data1), 62 | .rd_empty () 63 | ); 64 | 65 | assign taps0x = shiftin_d1; 66 | assign taps1x = fifo_rd_data0; 67 | assign taps2x = fifo_rd_data1; 68 | 69 | endmodule -------------------------------------------------------------------------------- /1.RTL/source/vip_gaussian_filter.v: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DOUDIU/Hardware-Implementation-of-the-Canny-Edge-Detection-Algorithm/e792cb1b6a6244a7d930431b2a35aba6e0d4db8c/1.RTL/source/vip_gaussian_filter.v -------------------------------------------------------------------------------- /2.C++_Model/canny.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #define M_PI 3.14159265358979323846 7 | 8 | using namespace cv; 9 | void gaussianConvolution(Mat& img, Mat& dst); 10 | void gaussianFilter(Mat& img, Mat& dst); 11 | void getGrandient(Mat& img, Mat& gradXY, Mat& theta); 12 | void nonLocalMaxValue(Mat& gradXY, Mat& theta, Mat& dst); 13 | void doubleThresholdLink(Mat& img); 14 | void doubleThreshold(double low, double high, Mat& img, Mat& dst); 15 | int main(int argc, char** argv) { 16 | Mat src; 17 | src = imread("../5.Pic/monkey.bmp"); 18 | if (!src.data) { 19 | printf("could not load image...\n"); 20 | return -1; 21 | } 22 | namedWindow("input image", CV_WINDOW_AUTOSIZE); 23 | imshow("input image", src); 24 | 25 | Mat gray_src; 26 | cvtColor(src, gray_src, CV_BGR2GRAY); 27 | imshow("gray image", gray_src); 28 | 29 | Mat gray_binary; 30 | threshold(gray_src, gray_binary, 125, 255, cv::THRESH_BINARY_INV); 31 | imshow("binary image", gray_binary); 32 | 33 | Mat gaussian_gray_src; 34 | gaussianFilter(gray_src, gaussian_gray_src); 35 | imshow("gaussian_gray_src image", gaussian_gray_src); 36 | 37 | Mat gradXY_src; 38 | Mat theta_src; 39 | getGrandient(gaussian_gray_src, gradXY_src, theta_src); 40 | imshow("gradXY_src image", gradXY_src); 41 | 42 | Mat nonLocalMaxValue_src; 43 | nonLocalMaxValue(gradXY_src, theta_src, nonLocalMaxValue_src); 44 | imshow("nonLocalMaxValue_src image", nonLocalMaxValue_src); 45 | 46 | Mat doubleThreshold_src; 47 | doubleThreshold(30, 60, nonLocalMaxValue_src, doubleThreshold_src); 48 | imshow("doubleThreshold_src image", doubleThreshold_src); 49 | 50 | waitKey(0); 51 | return 0; 52 | } 53 | 54 | void doubleThreshold(double low, double high, Mat& img, Mat& dst) { 55 | dst = img.clone(); 56 | 57 | // 区分出弱边缘点和强边缘点 58 | for (int j = 0; j < img.rows - 1; j++) { 59 | for (int i = 0; i < img.cols - 1; i++) { 60 | double x = double(dst.ptr(j)[i]); 61 | // 像素点为强边缘点,置255 62 | if (x > high) { 63 | dst.ptr(j)[i] = 255; 64 | } 65 | // 像素点置0,被抑制掉 66 | else if (x < low) { 67 | dst.ptr(j)[i] = 0; 68 | } 69 | } 70 | } 71 | 72 | // 弱边缘点补充连接强边缘点 73 | doubleThresholdLink(dst); 74 | } 75 | 76 | void doubleThresholdLink(Mat& img) { 77 | // 循环找到强边缘点,把其领域内的弱边缘点变为强边缘点 78 | for (int j = 1; j < img.rows - 2; j++) { 79 | for (int i = 1; i < img.cols - 2; i++) { 80 | // 如果该点是强边缘点 81 | if (img.ptr(j)[i] == 255) { 82 | // 遍历该强边缘点领域 83 | for (int m = -1; m < 1; m++) { 84 | for (int n = -1; n < 1; n++) { 85 | // 该点为弱边缘点(不是强边缘点,也不是被抑制的0点) 86 | if (img.ptr(j + m)[i + n] != 0 && img.ptr(j + m)[i + n] != 255) { 87 | img.ptr(j + m)[i + n] = 255; //该弱边缘点补充为强边缘点 88 | } 89 | } 90 | } 91 | } 92 | } 93 | } 94 | 95 | for (int j = 0; j < img.rows - 1; j++) { 96 | for (int i = 0; i < img.cols - 1; i++) { 97 | // 如果该点依旧是弱边缘点,及此点是孤立边缘点 98 | if (img.ptr(j)[i] != 255 && img.ptr(j)[i] != 0) { 99 | img.ptr(j)[i] = 0; //该孤立弱边缘点抑制 100 | } 101 | } 102 | } 103 | } 104 | 105 | void nonLocalMaxValue(Mat& gradXY, Mat& theta, Mat& dst) { 106 | dst = gradXY.clone(); 107 | for (int j = 1; j < gradXY.rows - 1; j++) { 108 | for (int i = 1; i < gradXY.cols - 1; i++) { 109 | double t = double(theta.ptr(j)[i]); 110 | double g = double(dst.ptr(j)[i]); 111 | if (g == 0.0) { 112 | continue; 113 | } 114 | double g0, g1; 115 | if ((t >= -(3 * M_PI / 8)) && (t < -(M_PI / 8))) { 116 | g0 = double(dst.ptr(j - 1)[i - 1]); 117 | g1 = double(dst.ptr(j + 1)[i + 1]); 118 | } 119 | else if ((t >= -(M_PI / 8)) && (t < M_PI / 8)) { 120 | g0 = double(dst.ptr(j)[i - 1]); 121 | g1 = double(dst.ptr(j)[i + 1]); 122 | } 123 | else if ((t >= M_PI / 8) && (t < 3 * M_PI / 8)) { 124 | g0 = double(dst.ptr(j - 1)[i + 1]); 125 | g1 = double(dst.ptr(j + 1)[i - 1]); 126 | } 127 | else { 128 | g0 = double(dst.ptr(j - 1)[i]); 129 | g1 = double(dst.ptr(j + 1)[i]); 130 | } 131 | 132 | if (g <= g0 || g <= g1) { 133 | dst.ptr(j)[i] = 0.0; 134 | } 135 | } 136 | } 137 | } 138 | 139 | void getGrandient(Mat& img, Mat& gradXY, Mat& theta) { 140 | gradXY = Mat::zeros(img.size(), CV_8U); 141 | theta = Mat::zeros(img.size(), CV_8U); 142 | 143 | for (int j = 1; j < img.rows - 1; j++) { 144 | for (int i = 1; i < img.cols - 1; i++) { 145 | double gradY = double(img.ptr(j - 1)[i - 1] + 2 * img.ptr(j - 1)[i] + img.ptr(j - 1)[i + 1] \ 146 | - img.ptr(j + 1)[i - 1] - 2 * img.ptr(j + 1)[i] - img.ptr(j + 1)[i + 1]); 147 | double gradX = double(img.ptr(j - 1)[i + 1] + 2 * img.ptr(j)[i + 1] + img.ptr(j + 1)[i + 1] \ 148 | - img.ptr(j - 1)[i - 1] - 2 * img.ptr(j)[i - 1] - img.ptr(j + 1)[i - 1]); 149 | 150 | gradXY.ptr(j)[i] = sqrt(gradX * gradX + gradY * gradY); //计算梯度 151 | theta.ptr(j)[i] = atan(gradY / gradX); //计算梯度方向 152 | } 153 | } 154 | } 155 | 156 | 157 | //void getGrandient(Mat& img, Mat& gradXY, Mat& theta) { 158 | // gradXY = Mat::zeros(img.size(), CV_8U); 159 | // theta = Mat::zeros(img.size(), CV_8U); 160 | // 161 | // for (int i = 0; i < img.rows - 1; i++) { 162 | // for (int j = 0; j < img.cols - 1; j++) { 163 | // double p = (img.ptr(j)[i + 1] - img.ptr(j)[i] + img.ptr(j + 1)[i + 1] - img.ptr(j + 1)[i]) / 2; 164 | // double q = (img.ptr(j + 1)[i] - img.ptr(j)[i] + img.ptr(j + 1)[i + 1] - img.ptr(j)[i + 1]) / 2; 165 | // gradXY.ptr(j)[i] = sqrt(p * p + q * q); //计算梯度 166 | // theta.ptr(j)[i] = atan(q / p); 167 | // } 168 | // } 169 | //} 170 | 171 | void gaussianFilter(Mat& img, Mat& dst) { 172 | // 对水平方向进行滤波 173 | Mat dst1 = img.clone(); 174 | gaussianConvolution(img, dst1); 175 | //图像矩阵转置 176 | Mat dst2; 177 | transpose(dst1, dst2); 178 | // 对垂直方向进行滤波 179 | Mat dst3 = dst2.clone(); 180 | gaussianConvolution(dst2, dst3); 181 | // 再次转置 182 | transpose(dst3, dst); 183 | } 184 | 185 | void gaussianConvolution(Mat& img, Mat& dst) 186 | { 187 | int nr = img.rows; 188 | int nc = img.cols; 189 | int templates[3] = { 1, 2, 1 }; 190 | 191 | // 按行遍历除每行边缘点的所有点 192 | for (int j = 0; j < nr; j++) 193 | { 194 | uchar* data = img.ptr(j); //提取该行地址 195 | for (int i = 1; i < nc - 1; i++) 196 | { 197 | int sum = 0; 198 | for (int n = 0; n < 3; n++) 199 | { 200 | sum += data[i - 1 + n] * templates[n]; //相称累加 201 | } 202 | sum /= 4; 203 | dst.ptr(j)[i] = sum; 204 | } 205 | } 206 | } 207 | -------------------------------------------------------------------------------- /3.Vivado_Project/CannyEdgeDetection.xpr: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 175 | 176 | 177 | 178 | 179 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 229 | 230 | 231 | 232 | 233 | 235 | 236 | 237 | 238 | 239 | 242 | 243 | 245 | 246 | 248 | 249 | 251 | 252 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | default_dashboard 311 | 312 | 313 | 314 | -------------------------------------------------------------------------------- /3.Vivado_Project/image_proces_tb_behav.wcfg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | cmos_clk 25 | cmos_clk 26 | 27 | 28 | cmos_rst_n 29 | cmos_rst_n 30 | 31 | 32 | cmos_vsync 33 | cmos_vsync 34 | 35 | 36 | cmos_href 37 | cmos_href 38 | 39 | 40 | cmos_clken 41 | cmos_clken 42 | 43 | 44 | cmos_data[23:0] 45 | cmos_data[23:0] 46 | 47 | 48 | post0_vsync 49 | post0_vsync 50 | 51 | 52 | post0_href 53 | post0_href 54 | 55 | 56 | post0_clken 57 | post0_clken 58 | 59 | 60 | post0_img_Y[7:0] 61 | post0_img_Y[7:0] 62 | 63 | 64 | post0_img_Cb[7:0] 65 | post0_img_Cb[7:0] 66 | 67 | 68 | post0_img_Cr[7:0] 69 | post0_img_Cr[7:0] 70 | 71 | 72 | gauss_vsync 73 | gauss_vsync 74 | 75 | 76 | gauss_hsync 77 | gauss_hsync 78 | 79 | 80 | gauss_de 81 | gauss_de 82 | 83 | 84 | img_gauss[7:0] 85 | img_gauss[7:0] 86 | 87 | 88 | canny_vsync 89 | canny_vsync 90 | 91 | 92 | canny_hsync 93 | canny_hsync 94 | 95 | 96 | canny_de 97 | canny_de 98 | 99 | 100 | canny_data 101 | canny_data 102 | 103 | 104 | img_canny 105 | img_canny 106 | 107 | 108 | -------------------------------------------------------------------------------- /4.QuestaSim/readme.md: -------------------------------------------------------------------------------- 1 | # build your modelsim project here! -------------------------------------------------------------------------------- /5.Pic/monkey.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DOUDIU/Hardware-Implementation-of-the-Canny-Edge-Detection-Algorithm/e792cb1b6a6244a7d930431b2a35aba6e0d4db8c/5.Pic/monkey.bmp -------------------------------------------------------------------------------- /5.Pic/outcom.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DOUDIU/Hardware-Implementation-of-the-Canny-Edge-Detection-Algorithm/e792cb1b6a6244a7d930431b2a35aba6e0d4db8c/5.Pic/outcom.bmp -------------------------------------------------------------------------------- /5.Pic/readme_pic/result.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DOUDIU/Hardware-Implementation-of-the-Canny-Edge-Detection-Algorithm/e792cb1b6a6244a7d930431b2a35aba6e0d4db8c/5.Pic/readme_pic/result.png -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Canny Edge Detection Based on FPGA 2 | 3 | ![result](5.Pic/readme_pic/result.png) 4 | 5 | **走过路过,不要忘记点赞关注订阅哟!** 6 | 7 | ### Introduction: 8 | 9 | ​ 这个工程是基于FPGA的Canny边缘检测系统的实现,并未使用一些IP,其中应用了cordic算法求梯度强度与角度,可通过选择流水等级实现高精准度,卷积窗利用FIFO实现,所有代码由Verilog编写,将很便利的移植到各种开发平台上,如Altera与Xilinx的各类型号fpga。 10 | 11 | ​ 有完整C++实现代码,PPT里面有算法具体实现的思路,如有疑惑欢迎提交补充。 12 | 13 | ### Folder Structure: 14 | 15 | ​ 其中***RTL工程***文件如下: 16 | 17 | > | Folder | Function | 18 | > | :-------------------------------: | :----------------------------------------------------------: | 19 | > | ***pic*** | #存放着用来仿真的图像 | 20 | > | ***QuestaSim*** | #存放Modelsim/QuestaSim的工程(为空,需自己创建工程至此目录) | 21 | > | ***RTL*** | #存放着所有源代码及仿真代码 | 22 | 23 | **注**:请把modelsim工程创建到Questasim文件夹,添加RTL文件下所有源文件,选择顶层***haze_removal_tb.sv***进行仿真,工程需仿真***14ms***才能出现结果。 24 | 25 | ​ 其中***验证工程***如下: 26 | 27 | > | Folder | Function | 28 | > | :-------------------------------: | :------------------------------------------: | 29 | > | ***C++*** | #基于opencv验证的canny边缘检测算法的验证方案 | 30 | 31 | ​ ***PPT演示文档***如下: 32 | 33 | https://docs.google.com/presentation/d/1ywzYQMz7mvWQlFPfZwrzZkoY8leG42afM7pXLFJ5wuY/edit?usp=sharing 34 | 35 | 如果有问题欢迎提交,如果有帮助的话,不要忘记点赞关注哦! 36 | 37 | ### Tips: 38 | 39 | ​ 本工程中的仿真文件中读取bmp与保存bmp用的读取函数都是使用***相对路径***,如果不想自己重新切换下路径的话,上述三个文件夹及Vivado工程创建路径需与此教程一致。 40 | 41 | ​ 本工程可以使用***Modelsim Simulator***或者***Vivado Simulator***进行仿真,仅需在仿真顶层**canny_tb.sv**的4-5两行注释另一仿真器代码,即可实现两个仿真器任意运行。但Vivado仿真比较慢,推荐使用Modelsim仿真。 42 | 43 | ​ 要进行处理的图片需经过win10自带的3D绘图保存为640x480大小的bmp,可以查看大小是否为900KB来验证图片是否可以应用仿真。(可以自己算一下,每个真彩色像素是3个字节,bmp的帧头是54个字节,按照640x480算应为640x480x3+54=900KB) 44 | 45 | **走过路过,不要忘记点赞关注订阅哟!** 46 | --------------------------------------------------------------------------------