いずれ消え行く無駄な情報を、密やかに発信する装置。つまり日記。
Sigma DP1が出力するファイル形式はX3Fという、なんだかよく分からないファイル形式になっています。その為、ちょっと画像をチェックしたい場合でも、 Sigma Photo ProというRAW現像ソフトを起動しなければなりません。はっきり言ってかなり面倒です。
そんな訳で、X3FファイルからJpegを抜き出すコードをC#で書いてみました。このJpegはRAWを現像したものではなく、カメラ内部で自動生成されるプレビュー用のJpegです。webでX3Fファイルの仕様を解析しているサイトがあったので、非常に簡単にコードはかけました。ライセンスは BSD Licenceで。
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Drawing; using System.IO; namespace X3F_Viewer { /// <summary> /// X3Fファイルを読み込むクラス。DP1専用。 /// /// 履歴 /// 2009/02/27 ver0.1 /// 2009/03/17 ver0.2 ToString()をoverride。 /// /// 参考文献 /// 1. http://rubyist.g.hatena.ne.jp/takuma104/20080505/1210006450 /// 2. http://anotherm8.exblog.jp/7838817/ /// 3. http://www.photofo.com/x3f-raw-format/ /// 4. http://www.photofo.com/downloads/x3f-raw-format.pdf /// </summary> class X3F { /// <summary> /// 画像の形式。 /// </summary> public enum ImageType { /// <summary> /// 通常サイズのJpeg画像。 /// </summary> jpeg = 0, /// <summary> /// サムネイル画像。 /// </summary> thumbnail = 4 } /// <summary> /// X3Fに記録された撮影情報の構造体。 /// </summary> public struct PhotoInfo { public string AEMODE; public string AFMODE; public string AP_DESC; public string APERTURE; public string BRACKET; public string BURST; public string CAMMANUF; public string CAMMODEL; public string CAMNAME; public string CAMSERIAL; public string DRIVE; public string EXPCOMP; public string EXPNET; public string EXPTIME; public string FIRMVERS; public string FLASH; public string FLENGTH; public string FLEQ35MM; public string FOCUS; public string IMAGERBOARDID; public string IMAGERTEMP; public string ISO; public string LENSARANGE; public string LENSFRANGE; public string LENSMODEL; public string PMODE; public string RESOLUTION; public string SENSORID; public string SH_DESC; public string SHUTTER; public string TIME; public string WB_DESC; public override string ToString() { StringBuilder sb = new StringBuilder(); sb.Append("AEMODE:"); sb.AppendLine(this.AEMODE); sb.Append("AFMODE:"); sb.AppendLine(this.AFMODE); sb.Append("AP_DESC:"); sb.AppendLine(this.AP_DESC); sb.Append("APERTURE:"); sb.AppendLine(this.APERTURE); sb.Append("BRACKET:"); sb.AppendLine(this.BRACKET); sb.Append("BURST:"); sb.AppendLine(this.BURST); sb.Append("CAMMANUF:"); sb.AppendLine(this.CAMMANUF); sb.Append("CAMMODEL:"); sb.AppendLine(this.CAMMODEL); sb.Append("CAMNAME:"); sb.AppendLine(this.CAMNAME); sb.Append("CAMSERIAL:"); sb.AppendLine(this.CAMSERIAL); sb.Append("DRIVE:"); sb.AppendLine(this.DRIVE); sb.Append("EXPCOMP:"); sb.AppendLine(this.EXPCOMP); sb.Append("EXPNET:"); sb.AppendLine(this.EXPNET); sb.Append("EXPTIME:"); sb.AppendLine(this.EXPTIME); sb.Append("FIRMVERS:"); sb.AppendLine(this.FIRMVERS); sb.Append("FLASH:"); sb.AppendLine(this.FLASH); sb.Append("FLENGTH:"); sb.AppendLine(this.FLENGTH); sb.Append("FLEQ35MM:"); sb.AppendLine(this.FLEQ35MM); sb.Append("FOCUS:"); sb.AppendLine(this.FOCUS); sb.Append("IMAGERBOARDID:"); sb.AppendLine(this.IMAGERBOARDID); sb.Append("IMAGERTEMP:"); sb.AppendLine(this.IMAGERTEMP); sb.Append("ISO:"); sb.AppendLine(this.ISO); sb.Append("LENSARANGE:"); sb.AppendLine(this.LENSARANGE); sb.Append("LENSFRANGE:"); sb.AppendLine(this.LENSFRANGE); sb.Append("LENSMODEL:"); sb.AppendLine(this.LENSMODEL); sb.Append("PMODE:"); sb.AppendLine(this.PMODE); sb.Append("RESOLUTION:"); sb.AppendLine(this.RESOLUTION); sb.Append("SENSORID:"); sb.AppendLine(this.SENSORID); sb.Append("SH_DESC:"); sb.AppendLine(this.SH_DESC); sb.Append("SHUTTER:"); sb.AppendLine(this.SHUTTER); sb.Append("TIME:"); sb.AppendLine(this.TIME); sb.Append("WB_DESC:"); sb.AppendLine(this.WB_DESC); return sb.ToString(); } } /// <summary> /// X3FファイルのDirectoryDataの構造体。 /// </summary> protected struct DirectoryData { /// <summary> /// 画像データのoffset。 /// </summary> public Int32 offset; /// <summary> /// 画像データのサイズ。 /// </summary> public Int32 size; /// <summary> /// データの種類。 /// </summary> public string tag; } /// <summary> /// X3Fファイルのパス。 /// </summary> public string Path { get { return this.path; } } /// <summary> /// X3Fファイルの撮影情報。 /// </summary> public PhotoInfo Info { get { return this.photoInfo; } } /// <summary> /// X3Fファイルのパス。 /// </summary> private string path; /// <summary> /// X3Fの撮影情報。 /// </summary> private PhotoInfo photoInfo; /// <summary> /// X3Fファイル内のディレクトリ情報を格納した配列。 /// </summary> private DirectoryData[] directoryDataArray; /// <summary> /// コンストラクタ。 /// X3Fファイルの基本情報を取得する。 /// </summary> /// <param name="path">X3Fファイルのパス。</param> public X3F(string path) { this.path = path; using (BinaryReader binaryReader = new BinaryReader(File.OpenRead(path))) { this.directoryDataArray = this.GetDirectoryIndex(binaryReader); this.photoInfo = this.GetPhotoInfo(binaryReader, this.directoryDataArray); } } /// <summary> /// X3Fファイルから画像を取得する。 /// </summary> /// <param name="type">取得する画像の種類。</param> /// <returns>画像。</returns> public Image GetImage(ImageType type) { Image img; using (BinaryReader binaryReader = new BinaryReader(File.OpenRead(path))) { byte[] imageData = new byte[this.directoryDataArray[(int)type].size - 28]; imageData = ReadBytes(binaryReader, this.directoryDataArray[(int)type].offset + 28, this.directoryDataArray[(int)type].size - 28); img = (Image)(new ImageConverter().ConvertFrom(imageData)); } return img; } /// <summary> /// X3Fファイルから撮影情報を取得する。 /// </summary> /// <param name="binaryReader">取得するX3FファイルのBinaryReader。</param> /// <param name="directoryDataArray">X3Fファイルのディレクトリ情報を記録した配列。</param> /// <returns>撮影情報。</returns> private PhotoInfo GetPhotoInfo(BinaryReader binaryReader, DirectoryData[] directoryDataArray) { Int32 entries_num = BitConverter.ToInt32(this.ReadBytes(binaryReader, directoryDataArray[2].offset + 8, 4), 0); Int32 length = BitConverter.ToInt32(this.ReadBytes(binaryReader, directoryDataArray[2].offset + 20, 4), 0);//文字長。2バイト文字でカウントされている。 Int32 offset = directoryDataArray[2].offset + 24 + entries_num * 8; string photoInfoString = Encoding.Unicode.GetString(this.ReadBytes(binaryReader, offset, length * 2)); PhotoInfo info = new PhotoInfo(); string[] tmp = photoInfoString.Split('\0'); info.AEMODE = tmp[1]; info.AFMODE = tmp[3]; info.AP_DESC = tmp[5]; info.APERTURE = tmp[7]; info.BRACKET = tmp[9]; info.BURST = tmp[11]; info.CAMMANUF = tmp[13]; info.CAMMODEL = tmp[15]; info.CAMNAME = tmp[17]; info.CAMSERIAL = tmp[19]; info.DRIVE = tmp[21]; info.EXPCOMP = tmp[23]; info.EXPNET = tmp[25]; info.EXPTIME = tmp[27]; info.FIRMVERS = tmp[29]; info.FLASH = tmp[31]; info.FLENGTH = tmp[33]; info.FLEQ35MM = tmp[35]; info.FOCUS = tmp[37]; info.IMAGERBOARDID = tmp[39]; info.IMAGERTEMP = tmp[41]; info.ISO = tmp[43]; info.LENSARANGE = tmp[45]; info.LENSFRANGE = tmp[47]; info.LENSMODEL = tmp[49]; info.PMODE = tmp[51]; info.RESOLUTION = tmp[53]; info.SENSORID = tmp[55]; info.SH_DESC = tmp[57]; info.SHUTTER = tmp[59]; info.TIME = tmp[61]; info.WB_DESC = tmp[63]; return info; } /// <summary> /// X3Fファイルからファイル内のディレクトリ情報を取得する。 /// </summary> /// <param name="binaryReader">取得するX3FファイルのBinaryReader。</param> /// <returns>ディレクトリ情報を格納した配列。</returns> private DirectoryData[] GetDirectoryIndex(BinaryReader binaryReader) { DirectoryData[] directoryDataArray; Int32 offset = BitConverter.ToInt32(ReadBytes(binaryReader, Convert.ToInt32(binaryReader.BaseStream.Length - 4), 4), 0); Int32 entries_num = BitConverter.ToInt32(ReadBytes(binaryReader, offset + 8, 4), 0); directoryDataArray = new DirectoryData[entries_num]; for (int i = 0; i < entries_num; i++) { Int32 p = offset + 12 + i * 4 * 3; directoryDataArray[i] = new DirectoryData(); directoryDataArray[i].offset = BitConverter.ToInt32(ReadBytes(binaryReader, p, 4), 0); directoryDataArray[i].size = BitConverter.ToInt32(ReadBytes(binaryReader, p + 4, 4), 0); directoryDataArray[i].tag = Convert.ToBase64String(ReadBytes(binaryReader, p, 4)); } return directoryDataArray; } /// <summary> /// BinaryReaderを指定のオフセットへ移動させ、指定のサイズ(Byte)だけ読み取る。 /// </summary> /// <param name="binaryReader">読み込むバイナリのソース。</param> /// <param name="offset">バイナリリーダーのオフセット。</param> /// <param name="size">読み込むサイズ。</param> /// <returns></returns> private Byte[] ReadBytes(BinaryReader binaryReader, Int32 offset, Int32 size) { binaryReader.BaseStream.Position = offset; return binaryReader.ReadBytes(size); } public override string ToString() { return this.Path; } } }