画像処理、ユーザインターフェース

Menu Menu


目的

この実験では,Unix上で動作するGUI(Graphical User Interface)の代表的なシステムのプログラムを行う。

    一番低レベルな ビットマップ (Frame Buffer) レベル
    X Window System レベル
    ツールキットレベル
    スクリプト言語レベル
    Open/GL ライブラリ

これらを用いて、コンピュータのインタフェースの設計に関する理解を深め経験を積むことが出来る。また、GUI はイベント駆動型のプログラムとオブジェクト指向計算が必須であり、これらの理解を深めることができる。


関連科目


level 1 ビットマップレベル

カラー画像の一点の色は、 RGB と呼ばれるように、三原色で表現することができる。現在のコンピュータでは、8bit ずつ24bit のRGB でカラーを表現することが一般的である。画像自体は、これらの一点を二次元配列を使って構成することができる。

適当なプログラム言語を用いて、

    struct bitmap {
        unsigned char r,g,b;
    } frame_buffer[x][y];

のような構造を定義せよ。

これらの上に、

     RGBの入れ換え
     輝度変換
     ガンマカーブ変換
     拡大縮小、回転、平行移動などのアフィン変換

などの変換を施すプログラムを作成せよ。

また、netpbm ライブラリを用いて、実際に、変換がうまく行っていることを確認せよ。そのためには、

    pnmnraw などの出力を frame_buffer に読み込む関数
    frame_buffer の内容をrawtoppm の入力に合わせて出力する関数

を作成するか、この frame_buffer の内容を表示する X11 lib レベルのプログラムを作成する必要がある。(xloadimage などのソースを参考にして良い)


level 2 直線の描画

frame_buffer をクリアする関数、および、任意の色の直線を描画する関数を作成せよ。

三角函数を使わずに描画する方法が速度的、誤差的に有利である。


level 3 ワイヤフレーム3次元画像表示

上記のframe_bufferの絵を、各面に持つ立方体の3次元画像表示を作成したい。とりあえず、

    立方体の3次元画像表示を格納するframe_buffer

を作成する。

表示する立方体の角度や位置を指定するパラメータとしては、座標変換行列を用いるのが簡単だと思われる。

座標変換行列は、単位行列を回転や拡大縮小などのアフィン変換を利用して作成する。3x3を用いるのが簡単だが、平行移動を入れた4x4の行列を用いる方法もある。

これらの変換行列を使って、立方体の頂点を作り、直線描画を使って、ワイヤフレーム画像をframe_buffer内に描画し、表示してみよ。


level 4 ポリゴン表示

立方体を描画する際に、各面に別な frame_buffer を表示したい。これはテクスチャ・マッピングと呼ばれる。

各面は、テクスチャ画像を、アフィン変換したものになる。長方形は、平行四辺形(遠近による縮小を考慮する場合は、4辺形)となる。

これらのアフィン変換を順番に適用し、立方体のポリゴン表示を行え。

適当な数の立方体の表示を行い、時間を測定せよ。プログラムを高速化するにはどうしたら良いか? ハードウェアで実現する場合を考慮して、考察し、実装せよ。

ポリゴンを書く順序を間違えると、おかしな画像を表示してしまう。これは、クリッピングと呼ばれる問題である。これを解決するのは、立方体ではやさしいが、一般的には、それほど簡単ではない。


level 5 ツールキットレベル

Mesa などの Open/GL を用いて、上記の立方体の表示と同じことを行え。時間も測定し、自分で書いたプログラムと比較せよ。時間の差は、どこから来るのか考察せよ。


level 6 ツールキットレベルによる2Dドローツール

任意のツールキットを用いて、簡単な2Dドローツールを作成せよ。

     直線、曲線、四角形、円、楕円

の描画ができること。

また、書いた内容を、SVGフォーマットなどに類似した XML で格納する機能を含めること。

Javaが楽か?

 


level 7 三次元CADツール

Open/GL または、Java/3D などを用いて、三次元モデリングを行うツールを作成せよ。

     立方体とその組合せ
     任意の視点からの表示
     DXFなどの標準フォーマットへの出力

は最低限可能であること。

どのようなユーザインタフェースが望ましいか考察し、実装せよ。

Lightwave 3Dのような方法が最適だとは思えないが、

     x,y,z 軸方向からの三つ投影図に対して、描画を行う

などの方法がある。


level 8

新しいレベルを提案し、実装および評価を行え。

ヒント: Draw Tool に文字表示機能を入れる。Draw Tool の機能を上げる。


まとめ

フレームバッファを用いる方法は良い方法なのだろうか? 現在、手に入る、Drawing Tool の機能を考察し、その実装方法の難しさなどについて考察しよう。


おまけ

GUI(グラフィカルユーザインターフェイス)は,計算機を利用するときに直接ユーザの目にふれるシステムであり,その優劣は計算機システム全体の使い勝手の善し悪しに大きく影響する.現在,GUIはウインドウシステムを中心に設計されており,いくつかのOSと深く結びついている.代表的なものとして,(1)UNIXを基盤とするX-Window,(2)PC系パソコンにおけるWindows,(3)Macintoshに大別される.これらのGUIは操作の標準化をもたらすことに貢献してきた.すなわち,(a)API,(b)Look&Feel,(c)UIMSである(用語の意味を各自調べよ).ここで,UIMSはすでにAPIに吸収される形になりつつある.

X-Window Systemは,MITにおいて「分散型教育環境開発プロジェクト」Athenaの一環として開発されたウインドウ環境であり,特定の機種に依存しない移植性の高さとソースコードの公開による公開性,およびネットワーク透過性が特徴である.

X-Windowでは,ウインドウマネージャというクライアントプログラムが特別な役割を果たしている.基本的に,ウインドウマネージャはウインドウシステムの操作やLook&Fe elに大きく影響している.X-Windowでは,ユーザの好みによってウインドウマネージャを自由に取り換えることが可能である.それゆえ,X-Windowシステムの自由度は非常に高いといえる.

X-Windowシステムでは,アプリケーションプログラムの開発にXlibというライブラリ関数群を使用する.その主な機能は,(1)ウインドウの生成,消滅,可視化の制御(2)入出力イベント管理,(3)線や点などの基本描画機能,(4)フォントやカラーマップ,Graphic ContextなどのX資源の管理である.開発者はこれらの機能を果たすX関数群(先頭にXという文字がつくことが多い. i.e. XDrawLine())を利用してプログラムを実装する.

XDrawLine()関数は,XDrawLine(display,window,gc,x1,y1,x2,y2)のような書式で用いられる.これは,指定されたディスプレイdisplay上のウインドウwindowにグラフィックコンテキストgcという性質の直線を座標(x1,y1),(x2,y2)間に描くことを意味する.ここで,display,window,gc等は,X-Windowで処理を行うために必要なX資源であり,プログラム中で使用する前に宣言・取得せねばならない.他のX関数も同様な書式で使用される.また,様々なデータを効率的に扱うために,いくつかの構造体が定められている.例えば,XEvent構造体などがある.その説明についてはmanを参照せよ.


思考実験


画面上にウインドウを開くプログラム

X-Windowプログラム(例えばx11_1.c)のコンパイルは,下記のようにおこなう.まず、X-Window のプログラムのソースは、ポータビリティ(portablitity)を維持するために、必ず Imakefile により、そのプログラムの作成方法を記述しなくてはならない。例えば、

     % cc x11_1.c -o x11_1 -lX11 -I/usr/X11R6/include -L/usr/X11R6/lib

とすれば、BSD/OS ではコンパイルできるが、ここで、cc, -lX11, -I... などのオプションは、すべてシステム依存である。だいたい、こんなに長いコマンドをいちいち入力するのはばかげている。そこで、より一般的な記述を Imakefile に記述する。

     1  XCOMM $Id$
     2  
     3  XCOMM  on BSDI use
     4  XCOMM      make CC=shlicc2 
     5  XCOMM  to create shared library based program
     6  
     7  DEPLIBS = $(DEPXLIB)
     8  LOCAL_LIBRARIES = $(XLIB)
     9  
    10  XCOMM SRCS =     x11_1.c x11_2.c x11_3.c
    11  XCOMM OBJS =   x11_1.o x11_2.o x11_3.o
    12  XCOMM PROGS =  
    13  
    14  all:: x11_1 x11_2 x11_3
    15  
    16  SingleProgramTarget(x11_1,x11_1.o,$(LOCAL_LIBRARIES),$(SYS_LIBRARIES))
    17  SingleProgramTarget(x11_2,x11_2.o,$(LOCAL_LIBRARIES),$(SYS_LIBRARIES))
    18  SingleProgramTarget(x11_3,x11_3.o,$(LOCAL_LIBRARIES),$(SYS_LIBRARIES))
    19  
    20  DependTarget()

XCOMM はコメントである。ここで重要なのは、LOCAL_LIBRARIES に $(XLIB) を指定することと、SingleProgramTarget(x11_1,x11_1.o,$(LOCAL_LIBRARIES),$(SYS_LIBRARIES))によりプログラムの作り方を指定すること、そして、all に何を最終的に作るかを指定することである。Imakefile の書き方は残念ながら詳しく説明している文章はあまりない。BSD/OSのソースなどを参照して勉強すること。

ここでは、前に述べたシステム依存の部分が出てこないことに注意すること。さらに、BSD/OS では、共有ライブラリを使うことによりX-Windowプログラムの大きさを劇的に下げることができる。そのためには、特別なccであるshlicc2を使う。そこで、このプログラムをコンパイルする手順は以下のようになる。下線がひかれている部分が入力した部分である。

    % xmkmf
    mv -f Makefile Makefile.bak
    imake -DUseInstalled -I/usr/X11R6/lib/X11/config
    % make depend
    gccmakedep  --   -I/usr/X11R6/include -I/include  -DCSRG_BASED       -- 
    % make CC=shlicc2
    shlicc2 -O2 -fno-strength-reduce     -I/usr/X11R6/include -I/include  -DCSRG_BASED      -c x11_1.c
    rm -f x11_1
    shlicc2 -o x11_1 -O2 -fno-strength-reduce      -L/usr/X11R6/lib x11_1.o -lXext -lX11      
    shlicc2 -O2 -fno-strength-reduce     -I/usr/X11R6/include -I/include  -DCSRG_BASED      -c x11_2.c
    rm -f x11_2
    shlicc2 -o x11_2 -O2 -fno-strength-reduce      -L/usr/X11R6/lib x11_2.o -lXext -lX11      
    shlicc2 -O2 -fno-strength-reduce     -I/usr/X11R6/include -I/include  -DCSRG_BASED      -c x11_3.c
    rm -f x11_3
    shlicc2 -o x11_3 -O2 -fno-strength-reduce      -L/usr/X11R6/lib x11_3.o -lXext -lX11      

まず、Makefile が生成されていることに注意すること。このMakefileを作るためのルールは、/usr/X11/lib/X11/config の下に書いてある。これを参照して、xmkmf そして、imake が システムに依存しない Imakefile から、Maekfile を作成している。次に、shlicc2 を指定しながら make を実行している。すると、Makefile を参照しながらコンパイルが行われる。

Imakefile を書かないX11のプログラムは、X11のプログラムの書き方に反するだけでなく、ポータビリティを下げてしまう。例えば、このプログラムは、Sun OS 4.0.3, Solaris 2.6, BSD/OS 3.1 で同じように作成することができる。(残念ながら、shared library の扱いだけが特殊である。これは BSD/OS 3.1 の config の内容の問題らしい)

    % make clean
    rm -f x11_1
    rm -f x11_2
    rm -f x11_3
    rm -f *.CKP *.ln *.BAK *.bak *.o core errs ,* *~ *.a .emacs_* tags TAGS make.log MakeOut   "#"*

として、不要なオブジェクトやコマンドを消去しておくこと。このようにすることにより、ひとつひとつrmするよりも安全に確実に不要なファイルを消すことができる。


イベント処理

X11のプログラムは、最低限、Expose イベントは処理する必要がある。なぜなら、Expose する前に描画が終わってしまうのが普通だからである。しかも、X11では再描画は自分の責任である。つまり、ウィンドウが再び現れた時、例えば、前にあったウィンドウが取り除かれた時には、Exposeイベントがプログラムに通知されるので、それにしたがって再描画を行う必要がある。サンプルプログラム3の42行目からの処理は、とりあえず、最初に表示されるのを待って描画している。この部分を削除すると、どうなるか確認せよ。また、このままでは、再描画が行われないので、イベントループを作成する必要がある。


ユーザ入力

XEvent構造体、XSelectInput(),XNextEvent(),XEvent構造体などを利用する.イベント受理部はサンプルプログラム2のように作成する.


マウスの位置

マウスは,GUIを特徴づける典型的なデバイスである.ディスプレイ上に開いたウインドウ上でボタンを押しながら動かしたマウスの位置座標を出力しなさい.検知した位置座標は,標準出力へ出力すればよい.

ウインドウ上で”マウスが押された”というイベントと”マウスが動いた”というイベントを処理することがポイントである.

XQueryPointer(),XDrawPoint()あるいはXDrawRectangle()などを使用する.


スクリプト言語の使用


Open/GL

Mesaの情報は http://www.mesa3d.org/にある。


画面のキャプチャ

xwd,xwudというコマンドがある.この2つのコマンドの使い方と働きについて,man を利用してしらべなさい.また,画面上のある一つのウィンドウのダンプをとり,保存しなさい.さらに,再表示しなさい.


Imakefile

Imakefile

サンプルプログラム1

x11_1.c
     1  #include <X11/Xlib.h>
     2  #include <X11/Xutil.h>
     3  #include <stdio.h>
     4  
     5  int
     6  main(int ac,char *av)
     7  {
     8      Display *d;
     9      Window W;
    10      unsigned long black,white;
    11  
    12      d=XOpenDisplay(NULL);
    13  
    14      black=BlackPixel(d,0);
    15      white=WhitePixel(d,0);
    16  
    17      W=XCreateSimpleWindow(d,RootWindow(d,0),
    18          100,100,500,400,2,black,white);
    19  
    20      XMapWindow(d,W);
    21  
    22      XFlush(d);
    23      getchar();
    24  
    25  }


サンプルプログラム2

x11_2.c

     1  #include<X11/Xlib.h>
     2  #include<X11/Xutil.h>
     3  #include<stdio.h>
     4  
     5  int
     6  main(int ac,char *av[])
     7  {
     8          Display *d;
     9          Window W,w1,w2;
    10          int i,j;
    11          XEvent event;
    12  
    13          d=XOpenDisplay(NULL);
    14          W=XCreateSimpleWindow(d,RootWindow(d,0),
    15              100,100,500,500,2,0,1);
    16          w1=XCreateSimpleWindow(d,W,10,10,
    17              300,100,2,0,1);
    18          w2=XCreateSimpleWindow(d,W,10,160,
    19              300,100,2,0,1);
    20  
    21          XSelectInput(d,w1,ButtonPressMask|ButtonReleaseMask);
    22          XSelectInput(d,w2,KeyPressMask);
    23  
    24          XMapWindow(d,W);
    25          XMapSubwindows(d,W);
    26  
    27          while(1)
    28          {
    29                  XNextEvent(d,&event);
    30                  switch(event.type)
    31                  {
    32                  case ButtonPress: 
    33                          printf("ButtonPress\n"); 
    34                          break;
    35                  case ButtonRelease: 
    36                          printf("ButtonRelease\n"); 
    37                          break;
    38                  case KeyPress: 
    39                          printf("KeyPress\n"); 
    40                          break;
    41                  default:
    42                          break;
    43                  }
    44          }
    45          /* NOT REACHED */
    46          return 0;
    47  }


サンプルプログラム3

x11_3.c

     1  #include <X11/Xlib.h>
     2  #include <X11/Xutil.h>
     3  #include <stdio.h>
     4  
     5  unsigned long MyColor(char cn[]);
     6  void draw_line(Window W);
     7  
     8  GC gc1;
     9  Display *dpy;
    10  
    11  int
    12  main(int ac,char *av[])
    13  {
    14          Window W;
    15          unsigned long fg,bg;
    16          unsigned long vmask;
    17          XGCValues xgcv;
    18          XSetWindowAttributes xswa;
    19          XEvent xev;
    20  
    21          int i,j;
    22  
    23          if (!(dpy = XOpenDisplay(NULL)))
    24          {
    25              fprintf(stderr,"cannot open display");
    26              exit(1);
    27          }
    28  
    29          fg=MyColor("red");
    30          bg=MyColor("white");
    31  
    32          xswa.event_mask = ExposureMask | StructureNotifyMask;
    33          xswa.background_pixel = bg;
    34          xswa.border_pixel = fg;
    35          
    36          W=XCreateWindow(dpy,RootWindow(dpy,0),100,100,500,500,0,
    37              DefaultDepth(dpy,DefaultScreen(dpy)),
    38              InputOutput, DefaultVisual(dpy, DefaultScreen(dpy)),
    39              CWEventMask | CWBackPixel | CWBorderPixel, &xswa);
    40          XMapWindow(dpy,W);
    41  
    42          for (;;) {
    43              XNextEvent(dpy, &xev);
    44              if (xev.type == Expose)
    45                  break;
    46          }
    47  
    48          vmask = (GCBackground | GCForeground | GCLineWidth);
    49          xgcv.background = bg;
    50          xgcv.foreground = fg;
    51          xgcv.line_width = 3;
    52  
    53          gc1  = XCreateGC (dpy, W, vmask, &xgcv);
    54  
    55          draw_line(W);
    56  
    57          XFlush(dpy);
    58  
    59          getchar();
    60          return 0;
    61  }
    62  
    63  void
    64  draw_line(Window W)
    65  {
    66          XDrawLine(dpy,W,gc1,120,120,300,300);
    67          XDrawLine(dpy,W,gc1,20,20,100,100);
    68          XDrawLine(dpy,W,gc1,20,120,100,100);
    69  }
    70  
    71  unsigned long
    72  MyColor(char cn[])
    73  {
    74          XColor c0,c1;
    75          Colormap cmap;
    76  
    77          cmap = DefaultColormap(dpy,0);
    78          if (!cmap) {
    79              fprintf(stderr,"no default colormap!");
    80              exit(1);
    81          }
    82  
    83          if (XAllocNamedColor(dpy,cmap,cn,&c1,&amp;c0)==0) {
    84              fprintf(stderr,"no such color \"%s\"",cn);
    85              exit(1);
    86          }
    87          return(c1.pixel);
    88  }


参考文献

    X-Windowハンドブック,西村亨監修,アスキー出版局
    「Xウインドウとその仲間たち」石田晴久編,bit別冊,共立出版
    X-Window Ver11プログラミング,木下凌一,日刊工業新聞社
    OSF/Motif Style Guide R1.2,Prentice-Hall
    ヒューマンマシンインタフェースのデザイン,吉田真編,共立出版

Shinji KONO / Sun May 19 00:16:30 2002