メモ置き場

メモ置き場です.開発したものや調べたことについて書きます.

[tex: ]

メディアン・フィルタ2: Verilog HDLでの実装

FPGA(Nexys4 DDR)を使ったリアルタイムエッジ検出 - メモ置き場
では,カメラから取り込んだ画像の平滑化としてメディアン・フィルタを使っている.前回に引き続きVerilog HDLで実装した方法を書いておく.

初めに

メディアンフィルタとは,中心の画素の値を領域に含まれる画素の中央値に置き換える,というものであった.
HDLで実装するには,与えられた画素(9個,25個,49個,…)をソートしてその中央値を返す仕組みが必要となる.

ソフトウェア的にはソートアルゴリズムの実装は簡単であるが,ハードウェアとしてやるには少し工夫が必要である.
今回は3×3のメディアン・フィルタを実装するので,9個の値から中央値を選び出すような回路を作成したい.
さらに欲を言うと,レイテンシ0で中央値を算出してくれる回路を作りたい.

調べてみると,次のような文献が見つかった:
3×3メディアンフィルタの高速アルゴリズム(浜村倫行, 2002)
この文献を元に実装する.具体的にざっくり説明すると,
1. 入力データを適当に3個ずつ3組に分け,それぞれの組のなかでソートする.
2. 3組をソートする.基準は,各組の中央値の大きい順とする.大きい順にA,B,C組とする.
3. Aの最小値,Bの中央値,Cの最大値を比較すると,9個の数の中央値がわかる
ということをやっている.結局3個の数のソートをVerilog HDLで実装すればいいわけだ.
今回はレイテンシを0にしたいので,Verilog HDLの実装は全て組み合わせ回路で記述する.具体的にどう実装したかについて説明する.

Verilogでの実装

3つの値のソートなので, _{3}C_{2} = 3通りの値比較をして,その結果を元にソートした値を出力する,と言う方針でいく.入力される値を{p0, p1, p2}として

wire [2:0] switch = {( (p0<p1) ? 1'b1 : 1'b0), ( (p0<p2) ? 1'b1 : 1'b0), ( (p1<p2) ? 1'b1 : 1'b0)}

とすればswitchの値をみることでp0, p1, p2の大小関係がわかるようになる.例えばswich = 3'b011{p0 \ge p1,   p0 \lt p2,  p1 \lt p2}なので{p1 \le p0 \lt p2}となる.
したがって,switchの値でセレクタを書けば良い.今回は組み合わせ回路を実装するので,次のようにfunctionを使った.sort_3data_funcは与えられたデータを(小,中,大)の信号線にまとめてくれる.

parameter DATA_WIDTH = 8;
function [DATA_WIDTH*3-1:0] sort_3data_func;
    input wire [DATA_WIDTH-1:0] p0,p1,p2;
    begin
    // swich shows the order of 3 inputs.
    // swich = 3'b011 means p0>=p1, p0<p2, p1<p2
    // so the order is p1 < p0 < p2
    
    case(switch)
        3'b_111 : sort_3data_func = {p0,p1,p2};
        // p0 < p1 < p2 case
        
        3'b_110 : sort_3data_func = {p0,p2,p1};
        // p0 < p2 < p1 case
        
        3'b_011 : sort_3data_func = {p1,p0,p2};
        // p1 < p0 < p2 case
        
        3'b_001 : sort_3data_func = {p1,p2,p0};
        // p1 < p2 < p0 case
        
        3'b_100 : sort_3data_func = {p2,p0,p1};
        // p2 < p0 < p1 case
        
        3'b_000 : sort_3data_func = {p2,p1,p0};
        // p2 < p1 < p0 case
        default : sort_3data_func = {p0,p1,p2};
    endcase
    end
endfunction

同様に3組のソートも行うfunctionを書き,中央値を出してくれるmoduleを作った.コード全体はgit repositoryのov7670_sobel.srcs/sources_1/new/median_filter.v である.

moduleのテスト

moduleのテストをVivadoのbehavior simulationで行なった.テスト用の入力パターンと対応する中央値はC++で作成した.
シミュレーション結果の波形を示す.
f:id:okchan08:20190119133244p:plain

d0〜d8が与えた9つの値.ansが事前に計算した出力されるべき中央値で,d_outが実際にモジュールのシミュレーションで出力された中央値である.
レイテンシ0で正しく中央値を吐き出していることが確認できた.