private T GetTag <T>(ImageFileDirectory ifd0, ImageFileDirectory raw) where T : PentaxPefFile.ImageFileDirectoryEntry { T tag = ifd0.GetEntry <T>(); if (tag == null) { //try raw tag tag = raw.GetEntry <T>(); } return(tag); }
public RawFile(string aFileName) : base(aFileName) { //set some default values: //color channels are assumed in order RGB if no tag present mColorTwist = new float[3, 4]; mColorTwist[0, 0] = 1.0f; mColorTwist[1, 1] = 1.0f; mColorTwist[2, 2] = 1.0f; mColorTwistIsIdentity = true; //it seems as if only Pentax is capable of giving us that info mRollAnglePresent = false; mRecordingDate = new DateTime(); //read all IFDs/Tags: byte a = mFileReader.ReadByte(); byte b = mFileReader.ReadByte(); bool fileIsLittleEndian; if (a == b && b == 'I') { fileIsLittleEndian = true; } else if (a == b && b == 'M') { fileIsLittleEndian = false; } else { throw new FileLoadException("Could not determine file endianess. Is this a proper TIFF/PEF/DNG file?", aFileName); } mEndianSwap = fileIsLittleEndian != BitConverter.IsLittleEndian; ushort magicNumber = ReadUI2(); if (magicNumber != 42) { throw new FileLoadException("This is not a valid TIFF/PEF/DNG file: Magic number is not 42.", aFileName); } uint offsetToFirstIFD = ReadUI4(); mFile.Seek(offsetToFirstIFD, SeekOrigin.Begin); mIfds = new List <ImageFileDirectory>(); while (true) { ImageFileDirectory ifd = new ImageFileDirectory(this); mIfds.Add(ifd); uint offsetToNext = ReadUI4(); if (offsetToNext == 0) { break; } Seek(offsetToNext, System.IO.SeekOrigin.Begin); } //until here PEF and DNG are the same. They diverge on how to read the tags }
public PEFFile(string aFileName) : base(aFileName) { ImageFileDirectory raw = SetMembers(); mRawImage = new ushort[mHeight, mWidth]; if (mHhuffmanTable != null && raw.GetEntry <IFDStripOffsets>().Value.Length == 1) { uint offset = raw.GetEntry <IFDStripOffsets>().Value[0]; Seek(offset, SeekOrigin.Begin); int[,] vpred = new int[2, 2]; int[] hpred = new int[2]; unsafe { fixed(ushort *huff = mHhuffmanTable.Value) { getbithuff(-1, null); for (int row = 0; row < mHeight; row++) { for (int col = 0; col < mWidth; col++) { int diff = ljpeg_diff(huff); if (col < 2) { hpred[col] = vpred[row & 1, col] += diff; } else { hpred[col & 1] += diff; } mRawImage[row, col] = (ushort)hpred[col & 1]; } } } } } else if (mHhuffmanTable != null) { throw new Exception("TODO: implement compressed multiple stripes"); } else { //uncompressed uint[] sizes = raw.GetEntry <IFDStripByteCounts>().Value; uint[] offsets = raw.GetEntry <IFDStripOffsets>().Value; uint rowsPerStrip = raw.GetEntry <IFDRowsPerStrip>().Value; for (int strip = 0; strip < offsets.Length; strip++) { byte[] data; Seek(offsets[strip], SeekOrigin.Begin); data = mFileReader.ReadBytes((int)sizes[strip]); uint pos = 0; int bitOffset = 0; for (int line = 0; line < rowsPerStrip; line++) { for (int pixel = 0; pixel < mWidth; pixel++) { int row = strip * (int)rowsPerStrip + line; int col = pixel; uint val = data[pos]; while (bitOffset < mBitDepth) { val = data[pos]; bitbuf = (bitbuf << 8) + val; bitOffset += 8; pos++; } val = bitbuf << (32 - bitOffset) >> (32 - mBitDepth); bitOffset -= mBitDepth; mRawImage[row, col] = (ushort)(val); } } } } //close the file Close(); }
public DNGColorSpec(uint aChannels, ImageFileDirectory ifd0, ImageFileDirectory raw) { fChannels = aChannels; if (GetTag <IFDDNGCalibrationIlluminant1>(ifd0, raw) != null) { fTemperature1 = ConvertIlluminantToTemperature(GetTag <IFDDNGCalibrationIlluminant1>(ifd0, raw).Value); } else { fTemperature1 = 0.0; } if (GetTag <IFDDNGCalibrationIlluminant2>(ifd0, raw) != null) { fTemperature2 = ConvertIlluminantToTemperature(GetTag <IFDDNGCalibrationIlluminant2>(ifd0, raw).Value); } else { fTemperature2 = 0.0; } fColorMatrix1 = GetTag <IFDDNGColorMatrix1>(ifd0, raw)?.Matrix; if (fColorMatrix1 == null) { fColorMatrix1 = DNGMatrix.Identity(fChannels); //best choice if nothing is given... } fColorMatrix2 = GetTag <IFDDNGColorMatrix2>(ifd0, raw)?.Matrix; if (fColorMatrix2 == null) { fColorMatrix2 = new DNGMatrix(); } fForwardMatrix1 = GetTag <IFDDNGForwardMatrix1>(ifd0, raw)?.Matrix; if (fForwardMatrix1 == null) { fForwardMatrix1 = new DNGMatrix(); } fForwardMatrix2 = GetTag <IFDDNGForwardMatrix2>(ifd0, raw)?.Matrix; if (fForwardMatrix2 == null) { fForwardMatrix2 = new DNGMatrix(); } fReductionMatrix1 = GetTag <IFDDNGReductionMatrix1>(ifd0, raw)?.Matrix; if (fReductionMatrix1 == null) { fReductionMatrix1 = new DNGMatrix(); } fReductionMatrix2 = GetTag <IFDDNGReductionMatrix2>(ifd0, raw)?.Matrix; if (fReductionMatrix2 == null) { fReductionMatrix2 = new DNGMatrix(); } fCameraCalibration1 = GetTag <IFDDNGCameraCalibration1>(ifd0, raw)?.Matrix; fCameraCalibration2 = GetTag <IFDDNGCameraCalibration1>(ifd0, raw)?.Matrix; if (fCameraCalibration1 == null) { fCameraCalibration1 = DNGMatrix.Identity(fChannels); } if (fCameraCalibration2 == null) { fCameraCalibration2 = DNGMatrix.Identity(fChannels); } fAnalogBalance = GetTag <IFDDNGAnalogBalance>(ifd0, raw)?.Vector.AsDiagonal(); if (fAnalogBalance == null) { fAnalogBalance = DNGMatrix.Identity(fChannels); } fForwardMatrix1 = NormalizeForwardMatrix(fForwardMatrix1); fColorMatrix1 = fAnalogBalance * fCameraCalibration1 * fColorMatrix1; if (fColorMatrix2.IsEmpty() || fTemperature1 <= 0.0 || fTemperature2 <= 0.0 || fTemperature1 == fTemperature2) { fTemperature1 = 5000.0; fTemperature2 = 5000.0; fColorMatrix2 = fColorMatrix1; fForwardMatrix2 = fForwardMatrix1; fReductionMatrix2 = fReductionMatrix1; fCameraCalibration2 = fCameraCalibration1; } else { fForwardMatrix2 = NormalizeForwardMatrix(fForwardMatrix2); fColorMatrix2 = fAnalogBalance * fCameraCalibration2 * fColorMatrix2; // Swap values if temperatures are out of order. if (fTemperature1 > fTemperature2) { double temp = fTemperature1; fTemperature1 = fTemperature2; fTemperature2 = temp; DNGMatrix T = fColorMatrix1; fColorMatrix1 = fColorMatrix2; fColorMatrix2 = T; T = fForwardMatrix1; fForwardMatrix1 = fForwardMatrix2; fForwardMatrix2 = T; T = fReductionMatrix1; fReductionMatrix1 = fReductionMatrix2; fReductionMatrix2 = T; T = fCameraCalibration1; fCameraCalibration1 = fCameraCalibration2; fCameraCalibration2 = T; } } IFDDNGAsShotNeutral neutral = GetTag <IFDDNGAsShotNeutral>(ifd0, raw); IFDDNGAsShotWhiteXY asShot = GetTag <IFDDNGAsShotWhiteXY>(ifd0, raw); DNGxyCoord white; if (asShot == null) { if (neutral == null) { throw new ArgumentException("The DNG spec says that one of the As Shot White balance tags must be present."); } DNGVector vec = new DNGVector((uint)neutral.Value.Length); for (uint c = 0; c < neutral.Value.Length; c++) { vec[c] = neutral.Value[c].Value; } double unify = 1.0 / vec.MaxEntry(); vec = unify * vec; white = NeutralToXY(vec); } else { double x = asShot.Value[0].Value; double y = asShot.Value[1].Value; white = new DNGxyCoord(x, y); } WhiteXY = white; }
public PEFFile(string aFileName) : base(aFileName) { byte a = mFileReader.ReadByte(); byte b = mFileReader.ReadByte(); bool fileIsLittleEndian; if (a == b && b == 'I') { fileIsLittleEndian = true; } else if (a == b && b == 'M') { fileIsLittleEndian = false; } else { throw new FileLoadException("Could not determine file endianess. Is this a proper TIFF/PEF file?", aFileName); } mEndianSwap = fileIsLittleEndian != BitConverter.IsLittleEndian; ushort magicNumber = ReadUI2(); if (magicNumber != 42) { throw new FileLoadException("This is not a valid TIFF/PEF file: Magic number is not 42.", aFileName); } uint offsetToFirstIFD = ReadUI4(); mFile.Seek(offsetToFirstIFD, SeekOrigin.Begin); List <ImageFileDirectory> ifds = new List <ImageFileDirectory>(); while (true) { ImageFileDirectory ifd = new ImageFileDirectory(this); ifds.Add(ifd); uint offsetToNext = ReadUI4(); if (offsetToNext == 0) { break; } Seek(offsetToNext, System.IO.SeekOrigin.Begin); } //Raw Data: ImageFileDirectory raw = ifds[0]; IFDExif exif = raw.GetEntry <IFDExif>(); mISO = exif.GetEntry <ExifEntry.ExifISOSpeedRatings>().Value; mBitDepth = raw.GetEntry <IFDBitsPerSample>().Value; ExifEntry.ExifMakerNote makernote = exif.GetEntry <ExifEntry.ExifMakerNote>(); mBayerPattern = exif.GetEntry <ExifEntry.ExifCFAPattern>().Value; mBayerWidth = exif.GetEntry <ExifEntry.ExifCFAPattern>().xCount; mBayerHeight = exif.GetEntry <ExifEntry.ExifCFAPattern>().yCount; MNHuffmanTable huffmanTable = makernote.Value.GetEntry <MNHuffmanTable>(); mWhiteLevel = makernote.Value.GetEntry <MNWhiteLevel>(); mWhitePoint = makernote.Value.GetEntry <MNWhitePoint>(); mBlackPoint = makernote.Value.GetEntry <MNBlackPoint>(); mScaling = makernote.Value.GetEntry <MNDataScaling>(); mWidth = (int)raw.GetEntry <IFDImageWidth>().Value; mHeight = (int)raw.GetEntry <IFDImageLength>().Value; uint offset = raw.GetEntry <IFDStripOffsets>().Value[0]; Seek(offset, SeekOrigin.Begin); mRawImage = new ushort[mHeight, mWidth]; int[,] vpred = new int[2, 2]; int[] hpred = new int[2]; unsafe { fixed(ushort *huff = huffmanTable.Value) { getbithuff(-1, null); for (int row = 0; row < mHeight; row++) { for (int col = 0; col < mWidth; col++) { int diff = ljpeg_diff(huff); if (col < 2) { hpred[col] = vpred[row & 1, col] += diff; } else { hpred[col & 1] += diff; } mRawImage[row, col] = (ushort)hpred[col & 1]; } } } } }
private ImageFileDirectory SetMembers() { //Raw Data: ImageFileDirectory raw = mIfds[0]; IFDExif exif = raw.GetEntry <IFDExif>(); mISO = exif.GetEntry <ExifISOSpeedRatings>().Value; mExposureTime = exif.GetEntry <ExifExposureTime>().Value; mRecordingDate = exif.GetEntry <ExifDateTimeDigitized>().Value; mBitDepth = raw.GetEntry <IFDBitsPerSample>().Value[0]; ExifMakerNote makernote = exif.GetEntry <ExifMakerNote>(); //needed to decompress image data: mHhuffmanTable = makernote.Value.GetEntry <MNHuffmanTable>(); ExifCFAPattern.BayerColor[] bayer = exif.GetEntry <ExifCFAPattern>().Value; mBayerPattern = new BayerColor[bayer.Length]; for (int i = 0; i < bayer.Length; i++) { mBayerPattern[i] = (BayerColor)(int)bayer[i]; } MNWhiteLevel whiteLevel = makernote.Value.GetEntry <MNWhiteLevel>(); MNWhitePoint whitePoint = makernote.Value.GetEntry <MNWhitePoint>(); MNBlackPoint blackPoint = makernote.Value.GetEntry <MNBlackPoint>(); MNDataScaling scaling = makernote.Value.GetEntry <MNDataScaling>(); float whiteLevelAll = (float)Math.Pow(2, mBitDepth); if (whiteLevel != null) { whiteLevelAll = whiteLevel.Value; } mBlackLevel = new float[3]; if (blackPoint != null) { //only one value for all colors if (blackPoint.Value.Length == 1) { mBlackLevel[0] = (float)blackPoint.Value[0]; mBlackLevel[1] = (float)blackPoint.Value[0]; mBlackLevel[2] = (float)blackPoint.Value[0]; } //values per color channel if (blackPoint.Value.Length == 3) { mBlackLevel[0] = (float)blackPoint.Value[0]; mBlackLevel[1] = (float)blackPoint.Value[1]; mBlackLevel[2] = (float)blackPoint.Value[2]; } //values per color bayer pattern if (blackPoint.Value.Length == 4) { //red int indexR = -1; for (int i = 0; i < mBayerPattern.Length; i++) { if (mBayerPattern[i] == BayerColor.Red) { indexR = i; break; } } mBlackLevel[0] = (float)blackPoint.Value[indexR]; //blue int indexB = -1; for (int i = 0; i < mBayerPattern.Length; i++) { if (mBayerPattern[i] == BayerColor.Blue) { indexB = i; break; } } mBlackLevel[2] = (float)blackPoint.Value[indexB]; //green, the two remaining indices int indexG1 = -1, indexG2 = -1; for (int i = 0; i < mBayerPattern.Length; i++) { if (mBayerPattern[i] == BayerColor.Green && indexG1 == -1) { indexG1 = i; } if (mBayerPattern[i] == BayerColor.Green && indexG1 != -1) { indexG2 = i; } } float g1 = (float)blackPoint.Value[indexG1]; float g2 = (float)blackPoint.Value[indexG2]; mBlackLevel[1] = Math.Max(g1, g2); //well, one could distinguish the two greens, but what for? } } mWhiteLevel = new float[] { whiteLevelAll, whiteLevelAll, whiteLevelAll }; mWhiteLevel[0] -= mBlackLevel[0]; mWhiteLevel[1] -= mBlackLevel[1]; mWhiteLevel[2] -= mBlackLevel[2]; float scale = scaling.Value; mWhiteBalance = new float[] { whitePoint.Value[0] / scale, whitePoint.Value[1] / scale, whitePoint.Value[3] / scale }; if (makernote.Value.GetEntry <MNLevelInfo>() != null) { mRollAngle = makernote.Value.GetEntry <MNLevelInfo>().Value.RollAngle; mRollAnglePresent = true; } mWidth = (int)raw.GetEntry <IFDImageWidth>().Value; mHeight = (int)raw.GetEntry <IFDImageLength>().Value; //look for orientation tag. if (raw.GetEntry <IFDOrientation>() != null) { mOrientation = new DNGOrientation(raw.GetEntry <IFDOrientation>().Value); } else { //no tag found, use default mOrientation = new DNGOrientation(DNGOrientation.Orientation.Normal); } //we always crop at least two pixels because of our algos... mCropLeft = 2; mCropTop = 2; mCroppedWidth = mWidth - 4; mCroppedHeight = mHeight - 4; mMake = raw.GetEntry <IFDMake>().Value; mUniqueModelName = raw.GetEntry <IFDModel>().Value; //missing data, like noise model, crop area, etc..., must be loaded afterwards! double[] colorMatrix = new double[] { 1, 0, 0, 0, 1, 0, 0, 0, 1 }; mColorSpec = new DNGColorSpec(colorMatrix, colorMatrix, IFDDNGCalibrationIlluminant.Illuminant.D50, IFDDNGCalibrationIlluminant.Illuminant.D50, mWhiteBalance); return(raw); }
public PEFFile(string aFileName, bool HeaderOnly) : base(aFileName) { byte a = mFileReader.ReadByte(); byte b = mFileReader.ReadByte(); bool fileIsLittleEndian; if (a == b && b == 'I') { fileIsLittleEndian = true; } else if (a == b && b == 'M') { fileIsLittleEndian = false; } else { throw new FileLoadException("Could not determine file endianess. Is this a proper TIFF/PEF file?", aFileName); } mEndianSwap = fileIsLittleEndian != BitConverter.IsLittleEndian; ushort magicNumber = ReadUI2(); if (magicNumber != 42) { throw new FileLoadException("This is not a valid TIFF/PEF file: Magic number is not 42.", aFileName); } uint offsetToFirstIFD = ReadUI4(); mFile.Seek(offsetToFirstIFD, SeekOrigin.Begin); List <ImageFileDirectory> ifds = new List <ImageFileDirectory>(); while (true) { ImageFileDirectory ifd = new ImageFileDirectory(this); ifds.Add(ifd); uint offsetToNext = ReadUI4(); if (offsetToNext == 0) { break; } Seek(offsetToNext, System.IO.SeekOrigin.Begin); } //Raw Data: ImageFileDirectory raw = ifds[0]; IFDExif exif = raw.GetEntry <IFDExif>(); mISO = exif.GetEntry <ExifEntry.ExifISOSpeedRatings>().Value; mBitDepth = raw.GetEntry <IFDBitsPerSample>().Value; ExifEntry.ExifMakerNote makernote = exif.GetEntry <ExifEntry.ExifMakerNote>(); mBayerPattern = exif.GetEntry <ExifEntry.ExifCFAPattern>().Value; mBayerWidth = exif.GetEntry <ExifEntry.ExifCFAPattern>().xCount; mBayerHeight = exif.GetEntry <ExifEntry.ExifCFAPattern>().yCount; MNHuffmanTable huffmanTable = makernote.Value.GetEntry <MNHuffmanTable>(); mWhiteLevel = makernote.Value.GetEntry <MNWhiteLevel>(); mWhitePoint = makernote.Value.GetEntry <MNWhitePoint>(); mBlackPoint = makernote.Value.GetEntry <MNBlackPoint>(); mScaling = makernote.Value.GetEntry <MNDataScaling>(); mWidth = (int)raw.GetEntry <IFDImageWidth>().Value; mHeight = (int)raw.GetEntry <IFDImageLength>().Value; uint offset = raw.GetEntry <IFDStripOffsets>().Value[0]; }
public DNGFile(string aFileName, bool HeaderOnly) : base(aFileName) { byte a = mFileReader.ReadByte(); byte b = mFileReader.ReadByte(); bool fileIsLittleEndian; if (a == b && b == 'I') { fileIsLittleEndian = true; } else if (a == b && b == 'M') { fileIsLittleEndian = false; } else { throw new FileLoadException("Could not determine file endianess. Is this a proper TIFF/PEF file?", aFileName); } mEndianSwap = fileIsLittleEndian != BitConverter.IsLittleEndian; ushort magicNumber = ReadUI2(); if (magicNumber != 42) { throw new FileLoadException("This is not a valid TIFF/PEF file: Magic number is not 42.", aFileName); } uint offsetToFirstIFD = ReadUI4(); mFile.Seek(offsetToFirstIFD, SeekOrigin.Begin); List <ImageFileDirectory> ifds = new List <ImageFileDirectory>(); while (true) { ImageFileDirectory ifd = new ImageFileDirectory(this); ifds.Add(ifd); uint offsetToNext = ReadUI4(); if (offsetToNext == 0) { break; } Seek(offsetToNext, System.IO.SeekOrigin.Begin); } IFDSubIFDs sub = ifds[0].GetEntry <IFDSubIFDs>(); mWidth = (int)sub.Value[0].GetEntry <IFDImageWidth>().Value; mHeight = (int)sub.Value[0].GetEntry <IFDImageLength>().Value; IFDExif exif = ifds[0].GetEntry <IFDExif>(); mISO = exif.GetEntry <ExifEntry.ExifISOSpeedRatings>().Value; mBitDepth = sub.Value[0].GetEntry <IFDBitsPerSample>().Value; mBayerPattern = sub.Value[0].GetEntry <IFDCFAPattern>().Value; mBayerWidth = sub.Value[0].GetEntry <IFDCFARepeatPatternDim>().Value[0]; mBayerHeight = sub.Value[0].GetEntry <IFDCFARepeatPatternDim>().Value[1]; mWhiteLevel = sub.Value[0].GetEntry <IFDDNGWhiteLevel>(); mBlackLevel = sub.Value[0].GetEntry <IFDDNGBlackLevel>(); mLinearizationTable = sub.Value[0].GetEntry <IFDDNGLinearizationTable>(); mMinVal = new float[3]; mMinVal[0] = (float)mBlackLevel.Value[0].Value; mMinVal[1] = (float)mBlackLevel.Value[mBlackLevel.Value.Length > 1 ? 1 : 0].Value; mMinVal[2] = (float)mBlackLevel.Value[mBlackLevel.Value.Length > 3 ? 3 : 0].Value; mMaxVal = new float[3]; mMaxVal[0] = (float)mWhiteLevel.Value[0]; mMaxVal[1] = (float)mWhiteLevel.Value[mWhiteLevel.Value.Length > 1 ? 1 : 0]; mMaxVal[2] = (float)mWhiteLevel.Value[mWhiteLevel.Value.Length > 3 ? 3 : 0]; mAsShotNeutral = ifds[0].GetEntry <IFDDNGAsShotNeutral>(); }
public DNGFile(string aFileName) : base(aFileName) { byte a = mFileReader.ReadByte(); byte b = mFileReader.ReadByte(); bool fileIsLittleEndian; if (a == b && b == 'I') { fileIsLittleEndian = true; } else if (a == b && b == 'M') { fileIsLittleEndian = false; } else { throw new FileLoadException("Could not determine file endianess. Is this a proper TIFF/PEF file?", aFileName); } mEndianSwap = fileIsLittleEndian != BitConverter.IsLittleEndian; ushort magicNumber = ReadUI2(); if (magicNumber != 42) { throw new FileLoadException("This is not a valid TIFF/PEF file: Magic number is not 42.", aFileName); } uint offsetToFirstIFD = ReadUI4(); mFile.Seek(offsetToFirstIFD, SeekOrigin.Begin); List <ImageFileDirectory> ifds = new List <ImageFileDirectory>(); while (true) { ImageFileDirectory ifd = new ImageFileDirectory(this); ifds.Add(ifd); uint offsetToNext = ReadUI4(); if (offsetToNext == 0) { break; } Seek(offsetToNext, System.IO.SeekOrigin.Begin); } IFDDNGVersion version = ifds[0].GetEntry <IFDDNGVersion>(); isDNGVersionLarge = version == null; if (version != null) { isDNGVersionLarge = version.Value[1] > 1; } IFDSubIFDs sub = ifds[0].GetEntry <IFDSubIFDs>(); mWidth = (int)sub.Value[0].GetEntry <IFDImageWidth>().Value; mHeight = (int)sub.Value[0].GetEntry <IFDImageLength>().Value; IFDExif exif = ifds[0].GetEntry <IFDExif>(); mISO = exif.GetEntry <ExifEntry.ExifISOSpeedRatings>().Value; mBitDepth = sub.Value[0].GetEntry <IFDBitsPerSample>().Value; mBayerPattern = sub.Value[0].GetEntry <IFDCFAPattern>().Value; mBayerWidth = sub.Value[0].GetEntry <IFDCFARepeatPatternDim>().Value[0]; mBayerHeight = sub.Value[0].GetEntry <IFDCFARepeatPatternDim>().Value[1]; mWhiteLevel = sub.Value[0].GetEntry <IFDDNGWhiteLevel>(); mBlackLevel = sub.Value[0].GetEntry <IFDDNGBlackLevel>(); mAsShotNeutral = sub.Value[0].GetEntry <IFDDNGAsShotNeutral>(); mLinearizationTable = sub.Value[0].GetEntry <IFDDNGLinearizationTable>(); mMinVal = new float[3]; mMinVal[0] = (float)mBlackLevel.Value[0].Value; mMinVal[1] = (float)mBlackLevel.Value[mBlackLevel.Value.Length > 1 ? 1 : 0].Value; mMinVal[2] = (float)mBlackLevel.Value[mBlackLevel.Value.Length > 3 ? 3 : 0].Value; mMaxVal = new float[3]; mMaxVal[0] = (float)mWhiteLevel.Value[0]; mMaxVal[1] = (float)mWhiteLevel.Value[mWhiteLevel.Value.Length > 1 ? 1 : 0]; mMaxVal[2] = (float)mWhiteLevel.Value[mWhiteLevel.Value.Length > 3 ? 3 : 0]; int tileWidth = (int)sub.Value[0].GetEntry <IFDTileWidth>().Value; int tileHeight = (int)sub.Value[0].GetEntry <IFDTileLength>().Value; uint[] offsets = sub.Value[0].GetEntry <IFDTileOffsets>().Value; uint[] byteCounts = sub.Value[0].GetEntry <IFDTileByteCounts>().Value; mRawImage = new ushort[mHeight, mWidth]; int row = 0; int col = 0; for (int tile = 0; tile < offsets.Length; tile++) { byte[] data; Seek(offsets[tile], SeekOrigin.Begin); data = mFileReader.ReadBytes((int)byteCounts[tile]); MemoryStream ms = new MemoryStream(data); ms.Seek(0, SeekOrigin.Begin); jhead jh = new jhead(); int ret = ljpeg_start(jh, 0, ms); int jrow, jcol; if (ret > 0 && jh != null) { int jwide = jh.wide; jwide *= jh.clrs; unsafe { if (jh.algo == 0xc3) //lossless JPEG { for (jrow = 0; jrow < jh.high; jrow++) { fixed(ushort *ptr = jh.row) { jh.rowPtr = ptr; ushort *rp = ljpeg_row(jrow, jh, ms); for (jcol = 0; jcol < jwide; jcol++) { if (jcol + col < mWidth && jrow + row < mHeight) { if (mLinearizationTable != null) { mRawImage[row + jrow, col + jcol] = mLinearizationTable.Value[rp[jcol] < mLinearizationTable.Value.Length ? rp[jcol] : mLinearizationTable.Value.Length - 1]; } else { mRawImage[row + jrow, col + jcol] = rp[jcol]; } } } jh.rowPtr = null; } } } } } col += tileWidth; if (col > mWidth) { col = 0; row += tileHeight; } } }
private ImageFileDirectory SetMembers() { ImageFileDirectory rawIFD = null; //find the IFD with the RAW Bayer image for (int i = 0; i < mIfds.Count; i++) { //well, it should actually be somewhere in IFD0... if (mIfds[i].GetEntry <IFDPhotometricInterpretation>() != null) { if (mIfds[i].GetEntry <IFDPhotometricInterpretation>().Value == IFDPhotometricInterpretation.PhotometricInterpretation.CFA) { rawIFD = mIfds[i]; break; } } } //no root IFD seems to contain RAW bayer, search for Sub-IFDs if (rawIFD == null) { //find the IFD with the RAW Bayer image for (int i = 0; i < mIfds.Count; i++) { IFDSubIFDs subIFD = mIfds[i].GetEntry <IFDSubIFDs>(); if (subIFD == null) { continue; } for (int j = 0; j < subIFD.Value.Count; j++) { if (subIFD.Value[j].GetEntry <IFDPhotometricInterpretation>().Value == IFDPhotometricInterpretation.PhotometricInterpretation.CFA) { rawIFD = subIFD.Value[j]; break; } } } } if (rawIFD == null) { throw new ArgumentException("Can't find IFD with Bayer RAW image."); } mLinearizationTable = rawIFD.GetEntry <IFDDNGLinearizationTable>(); mWidth = (int)rawIFD.GetEntry <IFDImageWidth>().Value; mHeight = (int)rawIFD.GetEntry <IFDImageLength>().Value; if (mIfds[0].GetEntry <IFDDateTime>() != null) { mRecordingDate = mIfds[0].GetEntry <IFDDateTime>().Value; } //in case of Pentax this will have succes: IFDDNGPrivateData privateData = mIfds[0].GetEntry <IFDDNGPrivateData>(); if (privateData != null) { MNLevelInfo levelInfo = privateData.PentaxMakerNotes.GetEntry <MNLevelInfo>(); if (levelInfo != null) { mRollAngle = levelInfo.Value.RollAngle; mRollAnglePresent = true; } } IFDExif exif = mIfds[0].GetEntry <IFDExif>(); if (exif != null) { mISO = exif.GetEntry <ExifISOSpeedRatings>().Value; mExposureTime = exif.GetEntry <ExifExposureTime>().Value; mRecordingDate = (exif.GetEntry <ExifDateTimeDigitized>()?.Value).GetValueOrDefault(mRecordingDate); } else if (rawIFD.GetEntry <IFDISOSpeedRatings>() != null) { mISO = rawIFD.GetEntry <IFDISOSpeedRatings>().Value; //very likely that exposure time is also present mExposureTime = rawIFD.GetEntry <IFDExposureTime>().Value; } else if (mIfds[0].GetEntry <IFDISOSpeedRatings>() != null) { mISO = mIfds[0].GetEntry <IFDISOSpeedRatings>().Value; //very likely that exposure time is also present mExposureTime = mIfds[0].GetEntry <IFDExposureTime>().Value; } mBitDepth = rawIFD.GetEntry <IFDBitsPerSample>().Value[0]; int bayerWidth = rawIFD.GetEntry <IFDCFARepeatPatternDim>().Value[0]; int bayerHeight = rawIFD.GetEntry <IFDCFARepeatPatternDim>().Value[1]; if (bayerHeight != 2 || bayerWidth != 2) { throw new ArgumentException("This file has a bayer pattern size different than 2x2. Can't decode that."); } ExifCFAPattern.BayerColor[] bayer = rawIFD.GetEntry <IFDCFAPattern>().Value; mBayerPattern = new BayerColor[bayer.Length]; for (int i = 0; i < bayer.Length; i++) { mBayerPattern[i] = (BayerColor)(int)bayer[i]; } IFDDNGCFAPlaneColor planeColor = rawIFD.GetEntry <IFDDNGCFAPlaneColor>(); int[] planeColorHelper = new int[] { 0, 1, 2 }; if (planeColor != null) //did it ever differ from 0,1,2? { //0 = red, 1 = gree, 2 = blue. //The debayer algo creates images with plane order red/green/blue. //If this order differs, we need to re-order the planes in order to //have the color matrices correct. //reset colorTwist matrix: mColorTwist = new float[3, 4]; if (planeColor.Value.Length > 3 || planeColor.Value.Length < 3) { throw new ArgumentException("This image doesn't contain three color planes."); } for (int i = 0; i < planeColor.Value.Length; i++) { int color = planeColor.Value[i]; planeColorHelper[i] = color; if (color > 2) { throw new ArgumentException("This image contains colors different than red/green/blue."); } mColorTwist[color, i] = 1; if (color != i) { mColorTwistIsIdentity = false; } } } mColorSpec = new DNGColorSpec(3, mIfds[0], rawIFD); IFDDNGWhiteLevel whiteLevel = rawIFD.GetEntry <IFDDNGWhiteLevel>(); IFDDNGBlackLevel blackLevel = rawIFD.GetEntry <IFDDNGBlackLevel>(); mBlackLevel = new float[3]; if (blackLevel != null) { //only one value for all colors if (blackLevel.Value.Length == 1) { mBlackLevel[0] = (float)blackLevel.Value[0].Value; mBlackLevel[1] = (float)blackLevel.Value[0].Value; mBlackLevel[2] = (float)blackLevel.Value[0].Value; } //values per color channel if (blackLevel.Value.Length == 3) { mBlackLevel[planeColorHelper[0]] = (float)blackLevel.Value[0].Value; mBlackLevel[planeColorHelper[1]] = (float)blackLevel.Value[1].Value; mBlackLevel[planeColorHelper[2]] = (float)blackLevel.Value[2].Value; } //values per color bayer pattern if (blackLevel.Value.Length == 4) { //red int indexR = -1; for (int i = 0; i < mBayerPattern.Length; i++) { if (mBayerPattern[i] == BayerColor.Red) { indexR = i; break; } } mBlackLevel[0] = (float)blackLevel.Value[indexR].Value; //blue int indexB = -1; for (int i = 0; i < mBayerPattern.Length; i++) { if (mBayerPattern[i] == BayerColor.Blue) { indexB = i; break; } } mBlackLevel[2] = (float)blackLevel.Value[indexB].Value; //green, the two remaining indices int indexG1 = -1, indexG2 = -1; for (int i = 0; i < mBayerPattern.Length; i++) { if (mBayerPattern[i] == BayerColor.Green && indexG1 == -1) { indexG1 = i; } if (mBayerPattern[i] == BayerColor.Green && indexG1 != -1) { indexG2 = i; } } float g1 = (float)blackLevel.Value[indexG1].Value; float g2 = (float)blackLevel.Value[indexG2].Value; mBlackLevel[1] = Math.Max(g1, g2); //well, one could distinguish the two greens, but what for? } } mWhiteLevel = new float[3]; mWhiteLevel[0] = (float)whiteLevel.Value[0]; mWhiteLevel[1] = (float)whiteLevel.Value[0]; mWhiteLevel[2] = (float)whiteLevel.Value[0]; //subtract black level from white level mWhiteLevel[0] -= mBlackLevel[0]; mWhiteLevel[1] -= mBlackLevel[1]; mWhiteLevel[2] -= mBlackLevel[2]; //get white balance from color spec DNGVector wb = mColorSpec.CameraWhite; mWhiteBalance = new float[3]; mWhiteBalance[planeColorHelper[0]] = 1.0f / (float)wb[0]; mWhiteBalance[planeColorHelper[1]] = 1.0f / (float)wb[1]; mWhiteBalance[planeColorHelper[2]] = 1.0f / (float)wb[2]; //look for orientation tag. If RAW ifd has the tag, choose that one if (rawIFD.GetEntry <IFDOrientation>() != null) { mOrientation = new DNGOrientation(rawIFD.GetEntry <IFDOrientation>().Value); } else if (mIfds[0].GetEntry <IFDOrientation>() != null) { mOrientation = new DNGOrientation(mIfds[0].GetEntry <IFDOrientation>().Value); } else { //no tag found, use default mOrientation = new DNGOrientation(DNGOrientation.Orientation.Normal); } //default Values: int cropLeft = 0; int cropTop = 0; int croppedWidth = mWidth; int croppedHeight = mHeight; IFDDNGActiveArea activeArea = rawIFD.GetEntry <IFDDNGActiveArea>(); //if active area is defined: if (activeArea != null) { int top, left, bottom, right; top = (int)activeArea.Value[0]; left = (int)activeArea.Value[1]; bottom = (int)activeArea.Value[2]; right = (int)activeArea.Value[3]; cropLeft += left; cropTop += top; croppedWidth = right - left; croppedHeight = bottom - top; //CFA pattern is defined on active area. If top/left is uneven we need to shift the CFA pattern accordingly if (top % 2 != 0) { BayerColor bayer0 = BayerPattern[0]; BayerColor bayer1 = BayerPattern[1]; BayerPattern[0] = BayerPattern[2]; BayerPattern[1] = BayerPattern[3]; BayerPattern[2] = bayer0; BayerPattern[3] = bayer1; } if (left % 2 != 0) { BayerColor bayer0 = BayerPattern[0]; BayerColor bayer2 = BayerPattern[2]; BayerPattern[0] = BayerPattern[1]; BayerPattern[2] = BayerPattern[3]; BayerPattern[1] = bayer0; BayerPattern[3] = bayer2; } } IFDDNGDefaultCropOrigin cropOrigin = rawIFD.GetEntry <IFDDNGDefaultCropOrigin>(); IFDDNGDefaultCropSize cropSize = rawIFD.GetEntry <IFDDNGDefaultCropSize>(); if (cropOrigin != null && cropSize != null) { int top, left, width, height; left = (int)(cropOrigin.Value[0].Value); top = (int)(cropOrigin.Value[0].Value); width = (int)(cropSize.Value[0].Value); height = (int)(cropSize.Value[0].Value); cropLeft += left; cropTop += top; croppedWidth = width; croppedHeight = height; } //we always crop at least two pixels because of our algos... mCropLeft = Math.Max(2, cropLeft); mCropTop = Math.Max(2, cropTop); mCroppedWidth = croppedWidth - Math.Max(0, (cropLeft + croppedWidth) - (mWidth - 2)); mCroppedHeight = croppedHeight - Math.Max(0, (cropTop + croppedHeight) - (mHeight - 2)); IFDDNGNoiseProfile noise = rawIFD.GetEntry <IFDDNGNoiseProfile>(); if (noise == null) { noise = mIfds[0].GetEntry <IFDDNGNoiseProfile>(); } if (noise != null) { //if noise level is given for all channels, //take the green one as it is usually scalled to one if (noise.Value.Length > 2) { mNoiseModelAlpha = (float)noise.Value[planeColorHelper[1] * 2]; mNoiseModelBeta = (float)noise.Value[planeColorHelper[1] * 2 + 1]; } else { mNoiseModelAlpha = (float)noise.Value[0]; mNoiseModelBeta = (float)noise.Value[1]; } } mMake = mIfds[0].GetEntry <IFDMake>().Value; mUniqueModelName = mIfds[0].GetEntry <IFDDNGUniqueCameraModel>().Value; return(rawIFD); }
public DNGFile(string aFileName) : base(aFileName) { IFDDNGVersion version = mIfds[0].GetEntry <IFDDNGVersion>(); isDNGVersionLarge = version == null; if (version != null) { isDNGVersionLarge = version.Value[1] > 1; } ImageFileDirectory rawIFD = SetMembers(); //try to read additional noise models try { ExtraCameraProfiles profiles = ExtraCameraProfiles.Load("ExtraCameraProfiles.xml"); string make = mIfds[0].GetEntry <IFDMake>()?.Value; string uniqueModel = mIfds[0].GetEntry <IFDDNGUniqueCameraModel>()?.Value; ExtraCameraProfile profile = profiles.GetProfile(make, uniqueModel); double a, b; (a, b) = profile.NoiseModel.GetValue(mISO); if (a != 0.0) { mNoiseModelAlpha = (float)a; mNoiseModelBeta = (float)b; } } catch (Exception) { } //get non-global blacklevels ushort[] blackLevelV = new ushort[mHeight]; ushort[] blackLevelH = new ushort[mWidth]; int maxBlackLevel = 0; IFDDNGBlackLevelDeltaV deltaV = rawIFD.GetEntry <IFDDNGBlackLevelDeltaV>(); IFDDNGActiveArea activeArea = rawIFD.GetEntry <IFDDNGActiveArea>(); int blackOffsetH = 0; int blackOffsetV = 0; int borderBottom = 0; int borderRight = 0; if (activeArea != null) { blackOffsetV = (int)activeArea.Value[0]; blackOffsetH = (int)activeArea.Value[1]; borderBottom = mHeight - (int)activeArea.Value[2]; borderRight = mWidth - (int)activeArea.Value[3]; } if (deltaV != null) { if (deltaV.Value.Length + blackOffsetV + borderBottom != mHeight) { throw new Exception("Count in IFDDNGBlackLevelDeltaV doesn't fit image height"); } for (int i = 0; i < deltaV.Value.Length; i++) { blackLevelV[blackOffsetV + i] = (ushort)deltaV.Value[i].Value; } } IFDDNGBlackLevelDeltaH deltaH = rawIFD.GetEntry <IFDDNGBlackLevelDeltaH>(); if (deltaH != null) { if (deltaH.Value.Length + blackOffsetH + borderRight != mWidth) { throw new Exception("Count in IFDDNGBlackLevelDeltaH doesn't fit image width"); } for (int i = 0; i < deltaH.Value.Length; i++) { blackLevelH[blackOffsetH + i] = (ushort)deltaH.Value[i].Value; } } //read actual image data //data stored in tiles: if (rawIFD.GetEntry <IFDTileWidth>() != null) { int tileWidth = (int)rawIFD.GetEntry <IFDTileWidth>().Value; int tileHeight = (int)rawIFD.GetEntry <IFDTileLength>().Value; uint[] offsets = rawIFD.GetEntry <IFDTileOffsets>().Value; uint[] byteCounts = rawIFD.GetEntry <IFDTileByteCounts>().Value; mRawImage = new ushort[mHeight, mWidth]; int row = 0; int col = 0; if (rawIFD.GetEntry <IFDCompression>().Value == IFDCompression.Compression.LosslessJPEG) { for (int tile = 0; tile < offsets.Length; tile++) { byte[] data; Seek(offsets[tile], SeekOrigin.Begin); data = mFileReader.ReadBytes((int)byteCounts[tile]); MemoryStream ms = new MemoryStream(data); ms.Seek(0, SeekOrigin.Begin); jhead jh = new jhead(); int ret = ljpeg_start(jh, 0, ms); int jrow, jcol; if (ret > 0 && jh != null) { int jwide = jh.wide; jwide *= jh.clrs; unsafe { if (jh.algo == 0xc3) //lossless JPEG { for (jrow = 0; jrow < jh.high; jrow++) { fixed(ushort *ptr = jh.row) { jh.rowPtr = ptr; ushort *rp = ljpeg_row(jrow, jh, ms); for (jcol = 0; jcol < jwide; jcol++) { if (jcol + col < mWidth && jrow + row < mHeight) { int black = blackLevelH[jcol + col]; black += blackLevelV[jrow + row]; maxBlackLevel = Math.Max(maxBlackLevel, black); if (mLinearizationTable != null) { mRawImage[row + jrow, col + jcol] = (ushort)Math.Max(0, (int)(mLinearizationTable.Value[rp[jcol] < mLinearizationTable.Value.Length ? rp[jcol] : mLinearizationTable.Value.Length - 1]) - black); } else { mRawImage[row + jrow, col + jcol] = (ushort)Math.Max(0, (int)(rp[jcol] - black)); } } } jh.rowPtr = null; } } } } } col += tileWidth; if (col >= mWidth) { col = 0; row += tileHeight; } } } else { throw new ArgumentException("I don't know how to read that file :("); } } //data stored in strips: else if (rawIFD.GetEntry <IFDStripOffsets>() != null) { uint[] offsets = rawIFD.GetEntry <IFDStripOffsets>().Value; uint[] byteCounts = rawIFD.GetEntry <IFDStripByteCounts>().Value; uint rowsPerStrip = rawIFD.GetEntry <IFDRowsPerStrip>().Value; if (rawIFD.GetEntry <IFDCompression>().Value == IFDCompression.Compression.LosslessJPEG) { mRawImage = new ushort[mHeight, mWidth]; for (int strip = 0; strip < offsets.Length; strip++) { byte[] data; Seek(offsets[strip], SeekOrigin.Begin); data = mFileReader.ReadBytes((int)byteCounts[strip]); MemoryStream ms = new MemoryStream(data); ms.Seek(0, SeekOrigin.Begin); jhead jh = new jhead(); int ret = ljpeg_start(jh, 0, ms); int jrow, jcol; if (ret > 0 && jh != null) { int jwide = jh.wide; jwide *= jh.clrs; unsafe { if (jh.algo == 0xc3) //lossless JPEG { for (jrow = 0; jrow < jh.high; jrow++) { fixed(ushort *ptr = jh.row) { jh.rowPtr = ptr; ushort *rp = ljpeg_row(jrow, jh, ms); for (jcol = 0; jcol < jwide; jcol++) { if (jcol < mWidth && jrow + (strip * rowsPerStrip) < mHeight) { int black = blackLevelH[jcol]; black += blackLevelV[(strip * rowsPerStrip) + jrow]; maxBlackLevel = Math.Max(maxBlackLevel, black); if (mLinearizationTable != null) { mRawImage[(strip * rowsPerStrip) + jrow, jcol] = (ushort)Math.Max(0, (int)((mLinearizationTable.Value[rp[jcol] < mLinearizationTable.Value.Length ? rp[jcol] : mLinearizationTable.Value.Length - 1]) - black)); } else { mRawImage[(strip * rowsPerStrip) + jrow, jcol] = (ushort)Math.Max(0, (int)(rp[jcol] - black)); } } } jh.rowPtr = null; } } } } } } } else if (rawIFD.GetEntry <IFDCompression>().Value == IFDCompression.Compression.NoCompression) { mRawImage = new ushort[mHeight, mWidth]; if (mBitDepth == 16) { for (int strip = 0; strip < offsets.Length; strip++) { byte[] data; Seek(offsets[strip], SeekOrigin.Begin); data = mFileReader.ReadBytes((int)byteCounts[strip]); unsafe { fixed(byte *ptr = data) { ushort *usptr = (ushort *)ptr; for (int pixel = 0; pixel < data.Length / 2; pixel++) { int row = strip * (int)rowsPerStrip; int col = pixel; if (col >= mWidth) { row += pixel / mWidth; col = pixel % mWidth; } int black = blackLevelH[col]; black += blackLevelV[row]; maxBlackLevel = Math.Max(maxBlackLevel, black); if (mLinearizationTable != null) { mRawImage[row, col] = (ushort)Math.Max(0, (int)(mLinearizationTable.Value[usptr[pixel] < mLinearizationTable.Value.Length ? usptr[pixel] : mLinearizationTable.Value.Length - 1]) - black); } else { mRawImage[row, col] = (ushort)Math.Max(0, (int)(usptr[pixel]) - black); } } } } } } else if (mBitDepth < 16) { for (int strip = 0; strip < offsets.Length; strip++) { byte[] data; Seek(offsets[strip], SeekOrigin.Begin); data = mFileReader.ReadBytes((int)byteCounts[strip]); uint pos = 0; int offset = 0; for (int line = 0; line < rowsPerStrip; line++) { for (int pixel = 0; pixel < mWidth; pixel++) { int row = strip * (int)rowsPerStrip + line; int col = pixel; uint val = data[pos]; while (offset < mBitDepth) { val = data[pos]; bitbuf = (bitbuf << 8) + val; offset += 8; pos++; } val = bitbuf << (32 - offset) >> (32 - mBitDepth); offset -= mBitDepth; int black = blackLevelH[col]; black += blackLevelV[row]; maxBlackLevel = Math.Max(maxBlackLevel, black); if (mLinearizationTable != null) { mRawImage[row, col] = (ushort)Math.Max(0, (int)(mLinearizationTable.Value[val < mLinearizationTable.Value.Length ? (ushort)(val) : mLinearizationTable.Value.Length - 1]) - black); } else { mRawImage[row, col] = (ushort)Math.Max(0, (int)(val) - black); } } } } } } else { throw new ArgumentException("I don't know how to read that file :("); } } //close the file Close(); WhiteLevel[0] -= maxBlackLevel; WhiteLevel[1] -= maxBlackLevel; WhiteLevel[2] -= maxBlackLevel; }