第02回 拡大・縮小

Processingの描画機能を使う

課題 1

    ベースのプログラム (マウスカーソルに白丸がくっついて表示される。これが拡大の基準点になる)
    PImage img; // 元画像の変数
    
    void setup() {
      size(600, 400);
      textAlign(CENTER, CENTER);
      img = loadImage("1元.jpg");
    }
    
    void draw() {
      background(255);
      image(img, 0, 0, width, height);// 画像を表示
      fill(255);
      ellipse(mouseX, mouseY, 10, 10);
    }
    
    // Processingの描画機能を使って拡大(課題1)
    void drawToImage(float s) {
      int w = img.width;
      int h = img.height;
      float cx = mouseX*w/width;  // 画像サイズに換算した拡大基準の横位置
      float cy = mouseY*h/height; // 画像サイズに換算した拡大基準の縦位置
      PImage outputImg = img.get(); // 出力画像
      PGraphics pg = createGraphics(w, h);
      pg.beginDraw();
      // ここに「拡大画像の描画」の命令を書く
      pg.endDraw();
      outputImg.pixels = pg.pixels;
      outputImg.save("data/1描画機能.jpg");
    }
    
    // 最近傍補間(課題2)
    void nearestNeighbor(float s) {
    }
    
    // 双一次補間(課題3)
    void bilinear(float s) {
    }
    
    // cを中心として画像を1/s倍したときのベクトルfの移動先のベクトルを返す(課題2, 3で使用)
    PVector getScaledPosition(PVector c, PVector f, float s) {
      f.sub(c); // fからcを引く
      f.div(s); // fを1/s倍する
      f.add(c); // fにcを加える
      return f;
    }
    
    void keyPressed() {
      drawToImage(10);     // 描画機能で10倍に拡大
      nearestNeighbor(10); // 最近傍補間で10倍に拡大
      bilinear(10);        // 双一次補間で10倍に拡大
    }
    


  1. Processingのエディタに上のサンプルプログラムのコードをコピー&ペーストする。
  2. 「img02」という名前で保存する。
  3. 適当に画像検索してサンプル用の画像(デジカメで撮った写真のようなもの)を用意する。
  4. 画像の形式に応じて以下の変更を加える。
  5. 「1元.jpg」をProcessingのウインドウにドラッグ&ドロップする。
  6. drawToImage関数の「// ここに「拡大画像の描画」の命令を書く」のところに、対応するコードを入れる。

  7. 実行し、ウインドウのどこかで一度クリックしてから、拡大したい場所にマウスポインタを置いてキーボードのキー(どれでもよい)を押す。
  8. ※ 選んだ位置を中心として10倍に拡大した状態のものになる。画像の解像度は元画像と同じ。

    ここで作る提出物
    ※ うまくいかないときはdrawToImage関数の最終状態とコードを見比べる。

最近傍補間

課題 2

  1. (いまは空っぽになっている)nearestNeighbor関数の中に、drawToImage関数の最初の5行と最後の1行をコピー&ペーストする。


  2. nearestNeighbor関数の最後の行のファイル名のところを「1描画機能.jpg」から「2最近傍補間.jpg」に変更する。
  3. nearestNeighbor関数の最後の行の前に、ピクセルの色を決めるための2重ループを追加する。

  4. (iが横方向、jが縦方向の繰り返し)

  5. ループの中に以下のコメント文を入れる (コピペ可)。
  6.       // 出力画像の(i, j)の位置に対応する元画像の位置(i_, j_)を求める
          // 出力画像の(i, j)の点の色を元画像の(i_, j_)の色にする
    	

  7. 「// 出力画像の(i, j)の位置に対応する元画像の位置(i_, j_)を求める」の下に、以下のコードを入れる (コピペ可)。
  8.       PVector ij = getScaledPosition(new PVector(cx, cy), new PVector(i, j), s);
    	
    ※ getScaledPositionは、このプログラムの下の方にある座標変換の関数。
    ※ その関数で計算した結果を入れた「ij」はベクトル型の変数で、横位置と縦位置の両方の情報を含む。

  9. その下に、以下のコードを入れる (コピペ可)。
  10.       int i_ = round(ij.x);
          int j_ = round(ij.y);
    	
    ※ 「ij.x」「ij.y」が元画像の対応点の横と縦の位置にあたる。ただし、値はfloat型。
    ※ 「round」は四捨五入の関数。これを使うことで(i_, j_)が「対応点に一番近い元画像のピクセルの位置」になる。

  11. 「// 出力画像の(i, j)の点の色を元画像の(i_, j_)の色にする」の下に、以下のコードを入れる (コピペ可)。
  12.       outputImg.pixels[i+j*w] = img.pixels[i_+j_*w];
    	
    ※ ピクセルの色情報は「画像変数.pixels」という配列に格納されている。
    ※ 配列の番号は図のような通し番号になる。つまり、番号は「1つ右にずれると1増える」「1つ下にずれると「横幅分」増える」ことになる。そのため、(i, j)のピクセルの情報はこの配列の「i+j*w」番目に入る。


  13. 実行し、ウインドウのどこかで一度クリックしてから、拡大したい場所にマウスポインタを置いてキーボードのキー(どれでもよい)を押す。
  14. ※ 実際に画像を開いて「1描画機能.jpg」と「2最近傍補間.jpg」を比べてみると、「2最近傍補間.jpg」の方は粗いブロック状になっているはず。これは、出力側の10×10個のピクセルが元画像の同じ点に対応してしまうため。

    ここで作る提出物
    ※ うまくいかないときはnearestNeighbor関数の最終状態とコードを見比べる。

双一次補間

課題 3

  1. nearestNeighbor関数の中のコードをbilinear関数の中にコピー&ペーストする。
  2. bilinear関数から以下の2行を削除する。
  3.       // 出力画像の(i, j)の点の色を元画像の(i_, j_)の色にする
          outputImg.pixels[i+j*w] = img.pixels[i_+j_*w];
    	
    ※ 双一次補間では特定のどれかのピクセルの色ではなく、4つのピクセルの色を混ぜる。前の関数の名残はここで消しておく。

  4. その部分に以下のコメント文を追加する (コピペ可)。
  5.       // 対応点の左上の点の色
          // 対応点の右上の点の色
          // 対応点の左下の点の色
          // 対応点の右下の点の色
          // 対応点の左上のピクセルからの横、縦のずれ
          // 対応点の色の赤成分
          // 対応点の色の緑成分
          // 対応点の色の青成分
          // (r, g, b)から色を作り、それを出力画像の(i, j)の点の色とする
    	


  6. ファイル出力の部分を以下のように書き換える。

  7. この時点でbilinear関数は以下のようになっているはず。


  8. ijからi_, j_を作る部分を以下のように書き換える。
  9.       int i_ = round(ij.x);
          int j_ = round(ij.y);
    	
          int i_ = int(ij.x);
          int j_ = int(ij.y);
    	
    ※ int関数では小数部分を切り捨てて整数化される。
    ※ つまり、(i_, j_)は元画像の対応点の左上のピクセルの座標になる。

  10. 「// 対応点の左上の点の色」の左に以下のコードを追加する。
  11.         color clt = img.pixels[i_+j_*w];
    	

  12. 同様にして、その下の3行でcolor型の変数crt, clb, crbを作り、然るべき値を入れる。

  13. 「// 対応点の左上のピクセルからの横、縦のずれ」の下に以下のコードを追加する。
  14.       float x = ij.x-i_;
          float y = ij.y-j_;
    	
    ※ これがまさに上の説明でいうx, yにあたる。

  15. 「// 対応点の色の赤成分」の左に以下のコードを追加する。
  16.       float r = (1-y)*((1-x)*red(clt)+x*red(crt))+y*((1-x)*red(clb)+x*red(crb));
    	
    ※ red関数は色の変数から赤成分を取り出す関数。同様にgreen関数、blue関数で緑と青の成分を取り出せる。
    ※ これが上の説明でいうx, yの値に応じて左上、右上、左下、右下のピクセルの色(の赤成分)を混ぜる工程。

  17. 同様にして「// 対応点の色の緑成分」「// 対応点の色の青成分」の左に、然るべき値をfloat型の変数gとbに入れるコードを追加する。

  18. 「// (r, g, b)から色を作り、それを出力画像の(i, j)の点の色とする」の左に以下のコードを追加する。
  19.       outputImg.pixels[i+j*w] = color(r, g, b);
    	


  20. 実行し、ウインドウのどこかで一度クリックしてから、拡大したい場所にマウスポインタを置いてキーボードのキー(どれでもよい)を押す。
  21. ※ 「3双一次補間.jpg」には粗いブロックはできていないはず。

    ここで作る提出物
    ※ うまくいかないときはbilinear関数の最終状態とコードを見比べる。

提出

小テスト予告



戻る inserted by FC2 system