
田中太郎
メディアンフィルタ回路を実装して画像データに実行しました
全体構成
BMPの画像に3×1のメディアンフィルタをかけてresult.bmpの名前で保存します
画像データをテストベンチで読み込み回路に与えます
enableがHighのときのdinが有効入力画像データで、
validがHighのときのdoutが有効出力画像データです
画像の水平方向両端は注目画素を出力します

top.sv
main.svとctrl.svをインスタンスしています
module top #(
parameter IMG_W = 64,
parameter IMG_H = 64
)(
input clk,
input rst_n,
input enable,
input [7:0] din,
output logic valid,
output logic [7:0] dout,
output logic interrupt
);
localparam LATENCY = 1;
logic edge_en; // 画像の水平方向両端を知らせる
ctrl #(
.IMG_W(IMG_W),
.IMG_H(IMG_H),
.LATENCY(LATENCY)
) i_ctrl(
.clk(clk),
.rst_n(rst_n),
.enable(enable),
.edge_en(edge_en),
.valid(valid),
.interrupt(interrupt)
);
main #(
) i_main(
.clk(clk),
.rst_n(rst_n),
.edge_en(edge_en),
.din(din),
.dout(dout)
);
endmodule
main.sv
メディアン処理を行う回路です
dinを2回FFで保持して中央値をdoutとして出力します
また、ctrl.svから出力されるedge_enが入力されると、注目画素が出力されます
edge_enは画像の水平方向両端であることを示します
module main #(
)(
input clk,
input rst_n,
input [7:0] din,
input edge_en,
output logic [7:0] dout
);
logic [7:0] data_d1;
logic [7:0] data_d2;
// 画像データを2画素ぶん保持する
always_ff @(posedge clk, negedge rst_n) begin
if (!rst_n) begin
data_d1 <= '0;
data_d2 <= '0;
end
else begin
data_d1 <= din;
data_d2 <= data_d1;
end
end
// 3画素の中央値を選択する
logic [7:0] median;
always_comb begin
if (data_d1 > data_d2) begin
if (data_d2 > din) begin
median = data_d2;
end
else if (din > data_d1) begin
median = data_d1;
end
else begin
median = din;
end
end
else if (data_d2 > din) begin
if (din > data_d1) begin
median = din;
end
else begin
median = data_d1;
end
end
else begin
median = data_d2;
end
end
// 水平方向両端は注目画素、それ以外のときはメディアン値を出力する
always_ff @(posedge clk, negedge rst_n) begin
if (!rst_n) begin
dout <= '0;
end
else begin
if (edge_en) begin
dout <= data_d1;
end
else begin
dout <= median;
end
end
end
endmodule
ctrl.sv
制御回路です
enableがHighになったらcntが初期化されて1クロック毎にカウントアップします
main.svのレイテンシを鑑みてvalidをHighにします
validはdoutが有効画像データであることを知らせます
また、注目画素が水平方向両端であることをmain.svに知らせるためにedge_enを生成します
module ctrl #(
parameter IMG_W = 512,
parameter IMG_H = 512,
parameter LATENCY = 1
)(
input clk,
input rst_n,
input enable,
output logic edge_en,
output logic valid,
output logic interrupt
);
logic [18:0] cnt;
logic enable_d;
// cntを初期化するためにenableのエッジを検出
always_ff @(posedge clk, negedge rst_n) begin
if (!rst_n) begin
enable_d <= '0;
end
else begin
enable_d <= enable;
end
end
// enableのエッジで初期化するカウンタ
always_ff @(posedge clk, negedge rst_n) begin
if (!rst_n) begin
cnt <= '1;
end
else begin
if (enable & ~enable_d) begin
cnt <= '0;
end
else if (cnt < IMG_W*IMG_H + LATENCY) begin
cnt <= cnt + 1'b1;
end
end
end
// 水平方向両端でedge_enがHighになりmain.svに知らせる
always_comb begin
if (cnt == 0 || cnt%IMG_W == 0) begin
edge_en = 1'b1;
end
else begin
edge_en = 1'b0;
end
end
// 入力画像をすべて処理したらinterrupがHighになる
always_comb begin
if (cnt == IMG_W*IMG_H + LATENCY) begin
interrupt = '1;
end
else begin
interrupt = '0;
end
end
// 出力するデータが有効かどうかをテストベンチにしらせる
always_comb begin
if (LATENCY <= cnt && cnt < IMG_W*IMG_H + LATENCY) begin
valid = '1;
end
else begin
valid = '0;
end
end
endmodule
tb.sv(テストベンチ)
画像データを読み込んで回路に1画素ずつ送信します
回路から出力されるvalidがHighのときのdoutを有効画像としてファイル(result.bmp)に保存します
module tb;
initial begin
$dumpfile("wave.vcd");
$dumpvars(0, tb);
end
localparam IMG_W = 64;
localparam IMG_H = 64;
int f;
bit [7:0] bmp[54+1024];
bit [7:0] data[IMG_H*IMG_W];
logic [7:0] din;
logic [7:0] dout;
bit enable;
bit valid;
bit interrupt;
bit clk = 0;
bit rst_n = 0;
always #5 clk = ~clk;
initial begin
f = $fopen("sample.bmp", "rb"); // sample.bmpを読み込む
$fread(bmp, f, 0, 54+1024); // ヘッダを保持する
$fread(data, f, 0, IMG_H*IMG_W); // 画素データ
$fclose(f);
f = $fopen("result.bmp", "wb"); // result.bmpに書き込む
foreach (bmp[i]) // ヘッダを書き込む
$fwrite(f, "%c", bmp[i]);
#50 rst_n = 1;
#10;
enable = 1;
fork
foreach (data[i]) begin // 1クロック毎に1画素ずつ回路に送る
din = data[i];
@(posedge clk);
end
join_none
@(posedge clk);
enable = 0;
forever begin
if (valid) begin // validがHighのときのデータをファイルに書き込み
$fwrite(f, "%c", dout);
end
@(posedge clk);
if (interrupt) begin // interruptがHighになったらテスト終了
break;
end
end
$fclose(f);
$finish;
end
top #(
.IMG_W(IMG_W),
.IMG_H(IMG_H)
) i_top(
.clk(clk),
.rst_n(rst_n),
.enable(enable),
.din(din),
.valid(valid),
.dout(dout),
.interrupt(interrupt)
);
endmodule
入力画像

出力画像

まとめ
メディアンフィルタ回路を実装しました
テストベンチを作成して画像にメディアンフィルタをかけました
コメント