0%

FPGA图像处理之常用图像增强算法

本节主要介绍了FPGA图像处理中常用的图像增强算法,包括直方图均衡算法、指数对比增强算法、Gamma映射算法,以及它的MATLAB与FPGA实现。

直方图均衡算法

  • 直方图均衡的基本原理,就是对在图像中像素个数多的灰度值(即对画面起主要作用的灰度值)进行拉伸,而对像素个数少的灰度值(即对画面不起主要作用的灰度值)进行合并,从而提高对比度,使图像清晰,达到增强的目的。

  • 图像的视觉效果与直方图有直接的对应关系,改变直方图的分布,对图像的视觉效果也有很大的影响

  • 图像$f(x,y)$为灰度直方图的一维离散函数,首先,将图像中每个像素的数量,即$f(x,y)$中灰度级为$k$的像素个数,表示为$n(k)$,这也对于这直方图中显示的纵坐标高度,进一步计算灰度级数出现的频率$P(k)$,计算公式如下(其中,$N$为图像的像素总数量):

    • 那么,$P(k) = \frac{n(k)}N$

    • 其次,计算原始图像灰度累计分布的概率$s(k)$,即截止当前像素个数占总个数的比例,计算公式为:$s(k)=\sum_{i=0}^k\frac{n(i)}N$

    • 整体的概率分布=1,那么将数值扩大255倍后,我们将概率拉伸到0~255,最终得到均衡后的直方图图像的概率,计算公式如下:
      $$
      s(k)’ = s(k)\times 255 = \sum_{i = 0}^k\frac{n(i)}N\times 255
      $$

  • 当原始图像整体偏亮的时候,直方图均衡就不能得到很好的效果。直方图均衡时一种全局处理方式。主要有如下缺点:

    • 直方图均衡后,图像灰度级数减少,部分细节容易丢失
    • 对于直方图有高峰时,对比度拉伸后将出现对比度不自然的过分增强现象。

1.直方图均衡的MATLAB实现

  • 基本思路就是首先计算归一化后灰度级数概率的累计值,再将结果拉伸到$0\sim 255$,因此直方图均衡,也称为直方图拉伸

  • 具体实现方式如下:

    • 计算当前灰度图$0\sim 255$级数的像素数量
    • 计算$0\sim 255$级数像素数量的累计值,即截止当前灰度级数的累积像素个数
    • 将累计值除以hw后归一化,将结果扩大$[0,255]$,以当前测试$500\times 500$的图像为例,$hw/255\approx 980$
  • matlab代码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    % -------------------------------------------------------------------------
    % Read PC image to Matlab
    IMG1 = imread('../../0_images/gsls_test1.tif'); % 读取jpg图像
    % IMG1 = rgb2gray(IMG1);
    h = size(IMG1,1); % 读取图像高度
    w = size(IMG1,2); % 读取图像宽度

    % ----------------------------------------------
    % Step1: 进行像素灰度级数统计
    NumPixel = zeros(1,256); %统计0-255灰度级数
    for i = 1:h
    for j = 1: w
    NumPixel(IMG1(i,j) + 1) = NumPixel(IMG1(i,j) + 1) + 1;
    end
    end

    % Step2: 进行像素灰度级数累积统计
    CumPixel = zeros(1,256);
    for i = 1:256
    if i == 1
    CumPixel(i) = NumPixel(i);
    else
    CumPixel(i) = CumPixel(i-1) + NumPixel(i);
    end
    end

    % Step3: 对灰度值进行映射(均衡化) = 归一化 + 扩大到255
    IMG2 = zeros(h,w);
    for i = 1:h
    for j = 1: w
    % IMG2(i,j) = CumPixel(IMG1(i,j)+1)/(h*w)*255;
    IMG2(i,j) = CumPixel(IMG1(i,j)+1)/980;
    % IMG2(i,j) = bitshift(CumPixel(IMG1(i,j)+1),-10);
    end
    end
    IMG2 = uint8(IMG2);


    % -------------------------------------------------------------------------
    % IMG2 = rgb2gray(IMG1); % 转灰度图像
    % figure;
    subplot(231), imshow(IMG1); title('Original Image');
    subplot(234), imhist(IMG1); title('Original Hist');

    % ----------------------------------------------
    % Step1: 进行像素灰度级数统计
    NumPixel2 = zeros(1,256); %统计0-255灰度级数
    for i = 1:h
    for j = 1: w
    NumPixel2(IMG2(i,j) + 1) = NumPixel2(IMG2(i,j) + 1) + 1;
    end
    end

    % Step2: 进行像素灰度级数累积统计
    CumPixel2 = zeros(1,256);
    for i = 1:256
    if i == 1
    CumPixel2(i) = NumPixel2(i);
    else
    CumPixel2(i) = CumPixel2(i-1) + NumPixel2(i);
    end
    end
    subplot(232), imshow(IMG2); title('Manual HistEQ Image');
    subplot(235), imhist(IMG2); title('Manual HistEQ Hist');

    % ----------------------------------------------
    % Matlab自带函数计算
    IMG3 = zeros(h,w);
    IMG3 = histeq(IMG1); % Matlab自带直方图均衡
    subplot(233), imshow(IMG3); title('Matlab HistEQ Image');
    subplot(236), imhist(IMG3); title('Matlab HistEQ Hist');


    % ----------------------------------------------
    figure;
    subplot(121),bar(CumPixel); title('原图灰度级数累积');
    subplot(122),bar(CumPixel2);title('拉伸后灰度级数累积');
    • 仿真结果:

      matlab仿真结果1matlab仿真结果2

2.直方图均衡的FPGA实现

  • hist_stat模块的功能是进行像素的灰度级数统计和灰度级数累积统计; histEQ_proc模块的功能是对原始图像进行直方图均衡。

    image-20241025210450619
  • hist_stat模块中的设计框图:

    image-20241025210953912image-20241025211018554

    • 其中灰度级数统计中的重点是预处理过程: hist_stat模块利用双端口BRAM实现灰度级数的统计, 其中像素灰度值作为BRAM的读写地址。 由于读写BRAM需要消耗1个clk周期, 所以每个像素的灰度级数统计需要消耗两个clk周期, 即从BRAM读出原来的灰度级数, 累加1后重新写入BRAM。 但为了避免灰度值相同的像素连续出现,导致BRAM的灰度级数统计异常, 灰度级数错误统计示意图, 如图3.10所示。 需要对输入像素进行预处理, 即当灰度值相同的像素连续出现时, 可以将两个像素当作1个像素来处理, 此时灰度级数需要累加2, 灰度级数正确统计示意图, 如图3.11所示。

      image-20241025211219748 image-20241025211244299
    • 图像灰度级数统计完成后, 通过BRAM B侧依次读出0~255灰度级的统计值, 同时通过BRAM A侧将读过统计值的地址空间清零, 为下一帧视频的灰度级数统计做准备。将0~255灰度级的统计值依次进行累加并作为结果输出。

    • hist_stat模块中BRAM的数据位宽取决于图像像素的总数量, 如分辨率为640×480的图像的像素总数量为307200(4B000H) , 需要用19bit的数据位宽表示, 设计中用了20bit的数据位宽, 可以进行更大分辨率图像的灰度级数统计。 由于像素灰度值直接作为BRAM的地址, 所以BRAM的深度取决于像素位宽, 设计中像素位宽为8bit, BRAM的深度为256。

  • histEQ_proc模块设计框图:

    image-20241025211933803
    • 直方图均衡的简化公式如下:
      $$
      s(k)’ = round[\frac{\sum_{i=0}^k n(i)}{980}] = round[\sum_{i=0}^k n(i)\frac{round(\frac{2^{27}}{980})}{2^{27}}]=round[\sum_{i=0}^k n(i)\times 136957 >> 27]
      $$

      1. 将上级hist_stat模块的所有灰度级数累积统计结果通过BRAM A侧写入BRAM

      2. 在(1) 完成后, 启动直方图均衡处理, 开始从外部存储器中读取原始图像。

      3. 输入像素的灰度值作为BRAM B侧的地址, 从BRAM中读取对应的灰度级数累积统计结果 ,并于136957相乘得到mult_result,其mult_result[34:27]为整数部分,mult_result[26:0]为小数部分

      4. 对mult_result进行四舍五入运算,得到直方图均衡后的图像post_img_gray,计算公式如下:
        $$
        post_img_gray = mult_result[34:27]+mult_result[26];
        $$

  • Verilog源代码:

    • hist_stat.v

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      50
      51
      52
      53
      54
      55
      56
      57
      58
      59
      60
      61
      62
      63
      64
      65
      66
      67
      68
      69
      70
      71
      72
      73
      74
      75
      76
      77
      78
      79
      80
      81
      82
      83
      84
      85
      86
      87
      88
      89
      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
      172
      173
      174
      175
      176
      177
      178
      179
      180
      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
      214
      215
      216
      217
      218
      219
      220
      221
      222
      223
      224
      225
      226
      227
      228
      229
      230
      231
      232
      233
      234
      235
      236
      237
      238
      239
      240
      241
      242
      243
      244
      245
      246
      247
      248
      249
      250
      251
      252
      253
      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
      311
      312
      313
      314
      315
      316
      317
      318
      319
      320
      321
      322
      323
      324
      325
      326
      327
      328
      329
      330
      331
      332
      333
      module hist_stat
      (
      input wire clk ,
      input wire rst_n ,

      input wire img_vsync ,
      input wire img_href ,
      input wire [ 7:0] img_gray ,

      output reg [ 7:0] pixel_level ,
      output reg [19:0] pixel_level_acc_num ,
      output reg pixel_level_valid
      );
      //----------------------------------------------------------------------
      // bram signals define
      wire bram_a_wenb;
      wire [ 7:0] bram_a_addr;
      wire [19:0] bram_a_rdata;
      wire bram_b_wenb;
      wire [ 7:0] bram_b_addr;
      wire [19:0] bram_b_wdata;
      wire [19:0] bram_b_rdata;

      //----------------------------------------------------------------------
      // preprocess
      reg [7:0] pixel_data;

      always @(posedge clk)
      begin
      pixel_data <= img_gray;
      end

      reg pixel_data_valid;

      always @(posedge clk or negedge rst_n)
      begin
      if(!rst_n)
      pixel_data_valid <= 1'b0;
      else
      pixel_data_valid <= img_href;
      end

      reg img_vsync_dly;

      always @(posedge clk or negedge rst_n)
      begin
      if(!rst_n)
      img_vsync_dly <= 1'b0;
      else
      img_vsync_dly <= img_vsync;
      end

      wire pixel_data_eop;
      assign pixel_data_eop = img_vsync_dly & ~img_vsync;

      //----------------------------------------------------------------------
      // preprocess
      reg [7:0] pixel_data_tmp;

      always @(posedge clk)
      begin
      pixel_data_tmp <= pixel_data;
      end

      reg pixel_data_valid_tmp1;

      always @(posedge clk or negedge rst_n)
      begin
      if(!rst_n)
      pixel_data_valid_tmp1 <= 1'b0;
      else
      begin
      if(pixel_data_valid == 1'b1)
      begin
      if(pixel_data_valid_tmp1 == 1'b0)
      pixel_data_valid_tmp1 <= 1'b1;
      else
      begin
      if(pixel_data_tmp == pixel_data)
      pixel_data_valid_tmp1 <= 1'b0;
      else
      pixel_data_valid_tmp1 <= 1'b1;
      end
      end
      else
      pixel_data_valid_tmp1 <= 1'b0;
      end
      end

      reg [1:0] pixel_data_cnt_tmp;

      always @(posedge clk or negedge rst_n)
      begin
      if(!rst_n)
      pixel_data_cnt_tmp <= 2'd1;
      else
      begin
      if((pixel_data_valid == 1'b1)&&(pixel_data_valid_tmp1 == 1'b1)&&(pixel_data_tmp == pixel_data))
      pixel_data_cnt_tmp <= 2'd2;
      else
      pixel_data_cnt_tmp <= 2'd1;
      end
      end

      reg pixel_data_eop_tmp;

      always @(posedge clk or negedge rst_n)
      begin
      if(!rst_n)
      pixel_data_eop_tmp <= 1'b0;
      else
      pixel_data_eop_tmp <= pixel_data_eop;
      end

      //----------------------------------------------------------------------
      // c1
      reg [7:0] pixel_data_c1;

      always @(posedge clk)
      begin
      pixel_data_c1 <= pixel_data;
      end

      reg pixel_data_eop_c1;
      reg pixel_data_valid_tmp_c1;

      always @(posedge clk or negedge rst_n)
      begin
      if(!rst_n)
      begin
      pixel_data_eop_c1 <= 1'b0;
      pixel_data_valid_tmp_c1 <= 1'b0;
      end
      else
      begin
      pixel_data_eop_c1 <= pixel_data_eop_tmp;
      pixel_data_valid_tmp_c1 <= pixel_data_valid_tmp1;
      end
      end

      //----------------------------------------------------------------------
      // c2 : delay 3 clock
      reg [7:0] pixel_data_c2;

      always @(posedge clk)
      begin
      pixel_data_c2 <= pixel_data_c1;
      end

      reg pixel_data_eop_tmp1_c2;
      reg pixel_data_eop_tmp2_c2;
      reg pixel_data_eop_c2;

      always @(posedge clk or negedge rst_n)
      begin
      if(!rst_n)
      begin
      pixel_data_eop_tmp1_c2 <= 1'b0;
      pixel_data_eop_tmp2_c2 <= 1'b0;
      pixel_data_eop_c2 <= 1'b0;
      end
      else
      begin
      pixel_data_eop_tmp1_c2 <= pixel_data_eop_c1;
      pixel_data_eop_tmp2_c2 <= pixel_data_eop_tmp1_c2;
      pixel_data_eop_c2 <= pixel_data_eop_tmp2_c2;
      end
      end

      //----------------------------------------------------------------------
      // c3
      reg bram_rw_ctrl_flag_c3;
      reg [8:0] bram_rw_ctrl_cnt;

      always @(posedge clk or negedge rst_n)
      begin
      if(!rst_n)
      bram_rw_ctrl_flag_c3 <= 1'b0;
      else
      begin
      if(pixel_data_eop_c2 == 1'b1)
      bram_rw_ctrl_flag_c3 <= 1'b1;
      else if(bram_rw_ctrl_cnt == 9'h100)
      bram_rw_ctrl_flag_c3 <= 1'b0;
      else
      bram_rw_ctrl_flag_c3 <= bram_rw_ctrl_flag_c3;
      end
      end

      reg [8:0] bram_rw_ctrl_cnt_dly;

      always @(posedge clk)
      begin
      bram_rw_ctrl_cnt_dly <= bram_rw_ctrl_cnt;
      end

      always @(*)
      begin
      if(bram_rw_ctrl_flag_c3 == 1'b1)
      bram_rw_ctrl_cnt <= bram_rw_ctrl_cnt_dly + 1'b1;
      else
      bram_rw_ctrl_cnt <= 9'b0;
      end

      wire [7:0] bram_addr_c3;
      assign bram_addr_c3 = bram_rw_ctrl_cnt - 1'b1;

      //----------------------------------------------------------------------
      // c4
      reg bram_rw_ctrl_flag_c4;

      always @(posedge clk or negedge rst_n)
      begin
      if(!rst_n)
      bram_rw_ctrl_flag_c4 <= 1'b0;
      else
      bram_rw_ctrl_flag_c4 <= bram_rw_ctrl_flag_c3;
      end

      reg [7:0] bram_addr_c4;

      always @(posedge clk)
      begin
      bram_addr_c4 <= bram_addr_c3;
      end

      //----------------------------------------------------------------------
      // c5
      reg [7:0] pixel_level_c5;

      always @(posedge clk)
      begin
      pixel_level_c5 <= bram_addr_c4;
      end

      reg [19:0] pixel_level_num_c5;

      always @(posedge clk)
      begin
      if(bram_rw_ctrl_flag_c4 == 1'b1)
      pixel_level_num_c5 <= bram_b_rdata;
      else
      pixel_level_num_c5 <= 20'b0;
      end

      reg pixel_level_valid_c5;

      always @(posedge clk or negedge rst_n)
      begin
      if(!rst_n)
      pixel_level_valid_c5 <= 1'b0;
      else
      pixel_level_valid_c5 <= bram_rw_ctrl_flag_c4;
      end

      //----------------------------------------------------------------------
      // c6
      reg [7:0] pixel_level_c6;

      always @(posedge clk)
      begin
      pixel_level_c6 <= pixel_level_c5;
      end

      reg [19:0] pixel_level_acc_num_c6;

      always @(posedge clk)
      begin
      if(pixel_level_valid_c5 == 1'b1)
      begin
      if(pixel_level_c5 == 8'b0)
      pixel_level_acc_num_c6 <= pixel_level_num_c5;
      else
      pixel_level_acc_num_c6 <= pixel_level_acc_num_c6 + pixel_level_num_c5;
      end
      else
      pixel_level_acc_num_c6 <= pixel_level_acc_num_c6;
      end

      reg pixel_level_valid_c6;

      always @(posedge clk or negedge rst_n)
      begin
      if(!rst_n)
      pixel_level_valid_c6 <= 1'b0;
      else
      pixel_level_valid_c6 <= pixel_level_valid_c5;
      end

      //----------------------------------------------------------------------
      // signal output
      always @(posedge clk)
      begin
      pixel_level <= pixel_level_c6;
      pixel_level_acc_num <= pixel_level_acc_num_c6;
      end

      always @(posedge clk or negedge rst_n)
      begin
      if(!rst_n)
      pixel_level_valid <= 1'b0;
      else
      pixel_level_valid <= pixel_level_valid_c6;
      end

      //----------------------------------------------------------------------
      // bram_a20d256_b20d256 module initialization
      assign bram_a_wenb = bram_rw_ctrl_flag_c4;
      assign bram_a_addr = (bram_rw_ctrl_flag_c4 == 1'b1) ? bram_addr_c4 : pixel_data_tmp;
      assign bram_b_wenb = pixel_data_valid_tmp_c1;
      assign bram_b_addr = (bram_rw_ctrl_flag_c3 == 1'b1) ? bram_addr_c3 : pixel_data_c2;
      assign bram_b_wdata = bram_a_rdata + pixel_data_cnt_tmp;

      bram_ture_dual_port
      #(
      .C_ADDR_WIDTH (8 ),
      .C_DATA_WIDTH (20)
      )
      u_bram_ture_dual_port
      (
      .clka (clk ),
      .wea (bram_a_wenb ),
      .addra (bram_a_addr ),
      .dina (20'b0 ),
      .douta (bram_a_rdata ),
      .clkb (clk ),
      .web (bram_b_wenb ),
      .addrb (bram_b_addr ),
      .dinb (bram_b_wdata ),
      .doutb (bram_b_rdata )
      );

      endmodule
    • histEQ_proc.v

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      50
      51
      52
      53
      54
      55
      56
      57
      58
      59
      60
      61
      62
      63
      64
      65
      66
      67
      68
      69
      70
      71
      72
      73
      74
      75
      76
      77
      78
      79
      80
      81
      82
      83
      84
      85
      86
      87
      88
      89
      90
      91
      92
      93
      94
      95
      96
      97
      98
      99
      100
      101
      102
      103
      104
      module histEQ_proc
      (
      input wire clk ,
      input wire rst_n ,

      input wire [ 7:0] pixel_level ,
      input wire [19:0] pixel_level_acc_num ,
      input wire pixel_level_valid ,
      output reg histEQ_start_flag ,

      input wire per_img_vsync ,
      input wire per_img_href ,
      input wire [ 7:0] per_img_gray ,

      output wire post_img_vsync ,
      output wire post_img_href ,
      output wire [ 7:0] post_img_gray
      );
      //----------------------------------------------------------------------
      // delay 1 clock
      wire bram_a_wenb;
      wire [ 7:0] bram_a_addr;
      wire [19:0] bram_a_wdata;
      wire [ 7:0] bram_b_addr;
      wire [19:0] bram_b_rdata;

      assign bram_a_wenb = pixel_level_valid;
      assign bram_a_addr = pixel_level;
      assign bram_a_wdata = pixel_level_acc_num;
      assign bram_b_addr = per_img_gray;

      bram_ture_dual_port
      #(
      .C_ADDR_WIDTH (8 ),
      .C_DATA_WIDTH (20)
      )
      u_bram_ture_dual_port
      (
      .clka (clk ),
      .wea (bram_a_wenb ),
      .addra (bram_a_addr ),
      .dina (bram_a_wdata ),
      .douta ( ),
      .clkb (clk ),
      .web (1'b0 ),
      .addrb (bram_b_addr ),
      .dinb (20'b0 ),
      .doutb (bram_b_rdata )
      );

      always @(posedge clk or negedge rst_n)
      begin
      if(!rst_n)
      histEQ_start_flag <= 1'b0;
      else
      begin
      if((pixel_level_valid == 1'b1)&&(pixel_level == 8'd255))
      histEQ_start_flag <= 1'b1;
      else
      histEQ_start_flag <= 1'b0;
      end
      end

      //----------------------------------------------------------------------
      // uint8(CumPixel/980) = round(CumPixel * 136957/2^27) , CumPixel <= 500*500
      reg [34:0] mult_result;

      always @(posedge clk)
      begin
      mult_result <= bram_b_rdata * 18'd136957;
      end

      reg [7:0] pixel_data;

      always @(posedge clk)
      begin
      pixel_data <= mult_result[34:27] + mult_result[26];
      end

      //----------------------------------------------------------------------
      // lag 3 clocks signal sync
      reg [2:0] per_img_vsync_r;
      reg [2:0] per_img_href_r;

      always @(posedge clk or negedge rst_n)
      begin
      if(!rst_n)
      begin
      per_img_vsync_r <= 3'b0;
      per_img_href_r <= 3'b0;
      end
      else
      begin
      per_img_vsync_r <= {per_img_vsync_r[1:0],per_img_vsync};
      per_img_href_r <= {per_img_href_r[1:0],per_img_href};
      end
      end

      //----------------------------------------------------------------------
      assign post_img_vsync = per_img_vsync_r[2];
      assign post_img_href = per_img_href_r[2];
      assign post_img_gray = pixel_data;

      endmodule
    • 仿真结果:

      image-20241025204828561


指数对比增强算法

  • 指数对比度增强有很多方法,但万变不离其宗,即以一定阈值为中心,提高阈值以上的亮度,并降低阈值以下的亮度,典型的以对数对比度增强函数为例,计算公式如下:
    $$
    q=\frac{1}{1+(\frac{Threshold}{p})^E}\times 255
    $$

  • E的值越大,对暗区的压缩及亮区的提升程度就越大,明暗之间的对比就越明显,即E可以表示为图像对比度增强的程度。

    不同强度的指数对比度图(MATLAB)

1.指数对比度增强的MATLAB实现

  • matlab代码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    % Read PC image to Matlab
    % IMG1 = imread('../../0_images/scart.jpg'); % 读取jpg图像
    IMG1 = imread('../../0_images/gsls_test1.tif'); % 读取jpg图像
    % IMG1 = rgb2gray(IMG1);
    h = size(IMG1,1); % 读取图像高度
    w = size(IMG1,2); % 读取图像宽度
    figure;
    subplot(121);imshow(IMG1);title('原图');

    % -------------------------------------------------------------------------
    THRESHOLD = 127;
    E=5;
    % IMG1 = double(IMG1);
    IMG2 = zeros(h,w);
    for i = 1:h
    for j = 1:w
    IMG2(i,j) = (1./(1 + (THRESHOLD./double(IMG1(i,j))).^E)) * 255;
    end

    end
    IMG2 = uint8(IMG2);
    subplot(122);imshow(IMG2);title('对比度增强效果');

    % -------------------------------------------------------------------------
    figure;
    subplot(231);imshow(IMG1);title('原图');
    subplot(232);imshow(IMG2);title('指数对比度增强图');
    subplot(235);imhist(IMG2);title('增强后直方图');

    IMG3 = zeros(h,w);
    IMG3 = histeq(IMG1);
    subplot(234);imhist((IMG1));title('原图直方图');
    subplot(233);imshow((IMG3));title('直方图拉伸图');
    subplot(236);imhist(IMG3);title('拉伸后直方图');
    • 仿真结果:

      MATLAB对比结果1
  • 对比度分析:

    • 由于原始图像素主要集中在阈值127以下,因此指数对比度增强后主要是压缩了暗区,亮区并没有明显的提高。
    • 但通过直方图拉伸后,将图像灰度拉伸到$0\sim 255$,明暗之间的对比度相对更明显,但一定程度上也造成了图像局部过曝的现象
  • 直方图分析:

    • 原始图像素集中在100左右,指数对比度增强后,像素拉伸到了$25\sim 150$,而直方图拉伸后,像素拉伸到了$0\sim 255$,因此从当前测试图来看,直方图拉伸后的动态范围更宽
    • 不过这也因图而异,比如原图就是比较亮的图,指数对比度增强后效果差强人意,而直方图拉伸后图像过暗,损失了部分细节。

2.指数对比度增强的FPGA实现

  • 指数对比度增强,无论是指数函数,还是各类曲线映射,其本质上就是一种像素映射操作。

  • 指数函数、 对数函数等,实时的计算非常耗时, 并且在FPGA上也很难实现(浮点) 。 但当选定参数后, 其结果是固定的, 因此可以根据参数先计算好函数的映射结果, 再以数组的方式进行索引, 得到计算后的结果。 这种方法,通俗地讲就是查找表, 通过查找表进行Mapping操作, 可在 X 、 Y 坐标上找到各自的映射点。

  • 在FPGA中进行Mapping操作时, 可以将数组存放在RAM或者以查找表的方式进行映射。 由于256Byte的存储不大, 同时为了提高移植的灵活度, 笔者推荐使用查找表存储的方式因此直接使用MATLAB生成Verilog文件

  • MATLAB生成verilog文件的MATLAB代码(好神奇的做法,第一次见,学到了):

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    THRESHOLD = 127;
    E = 7;

    fp_gray = fopen('.\Curve_Contrast_Array.v','w');
    fprintf(fp_gray,'//Curve THRESHOLD = %d, E = %d\n', THRESHOLD, E);
    fprintf(fp_gray,'module Curve_Contrast_Array\n');
    fprintf(fp_gray,'(\n');
    fprintf(fp_gray,' input\t\t[7:0]\tPre_Data,\n');
    fprintf(fp_gray,' output\treg\t[7:0]\tPost_Data\n');
    fprintf(fp_gray,');\n\n');
    fprintf(fp_gray,'always@(*)\n');
    fprintf(fp_gray,'begin\n');
    fprintf(fp_gray,'\tcase(Pre_Data)\n');
    Gray_ARRAY = zeros(1,256);
    for i = 1 : 256
    Gray_ARRAY(1,i) = (1./(1 + (THRESHOLD./(i-1)).^E)) * 255;
    Gray_ARRAY(1,i) = uint8(Gray_ARRAY(1,i));
    fprintf(fp_gray,'\t8''h%s : Post_Data = 8''h%s; \n',dec2hex(i-1,2), dec2hex(Gray_ARRAY(1,i),2));
    end
    fprintf(fp_gray,'\tendcase\n');
    fprintf(fp_gray,'end\n');
    fprintf(fp_gray,'\nendmodule\n');
    fclose(fp_gray);

    % -----------------------------------------------------------------------
    % 打印变形后的映射数组Gray_ARRAY
    reshape(Gray_ARRAY,16,16)
  • 生成的Verilog文件:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    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
    172
    173
    174
    175
    176
    177
    178
    179
    180
    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
    214
    215
    216
    217
    218
    219
    220
    221
    222
    223
    224
    225
    226
    227
    228
    229
    230
    231
    232
    233
    234
    235
    236
    237
    238
    239
    240
    241
    242
    243
    244
    245
    246
    247
    248
    249
    250
    251
    252
    253
    254
    255
    256
    257
    258
    259
    260
    261
    262
    263
    264
    265
    266
    267
    268
    269
    270
    //Curve THRESHOLD = 127, E = 7
    module Curve_Contrast_Array
    (
    input [7:0] Pre_Data,
    output reg [7:0] Post_Data
    );

    always@(*)
    begin
    case(Pre_Data)
    8'h00 : Post_Data = 8'h00;
    8'h01 : Post_Data = 8'h00;
    8'h02 : Post_Data = 8'h00;
    8'h03 : Post_Data = 8'h00;
    8'h04 : Post_Data = 8'h00;
    8'h05 : Post_Data = 8'h00;
    8'h06 : Post_Data = 8'h00;
    8'h07 : Post_Data = 8'h00;
    8'h08 : Post_Data = 8'h00;
    8'h09 : Post_Data = 8'h00;
    8'h0A : Post_Data = 8'h00;
    8'h0B : Post_Data = 8'h00;
    8'h0C : Post_Data = 8'h00;
    8'h0D : Post_Data = 8'h00;
    8'h0E : Post_Data = 8'h00;
    8'h0F : Post_Data = 8'h00;
    8'h10 : Post_Data = 8'h00;
    8'h11 : Post_Data = 8'h00;
    8'h12 : Post_Data = 8'h00;
    8'h13 : Post_Data = 8'h00;
    8'h14 : Post_Data = 8'h00;
    8'h15 : Post_Data = 8'h00;
    8'h16 : Post_Data = 8'h00;
    8'h17 : Post_Data = 8'h00;
    8'h18 : Post_Data = 8'h00;
    8'h19 : Post_Data = 8'h00;
    8'h1A : Post_Data = 8'h00;
    8'h1B : Post_Data = 8'h00;
    8'h1C : Post_Data = 8'h00;
    8'h1D : Post_Data = 8'h00;
    8'h1E : Post_Data = 8'h00;
    8'h1F : Post_Data = 8'h00;
    8'h20 : Post_Data = 8'h00;
    8'h21 : Post_Data = 8'h00;
    8'h22 : Post_Data = 8'h00;
    8'h23 : Post_Data = 8'h00;
    8'h24 : Post_Data = 8'h00;
    8'h25 : Post_Data = 8'h00;
    8'h26 : Post_Data = 8'h00;
    8'h27 : Post_Data = 8'h00;
    8'h28 : Post_Data = 8'h00;
    8'h29 : Post_Data = 8'h00;
    8'h2A : Post_Data = 8'h00;
    8'h2B : Post_Data = 8'h00;
    8'h2C : Post_Data = 8'h00;
    8'h2D : Post_Data = 8'h00;
    8'h2E : Post_Data = 8'h00;
    8'h2F : Post_Data = 8'h00;
    8'h30 : Post_Data = 8'h00;
    8'h31 : Post_Data = 8'h00;
    8'h32 : Post_Data = 8'h00;
    8'h33 : Post_Data = 8'h00;
    8'h34 : Post_Data = 8'h00;
    8'h35 : Post_Data = 8'h01;
    8'h36 : Post_Data = 8'h01;
    8'h37 : Post_Data = 8'h01;
    8'h38 : Post_Data = 8'h01;
    8'h39 : Post_Data = 8'h01;
    8'h3A : Post_Data = 8'h01;
    8'h3B : Post_Data = 8'h01;
    8'h3C : Post_Data = 8'h01;
    8'h3D : Post_Data = 8'h01;
    8'h3E : Post_Data = 8'h02;
    8'h3F : Post_Data = 8'h02;
    8'h40 : Post_Data = 8'h02;
    8'h41 : Post_Data = 8'h02;
    8'h42 : Post_Data = 8'h03;
    8'h43 : Post_Data = 8'h03;
    8'h44 : Post_Data = 8'h03;
    8'h45 : Post_Data = 8'h04;
    8'h46 : Post_Data = 8'h04;
    8'h47 : Post_Data = 8'h04;
    8'h48 : Post_Data = 8'h05;
    8'h49 : Post_Data = 8'h05;
    8'h4A : Post_Data = 8'h06;
    8'h4B : Post_Data = 8'h06;
    8'h4C : Post_Data = 8'h07;
    8'h4D : Post_Data = 8'h07;
    8'h4E : Post_Data = 8'h08;
    8'h4F : Post_Data = 8'h09;
    8'h50 : Post_Data = 8'h0A;
    8'h51 : Post_Data = 8'h0A;
    8'h52 : Post_Data = 8'h0B;
    8'h53 : Post_Data = 8'h0C;
    8'h54 : Post_Data = 8'h0D;
    8'h55 : Post_Data = 8'h0E;
    8'h56 : Post_Data = 8'h10;
    8'h57 : Post_Data = 8'h11;
    8'h58 : Post_Data = 8'h12;
    8'h59 : Post_Data = 8'h14;
    8'h5A : Post_Data = 8'h15;
    8'h5B : Post_Data = 8'h17;
    8'h5C : Post_Data = 8'h18;
    8'h5D : Post_Data = 8'h1A;
    8'h5E : Post_Data = 8'h1C;
    8'h5F : Post_Data = 8'h1E;
    8'h60 : Post_Data = 8'h20;
    8'h61 : Post_Data = 8'h22;
    8'h62 : Post_Data = 8'h24;
    8'h63 : Post_Data = 8'h26;
    8'h64 : Post_Data = 8'h28;
    8'h65 : Post_Data = 8'h2B;
    8'h66 : Post_Data = 8'h2D;
    8'h67 : Post_Data = 8'h30;
    8'h68 : Post_Data = 8'h33;
    8'h69 : Post_Data = 8'h35;
    8'h6A : Post_Data = 8'h38;
    8'h6B : Post_Data = 8'h3B;
    8'h6C : Post_Data = 8'h3E;
    8'h6D : Post_Data = 8'h41;
    8'h6E : Post_Data = 8'h44;
    8'h6F : Post_Data = 8'h47;
    8'h70 : Post_Data = 8'h4B;
    8'h71 : Post_Data = 8'h4E;
    8'h72 : Post_Data = 8'h51;
    8'h73 : Post_Data = 8'h55;
    8'h74 : Post_Data = 8'h58;
    8'h75 : Post_Data = 8'h5C;
    8'h76 : Post_Data = 8'h5F;
    8'h77 : Post_Data = 8'h63;
    8'h78 : Post_Data = 8'h67;
    8'h79 : Post_Data = 8'h6A;
    8'h7A : Post_Data = 8'h6E;
    8'h7B : Post_Data = 8'h71;
    8'h7C : Post_Data = 8'h75;
    8'h7D : Post_Data = 8'h78;
    8'h7E : Post_Data = 8'h7C;
    8'h7F : Post_Data = 8'h80;
    8'h80 : Post_Data = 8'h83;
    8'h81 : Post_Data = 8'h86;
    8'h82 : Post_Data = 8'h8A;
    8'h83 : Post_Data = 8'h8D;
    8'h84 : Post_Data = 8'h91;
    8'h85 : Post_Data = 8'h94;
    8'h86 : Post_Data = 8'h97;
    8'h87 : Post_Data = 8'h9A;
    8'h88 : Post_Data = 8'h9D;
    8'h89 : Post_Data = 8'hA1;
    8'h8A : Post_Data = 8'hA4;
    8'h8B : Post_Data = 8'hA7;
    8'h8C : Post_Data = 8'hA9;
    8'h8D : Post_Data = 8'hAC;
    8'h8E : Post_Data = 8'hAF;
    8'h8F : Post_Data = 8'hB2;
    8'h90 : Post_Data = 8'hB4;
    8'h91 : Post_Data = 8'hB7;
    8'h92 : Post_Data = 8'hB9;
    8'h93 : Post_Data = 8'hBC;
    8'h94 : Post_Data = 8'hBE;
    8'h95 : Post_Data = 8'hC0;
    8'h96 : Post_Data = 8'hC2;
    8'h97 : Post_Data = 8'hC5;
    8'h98 : Post_Data = 8'hC7;
    8'h99 : Post_Data = 8'hC9;
    8'h9A : Post_Data = 8'hCA;
    8'h9B : Post_Data = 8'hCC;
    8'h9C : Post_Data = 8'hCE;
    8'h9D : Post_Data = 8'hD0;
    8'h9E : Post_Data = 8'hD2;
    8'h9F : Post_Data = 8'hD3;
    8'hA0 : Post_Data = 8'hD5;
    8'hA1 : Post_Data = 8'hD6;
    8'hA2 : Post_Data = 8'hD8;
    8'hA3 : Post_Data = 8'hD9;
    8'hA4 : Post_Data = 8'hDB;
    8'hA5 : Post_Data = 8'hDC;
    8'hA6 : Post_Data = 8'hDD;
    8'hA7 : Post_Data = 8'hDE;
    8'hA8 : Post_Data = 8'hDF;
    8'hA9 : Post_Data = 8'hE1;
    8'hAA : Post_Data = 8'hE2;
    8'hAB : Post_Data = 8'hE3;
    8'hAC : Post_Data = 8'hE4;
    8'hAD : Post_Data = 8'hE5;
    8'hAE : Post_Data = 8'hE6;
    8'hAF : Post_Data = 8'hE7;
    8'hB0 : Post_Data = 8'hE7;
    8'hB1 : Post_Data = 8'hE8;
    8'hB2 : Post_Data = 8'hE9;
    8'hB3 : Post_Data = 8'hEA;
    8'hB4 : Post_Data = 8'hEB;
    8'hB5 : Post_Data = 8'hEB;
    8'hB6 : Post_Data = 8'hEC;
    8'hB7 : Post_Data = 8'hED;
    8'hB8 : Post_Data = 8'hED;
    8'hB9 : Post_Data = 8'hEE;
    8'hBA : Post_Data = 8'hEE;
    8'hBB : Post_Data = 8'hEF;
    8'hBC : Post_Data = 8'hF0;
    8'hBD : Post_Data = 8'hF0;
    8'hBE : Post_Data = 8'hF1;
    8'hBF : Post_Data = 8'hF1;
    8'hC0 : Post_Data = 8'hF2;
    8'hC1 : Post_Data = 8'hF2;
    8'hC2 : Post_Data = 8'hF3;
    8'hC3 : Post_Data = 8'hF3;
    8'hC4 : Post_Data = 8'hF3;
    8'hC5 : Post_Data = 8'hF4;
    8'hC6 : Post_Data = 8'hF4;
    8'hC7 : Post_Data = 8'hF4;
    8'hC8 : Post_Data = 8'hF5;
    8'hC9 : Post_Data = 8'hF5;
    8'hCA : Post_Data = 8'hF5;
    8'hCB : Post_Data = 8'hF6;
    8'hCC : Post_Data = 8'hF6;
    8'hCD : Post_Data = 8'hF6;
    8'hCE : Post_Data = 8'hF7;
    8'hCF : Post_Data = 8'hF7;
    8'hD0 : Post_Data = 8'hF7;
    8'hD1 : Post_Data = 8'hF7;
    8'hD2 : Post_Data = 8'hF8;
    8'hD3 : Post_Data = 8'hF8;
    8'hD4 : Post_Data = 8'hF8;
    8'hD5 : Post_Data = 8'hF8;
    8'hD6 : Post_Data = 8'hF9;
    8'hD7 : Post_Data = 8'hF9;
    8'hD8 : Post_Data = 8'hF9;
    8'hD9 : Post_Data = 8'hF9;
    8'hDA : Post_Data = 8'hF9;
    8'hDB : Post_Data = 8'hF9;
    8'hDC : Post_Data = 8'hFA;
    8'hDD : Post_Data = 8'hFA;
    8'hDE : Post_Data = 8'hFA;
    8'hDF : Post_Data = 8'hFA;
    8'hE0 : Post_Data = 8'hFA;
    8'hE1 : Post_Data = 8'hFA;
    8'hE2 : Post_Data = 8'hFB;
    8'hE3 : Post_Data = 8'hFB;
    8'hE4 : Post_Data = 8'hFB;
    8'hE5 : Post_Data = 8'hFB;
    8'hE6 : Post_Data = 8'hFB;
    8'hE7 : Post_Data = 8'hFB;
    8'hE8 : Post_Data = 8'hFB;
    8'hE9 : Post_Data = 8'hFB;
    8'hEA : Post_Data = 8'hFC;
    8'hEB : Post_Data = 8'hFC;
    8'hEC : Post_Data = 8'hFC;
    8'hED : Post_Data = 8'hFC;
    8'hEE : Post_Data = 8'hFC;
    8'hEF : Post_Data = 8'hFC;
    8'hF0 : Post_Data = 8'hFC;
    8'hF1 : Post_Data = 8'hFC;
    8'hF2 : Post_Data = 8'hFC;
    8'hF3 : Post_Data = 8'hFC;
    8'hF4 : Post_Data = 8'hFC;
    8'hF5 : Post_Data = 8'hFC;
    8'hF6 : Post_Data = 8'hFD;
    8'hF7 : Post_Data = 8'hFD;
    8'hF8 : Post_Data = 8'hFD;
    8'hF9 : Post_Data = 8'hFD;
    8'hFA : Post_Data = 8'hFD;
    8'hFB : Post_Data = 8'hFD;
    8'hFC : Post_Data = 8'hFD;
    8'hFD : Post_Data = 8'hFD;
    8'hFE : Post_Data = 8'hFD;
    8'hFF : Post_Data = 8'hFD;
    endcase
    end

    endmodule
    • 仿真结果:

      image-20241027153724186


Gamma映射算法

  • 将原始图像通过映射操作来满足人眼亮度响应的曲线,即为Gamma曲线,响应的变换即为Gamma变换。

  • 在ISP流程中,Gamma是很重要的一部分,为了适应人眼的亮度变化曲线,对图像传感器产生的图像进行Gamma变换,来提升图像的辨识度

  • 图像未经Gamma变换时,低灰度值区域在较大范围内表现为同一个值,造成了信息的丢失;同时高灰度值区域又被细分,造成了存储的浪费。但图像经Gamma变换后,低灰度值区域有了更多的灰阶信息,而高灰度值区域进行了一定程度地压缩,更符合人眼的特性。

  • Gamma的来龙去脉很复杂,但实现却很简单,只通过简单的Mapping操作,是对输入图像灰度值进行的非线性操作,使输出图像灰度值与输入图像灰度值呈指数关系,计算公式如下:
    $$
    V_{out} = AV_{in}^{\gamma}(A为归一化参数)
    $$

  • 由于PC的像素都是$0\sim 255$,因此对公式进一步变形,将像素扩展到$0\sim 255$,计算公式如下:
    $$
    f(x) = \begin{cases}
    V_{out} = \frac{255}{255^{2.2}}V_{in}^{2.2} &\text{Gamma = 2.2曲线}\
    V_{out’} = \frac{255}{255^{1/2.2}}V_{in}^{1/2.2} &\text{Gamma = 1/2.2曲线}\
    \end{cases}
    $$

1.Gamma映射的MATLAB实现

  • MATLAB代码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    % -------------------------------------------------------------------------
    % Read PC image to Matlab
    IMG1 = imread('../../0_images/scart.jpg'); % 读取jpg图像
    IMG1 = rgb2gray(IMG1);
    % IMG1 = imread('../../0_images/gsls_test1.tif'); % 读取jpg图像
    h = size(IMG1,1); % 读取图像高度
    w = size(IMG1,2); % 读取图像宽度
    subplot(221);imshow(IMG1);title('【1】原图');

    % -------------------------------------------------------------------------
    IMG2 = zeros(h,w);
    for i = 1:h
    for j = 1:w
    IMG2(i,j) = (255/255.^2.2)*double(IMG1(i,j)).^2.2;
    end

    end
    IMG2 = uint8(IMG2);
    subplot(222);imshow(IMG2);title('【2】Gamma=2.2映射');

    % -------------------------------------------------------------------------
    IMG3 = zeros(h,w);
    for i = 1:h
    for j = 1:w
    IMG3(i,j) = (255/255.^(1/2.2))*double(IMG1(i,j)).^(1/2.2);
    end

    end
    IMG3 = uint8(IMG3);
    subplot(223);imshow(IMG3);title('【3】Gamma=1/2.2映射效果');


    % -------------------------------------------------------------------------
    THRESHOLD = 127;
    E=4;
    IMG4 = zeros(h,w);
    for i = 1:h
    for j = 1:w
    IMG4(i,j) = (1./(1 + (THRESHOLD./double(IMG1(i,j))).^E)) * 255;
    end

    end
    IMG4 = uint8(IMG4);
    subplot(224);imshow(IMG4);title('【4】对比度增强效果');
    • 仿真结果:

      matlab仿真结果1jpg

2.Gamma映射的FPGA实现

  • 与指数对比增强算法一样,使用查找表的方式,采用MATLAB生成Verilog映射的代码如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    % ----------------------------------------------------------------------
    fp_gray = fopen('.\Curve_Gamma_2P2.v','w');
    fprintf(fp_gray,'//Curve of Gamma = 2.2\n');
    fprintf(fp_gray,'module Curve_Gamma_2P2\n');
    fprintf(fp_gray,'(\n');
    fprintf(fp_gray,' input\t\t[7:0]\tPre_Data,\n');
    fprintf(fp_gray,' output\treg\t[7:0]\tPost_Data\n');
    fprintf(fp_gray,');\n\n');
    fprintf(fp_gray,'always@(*)\n');
    fprintf(fp_gray,'begin\n');
    fprintf(fp_gray,'\tcase(Pre_Data)\n');
    Gray_ARRAY = zeros(1,256);
    for i = 1 : 256
    Gray_ARRAY(1,i) = (255/255.^2.2)*(i-1).^2.2;
    Gray_ARRAY(1,i) = uint8(Gray_ARRAY(1,i));
    fprintf(fp_gray,'\t8''h%s : Post_Data = 8''h%s; \n',dec2hex(i-1,2), dec2hex(Gray_ARRAY(1,i),2));
    end
    fprintf(fp_gray,'\tendcase\n');
    fprintf(fp_gray,'end\n');
    fprintf(fp_gray,'\nendmodule\n');
    fclose(fp_gray);
  • 由于仿真方法跟指数对比增强算法一样,这里就不再赘述了


Reference

  • 《基于MATLAB与FPGA的图像处理教程》图书及其配套资料
欢迎来到ssy的世界