C++で畳み込み演算を実装してみた
自分でフィルタを作ってみようと思った時
OpenCVだとfilter2Dとかいう神関数があるんだけど
それじゃなんというか糞っぽい感じがした
ので自分で畳み込みしてフィルタリングできるように、畳み込みのひな形を作ってみた。
とりあえず畳み込みとは
wikipediaによると、「関数fを平行移動しながらgを重ねて足し合わせること」らしい
実際この通り。
重ねる→乗算
足し合わせる→加算
が連想されるよね
だから h(t) = f(0) * g(t-0) + f(1) * g(t-1) … + f(n) + g(t-n) って感じ?
例題で示すと
f = { 1, 0, 2}
g = { 2, 4, 5, 3, 4}
とかだったら
(わかりやすいようにfの順番を逆転した)
f = { 2, 0, 1}
g = { 2, 4, 5, 3, 4 }
同じ列にあるものをかけて、足すので
h(0) = 1 * 2 = 2
f = { 2, 0, 1 }
g = { 2, 4, 5, 3, 4 }
h(1) = 0 * 2 + 1 * 4 = 4
f = { 2, 0, 1 }
g = { 2, 4, 5, 3, 4 }
h(2) = 2 * 2 + 0 * 4 + 1 * 5 = 9
みたいな?f に対してg を動かしていく感じ。
当然画像にフィルタを掛けるために関数を書くので、2次元配列にする
んでもってだいたいのフィルタは3 × 3の配列で収まるので、そんな感じでフィルタを定義することにした。
あ、いまさらやり直すのはめんどくさいけど、
畳み込みのところで16に割ってるの、フィルタの中の総和を1にしたいから
適当なフィルタかける時はちょっとググってもらいたい
多分このコードでかかってるのはガウシアンフィルタ
OpenCVだとfilter2Dとかいう神関数があるんだけど
それじゃなんというか糞っぽい感じがした
ので自分で畳み込みしてフィルタリングできるように、畳み込みのひな形を作ってみた。
とりあえず畳み込みとは
wikipediaによると、「関数fを平行移動しながらgを重ねて足し合わせること」らしい
実際この通り。
重ねる→乗算
足し合わせる→加算
が連想されるよね
だから h(t) = f(0) * g(t-0) + f(1) * g(t-1) … + f(n) + g(t-n) って感じ?
例題で示すと
f = { 1, 0, 2}
g = { 2, 4, 5, 3, 4}
とかだったら
(わかりやすいようにfの順番を逆転した)
f = { 2, 0, 1}
g = { 2, 4, 5, 3, 4 }
同じ列にあるものをかけて、足すので
h(0) = 1 * 2 = 2
f = { 2, 0, 1 }
g = { 2, 4, 5, 3, 4 }
h(1) = 0 * 2 + 1 * 4 = 4
f = { 2, 0, 1 }
g = { 2, 4, 5, 3, 4 }
h(2) = 2 * 2 + 0 * 4 + 1 * 5 = 9
みたいな?f に対してg を動かしていく感じ。
当然画像にフィルタを掛けるために関数を書くので、2次元配列にする
んでもってだいたいのフィルタは3 × 3の配列で収まるので、そんな感じでフィルタを定義することにした。
#include <iostream>
#include <stdlib.h>
#define N 7
using namespace std;
int main(int argc, char *argv[])
{
// フィルタ
float filter[3][3] = {
{ 1, 2, 1 },
{ 2, 4, 2 },
{ 1, 2, 1 }
};
// 畳み込まれる2次元配列
float src[N][N];
for(int i = 0; i < N; i++){
for(int j = 0; j < N; j++){
src[j][i] = rand()%5+1;
}
}
// 結果
float result[N][N];
for(int i = 0; i < N-2; i++){
for(int j = 0; j < N-2; j++){
result[j][i] = 0;
}
}
// 畳み込み
for(int i = 1; i < N-1; i++){
for(int j = 1; j < N-1; j++){
for(int k = -1; k <= 1; k++){
for(int l = -1; l <= 1; l++){
result[j-1][i-1] += filter[l+1][k+1]/16*src[j+l][i+k];
}
cout<<endl;
}
}
}
// 結果の表示
cout<<"result"<<endl;
for(int i = 1; i < N-1; i++){
for(int j = 1; j < N-1; j++)
cout<<result[j-1][i-1]<<" ";
cout<<endl;
}
return 0;
}
あ、いまさらやり直すのはめんどくさいけど、
畳み込みのところで16に割ってるの、フィルタの中の総和を1にしたいから
適当なフィルタかける時はちょっとググってもらいたい
多分このコードでかかってるのはガウシアンフィルタ
スポンサーサイト