メディアンフィルタとは
メディアンという名の通り、中央値を利用した平滑化フィルタのことである。ノイズ除去に利用される。
ノイズを除去する方法には他に、平均化フィルタの利用などが挙げられる。
両者の最大の相違点はエッジが残るか残らないかだ。
平均化フィルタは画素値の合計をフィルタ数で割るため、エッジがぼやけてしまう。
しかし、メディアンフィルタではフィルタ内画素の中央値を利用するため、エッジが残りやすい。
画像のノイズ処理をしたい。だけどエッジはぼやけさせたくない。
そんな時に活躍するだろう使いやすそうなフィルタである。
#実装に至った動機
洗いざらいに話すとテスト対策。
開発環境・使用言語など
- 使用言語 : c++
- 開発環境 : Xcode version9.4
- Opnecvを利用。
#ソースコード
#define FILE_NAME "/*お好きなように*/"
#define M 1
//中央値を取得する関数
int getMedian(int array[], int num){
//最初に配列を並び替える maxソートで行った
int damy = num;
int i;
int v;
for(i=0; i<num; i++){
for(v=0; v<damy-1; v++){
if( array[v] < array[v+1]){
int tmp = array[v+1];
array[v+1] = array[v];
array[v] = tmp;
}
}
damy = damy - 1;
}
int median;
return median = array[num/2+1];
}
int main(int argc, const char * argv[]) {
cv::Mat src_img = cv::imread(FILE_NAME,0);
cv::Mat dst_img = cv::Mat(src_img.size(),CV_8UC1);
if(src_img.empty()){
cout << "File is not opened." << endl;
return -1;
}
//注目画素をフィルタの中央値で置き換える処理
int array[(M*2+1)*(M*2+1)] = {0};
for(int i=0; i<src_img.rows; i++){
for(int v=0; v<src_img.cols; v++){
//画像端は黒く塗りつぶす。
if( i < M || v < M || src_img.rows - 1 - M < i || src_img.cols - 1 - M < v){
dst_img.at<unsigned char>(i,v) = 0;
}else{
int count=0;
for(int y=(-M); y<=M; y++){
for(int x=(-M); x<=M; x++){
array[count] = src_img.at<unsigned char>(i+y,v+x);
count = count + 1;
}
}
//注目画素を中央値で置き換える
dst_img.at<unsigned char>(i,v) = getMedian(array, (M*2+1)*(M*2+1));
}
}
}
//画像の表示
cv::imshow("median", dst_img);
cv::imshow("input", src_img);
cv::waitKey();
return 0;
}
おそらく、中央値の求め方、画像端の処理の方法、フィルタの画素にどうアクセスするかがわかっていれば書けるだろう。
プログラムを組む過程においてつまづいたところはgetMedian関数のreturn値だ。
array[num/2];
これではいけなかった。
なぜならフィルタ配列の要素数は奇数であることが前提だからだ。
偶数であるならば問題ないが、奇数だと切り捨てが発生してしまう。
例えば9/2なら4だ。フィルタサイズが3x3だった時、中央値は5番目の要素の値になるのでこれでは間違っている。
正しくは上記プログラムの通り、こうだ。
array[num/2+1];
これでちゃんと中央値が取れる。
###出力画像はないの?
出力画像については著作権がどうなってるかわからないので、掲示しない方向で。
確認が取れたら載せようと思う。
#Opencvでメディアンフィルタ
一応Opencvのメディアンフィルタも載せておく。
cv::medianBlur(src_img, dst_img, filter_size);
引数はそれぞれ、入力画像、出力画像、フィルタサイズだ。
3x3のフィルタであれば、fileter_sizeは3となる。
#まとめ
- メディアンフィルタはエッジを残したまま平滑化できる。