public static void WriteCinemaDngHeader(BinaryWriter bw, int W, int H, int bitsPerSample, CFAPatternType cfaPtn) { WriteImageFileHeader(bw); { // 最初の2バイトはnum of IFD Entriesなので2足す。 long startOfIFDEntry = bw.BaseStream.Position + 2; // CFAフルサイズ画像情報。 var es = new List <IFDEntry>(); es.Add(new IFDEntry(IFDEntry.Tag.NewSubfileType, IFDEntry.FieldType.LONG, 1, 0)); es.Add(new IFDEntry(IFDEntry.Tag.ImageWidth, IFDEntry.FieldType.LONG, 1, W)); es.Add(new IFDEntry(IFDEntry.Tag.ImageLength, IFDEntry.FieldType.LONG, 1, H)); es.Add(new IFDEntry(IFDEntry.Tag.BitsPerSample, IFDEntry.FieldType.SHORT, 1, new ushort[] { (ushort)bitsPerSample })); es.Add(new IFDEntry(IFDEntry.Tag.Compression, IFDEntry.FieldType.SHORT, 1, (ushort)IFDEntry.CompressionType.Uncompressed)); es.Add(new IFDEntry(IFDEntry.Tag.PhotometricInterpretation, IFDEntry.FieldType.SHORT, 1, (ushort)IFDEntry.PhotometricInterpretationType.ColorFilterArray)); es.Add(new IFDEntry(IFDEntry.Tag.Orientation, IFDEntry.FieldType.SHORT, 1, (ushort)IFDEntry.OrientationType.TopLeft)); es.Add(new IFDEntry(IFDEntry.Tag.SamplesPerPixel, IFDEntry.FieldType.SHORT, 1, 1)); es.Add(new IFDEntry(IFDEntry.Tag.PlanarConfiguration, IFDEntry.FieldType.SHORT, 1, (ushort)IFDEntry.PlanarConfigurationType.Chunky)); long stripOffsetsPos = startOfIFDEntry + es.Count * 12 + 8; es.Add(new IFDEntry(IFDEntry.Tag.StripOffsets, IFDEntry.FieldType.LONG, 1, 0)); es.Add(new IFDEntry(IFDEntry.Tag.RowsPerStrip, IFDEntry.FieldType.LONG, 1, H)); es.Add(new IFDEntry(IFDEntry.Tag.StripByteCounts, IFDEntry.FieldType.LONG, 1, W * H * bitsPerSample / 8)); es.Add(new IFDEntry(IFDEntry.Tag.CFARepeatPatternDim, IFDEntry.FieldType.SHORT, 2, new ushort[] { 2, 2 })); { byte[] cfaB = new byte[4]; // 0=R, 1=G, 2=B switch (cfaPtn) { case CFAPatternType.BGGR: cfaB = new byte[] { 2, 1, 1, 0 }; break; case CFAPatternType.GBRG: cfaB = new byte[] { 1, 2, 0, 1 }; break; case CFAPatternType.GRBG: cfaB = new byte[] { 1, 0, 2, 1 }; break; case CFAPatternType.RGGB: cfaB = new byte[] { 0, 1, 1, 2 }; break; } es.Add(new IFDEntry(IFDEntry.Tag.CFAPattern, IFDEntry.FieldType.BYTE, 4, cfaB)); } es.Add(new IFDEntry(IFDEntry.Tag.DNGVersion, IFDEntry.FieldType.BYTE, 4, new byte[] { 1, 1, 0, 0 })); es.Add(new IFDEntry(IFDEntry.Tag.UniqueCameraModel, IFDEntry.FieldType.ASCII, "Camera")); es.Add(new IFDEntry(IFDEntry.Tag.CFAPlaneColor, IFDEntry.FieldType.BYTE, 3, new byte[] { 0, 1, 2 })); es.Add(new IFDEntry(IFDEntry.Tag.CFALayout, IFDEntry.FieldType.SHORT, 1, (ushort)IFDEntry.CFALayoutType.RectangularOrSquare)); #if true es.Add(new IFDEntry(IFDEntry.Tag.CalibrationIlluminant1, IFDEntry.FieldType.SHORT, 1, (int)IFDEntry.LightSourceType.D55)); es.Add(new IFDEntry(IFDEntry.Tag.ColorMatrix1, IFDEntry.FieldType.SRATIONAL, 9, new IFDRational[] { new IFDRational(5840, 10000), new IFDRational(3469, 10000), new IFDRational(-2116, 10000), new IFDRational(-10896, 10000), new IFDRational(21096, 10000), new IFDRational(-767, 10000), new IFDRational(-4647, 10000), new IFDRational(7987, 10000), new IFDRational(4598, 10000) })); es.Add(new IFDEntry(IFDEntry.Tag.ForwardMatrix1, IFDEntry.FieldType.SRATIONAL, 9, new IFDRational[] { new IFDRational(10070, 10000), new IFDRational(-4671, 10000), new IFDRational(4243, 10000), new IFDRational(5037, 10000), new IFDRational(2130, 10000), new IFDRational(2831, 10000), new IFDRational(1041, 10000), new IFDRational(-8401, 10000), new IFDRational(15611, 10000) })); #else es.Add(new IFDEntry(IFDEntry.Tag.ColorMatrix1, IFDEntry.FieldType.SRATIONAL, 9, new IFDRational[] { new IFDRational(10000, 10000), new IFDRational(0, 10000), new IFDRational(0, 10000), new IFDRational(0, 10000), new IFDRational(10000, 10000), new IFDRational(0, 10000), new IFDRational(0, 10000), new IFDRational(0, 10000), new IFDRational(10000, 10000) })); es.Add(new IFDEntry(IFDEntry.Tag.CalibrationIlluminant1, IFDEntry.FieldType.SHORT, 1, (int)IFDEntry.LightSourceType.StandardLightA)); es.Add(new IFDEntry(IFDEntry.Tag.ColorMatrix2, IFDEntry.FieldType.SRATIONAL, 9, new IFDRational[] { new IFDRational(10000, 10000), new IFDRational(0, 10000), new IFDRational(0, 10000), new IFDRational(0, 10000), new IFDRational(10000, 10000), new IFDRational(0, 10000), new IFDRational(0, 10000), new IFDRational(0, 10000), new IFDRational(10000, 10000) })); es.Add(new IFDEntry(IFDEntry.Tag.CalibrationIlluminant2, IFDEntry.FieldType.SHORT, 1, (int)IFDEntry.LightSourceType.D65)); #endif /* * es.Add(new IFDEntry(IFDEntry.Tag.ISOSpeedRatings, IFDEntry.FieldType.SHORT, 1, 0x64)); * es.Add(new IFDEntry(IFDEntry.Tag.BlackLevelRepeatDim, IFDEntry.FieldType.SHORT, 2, new ushort[] { 2, 2 })); * es.Add(new IFDEntry(IFDEntry.Tag.BlackLevel, IFDEntry.FieldType.LONG, 4, new uint[] { 0, 0, 0, 0 })); * es.Add(new IFDEntry(IFDEntry.Tag.DefaultScale, IFDEntry.FieldType.RATIONAL, 2, * new IFDRational[] { new IFDRational(1000000, 1000000), new IFDRational(1000000, 1000000)})); * * es.Add(new IFDEntry(IFDEntry.Tag.AnalogBalance, IFDEntry.FieldType.RATIONAL, 3, new IFDRational[] { * new IFDRational(1000000, 1000000), new IFDRational(1000000, 1000000), * new IFDRational(1000000, 1000000)})); * es.Add(new IFDEntry(IFDEntry.Tag.AsShotNeutral, IFDEntry.FieldType.RATIONAL, 3, new IFDRational[] { * new IFDRational(1, 1), new IFDRational(1, 1), new IFDRational(1, 1)})); * es.Add(new IFDEntry(IFDEntry.Tag.BaselineExposure, IFDEntry.FieldType.SRATIONAL, 1, new IFDRational[] { * new IFDRational(0, 1) })); * es.Add(new IFDEntry(IFDEntry.Tag.BayerGreenSplit, IFDEntry.FieldType.LONG, 1, 0)); * es.Add(new IFDEntry(IFDEntry.Tag.AntiAliasStrength, IFDEntry.FieldType.RATIONAL, 1, * new IFDRational(1000000, 1000000))); * es.Add(new IFDEntry(IFDEntry.Tag.DNGPrivateData, IFDEntry.FieldType.BYTE, 5, new byte[] { (byte)'M', (byte)'a', (byte)'k', (byte)'e', 0 })); * es.Add(new IFDEntry(IFDEntry.Tag.FrameRate, IFDEntry.FieldType.SRATIONAL, 1, * new IFDRational(30000, 1000))); */ // Num of IFD Entriesが決まったので書き込む。 bw.Write((short)es.Count); // IFDEntryを書き込み。 foreach (var e in es) { e.WriteEntry(bw); } // Next IFD offset == 0 bw.Write((int)0); // 4バイトを超えるデータを書き出す。 foreach (var e in es) { e.WriteData(bw); } // 画像の先頭位置が決まったので書き込み。 WriteUint32At(bw, stripOffsetsPos, (uint)bw.BaseStream.Position); } // この後ろにCFA RAW画像データを書き込む。 }
public static void WriteDngHeader(BinaryWriter bw, int W, int H, int bitsPerSample, CFAPatternType cfaPtn) { int thumbnailW = 256; int thumbnailH = 256; WriteImageFileHeader(bw); /* * Tiff fields for 8bit CFA: * NewSubfileType = 0xfe, //< 0 * ImageWidth = 0x100, //< W * ImageLength = 0x101, //< H * BitsPerSample = 0x102, //< 8 8 8 (ushort) * Compression = 0x103, //< 1 (uncompressed) * PhotometricInterpretation = 0x106, //< 0x8023 (ColorFilterArray) * Make = 0x10f, //< " " * Model = 0x110, //< " " * StripOffsets = 0x111, //< image data file position * Orientation = 0x112, //< 1 * SamplesPerPixel = 0x115, //< 1 * RowsPerStrip = 0x116, //< H * StripByteCounts = 0x117, //< W * H * PlanarConfiguration = 0x11c, //< 1 * Software = 0x131, //< " " # DateTime = 0x132, //< " " # # CFA tags # CFARepeatPatternDim // SHORT 2 2 Rows=2, Cols=2 # CFAPattern // BYTE 0 1 1 2 # CFAPlaneColor // BYTE 0 1 2 # CFALayout // SHORT 1 RectangularOrSquare * * DNG Required fields: * DNGVersion // BYTE 1 4 0 0 * UniqueCameraModel // " " */ { // 最初の2バイトはnum of IFD Entriesなので2足す。 long startOfIFDEntry = bw.BaseStream.Position + 2; // 包括情報+サムネイル画像の情報。 var es = new List <IFDEntry>(); es.Add(new IFDEntry(IFDEntry.Tag.NewSubfileType, IFDEntry.FieldType.LONG, 1, 1)); es.Add(new IFDEntry(IFDEntry.Tag.ImageWidth, IFDEntry.FieldType.LONG, 1, thumbnailW)); es.Add(new IFDEntry(IFDEntry.Tag.ImageLength, IFDEntry.FieldType.LONG, 1, thumbnailH)); es.Add(new IFDEntry(IFDEntry.Tag.BitsPerSample, IFDEntry.FieldType.SHORT, 3, new ushort[] { 8, 8, 8 })); es.Add(new IFDEntry(IFDEntry.Tag.Compression, IFDEntry.FieldType.SHORT, 1, (ushort)IFDEntry.CompressionType.Uncompressed)); es.Add(new IFDEntry(IFDEntry.Tag.PhotometricInterpretation, IFDEntry.FieldType.SHORT, 1, (ushort)IFDEntry.PhotometricInterpretationType.RGB)); es.Add(new IFDEntry(IFDEntry.Tag.Make, IFDEntry.FieldType.ASCII, "Make")); es.Add(new IFDEntry(IFDEntry.Tag.Model, IFDEntry.FieldType.ASCII, "Model")); long stripOffsetsPos = startOfIFDEntry + es.Count * 12 + 8; es.Add(new IFDEntry(IFDEntry.Tag.StripOffsets, IFDEntry.FieldType.LONG, 1, 0)); es.Add(new IFDEntry(IFDEntry.Tag.Orientation, IFDEntry.FieldType.SHORT, 1, (ushort)IFDEntry.OrientationType.TopLeft)); es.Add(new IFDEntry(IFDEntry.Tag.SamplesPerPixel, IFDEntry.FieldType.SHORT, 1, 3)); es.Add(new IFDEntry(IFDEntry.Tag.RowsPerStrip, IFDEntry.FieldType.LONG, 1, thumbnailH)); es.Add(new IFDEntry(IFDEntry.Tag.StripByteCounts, IFDEntry.FieldType.LONG, 1, thumbnailW * thumbnailH * 3)); es.Add(new IFDEntry(IFDEntry.Tag.PlanarConfiguration, IFDEntry.FieldType.SHORT, 1, (ushort)IFDEntry.PlanarConfigurationType.Chunky)); es.Add(new IFDEntry(IFDEntry.Tag.Software, IFDEntry.FieldType.ASCII, "Software")); es.Add(new IFDEntry(IFDEntry.Tag.DateTime, IFDEntry.FieldType.ASCII, "2018:10:07 11:11:01")); long subIFDPos = startOfIFDEntry + es.Count * 12 + 8; es.Add(new IFDEntry(IFDEntry.Tag.SubIFDs, IFDEntry.FieldType.LONG, 1, 0)); es.Add(new IFDEntry(IFDEntry.Tag.ImageNumber, IFDEntry.FieldType.LONG, 1, 1)); es.Add(new IFDEntry(IFDEntry.Tag.DNGVersion, IFDEntry.FieldType.BYTE, 4, new byte[] { 1, 1, 0, 0 })); es.Add(new IFDEntry(IFDEntry.Tag.CalibrationIlluminant1, IFDEntry.FieldType.SHORT, 1, (int)IFDEntry.LightSourceType.StandardLightA)); es.Add(new IFDEntry(IFDEntry.Tag.ColorMatrix1, IFDEntry.FieldType.SRATIONAL, 9, new IFDRational[] { new IFDRational(7960, 10000), new IFDRational(-3736, 10000), new IFDRational(3350, 10000), new IFDRational(-1422, 10000), new IFDRational(6513, 10000), new IFDRational(5888, 10000), new IFDRational(1203, 10000), new IFDRational(-42, 10000), new IFDRational(8799, 10000) })); es.Add(new IFDEntry(IFDEntry.Tag.CameraCalibration1, IFDEntry.FieldType.SRATIONAL, 9, new IFDRational[] { new IFDRational(1, 1), new IFDRational(0, 1), new IFDRational(0, 1), new IFDRational(0, 1), new IFDRational(1, 1), new IFDRational(0, 1), new IFDRational(0, 1), new IFDRational(0, 1), new IFDRational(1, 1) })); es.Add(new IFDEntry(IFDEntry.Tag.CalibrationIlluminant2, IFDEntry.FieldType.SHORT, 1, (int)IFDEntry.LightSourceType.D55)); es.Add(new IFDEntry(IFDEntry.Tag.ColorMatrix2, IFDEntry.FieldType.SRATIONAL, 9, new IFDRational[] { new IFDRational(8716, 10000), new IFDRational(-3218, 10000), new IFDRational(1392, 10000), new IFDRational(-984, 10000), new IFDRational(7487, 10000), new IFDRational(4196, 10000), new IFDRational(1919, 10000), new IFDRational(-72, 10000), new IFDRational(8243, 10000) })); es.Add(new IFDEntry(IFDEntry.Tag.CameraCalibration2, IFDEntry.FieldType.SRATIONAL, 9, new IFDRational[] { new IFDRational(1, 1), new IFDRational(0, 1), new IFDRational(0, 1), new IFDRational(0, 1), new IFDRational(1, 1), new IFDRational(0, 1), new IFDRational(0, 1), new IFDRational(0, 1), new IFDRational(1, 1) })); es.Add(new IFDEntry(IFDEntry.Tag.AnalogBalance, IFDEntry.FieldType.RATIONAL, 3, new IFDRational[] { new IFDRational(1, 1), new IFDRational(1, 1), new IFDRational(1, 1) })); es.Add(new IFDEntry(IFDEntry.Tag.BaselineExposure, IFDEntry.FieldType.SRATIONAL, 1, new IFDRational[] { new IFDRational(0, 100) })); es.Add(new IFDEntry(IFDEntry.Tag.BaselineNoise, IFDEntry.FieldType.RATIONAL, 1, new IFDRational[] { new IFDRational(100, 100) })); es.Add(new IFDEntry(IFDEntry.Tag.BaselineSharpness, IFDEntry.FieldType.RATIONAL, 1, new IFDRational[] { new IFDRational(100, 100) })); es.Add(new IFDEntry(IFDEntry.Tag.LinearResponseLimit, IFDEntry.FieldType.RATIONAL, 1, new IFDRational[] { new IFDRational(100, 100) })); es.Add(new IFDEntry(IFDEntry.Tag.UniqueCameraModel, IFDEntry.FieldType.ASCII, "Camera")); es.Add(new IFDEntry(IFDEntry.Tag.CameraSerialNumber, IFDEntry.FieldType.ASCII, "1")); es.Add(new IFDEntry(IFDEntry.Tag.LensInfo, IFDEntry.FieldType.RATIONAL, 4, new IFDRational[] { new IFDRational(500, 10), new IFDRational(500, 10), new IFDRational(14, 10), new IFDRational(220, 10) })); es.Add(new IFDEntry(IFDEntry.Tag.ShadowScale, IFDEntry.FieldType.RATIONAL, 1, new IFDRational[] { new IFDRational(1, 1) })); es.Add(new IFDEntry(IFDEntry.Tag.DNGPrivateData, IFDEntry.FieldType.BYTE, 5, new byte[] { (byte)'M', (byte)'a', (byte)'k', (byte)'e', 0 })); // Num of IFD Entriesが決まったので書き込む。 bw.Write((short)es.Count); // IFDEntryを書き込み。 foreach (var e in es) { e.WriteEntry(bw); } // Next IFD offset == 0 bw.Write((int)0); // 4バイトを超えるデータを書き出す。 foreach (var e in es) { e.WriteData(bw); } // サムネイル画像の先頭位置が決まったので書き込み。 WriteUint32At(bw, stripOffsetsPos, (uint)bw.BaseStream.Position); // サムネイル画像データを書き込み。 int v = 0; for (int y = 0; y < thumbnailH; ++y) { for (int x = 0; x < thumbnailW; ++x) { bw.Write((byte)(v)); bw.Write((byte)(v >> 8)); bw.Write((byte)(v >> 16)); ++v; } } // subIFD(フルサイズのCFA RAW画像用)の位置が決まったので書き込む。 WriteUint32At(bw, subIFDPos, (uint)bw.BaseStream.Position); } { // 最初の2バイトはnum of IFD Entriesなので2足す。 long startOfIFDEntry = bw.BaseStream.Position + 2; // フルサイズのCFA RAW画像の情報。 var es = new List <IFDEntry>(); es.Add(new IFDEntry(IFDEntry.Tag.NewSubfileType, IFDEntry.FieldType.LONG, 1, 0)); es.Add(new IFDEntry(IFDEntry.Tag.ImageWidth, IFDEntry.FieldType.LONG, 1, W)); es.Add(new IFDEntry(IFDEntry.Tag.ImageLength, IFDEntry.FieldType.LONG, 1, H)); es.Add(new IFDEntry(IFDEntry.Tag.BitsPerSample, IFDEntry.FieldType.SHORT, 1, new ushort[] { (ushort)bitsPerSample })); es.Add(new IFDEntry(IFDEntry.Tag.Compression, IFDEntry.FieldType.SHORT, 1, (ushort)IFDEntry.CompressionType.Uncompressed)); es.Add(new IFDEntry(IFDEntry.Tag.PhotometricInterpretation, IFDEntry.FieldType.SHORT, 1, (ushort)IFDEntry.PhotometricInterpretationType.ColorFilterArray)); long stripOffsetsPos = startOfIFDEntry + es.Count * 12 + 8; es.Add(new IFDEntry(IFDEntry.Tag.StripOffsets, IFDEntry.FieldType.LONG, 1, 0)); es.Add(new IFDEntry(IFDEntry.Tag.SamplesPerPixel, IFDEntry.FieldType.SHORT, 1, 1)); es.Add(new IFDEntry(IFDEntry.Tag.RowsPerStrip, IFDEntry.FieldType.LONG, 1, H)); es.Add(new IFDEntry(IFDEntry.Tag.StripByteCounts, IFDEntry.FieldType.LONG, 1, W * H * bitsPerSample / 8)); es.Add(new IFDEntry(IFDEntry.Tag.PlanarConfiguration, IFDEntry.FieldType.SHORT, 1, (ushort)IFDEntry.PlanarConfigurationType.Chunky)); es.Add(new IFDEntry(IFDEntry.Tag.CFARepeatPatternDim, IFDEntry.FieldType.SHORT, 2, new ushort[] { 2, 2 })); { byte[] cfaB = new byte[4]; // 0=R, 1=G, 2=B switch (cfaPtn) { case CFAPatternType.BGGR: cfaB = new byte[] { 2, 1, 1, 0 }; break; case CFAPatternType.GBRG: cfaB = new byte[] { 1, 2, 0, 1 }; break; case CFAPatternType.GRBG: cfaB = new byte[] { 1, 0, 2, 1 }; break; case CFAPatternType.RGGB: cfaB = new byte[] { 0, 1, 1, 2 }; break; } es.Add(new IFDEntry(IFDEntry.Tag.CFAPattern, IFDEntry.FieldType.BYTE, 4, cfaB)); } es.Add(new IFDEntry(IFDEntry.Tag.CFAPlaneColor, IFDEntry.FieldType.BYTE, 3, new byte[] { 0, 1, 2 })); es.Add(new IFDEntry(IFDEntry.Tag.CFALayout, IFDEntry.FieldType.SHORT, 1, (ushort)IFDEntry.CFALayoutType.RectangularOrSquare)); /* * es.Add(new IFDEntry(IFDEntry.Tag.BlackLevelRepeatDim, IFDEntry.FieldType.SHORT, 2, new ushort[] { 1, 1 })); * es.Add(new IFDEntry(IFDEntry.Tag.BlackLevel, IFDEntry.FieldType.RATIONAL, 1, new IFDRational[] { new IFDRational(0, 256) })); * switch (bitsPerSample) { * case 8: * es.Add(new IFDEntry(IFDEntry.Tag.WhiteLevel, IFDEntry.FieldType.SHORT, 1, 255)); * break; * case 16: * es.Add(new IFDEntry(IFDEntry.Tag.WhiteLevel, IFDEntry.FieldType.SHORT, 1, 32767)); * break; * default: * throw new NotImplementedException(); * } * es.Add(new IFDEntry(IFDEntry.Tag.DefaultScale, IFDEntry.FieldType.RATIONAL, 2, new IFDRational[] { * new IFDRational(1, 1), new IFDRational(1, 1) })); * es.Add(new IFDEntry(IFDEntry.Tag.BestQualityScale, IFDEntry.FieldType.RATIONAL, 1, new IFDRational[] { new IFDRational(1, 1) })); * es.Add(new IFDEntry(IFDEntry.Tag.BayerGreenSplit, IFDEntry.FieldType.LONG, 1, 0xfa)); * es.Add(new IFDEntry(IFDEntry.Tag.AntiAliasStrength, IFDEntry.FieldType.RATIONAL, 1, new IFDRational[] { new IFDRational(100, 100) })); */ // Num of IFD Entriesが決まったので書き込む。 bw.Write((short)es.Count); // IFDEntryを書き込み。 foreach (var e in es) { e.WriteEntry(bw); } // Next IFD offset == 0 bw.Write((int)0); // 4バイトを超えるデータを書き出す。 foreach (var e in es) { e.WriteData(bw); } // CFA RAW画像の先頭位置が決まったので書き込み。 WriteUint32At(bw, stripOffsetsPos, (uint)bw.BaseStream.Position); // この後ろにCFA RAW画像データを書き込む。 } }