Last modified 12 years ago Last modified on 11/23/05 17:31:14

MISTを用いた簡単な画像処理フィルタの実装

本チュートリアルでは,MISTのアルゴリズムを利用して,しきい値処理フィルタを実装してみましょう.

MISTでは,しきい値を決定するアルゴリズム(Pタイル法,判別分析法)の2種類を用意しています. これらのアルゴリズムは,しきい値を決定するだけなので,実際にしきい値処理を行う関数を実装してみたいと思います.

ヘッダファイルの準備

まずは,データを格納するMISTコンテナを準備しましょう.

#include <mist/mist.h>

さらに,今回はしきい値処理を行うために次のヘッダもインクルードします.

#include <mist/threshold.h>



テンプレート関数

今回実装するしきい値処理フィルタでは,入力となる画像がどのようなものかわかりません. つまり,どんなデータ型(double とか unsigned int とか・・・)の画像がくるかわからないということです. そこで,C++のテンプレート関数という機能を利用し,どんな画像がきてもちゃんと動作するようにしてみましょう.

MISTのコンテナを受け取るテンプレートの書き方には,大きく分けて3種類ほどあります.

  1. コンテナを指定して入力とする
    template< class T, class Allocator >
    void func( mist::array2< T, Allocator > &image ){ ... }
    
  2. MISTのどんなコンテナが来ても当てはまるようにする
    template< class T, class Allocator >
    void func( mist::array< T, Allocator > &image ){ ... }
    
  3. どんなコンテナが来ても当てはまるようにする
    template< class Array >
    void func( Array &image ){ ... }
    

ここで,3番目の書き方をした場合には,Array というものがどんな型でも受け付けてしまうため,関数 func を呼び出すときには注意が必要です. たとえば,

func( "あいうえお" );

という呼び出しも受け付けてしまうということです.

今回は,しきい値処理なので,何次元の画像が入力されても同じ処理で実現できるため,2番の方法で実装してみることにします. 関数の雛形はこんな感じになります.

template< class T, class Allocator >
void thresholding( mist::array< T, Allocator > &data )
{
    typedef typename mist::array< T, Allocator >::size_type  size_type;
    typedef typename mist::array< T, Allocator >::value_type value_type;
}

ここで,関数の内部で size_typevalue_type というものを定義しています. これは,MISTコンテナの要素の型を識別するために必要です.



しきい値処理関数の実装

テンプレート関数がどんなものかわかったら,実際にしきい値処理を行う関数を実装してみましょう. MISTで用意しているしきい値決定アルゴリズムのうち,今回は判別分析法を利用します. 判別分析法は,名前空間 mist::discriminant_analysis の中で次のように定義されています.

template < class T, class Allocator >
array< T, Allocator >::value_type threshold( const array< T, Allocator > &in )

要するに,何らかの音声・画像データを受け取り,判別分析法を行ってしきい値を返すというものです. それがわかれば,しきい値処理なんて簡単です. 入力された音声・画像データを入力して,しきい値を得ればいいわけなので,次のように書けます.

value_type th = mist::discriminant_analysis::threshold( data );

ここで,戻り値を value_type 型で受けていることに注意してください. これは,入力されるコンテナのデータ型がわからない(テンプレートなので,どんな型が入ってくるかわからない)ためです. そこで,こうしておくことによりコンテナのデータ型が識別できて,キャストをする必要がなくなります(というのも,しきい値選択のアルゴリズムの戻り値がそうなっているため).

あとは,このしきい値未満のデータを 0 にし,それ以外を 1 にすれば良いわけです. まとめると,次のような関数が出来上がります.

template< class T, class Allocator >
void thresholding( mist::array< T, Allocator > &data )
{
    typedef typename mist::array< T, Allocator >::size_type  size_type;
    typedef typename mist::array< T, Allocator >::value_type value_type;

    value_type th = mist::discriminant_analysis::threshold( data );

    for( size_type i = 0 ; i < data.size( ) ; i++ )
    {
        if( data[ i ] < th )
        {
            data[ i ] = 0;
        }
        else
        {
            data[ i ] = 1;
        }
    }
}



画像をしきい値処理してみよう

今回実装したしきい値処理関数を用いて,画像を2値化してみましょう. まずは,画像を読み込む処理が必要になるので,画像の入出力 を呼んで画像の入出力関数を実装してみましょう. ただし,入力画像をカラー画像として読み込むとエラーが出るので注意してください(というのも,カラー画像に対してしきい値を決定することができないためです). また,今回実装したしきい値処理関数の出力は 01 であるため,そのまま画像を保存しても何にも表示されないと思います. 今回は,画像を表示するために,しきい値処理の出力が 0255 になるようにしてみましょう!!

#include <iostream>

// MISTの一番メインとなるヘッダファイル
// 1・2・3次元画像を扱うコンテナが用意されてます
#include <mist/mist.h>

// しきい値選択を行うためのアルゴリズムが用意されてます
#include <mist/threshold.h>


// ファイルから画像を読み込んでMISTで提供するコンテナにデータを格納します
// サポートされている形式は
// JPEG, PNG, TIFF, BMP, PNM です
#include <mist/io/image.h>


template< class T, class Allocator >
void thresholding( mist::array< T, Allocator > &data )
{
    typedef typename mist::array< T, Allocator >::size_type  size_type;
    typedef typename mist::array< T, Allocator >::value_type value_type;

    value_type th = mist::discriminant_analysis::threshold( data );

    for( size_type i = 0 ; i < data.size( ) ; i++ )
    {
        if( data[ i ] < th )
        {
            data[ i ] = 0;
        }
        else
        {
            data[ i ] = 255;
        }
    }
}


int main( int argc, char *argv[ ] )
{
    if( argc < 2 )
    {
        std::cout << "入力画像のファイル名が必要です!!" << std::endl;
    }

    // mist::???? という風にMISTは ''mist'' という名前空間で囲まれています
    //     (C++の知識が必要?なければおまじないだと思って・・・)
    // MISTは全てC++のtemplateの機能を利用して作成されてます.
    // なので,2次元画像といってもいろんなデータ型が作成できます
    //     (例えばグレースケールだったりカラーだったり・・・).
    // まずは,画像のデータ形式がどんな物かを決めます(重要です!!)
    typedef unsigned char value_type;  // 画像の要素がRGBの3成分からなるようにします
    typedef mist::array2< value_type > image_type;  // RGBが要素となる2次元画像の設定

    image_type image; // 画像の実体を作ります(プログラムミスを考えて,newで確保するのは控えめに・・・).

    if( !mist::read_image( image, argv[ 1 ] ) )
    {
        // ファイルが読めないみたい・・・
        return( 1 );
    }


    // img に画像が読み込まれてるはずなので,なんか処理してください
    thresholding( image );


    mist::write_image( image, std::string( argv[ 1 ] ) + ".bmp" );

    return( 0 );
}