[ 前へ | 目次 | 次へ ]
Now Loading...
ダウンロード
PC (※スマートフォンでは動きません) でダウンロードし、ZIPファイルを右クリックメニューから展開して、できたフォルダ内の「 VCSSL.bat(バッチファイル) 」をダブルクリックすると起動します。 Linux等では「 VCSSL.jar 」をコマンド実行してください。
» 詳しい使用方法や、エラーで展開できない際の対応方法などはこちら

条件を満たす色を透明にする簡易ツール(複数ファイル一括処理版)

このプログラムは、フォルダ内にある全ての画像ファイル(PNG形式またはJPEG形式)を開き、条件に一致する範囲の色を透明に置き換えた上で、別のフォルダに保存する、VCSSL製の簡易ツールです。

グラフィック作業や画像解析などで、画像内の特定の部分だけを残して、 それ以外の部分を透明化したいといった場面はよくありますね。 前回は、そのような処理を行う簡単なツールとして、 画像内の特定色を透明で置き換えるものを公開しました:

単純に、特定色を透明で置き換える例
前回のコードで行った、特定色からの単純な一致比較・置き換え処理です。透明化したい部分を、あらかじめ均質な色で塗れる場合には便利です。しかし、色ムラがあると対応できません。

上記ような、単色を透明で置き換えるツールは、透明化したい部分を均一な色で着色できるような場合には、単純で便利です。 自分で元の画像を描いていて、「 緑色で塗った部分を透明にしたい 」といった用途であれば、このページのツールよりも、上記のツールの方がおすすめです。

しかし、「 カメラで撮影した写真の中で、緑っぽい部分を透明にしたい 」といった場合には、 透明化対象の部分にはある程度の色ムラがあって、「 この色 」と1個に定める事ができないため、 上記のような単純な方式はあまり役に立ちません。

そこで今回のプログラムでは、透明化対象を単色ではなく、 RGB成分が満たす条件で指定できるようになっています。 例えば、「 赤色成分が50以下、かつ緑色成分が100以上の部分を透明化 」といった処理が可能です。

透明化対象の色を、RGB成分が満たす条件で指定する
透明化対象の色に幅をもたせる事ができ、透明化したい部分と残したい部分を、条件指定によって調整できます。

これなら、透明化対象の色に幅をもたせる事ができるので、元の画像にある程度の色ムラがあっても、透明化したい部分と残したい部分を、条件指定によって自由に調整できます。

なお、画像ファイル1個だけに対して、同様の処理を行うプログラムは、既に「 特定の色を透明にする簡易ツール 」の回で扱いました。 ファイルを1個だけ処理したい場合は、そちらをご利用ください。 今回のプログラムは、それをフォルダ内の複数ファイルに対して一括処理したい場合のために、少し改造したものです。

使用方法

ダウンロードと展開(解凍)

まず、PC(スマホは未対応)で上の画面の「 ダウンロード 」ボタンを押してください。 するとZIP形式で圧縮されたファイルがダウンロードされます。

Windows をご使用の方は、ここでまずZIPファイルを右クリックし、「プロパティ」を選んで開かれる画面で、 下の方にあるセキュリティ項目の「許可する」にチェックを入れて「OK」で閉じてください。 これを行わないと、ZIP展開やソフト起動時に、警告メッセージが出て展開完了/起動できない場合があります。

その後、ZIPファイルを右クリックして「すべて展開」や「ここに展開」などで展開(解凍)してください。 展開が成功すると、ZIPファイルと同じ名前のフォルダができ、その中にZIPファイルの中身が入っています。

» 展開がエラーで止まってしまう場合や、ファイル名が文字化けしてしまう場合は…

プログラムの起動

Windows をご使用の場合

上記でZIPファイルを展開したフォルダ内にある、以下のバッチファイルをダブルクリック実行してください:

VCSSL__ダブルクリックでプログラム実行.bat

もしプログラムを書き変えながら使いたい場合は、代わりに「 VCSSL_Editor__プログラム編集はこちら.bat 」を実行してください。

正常に起動できると、初回のみ、Java実行環境を入手するか等を尋ねられるので、適時答えて済ませると、プログラムが起動します。 2回目以降はすぐに起動します。

» うまく起動できずにエラーになってしまう場合は…

Linux 等をご使用の場合

ZIPファイルを展開したフォルダ内へコマンドライン端末で cd して、以下の通り入力して実行してください:

java -jar VCSSL.jar
(プログラムの内容を書き変えながら使いたい場合は、代わりに VCSSL_Editor.jar を実行)

» javaコマンドが使用できない等のエラーが表示される場合は…

実行中にメモリー容量が不足する場合は…

このプログラムは、使い方によっては、それなりに多くのメモリーを使用します。 そのため、デフォルトのメモリー容量設定では不足する場合があります。 その場合は以下の対処方法をご参照ください:

» 実行中にメモリー容量が不足する場合(Microsoft® Windows® をご使用の場合)
» 実行中にメモリー容量が不足する場合(Linux® 等やその他のOSをご使用の場合)

※ 目安として、4288 x 2848 サイズ(約1200万画素)の写真データを加工する場合、大体 3GB(≒3000MB)くらい割り当てれば動くようです。

透明化したい部分を、RGB値の条件で指定

起動すると、まず 透明化したい部分の色が満たす条件 を尋ねられるので、 テキストフィールドに記入します。 条件は、以下の書き方に基づいて記述してください。

条件の書き方
条件の記述スタイル(文法)
C言語で if 文の中に書くような(条件式の)スタイルで記述します。正確には、C言語ではなくVCSSLの文法で書く必要がありますが、条件式については大体同じです。
RGBの各成分を表す変数
赤成分は「 r 」、緑成分は「 g 」、青成分は「 b 」 という名前の変数で表します。
「〜以上」や「〜未満」といった関係
「 <= 」や「 < 」、「 >= 」や「 > 」といった、不等号の記号が使えます(イコールは必ず < > の右側に書いてください)。
例: r <= 100    (赤成分が100以下の色を透明化)
一致と不一致
「 一致する 」は「 ==(イコールが2個連続) 」、「 一致しない 」は「 != 」で表せます。
例: r == 100    (赤成分がちょうど100の色を透明化)
「 かつ 」や「 または 」
「 かつ 」は「 && 」、「 または 」は「 || 」で表せます。 複数組み合わせて使用する場合は、必ずカッコ ( ) を使用して、かかる範囲をはっきりさせる必要があります( この点はC言語よりも厳しくなっています )。
例: (r < 50 && g >= 80) || b > 100
(『「赤が50未満かつ緑が80以上」か、または青が100より大きい』部分を透明化)

※「RGB値なんて初耳!よくわからない!」という方は、下記ページで詳しい解説と色表示ツールの公開を行っていますので、まずはそちらをご参照ください。

ここでは、サンプル画像の緑っぽい部分から青っぽい部分にかけてを透明化してみましょう。 テキストフィールドに「 (r < 50 && g >= 80) || b > 100 」と入力して「 OK 」を押してください。 (試しやすいように、デフォルトでこの内容になっています。)

入力先フォルダの選択

その後は、入力先フォルダ(加工したい画像ファイルが入っているフォルダ) を指定するか尋ねられるので、 指定したい場合は「 はい 」を選択して、続けてフォルダの場所を選択してください。

「 いいえ 」を押してスキップすると、ダウンロード・展開したフォルダ内にある「 input 」という名前のフォルダが、自動的に入力先フォルダとみなされます。 この中には以下のサンプル画像「 sample.png 」が入っているので、初めての場合はここでは「 いいえ 」を選択してみましょう。 このサンプル画像の内容は下記の通り、様々な色がグラデーションで混ざっています。

サンプル画像
サンプル画像「 sample.png 」
ダウンロード・解凍したフォルダ内にある、「 input 」フォルダ(デフォルトの入力先フォルダ)内に同梱されています。

なお、このツールではPNG形式の他にも、JPEG形式の画像もサポートしています。 ただ、JPEG形式は不可逆圧縮に際して色のむらが増加するため、PNG形式とJPEG形式のどちらのファイルもある場合は、PNG形式の方が無難だと思います。 (※ だからといって、既にJPEG形式になっているファイルをPNG形式で保存しなおす必要はありません。JPEG画像しか無い場合はJPEGのままでOKです。)

出力先フォルダの選択

続いて同様に、出力先フォルダ(加工済みの画像ファイルを保存するフォルダ) を指定するか尋ねられるので、 指定したい場合は「 はい 」を選択して、続けてフォルダの場所を選択してください。

ここでも「 いいえ 」を押してスキップすると、ダウンロード・展開したフォルダ内にある「 output 」という名前のフォルダが、自動的に入力先フォルダとみなされます。 初めての場合はここでも「 いいえ 」を選択してみましょう。

透明化処理の実行

あとは放っておけばOKです。入力先フォルダ内にある全てのPNG形式画像ファイルが、 指定色を透明化した上で、出力先フォルダ内に自動で保存されます。 その際、保存されるファイル名は、元のファイル名の末尾に「 _clear.png 」が付いたものになります。 画像形式はPNGになります。

入力先&出力先フォルダの指定を「 いいえ 」でスキップした場合は、 「 input 」フォルダ内にある先ほどのサンプル画像を透明化したものが、 「 output 」フォルダ内に保存されているはずです。 その内容は下図の通りです(実際にこのツールで変換したファイルそのものを表示しています)。 ちゃんと、一部が透明化されていますね。この透明になった部分が、先ほど入力した条件が満たしていた部分です。

サンプル画像の透明化結果
サンプル画像を透明化した結果「 sample.png_clear.png 」
元画像から、条件で指定した色の部分が透明になっています。

コード解説

このプログラムのコードはVCSSLで記述されています。

条件を満たす色を、透明ではなく別の色に置き換えたいといった場合には、 プログラムのコード「 ConditionedColorToClearMulti.vcssl 」をテキストエディタで開いて改造してください。 スクリプト言語なので、コンパイラなどの別ソフトは不要で、コードを書き換えるだけでOKです。 VCSSLはC系の単純な文法の言語なので、C言語などに触れた事のある方なら簡単に読めると思います。

なお、このコードは「 条件を満たす色を透明にする簡易ツール 」の回のコードを土台に、 複数ファイルを一括処理するように改造したものです。 具体的には、上記の回のコードの処理を関数にまとめて、フォルダ内の全ファイルに対して毎回呼び出して実行しているような流れです。 なので、最初に上記の回のコードから読み進めるほうがスムーズかもしれません。

また、似たような「単一ファイル処理 → 複数ファイル一括処理」への改造を、 ちょうど前回 「 特定の色を透明にする簡易ツール(複数ファイル一括処理版) 」 のコードでも行いました。実は今回のコードは、それをかなりコピペして流用しています。 なので、今回のコードが長く感じる場合は、そちらも先に読んでおくとスムーズかもしれません。

[前提となるコード]

コード全体

それでは、まずはコード全体を見てみましょう。


coding UTF-8;       // 文字コードの明示(文字化け予防)
import Graphics;    // 画像処理に必要なライブラリの読み込み
import File;        // ファイルやフォルダの情報を扱うライブラリの読み込み
import Text;        // 文字列の操作や判定などに必要なライブラリの読み込み
import Math;        // 数学関数ライブラリの読み込み(ユーザーが条件式内で使えるように)


// 入出力フォルダのパスのデフォルト値
const string DEFAULT_INPUT_DIRECTORY = "./input";
const string DEFAULT_OUTPUT_DIRECTORY = "./output";

// 変換する色の条件式をユーザーに入力してもらう
string condition = input("透明化する色の条件は?",   "(r < 50 && g >= 80) || b > 100");

// 変換後の色(透明以外の色にしたい場合はここを書き換える)
int toRed   = 0; // 赤成分
int toGreen = 0; // 青成分
int toBlue  = 0; // 緑成分
int toAlpha = 0; // 不透明度(0で完全な透明、255で完全な不透明)


// ==================================================
// 全体的な処理 - この関数は起動時に自動で実行されます
// ==================================================

void main () {
	
	// 入出力フォルダのフルパスを用意
	string inputDirectoryPath = getFilePath(DEFAULT_INPUT_DIRECTORY);
	string outputDirectoryPath = getFilePath(DEFAULT_OUTPUT_DIRECTORY);
	
	// 入出力フォルダをデフォルトから変更するかユーザーに尋ね、必要なら変更
	if (confirm("入力先/出力先フォルダを指定しますか?(「いいえ」でデフォルトの場所使用)")) {
		inputDirectoryPath = choose("入力先フォルダを選択", ".");
		outputDirectoryPath = choose("出力先フォルダを選択", ".");
	}
	
	// 入力先フォルダ内の全ファイルを処理し、出力先フォルダに保存
	processAllFiles(inputDirectoryPath, outputDirectoryPath);
	
	// 完了メッセージをユーザーに表示
	pop("完了しました。画面上のファイル一覧を確認の上、ウィンドウを閉じてください。");
}


// ==================================================
// フォルダ内の全ファイルを走査して処理する関数
// ==================================================

void processAllFiles(string inputDirectoryPath, string outputDirectoryPath) {
	
	// 入力フォルダ内にあるファイル名の一覧とファイル個数を取得
	string fileNames[] = listDirectory(inputDirectoryPath);
	int fileN = length(fileNames);
	
	// 入力フォルダ内にある個々のファイルについて処理
	for (int i=0; i<fileN; i++) {
		
		// ファイル名の入力フォルダのパスを結合して、ファイルパスに変換
		string inputFilePath = getFilePath(fileNames[i], inputDirectoryPath);
		
		// フォルダではなく、PNG/JPEG形式の拡張子が付いてれば、入力ファイルと見なす
		if (!isDirectory(inputFilePath) && checkText(inputFilePath, ".(png|jpg)", Text.END_PATTERN)) {
			
			// 入力ファイル名から出力ファイル名(入力ファイル名+_clear.png)を求める
			string outputFileName = fileNames[i] + "_clear.png";  // 出力ファイル名
			
			// 出力ファイル名に出力フォルダのパスを合成して、ファイルパスに変換
			string outputFilePath = getFilePath(outputFileName, outputDirectoryPath);
			
			// これから処理する入出力ファイルのパスをコンソールに表示
			println("INPUT: " + inputFilePath);
			println("  > OUTPUT: " + outputFilePath);
			
			// 色の透明化処理を実行
			processFile(inputFilePath, outputFilePath);
		}
	}
}


// ==================================================
// 画像ファイル1個を読み込み、透過処理して保存する関数
// ==================================================

void processFile(string inputFilePath, string outputFilePath) {
	
	// 画像ファイルを開いてグラフィックスデータを生成
	int inputGraphics = newGraphics(inputFilePath);
	
	// 画像の幅と高さ、および全ピクセルの赤,緑,青,α値を配列として取得
	int width  = getGraphicsWidth(inputGraphics);
	int height = getGraphicsHeight(inputGraphics);
	int pixel[][][] = getGraphicsPixel(inputGraphics); // インデックスは[Y][X][色]
	
	// メモリー節約のため、必要なデータを取得した後はグラフィックスデータを解放
	deleteGraphics(inputGraphics);
	
	// 全ピクセルに対して、色を条件判定して置き換える処理を実行
	for (int y=0; y < height; y++) {     // 画像の縦方向へのピクセル単位のループ(上から下)
		for (int x=0; x < width; x++) {  // 画像の横方向へのピクセル単位のループ(左から右)
			
			// このピクセル上での赤,緑,青,α成分を1文字変数に控える
			//(透明化の条件式を簡単に書けるようにするため)
			int r = pixel[y][x][0]; // 赤成分(0〜255)
			int g = pixel[y][x][1]; // 緑成分(0〜255)
			int b = pixel[y][x][2]; // 青成分(0〜255)
			int a = pixel[y][x][3]; // α成分(0〜255、0で完全な透明、255で完全な不透明)
			
			// ユーザーが入力した条件式(グローバル変数condition)が文法的に解釈可能か検査
			if ( !evaluable(condition) ) {
				alert("条件を解釈できません。終了します...");
				exit();
			}
			
			// 条件式を評価し、このピクセルの色が透明化対象であるかどうかを bool 型変数に控える
			// (条件式が文法的に正しくても、値がbool型ではない場合はここで型変換エラーとなる)
			bool shouldReplaceColor = eval(condition);
			
			// 評価結果が true なら透明化対象なので、ピクセルの色を書き換える
			if (shouldReplaceColor) {
				pixel[y][x][0] = toRed;
				pixel[y][x][1] = toGreen;
				pixel[y][x][2] = toBlue;
				pixel[y][x][3] = toAlpha;
			}
		}
	}
	
	// 書き換えたピクセル配列を元にグラフィックスデータを生成
	int outputGraphics = newGraphics(pixel);
	
	// グラフィックスデータの内容に、別のファイル名をつけて保存
	exportGraphics(outputGraphics, outputFilePath, "PNG"); // PNG形式のファイルとして保存
	
	// 保存が済んだグラフィックスデータもすぐ破棄
	deleteGraphics(outputGraphics);
}
ConditionedColorToClearMulti.vcssl

以上です。140行程度のコードですね。 上から下へ順に読むと、トップダウンで処理階層が深く&細かくなる流れになっています。 各部で行っている処理はコード内のコメントの通りですが、 以下では順を追ってもう少し詳しく説明します。

先頭領域

まずは、先頭の5行です。


coding UTF-8;       // 文字コードの明示(文字化け予防)
import Graphics;    // 画像処理に必要なライブラリの読み込み
import File;        // ファイルやフォルダの情報を扱うライブラリの読み込み
import Text;        // 文字列の操作や判定などに必要なライブラリの読み込み
import Math;        // 数学関数ライブラリの読み込み(ユーザーが条件式内で使えるように)
code/import.txt

1行目では、コードのファイルで使用している文字コード(Shift_JIS)を宣言しています。 書かなくても動きますが、書いておくと文字化けを防げます。Shift_JISの他にUTF-8も使用できます。

2行目では、グラフィックスデータを扱うための機能を提供する Graphics ライブラリを読み込んでいます。 ここで「 グラフィックスデータって一体何? 」となるかと思いますが、いわゆる画像データの事だと思ってください。 VCSSLでは、画像を読み込んだり表示したり描画したりといった事に必要なデータを、まとめてグラフィックスデータという形で扱います。

その後に読み込んでいる File ライブラリは、ファイルシステムや各ファイルの情報等にアクセスする機能を提供してくれるものです。 今回はフォルダのフルパスを取得したり、フォルダ内のファイル名の一覧を取得したり、そのファイル名が指す対象がフォルダかどうかを判定するため等に使用します。

Text ライブラリは文字列処理機能を提供してくれるもので、 今回はファイルのパスの末端に付いている拡張子が、このツールが対応しているPNG形式のもの( .png )かどうかを判定するのに使用します。

最後の行では、各種の数学関数を提供する Math ライブラリを読み込んでいます。 と言っても、このコード内では特に使っていません。 ユーザー側が指定する条件式の中で、もしかしたら sin や cos などを使いたいという場合もあるかもしれないので、 そのために読み込んでいます。

グローバル変数

続いて、グローバル変数の宣言部分です。


// 入出力フォルダのパスのデフォルト値
const string DEFAULT_INPUT_DIRECTORY = "./input";
const string DEFAULT_OUTPUT_DIRECTORY = "./output";

// 変換する色の条件式をユーザーに入力してもらう
string condition = input("透明化する色の条件は?",   "(r < 50 && g >= 80) || b > 100");

// 変換後の色(透明以外の色にしたい場合はここを書き換える)
int toRed   = 0; // 赤成分
int toGreen = 0; // 青成分
int toBlue  = 0; // 緑成分
int toAlpha = 0; // 不透明度(0で完全な透明、255で完全な不透明)
code/var.txt

先頭では、入出力フォルダの指定を「 いいえ 」でスキップした際に使用する、デフォルトの入出力フォルダを、定数(const)として定義しています。 なお、名前の末尾に付いている「 DIRECTORY 」は「 ディレクトリ 」と読みますが、これは「 フォルダ 」のちょっと堅い読み方のようなものと思ってください。

その後は、透明化したい色のRGBA値に関する条件式を格納する、文字列の変数を宣言しています。 ユーザーによる条件式の入力も、宣言行で input 関数を呼んでその場で行ってしまっています。 このようにグローバル領域に書いた処理は、タイミング的には main 関数の実行よりも先に行われます。 その後も続いて、変換後の色のRGBA値を格納する変数 toRed, toRed, toRed, toRed を宣言しています。

本当はこれらの条件式やRGBA値などはグローバル変数である必要は無く、main 関数内でローカルに宣言して下層の関数の引数に渡せば(行儀的にも)よいのですが、 こういう短い簡易ツールのコードであまり堅くするのも改造や流用時に面倒になりそうなので、パラメータのまとめ書きっぽくグローバルに置いています。 もし透明化する色の条件式を決め打ちしたかったり、変換後の色を(透明ではなく)別の色にしたい場合は、このあたりの宣言行の右辺を書き換えてください。

全体の処理の大枠になっている main 関数

続いて main 関数です。C言語などではおなじみですが、 main 関数は、プログラム実行時に自動で呼び出される関数です。 ここには処理全体の最も上層になる、大枠的な処理を書いています。


// ==================================================
// 全体的な処理 - この関数は起動時に自動で実行されます
// ==================================================

void main () {
	
	// 入出力フォルダのフルパスを用意
	string inputDirectoryPath = getFilePath(DEFAULT_INPUT_DIRECTORY);
	string outputDirectoryPath = getFilePath(DEFAULT_OUTPUT_DIRECTORY);
	
	// 入出力フォルダをデフォルトから変更するかユーザーに尋ね、必要なら変更
	// (毎回フォルダ選択が面倒な場合はここをコメントアウト)
	if (confirm("入力先/出力先フォルダを指定しますか?(「いいえ」でデフォルトの場所使用)")) {
		inputDirectoryPath = choose("入力先フォルダを選択", ".");
		outputDirectoryPath = choose("出力先フォルダを選択", ".");
	}
	
	// 入力先フォルダ内の全ファイルを処理し、出力先フォルダに保存
	processFilesInDirectory(inputDirectoryPath, outputDirectoryPath);
	
	// 完了メッセージをユーザーに表示
	pop("完了しました。画面上のファイル一覧を確認の上、ウィンドウを閉じてください。");
}
code/main.txt

頭のほうでは、まず入出力フォルダのフルパスを格納する変数を宣言し、そこにデフォルトの入出力フォルダのフルパスを getFilePath 関数で取得して代入しています。 続いて、ユーザーに入出力フォルダを指定したいか尋ねて、「 はい 」が選択された場合は、choose 関数でユーザーに選んでもらったフォルダのパスで上書きしています。

そして、入出力フォルダのフルパスを引数として processAllFiles 関数を呼び出しています。 これはこのコード内で宣言されている関数で、詳細はすぐ後で解説しますが、 フォルダ内の全ファイルを走査し、それぞれに透過処理を実行する処理をまとめたものです。 このツールの目的となっている処理ですね。

その処理が終わると、ユーザーに pop 関数で処理完了を通知しています。 大枠の処理はこれで終わりです。

最後に exit 関数などでプログラムを実行終了してもいいのですが、 そうすると自動でウィンドウが閉じてしまい、ユーザーが処理済みファイルの一覧を確認できなくなってしまうので、 ここでは処理完了メッセージを出すのに留めています。 ユーザーがウィンドウを手で閉じた時点で、プログラムが終了します。

フォルダ内の全ファイルを走査し、透過処理を実行する processAllFiles 関数

続いて、上で見た main 関数の中から呼んでいた、processAllFiles 関数を見てみましょう。 この関数は、引数 inputFilePath に入力先フォルダ、 outputPath に出力先フォルダを受け取ります。 そして、入力先フォルダの中にある全てのPNG形式画像ファイルに対して、透過処理を実行した上で、出力先フォルダに保存します。


// ==================================================
// フォルダ内の全ファイルを走査して処理する関数
// ==================================================

void processAllFiles(string inputDirectoryPath, string outputDirectoryPath) {
	
	// 入力フォルダ内にあるファイル名の一覧とファイル個数を取得
	string fileNames[] = listDirectory(inputDirectoryPath);
	int fileN = length(fileNames);
	
	// 入力フォルダ内にある個々のファイルについて処理
	for (int i=0; i<fileN; i++) {
		
		// ファイル名の入力フォルダのパスを結合して、ファイルパスに変換
		string inputFilePath = getFilePath(fileNames[i], inputDirectoryPath);
		
		// フォルダではなく、PNG/JPEG形式の拡張子が付いてれば、入力ファイルと見なす
		if (!isDirectory(inputFilePath) && checkText(inputFilePath, ".(png|jpg)", Text.END_PATTERN)) {
			
			// 入力ファイル名から出力ファイル名(入力ファイル名+_clear.png)を求める
			string outputFileName = fileNames[i] + "_clear.png";  // 出力ファイル名
			
			// 出力ファイル名に出力フォルダのパスを合成して、ファイルパスに変換
			string outputFilePath = getFilePath(outputFileName, outputDirectoryPath);
			
			// これから処理する入出力ファイルのパスをコンソールに表示
			println("INPUT: " + inputFilePath);
			println("  > OUTPUT: " + outputFilePath);
			
			// 色の透明化処理を実行
			processFile(inputFilePath, outputFilePath);
		}
	}
}
code/processAllFiles.txt

先頭では、まず入力先フォルダ内の全ファイルの名前一覧を、listDirectory 関数で配列として取得し、 その要素数 = ファイルの個数を変数 fileN に控えています。 続く for 文で、それらのファイル名を1個1個辿っていっています。

for 文の冒頭で、ファイル名に入力フォルダのパスを結合し、ファイルパスに変換した変数 inputFilePath を宣言しています。 そして、直後の if 文でその内容を検査し、このツールの処理対象ファイルかどうかを判定しています。

if 文の条件式を日本語にすると「 inputFilePath の指すファイルがフォルダではなく、かつ、inputFilePath の内容が『 .png 』または『 .jpg 』で終わる場合」となります。 前半の条件は、入力フォルダ内でさらにフォルダがある場合に、それを処理対象から除外するためのものです(当然、フォルダを画像ファイルとして開く事はできません)。 後半の条件は、フォルダ内の色々なファイルの中で、PNG形式かJPEG形式の画像ファイルだけを処理対象に含めるためのもので、拡張子の一致確認( .png か .jpg なら通す)をしています。 このように、文字列に対して複数通りの一致確認をするには正規表現が便利で、 実際にこの条件「 .(png|jpg) 」も正規表現で書いてあります ( checkText 関数の最後の引数に渡している END_PATTERNは、文字列の終端に対して正規表現でパターンマッチ判定するオプションです )。

if 文の { 〜 } の内側は、処理対象ファイルに対して行う処理が書かれています。 まず出力ファイル名とそのフルパスを用意した上で、 ユーザーが処理結果を確認しやすいように、処理対象ファイルの入出力先パスを println関数で画面上に表示しています。

そして、引数に処理対象ファイルの入出力先パスを渡して、processFile 関数を呼び出しています。 これもこのコード内で宣言されている関数で、詳しくはすぐ後で説明しますが、このツールの中核である、ファイルの透過処理を行います。

processAllFiles 関数の中身は以上です。 つまるところ、このプログラムの流れとしては、入力先フォルダ内の全ファイルに対して、処理対象ファイルかどうかを確認し、 もしそうであれば、そのファイルに processFile 関数の処理を適用する、という具合になっています。

画像ファイル1個を読み込み、透過処理を行って保存する processFile 関数

それでは最後に、肝心の processFile 関数の中身を見てみましょう。 この関数は、引数 inputFilePath に入力ファイルのパス、outputFilePath に出力ファイルのパスを受け取ります。 そして、入力ファイルを画像ファイルとして開いて、色の変換処理(=透過処理)を行い、 それを出力ファイルパスの場所に、PNG形式画像ファイルとして保存します。

なお、この関数は、条件を満たす色を透明にする簡易ツール の回のコードで行った処理をまとめたものです。詳しい説明については、そちらをご参照ください。 フルで説明するとここだけで結構長くになってしまうので、以下では要点をピックアップして見ていきます。


// ==================================================
// 画像ファイル1個を読み込み、透過処理して保存する関数
// ==================================================

void processFile(string inputFilePath, string outputFilePath) {
	
	// 画像ファイルを開いてグラフィックスデータを生成
	int inputGraphics = newGraphics(inputFilePath);
	
	// 画像の幅と高さ、および全ピクセルの赤,緑,青,α値を配列として取得
	int width  = getGraphicsWidth(inputGraphics);
	int height = getGraphicsHeight(inputGraphics);
	int pixel[][][] = getGraphicsPixel(inputGraphics); // インデックスは[Y][X][色]
	
	// メモリー節約のため、必要なデータを取得した後はグラフィックスデータを解放
	deleteGraphics(inputGraphics);
	
	// 全ピクセルに対して、色を条件判定して置き換える処理を実行
	for (int y=0; y < height; y++) {     // 画像の縦方向へのピクセル単位のループ(上から下)
		for (int x=0; x < width; x++) {  // 画像の横方向へのピクセル単位のループ(左から右)
			
			// このピクセル上での赤,緑,青,α成分を1文字変数に控える
			//(透明化の条件式を簡単に書けるようにするため)
			int r = pixel[y][x][0]; // 赤成分(0〜255)
			int g = pixel[y][x][1]; // 緑成分(0〜255)
			int b = pixel[y][x][2]; // 青成分(0〜255)
			int a = pixel[y][x][3]; // α成分(0〜255、0で完全な透明、255で完全な不透明)
			
			// ユーザーが入力した条件式(グローバル変数condition)が文法的に解釈可能か検査
			if ( !evaluable(condition) ) {
				alert("条件を解釈できません。終了します...");
				exit();
			}
			
			// 条件式を評価し、このピクセルの色が透明化対象であるかどうかを bool 型変数に控える
			// (条件式が文法的に正しくても、値がbool型ではない場合はここで型変換エラーとなる)
			bool shouldReplaceColor = eval(condition);
			
			// 評価結果が true なら透明化対象なので、ピクセルの色を書き換える
			if (shouldReplaceColor) {
				pixel[y][x][0] = toRed;
				pixel[y][x][1] = toGreen;
				pixel[y][x][2] = toBlue;
				pixel[y][x][3] = toAlpha;
			}
		}
	}
	
	// 書き換えたピクセル配列を元にグラフィックスデータを生成
	int outputGraphics = newGraphics(pixel);
	
	// グラフィックスデータの内容に、別のファイル名をつけて保存
	exportGraphics(outputGraphics, outputFilePath, "PNG"); // PNG形式のファイルとして保存
	
	// 保存が済んだグラフィックスデータもすぐ破棄
	deleteGraphics(outputGraphics);
}
code/processFile.txt

まずは、引数に受け取った画像ファイルのパス inputFilePath を newGraphics 関数の引数に渡していますが、 これによってファイルが画像として開かれ、その内容を保持するグラフィックスデータが生成されます。 要するに画像データがメモリー上に展開されます。

画像ファイルを開いた直後には、幅(width)や高さ(height)、 および全ピクセルの色を格納する配列(pixel[][][])など、 変換処理に必要なデータを取りだしています。

(※「 全ピクセルの色を格納する配列 」という概念が分かりづらい方は、この記事の末尾にある解説をご参照ください。)

ここからがいよいよ処理の中核です。 画像の縦方向および横方向のループを回して、 画像内の全ピクセルを一つ一つ巡って、処理していきます。


	// 全ピクセルに対して、色を条件判定して置き換える処理を実行
	for (int y=0; y < height; y++) {     // 画像の縦方向へのピクセル単位のループ(上から下)
		for (int x=0; x < width; x++) {  // 画像の横方向へのピクセル単位のループ(左から右)
			
			// このピクセル上での赤,緑,青,α成分を1文字変数に控える
			//(透明化の条件式を簡単に書けるようにするため)
			int r = pixel[y][x][0]; // 赤成分(0〜255)
			int g = pixel[y][x][1]; // 緑成分(0〜255)
			int b = pixel[y][x][2]; // 青成分(0〜255)
			int a = pixel[y][x][3]; // α成分(0〜255、0で完全な透明、255で完全な不透明)
			
			// ユーザーが入力した条件式(グローバル変数condition)が文法的に解釈可能か検査
			if ( !evaluable(condition) ) {
				alert("条件を解釈できません。終了します...");
				exit();
			}
			
			// 条件式を評価し、このピクセルの色が透明化対象であるかどうかを bool 型変数に控える
			// (条件式が文法的に正しくても、値がbool型ではない場合はここで型変換エラーとなる)
			bool shouldReplaceColor = eval(condition);
			
			// 評価結果が true なら透明化対象なので、ピクセルの色を書き換える
			if (shouldReplaceColor) {
				pixel[y][x][0] = toRed;
				pixel[y][x][1] = toGreen;
				pixel[y][x][2] = toBlue;
				pixel[y][x][3] = toAlpha;
			}
		}
	}
code/loop.txt

まずは、各ピクセルについて、色のRGBA各成分を変数 r, g, b, a として取り出しています。 そして、取り出したRGBA各成分 r, g, b, a の値が、ユーザーに指定された透明化条件を満たすかどうかを判定しています。

ただし透明化条件は、ただの文字列として変数 condition に格納してあるので、 その内容をプログラムのコードと見なして実行する必要があります。 VCSSLでは、eval 関数がそれを行ってくれます。

ただ、ユーザーが色々とおかしな条件式を入力してしまう事もあるので、 ここではまず、変数 condition に控えられた条件式がコードとして正しいかどうか evaluable 関数で確認した上で、 eval 関数で実行(評価)し、結果を bool 型変数 shouldReplaceColor に控えています。

ここでは、まず変数 condition に控えられた条件式がコードとして正しいかどうか確認した上で、 eval 関数で実行(評価)し、結果を bool 型変数 shouldReplaceColor に控えています。 「 shouldReplaceColor 」は「 色を置き換えるべきか 」という意味で、 これが true なら、このピクセルの色は透明化条件を満たしているので、透明色で置き換えるべきだ、という事になります。

後は本当に素直に if 文で、shouldReplaceColor が true ならピクセルの色を透明で置き換えるだけです。 透明の色は、このプログラムの冒頭で変数 toRed 〜 toAlpha に用意しておいたので、 その値をピクセル配列の各色成分に代入しています。

ここまでの処理を全ピクセルについて行えばいいだけなので、for 文もここで閉じていますね。

以上で重要な部分は終わりです。 あとは、変換済みのピクセル配列データから、 画像保存用にグラフィックスデータを生成し、 実際にPNG形式の画像ファイルとして保存した上で、 グラフィックスデータを破棄して終了です。


	// 書き換えたピクセル配列を元にグラフィックスデータを生成
	int outputGraphics = newGraphics(pixel);
	
	// グラフィックスデータの内容に、別のファイル名をつけて保存
	exportGraphics(outputGraphics, outputFilePath, "PNG"); // PNG形式のファイルとして保存
	
	// 保存が済んだグラフィックスデータもすぐ破棄
	deleteGraphics(outputGraphics);
code/export.txt

と、以上の通り説明した processFile 関数内の処理が、フォルダ内の全ての画像ファイルに対して(processAllFiles関数内のループから呼び出される事によって)実行されます。 全画像ファイルに対して処理が終わると、processAllFiles関数のループを抜けて、main 関数の終端に達し、全体の処理は終了します。

( ただし、プログラムを終了させる exit 関数などは呼んでいないので、その後もユーザーがコンソール画面を手動で閉じるまでは、プログラムは「何もしない」状態で待機し続けます。 これは、処理された画像ファイルの一覧を、ユーザーがコンソール画面上でゆっくり確認できるように、あえてそうしています。)

コード内容は以上です。

※ 「 全ピクセルの色を格納する配列 」という概念について

ここで「 全ピクセルの色を格納する配列 」という概念が、 あまりプログラミングで描画や画像処理を扱った事がない場合はイメージし辛いかもしれませんので、少し補足しておきましょう。

テレビやPCの画面は、拡大してよ〜く見ると、「ピクセル」や「画素」と呼ばれる、小さな四角いマス目のような単位が、大量に集まって構成されています。 そして、各ピクセルをさらによく見ると、赤/緑/青の3つの光源がまとめられています。

ピクセルの拡大図
ピクセルの拡大図
画面や画像は、ピクセルが集まって構成されています。各ピクセルはそれぞれ赤/緑/青色の成分を持っています。

この3つの光源の明るさを絶妙に調整する事で、そのピクセル色が表現されています (赤/緑/青は光の三原色と呼ばれ、これらを絶妙なバランスで混ぜ合わせれば、一応は人間に見えるほとんどの色が表せます)。

従って、ピクセル1個の色は、赤(Red)/緑(Green)/青(Blue)の3個の光源の明るさを数値に置き換えれば、データとして表す事ができます。 これはテレビやPC画面だけではなく、いま扱っているような画像データについても全く同じ話です。 この色の表し方をRGBと言います。 一般によく使用されるのは、赤/緑/青成分をそれぞれ 0 〜 255 までの整数で表す方式(24bit RGB)です。 透明を扱うためには、もう一つ「 α(アルファ) 」と呼ばれる成分が追加されます。 これも 0 〜 255 の範囲で扱う事が多く、0 で完全な透明、255で完全な不透明になります。 このように、RGBにαを加えた形式を RGBA またはARGBと呼んだりします。

さて、長い前置きになりましたが、要するに今ここで扱っているような画像は、 データ上は全ピクセルのRGBA値の集合なので、画像内の横方向のピクセル位置を x、縦方向のピクセル位置を y とすれば、

pixel [ y ][ x ][ RGBAのどの成分かを指定する番号 ]

のようにインデックスを割りふった配列で表せます。 配列の要素数は [ 画像の幅 ][ 画像の高さ ][ 色成分の数 = 4 ] です。 実際に上のコードで呼び出している getGraphicsPixel 関数は、 この形式で画像の全ピクセル色情報を配列化して返してくれるものです。

ライセンス

このVCSSL/Vnanoコード( 拡張子が「.vcssl」や「.vnano」のファイル )は実質的な著作権フリー(パブリックドメイン) である CC0 の状態で公開しています。 記事中にC言語/C++/Java言語などでのサンプルコードが掲載されいてる場合は、それらについても同様です。 そのままでのご利用はもちろん、改造や流用などもご自由に行ってください。

※ ただし、このコードの配布フォルダ内には、ダウンロード後すぐに実行できるように、 VCSSLの実行環境も同梱されており、そのライセンス文書は「 License 」フォルダ内に同梱されています (要約すると、商用・非商用問わず自由に使用できますが、使用の結果に対して開発元は一切の責任を負いません、といった具合の内容です)。 配布フォルダ内の各構成物の一覧やライセンスについては「 ReadMe_使用方法_必ずお読みください.txt 」をご参照ください。

※ Vnano の実行環境については、別途スクリプトエンジンのソースコードも一般公開しており、 何らかのソフトウェア内に組み込んでご利用いただく事も可能です。詳細はこちらをご参照ください。

この記事中の商標などについて

  • OracleとJavaは、Oracle Corporation 及びその子会社、関連会社の米国及びその他の国における登録商標です。文中の社名、商品名等は各社の商標または登録商標である場合があります。
  • Windows は、米国 Microsoft Corporation の米国およびその他の国における登録商標です。この記事は独立著作物であり、Microsoft Corporation と関連のある、もしくはスポンサーを受けるものではありません。
  • Linux は、Linus Torvalds 氏の米国およびその他の国における商標または登録商標です。
  • その他、文中に使用されている商標は、その商標を保持する各社の各国における商標または登録商標です。

[ 前へ | 目次 | 次へ ]
画像を任意サイズに拡大・縮小する簡易ツール(複数ファイル一括処理版)

フォルダ内にある全ての画像ファイルを開き、任意のサイズに拡大・縮小して、別のフォルダに保存する簡易ツールです。
画像を任意サイズに拡大・縮小する簡易ツール

画像ファイルを開き、任意のサイズに拡大・縮小して、別名で保存する簡易ツールです。
画像の矩形(四角形)領域を切り抜く簡易ツール(複数ファイル一括処理版)

フォルダ内にある全ての画像ファイルを開き、その中の矩形(四角形)領域を切り抜いて、別のフォルダに保存する簡易ツールです。
画像の矩形(四角形)領域を切り抜く簡易ツール

画像ファイルを開き、その中の矩形(四角形)領域を切り抜いて保存する簡易ツールです。
連番画像をアニメーション再生する簡易ツール

フォルダ内の連番画像ファイルを、動画への変換不要で、そのままアニメーションとして再生できる簡易ツールです。
条件を満たす色を透明にする簡易ツール(複数ファイル一括処理版)

フォルダ内の全画像ファイルに対して、条件を満たす範囲の色を透明に置き換え、別のフォルダに保存する簡易ツールです。
特定の色を透明にする簡易ツール(複数ファイル一括処理版)

フォルダ内にある全てのPNG形式画像ファイルを開き、特定の色を透明に置き換えた上で、別のフォルダに保存する簡易ツールです。
条件を満たす色を透明にする簡易ツール

画像ファイルを開き、指定された条件を満たす色を透明に置き換えて保存する簡易ツールです。
特定の色を透明にする簡易ツール

画像ファイルを開き、特定の色を透明に置き換えて保存する簡易ツールです。
2DCGと3DCGの合成

2DCGと3DCGを一枚に合成し、画面に表示するプログラムの例です。
RGBやカラーコードの色表示と相互変換ができる簡易ツール

RGB値とカラーコードから、GUI画面上で色の表示や相互変換を行う事ができる簡易ツールです。
頂点配列によるモデルの変形アニメーション

頂点配列によってモデルを変形アニメーションさせるサンプルです。
頂点配列によるモデルの作成(四角形格子メッシュ形式)

四角形格子メッシュの形式で、頂点配列からモデルを作成するサンプルです。
この階層の目次
[ 前へ | 目次 | 次へ ]
RINEARN からのお知らせ
※ VCSSL は RINEARN が開発しています。

各ソフトやVCSSLの英語版ドキュメント整備がほぼ完了
2025-06-30 - RINEARNでは2年前から、AIの補助による英語版ドキュメントの大幅拡充計画を進めてきました。今回、主要ドキュメント&コンテンツの英訳がほぼ完了し、一応の目標水準に達しました。詳細をお知らせします。

VCSSLの最新版をリリース: 外部プログラムとの連携機能を少し強化、他
2025-05-25 - VCSSL 3.4.52をリリースしました。外部プログラム(C言語製の実行ファイル等)との連携機能を少し強化し、文字化け対策やOS判別などを可能にしました。他にも細かい機能追加があります。詳細をお知らせします。

VCSSLの最新版をリリース、Java 24上での非互換な挙動を対処
2025-04-22 - VCSSL 3.4.50をリリースしました。Java 24環境上でのネットワークドライブ関連のファイルパス解決で、従来環境とは異なる挙動が生じていたのを解消しました。詳細をお知らせします。

リニアングラフやVCSSLの最新版をリリース、目盛りの位置や内容を自由に指定可能に!
2024-11-24 - リニアングラフ3D/2Dを更新し、自由な位置に、自由な表記内容の目盛りを描けるようになりました! 併せて、Java言語やVCSSLでの、プログラム制御用APIも拡張しています。詳細をお知らせします。

Exevalator 2.2 をリリース、TypeScript 対応によりWebブラウザ上で動作可能に
2024-10-22 - オープンソースの式計算ライブラリ「Exevalator(エグゼバレータ)」の2.1をリリースしました。新たに TypeScript に対応し、Webブラウザ上での式計算にも使えるようになりました。詳細を解説します。

アシスタントAI作成の舞台裏(その2、作成編)
2024-10-12 - アシスタントAIの作成方法解説の後編です。実際にChatGPTの「GPTs」機能を用いて、アシスタントAIを作成する手順や、独自の知識をもたせたり、精度を出すためのノウハウなどを解説しています。

アシスタントAI作成の舞台裏(その1、基礎知識編)
2024-10-07 - アシスタントAI作成方法解説の前編です。今回はまず、アシスタントAIを作る前に抑えておきたい、基礎知識を延々と解説しています。そもそもLLM型AIとはどんな存在か? RAGとは何か? 等々です。

ソフトの利用をサポートしてくれるアシスタントAIを提供開始!
2024-09-20 - RINEARN製ソフトの使い方の質問応答や、一部作業のお手伝いをしてくれる、アシスタントAIを提供開始しました。ChatGPTアカウントさえあれば、誰でも無料で使用できます。使い方を解説します。

Exevalator 2.1 をリリース、新たに Visual Basic に対応
2024-07-28 - オープンソースの式計算ライブラリ「Exevalator(エグゼバレータ)」の2.1をリリースしました。今回から、新たに Visual Basic(VB.NET)でも使用できるようになりました。詳細を解説します。

関数電卓 RINPn(りんぷん)、Esc キーで計算式の一発クリアが可能に
2024-07-20 - 関数電 RINPn の Ver.1.0.2 をリリースしました。今回から、キーボードの「 Esc 」キーを押すと、入力中の計算式を一発でクリアできるようになりました。詳細を解説します。