2018-10-16 08:27:01 qq_33810188 阅读数 371
  • 深度学习-Pytorch项目实战-垃圾分类

    【课程介绍】       Pytorch项目实战 垃圾分类 课程从实战的角度出发,基于真实数据集与实际业务需求,结合当下最新话题-垃圾分类问题为实际业务出发点,介绍最前沿的深度学习解决方案。     从0到1讲解如何场景业务分析、进行数据处理,模型训练与调优,最后进行测试与结果展示分析。全程实战操作,以最接地气的方式详解每一步流程与解决方案。     课程结合当下深度学习热门领域,尤其是基于facebook 开源分类神器ResNext101网络架构,对网络架构进行调整,以计算机视觉为核心讲解各大网络的应用于实战方法,适合快速入门与进阶提升。 【课程要求】 (1)开发环境:python版本:Python3.7+; torch 版本:1.2.0+; torchvision版本:0.4.0+ (2)开发工具:Pycharm; (3)学员基础:需要一定的Python基础,及深度学习基础; (4)学员收货:掌握最新科技图像分类关键技术; (5)学员资料:内含完整程序源码和数据集; (6)课程亮点:专题技术,完整案例,全程实战操作,徒手撸代码 【课程特色】 阵容强大 讲师一直从事与一线项目开发,高级算法专家,一直从事于图像、NLP、个性化推荐系统热门技术领域。 仅跟前沿 基于当前热门讨论话题:垃圾分类,课程采用学术届和工业届最新前沿技术知识要点。 实战为先 根据实际深度学习工业场景-垃圾分类,从产品需求、产品设计和方案设计、产品技术功能实现、模型上线部署。精心设计工业实战项目 保障效果 项目实战方向包含了学术届和工业届最前沿技术要点 项目包装简历优化 课程内垃圾分类图像实战项目完成后可以直接优化到简历中 【课程思维导图】 【课程实战案例】

    507 人正在学习 去看看 沈福利

· 能从硬件结构方面消除不利影响,最好不要从图像算法方面消除不利影响(预防隐患法则)

· 截至目前为止,图像算法没有一个标准的算法流程适合所有的图像处理,所以具体问题具体分析

· 图像算法处理的实际应用遵循海森堡不确定性原则,所以在实际应用中不断优化算法十分必要

· 图像算法处理的实际应用符合墨菲定律,所以图像算法前期的设计隐患一定会在实际应用中产生

· 图像处理算法一定是定量分析算法,模糊不定的处理方式必然会导致问题

· 图像处理问题归根结底是一个数学问题,所以扎实的数学功底必不可少!

---------------------------------------------------------------------------------------------------------------

1.图像算法的设计一定要考虑数据结构类型,不友好的数据结构会使后续的代码结构十分繁琐冗余!

2.无论函数名,还是参数名,在取名的时候千万避免名称相似(例如,如果有一个camsPixels,不可再取一个camPixels,否则在查看代码时,直观上很容易分辨错误!浪费解读代码时间!);

3.不要重复命名函数,否则哪天封装多个带有重复名称的函数时,会出现命名冲突!

4.尽量不要重复造轮子,能够借鉴的代码要借鉴并优化,不断学习别人的代码风格和代码逻辑;

5.一位优秀的算法工程师,也必须是一位不错的软件工程师!

6.

 

 

2017-07-11 21:59:33 loujiong 阅读数 1609
  • 深度学习-Pytorch项目实战-垃圾分类

    【课程介绍】       Pytorch项目实战 垃圾分类 课程从实战的角度出发,基于真实数据集与实际业务需求,结合当下最新话题-垃圾分类问题为实际业务出发点,介绍最前沿的深度学习解决方案。     从0到1讲解如何场景业务分析、进行数据处理,模型训练与调优,最后进行测试与结果展示分析。全程实战操作,以最接地气的方式详解每一步流程与解决方案。     课程结合当下深度学习热门领域,尤其是基于facebook 开源分类神器ResNext101网络架构,对网络架构进行调整,以计算机视觉为核心讲解各大网络的应用于实战方法,适合快速入门与进阶提升。 【课程要求】 (1)开发环境:python版本:Python3.7+; torch 版本:1.2.0+; torchvision版本:0.4.0+ (2)开发工具:Pycharm; (3)学员基础:需要一定的Python基础,及深度学习基础; (4)学员收货:掌握最新科技图像分类关键技术; (5)学员资料:内含完整程序源码和数据集; (6)课程亮点:专题技术,完整案例,全程实战操作,徒手撸代码 【课程特色】 阵容强大 讲师一直从事与一线项目开发,高级算法专家,一直从事于图像、NLP、个性化推荐系统热门技术领域。 仅跟前沿 基于当前热门讨论话题:垃圾分类,课程采用学术届和工业届最新前沿技术知识要点。 实战为先 根据实际深度学习工业场景-垃圾分类,从产品需求、产品设计和方案设计、产品技术功能实现、模型上线部署。精心设计工业实战项目 保障效果 项目实战方向包含了学术届和工业届最前沿技术要点 项目包装简历优化 课程内垃圾分类图像实战项目完成后可以直接优化到简历中 【课程思维导图】 【课程实战案例】

    507 人正在学习 去看看 沈福利

1.图像边缘检测算法的简介:

1.1 在本学期的实践中用FPGA实现了图像的边缘检测算法,边缘检测是分析视频图像的重要手段之一, 并且应用领域广泛。
1.2 在现在的图像处理研究中是非常重要的基础研究,在安全监控,计算机图形处理,机器识别等领域越来越成为研究的重点。
1.3 边缘检测是确定一幅图像在哪些区域上亮度发生突变。 这些亮度突变的区域通常就是物体的边缘。

2. 设计的具体实现要求:

2.1 图像边沿滤波器,执行sobel算法。
2.2 原始图像亮度保存在MEM中。
2.3 启动算法后,滤波器将亮度图像转变为导数图像,保存在MEM中。
2.4 导数的计算避免开方根,采用偏导数绝对值之和近似计算。

边缘检测算法的数学公式推导过程:

分辨率为 MxN 的图像是由 MxN 个像素组成。对于灰度图像,每个像素点一般由 8比特来表示该像素的亮度值。我们以灰度图像为例,假设图像按行存储在内存中,并且每行里从左到右连续的像素点占据着内存中连续的存贮单元。像素值是无符号整数,范围从 0(黑色)到 255(白色) 。这里,我们采用一种相对简单的算法来完成图像边缘检测,叫做Sobel 边缘检测法。它的机理是计算 x 和 y 方向亮度信号的导数值并且寻找导数中的最大值和最小值。这些区域就是亮度变化最剧烈的区域,即图像边缘。Sobel 检测法通过一个叫做卷积的过程来估计每个像素点每个方向上的导数值。把中心像素点和离它最近的八个像素点每个乘以一个系数后相加。该系数通常用一个卷积表(convolution mask) 来表示。 分别用于计算 x 和 y 方向导数值的 Sobel 卷积表 Gx 和 Gy 如下所示。
-1 0 +1
-2 0 +2
-1 0 +1
Gx
+1 +2 +1
0 0 0
-1 -2 -1
Gy
我们把每个像素值分别乘以卷积表中对应的系数, 再把相乘得到的九个数相加就得到了x 方向和 y 方向的偏导数值 Dx 和 Dy。 然后, 利用这两个偏导数值计算中心像素点的导数。
计算公式如下:
这里写图片描述
由于我们只想找到导数幅值的最大值和最小值,对上式作如下简化:
这里写图片描述
这样近似能够满足计算要求, 因为开平方和平方函数都是单调的, 实际计算幅度的最大值、最小值与近似以后计算的最大值、最小值发生在图像的同一个地方。并且,与计算平方和开平方相比,计算绝对值所用的硬件资源少得多。我们需要重复地计算图像中每个像素位置的导数幅值。 但是, 注意到环绕图像边缘的像素点并没有一个完整的相邻像素组来计算偏导数和导数, 所以我们需要对这些像素进行单独处理。最简单的方法就是把图像中边缘像素点的导数值值 |D|设置为 0。

3. world技术文档如下

3.1 顶层设计

这里写图片描述

3.2顶层架构

这里写图片描述

3.3 sobel计算器的架构

这里写图片描述

3.4具体节拍分析如下图(流水线方式)

这里写图片描述

这里写图片描述

这里写图片描述

这里写图片描述

4. 根据设计的技术文档设计verilog代码

4.1 首先例化存储器模块(MEM)

存储器模块, 只需当外部电路产生既定读写时序时, 其能将数据送往MEM进行数据的存储。存储器读写时序与 Sobel 从机接口读写
时序一致。 此外, 存储器模型中, 在初始化阶段便将一幅原始图像的像素值导入到存储器中,模拟摄像头图像采集功能; 而当原始图像完成边缘检测后, 我们没有将导数像素值真正地存入存储器中,而是将其直接存为文件,以供验证

module memory(clk, rst_n, data_out, data_in, addr, write, read);

    input clk, rst_n;
    input [31:0] data_out;
    output reg [31:0] data_in;
    input [21:0] addr;
    input write, read;

    reg [7:0] mem [22'h3fffff:0];
    integer DATAFILE;

    initial begin
        $readmemh("bmp1.txt", mem);
    end

    initial begin
        DATAFILE = $fopen("post1.txt");
    end

    always @ (posedge clk)
    begin
        if(write)
            $fdisplay(DATAFILE, "", data_out[31:24], data_out[23:16], data_out[15:8], data_out[7:0]);
        else if(read)
            data_in <= {mem[addr], mem[addr+1], mem[addr+2], mem[addr+3]};
    end

endmodule 

4.2实现检测算法的顶层模块verilog代码

根据技术文档,分别由 地址模块(addr_gen) , (计算模块)computer ,(控制器模块) sfz_fsm 构成,代码如下:

module sobel_filter_zx1704(clk, rst_n, src_addr, dest_addr, start, done, data_out, data_in, addr, write, read);

    parameter WIDTH = 600;
    parameter HIGH = 400;

    input clk, rst_n;
    input [21:0] src_addr, dest_addr;
    input start;
    output done;
    output [31:0] data_out;
    input [31:0] data_in;
    output [21:0] addr;
    output write, read;

    wire pr_send, cr_send, nr_send, dr_send, pr_load, cr_load, nr_load, shift_en, set_zero, row_buf_load;

    addr_gen 
    #(.WIDTH(WIDTH), .HIGH(HIGH))
    AG(
        .clk(clk), 
        .rst_n(rst_n), 
        .src_addr(src_addr), 
        .dest_addr(dest_addr), 
        .pr_send(pr_send), 
        .cr_send(cr_send), 
        .nr_send(nr_send), 
        .dr_send(dr_send), 
        .addr(addr),
        .start(start)
    );

    computer COM(
        .clk(clk), 
        .rst_n(rst_n), 
        .data_in(data_in), 
        .pr_load(pr_load), 
        .cr_load(cr_load), 
        .nr_load(nr_load), 
        .shift_en(shift_en), 
        .data_out(data_out),
        .row_buf_load(row_buf_load),
        .set_zero(set_zero)
    );

    sfz_fsm 
    #(.WIDTH(WIDTH), .HIGH(HIGH))
    SFSM(
        .clk(clk), 
        .rst_n(rst_n), 
        .start(start), 
        .done(done), 
        .write(write), 
        .read(read), 
        .pr_send(pr_send), 
        .cr_send(cr_send), 
        .nr_send(nr_send), 
        .dr_send(dr_send), 
        .pr_load(pr_load), 
        .cr_load(cr_load), 
        .nr_load(nr_load), 
        .shift_en(shift_en),
        .row_buf_load(row_buf_load),
        .set_zero(set_zero)     
    );

endmodule

4.3 地址模块(addr_gen)

地址模块主要作用是对(MEM)中的地址进行控制
这是一个简单的有限自动机。可以直接写代码:
1.1 start后,AG必须捕获src_addr和dest_addr至内部寄存器(例如内部22位的src_pr, src_cr, src_nr和dest)
1.2 因此,start后,必须将src_addr装到src_pr
1.3 将src_addr+WIDTH装到src_cr
1.4 将src_addr+2*WIDTH装到src_nr
1.5 将dest_addr装到dest

1.6 之后,在沿敏感中用if语句,检测pr_send为真,则将src_pr装配到addr,并且src_pr加4
1.7 之后,用else if语句,检测cr_send为真,则将src_cr装配到addr,并且src_cr加4
1.8 之后,用else if语句,检测nr_send为真,则将src_nr装配到addr,并且src_nr加4
1.9 之后,用else if语句,检测dr_send为真,则将dest装配到addr,并且dest加4

module addr_gen(clk, rst_n, src_addr, dest_addr, pr_send, cr_send, nr_send, dr_send, start, addr);

    parameter WIDTH = 600;
    parameter HIGH = 400;

    input clk, rst_n;
    input [21:0] src_addr, dest_addr;
    input pr_send, cr_send, nr_send, dr_send, start;
    output reg [21:0] addr; 

    reg [21:0] src_pr, src_cr, src_nr, dest;

    always @ (posedge clk)
    begin
        if (!rst_n)
            begin
                src_pr <= 0;
                src_cr <= 0;
                src_nr <= 0;
                dest <= 0;
                addr <= 0;
            end
        else if (start)
            begin
                src_pr <= src_addr;
                src_cr <= src_addr + WIDTH;
                src_nr <= src_addr + 2*WIDTH;
                dest <= dest_addr;
            end
        else if (pr_send)
            begin
                addr <= src_pr;
                src_pr <= src_pr + 22'd4;
            end 
        else if (cr_send)
            begin
                addr <= src_cr;
                src_cr <= src_cr + 22'd4;
            end
        else if (nr_send)
            begin
                addr <= src_nr;
                src_nr <= src_nr + 22'd4;
            end 
        else if (dr_send)
            begin
                addr <= dest;
                dest <= dest + 22'd4;
            end 
    end

endmodule

4.4 算法运算模块(computer)

主要负责边缘检测算法的数学运算,关于computer部分偏导数的计算:

  1. 从无符号类型的Z1~Z9,根据公式(见Gonzalez教材260页公式),注意这里Z1至Z9组成的图像卷积模板,其Z1是图像的左上角,Z9是右下角(注意不要平方,使用偏导数的绝对值之和)
  2. 由于有负数参与运算,故这里要用有符号加法器
  3. 因此,模板中系数为+1的部分,就是加上Zi的正数
  4. 模板中系数为-1的部分,就是加上Zi的负数
  5. 模板中系数为+2的部分,就是Zi左移一位后,加上其正数
  6. 模板中系数为-2的部分,就是Zi左移一位后,加上其负数
  7. 有符号加法器的最后和,就是偏导数GX和GY,它们必须定义成11位的有符号数:
    reg signed [10:0] gx, gy;
  8. 绝对值的计算,可以这样:
    reg signed [10:0] abs_gx, abs_gy;

    if (gx > 0)
    abs_gx = gx;
    else
    abs_gx = -gx;
module computer(clk, rst_n, data_in, pr_load, cr_load, nr_load, shift_en, data_out, row_buf_load, set_zero);

    input clk, rst_n;
    input row_buf_load, set_zero;
    input [31:0] data_in;
    input pr_load, cr_load, nr_load, shift_en;
    output [31:0] data_out;

    reg [31:0] pr, cr, nr;
    reg [31:0] prb, crb, nrb;
    reg [7:0] z1, z2, z3, z4, z5, z6, z7, z8, z9;
    reg signed [10:0] dx, dy;
    reg [7:0] abs;
    reg [47:0] res_reg;

    always @(posedge clk)
    begin : PR
        if(!rst_n)
            pr <= 0;
        else if(pr_load)
            pr <= data_in;
    end

    always @(posedge clk)
    begin : CR
        if(!rst_n)
            cr <= 0;
        else if(cr_load)
            cr <= data_in;
    end

    always @(posedge clk)
    begin : NR
        if(!rst_n)
            nr <= 0;
        else if(nr_load)
            nr <= data_in;
    end

    always @(posedge clk)
    begin : PRB
        if(row_buf_load)
            prb <= pr;
        else if(shift_en)
            prb[31:8] <= prb[23:0];
    end

    always @(posedge clk)
    begin : CRB
        if(row_buf_load)
            crb <= cr;
        else if(shift_en)
            crb[31:8] <= crb[23:0];
    end

    always @(posedge clk)
    begin : NRB
        if(row_buf_load)
            nrb <= nr;
        else if(shift_en)
            nrb[31:8] <= nrb[23:0];
    end

    always @(posedge clk)
    begin : Z1_Z9
        if(shift_en) begin  
            z3 <= prb[31:24];
            z6 <= crb[31:24];
            z9 <= nrb[31:24];

            z2 <= z3;
            z5 <= z6;
            z8 <= z9;

            z1 <= z2;
            z4 <= z5;
            z7 <= z8;
        end
    end

    always @(posedge clk)
    begin : DXY
        if(shift_en) begin
            dx <= -$signed({3'b000, z1}) + $signed({3'b000, z3}) - ($signed({3'b000, z4})<<1)
                    + ($signed({3'b000, z6})<<1) - $signed({3'b000, z7}) + $signed({3'b000, z9});
            dy <= $signed({3'b000, z1}) + ($signed({3'b000, z2})<<1) + $signed({3'b000, z3})
                    - $signed({3'b000, z7}) - ($signed({3'b000, z8})<<1) - $signed({3'b000, z9});
        end
    end

    function [10:0] absd (input signed [10:0] x);
        absd = (x > 0) ? x : -x;
    endfunction

    always @(posedge clk)
    begin : ABSD
        if(set_zero)
            abs <= 0;
        else if(shift_en)
            abs <= (absd(dx) + absd(dy)) >> 3;
    end

    always @(posedge clk)
    begin : RR  
        if(shift_en) begin
            res_reg[47:8] <= res_reg[39:0];
            res_reg[7:0] <= abs;
        end
    end

    assign data_out = res_reg[47:16];

endmodule

4.5 控制模块(sfz_fsm)

根据状态转移图设计控制模块,控制驱动各个模块的执行顺序和过程

module sfz_fsm(clk, rst_n, start, done, write, read, pr_send, cr_send, nr_send, dr_send, pr_load, cr_load, nr_load, shift_en, row_buf_load,
    set_zero);

    parameter WIDTH = 600;
    parameter HIGH = 400;

    input clk, rst_n;
    input start;
    output reg pr_send, cr_send, nr_send, dr_send, pr_load, cr_load, nr_load, shift_en;
    output reg done, write, read;
    output reg row_buf_load, set_zero;

    reg [1:0] beat;
    reg [7:0] col;
    reg [8:0] row;

    always @ (posedge clk)
    begin : LSM_1S
        if(!rst_n)
            begin
                row <= 0;
                col <= 0;
                beat <= 0;
            end
        else
            casex({row, col, beat})
                {9'd0, 8'd0, 2'd0}  :   if(start) beat <= 1;
                {9'dx, 8'dx, 2'd3}  :   if(row == 398 && col == 3) 
                                                    begin
                                                        beat <= 0;
                                                        col <= 0;
                                                        row <= 0;
                                                    end
                                                else if(col == 149)
                                                    begin
                                                        beat <= 0;
                                                        col <= 0;
                                                        row <= row + 1;
                                                    end
                                                else
                                                    begin
                                                        beat <= 0;
                                                        col <= col + 1;
                                                    end
                default :   beat <= beat + 1;
            endcase
    end

    always @ (posedge clk)
    begin : LSM_2S
        if(!rst_n)
            task_reset;
        else if(row == 0 && col == 0)
            task_r0c0;
        else if(row == 0 && col == 1)
            task_r0c1;
        else if(row == 0 && col == 2)
            task_r0c2;
        else if(row == 0 && col == 3)
            task_r0c3;
        else if(col == 2)
            task_c2;
        else if(row == 398 && col == 3)
            task_end;
        else
            task_c4;
    end 

    task task_reset;
    begin
        read <= 0;
        write <= 0;
        pr_send <= 0;
        cr_send <= 0;
        nr_send <= 0;
        dr_send <= 0;
        pr_load <= 0;
        cr_load <= 0;
        nr_load <= 0;
        row_buf_load <= 0;
        set_zero <= 0;
        shift_en <= 0;
        done <= 1;
    end
    endtask

    task task_r0c0;
        case(beat)
            0   :   if(start) begin pr_send <= 1;   done <= 0; end
            1   :   begin read <= 1; cr_send <= 1; pr_send <= 0; end
            2   :   begin pr_load <= 1; read <= 1; nr_send <= 1;    cr_send <= 0; end
            3   :   begin cr_load <= 1; read <= 1; pr_load <= 0; nr_send <= 0; end
        endcase
    endtask

    task task_r0c1;
        case(beat)
            0   :   begin pr_send <= 1; nr_load <= 1; cr_load <= 0; read <= 0; end
            1   :   begin read <= 1; cr_send <= 1; row_buf_load <= 1; pr_send <= 0; nr_load <= 0; end
            2   :   begin pr_load <= 1; read <= 1; nr_send <= 1;    cr_send <= 0; row_buf_load <= 0;    shift_en <= 1; end
            3   :   begin cr_load <= 1; read <= 1; pr_load <= 0; nr_send <= 0; end
        endcase
    endtask

    task task_r0c2;
        case(beat)
            0   :   begin pr_send <= 1; nr_load <= 1; cr_load <= 0; read <= 0; end
            1   :   begin read <= 1; cr_send <= 1; row_buf_load <= 1; pr_send <= 0; nr_load <= 0; set_zero <= 1; end
            2   :   begin pr_load <= 1; read <= 1; nr_send <= 1;    cr_send <= 0; row_buf_load <= 0;    set_zero <= 0; end
            3   :   begin cr_load <= 1; read <= 1; pr_load <= 0; nr_send <= 0; end
        endcase
    endtask

    task task_r0c3;
        case(beat)
            0   :   begin pr_send <= 1; nr_load <= 1; cr_load <= 0; read <= 0; end
            1   :   begin read <= 1; cr_send <= 1; row_buf_load <= 1; pr_send <= 0; nr_load <= 0; end
            2   :   begin pr_load <= 1; read <= 1; nr_send <= 1;    cr_send <= 0; row_buf_load <= 0; end
            3   :   begin cr_load <= 1; read <= 1; dr_send <= 1;    pr_load <= 0; nr_send <= 0; end
        endcase
    endtask

    task task_c4;
        case(beat)
            0   :   begin pr_send <= 1; nr_load <= 1; write <= 1; cr_load <= 0; read <= 0; dr_send <= 0; end
            1   :   begin read <= 1; cr_send <= 1; row_buf_load <= 1; pr_send <= 0; nr_load <= 0; write <= 0; end
            2   :   begin pr_load <= 1; read <= 1; nr_send <= 1;    cr_send <= 0; row_buf_load <= 0; end
            3   :   begin cr_load <= 1; read <= 1; dr_send <= 1;    pr_load <= 0; nr_send <= 0; end
        endcase
    endtask

    task task_c2;
        case(beat)
            0   :   begin pr_send <= 1; nr_load <= 1; write <= 1; cr_load <= 0; read <= 0; dr_send <= 0; set_zero <= 1; end
            1   :   begin read <= 1; cr_send <= 1; row_buf_load <= 1; pr_send <= 0; nr_load <= 0; write <= 0;   set_zero <= 1; end
            2   :   begin pr_load <= 1; read <= 1; nr_send <= 1;    cr_send <= 0; row_buf_load <= 0;    set_zero <= 0; end
            3   :   begin cr_load <= 1; read <= 1; dr_send <= 1;    pr_load <= 0; nr_send <= 0; end
        endcase
    endtask

    task task_end;
        case(beat)
            0   :   begin pr_send <= 1; nr_load <= 1; write <= 1; cr_load <= 0; read <= 0; dr_send <= 0; end
            1   :   begin read <= 1; cr_send <= 1; row_buf_load <= 1; pr_send <= 0; nr_load <= 0; write <= 0; end
            2   :   begin pr_load <= 1; read <= 1; nr_send <= 1;    cr_send <= 0; row_buf_load <= 0; end
            3   :   begin cr_load <= 1; read <= 1; dr_send <= 1;    pr_load <= 0; nr_send <= 0; done <= 1; end
        endcase
    endtask

endmodule

4.6 各大模块之下小模块代码

module row_shift(clk, rst_n, data_in, load, shift, shift_num);

    input clk, rst_n;
    input [31:0] data_in;
    input load, shift;
    output reg [7:0] shift_num;

    reg [31:0] shift_temp;

    always @ (posedge clk)
    begin
        if (!rst_n)
            begin
                shift_num <= 0;
                shift_temp <= 0;
            end
        else if (load)
            shift_temp <= data_in;
        else if (shift)
            begin
                shift_num <= shift_temp[31:24];
                shift_temp <= shift_temp << 8;
            end
    end

endmodule
module unit(clk, rst_n, shift_en, num_in, num_out);

    input clk, rst_n;
    input shift_en;
    input [7:0] num_in;
    output reg [7:0] num_out;

    always @ (posedge clk)
    begin
        if (!rst_n)
            num_out <= 0;
        else if (shift_en)
            num_out <= num_in;
    end

endmodule
module dx_dy(clk, rst_n, shift_en, z1, z2, z3, z4, z5, z6, z7, z8, z9, dx, dy);

    input clk, rst_n;
    input shift_en; 
    input [7:0] z1, z2, z3, z4, z5, z6, z7, z8, z9;
    output reg signed [10:0] dx, dy;

    reg signed [10:0] dx_s, dy_s;

    always @ (posedge clk)
    begin
        if (!rst_n)
            begin
                dx_s <= 0;
                dy_s <= 0;
            end
        else if (shift_en)
            begin
                dx_s <= -$signed({3'b000, z1}) + $signed({3'b000, z3}) - ($signed({3'b000, z4}) << 1)
                        + ($signed({3'b000, z6}) << 1) - $signed({3'b000, z7}) + $signed({3'b000, z9});
                dy_s <= -$signed({3'b000, z7}) + $signed({3'b000, z1}) - ($signed({3'b000, z8}) << 1)
                        + ($signed({3'b000, z2}) << 1) - $signed({3'b000, z9}) + $signed({3'b000, z3});
            end
    end

    always @ (*)
    begin
        if (dx_s > 0)
            dx = dx_s;
        else
            dx = -dx_s;
    end

    always @ (*)
    begin
        if (dy_s > 0)
            dy = dy_s;
        else
            dy = -dy_s;
    end

endmodule
module abs_d(clk, rst_n, shift_en, dx, dy, abs);

    input clk, rst_n;
    input shift_en;
    input [10:0] dx, dy;
    output [7:0] abs;

    reg [10:0] abs_t;

    assign abs = abs_t[7:0];

    always @ (posedge clk)
    begin
        if (!rst_n)
            abs_t <= 0;
        else if (shift_en)
            abs_t <= (dx + dy) >> 3;
    end

endmodule
module res_reg(clk, rst_n, shift_en, abs, data_out);

    input clk, rst_n;
    input shift_en;
    input [7:0] abs;
    output reg [31:0] data_out;

    always @ (posedge clk)
    begin
        if (!rst_n)
            data_out <= 0;
        else if (shift_en)
            data_out <= {data_out[23:0], abs};
    end

endmodule

4.7 仿真测试程序如下

`timescale 1ns/1ps

module sobel_filter_zx1704_tb;

    reg clk, rst_n;
    reg start;
    reg [21:0] src_addr, dest_addr;
    wire done, write, read;
    wire [31:0] data_out, data_in;
    wire [21:0] addr;

    sobel_filter_zx1704 DUT(
        .clk(clk), 
        .rst_n(rst_n), 
        .src_addr(src_addr), 
        .dest_addr(dest_addr), 
        .start(start), 
        .done(done), 
        .data_out(data_out), 
        .data_in(data_in), 
        .addr(addr), 
        .write(write), 
        .read(read)
    );

    memory MEM(
        .clk(clk), 
        .rst_n(rst_n), 
        .data_out(data_out), 
        .data_in(data_in), 
        .addr(addr), 
        .write(write), 
        .read(read)
    );

    initial begin
        clk = 1;
        rst_n = 0;
        start = 0;
        src_addr = 0;
        dest_addr = 0;

        #200
        @(posedge clk)
        rst_n = 1;

        #200
        @(posedge clk)
        start = 1;
        dest_addr = 22'h100000;
        src_addr = 0;
        @(posedge clk)
        start = 0;

        #200
        @(posedge done)
        #200
        $stop;     
    end

    always #10 clk = ~clk;

endmodule 

仿真波形:

这里写图片描述

结果分析:

1.根据波形观测数据输入之后,经过程序运算之后,数据输出正常。

2.用一幅图像进行检测如下:

    原始图像

这里写图片描述

    处理过后的图像

这里写图片描述

3.对比两幅图像可知设计的算法基本实现边沿检测的效果,经过推导简化的sobel边缘检测算法实现方法更加简单易行,满足了传统算法最优化的准则,具有较好的精确性和方便使用性的特点。

4.边缘检测对于图像理解,图像识别, 图像分析来说是一个基础性的课题,是图像分割视觉匹配的基础,也是我们实验室研究的领域重要的基础内容,将继续研究算法的优化,提高运行效率。

2014-01-13 21:50:00 xj2419174554 阅读数 5609
  • 深度学习-Pytorch项目实战-垃圾分类

    【课程介绍】       Pytorch项目实战 垃圾分类 课程从实战的角度出发,基于真实数据集与实际业务需求,结合当下最新话题-垃圾分类问题为实际业务出发点,介绍最前沿的深度学习解决方案。     从0到1讲解如何场景业务分析、进行数据处理,模型训练与调优,最后进行测试与结果展示分析。全程实战操作,以最接地气的方式详解每一步流程与解决方案。     课程结合当下深度学习热门领域,尤其是基于facebook 开源分类神器ResNext101网络架构,对网络架构进行调整,以计算机视觉为核心讲解各大网络的应用于实战方法,适合快速入门与进阶提升。 【课程要求】 (1)开发环境:python版本:Python3.7+; torch 版本:1.2.0+; torchvision版本:0.4.0+ (2)开发工具:Pycharm; (3)学员基础:需要一定的Python基础,及深度学习基础; (4)学员收货:掌握最新科技图像分类关键技术; (5)学员资料:内含完整程序源码和数据集; (6)课程亮点:专题技术,完整案例,全程实战操作,徒手撸代码 【课程特色】 阵容强大 讲师一直从事与一线项目开发,高级算法专家,一直从事于图像、NLP、个性化推荐系统热门技术领域。 仅跟前沿 基于当前热门讨论话题:垃圾分类,课程采用学术届和工业届最新前沿技术知识要点。 实战为先 根据实际深度学习工业场景-垃圾分类,从产品需求、产品设计和方案设计、产品技术功能实现、模型上线部署。精心设计工业实战项目 保障效果 项目实战方向包含了学术届和工业届最前沿技术要点 项目包装简历优化 课程内垃圾分类图像实战项目完成后可以直接优化到简历中 【课程思维导图】 【课程实战案例】

    507 人正在学习 去看看 沈福利

原创作品,出自 “晓风残月xj” 博客,欢迎转载,转载时请务必注明出处(http://blog.csdn.net/xiaofengcanyuexj)。

由于各种原因,可能存在诸多不足,欢迎斧正!

1.课程论文题目

去雾。

本次论文我的选题是去雾。由于对《图像处理与分析》这门课很感兴趣,同时也为了锻炼一下自己学习新知识的能力,我没有选择一些容易的常规的图像处理与分析算法,而是选择较为前沿的去雾算法。作为一名计算机专业的学生,我认为仅仅掌握基本的程序设计思想与常规算法是远远不够的,还要培养自己不断学习的能力—学习新知识、学习新思想的能力。这方面很重要的体现就是了解相关科技前沿,尽自己最大努力研读新论文。此外,我认为一个好的课程报告应该有一定的实用价值,或者说写报告的人应该尽可能使自己的选题有实用价值。众所周知,伴随着我国经济的快速发展,一些一味追求GDP的粗制乱造的发展模式所带来的严重后果正在干扰着我们的正常生活方式。我这次报告所要探讨的主题—雾就是其中不可忽视的因素。我这里所说的雾,是广义的雾,包括雾、霾、沙尘、烟等一切导致视觉效果受限的物理现象。由于雾的存在,户外图像质量降低,如果不处理,往往满足不了相关研究、应用的的要求。在雾的影响下,经过物体表面的光被大气中的颗粒物吸收和反射,导致获取的图像质量差,细节模糊、色彩暗淡。通过一定的技术手段,去掉物体表面的雾霭,增强图片效果显得尤为重要。

2.程序设计思路

由于我的程序可处理的图片可以是灰度的、彩色的两种。所以我设计了两种算法实现不同格式图片的处理:直方图灰度分布均衡化去雾算法,基于单幅图像的快速去雾算法。

 

1)、直方图灰度分布均衡化去雾算法

在此我先谈谈我对直方图均衡化的认识。

直方图均衡化(英文名Histogram Equalization),通过使用累积函数对灰度值进行“调整”以实现对比度的增强。具体说来,即把原图像对应的灰度直方图从比较集中的灰度区间通过一定的转换变成在全部灰度范围内的均匀分布。直方图均衡化就是对图像进行非线性拉伸,重新分配图像像素值,使一定灰度范围内的像素数量大致相同。

直方图均衡化的具体编程实现为

    A.统计每个灰度等级对应的像素点数 

    B.计算累计概率

    C.重新映射图像像素点灰度

在这里我首先统计0-255个灰度等级i的像素点数Color[i],然后分别除以图像总的像素点求出每个灰度等级对应的概率PerProbability[i];接着运用积分的思想来计算累计概率AccuProbability[i];然后映射求得新的像素值NewColor[i]

 

 上述坐标图和积分式解释了积分思想求累计概率的正确性。下面举个例子展示整个过程。

a.假设下面使是图像对应的像素点:

 

b.下面是中间处理工程

 

c.经过处理后图像对应的像素点:

 

为什么直方图灰度分布均衡化可以在一定成都上达到去雾的效果呢?这是因为通过使用累积函数对灰度值进行“调整”以实现对比度的增强,一定程度上使暗的地方更暗、亮的地方更亮,从而在视觉上达到去雾的效果。

但是上面的去雾效果仅仅局限于灰度图片,而不能处理更为实用的彩色图像。经过上述直方图处理后,达到均衡化图像的目的,但是图像会在原图基础上失真或称为走样,即整幅图像的颜色发生很大改变,严重偏离去雾的初衷(去雾是在原图基础上除去表面的薄纱从而增强整幅图像的视觉效果)。

 综上,直方图灰度分布均衡化去雾算法可以处理灰色图,但对于彩色图像就会出现走样,达不到去雾的效果。为了解决彩色图像的去雾问题,我又看了一些最新论文,其中包括何凯明博士的《Single Image Haze Removal Using Dark Channel Prior》论文,这是一篇非常深刻的文章,里面公式推导很多,我硬着头皮读完后看不懂,于是又到博客园、CSDN上筛选出几篇相对容易理解和实现的论文,其中我对清华大牛刘倩、陈茂银、周东华的《基于单幅图像的快速去雾算法》论文非常感兴趣,决定以其为突破点尝试彩色图像的去雾算法实现。

 

 

2.基于单幅图像的快速去雾算法

由于基于单幅图像的快速去雾算法涉及的学科非常多、理论性非常强,在此我的设计思路主要是取自清华大牛刘倩、陈茂银、周东华的《基于单幅图像的快速去雾算法》论文以及在编写程序的过程中在一定程度上参考了CSDN博客兰专家laviewpbt博客《一种可实时处理 O(1)复杂度图像去雾算法的实现》,由于所处理图片的格式不同,以及论文、博客里都没提供源码,所以在编写程序的过程中我并无抄袭。在读论文的过程中,真的是充满了对论文撰写者的佩服之情。之前徐老师在课堂上介绍说《图像处理与分析》是门交叉学科,博大精深,那时还愚昧的不以为然,一则是因为所学知识太少,眼界受限,没有了解相关的前言领域;二来是课下编程不够,虽说自己实现了图像几何变换、图像增强等算法,但总的难度很低,而且刚开始接触时不会还能参照先驱的程序。在看了几篇图像处理的前研论文之后,我感觉到自己的愚昧无知。下面就谈谈对我《基于单幅图像的快速去雾算法》论文的一些理解。

在计算机视觉领域,通常运用严密的数学物理方法来描述雾霾等自然现象,《基于单幅图像的快速去雾算法》论文中提出算法的出发点就是基于衰减模型和环境光模型的,并且提出了一个模型表达式:

 

x为空间坐标,F是待恢复的无雾图像,r是大气散射系数,d是景物深度,A是与x无关的全局大气光,H为有雾情况下的景物图像。

这是基于大气理论的光学表达式,由于知识有限,我只能照搬了。

 

      上面是程序的算法流程,下面我将依次谈谈我对每个步骤的理解。

Step 1  读入待处理的图像

论文以及其他关于去雾的程序都是基于较高格式的图像的,普通bmp尤其是268色位图的处理没有涉及,这也是我选择去雾算法实现的一个重要原因—不简单机械地重复别人做过的东西。

 

 Step 2 求空间最小颜色通道

基本色通道(color channel),就是将构成整体图像的颜色信息整理并表现为单色图像的工具。我们知道现实世界中的颜色都是由红、绿、蓝3中颜色组成的。本步骤是要求出所有空间坐标位置对应的最小颜色通道,即Ri,Gi,Bi中最小者,得到新图像M

 

 Step 3 M进行均值滤波

     均值滤波是徐老师在课堂上重点讲过的。均值滤波是典型的线性滤波算法,它是指在图像上对目标像素给一个模板,该模板包括了其周围的临近像素,再用模板中的全体像素的平均值来代替原来像素值。 可见均值滤波的关键是选择模板,然后对应相乘相加相除,这个过程是比较简单。

 

Step 4 M所有像素点求均值Mav

直接把M中的下所有像素相加然后除以总像素数即可。

 

Step 5  求环境光估计值

去雾很大程度上就是要考虑环境光的影响,还远物体的庐山真面目。利用均值滤波求出的环境光具有深度缓变处平滑,深度跳变处随之跳变变的特点。当M的深度发生较大变化时M,Mav也随之发生较大变化,最终体现在L上。

 

Step 6 求出全局大气光

全局大气光也是对图像的重要影响因素。通常取最大灰度值为全局大气光A

 

Step 7 还原图像

     考虑环境光、全局大气光的影响,利用算法开头给出的模型表达式还原图片。

 

   上面就是整个过程。在原论文中,上述都给出较为严格的证明,由于相关专业知识有限,我就不交代推理证明过程了。

    

   

3.数据结构与算法设计

1)、直方图灰度分布均衡化去雾算法

     由于整个处理过程中没有特殊要求的操作,所以开辟二维数组就可解决问题。分别开辟3个二维数组

int *Color=new int[MAXNCOLOR];//Color[i]存储该灰度等级i对应的像素点数,同时为了节省内存空间,在映射后Color[i]则存储原来灰度等级i对应的新灰度等级,这样做可以少申请一个二维数组,由于图像处理很占内存空间,因此优化是必须的。

double *PerProbability=new double[MAXNCOLOR];//存储灰度等级i对应的概率PerProbability[i].

double* AccuProbability=new double[MAXNCOLOR];//存储灰度等级i对应的累加概率AccuProbability[i],此数组可以和上面的PerProbability[]数组合成一个,为了程序的可读性,我开了两个数组,但须明确此处可以优化。

 

此算法原理理解起来可能不是很容易,但实现起来却很简单,无需特殊编程技巧,在此就不给出伪代码,只给出大致流程:统计每个灰度等级对应的像素点数 ,计算累计概率,重新映射屏幕像素点灰度,具体实现见第4部分直方图灰度分布均衡化去雾算法源代码。此算法时间复杂度为O(n),空间复杂度为O(n),n为空间像素点数。

 

2.基于单幅图像的快速去雾算法

     上面的空间最小颜色通道和均值Mav可以在求所有点的颜色通道是一起求出,不必另写循环语句比较求得。由于编程实现较简单,在此就不多说了。

    具体算法步骤见程序设计思路部分,具体源代码见第4部分基于单幅图像的快速去雾算法。

 

4.程序关键代码

、直方图灰度分布均衡化去雾算法源代码
/*************************************************
*功能:直方图灰度分布均衡化去雾算法
**************************************************/
void CMyDIPView::OnMenuitem32790() 
{
	const int MAXNCOLOR=256+10;
	unsigned char *lpSrc;
	CMyDIPDoc* pDoc = GetDocument();
	ASSERT_VALID(pDoc);
	if(pDoc->m_hDIB == NULL)
		return ;
	LPSTR lpDIB = (LPSTR) ::GlobalLock((HGLOBAL) pDoc->m_hDIB);
	LPSTR lpDIBBits=::FindDIBBits (lpDIB);
	int cxDIB = (int) ::DIBWidth(lpDIB);
	int cyDIB = (int) ::DIBHeight(lpDIB);
	int *Color=new int[MAXNCOLOR];
	memset(Color,0,sizeof(Color));
	long lLineBytes = WIDTHBYTES(cxDIB * 8);

	//统计每个灰度等级对应的像素点数
	for(int i = 0; i < cyDIB; i++)
	{
		for(int j = 0; j < cxDIB; j++)
		{
			Color[*(lpDIBBits + lLineBytes * (cyDIB - 1 - i) + j)]++;
		}
	}	

	//计算每个灰度等级对应的概率
	double *PerProbability=new double[MAXNCOLOR];
	double TotalPixelNumber=cxDIB*cyDIB*1.0;
	for(i=0;i<256;i++)
	{
		PerProbability[i]=Color[i]*1.0/TotalPixelNumber;
	}
    //计算每个灰度等级对应的累加概率
	double* AccuProbability=new double[MAXNCOLOR];
	AccuProbability[0]=PerProbability[0];
	Color[0]=(int)(AccuProbability[0]*255.0f+0.5f);
	for(i=1;i<256;i++)
	{
		AccuProbability[i]=AccuProbability[i-1]+PerProbability[i];
		Color[i]=(int)(AccuProbability[i]*255.0f);
	}
	
	//重新映射屏幕像素点灰度
	for(i = 0; i < cyDIB; i++)
	{
		for(int j = 0; j < cxDIB; j++)
		{
			unsigned char perpixel=*(lpDIBBits + lLineBytes * (cyDIB - 1 - i) + j);
			*(lpDIBBits + lLineBytes * (cyDIB - 1 - i) + j)=Color[perpixel];
			
		}
	}
	::GlobalUnlock((HGLOBAL) pDoc->m_hDIB);
	Invalidate(TRUE);
}



.基于单幅图像的快速去雾算法
/*************************************************
*功能:基于单幅图像的快速去雾算法
**************************************************/
void CMyDIPView::OnMenuitem32791() 
{
	unsigned char *lpSrc;
	CMyDIPDoc* pDoc = GetDocument();
	ASSERT_VALID(pDoc);
	if(pDoc->m_hDIB == NULL)
		return ;
	LPSTR lpDIB = (LPSTR) ::GlobalLock((HGLOBAL) pDoc->m_hDIB);
	LPSTR lpDIBBits=::FindDIBBits (lpDIB);
	int cxDIB = (int) ::DIBWidth(lpDIB); 
	int cyDIB = (int) ::DIBHeight(lpDIB);  
	long lLineBytes = WIDTHBYTES(cxDIB * 8); 
	double sumMinColor=0;//所有颜色通道的累加和
	double aveMinColor=0;//所有颜色通道的平均值
	unsigned char minColor;//颜色通道最小值
	unsigned char maxColor=0;//所有颜色通道最大值
	int maxFlag=0;//标记颜色通道最大值权重
	int minFlag=0;//标记颜色通道最小值权重
	unsigned char *mNewPicture=new unsigned char[cxDIB*cyDIB];
	for(int i = 0; i < cyDIB; i++)
	{
		for(int j = 0; j < cxDIB; j++)
		{
			
			unsigned char perpixel=*(lpDIBBits + lLineBytes * (cyDIB - 1 - i) + j);
			unsigned char red=perpixel/32;
			minFlag=32;
            minColor=red;
			if(maxColor<red)
			{
				maxColor=red;
				maxFlag=32;
			}
			perpixel%=32;
			unsigned char green=perpixel/8;
			if(minColor>green)
			{
				minColor=green;	
				minFlag=8;
			}
			if(maxColor<green)
			{
				maxColor=green;
				maxFlag=8;
			}
			
			unsigned char blue=perpixel%8;
			if(minColor>blue)
			{
				minFlag=1;
				minColor=blue;
			}
			if(maxColor<blue)
			{
				maxColor=blue;
				maxFlag=1;
			}
			mNewPicture[cxDIB * (cyDIB - 1 - i) + j]=minColor*minFlag;
			sumMinColor+=minColor*minFlag;
		}
	}
	aveMinColor=sumMinColor/(cxDIB*cyDIB);//颜色通道平均值
	maxColor*=maxFlag;
	double maxAveColor=0;//均值滤波后颜色通道平均值
	double *mAveNewPicture=new double[cxDIB*cyDIB];
	double xishu=1.0/100.0;
	int h=cyDIB,w=cxDIB;
	int tem_w=10,tem_h=10;
	int dw = cxDIB;
	double sum;
    for(i=0;i<h;i++)
    {  
		for(int j=0;j<w;j++)
		{   	
			sum=0;	
			if( j<((tem_w-1)/2) || j>(w-(tem_w+1)/2) || i<((tem_h-1)/2) || i>(h-(tem_h+1)/2) )
				mAveNewPicture[i*dw+j]=mNewPicture[i*dw+j];
			else 
			{ 	
				for(int m=i-((tem_h-1)/2);m<=i+((tem_h-1)/2);m++)
				{
					for(int n=j-((tem_w-1)/2);n<=j+((tem_w-1)/2);n++)
					sum+=mNewPicture[m*dw+n];

				}	
				sum=sum*xishu;sum = fabs(sum);	
				if(sum<0)     sum=0;	
				if(sum>255)sum=255;
				mAveNewPicture[i*dw+j]=sum;
			}
			if(maxAveColor<mAveNewPicture[i*dw+j])
				maxAveColor=mAveNewPicture[i*dw+j];
		}
	} 
	double *lNewPicture=new double[cxDIB*cyDIB];
	for(i = 0; i < cyDIB; i++)
	{
		for(int j = 0; j < cxDIB; j++)
		{
          lNewPicture[i*cxDIB+j]=min(min(aveMinColor*1.28,0.9)*mAveNewPicture[i*cxDIB+j],mNewPicture[i*cxDIB+j]);
		}
	}
	double A=(maxColor+maxAveColor)/2.0;
	for(i = 0; i < cyDIB; i++)
	{
		for(int j = 0; j < cxDIB; j++)
		{
			double H=*(lpDIBBits + lLineBytes * (cyDIB - 1 - i) + j);
	        double L=lNewPicture[cxDIB * (cyDIB - 1 - i) + j];
			*(lpDIBBits + lLineBytes * (cyDIB - 1 - i) + j)=(H-L)/(1-L/A);
		}
	}
	::GlobalUnlock((HGLOBAL) pDoc->m_hDIB);
	Invalidate(TRUE);
}


 

 5.程序运行结果

(2)、直方图灰度分布均衡化去雾算法

图像1

  去雾前

 

 

图像2

   去雾前:

 


图像3

 

 

    对于上面3张灰度图像,直方图灰度分布均衡化达到的去雾效果是不错的,但是对于彩色图像,却无法回避走样问题。下面给出的就是直方图灰度分布均衡化在处理彩色图像时遇到的问题。

图像4

   去雾前

 

   图像4在直方图灰度分布均衡化后景物形状保持了,但色彩却严重偏离了,关于原因,上面已有解释。



(2).基于单幅图像的快速去雾算法

图像5

 

 

图像6

  去雾前

 

 

图像7

  去雾前

 

 

6.编程中遇到的困难及解决方法

1)、选题

    由于徐老师没有要求具体做什么,这个长期习惯老师出题然后自己动手编程的我带来一定干扰。自主选题也是一种自主学习能力,结合自己的兴趣与能力选择适合自己的选题。由于之前做ACM,我认为自己有一定的解决问题能力,于是选择了较为前沿的去雾算法。

  

(2)、查资料

由于本次我的选题较为前沿,所以相关资料不好找,有用的思想、算法等都得通过最近几年的论文获得。之前没有读过多少论文,尤其是像这次这样理论性很强的论文,所以在这方面遇到一定的麻烦。最后,通过CSDN和博客园两个平台,我找的了相关论文和可供参考的博客(在此对大牛们表示感谢)。

 

(3)、关于bmp格式的256色位图的处理

在我看到的相关论文里讨论的都是较高格式的图像,而徐老师给的程序框架是基于bmp格式的256色位图。我首先想到的是修改老师的程序,使之变成具有处理较高格式图片的能力,但由于时间以及没有牢固的MFC编程基础,最终没能动手改。但通过仔细阅读相关论文发现算法是可以处理bmp格式的256色位图的。

 

(4)、直方图灰度分布均衡化去雾算法处理彩色图

开始我打算直接用直方图灰度分布均衡化去雾算法处理彩色图片,但总是达不到预期的效果。图像上景物的轮廓是保持了,但颜色却发生很大的变化,没有达到去雾的效果。在多次修改程序不见改善的情况下我选择了重新看课本,经过一番理论分析,我搞清楚了直接直方图均衡化能增强整幅图像的效果,但不能使彩色图像的黯淡部分在不走样的情况下变亮。当然后来看到博客园上专家Imageshop的

 《限制对比度自适应直方图均衡化算法原理、实现及效果》以及

 《局部自适应自动色阶/对比度算法在图像增强上的应用》文章发现用人工智能中自适应方法也可以使直方图均衡化具备处理彩色图像的能力。但自适应的相关理论对刚开始接触图像处理的我来说有难度,所以我只用直方图均衡化处理灰度图像。

 

(5)、获取bmp格式的256色位图的颜色通道

基于单幅图像的快速去雾算法有一个很重要的一点就是获取原图像空间坐标像素点处的颜色通道中的最小者。关于获取颜色通道,在24位存储格式时可以通过依次访问3个字节获取相应的颜色通,也可以通过GetRValueGetRValueGetRValue等函数直接取出红绿蓝3种颜色通道,等等,但这些都不能处理8位的图像,后来我在CSDN上论坛上问别人,有人说RGB在一个字节中分别占3,2,3个比特位,于是我选用想到位操作或求模两种方法。

 

(6)、均值滤波模板大小的设置

     关于这点,徐老师课堂上是可以结合实际情况自己设定。但当面对具体情况时,模板大小的设定很有技巧。均值滤波可以对图像进行模糊处理、平滑处理,在一定范围了随模板大小的增大而增大,当达到某个峰值之后随模板大小的增大而减小。上述是本次实验经验所得,可能不符合理论,但我的程序结果却支持这种结论。

 

(7)、ρ参数的大小设置

   ρ参数的大小控制了图像去雾能力的大小,越大,雾气越少,图像越显得暗,越小,图像偏白,雾气越浓。所以在合适的范围内参数ρ应该尽可能取得大点,实验中我去的是1.28

 

7.总结心得

     在本次写报告的过程中,我最大的感觉就是知识欠缺。在我目前接触的图像处理算法中,没有特别难编程实现的,大多可以通过基本的编程方法解决。但是,其中的指导思想却是很需要用心体会的。在编程时一定要上升到理论层次,多问几个为什么:为什么这么做,为什么会有这样的效果等等。

    在我看来,图像处理就是一个矩阵变换,变换的是矩阵中对应空间坐标的像素值。而变换过程中要用到很多模板、算子、算法等,这些是进行矩阵变换的依据。在理论工作完成以后,再进行动手编程,这样才能达到真正学习图像处理与分析的目的。有的理论看起来很简单,但却温含着丰富的专业知识,需要我们静下心来慢慢体会,仔细琢磨;有的理论看起来就很难,就像我选的《基于单幅图像的快速去雾算法》这篇论文,从头到尾都体现着丰富的专业知识、严密的逻辑推理能力与深厚的科学内涵,对于这样的理论,更是难得的思想启蒙,我们应该迎难而上,尽可能的读懂它。当然在编程的过程中,我们也要不断优化自己的程序,图像对象的数据量很大,稍不留神算法的时间或空间复杂度就会很高,即便常数级的优化也是很有必要的,能少开辟数组是就少开辟,能少用一层循环就少用一层循环。

有人的地方就会有审美需求,而图像处理就是一种美化过程。图像处理是指对图像信息进行加工,从而满足人类的心理、视觉或者应用需求的思想与技术。常用的图像处理方法有图像的采集与数字化、图像的编码、图像的增强、图像恢复、图像分割、图像分析等。在学习《图像处理与分析》课时要注重理论,多想想为什么;同时也应多动手,获得相应的编程体验。

 

 

 

8、参考文献

1.刘倩、陈茂银、周东华 《基于单幅图像的快速去雾算法》论文

 

2.CSDN博客兰专家laviewpbt博客《一种可实时处理 O(1)复杂度图像去雾算法的实现

 

3.何凯明 《Single Image Haze Removal Using Dark Channel Prior》论文

这是我图像处理与分析结课报告,参考了很多大牛的论文、博客,在此对他们表示感谢!文章和程序中可能有很多缺陷或错误,还请大牛斧正。


    如果觉得对你有所帮助,给几毛资助也是不错的想法!下面是博主的支付宝二维码,如果有一天收到你的资助,将无比欣慰!

                   

2019-02-28 21:14:46 YEYUANGEN 阅读数 414
  • 深度学习-Pytorch项目实战-垃圾分类

    【课程介绍】       Pytorch项目实战 垃圾分类 课程从实战的角度出发,基于真实数据集与实际业务需求,结合当下最新话题-垃圾分类问题为实际业务出发点,介绍最前沿的深度学习解决方案。     从0到1讲解如何场景业务分析、进行数据处理,模型训练与调优,最后进行测试与结果展示分析。全程实战操作,以最接地气的方式详解每一步流程与解决方案。     课程结合当下深度学习热门领域,尤其是基于facebook 开源分类神器ResNext101网络架构,对网络架构进行调整,以计算机视觉为核心讲解各大网络的应用于实战方法,适合快速入门与进阶提升。 【课程要求】 (1)开发环境:python版本:Python3.7+; torch 版本:1.2.0+; torchvision版本:0.4.0+ (2)开发工具:Pycharm; (3)学员基础:需要一定的Python基础,及深度学习基础; (4)学员收货:掌握最新科技图像分类关键技术; (5)学员资料:内含完整程序源码和数据集; (6)课程亮点:专题技术,完整案例,全程实战操作,徒手撸代码 【课程特色】 阵容强大 讲师一直从事与一线项目开发,高级算法专家,一直从事于图像、NLP、个性化推荐系统热门技术领域。 仅跟前沿 基于当前热门讨论话题:垃圾分类,课程采用学术届和工业届最新前沿技术知识要点。 实战为先 根据实际深度学习工业场景-垃圾分类,从产品需求、产品设计和方案设计、产品技术功能实现、模型上线部署。精心设计工业实战项目 保障效果 项目实战方向包含了学术届和工业届最前沿技术要点 项目包装简历优化 课程内垃圾分类图像实战项目完成后可以直接优化到简历中 【课程思维导图】 【课程实战案例】

    507 人正在学习 去看看 沈福利

1.需求分析

  • 实时图像处理技术,尤其是基于多核DSP+FPGA架构的实时图像处理技术,因为该种方案本身所具有种种优点,又成为了现今各项图像处理新技术研究中的备受关注的焦点
  • 红外图像
  • 细胞图像
  • 图像拼接

2.总体设计方案:

FPGA负责完成视频图像采集、显示的逻辑控制;DSP负责完成图像拼接算法的实现;并利用FPGA内部FIFO与DSP的EMIFA接口直接通信,实现FPGA与DSP之间的数据交换。

2.1 DSP与FPGA的通信接口设计

  • 该框架基于数据流模式进行处理器内部的核间数据交互,采用了高速传输接口SRIO与FPGA之间进行通信。
  • 多核DSP与FPGA之间可选用uPP传输模式或者RapidIO高速数据通道进行双工数据通信。
  • PciE

2.2 图像采集接口

  • FPGA读采集图像时所需的标准接口Camera Link协议模块
  • 该系统的数字图像输入前端由SDI数字输入端、USB3.0传输模块、PAL制视频图像采集模块共同构成。

 

2.3 FPGA作为系统的主控制单元(除了做图像高速采集协处理器外,还起到类似ARM的作用?)

  • FPGA对图像进行显示的标准接口HDMI模块
  • FPGA实现PAL制式或DVI制式的视频图像采集,将采集到的图像数据传给DSP,接收DSP回传的结果并显示输出。

 

3.应用例子

  • 基于XILINX V6系列FPGA和TI TMS320C6657双核DSP的实时图像处理系统。系统选用TI多核DSP作为图像数据运算的主处理器,用以实现数字图像实时处理的核心算法。系统选用XILINX公司Virtex系列高端FPGA作为协处理器和系统的主控制单元,用来协调系统中各个外设功能模块,并完成图像采集和显示控制的功能。
  • 为了完成图像目标点的检测和确认,同时实现FPGA与DSP之间的通信数据高速传输,文中基于高速DSP芯片TMS320C6455提出了一种数字图像处理系统设计方案
  • 以TMS320C6657 DSP和XC6VLX240T FPGA为核心芯片,设计了一套基于图像处理的硬件系统平台
  • TI TMS320C6657双核DSP + Artix-7 FPGA
  • 基于视频的车辆异常行为检测系统设计与开发:采用2片ADI公司TigerSHARC系列——ADSP TS201芯片作为核心处理器;两片Xilinx公司的Spartan-6系列FPGA芯片XC6SLX100-3CSG484I,作为通信传输和接口控制的核心处理器
  • 选用TI公司TMS320C6000系列DSP和Altera公司的Cyclone II系列FPGA作为核心器件

关键词:多核DSP; TMS320C6657; 核间通信; 硬件系统平台; 图像匹配;

2015-10-07 00:02:06 ChinaJane163 阅读数 1145
  • 深度学习-Pytorch项目实战-垃圾分类

    【课程介绍】       Pytorch项目实战 垃圾分类 课程从实战的角度出发,基于真实数据集与实际业务需求,结合当下最新话题-垃圾分类问题为实际业务出发点,介绍最前沿的深度学习解决方案。     从0到1讲解如何场景业务分析、进行数据处理,模型训练与调优,最后进行测试与结果展示分析。全程实战操作,以最接地气的方式详解每一步流程与解决方案。     课程结合当下深度学习热门领域,尤其是基于facebook 开源分类神器ResNext101网络架构,对网络架构进行调整,以计算机视觉为核心讲解各大网络的应用于实战方法,适合快速入门与进阶提升。 【课程要求】 (1)开发环境:python版本:Python3.7+; torch 版本:1.2.0+; torchvision版本:0.4.0+ (2)开发工具:Pycharm; (3)学员基础:需要一定的Python基础,及深度学习基础; (4)学员收货:掌握最新科技图像分类关键技术; (5)学员资料:内含完整程序源码和数据集; (6)课程亮点:专题技术,完整案例,全程实战操作,徒手撸代码 【课程特色】 阵容强大 讲师一直从事与一线项目开发,高级算法专家,一直从事于图像、NLP、个性化推荐系统热门技术领域。 仅跟前沿 基于当前热门讨论话题:垃圾分类,课程采用学术届和工业届最新前沿技术知识要点。 实战为先 根据实际深度学习工业场景-垃圾分类,从产品需求、产品设计和方案设计、产品技术功能实现、模型上线部署。精心设计工业实战项目 保障效果 项目实战方向包含了学术届和工业届最前沿技术要点 项目包装简历优化 课程内垃圾分类图像实战项目完成后可以直接优化到简历中 【课程思维导图】 【课程实战案例】

    507 人正在学习 去看看 沈福利

1.活动安排问题

使剩余安排时间极大化

#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
struct Node
{
	int s;
	int f;
};
bool cmp(Node a,Node b)
{
	return a.f<b.f;
}
int greedySelector(vector<Node> &x,vector<bool> &a)
{
	int n=x.size()-1;
	int j=0;
	int count=1;
	a[0]=true;
	for(int i=1;i<=n;i++)
	{
		if(x[i].s>=x[j].f)
		{
			a[i]=true;
			j=i;
			count++;
		}
		else
		{
			a[i]=false;
		}
	} 	
	return count;
}
int main()
{
	vector<Node> n;
	vector<bool> a;
	int num;
	cin>>num;
	for(int i=0;i<num;i++)
	{
		Node temp;
		cin>>temp.s>>temp.f;
		n.push_back(temp);
		a.push_back(false);
	}
	sort(n.begin(),n.end(),cmp);
	cout<<greedySelector(n,a)<<endl;
	return 0;
}

2.最优装载

#include<iostream>
#include<vector>
#include<algorithm>
#include<cstdlib>
using namespace std;
struct Node
{
	double w;
	int i;
};
bool cmp(Node a,Node b)
{
	return a.w<b.w;
}
double random(int start,int end)
{
	return (start+rand()%(end-start)+0.0)/10;
}
int loading(vector<Node> &x,vector<int> &a,double c)
{
	int i=0;
	while(i<x.size())
	{
		if(x[i].w<=c)
		{
			c=c-x[i].w;
			a.push_back(x[i].i);
			i++;	
		}
		else
		{
			break;
		}
	}
	return i;
}
int main()
{
	vector<Node> n;
	vector<int> a;
	int num;
	double c;
	cin>>num>>c;
	for(int i=0;i<num;i++)
	{
		Node temp;
		temp.w=random(1,2000);
		temp.i=i;
		n.push_back(temp);
		cout<<"n["<<i<<"]: "<<temp.w<<"\t";
	}
	cout<<endl;
	sort(n.begin(),n.end(),cmp);
	cout<<loading(n,a,c)<<endl;
	return 0;
} 

3.哈夫曼编码

#include<iostream>
#include<cstdlib>
#include<queue>
using namespace std;
struct Tree
{
	Tree* lchlid;
	Tree* rchlid;
	int f;//频率
	int data;//值 
};
struct cmp//与sort的cmp不同,结构体中重载调用操作符。也可以重载>操作符,priority_queue<Node,vector<Node>,greater<Node>> Q;
{
 	bool operator()(Tree a,Tree b)
 	{
 		return a.f>b.f;//>升序,<降序 
 	}
};
int random(int start,int end)
{
	return start+rand()%(end-start);
}
void HuffmanTree(priority_queue<Tree,vector<Tree>,cmp> &q) 
{
	while(q.size()>=2)
	{/*
		Tree a=q.top();
		cout<<"&a="<<&a<<endl;//局部变量的地址每次申请的都是相同的,所以后续错了
		q.pop();
		Tree b=q.top();
		cout<<"&b="<<&b<<endl;/花了2个多小时查的,此处需注意了
		q.pop();
		Tree c;
		c.lchlid=&a;
		c.rchlid=&b;
		c.f=a.f+b.f;
		c.data=-1;
		q.push(c);*/
		Tree *a=new Tree;
		a->data=q.top().data;
		a->lchlid=q.top().lchlid;
		a->rchlid=q.top().rchlid;
		a->f=q.top().f;
		q.pop();
		Tree *b=new Tree;
		b->data=q.top().data;
		b->lchlid=q.top().lchlid;
		b->rchlid=q.top().rchlid;
		b->f=q.top().f;
		q.pop();
		Tree c;
		c.lchlid=a;
		c.rchlid=b;
		c.f=a->f+b->f;
		c.data=-1;
		q.push(c);	
	}
}
void LT(const priority_queue<Tree,vector<Tree>,cmp> &p)
{
	queue<Tree*> q;
	Tree n=p.top();
	q.push(&n);
	while(!q.empty())
	{
		Tree *temp=q.front();
		q.pop();
    	if(temp->data==-1)
		{
			cout<<"Node\t";
		}
		else
		{
			cout<<temp->f<<"\t";
		}
		if(temp->lchlid!=NULL)
		{
			q.push(temp->lchlid);
		}
		if(temp->rchlid!=NULL)
		{
			q.push(temp->rchlid);
		}
	}
}
int main()
{
	int num;
	priority_queue<Tree,vector<Tree>,cmp> q;
	cin>>num;
	for(int i=0;i<num;i++)
	{
		Tree temp;
		temp.lchlid=NULL;
		temp.rchlid=NULL;
		temp.f=random(0,1000);
		temp.data=random(1,50);
		q.push(temp);
		cout<<temp.f<<"\t";
	}
	cout<<endl;
	HuffmanTree(q); 
	LT(q);
}

4.单源最短路径

此处直接使用了书本测试数据,代码写死了。可以用vector来代替重写

#include<iostream>
#include<vector>
#include<climits>
using namespace std;
int getMin(int dist[5],bool vt[5])
{
	int min=INT_MAX;
	int mark;
	for(int i=0;i<5;i++)
	{
		if(!vt[i]&&min>dist[i]&&dist[i]>0)
		{
			min=dist[i];
			mark=i;	
		}
	}
	return mark;
}
void dijkstra(int dis[5][5],bool vt[5],int dist[5],int v)
{
	int mark;
	vt[v]=true;
	for(int i=0;i<5;i++)
	{
		dist[i]=dis[v][i];
		cout<<dist[i]<<"\t";
	}
	cout<<endl;
	for(int j=1;j<5;j++)
	{
		mark=getMin(dist,vt);
		vt[mark]=true;
		for(int i=0;i<5;i++)
		{
			if(!vt[i]&&((dist[i]>0&&dist[mark]+dis[mark][i]<dist[i]&&dis[mark][i]>0)||(dist[i]<0&&dis[mark][i]>0&&dis[mark][i]>0)))
			{
				dist[i]=dist[mark]+dis[mark][i];
			}
			cout<<dist[i]<<"\t";
		}
		cout<<endl;
	}
}
int main()
{
	int dis[5][5]={{0,10,-1,30,100},{-1,0,50,-1,-1},{-1,-1,0,-1,10},{-1,-1,20,0,60},{-1,-1,-1,-1,0}};
	bool vt[5]={false};
	int dist[5]={-1};
	int v;
	cin>>v;
	dijkstra(dis,vt,dist,v);
	cout<<"the distance is:"<<endl;
	for(int i=0;i<5;i++)
	{
		cout<<dist[i]<<"\t";
	}
}
5.最小生成树

直接用了年前敲过的代码

A.Prim最小生成树

/************************************************************************************************************************** 
Prim最小生成树:1.从顶点0开始搜素最小权边
                2.搜索到最短边之后,将另一个顶点u加入点集,将最短边值加入ret(总边权值)
		3.判断从顶点u出发的边map[u][j](j为未加入点集的点),是否小于原先点集的点到点j的距离,如果是就替换掉
		4.prim是遍历顶点,所以邻接矩阵比较合适
***************************************************************************************************************************/ 

#define MaxN 101

int n,ret;
int map[MaxN][MaxN];

void prim()
{
	int closet[MaxN];//该点是否加入点集,1表示加入,0表示不加入 
	int dist[MaxN];//待搜索边集,从某个顶点出发的所有带权值边 
	int i,j;
	for(i=0;i<n;i++)
	{
		closet[i]=0;//初始化全未加入
		dist[i]=map[0][i];//初始化待搜索边集为顶点0的边集 
	}
	closet[0]=1;//顶点0,加入点集
	int u,min;
	for(i=1;i<n;i++)//共n-1条边需要加入边集(直到所有点加入点集)
	{
		min=100001;
		for(j=1;j<n;j++)//搜索待选边集最小边权边
		{
			if(!closet[j]&&dist[j]<min&&dist[j]>0)//点j未加入点集,点j与顶点之间的有边,且边权小于min
			{
				u=j;
				min=dist[j];
			}
		}
		closet[u]=1;//顶点u加入点集
		ret=ret+min;//总边权值
		for(j=1;j<n;j++)
		{
			if(!closet[j]&&map[u][j]<dist[j])//从点u出发到点j的边小于点集其他点到点j的边&&点j未加入点集,则更新边权
			{								 
				dist[j]=map[u][j];//将待搜索边集改为顶点u的边集			
			}
		} 
	} 
}
B.Kruscal最小生成树
#include<iostream>
#include<queue>
using namespace std;
#define Maxv 100+5
struct Node
{
	int v2;
	int v1;
	int len;
};
struct cmp
{
	bool operator()(Node a,Node b)
	{
		return a.len>b.len;
	}
};

int dis[Maxv][Maxv];//dis[i][j]等于0时表示不连通 ,不等于1时表示边权值 
int fa[Maxv];//father,并查集 

int Getfa(int i)//查找根节点的函数 
{
	if(fa[i]!=i)//如果不是根节点 
		fa[i]=Getfa(fa[i]);//找根节点 
	return fa[i];//返回节点i所在集合的根节点	
} 
int main()
{
   	 int sum;//最小生成树代价
     priority_queue<Node,vector<Node>,cmp> Q;//声明小顶堆,返回最小数 
 
 	 int vn;//图中的顶点个数 
	 int i;
	 int j;	  
 	 cin>>vn;
	//输入图
	 for(i=1;i<=vn;i++)
	 {
		for(j=1;j<=vn;j++)
		{
			cin>>dis[i][j];	
   		}
	 } 
	 for(i=1;i<=vn;i++)
	 {
		fa[i]=i;//并查集,father,一开始有vn个节点,就有vn个集合 
	 }
	 while(!Q.empty())
	 {
		Q.pop();
	 }
	 //把每条边压入堆中
	 for(i=1;i<vn;i++)
 	 {
 		for(j=i+1;j<=vn;j++)
 		{
			if(dis[i][j])//如果边权值不为0,即顶点之间有边,压入该边 
			{
				Node e;
 				e.v1=i;
  				e.v2=j;
	  			e.len=dis[i][j];
	  			Q.push(e);
	  		}
  		}
    }
    sum=0;
    while(Q.size()!=0)
    {
  		Node e;
   		e=Q.top();
   		Q.pop();
  	 	if(Getfa(e.v1)!=Getfa(e.v2))//若两个顶点不属于同一个点集,表示该边不是回路;也即两个节点的根节点是否相同 
  	 	{                           //根节点不同,则为不同集合,不构成回路  
    		sum=sum+e.len;
    		fa[Getfa(e.v2)]=Getfa(e.v1);//把e.v1的根节点作为e.v2的根节点的爹,也即合并两个集合 
 	    }
   	 }
   	 cout<<sum;
 	 return 0;    
}

6.贪心算法的理论基础









vivado HLS算法分析

阅读数 611

没有更多推荐了,返回首页