スポンサーサイト

Tags :
上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

Flashで画像の色の分布を3Dグラフにプロットしてみる

同じネタで引っ張ります。

以前のエントリで、画像の色(『L*a*b*』値)の分布をPhotoshop&JavaScriptで書き出して、それをMacOSX付属の『Grapher』に読み込ませて3D空間にプロットするってのをやりましたが、今回はActionScriptの勉強も兼ねて、Flashで3Dの分布図を作成してみました。3Dの表示には定番のライブラリ『Papervision3D』を利用しています。

PV3Dのコードを書くに当たり、『ClockMaker』さんの一連のエントリ、『フレームアクションで覚える Papervision3D チュートリアル』が非常に参考になりました。

以下が今回作ったFlash。マウスでぐりぐり動かせます。(ホントは『wonderfl』のアカウント取ったんでそっちを貼りたかったんですが、現在ログイン出来なくなってる様なので生のswf貼ります。)


画像の『L*a*b*値』のデータは、以前のエントリのJavaScriptで書き出したものを外部ファイルとしてFlashから読み込んで表示する様になっているので、それを変える事によって色々な画像の色の分布が表示出来ます。

例えば、デフォルトで読み込まれるデータは、AbobeRGBの色域のグラデーションの『L*a*b*』値なんですが、実際に読み込んでるデータはこれになります。

次の画像の『L*a*b*値』を書き出したファイルはこれなんですが、このファイルをダウンロードして、『Load L*a*b* coordinates』ボタンで読み込ませると下の画像の様な表示になります。



一連のエントリの出発点が、『Photoshopでのカラーマネジメントが効いた状態の色をプロットする』という所から始まっているので、画像を直接読み込むのではなく(Flashでカラープロファイルが扱えれば良いんですが)わざわざ座標を書き出してから読み込んで、それを元のRGBに戻すなんて事をしています。他のエントリのJavaScript等もそうなんですが、実用というよりは、『思い付いたものを形に出来るか?』といったアプローチで、訓練としてやってる様なものが多いですね。

今回のActionScriptのソースは以下の通りです。FlashPlayer10で動作確認しています。
package
{	
	import flash.net.*;
	import flash.events.*;
	import flash.text.*;
	import flash.ui.Mouse;
	import flash.system.Security;
	import org.papervision3d.view.*
	import org.papervision3d.objects.*;
	import org.papervision3d.materials.*
	import org.papervision3d.objects.primitives.*
	import org.papervision3d.core.geom.*;
	import org.papervision3d.core.geom.renderables.*;
	import org.papervision3d.materials.special.ParticleMaterial;
	import org.papervision3d.objects.special.ParticleField;
	import org.papervision3d.materials.utils.MaterialsList;
	import net.hires.debug.Stats;
	
	
	[SWF(width = "500", height = "400", frameRate = "30", backgroundColor = "0x000000")]
	
	public class PlotLab extends BasicView
	{

		private var world:BasicView = new BasicView();
		private var file:FileReference = new FileReference();
		private var pChips:Particles = new Particles("pChips");
		private var isMouseDown:Boolean = false;
		private var oldX:Number = 0;
		private var oldY:Number = 0;
		private var nowX:Number = 0;
		private var nowY:Number = 0;
		private var targetRot:Number = 180;
		private var targetPitch:Number = 0;	
		private var rot:Number = 0;
		private var pitch:Number = 0;
		private var text1:TextField = new TextField();
		private var lab_colors:Array = [];
		private var mes1:String = 'Load L*a*b* coordinates';
		private var mes2:String = '...Loading';
		
		public function PlotLab():void {
			
			// via http://5ivestar.org/blog/2008/12/wonderfl-webproxy/ 
			// Thanks!!
			Security.loadPolicyFile("http://5ivestar.org/proxy/crossdomain.xml");
			
			addChild(new Stats({bg: 0x000000, fps: 0xC0C0C0, ms: 0x505050, mem: 0x707070, memmax: 0xA0A0A0}));
			world.startRendering();
			addChild(world);
		
			var loader:URLLoader = new URLLoader();
			loader.addEventListener(Event.COMPLETE, completeHandler);
			
			//by default, loading the coordinates of a fullcolor image on AdobeRGB   
			loader.load(new URLRequest("http://5ivestar.org/proxy/http://files.getdropbox.com/u/271700/gamut_AdobeRGB.txt"));	
			
			file.addEventListener(Event.SELECT, selectHandler);
			file.addEventListener(Event.COMPLETE, onFileLoad);
			
			stage.addEventListener(Event.ENTER_FRAME, enterFarme);
			stage.addEventListener(MouseEvent.MOUSE_DOWN, downHandler);
			stage.addEventListener(MouseEvent.MOUSE_UP, upHandler);
			stage.addEventListener(MouseEvent.MOUSE_MOVE, moveHandler);
			
			makeWorld();
		}
		
		
		private function completeHandler(event:Event):void {
			lab_colors = event.target.data.split('\r');
			makeParticles();
		}
		
		
		private function makeParticles():void {
			
			pChips.removeAllParticles();
			
			text1.text = mes1;
			var coordinates:Array = [];
			var rgb:Object = {};
			var xyz:Object = {};
			var plot_color:String;
			
			for (var i:Number = 0; i < lab_colors.length-1; i++) {
				coordinates = lab_colors[i].split('\t');
				xyz = lab2xyz(coordinates[2], coordinates[0], coordinates[1]);
				rgb = xyz2rgb(xyz.x, xyz.y, xyz.z);
				plot_color = toHexRgb(rgb);
				var particleMat:ParticleMaterial = new ParticleMaterial(parseInt(plot_color), 1)
				var pt:Particle = new Particle(particleMat, 5, coordinates[0]*5, (coordinates[2]-50)*5, coordinates[1]*5);
				pChips.addParticle(pt);
			}
			
		}
		
		
		private function enterFarme(e:Event):void {						  
				// easing: (target - current) * deceleration
				rot += (targetRot - rot) * 0.05;
				pitch += (targetPitch - pitch) * 0.05;
				
				pitch = Math.max(pitch, -90);
				pitch = Math.min(pitch, 90);
				
				world.camera.x = 1000 * Math.sin(rot * Math.PI / 180);
				world.camera.z = 1000 * Math.cos(rot * Math.PI / 180);
				world.camera.y = 1000 * Math.sin(pitch * Math.PI / 180);
				//rot += 1.5;
			}
	
	
		private function downHandler(e:MouseEvent):void {
			isMouseDown = true;
			oldX = mouseX;
			oldY = mouseY;
		}
		
	
		private function upHandler(e:MouseEvent):void {
			isMouseDown = false;
		}
		
	
		private function moveHandler(e:MouseEvent):void {
			if(isMouseDown){
				var dx:Number = e.stageX - oldX;
				var dy:Number = e.stageY - oldY;
				
				targetRot += dx * 0.5;
				targetPitch += dy * 0.5;
				
				oldX = e.stageX;
				oldY = e.stageY;
			}
		}
		
		
		private function makeWorld():void {
			
			text1.addEventListener(MouseEvent.CLICK, onClickLoadButton);
			text1.addEventListener(MouseEvent.MOUSE_OVER, onOverLoadButton);
			text1.addEventListener(MouseEvent.MOUSE_OUT, onOutLoadButton);
			text1.type = TextFieldType.DYNAMIC;
			text1.width = 150;
			text1.height = 30;
			text1.x = 350;
			text1.y = 350;
			text1.textColor = 0xFFFFFF;
			text1.autoSize = TextFieldAutoSize.CENTER;
			text1.border = true;
			text1.borderColor = 0xFFFFFF;
			text1.text = mes2;
			addChild(text1);
		
			var line_mat = new WireframeMaterial(0xAAAAAA);
			line_mat.doubleSided = true;	
			var line_a = new Plane(line_mat, 1, 1000, 1, 1);
			world.scene.addChild(line_a);
			line_a.y = -250;
			line_a.rotationZ = 90;
			var line_b = new Plane(line_mat, 1, 1000, 1, 1);
			world.scene.addChild(line_b);
			line_b.y = -250;
			line_b.rotationX = 90;
			world.scene.addChild(pChips);
		
		}
		
		
		private function onOverLoadButton(e:Event):void {
			Mouse.cursor = flash.ui.MouseCursor.BUTTON;
		}
		
		private function onOutLoadButton(e:Event):void {
			Mouse.cursor = flash.ui.MouseCursor.ARROW;
		}
		
		
		private function onClickLoadButton(e:Event):void {
			file.browse();
		}
		
		
		private function selectHandler(e:Event):void {
			file.load();
			text1.text = mes2;
		}
		
		private function onFileLoad(e:Event):void {
			targetRot = 180;
			targetPitch = 0;
			lab_colors = file.data.toString().split('\r');
			text1.text = mes1;
			makeParticles();
		}
		
		private function toHexRgb(rgb:Object):String {
			var r:String = rgb.r.toString(16);
			var g:String = rgb.g.toString(16);
			var b:String = rgb.b.toString(16);
			if (r.length == 1) r = '0' + r;
			if (g.length == 1) g = '0' + g;
			if (b.length == 1) b = '0' + b;
			var ret:String = '0x' + r + g + b;
			return ret;
		}
		
		
		private function lab2xyz( l:Number, a:Number, b:Number ):Object {
			const REF_X:Number = 95.047; // Observer= 2digrees Illuminant= D65
			const REF_Y:Number = 100.000; 
			const REF_Z:Number = 108.883; 
			var y:Number = (l + 16) / 116;
			var x:Number = a / 500 + y;
			var z:Number = y - b / 200;
			if ( Math.pow( y , 3 ) > 0.008856 ) { y = Math.pow( y , 3 ); }
			else { y = ( y - 16 / 116 ) / 7.787; }
			if ( Math.pow( x , 3 ) > 0.008856 ) { x = Math.pow( x , 3 ); }
			else { x = ( x - 16 / 116 ) / 7.787; }
			if ( Math.pow( z , 3 ) > 0.008856 ) { z = Math.pow( z , 3 ); }
			else { z = ( z - 16 / 116 ) / 7.787; }
			var xyz:Object = {x:0, y:0, z:0};
			xyz.x = REF_X * x;  
			xyz.y = REF_Y * y;
			xyz.z = REF_Z * z;
		 
			return xyz;
		}
		
		
		private function xyz2rgb(X:Number, Y:Number, Z:Number):Object {
			var x:Number = X / 100;        
			var y:Number = Y / 100;        
			var z:Number = Z / 100;        
			var r:Number = x * 3.2406 + y * -1.5372 + z * -0.4986;
			var g:Number = x * -0.9689 + y * 1.8758 + z * 0.0415;
			var b:Number = x * 0.0557 + y * -0.2040 + z * 1.0570;
		 
			if ( r > 0.0031308 ) { r = 1.055 * Math.pow( r , ( 1 / 2.4 ) ) - 0.055; }
			else { r = 12.92 * r; }
			if ( g > 0.0031308 ) { g = 1.055 * Math.pow( g , ( 1 / 2.4 ) ) - 0.055; }
			else { g = 12.92 * g; }
			if ( b > 0.0031308 ) { b = 1.055 * Math.pow( b , ( 1 / 2.4 ) ) - 0.055; }
			else { b = 12.92 * b; }
			var rgb:Object = {r:0, g:0, b:0}
			var tmp_r = Math.min(r*255, 255);
			var tmp_g = Math.min(g*255, 255);
			var tmp_b = Math.min(b*255, 255);
			rgb.r = Math.max(tmp_r, 0);
			rgb.g = Math.max(tmp_g, 0);
			rgb.b = Math.max(tmp_b, 0);
			return rgb;
		}
	
	}
}

画像の色の分布を『xy色度図』にプロットしてみる(あと、CMYKプロファイルの事も少し)

MacOSX付属の『Grapher』を使った画像の色の分布図の作成その2。

今回は、カラーマネジメント関係の書籍等には必ず出てくる、馬蹄形の『xy色度図』上の座標に、画像の色の分布をプロットしてみます。

前のエントリではPhotoshopで画像を開き、JavaScriptを使ってサンプリングしたピクセルの『L*a*b*値』をファイルに書き出して、そのファイルをGrapherに読み込ませて表示させましたが、今回はピクセルの色を『xyY』の値で書き出すので、『L*a*b*』から『xyY』の値に変換する部分をJavaScriptに新たに書き加えました。

今回は色度を表す『xy』の二次元のグラフなので、明度を示す『Y』の値は使いません。

『L*a*b*』から『xyY』ヘの変換式は、
Color Conversion Algorithms 』(http://www.cs.rit.edu/~ncs/color/t_convert.html)や、
Color Conversion Library in ANSI C』 (http://www.tecgraf.puc-rio.br/~mgattass/color/ColorIndex.html)
あたりをを参考にしています。

PhotoshopのAdobeRGB環境で下のグラデーデョン画像を開き、JavaScriptを実行して書き出されたファイルをGrapherで読み込んで、色度図にプロットしてみます。


赤と緑の三角形の部分はそれぞれ『AdobeRGB』と『sRGB』の色域を表していますが、それらは色度図上の値をあらかじめ調べておいてグラフに数値を入力しました。また、グレーの馬蹄形の部分、360nm~830nmの各波長の単色光の色度を示す『スペクトル軌跡』も別途計算して求めた数値を読み込ませてプロットしてあります。


上のグラデーション画像を『JapanColor2001corted』のプロファイル、レンダリングインテントが『知覚的』の設定でCMYKに変換した場合の色の分布を見てみると、色域が以下の様に圧縮されているのが解ります。



次に、下の画像は始めからCMYKモードで作成した、CMYK各色100%と、その二次色のレッド、グリーン、ブルーをグラデーションにしたもので、


これを『JapanColor2001corted』で開いた状態でプロットしてみると、非常になめらかな色の分布を持つグラフがプロットされました。


同じ画像を、『某印刷所の本機(枚葉)の印刷物を測色して作成したプロファイル』を適用した状態でプロットすると、

ややいびつな輪郭でプロットされました。グリーン(上の頂点の部分)の色域がやや狭く、イエロー方向に転んでいるのが見て取れます。

ただ、これはいびつなプロファイルが悪いと言うのではなく、あくまで『プロファイル』として実際の印刷物の状態を表したものなので、特に、JapanColorをターゲットとした『印刷の標準化』が行われていない印刷会社では、おそらく色が合わない等の問題も出てくる筈なので、必ずしも『JapanColor2001corted』の方が良い結果になるとは言い切れないと思います。

今回プロットした画像を見て思ったんですが、『JapanColor2001corted』の不自然なほど整ったプロファイルは、『理想的なJapanColor』をターゲットにして最適化の処理がされた、『架空のプロファイル』に近いものの様な気がします。

カラーマネジメントの書籍等では『CMYKのカラー設定はJapanColor2001corted推奨』というのが殆どで、もちろん出鱈目なプロファイルで運用する事に比べればベストに近い選択という事で異論は無いんですが、画像をCMYK分解する時点ではトーンジャンプも無くキレイに分解されていても、後の工程でRIPにデバイスリンクプロファイルを噛ましたり、刷版や印刷で色を調整する必要が出るとすれば、『画像が劣化する要因が他の工程に移っただけ』とも考えられるので、その辺りもツッ込んで解説してあればなぁとか思った次第です。(画像の段階で劣化しないのが望ましいとは思いますが)

画像を『xyY』の数値で書き出すのに使ったJavaScriptは以下の通り。

■動作確認
MacOSX 10.5.7
Photoshop CS4
#target Photoshop

var samplingPixelsAsYxy = {
	
	'tristimulus_values': {
		/* 2 degrees */
		'ccTA_2': [109.850, 100.000, 35.585],
		'ccTC_2': [98.074,  100.000, 118.232],
		'D50_2': [96.422,  100.000, 82.521],
		'D55_2': [95.682,  100.000, 92.149],
		'D65_2': [95.047,  100.000, 108.883],
		'D75_2': [94.972,  100.000, 122.638],
		'F2_2': [99.187,  100.000, 67.395],
		'F7_2': [95.044,  100.000, 108.755],
		'F11_2': [100.966, 100.000, 64.370],

		/* 10 degrees */
		'A_10': [111.144, 100.000, 35.200],
		'C_10': [97.285,  100.000, 116.145],
		'D50_10': [96.720,  100.000, 81.427],
		'D55_10': [95.799,  100.000, 90.926],
		'D65_10': [94.811,  100.000, 107.304],
		'D75_10': [94.416,  100.000, 120.641],
		'F2_10': [103.280, 100.000, 69.026],
		'F7_10': [95.792,  100.000, 107.687],
		'F11_10': [103.866, 100.000, 65.627],
	
		/* PCS illuminant of AdobeRGB & sRGB profiles */
		'PCS_illuminant': [96.420, 100.000, 82.491],
		
		/* reference white in xyY coordinates */
		'chromaD65': [0.3127, 0.3290, 100.0]
	},


	'convertCIELabToXYZ': function(L, a, b) {
		var ref = this.tristimulus_values['PCS_illuminant']; //tristimulus of reference white
		var X, Y, Z;
		
		/* via http://www.cs.rit.edu/~ncs/color/API_JAVA/XYZSet.java
		var frac = (L + 16) / 116;
		if ( L < 7.9996 ) {
			Y = L / 903.3;
			X = a / 3893.5 + Y;
			Z = Y - b / 1557.4;
		} else {
			var tmp = frac + a / 500;
			X = tmp * tmp * tmp * ref[0];
			Y = frac * frac * frac * ref[1];
			tmp = frac - b / 200;
			Z = tmp * tmp * tmp * ref[2];
		}
		*/
		
		/* via http://www.tecgraf.puc-rio.br/~mgattass/color/CIELabtoXYZ.htm */
		var var_Y = ( L + 16 ) / 116;
		var var_X = a / 500 + var_Y;
		var var_Z = var_Y - b / 200;
		var_Y = Math.pow(var_Y, 3) > 0.008856 ? Math.pow(var_Y, 3) : ( var_Y - 16 / 116 ) / 7.787;
		var_X = Math.pow(var_X, 3) > 0.008856 ? Math.pow(var_X, 3) : ( var_X - 16 / 116 ) / 7.787;
		var_Z = Math.pow(var_Z, 3) > 0.008856 ? Math.pow(var_Z, 3) : ( var_Z - 16 / 116 ) / 7.787;
		X = var_X * ref[0];
		Y = var_Y * ref[1];
		Z = var_Z * ref[2];
		//
		return [X, Y, Z];
	},

	'main': function() {
		var t = new Date();
		var outputFile = '~/Desktop/xy_colors_64x64.txt';
		var sampling_size = 64;
		//var threshold = 10;
		var sampling_data = '';
		var ru = app.preferences.rulerUnits;
		app.preferences.rulerUnits = Units.PIXELS;
		var doc = app.activeDocument;
		doc.flatten();
		doc.resizeImage(sampling_size, sampling_size, 72, ResampleMethod.BICUBIC);
		//doc.resizeImage(sampling_size*2, sampling_size*2, 72, ResampleMethod.NEARESTNEIGHBOR);
		
		var smpl = doc.colorSamplers.add([0, 0]);
		var col = new SolidColor();
		var Yxy_x, Yxy_y;
		var XYZ = [];
		var X, Y, Z;
		for (var i=0; i<sampling_size; i++) {
			for (var j=0; j<sampling_size; j++) {
				smpl.move([j+0.5, i+0.5]);
				col = smpl.color;
				XYZ = this.convertCIELabToXYZ(col.lab.l, col.lab.a, col.lab.b);
				X = XYZ[0]; Y = XYZ[1]; Z = XYZ[2];
				Yxy_x = X / ( X + Y + Z );
				Yxy_y = Y / ( X + Y + Z );
				if (Y > 1) { sampling_data += Yxy_x + '\t' + Yxy_y + '\r'; }
			}
		}
		
		var f = new File(outputFile);  
		if (f.open("w")) {  
			f.encoding = 'BINARY';  
			f.write(sampling_data);
			f.close();
		}
		app.preferences.rulerUnits = ru;
		var now = new Date();
		alert('Finished!   time: ' + (now - t));
	}
}

if (app.documents.length > 0) {
	samplingPixelsAsYxy.main();
}

画像の色の分布を3Dグラフにプロットしてみる

MacOSXに付属しているグラフ作成アプリ『Grapher』を使って、画像の色(L*a*b*値)の分布を3Dのグラフにプロットしてみました。

この手の情報の表示は、高価なカラーマネジメントツールを買うとか、年間18万9千円の会費を払って研究会に参加すれば実現できると思いますが、手持ちのツールを使えばまあそこそこ近い事は出来るのではないかと思いやってみました。先日のエントリ、『PhotoshopでのCMYK分解カーブをJavaScriptを使ってプロットしてみる』を書いたのも、そんなふうに思ったからです。

まず、JavaScriptを使ってPhotoshopで開いている画像を64 x 64ピクセルに縮小し、その4096のポイントをサンプリングしてそれぞれのL*a*b*値をタブ区切りのテキストでファイルに書き出します。そしてそれをGrapherで読み込んで、3Dのグラフにプロットする様にしました。

Photoshopのカラー設定でAdobeRGBが設定された状態で、下のグラデーション画像のL*a*b*値を書き出してプロットすると、



AdobeRGBの色域一杯に色が均等に分布したグラフが現れました。

このグラフは横軸にa*チャンネル(-128~128)、縦軸がb*チャンネル(-128~128)、垂直のZ軸がL*チャンネル(0~100)になっています。

同じ画像がsRGBの色空間だと、

こんな感じになります。(比較の為にAdobeRGBのプロットを表示しています)

そして下の画像はAdobeRGBのカラースペースで開いてプロットしましたが、色の分布はsRGBの空間に収まっているようです。画像を保存するまでの過程でプロファイル変換されているのかも知れません。




Photoshop&JavaScriptでピクセルの色を取得してファイルに書き出す所で、CMYK分解カーブのプロットの時と同じく、ピクセル単位での操作が今ひとつ解らず、色を取得するのに1ピクセルの選択範囲を作ってその中のヒストグラムが立った数値を調べるという回りくどい事をしています。(なので処理は劇遅。カラーサンプラーを置いて取得するのも試みましたが、さらに遅かったです)

あと、Photpshopで色を扱う、『SolidColor』オブジェクトは、RGBなど1つのカラーモードの値を与えてやれば、後はカラー設定の作業用スペースに設定されたプロファイルに応じて、CMYKやL*a*b*の値が一意に決まるので簡単に他のカラーモードでの値を取り出せるみたいです。ただ、ドキュメントのカラープロファイルは無視される様なので注意が必要です。

■2009.06.06追記
ちょっとやっつけだったコードを直しました。ピクセルの色の取得にカラーサンプラを使う様に変更してあります。ほんの少しだけですが速くなりました。


■2010.03.08追記
このエントリで使用している『Grapher』のファイルをアップロードしました。以下のリンクから落とせます。
http://dl.dropbox.com/u/271700/cielab.gcx.zip


■画像のL*a*b*値を書き出すJavaScriptのソース(遅いです)
#target Photoshop

/*
function getPixelColor(x, y) {
	var hg;
	doc.selection.select([[x, y], [x + 1, y], [x + 1, y + 1], [x, y + 1]], SelectionType.REPLACE, 0, false);
	hg = doc.channels[0].histogram;
	for (var i=0; i<256; i++) if (hg[i] != 0) { colObj.rgb.red = i; break; }
	hg = doc.channels[1].histogram;
	for (var i=0; i<256; i++) if (hg[i] != 0) { colObj.rgb.green = i; break; }
	hg = doc.channels[2].histogram;
	for (var i=0; i<256; i++) if (hg[i] != 0) { colObj.rgb.blue = i; break; }
}
*/

var t = new Date();
var sampling_size = 64;
var ru = app.preferences.rulerUnits;
app.preferences.rulerUnits = Units.PIXELS;
var doc = app.activeDocument;
doc.flatten();
doc.resizeImage(sampling_size, sampling_size, 72, ResampleMethod.BICUBIC);
doc.resizeImage(sampling_size*2, sampling_size*2, 72, ResampleMethod.NEARESTNEIGHBOR);

var smpl = doc.colorSamplers.add([0, 0]);
var sampling_data = '';

for (var i=0; i<sampling_size*2; i+=2) {
	for (var j=0; j<sampling_size*2; j+=2) {
		smpl.move([j+1, i+1]);
		sampling_data += smpl.color.lab.a + '\t' + smpl.color.lab.b + '\t' + smpl.color.lab.l + '\r';
	}
}

var f = new File('~/Desktop/clelab_colors_64x64.txt');  
if (f.open("w")) {  
	f.encoding = 'BINARY';  
	f.write(sampling_data);
	f.close();
}

app.preferences.rulerUnits = ru;
alert('Finished.   time: ' + (new Date() - t));

AppleScriptのシンタックスをハイライトするJavaScriptを少し改良

ウチのblogではAppleScriptとかJavaSciptのソースコードをいろいろ書き散らして晒してますが、ソースコードの構文を色分け表示するのに、AppleScriptの場合は自作のJavaScriptを使ってハイライト表示させています。(AppleScript以外のソースコードは、『SyntaxHighlighter』を利用)

で、今回ちょっと不具合があったのを直したついでに少し改良して、スクリプトエディタでの表示と同じ様に、主要な構文がボールドの文字で表示出来る様にしたのと、あとリストのラベル名やAppleEventが含まれるコードもできるだけスクリプトエディタでの色分けに合わせる様にしました。ソースコードのクリップボードへのコピーをする部分は、flashを使った外部のライブラリ『Zero Clipboard』を使っています。

<pre class="applescript">~</pre>でAppleScriptのソースを囲むとこんな感じに表示する様になってます。
on «event appSscrW» theObject given «class eveE»:theEvent
	set theValue to «class conT» of theObject as number
	(«class delY» of theEvent)
	set theValue to theValue + («class delY» of theEvent)
	set theEvent to theEvent
	set «class conT» of theObject to theValue
	tell window of theObject
		set contents of «class texF» "seconds" to contents of «class sliI» "secondsSlider"
		set secsToPlay to contents of «class texF» "seconds"
	end tell
end «event appSscrW»


on create_thumb(tgt_file, dest_file)
	tell application "Adobe Photoshop CS4"
		activate
		open tgt_file showing dialogs never
		set theDoc to current document
		tell theDoc
			if width > height then
				resize image width thumb_size as pixels resolution 72 resample method bicubic
			else
				resize image height thumb_size as pixels resolution 72 resample method bicubic
			end if
			change mode to RGB
			convert to profile "sRGB IEC61966-2.1" intent relative colorimetric with blackpoint compensation and dithering
			set save_opts to {class:JPEG save options, embed color profile:false, quality:10}
			save in file dest_file as JPEG with options save_opts appending no extension with copying
			close without saving
		end tell
	end tell
end create_thumb


(*  via MacWiki http://macwiki.sourceforge.jp/wiki/index.php/AppleScriptTips *)
set chars to {¬
	{uc:"A", lc:"a", ucj:"A", lcj:"a"}, {uc:"B", lc:"b", ucj:"B", lcj:"b"}, ¬
	{uc:"C", lc:"c", ucj:"C", lcj:"c"}, {uc:"D", lc:"d", ucj:"D", lcj:"d"}, ¬
	{uc:"E", lc:"e", ucj:"E", lcj:"e"}, {uc:"F", lc:"f", ucj:"F", lcj:"f"}, ¬
	{uc:"G", lc:"g", ucj:"G", lcj:"g"}, {uc:"H", lc:"h", ucj:"H", lcj:"h"}, ¬
	{uc:"I", lc:"i", ucj:"I", lcj:"i"}, {uc:"J", lc:"j", ucj:"J", lcj:"j"}, ¬
	{uc:"K", lc:"k", ucj:"K", lcj:"k"}, {uc:"L", lc:"l", ucj:"L", lcj:"l"}, ¬
	{uc:"M", lc:"m", ucj:"M", lcj:"m"}, {uc:"N", lc:"n", ucj:"N", lcj:"n"}, ¬
	{uc:"O", lc:"o", ucj:"O", lcj:"o"}, {uc:"P", lc:"p", ucj:"P", lcj:"p"}, ¬
	{uc:"Q", lc:"q", ucj:"Q", lcj:"q"}, {uc:"R", lc:"r", ucj:"R", lcj:"r"}, ¬
	{uc:"S", lc:"s", ucj:"S", lcj:"s"}, {uc:"T", lc:"t", ucj:"T", lcj:"t"}, ¬
	{uc:"U", lc:"u", ucj:"U", lcj:"u"}, {uc:"V", lc:"v", ucj:"V", lcj:"v"}, ¬
	{uc:"W", lc:"w", ucj:"W", lcj:"w"}, {uc:"X", lc:"x", ucj:"X", lcj:"x"}, ¬
	{uc:"Y", lc:"y", ucj:"Y", lcj:"y"}, {uc:"Z", lc:"z", ucj:"Z", lcj:"z"}}
set theText to "AppleScript アップルスクリプト AppleScript"
set newText to ""
repeat with aChar in every character of theText
	repeat with c in chars
		if aChar as string is uc of c then set aChar to lc of c -- tr/A-Z/a-z/
		-- if aChar as string is lc of c then set aChar to uc of c -- tr/a-z/A-Z/
	end repeat
	set newText to newText & aChar
end repeat
-- display dialog newText
クォートされた文字列を赤くしてるのは、ウチのblogにあるAppleScriptのコードはシェルとか他の言語に処理を投げてるのが多いので、その部分を目立つ様にするためです。

AppleScriptはアプリケーションの数だけ、それこそ無限にプロパティやメソッド等の予約語があるので、完璧に構文を正しく表示するのは難しいですが、基本的にこのJavaScriptでは『変数名とハンドラ名と記号と数字とコメントとクォートされた文字列以外はすべて予約語』として扱ってます。(で、イレギュラーなものを個別に登録して処理。)

変数名やハンドラかどうかは一旦コードを頭からスキャンして判断してるので、コードの一部を載せた様な場合いきなり変数名が出て来ても対応出来ず、スクリプトエディタでコンパイルが通る形のものでないときちんと処理出来ないという弱点はあります。

まあ、今後Adobeはアプリの新機能をAppleScriptでサポートしなくなるというのもあって、AppleScript書く事が少なくなってるので出番はあまり無いかも知れません。

JacvaScriptでInDesignやIllustratorのドキュメントに配置されているEPS画像の情報を取得する

じわじわと過去の物にされようとしているEPSですが、まだまだ製版の現場では出力用の画像として幅を利かせてると思います。が、InDesignやIllustratorでは配置されているEPS画像の解像度等の情報をJavaScriptで得るには画像ファイルを直接調べてみるしか無い様なので、そのための関数を書きました。

一応、IllustratorとInDesign両方で動く様になっています。InDesignの場合はEPSクラスのprototypeを拡張する書き方の方が良いかもしれませんが。

InDesignのJavaScript書く時いつも参考にしているサイト InDesign Scripting ReferenceのEPSクラスのプロパティの所に解像度を示す『actualPpi』『effectivePpi』の記述があるので、もしや?と思ったんですけど本当に値を持ってるのか確認できず…(for ~ inでオブジェクトを見たんですが解像度がらみのプロパティは表示されませんでした。ひょっとして英語版との差とかあるんでしょうか)

■使用例
以下の様に、配置された画像を引数として渡します。
/*
	epsObj は処理するEPS画像

	InDesignの場合はallGraphics等で得られるEPSオブジェクト
	Illustratorの場合はPlacedItemオブジェクトを渡す

*/

 //クリエータを得る
var creator = epsInfo.getCreator(epsObj);

//カラーモードを得る
var mode = epsInfo.getMode(epsObj);

 //エンコーディングを得る
var encoding = epsInfo.getEncoding(epsObj);

 //解像度を得る
var ppi = epsInfo.getPpi(epsObj);

//まとめて連想配列で得る
var epsInfoObj = epsInfo.getAll(epsObj);
最近のPhotoshopやIllustratorで保存されたEPSなら対応出来てると思いますが、変な訛りのあるEPSだとちゃんと動作しないかも知れません。処理出来なかった場合はundefinedが返ります。

■動作確認
MacOSX 10.5.7
InDesign CS4
Illustrator CS4
var epsInfo = {
	'parseHeader': function(imgObj) {
		if (imgObj.constructor.name == 'EPS') { 
			var f = new File(imgObj.itemLink.filePath); // InDesign
		} else if (imgObj.constructor.name == 'PlacedItem') {
			var f = new File(imgObj.file); // Illustrator
		} else {
			return [];
		}
		if (!f.open("r")) return [];
		for (var i=0; i<500; i++) {
			var line = f.readln();
			if (line.indexOf("%%Creator", 0) != -1) {
				var creator = line.slice(10);
			} else if (line.indexOf("%%HiResBoundingBox", 0) != -1) {
				var hrbbParams = line.split(" ");
			} else if (line.indexOf("%ImageData", 0) != -1) {
				var idParams = line.split(" ");
				break;
			}
		}
		f.close();
		return [creator, hrbbParams, idParams];
	},

	'getMode': function(imgObj) {
		var headerObj = this.parseHeader(imgObj);
		if (!headerObj[2]) return;
		var idParams = headerObj[2];
		switch(idParams[4]) {
			case "4":
				return "CMYK";
				break;
			case "3":
				return "RGB";
				break;
			case "2":
				return "Lab";
				break;
			case "1":
				return (idParams[3] == 1) ? "1bit" : "Gray";
				break;
			default:
				return;
		}
	},

	'getEncoding': function(imgObj) {
		var headerObj = this.parseHeader(imgObj);
		if (!headerObj[2]) return;
		var idParams = headerObj[2];
		switch(idParams[7]) {
			case "7":
				return "ASCII85";
				break;
			case "6":
				return "JPEG:Max";
				break;
			case "5":
				return "JPEG:High";
				break;
			case "4":
				return "JPEG:Mid";
				break;
			case "3":
				return "JPEG:Low";
				break;
			case "2":
				treturn "ASCII";
				break;
			case "1":
				return "Binary";
				break;
			default:
				return;
		}
	},

	'getPpi': function(imgObj) {
		var headerObj = this.parseHeader(imgObj);
		if (!headerObj[1] || !headerObj[2]) return;
		var hrbbParams = headerObj[1];
		var idParams = headerObj[2];
		var pt_x = hrbbParams[3];
		//var pt_y = hrbbParams[4];
		var px_x = idParams[1];
		//var px_y = idParams[2];
		return (px_x / pt_x * 72).toFixed(1);
	},

	'getCreator': function(imgObj) {
		var headerObj = this.parseHeader(imgObj);
		return headerObj[0] ? headerObj[0] : undefined;
	},

	'getAll': function(imgObj) {
		return {
			'creator': this.getCreator(imgObj),
			'mode': this.getMode(imgObj),
			'encoding': this.getEncoding(imgObj),
			'ppi': this.getPpi(imgObj)
		}
	}
}
Profile
choco
Author : choco

印刷・製版の現場を経て、広告制作会社でPhotoshopを使ったビジュアル制作を担当。

→現在は車載機器開発ベンダにて、組み込み3Dデータ作成やUIデザインなどを行っています。

Categories
Favorites


Search
上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。