TabIe 2 0 「 acle で利用できるデータ型 ァータ CHA 日 ( サイズ ) VA 日 CHA 日 2 ( サイズ ) NUMBER( 総桁数 , 小数部桁数 ) DATE BLOB 日 AW ( サイズ ) LONG ない項目のことです。つまり , これを決定 プルを作成するための SQL 文は次のように ープルを作成しておきましよう。このテー では , SQL * Plus などを使用してこのテ 主キーにすることにしました。 すが , 今回は簡単のため「ソフト名」のみを 項目をまとめて主キーとすることも可能で 設定すべきと思います。具体的には複数の あってもいいことはないと思うので , 常に は , 重複レコードが発生することは , 害は 複数発生することになります。私の経験で しないと , まったく同じ内容のレコードが なります。 create tab 厄 GAME—DATA ( TabIe 3 GAME_DATA テーブル 明 2000 文字以下の固定長文字列 4000 文字以下の可変長文字列 数値データ 日付データ 4G バイト以下のバイナリテータ 2000 バイト以下のバイナリデータ 2G バイト以下の可変長文字列 プページの HTML になります。このページ ーリンクから「データ入力」 , 「デー のハイノヾ タ参照・編集」のページを出力する CGI を起 動できるようになっています。 List3 はデータベースの接続を行う処理を 簡単に記述できるようにするため , 関数 ( K M_ConnectOracIe) を定義しています。デ 項目名項目内容 NAME ソフト名 COMPANY 会社名 R_DATE 発売日 データ型 文字列型 64 バイト 文字列型 32 バイト 日付型 ータベースの接続時には環境変数の設定や ユーザ名 , パスワードの指定などがあるた め , このように他のファイルに関数として 記述していると便利です。 List4 は「データ入力」ページの処理を行 う CGI です。まず , 最初のほうで読み込ん でいる「 mperLlib. pl 」という見慣れないファ イルがあります。これは PO もしくは GE T されたデータを受け取る処理を行うモジ ュールです。 require した後で , 呼び出して NAME COMPANY R—DATE 文字コード varchar ( 64 ) primary key, date varchar ( 32 ) , List 2 ゲームソフトデータベースのトップページ (index. html) <html> く head> <meta http-equiv="Content-Type content= "text /htm 嶹 charset=nshift—j 土 s ” > く t 土凵 e 〉ゲームソフトデータベースサンプルく / ti 凵 e > く /head> く body bgcolor="#ffffff" し ext = ” # 000055 ”〉 く hl > ゲームソフトく br> データベースサンプルく / hl > <u くは > く a href=mregist.cgi"> データ入力く / く / は > くは > く a href="data ・ cgi"> テータ参照・編集く / a > く / は〉 く / u く div a は gn = ″て土 ght ” > & copy 2001 KIT-M く br» <a href="mai Ito:masa26@naa.att.ne ・ jp">masa26@naa.att.ne.jpく/diV> く /bodY> く /html> List 3 データベースへの接続処理用関数 ( connect ー db. pl) # CONNECT DB ー Oracle データベースへの接続処理 by KIT-M あともう 1 つ決定しておきたいことがあ ります。データベースに格納する文字列型 データの文字コードについてです。日本語 を格納することになるので , たいてい EUC コードか Shift JIS コードのどちらかになる と思います。本来 UNIX は EUC コードのほ うが相性がいいと思うので EUC を選択すべ きですが , 今回は ShiftJIS コードを使用し たいと思います。理由は私の好みです。 ・・・すみません ( ^ ^ ; 。なお , 今回は必要あり ませんが , もし文字列コードの変換が必要 な場合はその用途によく利用される n 灯など のライプラリを使用すれば簡単に行えます。 ソース解説 List 2 はゲームソフトデータベースのトッ 実際のソースを List2 ~ 5 へ示します。 1 20 C MAGAZINE 2001 7 use DBI; $ENV{'ORACLE-HOME') $ENV{ 'NLS-LANG') = '/export/home/oracle'; = 'japanese—japan. ja16sjis'; # 文字コードは SJIS # ' japanese—japan. ja16euc'; # EUC コードの場合はこれ $ENV{ 'LD—LIBRARY-PATH' } '/export/home/oracle/lib'; # LINUX 用 $dbh $sth # KM—ConnectOracIe - Oracle DB への接続 sub KM—ConnectOrac 厄 } # KM—ConnectOracl e ex 辻 ( 1 print STDERR. $s; $userid, "$DBI: :errstrn ・ ま j0 土れ ' if ($dbh = {P て intError = > 0 , AutoCommit => 0 } ) Ⅱ warn ”基 n ・ ' Nek0 ' 'KIT—M' $dbh = DBI->connect( 'dbi:Oracle:
末尾から前に向かってたどっていくしかな いのです。つまり , リストはテータを飛び 飛びにアクセスするのには向いていませ ん。ー連のデータを順番にたどっていくこ としかできないのです。ですから , やはり " リストは配列よりも優れている ! " などと は言えないわけです。飛び飛びのデータに ランダムにアクセスしたり , 何度も前後を 行ったり来たりする場合は配列がよいでし ようし , そうでなければリストを使って楽 にコーディングするチャンスがあるかもし れません。 [ 注 4 ] 「 a ay [ 5 ] 」というのは「 * (array + 5 ) 」と まったく等価になります。要するに " array が配列の先頭を指すポインタで , そこから 5 っ先のメモリ位置にアクセス する”という意味です。リストはメモリ 内でバラバラな位置に格納されているの で , リストの先頭から 5 っ先のメモリ位 置には全然関係のないデータが入ってい るのです。 応用例・自己組織化探索 さて , 前回で探索を取り上げたときに 「自己組織化探索」という方法を紹介しまし た。データの探し方はリニアサーチと変わ りませんが , 毎回見つかった項目を先頭方 向に移動してくることで , 頻繁に検索され る値が先頭付近に集まり , 結果としてリニ アサーチによる検索が効率化される , とい うものです。 前回紹介した配列版自己組織化探索では , Fig. 7 のように見つかったデータを 1 つ前に 移動していくだけでした。本当は Fig. 8 の ように見つかったデータをいっきに先頭ま で移動させられれば理想的です。なぜそれ ができなかったのか。 List 6 を見てくださ い。この処理だけで計算量オーダが 0 ( N ) になってしまうため , データ量が多くなれ ば効率化のための工夫がかえって逆効果に なってしまうのです。 これに対し , 先ほど述べたように , デー タを頻繁に削除・挿入する場合にはリスト 構造が強力な武器になります。自己組織化 探索をリスト構造を用いて実装すると , デ 82 C MAGAZINE 2 1 7 リストの中のテータの検索とデータの削除 #include く s セ d は b. # incl ude く stdio. い typedef struct tagListNode 。。 / * リストの要素 ( ノード ) を表す構造体 * / int data; / * この要素が持っているデータ * / struct tagListNode *next ー / * 次の要素へのポインタ * / struct tagListNode *prev; / * 前の要素へのポインタ * / } ListNode; 土 n し main(int argc, char* argv[ ] ) int buf; ListNode *firstnode, *newnode, firstnode = lastnode NULL; * thisnode , *removenode ー printf ( ”整数を入力して下さい ( 0 を入力すると終了 ) : ” scanf げ %d", &buf if(buf) / * 新たな入力があったら * / / * 新しいノードを作成 * / newnode = (ListNode*)malloc(sizeof(ListNode) ); if(buf ! = 0 ) scanf( "%d", &buf p て i f ( ″検索する値を入力して下さい : / * 検索値を入力 * / } while(buf ! = 0 newnode—>prev = NULL ・ firstnode = lastnode = / * これが最初の要素だった場合 * / lastnode = newnode; newnode—>prev = lastnode; ー astnode—>next = newnode ー / * 既にあるリストの末尾に新しいノードをつなげる * / if い astnode ! = NULL ) newnode->next = NULL ・ newnode->data = buf; if(thåsnode = = ) break; free ( thisnode lastnode = thisnode->prev ー thisnode->next—>prev = thisnode->P て ev; if(thisnode->next ! = NULL) firstnode = thisnode->next; thi snode->prev->next = thisnode—>next; if(thisnode->prev ! = NULL) p て土 n ( ”入力された値の中に宅 d が見付かりました。ノードを削除します。” , buf); if ( thisnode->data = = buf ) for ( thisnode = firstnode; thisnode ! = NULL; thisnode = thisnode-»next ) / * 最初に入力した値の中から検索し、見付かったら削除 * / prin し f ( d は入力されていないか、あるいは既に削除されています。″ , buf); て eturn EXIT—SUCCESS ー free ( removenode 潺 thisnode = thisnode->next ー removenode = thi snode ー for(thisnode = firstnode; thisnode ! = NULL; ) / * 残ったノードを全て削除 * / } while(buf ! = 0
画像データの圧縮 画像データに限らずデータの圧縮を行う には , 元の情報に「統計的な偏り」がなけれ ばなりません。「統計的な偏り」とは , ある データ値がほかのデータ値に比べて登場頻 度が大きい , データの並び方に特徴がある などの状態を指します。ですから , 完全に ランダムに並んだデータは , どうやっても 圧縮することはできません。このように圧 縮の効果が期待しにくいデータを「冗長性 の低い」データといいます。ただ , 画像デ ータは , 一般的に「冗長性の高い」データで あることが多いため , 何かしらの圧縮方法 により , データ量の削減を図れます ( 冗長 性については , 情報理論 ( シャノン理論 ) の 中で数学的に述べられています。興味のあ る人は文献をあたってみてください ) 。 また画像データには , 一般のデータとは 異なる特徴が 1 つあります。それは , デー タの一部が変わってしまった場合でも , 元 の情報をある程度保持できる点です。文書 などのデータでは , たとえー部でもデータ が変わってしまうことは許されません。変 わってしまった場合 , そのデータそのもの の価値がすべて失われてしまう可能性もあ ります。しかし , 画像データでは , 1 画素 の値が変わってしまってもあまり問題には なりません。 1 画素だけでなく全体の画素 値が多少変化したとしても , その差が 1 , 2 程度であれば , その差異を見つけること Fig. 3 ランレングスを示すコードが出現し たときの処理 元データ FF 0xFF 1 」 テータ長が 1 であっても , ランレングス符号と して出力する。この場合 , 元のデータよりも大 きくなってしまう は難しいでしよう。つまり , 画像データの 圧縮では , 元のデータをある程度変更して 圧縮しやすくすることが許されていること よって , 画像データの圧縮方法には大き になります。 Fig. 4 フルカラー画像の場合のデータ構成方法 圧縮法も成り立っことになります。可逆圧 完全に損なうことはないため , このような ータはデータが多少変化してもその価値を まいます。しかし , 前述のとおり , 画像デ 縮は , 圧縮を行うと元データが変化してし 性の高いデータだけです。一方 , 非可逆圧 を行います。可逆圧縮が行えるのは , 冗長 逆圧縮は元のデータを損なうことなく圧縮 それぞれの違いは名前が示すとおりで , 可 1 つは可逆圧縮 , もう 1 つは非可逆圧縮です。 く分けて 2 種類が存在することになります。 縮は , 画像データに限らず , 文書・プログ ラムなど , あらゆるデータを対象にするこ とができます ( ただし , 元データの統計的 性質によって , 有効に利用できるアルゴリ ズムは変わってきます ) 。一方 , 非可逆圧 縮が適用できるのは , 画像・音声など一部 のデータに限られます。 可逆圧縮と非可逆圧縮とでは , 目的が大 きく異なるといえるでしよう。可逆圧縮は , データサイズが多少大きくても , 元のデー タを損なうことがないようにしたい場合に 利用します。非可逆圧縮はその逆で , 元デ ータの品質が劣化しても , とにかくデータ 量を減らしたい場合に利用します。 一般的に非可逆圧縮のほうが圧縮率は高 くなります。非可逆圧縮は , 元のデータが お、分デをタ 同じ色成分のデータだけを集めていく Pmacs 画像出力プラグイン Pm acs は主だった機能をすべてプラグ インで実現しているグラフィックエデイタ ですが物そのひとつに画像出力用のプラグ インがあります。このプラグインは D 旧形 式の画像データを与えると , 対応する画像 形式でファイルに保存する機能を持ってい ます。 Pm acs 画像出力プラグインは , いくつ かの関数から成り立っています。このプラ グインの仕様は以下のとおりですが , 基本 的な部分は画像ビューアで有名な「 susie 」 のプラグインの仕様がべースになっている ので , Susie プラグインの仕様を理解して いる方なら理解は容易だと思います。 画像出力を行う関数は W ⅲ ePicture です。 そのほかの関数は , プラグインを利用する アプリケーションに向けて , プラグイン情 報を提供するためのものです。この中でア プリケーションにとってとくに必要なの は , Suppo mage によって提供されるサ ポートしている色数の情報です。画像フォ ーマットによってはフルカラーのみ対応し ているものや 256 色の画像のみサポートし ているものなどがあるので , プラグインに D 旧形式のデータを渡す際に , 適切な色数 のデータを渡す必要があります。 TabIe A に Pmacs 画像出力プラグインを 構成している関数とその仕様の説明を掲載 します。 パラメータの仕組みを含め , より詳しい 情報がほしい方は , http:″www.asahi-net.or.jprgv9k-setg/ mado. html を参照してください。 1 0 イ C MAGAZINE 2001 7
カリ 画像処理を極める 変化しても圧縮率を高めようという考えで 行うのですから , 可逆圧縮のほうが圧縮率 が高いならば , 非可逆圧縮の存在価値は皆 無ということになります。 TabIe A Pmacs 画像出力プラグインの関数 int GetAddinInfo(int infon. LPSTR st 「 , int st 「 I) : 簡単な圧縮アルゴリズム ~ ランレングス符号化 画像に対するデータ圧縮法の中で , もっ Add - in に関する情報を返す。この関数の仕様は Susie 曰 ug - in と基本的に同じだが , 引数によってプラグイン名 , 対応する画像フォーマットの拡張子などを返す。主 にプラグインを利用するアプリケーションにプラグイン情報を与えるために利用 引 数 する infono str strl 取得する情報番号 PIug-inAPlJéジョン 0 Plug-in 名 , バージョンおよび copy 「 ight 1 2n + 2 代表的な拡張子 (" *. JPG"" * . RGB"" *. 00 " など ) 2n + 3 ファイル形式名 情報を納めるバッファ バッファ長 ( byte ) 戻り値 バッフアに書き込んだ文字数 ( 情報番号が無効の場合 0 ) int Supportlmage(void) : 内容 戻り値 プラグインを利用するアプリケーションのための情報を与える。アプリケーショ ンはここで得られた情報を元に適切な色数の画像データを用意しプラグインに 渡す必要がある サポートしている色数の情報。プラグインがサポートしている色数 ( 1 画素あたり のビット数 ) の情報を返す。以下の定数が定義されているので , これらの O 日をと った値を返す マクロ SUPPORT_32 SUPPORT_24 SUPPORT_16 SUPPORT_8 SUPPORT_4 SUPPORT_2 SUPPORT_I 実際の値 ② ( 8 ) ( 16 ) ( 32 ) ( 64 ) 256 65536 Full Colo 「 Full Color 色数 2 4 int W 「 itePictu 「 e(LPSTR *fname, unsigned int flag. BITMAPINFO * pHBlnfo, void * pHBm, FARPROC lpP 「 gressCallback, long IData,int a 「 gc, cha 「 *argv[ ] ) : 内 引 画像をファイルに出力する。この関数がプラグインのメインとなる。この関数に 下記の引数のように D 旧形式で収納された画像データのヘッダ , およびビットマ ップテータを引き渡すと , 対応する画像形式でファイルに保存する。画像のセー ブでは , 圧縮率の指定などバラメータを与える必要があるが , Pmacs プラグイン ではこのようなバラメータを最後の 2 つの引数 , argc, a 「 gv によって与える。こ れは C 言語の main 関数と同じで , バラメータの数とバラメータの入ったメモリ領 域へのポインタを表している ( すべてのパラメータは文字列で与えられる ) 。この 関数の先頭部分ではこのバラメータを解釈して指示されたとおりにセーブできる ようにする必要がある 数 fname flag pHBInfo pHBm lpPrgressCaIIback lData argc argv Save するファイル名 ( フルバス ) 追加情報 ( 現状意味なし ) 画像データ ( D 旧 ) ヘッダ情報へのポインタ 画像データ本体へのポインタ 途中経過を表示するコールバック関数へのポインタ。 仕様は Susie のものと同じ コールバック関数に渡す long データ。ポインタなどを 必要に応じて受け渡せる 引き渡しパラメータの数 引き渡しバラメータ 戻り値 0 : 正常終了 , それ以外はエラー ( 工ラー値は Susie プラグインに準拠 ) とも簡単なもののひとつにランレングス符 号化があります。ランレングス符号化は , データ中に同じ値のデータが並んでいる場 合 , その並びの数を記録していきます。た とえば , Fig. 1 のようにデータが並んでい る場合 , その下のように符号化を行います。 このようにすれば , 同じ値が固まって存在 するようなデータの場合 , データ量が少な くなることが期待できます。 ランレングス符号化は基本的に可逆圧縮 による圧縮方法で , データの「同じ値の部 分が偏って存在する」という統計的性質を 元に圧縮を行います。このようなデータは 画像データ以外にはあまり存在しないので , ランレングス符号化は画像データ以外には 適用されることは少ないでしよう。逆にい えば , 画像データであっても , 「同じ値の 部分が偏って存在する」という特徴を持っ ていない画像データには適用できないこと になります。写真などの自然画像では , 画 素値は激しく変化しているので , ランレン グス符号化の適用は困難です。 ランレングス符号化は非常に簡単なアル ゴリズムですが , それでもいくつかのバリ 工ーションが考えられます。ここでは , デ ータの表現方法に関して , いくつかのバリ 工ーションを考えてみます。まず , それぞ れのパターンによる圧縮結果を Fig. 2 に示 します。この結果と以下の説明を見比べな がら読み進めてください。なお , 以下の説 明中で「データ長」とは , 同じ画素値が並ん でいる部分の長さを指すものとします。 ①テータ値 + データ長の並びで表現する このパターンでは , データの並びは , 常 にデータ値とその並びのデータ長をひとま とめにして表現していきます。このパター ンはデータ構造が非常に単純なので , 実装 が簡単です。しかし , たとえ 1 バイト長の データがあったとしても必ず 2 バイトを利 用するため , 最悪のケースでは元のデータ 量の 2 倍に増えてしまうことがあります。 そこまでいかなくても , 変化の激しいデー タの場合 , データ量が逆に増えてしまう可 能性があります。ランレングス符号化は本 画像処理を極めるアルゴリズムラボ 105
アルゴリズム 、入門 フログラミン ~ 箱 たくさんのデータを格納するために , 「配列」が配列に少し似ている , 強烈に便利な " データ構 厘です。リストを使いこなせるようになると , よく使われます。けれども , 配列にも不便な点 " がいくつかあります。今回紹介する「リスト」は , データのかたまりの扱いが楽になります。 リストとデータ構造 第 3 回 春日イ申弥 BorIand c + + 5.5.1 compiler 日本語版収録 紀平拓男 協力 : ポーランド ( 株 ) ( 有 ) スウイフト : りません。今回の「リスト」 , 次回以降で紹 上に , 非常に便利なテクニックです。今回 ァータ構造” ? 66 ーヾ のテーマ「リスト」にいたっては , 知らなけ 介する「スタック」「キュー」 , そして「マッ プ」「ツリ れば損をする ! と断言してもよいくらいか なんていわれても , 配 前回・前々回と , 探索や並べ替えといっ 列しか知らない人にとっては「配列のほか もしれません。 た、、アルゴリズム ( 方法 ) " を解説してきまし にいったいどんな方法があるっていうの ? 」 いくつあるかわからない た。今回は少し毛色を変えて , 「リスト」と と , なかなか想像がつきにくいかもしれま データ いう " データ構造 " を紹介します。 ・・いえいえ , いかめしい名前を聞 せん。 今回は抽象的なことを言っていても始ま 大量のデータがあるときにそれを格納 ( 記 いて緊張することはありません。どれもと らないので , いきなり具体例を出してしま てもシンプルな考え方です。そしてそれ以 憶 ) しておく方法は , 何も配列だけではあ 入力したいくつかの数値とその合計を出力する 入力する数値の数を最初に確認する # 土 n 引 ude く stdio. h> #include <stdio. h> #include «stdlib. h> #include <stdlib. h> int main(int argc, char* argv[ ] ) #define NMAX 10 int main ( int argc , char* argv [ ] ) int buf, sum, count' n; int *array ー int buf, sum, count, n; / * 入力するデータの個数を最初に聞いて、必要なメモリを確保 * / int array[NMAX]; p て intf ( ”何個の数値を入力しますか : ” scanf ( 物 d ” , &count count = 0 ・ array = (int*)malloc(sizeof(int) * count); n = p て土 ntf ( ”整数を入力して下さい ( 0 を入力すると終了 ) : ” scanf ( d ” , &buf if(buf) array [ count ] = buf ・ 十十 count ー ) while(buf ! = 0 / * 合計値を算出 * / printf("-- 入力されたのは以下の数です一如” for(sum = n = 0 ・ n く = count; 十十 n ) , array[n] sum 十 = array[n]; printf( -- 以上の数の合計値は %d です。″ return EXIT—SUCCESS ー p て intf ( ”整数を入力して下さい ( 0 を入力すると終了 ) : ″ scanf ( "%d", &buf if(buf ) array[n] = buf; 十十 n ー } while(buf ! = 0 / * 合計値を算出 * / printf("-- 入力されたのは以下の数です一” for(sum = n = 0 ー n く count; 十十 n ) printf( d 基ビ , arraytnJ sum 十 = array[nJ; p て土 ntf ( 、 n - ー - 以上の数の合計値はです。跏” sum free ( array 潺 return EXIT—SUCCESS ー / 8 C MAGAZINE 2001 7
LiIlllX PV0$ilIIlIlllIlg ハッシュテーブル操作関数の使用例 GHashTab 厄 * tabl table = g—hash—table—new( g—direct—hash' g—direct—equal TIPS Fig. 5 List 1 0 の実行の様子 $ rpm -qa --qf "%{NAME) %{VERSION) %{RELEASE)*nn kernel 2 . 2 .18 2 the size of table= 604 $ rpm -qa --qf "%{NAME) %{VERSION} %{RELEASE}*nn the size of table= 604 . /hash kerne ー . /hash ( 処理 ) g—hash—tab le—destroy( tab 厄 List オリジナルのハッシュ関数・比較関 数を実装したい場合の例 static gu 土北 hash-func(gconstpointer key) fo て ( 辷の土く st ( s に土十 + ) { ( ke = も ) て e セ n guint val = gchar *str = (gchar*)key; val = (vaI<<4) 十 (val (guint)str[i]); return val; めんなさい , RPM がインストールされてい トを処理するプログラムだったりします ( ご ラム , インストールされている RPM のリス ログラムになっています。実はこのプログ をキーとしハッシュテープルに挿入するプ つのトークンを読み込み , 先頭のトークン 録 ) 。この例は , 標準入力から 1 行ごとに 3 を紹介します ( List 10 , 付録 CD - ROM に収 次に , キー , データを動的に確保する例 す。 がないので比較的処理は簡単になっていま ているため , メモリを動的に確保する必要 ムです。この例では , 文字列定数を使用し に紹介した関数を使ったサンプルプログラ List 9 ( 付録 CD-ROM に収録 ) は , 今まで ります。 ドレスをあらかじめ取得しておく必要があ ded ( ) によりオリジナルキー , データのア になります。 g_hash_table lookup_exten 解放しないままリンクを切ってしまうこと 領域のみを解放し , キーやデータの領域を hash—table—remove ( ) を呼ぶとノヾケットの にメモリ確保している場合は , 不用意に g ー 注意が必要です。もしキーやデータを動的 すが , g_hash—table—remove ( ) の使用には TabIe 4 ハッシュテープレ操作関数 ( データの操作 ) 関数名 void g—hash—table 」 nse 「 t( GHashTable * hash_table, gpointe 「 key, gpointe 「 value); void g—hash—table—「 emove( GHashTable * hash_table, gconstpointer key); gpointe 「 g—hash—table lookup( GHashTable * hash_table, gconstpointer key); gboolean g—hash—table 」 ookup—extended( gpointer user_data); func. GHFunc GHashTable * hash_table, void g—hash_table_foreach( *value); gpointe 「 gpointe 「 * orig—key, gconstpointer lookup—key. GHashTable * hash_table, user_data); gpointe 「 value, gpointe 「 void ( * GHFunc) (gpointe 「 key, ハッシュテーブルにキー / データを挿入する ハッシュテーブルからデータを検索する ( オリジナルキー ハッシュテーブルのデータを検索する ハッシュテーブルからキー / データを削除する / データの取得 ) GHFunc をもとにした全データの操作 ハッシュテーブル操作関数 ( その他のテータ操作 ) TabIe 5 void g—hash—table-thaw(GHashTabIe * hash—table); void g—hash—table—f 「 eeze(GHashTable * hash—table); guint g—hash—table—size(GHashTabIe * hash—table); 関数名 説明 ェーブ丿レのリサイズを再開する ェープレのリサイズを止める ァープレサイズを取得する ないシステムを使っているみなさん ) 。各 行は , 最初のトークンが RPM 名 , 2 番目が バージョン , 3 番目がリリースの文字列を 持ち , コマンドの第 1 引数に RPM 名を指定 すると検索し , 引数がない場合は全部のリ ストを表示するようになっています。たと えば , Fig. 5 のように実行します。 そのほか , ハッシュテープルの関数とし て , テープルサイズに関係するものがあり ます (TabIe 5 ) 。 最後に 今回は , GLib が提供する機能の氷山の一 角しか紹介できませんでしたが , まだまだ 興味深い機能がたくさんあります。たとえ ば , 字句解析 (Lexical Scanner) , I/O 関連 , スレッドサポートなど , 大物がいつばいあ ります。リファレンスやソースコードを手 がかりにしてハックしてみましよう。 1 28 C MAGAZINE 2 1 7
力リ 画像処理を極める タでも , 3 バイトの並びで表現します。 れにより , データ展開時に問題が発生する ことはなくなりますが , 0xFF が頻繁に現 れるデータの場合 , データ量が元データよ り増えてしまう可能性が出てきます。 号の始まりを示すコードとして利用するテ strncpy(str, "00XN",strI strncpy(str, ” *. RL2",strI strncpy(str, ″ RL2 ″ , s セ r い一 ③テータ長の頭 1 ヒットをランレングス符 いため , 適用には若干注意が必要です。 で登場頻度の少ない値というのは考えにく あります。画像データでは , あらゆる画像 ドには登場頻度の少ないものを選ぶ必要が ですから , ランレングス符号を示すコー ータ形式 この方法も , ②の方法と同じく , ランレ ングスが短いデータ部分は , そのまま出力 します。違いは , データ長 + データ値形式 での出力の際 , データ長部の上位 1 ビット をランレングス符号化を示すコードとする ランレングス符号化による圧縮② ( ランレングスを示す符号を利用する ) List #include く windows. h> / * 変更すべきポイント * / #include く stdio. h> List 2 0 case 8 : switch(dep) { #def ine SUPPORT_I #def ine SUPPORT_2 #define SUPPORT_4 #def ine SUPPORT_8 #def ine SUPPORT—16 #def ine SUPPORT_24 #define SUPPORT—32 char pbuf[256]; 1 2 4 8 16 32 64 /*void xvbzero(), len) char * 町 size—t len; fo て ( ー厄 n > の len--) * s 十十 = 0 ・ int WINAPI GetAddinInfo(int infon , LPSTR str は n に return 0 ー default: break; case 3 : break; case 2 : break; case 1 : break; case 0 : switch(infon) { 土 n セ strl ) maxx=()x + 3) / 4 * f0 て ( 辷 my -1 洋 > = の土ーー ) { output—raster ( fp , &img [ 土 *maxx ] , mx if( IpprgressCaIIback) lpprgressCallback(i,my,0); fu Ⅱ col or=FALSE; break; default: MessageBox(NULL,DNOt support! % ” ERR ”田一 OK return 1 ー if( lpPrgressCaIIback) lpPrgressCaIIback(my,my,0) fc lose ( fp getRLE ( byte *img は n し x は n セ mx , int lim はれヒ *run , int *code ) return px ー px 十十一 px 十十一 *code=img [ px *run=l ー int px; while(px«mx & & *code==img[px] & & *run<lim ) { strncpy(str,"RunLength saver Ver0.01 By Kz",strl) デ return SUPPORT—8; int WINAPI supportlmage(void) return str ー en ( str if (IpPrgressCallback) IpPrgressCaIIback(0,O,0); if( (fp=fopen( (char*)fname,"wb") )==NULL) て eturn FALSE; dep=bmih->biBitCount ー my=bmih->biHeight; mx=bmih->biWidth; img=pHBm ー colmap=pHBInfo—>bmiColors; bmih=(BITMAPINFOHEADER *)pHBInfo; int temp ー int mx,my,maxxßep; int fu 日 col 0 土 n し土 , byte *img; RGBQUAD *colmap; BITMAPINFOHEADER *bmih; FILE *fp; FARPROC lpprgresscallback,int ac,char *av[ ] ) BITMAPINFO *pHBInfo,void* pHBm, unsigned 土 n セ f lag , LPSTR *fname, 土 n に WINAP I WritePicture ( int output—raster(FILE *fp,byte *img, int mx) fputc ( code , fp fputc(run,fp); fputc(Oxff,fp); se { fputc ( code , fp fputc(0x02 ,fp); fputc(0xff ,fp); else { fputc ( code ,fp fputc(code,fp); if(code!=0xff) { else if(run==2) { fputc(code,fp); fputc(0x01,fp); fputc(0xff,fp); else { fputc(code,fp); if ( code ! =Oxff ) { if(run<2) { //MessageBox(NULL,pbuf,"MES" 靆田一 OK //sprintf(pbuf , nx=%d run=%d code=%d" ,x,run,code); x=getRLE(img,x,mx,0xff ,&run,&code); while(x く皿) { //MessageBOX(NULL,pbuf, ” ES ”川 B ー OK //sprintf(pbuf, ” d % x=0; int run , code; 画像処理を極めるアルゴリズムラボ 107
第 22 回 画像処理を極める 画像圧縮アルゴリズム① 画像圧縮の基本 昌達 K'Z 精密になればなるほど膨大なデータ量 種類やプログラムの種類によってさま ざまなものが存在します。今回から数 になる画像データ。これをうまく運用 するために : そのデータを圧縮してデ 回にわたり , 画像圧縮について取り上 ータ量を削減するのがほとんどです。 げます。今回は画像圧縮の基本を扱い 画像データの圧縮方法は , その画像の ます。 に強いもの , 逆に CG 的な画像に強いもの しいでしよう。このように , 圧縮アルゴリ はじめに ズムの処理効率も考慮すべき場合もありえ などです。逆にいえば , あらゆる画像を最 適に圧縮できるようなアルゴリズムは存在 ます。場合によっては , その画像に最適な しません。また , 圧縮率や画質だけでなく , アルゴリズムを新たに構築する必要がある 画素の集まりによって表現されるビット 処理速度が問題となる場合もあります。ゲ マップ形式の画像データは , 非常に表現力 かもしれません。 が高く , イラスト的な絵から写真などの自 ームなどで用いる画像でも , そのままでは 本稿では , 今回から数回にわたって , 画 データ量が大きいので圧縮を施すことがあ 像圧縮の基本や比較的よく用いられる画像 然画像まで , あらゆる画像に対応すること ります。その場合 , データの復元処理にか 圧縮アルゴリズムを紹介していきたいと思 ができます。さらに , 画素数を上げていく かる時間ができるだけ短くなることが望ま います。 ことにより , 細部までも再現することが可 能です。しかし , その表現力の高さとあい Fig. 1 ランレングス符号化 まって , データ量は非常に大きなものとな 元データ ります。たとえば , 今の PC の一般的なディ スプレイサイズ 1024X768 の画像でグレー スケールや 256 色のみを考えた場合でも , データ量は 768K バイトに達します。これ がフルカラーとなると , いっきに 3 倍に膨 れ上がります。精密さを求めるために解像 Fig. 2 ランレングス符号化の表現方法 度を上げていくと , データ量はさらに増加 元データ します。 このように画像データは , データ量が非 常に大きくなるため , そのままではデータ の保存 , 運用が非常に困難です。ゆえに多 くの場合 , 圧縮処理を施して , データ量を 削減してから利用する場合がほとんどです。 画像データの圧縮については , 初期のこ ろからさまざまな方法が考えられています。 それぞれ得意とする画像の種類 , 目的が異 なります。たとえば , 写真などの自然画像 00 1 1 2 2 2 1 1 1 1 1 1 1 圧縮データ 2 2 2 1 1 1 1 1 1 1 1 1 1 方法① 3 1 方法② FF 4 1 方法③ x84 1 3 2 FF 3 FF 5 ↑ FF 4 1 0 3 X85 1 X83 2 0 x84 1 103 画像処理を極めるアルゴリズムラボ
技術を知って実践しよう ! ネットワークプログラミングのアトリエ ロ 2 当葺第本連載ではプログラミング言語や開発環境を問わず , さまざまなネ , トワ ' = を ! 第 3 朝象 4 朝ラムを作りたいときにどのようにすればいし、のだろう」というときにお役、 4 き、受誉 0 に立てる内容をお届けします。今回は , Web サイトのさまざまなサービス を提供する CG にデータベース操作を伴う処理の作り方について , Perl& 0 「 acle データベースを使った実例を示しながら解説します。 Per による CG に OracIe データベースを扱う KIT-M (masa26@naa.att.ne.jp, http://macosx2.ncs.gr.jprmasa/) て読み進んでください。 なお , 今回 Oracle と DBI, DBD::OracIe モ ジュールはすでにインストール済みという 本稿を執筆するにあたり , web サイトで それでは , このデータベース構築に使用 前提で解説していきます。そのため各イン データベースについて検索してみました。 するソフトウェアなどについて説明しまし ストール方法については解説しないので , とてもたくさんの結果が出てきます。声優 よう。 ご了承ください。 のデータを名前で検索できるものや料理の 今回はデータベースとして Oracle を使用 分類により料理のレシピを検索できるもの , します。 OracIe は Linux や Sun などで使用で 昔発売されたゲームソフトについて調べら きるもので , 別のマシンにあるプログラム れるものなど , いろいろあるようです。仕 からデータヘアクセスすることもでき , な データベースのメリットというと , レコ 組みもさまざまです。たとえば , データの かなか便利なデータベースです。本稿では ードの検索 / 抽出の速さをあげるかもしれ 数だけ HTML ファイルを直接記述している OracIe を対象として解説しますが , Postgr ません。たしかにそれもありますが , もっ 単純な構造のものもありました。百件程度 es や MySQL などでも同じ API を使用してい と重要なことがあります。データベースの のちょっとしたデータ数の場合なら , この ます。ほかのデータベースシステムを使い 本当のメリットは検索 / 抽出 / ソートなどの 方法がいちばんかもしれません。しかし比 機能が簡単な関数の呼び出しで実行できる たい方は , OracIe 独自の SQL やデータ型を 較的大規模なデータ , 数千 , 数万件程度の ということです。それは , どのように簡単 除いて OracIe を Postgres などと置き換えて データ ( レコード ) を扱いたい場合はどうで 読んでみてください。 なのでしようか ? 一般的にデータベース データヘアクセスするプログラム言語に しようか。数万件の HTML ファイルをいち の操作には , SQL という構造化問い合わせ いち記述するのは並大抵ではありません。 は , メンテナンスのしやすさを考えて Perl 言語を使用します。 DBI モジュールを使用 を使用することにしました。 Perl から Orac する場合も例外ではありません。データベ しかも項目別にソートができるようにした い場合 , または複数の人がリアルタイムに le ヘアクセスするには , DBI モジュールと D ースを操作するには基本的にこの SQL を文 データを追加 / 更新したい場合はどうした BD::OracIe モジュール (DBI モジュールのた 字列として DBI モジュールの API メソッドへ らいいのか ? という問題も出てきます。 めの Oracle データベースドライノヾ ) というも 渡してやるだけです。たとえば , さて , どうしたらいいでしよう。今回は のを一般的に使用します。もちろん C 言語 $ dbh->do(' SQL 文 "潺 このような比較的大規模なデータベースを や C + + で作ることも可能ですが , こんな感じになります。実に単純ですよね。 こでは 扱う簡単な方法を紹介したいと思います。 とくに解説しません。 この文の内容や SQL については , 後ほど解 簡単といっても安定性がないとか速度が遅 Perl で記述した CGI によって Webvx—ジ 説するので安心してください。ここではデ いとかいうことはありません。商品データ からこのデータベースをアクセスできるよ ータベースプログラムが実に簡単なことで や顧客データなどの大事なデータに対して うにしてみます (Fig. 1 ) 。プログラム自体 あるというのをわかっていただければと思 はとても単純なので , どうぞリラックスし も十分に使用できるものです。 います。 1 1 6 C MAGAZINE 2 1 7 データベースのソフトウェア データベースのメリット
必要な部分だけを追記すればよい。 TabIe 2 VB と Delphi のデータ型比較 型 クラス内のメンバは private と public キー 整数型 ワードによって , そのアクセス範囲が規定 長整数型 される。 VB の Private/PubIic と意味的には 単精度実数型 同じだ。 倍精度実数型 private: ここで宣言したメンバ ( 変数 通貨型 関数 , 手続き ) は , このユニット内でしか 日付型 利用できない。ほかのユニットから参照さ ( 記 文字列型 れたくない変数や関数 , 手続きは バリアント型 述する。 バイト型 public: ここで宣言したメンバ ( 変数 論理型 関数 , 手続き ) は , ほかのユニットからも オブジェクト型 参照が可能となる。 [var 節 ] プロジェクト全体で参照可能な変数 , 関 TabIe 3 D phi のデータ型 ~ 整数型 数 , 手続きを宣言する。 uses 節でほかのユ 囲 ニットの参照を記述すれば , そのユニット Shortint 符号付き 8 ビット -128 ~ 127 上の変数 , 関数 , 手続きも宣言できる。 Smallint 符号付き 16 ビット -32768 ~ 32767 符号付き 32 ビット Longint ー 2147483647 ~ 2147483647 •lmplementation 部 ー 263 ~ 263 ー 1 符号付き 64 ビット int64 lnterface 部で宣言した関数や手続きの具 符号なし 8 ビット Byte 0 ~ 255 体的なコードを記述する。要するに関数や 符号なし 1 6 ビット Wo 「 d 0 ~ 65535 手続きは「 lnterface 部で宣言し , lmplemen 符号なし 32 ビット Longword 0 ~ 4294967295 ねⅱ。 n 部で実体を定義する」ことになる。 符号付き 32 ビット lntege 「 ー 2147483647 ~ 2147483647 ちょっと乱暴な言い方になるが , VB は I Cardinal 符号なし 32 ビット 0A42147483647 nterface 部がなく , いきなり lmplementati さらに DeIphi には , 整数 , 実数 , 文字など on 部でプロシージャの宣言と定義を行っ 腹である。厳密な規則に従って , 多種のデ ータ型を扱わなければならない。 の各要素を扱うデータ型が多数用意されて ている一一と , とらえてもいいだろう。 いる。 Table3 ~ 7 をご覧いただきたい。文 VB と DeIphi のデータ型 字と文字列も明確に分かれ , 文字型はコー VB は DeIphi に比べてデータ型の種類が ドセットによって分けられている。 部分だが , ほとんど使用されることはない。 少なく , その扱いも鷹揚 ( というか , けっ VB から DeIphi へとコードを移植する場 こうアバウト ) だ。まず , VB と基本的に共 合 , Table2 に基づいて , 通するデータ型を TabIe2 にあげておこう。 lnteger 型→ Shortlnt 型 String 型→ string 型 TabIe 4 D 引 phi のデータ型 ~ 実数型 のように単純に置き換えることが可能だ。 型名 有効桁数 ただし , pascal 本来の厳密な記述は , Ta 6 バイト ble 3 以降にあげた詳細なデータ型を駆使 4 バイト するところから始まる。 8 バイト 15 ~ 16 1 9-20 1 0 バイト 1 9-20 8 バイト 1 9-20 8 バイト Delphi VB Shortlnt Longlnt single double currency TDateTime st 「 ing va 「 iant byte boolean TObject(VCL) , variant(OLE) lntege 「 Long Single Double Cur 「 ency Date String Variant Byte Boolean Object 形式 型名 ・ lnitialization 部 初期化処理 , 要するに最初に実行される ・ Finalization 部 lnitialization 部で使用したオプジェクト をメモリから解放する処理を記述するのだ が , lnitialization 部と同様 , ほとんど使用 されることはない。 データ型と型キャスト PascaI をベースにした Delphi のデータ型 は豊富だ。豊富ということは , 便利さと裏 おうよう ~ 12 日 e 引 Single Double Extended Comp Currency 型変換 繰り返すが , DeIphi はデータ型に関して 非常に厳格な言語である。 VB では問題な 1 54 c MAGAZINE 2 1 7