« Cinema 4Dスクリプトで画像ファイルを読み込む | トップページ | スクリプトで動画ファイルの作成 »

スクリプトで読み込んだ画像に手を加えて保存する

読み込んだ画像に手を加えると言っても、Photoshopのように高度なフィルタ掛けて処理するわけではないです。BaseBitmapクラスで用意されているラインと長方形で落書きして見ます。実際Photoshopの様なフィルタを作ることはBaseBitmapクラスのメンバ関数(GetPixel()とDrawPixel())を使用すると可能です。ただ、C.O.F.F.E.E.の実行速度が遅いので、広範囲の強いブラーを掛けたりするようなコーディングすると、復帰するまでかなり時間がかかります。ま、画像サイズにもよりますが。画像処理をするならわざわざC.O.F.F.E.E.を使わなくてもPhotoshop等を使えば良いのですが…

BaseBitmapクラスのメンバ関数で描画するとき、Bitmapの範囲からはみ出すと何も描画しません。自動でクリッピングが行われないので、はみ出さないようにするか、クリッピングするコードを作るか…あぁ、2つとも同じか。

/*
読み込んだ画像に手を加えて保存

08.05.31
*/


regular_polygon(B_map , rp_x , rp_y , rp_n , rp_r , col)//正多角形描画(ビットマップ、座標x、座標y、角数、半径、色)
{
  var x , y , lx , ly , rad , i;

  B_map->SetPen(col);//色の設定

  x = rp_x + rp_r;//始点の座標
  y = rp_y;
  for(i = 1 ; i <= rp_n ; i++){
    rad = PI2 * i / rp_n;
    lx = cos(rad) * rp_r + rp_x;//終点の座標
    ly = sin(rad) * rp_r + rp_y;
    B_map->DrawLine(x , y , lx , ly);
    x = lx;//終点の座標を次の始点の座標へ
    y = ly;
  }
}

main(doc , op)
{
  var l_path = new(Filename);
  var bm = new(BaseBitmap , 1 , 1);
  var res;
  var bm_w , bm_h;

  res = l_path->FileSelect("画像を選択" , FALSE);if(!res)return;
  bm->Load(l_path);
  bm_w = bm->GetWidth();//画像の幅を取得
  bm_h = bm->GetHeight();//画像の高さを取得

  var rnd = new(Random);//乱数を扱うランダムクラス
  var i;
  var figure_x , figure_y , figure2_x , figure2_y;
  var reg_pol_n , reg_pol_r , pen_col;

  //ランダムな10個の長方形の描画
  for(i = 0 ; i < 10 ; i++){
    figure_x = rnd->Get01() * bm_w;//左上の座標
    figure_y = rnd->Get01() * bm_h;
    figure2_x = figure_x + rnd->Get01() * (bm_w * 0.1);//右下の座標
    figure2_y = figure_y + rnd->Get01() * (bm_h * 0.1);
    if(figure2_x >= bm_w)figure2_x = bm_w - 1;//単純な右のクリッピング
    if(figure2_y >= bm_h)figure2_y = bm_h - 1;//単純な下のクリッピング
    pen_col = vector(rnd->Get01() , rnd->Get01() , rnd->Get01());//色
    bm->SetPen(pen_col);//色の設定
    bm->DrawRect(figure_x , figure_y , figure2_x , figure2_y);//長方形の描画
  }

  //ランダムな10個の正多角形の描画
  for(i = 0 ; i < 10 ; i++){
    figure_x = rnd->Get01() * bm_w;//中心の座標
    figure_y = rnd->Get01() * bm_h;
    reg_pol_n = int(rnd->Get01() * 9) + 3;//角数
    reg_pol_r = rnd->Get01() * bm_w * 0.5;//半径
    pen_col = vector(rnd->Get01() , rnd->Get01() , rnd->Get01());//色
    regular_polygon(bm , figure_x , figure_y , reg_pol_n , reg_pol_r , pen_col);//多角形の描画
  }

  ShowBitmap(bm);//画像表示ウィンドウへ表示

  //ここからがJPEG保存

  var s_path = l_path->GetClone();//保存用ファイルネームクラスの取得。
  s_path->DelSuffix();//ファイル名を操作しやすくするために拡張子を削除する。
  var f_name = s_path->GetLastString() + "の複製";//ファイル名に「の複製」を付け足す。
  s_path->SetLastString(f_name);//ファイルパスの最後のファイル名を変更。
  s_path->SetSuffix("JPG");//拡張子を設定。
  res = s_path->FileSelect("画像の保存" , TRUE);//保存ダイアログを開く
  if(!res)return;
  s_path->SetSuffix("JPG");//ダイアログで拡張子が消された場合のために再度設定

  bm->Save(s_path , FILTER_JPG);//JPGで保存
}

実験なのに無駄に長くなってしまった。
今回のスクリプトは、正多角形を描画する為の関数を作ったのでmain()関数を記述する。前編:読み込み、中編:描画、後編:保存となっている。
前編は前回と同じ。
中編は、BaseBitmapクラスの描画用のメンバ関数(SetPen():描画カラーの設定/DrawLine():線分の描画/DrawRect():長方形の描画)とRandomクラスの乱数発生のメンバ関数(Get01():0.0~1.0までの乱数)を使用。
後編は、JPEGとして保存する。
ま、だいたい読込んだ画像を加工して保存するときは、読込んだ画像が保存されているフォルダと同じフォルダを指し示すほうが都合が良いので、読込んだ時のファイルパスを複製しました。今回は、読込み用パスと保存用パスは別々に用意しましたが。同じパス(Filename)を使ってファイル名のみを操作して変更しても問題ないでしょう。ただし読込みようパスをキープしなくても良ければの話ですが。
ファイル名に「の複製」を追加するため、作業しやすいように拡張子を削除するためDelSuffix()を使用。このDelSuffix()はピリオドごと拡張子を削除します。これを使用しなければ、String関数のstrchr()を使って`.`ピリオドを探して、前半部をstrmid()を使って取り出し、それに「の複製」を追加する。String関数を使うのは面倒なので、Filenameクラスのメンバ関数を使ったほうが楽ですね。
パスの最後に在るファイル名を取得する為にGetLastString()メンバ関数を使い、それに「の複製」を追加。その操作したファイル名をパスの最後のファイル名にGetLastString()で上書きする。
保存ダイアログを開き、その後に拡張子”JPG”を追加するので、その前に拡張子”JPG”の追加は必要ないと思われますが、ツールを使う側に「これからJPGで保存しますよ」と伝える意味で追加しました。親切ですね。親切か?大きなお世話か?まぁ保存ダイアログのFileSelect("JPEG保存" , TRUE);でタイトルを表示させても良いけど…
あとは保存ダイアログで拡張子を付けずファイル名だけで保存ボタンが押される可能性があるので、最後にSetSuffix()で拡張子を付ける。このSetSuffix()メンバ関数は、拡張子のついていないファイル名には拡張子を追加するし、付いていれば変更します。ですから、○○.JPG.JPGなんて事にはなりません。
最後にJPEG保存。BaseBitmapクラスのSave()メンバ関数にFilenameクラス変数とファイルタイプを渡す。他のファイルタイプは…

FILTER_JPG JPEG
FILTER_TIF TIFF
FILTER_TGA TARGA
FILTER_BMP Win BMP
FILTER_PICT Mac PICT
FILTER_IFF IFF
FILTER_PSD Photoshop

ん?JPEGなら圧縮はどうした?と思われるでしょう。では圧縮設定。
保存部分の変更。

//ここからがJPEG保存

  var s_path = l_path->GetClone();
  s_path->DelSuffix();
  s_path->SetLastString(s_path->GetLastString() + "の複製.JPG");
  res = s_path->FileSelect("JPEG画像の保存" , TRUE);
  if(!res)return;
  s_path->SetSuffix("JPG");

  var comp = new(BaseContainer);//圧縮設定のためのBaseContainerを作成。
  comp->SetData(JPGSAVER_QUALITY , 75);//画質75を設定(0~100)

  bm->Save(s_path , FILTER_JPG , comp);//画質設定をしたJPGで保存。
}

JPEG画質(圧縮)の設定はBaseContainerにID:JPGSAVER_QUALITYに0~100までの整数で設定しそれをBaseBitmapクラスのSave()メンバ関数の第3引数で渡します。
画像ファイルの保存は画像とパスとファイルタイプを指定すれば良いので保存ダイアログ付きの汎用性のある関数にする?
渡す引数はファイルパスのFilenameクラス変数と画像のBaseBitmapクラス変数とファイルタイプ、ファイル名に追加するstring、画質設定値の整数。
エラー処理は省きました。ごめんね。間違った変数を渡すとCOFFEEエラーが出るね。間違わないように変数を渡そう。

BitmapSave(path , b_map , typ , add_fname , qlt)//ファイルパス、ビットマップ、クオリティ、追加ファイル名、タイプ
{
  var suffix;
  switch(typ)
  {
    case FILTER_JPG:
      suffix = "JPG";
      break;
    case FILTER_TIF:
      suffix = "TIF";
      break;
    case FILTER_TGA:
      suffix = "TGA";
      break;
    case FILTER_BMP:
      suffix = "BMP";
      break;
    case FILTER_PICT:
      suffix = "PCT";
      break;
    case FILTER_IFF:
      suffix = "IFF";
      break;
    case FILTER_PSD:
      suffix = "PSD";
      break;
    default:
      return FALSE;
  }

  path->DelSuffix();
  path->SetLastString(path->GetLastString() + add_fname + "." + suffix);
  var res = path->FileSelect(suffix + "画像の保存" , TRUE);
  if(!res)return FALSE;
  path->SetSuffix(suffix);

  if(typ == FILTER_JPG){
    var comp = new(BaseContainer);
    comp->SetData(JPGSAVER_QUALITY , qlt);
    res = b_map->Save(path , typ , comp);
  }else res = b_map->Save(path , typ);//JPG以外

  return res;
}

これで、他のタイプの保存にも対応できるか?main()関数を追加して実験してみよう。
今度は読込んで保存するのみ。

main(doc , op)
{
  var l_path = new(Filename);
  var bm = new(BaseBitmap , 1 , 1);
  var res = l_path->FileSelect("Open Files" , FALSE);
  if(!res)return;
  bm->Load(l_path);

  ShowBitmap(bm);

  var s_path = l_path->GetClone();
  res = BitmapSave(s_path , bm , FILTER_PSD , "のコピー" , 0);//ファイルタイプを変えてみよう。FILTER_JPG/FILTER_TIF/FILTER_TGA/FILTER_BMP/FILTER_PICT/FILTER_IFF/FILTER_PSD
}

では次に行こう。
BaseBitmapクラスは、動画をフレーム指定で読込む事ができます。画像ファイルと同じでLoad()メンバ関数を使います。Load(Filename , frame)です。動画も同様C4Dで読込めるファイル(mov/avi等)であれば気にせず簡単に読めます。

/*
動画ファイルの指定フレームの読込みテスト

08.05.31
*/


var path = new(Filename);
var bm = new(BaseBitmap , 1 , 1);

var res = path->FileSelect("動画ファイルを開いてください" , FALSE);
if(!res)return;

bm->Load(path , 10);//フレーム10を読み込みます。

ShowBitmap(bm);

次回に続く…

|

« Cinema 4Dスクリプトで画像ファイルを読み込む | トップページ | スクリプトで動画ファイルの作成 »

コメント

この記事へのコメントは終了しました。