SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

DeveloperZine(デベロッパージン)- エンジニアの意思決定を支える技術情報メディア ProductZine

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

サーバサイドではじめるJavaグラフィック講座

Javaにおけるイメージのフィルタ処理

サーバサイドではじめるJavaグラフィック講座 4

ぼかし処理について

 フィルタの中には、周囲のドットの輝度との関係から新たな値を算出するものもあります。例えば、一般に「ぼかし」として使われるフィルタなどもその一つでしょう。ぼかしは、そのドットの周辺のドットの輝度を利用して新たな輝度を計算します。

 こうしたフィルタ処理に用いられるのがConvolveOpクラスです。これはKernelと呼ばれる行列データを使ってフィルタ処理を行うクラスです。

 ぼかしなどのフィルタは、一般に「畳み込み演算」と呼ばれる計算によって処理します。まず、あらかじめドットに隣接するドットの影響を示す係数を示す行列(実際には配列として扱われます)を用意しておきます。そしてドットの輝度を計算する際には、そのカーネルの情報を元に、周辺ドットの影響を計算して新たな値を算出します。

 例えば、こんな行列データがあったとしましょう。

 この中心となる値(0.2の位置)が対象となるドットの係数となり、それ以外はその隣接ドットの係数となります。新しい輝度は、対象ドットと隣接ドットの輝度にそれぞれ対応する係数を乗算した値の合計となります。それぞれのドットの輝度を次のように考えてみましょう。

 ここで、中心であるy2の新しい輝度というのは、次のような計算によって求められることになります。

result = x1 * 0.1 + x2 * 0.1 + x3 * 0.1
       + y1 * 0.1 + y2 * 0.2 + y3 * 0.1
       + z1 * 0.1 + z2 * 0.1 + z3 * 0.1

 この「行列データをもとに隣接ドットの輝度を乗算した値の合計」によって新しいドットの輝度が計算されるわけです。

 この基本的な考え方が分かれば、ConvolveOpによるフィルタは非常に面白い効果を作り出すことができます。まず、「ぼかし」のフィルタから考えてみましょう。

public BufferedImage getModifiedImage(BufferedImage im) {
    float[] data = new float[]{
            1/25f,1/25f,1/25f,1/25f,1/25f,
            1/25f,1/25f,1/25f,1/25f,1/25f,
            1/25f,1/25f,1/25f,1/25f,1/25f,
            1/25f,1/25f,1/25f,1/25f,1/25f,
            1/25f,1/25f,1/25f,1/25f,1/25f
    };
    
    int w1 = im.getWidth(), h1 = im.getHeight();
    BufferedImage im2 = new BufferedImage(w1, h1, im.getType());

    Kernel kernel = new Kernel(5,5,data);
    ConvolveOp op = new ConvolveOp(kernel);
    op.filter(im, im2);

    return im2;
}
イメージにぼかし処理をする
イメージにぼかし処理をする

 これは、5×5の領域内のドットの平均を新たな輝度として設定するようにしたものです。こうすることで、周辺ドットの平均値が新たな輝度となり、結果、全体として輝度が平均化された、いわゆる「ぼかし」がかかった状態になります。

 ConvolveOpクラスは、行列データであるカーネルKernelクラスを使って作成します。最初に次のようにしてあらかじめKernelを作成しておきます。

Kernel kernel = new Kernel(横数, 縦数, float配列);

 注意しておきたいのは、行列データである配列は、2次元配列などではなく1次元の配列である、という点です。配列は、周辺ドットの「左上から横方向へ」順に値をまとめた形で用意します。またドットの範囲を示すための「縦横、何ドット幅のデータか?」は別に引数で指定をします。こうして作成したKernelを引数にしてConvolveOpを作成し、後は今までと同様にfilterでフィルタ処理を行えばOKです。

さまざまなフィルタを考える

 Kernelの作成さえできるようになれば、さまざまなフィルタを作成することができます。例えば、輝度の違いを明確にするシャープネス処理ならば、float配列を次のような形で用意すればよいでしょう。

float[] data = new float[]{
        -0.1f,-0.1f,-0.1f,-0.1f,-0.1f,
        -0.1f,-0.1f,-0.1f,-0.1f,-0.1f,
        -0.1f,-0.1f,3.4f,-0.1f,-0.1f,
        -0.1f,-0.1f,-0.1f,-0.1f,-0.1f,
        -0.1f,-0.1f,-0.1f,-0.1f,-0.1f
};
イメージをシャープネス処理する
イメージをシャープネス処理する

 シャープネスは、周辺ドットとの輝度の差をより大きくする処理です。従って、周辺ドットの値をマイナス値として得、対象ドットのプラスの値とあわせて新たな値を設定するようにすれば、周囲が明るく対象ドットが暗い場合はより暗く、周囲が暗く対象ドットが明るい場合はより明るく変更されるようになるでしょう。

 また、よくある「モーションブロア」(高速で動いているような効果を出すぼかし)などは、動かす方向のドットの輝度を平均化するようにカーネルを用意すればよいでしょう。例えば横方向にモーションをつけるならば、横一列のカーネルを用意すればできるようになります。

float[] data = new float[]{
        0.1f,0.1f,0.1f,0.1f,0.1f,
        0.1f,0.1f,0.1f,0.1f,0.1f
};
横方向にモーション処理をする
横方向にモーション処理をする

 このような感じです。ここでは横方向に10個のドットの平均をとるようにしてあります。こうしてKernel作成の際に、縦横の引数を横10、縦1に設定するわけです。

Kernel kernel = new Kernel(10,1,data);

 配列の値をいろいろと調整してみると、効果がよく分かるでしょう。配列を作成するとき、ポイントは「全要素の合計値が1になるようにする」という点です。そうすれば、イメージ全体の輝度はほぼ変わりません。これが1以上になると明るく、1以下だと暗くなってしまうので注意が必要です。

まとめ

 フィルタ処理が行えるようになると、イメージの処理はかなり幅が広がってきます。例えば、テキストを入力するとバナーイメージを自動的に作成するようなサイトがありますが、こうしたイメージを生成するサイト作りに威力を発揮することでしょう。

 フィルタ処理は、使用する係数や配列の値などの調整によってかなり作成されるイメージが変わってきます。これらは実際にいろいろと試してやってみるしかありません。ここであげたサンプルを元に、それぞれで応用してみてください。

この記事は参考になりましたか?

連載通知を行うには会員登録(無料)が必要です。
既に会員の方はを行ってください。
サーバサイドではじめるJavaグラフィック講座連載記事一覧

もっと読む

この記事の著者

掌田 津耶乃(ショウダ ツヤノ)

三文ライター&三流プログラマ。主にビギナーに向けたプログラミング関連の執筆を中心に活動している。※現在、入門ドキュメントサイト「libro」、カード型学習サイト「CARD.tuyano.com」を公開...

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

CodeZine(コードジン)
https://codezine.jp/article/detail/1145 2007/04/06 08:00

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー