Пример #1
0
        public void Test(string reference, string test)
        {
            using var refImage = Image.Load <Rgb24>(reference);

            using var tiff = TiffFileReader.Open(test);

            TiffStreamOffset ifdOffset = tiff.FirstImageFileDirectoryOffset;

            while (!ifdOffset.IsZero)
            {
                TiffImageFileDirectory ifd     = tiff.ReadImageFileDirectory(ifdOffset);
                TiffImageDecoder       decoder = tiff.CreateImageDecoder(ifd, new TiffImageDecoderOptions {
                    UndoColorPreMultiplying = true
                });

                Assert.Equal(refImage.Width, decoder.Width);
                Assert.Equal(refImage.Height, decoder.Height);
                TiffRgb24[] pixels = new TiffRgb24[decoder.Width * decoder.Height];

                decoder.Decode(TiffPixelBuffer.Wrap(pixels, decoder.Width, decoder.Height));

                AssertEqual(refImage, pixels);

                using (var image = new Image <Rgb24>(decoder.Width, decoder.Height))
                {
                    decoder.Decode(image);
                    AssertEqual(refImage, image);
                }

                ifdOffset = ifd.NextOffset;
            }
        }
Пример #2
0
        public async Task TestAsync(string reference, string test)
        {
            using var refImage = Image.Load <L8>(reference);

            await using TiffFileReader tiff = await TiffFileReader.OpenAsync(test);

            TiffStreamOffset ifdOffset = tiff.FirstImageFileDirectoryOffset;

            while (!ifdOffset.IsZero)
            {
                TiffImageFileDirectory ifd = await tiff.ReadImageFileDirectoryAsync(ifdOffset);

                TiffImageDecoder decoder = await tiff.CreateImageDecoderAsync(ifd, new TiffImageDecoderOptions { UndoColorPreMultiplying = true });

                Assert.Equal(refImage.Width, decoder.Width);
                Assert.Equal(refImage.Height, decoder.Height);
                TiffGray8[] pixels = new TiffGray8[decoder.Width * decoder.Height];

                await decoder.DecodeAsync(TiffPixelBuffer.Wrap(pixels, decoder.Width, decoder.Height));

                AssertEqual(refImage, pixels);

                using (var image = new Image <L8>(decoder.Width, decoder.Height))
                {
                    decoder.Decode(image);
                    AssertEqual(refImage, image);
                }

                ifdOffset = ifd.NextOffset;
            }
        }
Пример #3
0
 /// <summary>
 /// Adds the specified tag to the Image File Directory.
 /// </summary>
 /// <param name="directory">The Image File Directory.</param>
 /// <param name="tag">The tag.</param>
 /// <param name="metadata">The geometry metadata.</param>
 /// <param name="tagName">The name of the tag.</param>
 protected void AddImageTag(TiffImageFileDirectory directory, UInt16 tag, IDictionary <String, Object> metadata, String tagName)
 {
     if (metadata.ContainsKey(tagName))
     {
         directory[tag] = new Object[] { metadata[tagName] };
         metadata.Remove(tagName);
     }
 }
Пример #4
0
        public async Task TestDoubleCompatibility(int length, bool bigTiff)
        {
            double[] refData = new double[length];
            var      rand    = new Random();

            for (int i = 0; i < refData.Length; i++)
            {
                refData[i] = rand.NextDouble();
            }

            using Stream stream = await GenerateTiffAsync(bigTiff, async ifd =>
            {
                await ifd.WriteTagAsync((TiffTag) 0x1234, TiffValueCollection.UnsafeWrap(refData));
            });

            await using (TiffFileReader reader = await TiffFileReader.OpenAsync(stream, leaveOpen: true))
            {
                TiffImageFileDirectory ifd = await reader.ReadImageFileDirectoryAsync();

                TiffFieldReader fieldReader = await reader.CreateFieldReaderAsync();

                TiffImageFileDirectoryEntry entry = ifd.FindEntry((TiffTag)0x1234);
                Assert.Equal((TiffTag)0x1234, entry.Tag);

                // Byte
                await TestInvalidConversionAsync <byte>(fieldReader, entry, nameof(fieldReader.ReadByteFieldAsync), refData.Length);

                // SSbyte
                await TestInvalidConversionAsync <sbyte>(fieldReader, entry, nameof(fieldReader.ReadSByteFieldAsync), refData.Length);

                // Short
                await TestInvalidConversionAsync <ushort>(fieldReader, entry, nameof(fieldReader.ReadShortFieldAsync), refData.Length);

                // SShort
                await TestInvalidConversionAsync <short>(fieldReader, entry, nameof(fieldReader.ReadSShortFieldAsync), refData.Length);

                // Long
                await TestInvalidConversionAsync <uint>(fieldReader, entry, nameof(fieldReader.ReadLongFieldAsync), refData.Length);

                // SLong
                await TestInvalidConversionAsync <int>(fieldReader, entry, nameof(fieldReader.ReadSLongFieldAsync), refData.Length);

                // Float
                await TestInvalidConversionAsync <float>(fieldReader, entry, nameof(fieldReader.ReadFloatFieldAsync), refData.Length);

                // Double
                await TestValidConversionAsync(fieldReader, entry, nameof(fieldReader.ReadDoubleFieldAsync), refData);

                // Rational
                await TestInvalidConversionAsync <TiffRational>(fieldReader, entry, nameof(fieldReader.ReadRationalFieldAsync), refData.Length);

                // SRational
                await TestInvalidConversionAsync <TiffSRational>(fieldReader, entry, nameof(fieldReader.ReadSRationalFieldAsync), refData.Length);
            }
        }
Пример #5
0
        /// <summary>
        /// Writes the geo-key directory into the Image File Directory.
        /// </summary>
        /// <param name="imageFileDirectory">The Image File Directory.</param>
        /// <param name="geoKeyDirectory">The geo-key directory.</param>
        private void WriteGeoKeyDirectory(TiffImageFileDirectory imageFileDirectory, GeoKeyDirectory geoKeyDirectory)
        {
            if (geoKeyDirectory.Count == 0)
            {
                return;
            }

            Object[]      geoKeyDirectoryTag = new Object[(geoKeyDirectory.Count + 1) * 4];
            List <Object> geoDoubleParamsTag = new List <Object>();
            List <Object> geoAsciiParamsTag  = new List <Object>();

            // header data (version, revision, count)
            geoKeyDirectoryTag[0] = (Int16)1;
            geoKeyDirectoryTag[1] = (Int16)0;
            geoKeyDirectoryTag[2] = (Int16)0;
            geoKeyDirectoryTag[3] = (Int16)geoKeyDirectory.Count;

            // content
            Int32 byteIndex = 4;

            foreach (KeyValuePair <Int16, Object> geoData in geoKeyDirectory)
            {
                geoKeyDirectoryTag[byteIndex]     = geoData.Key;
                geoKeyDirectoryTag[byteIndex + 1] = (UInt16)0;
                geoKeyDirectoryTag[byteIndex + 2] = (UInt16)1;

                if (geoData.Value is Int16)
                {
                    geoKeyDirectoryTag[byteIndex + 3] = geoData.Value;
                }

                if (geoData.Value is Double)
                {
                    geoDoubleParamsTag.Add(geoData.Value);
                }

                if (geoData.Value is String)
                {
                    geoAsciiParamsTag.Add(geoData.Value);
                }

                byteIndex += 4;
            }

            imageFileDirectory.Add(TiffTag.GeoKeyDirectoryTag, geoKeyDirectoryTag);

            if (geoDoubleParamsTag.Count > 0)
            {
                imageFileDirectory.Add(TiffTag.GeoDoubleParamsTag, geoDoubleParamsTag.ToArray());
            }
            if (geoAsciiParamsTag.Count > 0)
            {
                imageFileDirectory.Add(TiffTag.GeoAsciiParamsTag, geoAsciiParamsTag.ToArray());
            }
        }
Пример #6
0
        /// <summary>
        /// Computes the image file directory of a geometry.
        /// </summary>
        /// <param name="geometry">The geometry.</param>
        /// <param name="compression">The compression.</param>
        /// <param name="format">The sample format.</param>
        /// <param name="startPosition">The starting position of the raster content within the stream.</param>
        /// <param name="endPosition">The ending position of the raster content within the stream.</param>
        /// <returns>The computed image file directory.</returns>
        protected virtual TiffImageFileDirectory ComputeImageFileDirectory(ISpectralGeometry geometry, TiffCompression compression, TiffSampleFormat format)
        {
            TiffImageFileDirectory imageFileDirectory = new TiffImageFileDirectory();

            // compute photometric interpretation
            TiffPhotometricInterpretation photometricInterpretation = ComputePhotometricInterpretation(geometry);

            // add raster properties
            AddImageTag(imageFileDirectory, TiffTag.PhotometricInterpretation, (UInt16)photometricInterpretation);
            AddImageTag(imageFileDirectory, TiffTag.Compression, (UInt16)compression);
            AddImageTag(imageFileDirectory, TiffTag.ImageLength, (UInt32)geometry.Raster.NumberOfRows);
            AddImageTag(imageFileDirectory, TiffTag.ImageWidth, (UInt32)geometry.Raster.NumberOfColumns);
            AddImageTag(imageFileDirectory, TiffTag.ResolutionUnit, (UInt16)2);
            AddImageTag(imageFileDirectory, TiffTag.XResolution, (geometry.Raster.Mapper != null) ? (Rational)geometry.Raster.Mapper.ColumnSize : (Rational)1);
            AddImageTag(imageFileDirectory, TiffTag.YResolution, (geometry.Raster.Mapper != null) ? (Rational)geometry.Raster.Mapper.RowSize : (Rational)1);
            AddImageTag(imageFileDirectory, TiffTag.RowsPerStrip, (UInt32)geometry.Raster.NumberOfRows);
            AddImageTag(imageFileDirectory, TiffTag.StripOffsets, (UInt32)0);
            AddImageTag(imageFileDirectory, TiffTag.StripByteCounts, (UInt32)0);
            AddImageTag(imageFileDirectory, TiffTag.BitsPerSample, Enumerable.Repeat((UInt16)geometry.Raster.RadiometricResolution, geometry.Raster.NumberOfBands).Cast <Object>().ToArray());
            AddImageTag(imageFileDirectory, TiffTag.SamplesPerPixel, (UInt32)geometry.Raster.NumberOfBands);
            AddImageTag(imageFileDirectory, TiffTag.SampleFormat, (UInt16)format);

            // add color palette
            if (photometricInterpretation == TiffPhotometricInterpretation.PaletteColor)
            {
                imageFileDirectory.Add(TiffTag.ColorMap, ComputeColorMap(geometry));
            }

            // add metadata
            AddImageTag(imageFileDirectory, TiffTag.DocumentName, geometry.Metadata, "GeoTIFF::DocumentName");
            AddImageTag(imageFileDirectory, TiffTag.ImageDescription, geometry.Metadata, "GeoTIFF::ImageDescription");
            AddImageTag(imageFileDirectory, TiffTag.DocumentName, geometry.Metadata, "GeoTIFF::Make");
            AddImageTag(imageFileDirectory, TiffTag.Make, geometry.Metadata, "GeoTIFF::DocumentName");
            AddImageTag(imageFileDirectory, TiffTag.Model, geometry.Metadata, "GeoTIFF::Model");
            AddImageTag(imageFileDirectory, TiffTag.PageName, geometry.Metadata, "GeoTIFF::PageName");
            AddImageTag(imageFileDirectory, TiffTag.Artist, geometry.Metadata, "GeoTIFF::Artist");
            AddImageTag(imageFileDirectory, TiffTag.HostComputer, geometry.Metadata, "GeoTIFF::HostComputer");
            AddImageTag(imageFileDirectory, TiffTag.Copyright, geometry.Metadata, "GeoTIFF::Copyright");
            AddImageTag(imageFileDirectory, TiffTag.Software, "AEGIS Geospatial Framework");
            AddImageTag(imageFileDirectory, TiffTag.DateTime, DateTime.Now.ToString(CultureInfo.InvariantCulture.DateTimeFormat));

            Dictionary <String, Object> attributes = geometry.Metadata.Where(attribute => !attribute.Key.Contains("GeoTIFF")).ToDictionary(attribute => attribute.Key, attribute => attribute.Value);

            if (geometry.Metadata.Count(attribute => !attribute.Key.Contains("GeoTIFF")) > 0)
            {
                imageFileDirectory.Add(TiffTag.AegisAttributes, new Object[] { JsonConvert.SerializeObject(attributes, Formatting.None, new JsonSerializerSettings {
                        TypeNameHandling = TypeNameHandling.All
                    }) });
            }

            return(imageFileDirectory);
        }
Пример #7
0
        public static async Task <int> Merge(FileInfo[] source, FileInfo output, CancellationToken cancellationToken)
        {
            if (source is null || source.Length == 0)
            {
                Console.WriteLine("Input TIFF file are not specified.");
                return(1);
            }
            if (output is null)
            {
                Console.WriteLine("Output TIFF file is not specified");
                return(1);
            }

            await using TiffFileWriter writer = await TiffFileWriter.OpenAsync(output.FullName);

            TiffStreamOffset fistIfdOffset     = default;
            TiffStreamOffset previousIfdOffset = default;

            foreach (FileInfo sourceFile in source)
            {
                await using TiffFileReader reader = await TiffFileReader.OpenAsync(sourceFile.FullName);

                await using TiffFileContentReader contentReader = await reader.CreateContentReaderAsync();

                await using TiffFieldReader fieldReader = await reader.CreateFieldReaderAsync(cancellationToken);

                TiffStreamOffset inputIfdOffset = reader.FirstImageFileDirectoryOffset;
                while (!inputIfdOffset.IsZero)
                {
                    TiffImageFileDirectory ifd = await reader.ReadImageFileDirectoryAsync(inputIfdOffset, cancellationToken);

                    using (TiffImageFileDirectoryWriter ifdWriter = writer.CreateImageFileDirectory())
                    {
                        await CopyIfdAsync(contentReader, fieldReader, ifd, ifdWriter, cancellationToken);

                        previousIfdOffset = await ifdWriter.FlushAsync(previousIfdOffset);

                        if (fistIfdOffset.IsZero)
                        {
                            fistIfdOffset = previousIfdOffset;
                        }
                    }

                    inputIfdOffset = ifd.NextOffset;
                }
            }

            writer.SetFirstImageFileDirectoryOffset(fistIfdOffset);
            await writer.FlushAsync();

            return(0);
        }
Пример #8
0
        /// <summary>
        /// Apply the write operation for the specified geometry.
        /// </summary>
        /// <param name="geometry">The geometry.</param>
        /// <exception cref="System.ArgumentException">The number of images in the stream has reached the maximum.</exception>
        protected override void ApplyWriteGeometry(IGeometry geometry)
        {
            if (_imageCount == _maxImageCount)
            {
                throw new InvalidOperationException("The number of images in the stream has reached the maximum.");
            }

            IRaster raster = (geometry as ISpectralGeometry).Raster;

            if (raster == null)
            {
                return;
            }

            // TODO: compute the format more exactly
            Int64 contentSize = (Int64)raster.RadiometricResolution / 8 * raster.NumberOfBands * raster.NumberOfColumns * raster.NumberOfRows;

            InitializeStream(contentSize > BigTiffThreshold ? TiffStructure.BigTiff : TiffStructure.RegularTiff);

            TiffCompression  compression  = TiffCompression.None; // TODO: support other compressions
            TiffSampleFormat sampleFormat = (geometry as ISpectralGeometry).Raster.Format == RasterFormat.Floating ? TiffSampleFormat.Floating : TiffSampleFormat.UnsignedInteger;

            TiffImageFileDirectory imageFileDirectory = ComputeImageFileDirectory(geometry as ISpectralGeometry, compression, sampleFormat);

            // compute and update raster content position
            Int64 imageFileDirectorySize     = ComputeImageFileDirectorySize(imageFileDirectory);
            Int64 rasterContentStartPosition = _baseStream.Position + imageFileDirectorySize;
            Int64 rasterContentSize          = ComputeRasterContentSize(raster);

            // strip offset and length
            switch (_structure)
            {
            case TiffStructure.RegularTiff:
                imageFileDirectory[TiffTag.StripOffsets][0]    = (UInt32)rasterContentStartPosition;
                imageFileDirectory[TiffTag.StripByteCounts][0] = (UInt32)rasterContentSize;

                WriteImageFileDirectory(imageFileDirectory);
                break;

            case TiffStructure.BigTiff:
                imageFileDirectory[TiffTag.StripOffsets][0]    = (UInt64)rasterContentStartPosition;
                imageFileDirectory[TiffTag.StripByteCounts][0] = (UInt64)rasterContentSize;

                WriteBigImageFileDirectory(imageFileDirectory);
                break;
            }

            // perform writing based on representation
            WriteRasterContentToStrip((geometry as ISpectralGeometry).Raster, compression, sampleFormat);

            _imageCount++;
        }
Пример #9
0
        private async Task <IImageInfo> IdentifyCoreAsync(TiffFileContentSource contentSource, CancellationToken cancellationToken)
        {
            using TiffFileReader tiff = await TiffFileReader.OpenAsync(contentSource, cancellationToken : cancellationToken).ConfigureAwait(false);

            TiffImageFileDirectory ifd = await tiff.ReadImageFileDirectoryAsync(cancellationToken).ConfigureAwait(false);

            TiffImageDecoder decoder = await tiff.CreateImageDecoderAsync(ifd, _options, cancellationToken).ConfigureAwait(false);

            return(new ImageInfo()
            {
                Width = decoder.Width,
                Height = decoder.Height
            });
        }
Пример #10
0
        public async Task TestSLongCompatibility(int length, bool bigTiff)
        {
            int[] refData = new int[length];
            new Random(42).NextBytes(MemoryMarshal.AsBytes(refData.AsSpan()));

            using Stream stream = await GenerateTiffAsync(bigTiff, async ifd =>
            {
                await ifd.WriteTagAsync((TiffTag) 0x1234, TiffValueCollection.UnsafeWrap(refData));
            });

            await using (TiffFileReader reader = await TiffFileReader.OpenAsync(stream, leaveOpen: true))
            {
                TiffImageFileDirectory ifd = await reader.ReadImageFileDirectoryAsync();

                TiffFieldReader fieldReader = await reader.CreateFieldReaderAsync();

                TiffImageFileDirectoryEntry entry = ifd.FindEntry((TiffTag)0x1234);
                Assert.Equal((TiffTag)0x1234, entry.Tag);

                // Byte
                await TestInvalidConversionAsync <byte>(fieldReader, entry, nameof(fieldReader.ReadByteFieldAsync), refData.Length);

                // SSbyte
                await TestInvalidConversionAsync <sbyte>(fieldReader, entry, nameof(fieldReader.ReadSByteFieldAsync), refData.Length);

                // Short
                await TestInvalidConversionAsync <ushort>(fieldReader, entry, nameof(fieldReader.ReadShortFieldAsync), refData.Length);

                // SShort
                await TestInvalidConversionAsync <short>(fieldReader, entry, nameof(fieldReader.ReadSShortFieldAsync), refData.Length);

                // Long
                await TestValidConversionAsync(fieldReader, entry, nameof(fieldReader.ReadLongFieldAsync), Array.ConvertAll(refData, v => (uint)v));

                // SLong
                await TestValidConversionAsync(fieldReader, entry, nameof(fieldReader.ReadSLongFieldAsync), refData);

                // Float
                await TestInvalidConversionAsync <float>(fieldReader, entry, nameof(fieldReader.ReadFloatFieldAsync), refData.Length);

                // Double
                await TestInvalidConversionAsync <double>(fieldReader, entry, nameof(fieldReader.ReadDoubleFieldAsync), refData.Length);

                // Rational
                await TestInvalidConversionAsync <TiffRational>(fieldReader, entry, nameof(fieldReader.ReadRationalFieldAsync), refData.Length);

                // SRational
                await TestValidConversionAsync(fieldReader, entry, nameof(fieldReader.ReadSRationalFieldAsync), Array.ConvertAll(refData, v => new TiffSRational(v, 1)));
            }
        }
Пример #11
0
        public static async Task <int> Dump(FileInfo file, long?offset, CancellationToken cancellationToken)
        {
            await using TiffFileReader tiff = await TiffFileReader.OpenAsync(file.FullName);

            await using TiffFieldReader fieldReader = await tiff.CreateFieldReaderAsync(cancellationToken);

            Console.WriteLine("Input File: " + file.FullName);
            Console.WriteLine($"StandardTIFF: {tiff.IsStandardTiff}, BigTIFF: {tiff.IsBigTiff}, IsLittleEndian: {tiff.IsLittleEndian}");
            Console.WriteLine("First IFD: " + tiff.FirstImageFileDirectoryOffset);
            Console.WriteLine();

            int ifdIndex = 0;
            TiffStreamOffset ifdOffset = offset.HasValue ? new TiffStreamOffset(offset.GetValueOrDefault()) : tiff.FirstImageFileDirectoryOffset;

            while (!ifdOffset.IsZero)
            {
                TiffImageFileDirectory ifd = await tiff.ReadImageFileDirectoryAsync(ifdOffset, cancellationToken);

                Console.WriteLine($"IFD #{ifdIndex++} (Offset = {ifdOffset})");

                Console.WriteLine("  Well-known Tags:");
                await DumpWellKnownTagsAsync(fieldReader, ifd, cancellationToken);

                Console.WriteLine();

                Console.WriteLine("  Tags:");
                for (int i = 0; i < ifd.Count; i++)
                {
                    TiffImageFileDirectoryEntry entry = ifd[i];
                    await DumpIfdEntryAsync(i, fieldReader, entry, cancellationToken);
                }
                if (ifd.Count == 0)
                {
                    Console.WriteLine("    No tags found.");
                }

                Console.WriteLine();
                Console.WriteLine();

                ifdOffset = ifd.NextOffset;
            }

            return(0);
        }
Пример #12
0
        private async Task <Image <TPixel> > DecodeCoreAsync <TPixel>(TiffFileContentSource contentSource, CancellationToken cancellationToken)
            where TPixel : unmanaged, IPixel <TPixel>
        {
            using TiffFileReader tiff = await TiffFileReader.OpenAsync(contentSource, cancellationToken : cancellationToken).ConfigureAwait(false);

            TiffImageFileDirectory ifd = await tiff.ReadImageFileDirectoryAsync(cancellationToken).ConfigureAwait(false);

            TiffImageDecoder decoder = await tiff.CreateImageDecoderAsync(ifd, _options, cancellationToken).ConfigureAwait(false);

            Image <TPixel>?image = new Image <TPixel>(_configuration, decoder.Width, decoder.Height);

            try
            {
                await decoder.DecodeAsync(image, cancellationToken).ConfigureAwait(false);

                return(Interlocked.Exchange <Image <TPixel>?>(ref image, null) !);
            }
            finally
            {
                image?.Dispose();
            }
        }
Пример #13
0
        /// <summary>
        /// Computes the size of the image file directory.
        /// </summary>
        /// <param name="imageFileDirectory">The image file directory.</param>
        /// <returns>The size of the image file directory (in bytes).</returns>
        private Int64 ComputeImageFileDirectorySize(TiffImageFileDirectory imageFileDirectory)
        {
            Int64 size = (_structure == TiffStructure.BigTiff ? 8 : 2)             // number of entries
                         + _imageFileDirectoryEntrySize * imageFileDirectory.Count // entries
                         + _imageFileDirectoryFieldSize;                           // position of next directory

            TiffTagType entryType;
            UInt16      dataSize;
            Int32       dataCount;

            foreach (UInt16 entryTag in imageFileDirectory.Keys)
            {
                entryType = TiffTag.GetType(imageFileDirectory[entryTag][0]);
                dataSize  = TiffTag.GetSize(entryType);

                if (entryType == TiffTagType.ASCII)
                {
                    dataCount = 0;
                    for (Int32 i = 0; i < imageFileDirectory[entryTag].Length; i++)
                    {
                        dataCount += (imageFileDirectory[entryTag][i] as String).Length;
                    }
                }
                else
                {
                    dataCount = imageFileDirectory[entryTag].Length;
                }

                if (dataCount * dataSize > _imageFileDirectoryFieldSize)
                {
                    size += dataCount * dataSize + (dataCount * dataSize) % 2;
                }
            }

            return(size);
        }
Пример #14
0
        internal async Task <TiffStreamOffset> GenerateReducedResolutionLayerAsync(int layer, TiffStreamOffset ifdOffset, IRipperReducedResolutionGenerationReporter reporter, CancellationToken cancellationToken)
        {
            int outputTileSize         = _options.OutputTileSize;
            int outputTileSize2        = 2 * outputTileSize;
            TiffImageFileDirectory ifd = await _fileReader.ReadImageFileDirectoryAsync(ifdOffset).ConfigureAwait(false);

            TiffImageDecoder image = await _fileReader.CreateImageDecoderAsync(ifd);

            int width  = image.Width;
            int height = image.Height;

            int tiffRowCount = (width / 2 + outputTileSize - 1) / outputTileSize;
            int tiffColCount = (height / 2 + outputTileSize - 1) / outputTileSize;

            int index = 0;

            ulong[] offsets        = new ulong[tiffRowCount * tiffColCount];
            ulong[] byteCounts     = new ulong[tiffRowCount * tiffColCount];
            ulong   totalByteCount = 0;

            reporter?.ReportStartReducedResolutionLayerGeneration(layer, offsets.Length, width / 2, height / 2);

            using (Image <Rgb24> canvas2 = new Image <Rgb24>(_configuration, outputTileSize2, outputTileSize2))
            {
                for (int y = 0; y < height; y += outputTileSize2)
                {
                    for (int x = 0; x < width; x += outputTileSize2)
                    {
                        cancellationToken.ThrowIfCancellationRequested();
                        MemoryMarshal.AsBytes(canvas2.GetPixelSpan()).Clear();

                        await image.DecodeAsync(new TiffPoint(x, y), canvas2).ConfigureAwait(false);

                        using (Image <Rgb24> tile24 = canvas2.Clone(ctx =>
                        {
                            ctx.Resize(outputTileSize, outputTileSize);
                        }))
                        {
                            cancellationToken.ThrowIfCancellationRequested();
                            TiffStreamRegion region = await _encoder.EncodeAsync(_fileWriter, tile24).ConfigureAwait(false);

                            offsets[index]    = (ulong)region.Offset.Offset;
                            byteCounts[index] = (uint)region.Length;
                            totalByteCount   += (uint)region.Length;
                            index++;

                            reporter?.ReportReducedResolutionLayerGenerationProgress(layer, index, offsets.Length);
                        }
                    }
                }
            }

            cancellationToken.ThrowIfCancellationRequested();
            using (TiffImageFileDirectoryWriter ifdWriter = _fileWriter.CreateImageFileDirectory())
            {
                await ifdWriter.WriteTagAsync(TiffTag.NewSubfileType, new TiffValueCollection <ushort>((ushort)TiffNewSubfileType.ReducedResolution)).ConfigureAwait(false);

                await ifdWriter.WriteTagAsync(TiffTag.PhotometricInterpretation, new TiffValueCollection <ushort>((ushort)TiffPhotometricInterpretation.YCbCr)).ConfigureAwait(false);

                await ifdWriter.WriteTagAsync(TiffTag.Compression, new TiffValueCollection <ushort>((ushort)TiffCompression.Jpeg)).ConfigureAwait(false);

                await ifdWriter.WriteTagAsync(TiffTag.SamplesPerPixel, new TiffValueCollection <ushort>((ushort)3)).ConfigureAwait(false);

                await ifdWriter.WriteTagAsync(TiffTag.TileWidth, new TiffValueCollection <ushort>((ushort)outputTileSize)).ConfigureAwait(false);

                await ifdWriter.WriteTagAsync(TiffTag.TileLength, new TiffValueCollection <ushort>((ushort)outputTileSize)).ConfigureAwait(false);

                //ifdWriter.AddTag(TiffTag.ResolutionUnit, (ushort)TiffResolutionUnit.Inch);

                //await ifdWriter.WriteTagAsync(TiffTag.XResolution, new TiffValueCollection<TiffRational>(new TiffRational(72, 1)));
                //await ifdWriter.WriteTagAsync(TiffTag.YResolution, new TiffValueCollection<TiffRational>(new TiffRational(72, 1)));
                await ifdWriter.WriteTagAsync(TiffTag.SampleFormat, new TiffValueCollection <ushort>(new ushort[] { 1, 1, 1 })).ConfigureAwait(false);

                await ifdWriter.WriteTagAsync(TiffTag.BitsPerSample, new TiffValueCollection <ushort>(new ushort[] { 8, 8, 8 })).ConfigureAwait(false);

                if (UseBigTiff)
                {
                    await ifdWriter.WriteTagAsync(TiffTag.ImageWidth, new TiffValueCollection <ulong>((ulong)(width / 2))).ConfigureAwait(false);

                    await ifdWriter.WriteTagAsync(TiffTag.ImageLength, new TiffValueCollection <ulong>((ulong)(height / 2))).ConfigureAwait(false);

                    await ifdWriter.WriteTagAsync(TiffTag.TileOffsets, new TiffValueCollection <ulong>(offsets)).ConfigureAwait(false);

                    await ifdWriter.WriteTagAsync(TiffTag.TileByteCounts, new TiffValueCollection <ulong>(byteCounts)).ConfigureAwait(false);
                }
                else
                {
                    await ifdWriter.WriteTagAsync(TiffTag.ImageWidth, new TiffValueCollection <uint>((uint)(width / 2))).ConfigureAwait(false);

                    await ifdWriter.WriteTagAsync(TiffTag.ImageLength, new TiffValueCollection <uint>((uint)(height / 2))).ConfigureAwait(false);

                    uint[] tempArr = new uint[offsets.Length];
                    for (int i = 0; i < tempArr.Length; i++)
                    {
                        tempArr[i] = (uint)offsets[i];
                    }
                    await ifdWriter.WriteTagAsync(TiffTag.TileOffsets, new TiffValueCollection <uint>(tempArr)).ConfigureAwait(false);

                    for (int i = 0; i < tempArr.Length; i++)
                    {
                        tempArr[i] = (uint)byteCounts[i];
                    }
                    await ifdWriter.WriteTagAsync(TiffTag.TileByteCounts, new TiffValueCollection <uint>(tempArr)).ConfigureAwait(false);
                }

                string software = Software;
                if (!string.IsNullOrEmpty(software))
                {
                    await ifdWriter.WriteTagAsync(TiffTag.Software, new TiffValueCollection <string>(software));
                }

                ifdOffset = await ifdWriter.FlushAsync(ifdOffset).ConfigureAwait(false);
            }

            reporter?.ReportCompleteReducedResolutionLayerGeneration(layer, offsets.Length, (long)totalByteCount);


            return(ifdOffset);
        }
Пример #15
0
        private static async Task CopyIfdAsync(TiffFileContentReader contentReader, TiffFieldReader fieldReader, TiffImageFileDirectory ifd, TiffImageFileDirectoryWriter dest, CancellationToken cancellationToken)
        {
            var tagReader = new TiffTagReader(fieldReader, ifd);

            foreach (TiffImageFileDirectoryEntry entry in ifd)
            {
                // Stripped image data
                if (entry.Tag == TiffTag.StripOffsets)
                {
                    await CopyStrippedImageAsync(contentReader, tagReader, dest, cancellationToken);
                }
                else if (entry.Tag == TiffTag.StripByteCounts)
                {
                    // Ignore this
                }

                // Tiled image data
                else if (entry.Tag == TiffTag.TileOffsets)
                {
                    await CopyTiledImageAsync(contentReader, tagReader, dest, cancellationToken);
                }
                else if (entry.Tag == TiffTag.TileByteCounts)
                {
                    // Ignore this
                }

                // Other fields
                else
                {
                    await CopyFieldValueAsync(fieldReader, entry, dest, cancellationToken);
                }
            }
        }
Пример #16
0
 /// <summary>
 /// Adds the specified tag to the Image File Directory.
 /// </summary>
 /// <param name="directory">The Image File Directory.</param>
 /// <param name="tag">The tag.</param>
 /// <param name="value">The array of values.</param>
 protected void AddImageTag(TiffImageFileDirectory directory, UInt16 tag, Object[] value)
 {
     directory[tag] = value;
 }
Пример #17
0
 /// <summary>
 /// Adds the specified tag to the Image File Directory.
 /// </summary>
 /// <param name="directory">The Image File Directory.</param>
 /// <param name="tag">The tag.</param>
 /// <param name="value">The value.</param>
 protected void AddImageTag(TiffImageFileDirectory directory, UInt16 tag, Object value)
 {
     directory[tag] = new Object[] { value };
 }
Пример #18
0
        /// <summary>
        /// Writes an image file directory to the stream.
        /// </summary>
        /// <param name="imageFileDirectory">The image file directory.</param>
        private void WriteImageFileDirectory(TiffImageFileDirectory imageFileDirectory)
        {
            _currentImageFileDirectoryStartPosition = _baseStream.Position;

            // write starting position
            _baseStream.Seek(_currentImageFileDirectoryEndPosition, SeekOrigin.Begin);
            _baseStream.Write(EndianBitConverter.GetBytes((UInt32)_currentImageFileDirectoryStartPosition), 0, _imageFileDirectoryFieldSize);

            // compute size of directory (without additional fields)
            Int64 imageFileDirectorySize = 2 + _imageFileDirectoryEntrySize * imageFileDirectory.Count;

            _currentImageFileDirectoryEndPosition = _currentImageFileDirectoryStartPosition + imageFileDirectorySize;

            // position after the IFD to write exceeding values
            _baseStream.Seek(_currentImageFileDirectoryEndPosition + _imageFileDirectoryFieldSize, SeekOrigin.Begin);

            // the IFD should be written in one operation
            Byte[] bytes;
            if (_imageCount == _maxImageCount - 1)
            {
                bytes = new Byte[imageFileDirectorySize + _imageFileDirectoryFieldSize];
            }
            else
            {
                bytes = new Byte[imageFileDirectorySize];
            }

            EndianBitConverter.CopyBytes((UInt16)imageFileDirectory.Count, bytes, 0); // number of entries
            Int32 byteIndex = 2;

            TiffTagType entryType;
            UInt16      dataSize;
            Int64       dataCount;

            foreach (UInt16 entryTag in imageFileDirectory.Keys)
            {
                entryType = TiffTag.GetType(imageFileDirectory[entryTag][0]);
                dataSize  = TiffTag.GetSize(entryType);

                if (entryType == TiffTagType.ASCII)
                {
                    dataCount = 0;
                    for (Int32 i = 0; i < imageFileDirectory[entryTag].Length; i++)
                    {
                        dataCount += (imageFileDirectory[entryTag][i] as String).Length;
                    }
                }
                else
                {
                    dataCount = imageFileDirectory[entryTag].Length;
                }

                EndianBitConverter.CopyBytes(entryTag, bytes, byteIndex);
                EndianBitConverter.CopyBytes((UInt16)entryType, bytes, byteIndex + 2);
                EndianBitConverter.CopyBytes((UInt32)dataCount, bytes, byteIndex + 4);

                // values exceeding he field size (4) must be written to another position
                Byte[] dataBytes;
                Int32  dataStartIndex;
                if (dataCount * dataSize <= 4)
                {
                    dataBytes      = bytes;
                    dataStartIndex = byteIndex + 8;
                }
                else
                {
                    dataBytes      = new Byte[dataCount * dataSize + (dataCount * dataSize) % 2];
                    dataStartIndex = 0;
                }

                for (Int32 valueIndex = 0; valueIndex < imageFileDirectory[entryTag].Length; valueIndex++)
                {
                    dataStartIndex = TiffTag.SetValue(entryType, imageFileDirectory[entryTag][valueIndex], dataBytes, dataStartIndex);
                }

                if (dataCount * dataSize > 4)
                {
                    Int64 valuePosition = _baseStream.Position;
                    _baseStream.Write(dataBytes, 0, dataBytes.Length);

                    EndianBitConverter.CopyBytes((UInt32)valuePosition, bytes, byteIndex + 8);
                }

                byteIndex += _imageFileDirectoryEntrySize;
            }

            _currentImageStartPosition = _baseStream.Position;

            // write the IFD
            _baseStream.Seek(_currentImageFileDirectoryStartPosition, SeekOrigin.Begin);
            _baseStream.Write(bytes, 0, bytes.Length);
        }
Пример #19
0
        private static async Task DumpWellKnownTagsAsync(TiffFieldReader fieldReader, TiffImageFileDirectory ifd, CancellationToken cancellationToken)
        {
            int count     = 0;
            var tagReader = new TiffTagReader(fieldReader, ifd);

            if (ifd.Contains(TiffTag.PhotometricInterpretation))
            {
                Console.WriteLine("    PhotometricInterpretation = " + (await tagReader.ReadPhotometricInterpretationAsync(cancellationToken)));
                count++;
            }
            if (ifd.Contains(TiffTag.SamplesPerPixel))
            {
                Console.WriteLine("    SamplesPerPixel = " + (await tagReader.ReadSamplesPerPixelAsync(cancellationToken)));
                count++;
            }
            if (ifd.Contains(TiffTag.BitsPerSample))
            {
                Console.Write("    BitsPerSample = ");
                DumpValueCollecionSimple(await tagReader.ReadBitsPerSampleAsync(cancellationToken));
                Console.WriteLine();
                count++;
            }
            if (ifd.Contains(TiffTag.ImageWidth))
            {
                Console.WriteLine("    ImageWidth = " + (await tagReader.ReadImageWidthAsync(cancellationToken)));
                count++;
            }
            if (ifd.Contains(TiffTag.ImageLength))
            {
                Console.WriteLine("    ImageLength = " + (await tagReader.ReadImageLengthAsync(cancellationToken)));
                count++;
            }
            if (ifd.Contains(TiffTag.Compression))
            {
                Console.WriteLine("    Compression = " + (await tagReader.ReadCompressionAsync(cancellationToken)));
                count++;
            }
            if (ifd.Contains(TiffTag.FillOrder))
            {
                Console.WriteLine("    FillOrder = " + (await tagReader.ReadFillOrderAsync(cancellationToken)));
                count++;
            }
            if (ifd.Contains(TiffTag.Predictor))
            {
                Console.WriteLine("    Predictor = " + (await tagReader.ReadPredictorAsync(cancellationToken)));
                count++;
            }
            if (ifd.Contains(TiffTag.Orientation))
            {
                Console.WriteLine("    Orientation = " + (await tagReader.ReadOrientationAsync(cancellationToken)));
                count++;
            }
            if (ifd.Contains(TiffTag.RowsPerStrip))
            {
                Console.WriteLine("    RowsPerStrip = " + (await tagReader.ReadRowsPerStripAsync(cancellationToken)));
                count++;
            }
            if (ifd.Contains(TiffTag.TileWidth))
            {
                Console.WriteLine("    TileWidth = " + (await tagReader.ReadTileWidthAsync(cancellationToken)));
                count++;
            }
            if (ifd.Contains(TiffTag.TileLength))
            {
                Console.WriteLine("    TileLength = " + (await tagReader.ReadTileLengthAsync(cancellationToken)));
                count++;
            }

            if (count == 0)
            {
                Console.WriteLine("    No well-known tags found.");
            }
        }
Пример #20
0
        /// <summary>
        /// Computes the Image File Directory of a geometry.
        /// </summary>
        /// <param name="geometry">The geometry.</param>
        /// <param name="compression">The compression.</param>
        /// <param name="format">The sample format.</param>
        /// <param name="startPosition">The starting position of the raster content within the stream.</param>
        /// <param name="endPosition">The ending position of the raster content within the stream.</param>
        /// <returns>The computed Image File Directory.</returns>
        protected override TiffImageFileDirectory ComputeImageFileDirectory(ISpectralGeometry geometry, TiffCompression compression, TiffSampleFormat format)
        {
            TiffImageFileDirectory imageFileDirectory = base.ComputeImageFileDirectory(geometry, compression, format);

            CoordinateReferenceSystem referenceSystem = geometry.ReferenceSystem as CoordinateReferenceSystem;

            GeoKeyDirectory geoKeyDirectory = new GeoKeyDirectory();

            AddGeoKey(geoKeyDirectory, GeoKey.Citation, geometry.Metadata, "GeoTIFF::GeoCitation");
            AddGeoKey(geoKeyDirectory, GeoKey.GeodeticCoordinateReferenceSystemCitation, geometry.Metadata, "GeoTIFF::GeodeticCoordinateReferenceSystemCitation");
            AddGeoKey(geoKeyDirectory, GeoKey.ProjectedCoordinateReferenceSystemCitation, geometry.Metadata, "GeoTIFF::ProjectedCoordinateReferenceSystemCitation");

            if (geometry.Raster.Mapper != null) // if mapper is available
            {
                geoKeyDirectory.Add(GeoKey.RasterType, (Int16)((geometry.Raster.Mapper.Mode == RasterMapMode.ValueIsArea) ? 1 : 2));

                imageFileDirectory.Add(TiffTag.ModelTiepointTag, new Object[] { 0.0, 0.0, 0.0, geometry.Raster.Mapper.Translation.X, geometry.Raster.Mapper.Translation.Y, 0.0 });
                imageFileDirectory.Add(TiffTag.ModelPixelScaleTag, new Object[] { geometry.Raster.Mapper.ColumnSize, geometry.Raster.Mapper.RowSize, 1.0 });
            }

            if (referenceSystem != null) // if reference system is available (and supported)
            {
                switch (referenceSystem.Type)
                {
                case ReferenceSystemType.Projected:
                    ComputeProjectedCoordinateReferenceSystem(geoKeyDirectory, referenceSystem as ProjectedCoordinateReferenceSystem);
                    break;

                case ReferenceSystemType.Geographic2D:
                case ReferenceSystemType.Geographic3D:
                    ComputeGeodeticCoordinateReferenceSystem(geoKeyDirectory, referenceSystem as GeographicCoordinateReferenceSystem);
                    break;

                default:     // other reference systems are not supported
                    return(imageFileDirectory);
                }
            }

            WriteGeoKeyDirectory(imageFileDirectory, geoKeyDirectory);

            if (geometry.Imaging != null) // add imaging data
            {
                imageFileDirectory.Add(57410, new Object[] { geometry.Imaging.Device.Name });
                imageFileDirectory.Add(57411, new Object[] { geometry.Imaging.Time.ToString(CultureInfo.InvariantCulture.DateTimeFormat) });
                imageFileDirectory.Add(57412, new Object[] { geometry.Imaging.DeviceLocation.Latitude.BaseValue, geometry.Imaging.DeviceLocation.Longitude.BaseValue, geometry.Imaging.DeviceLocation.Height.BaseValue });
                imageFileDirectory.Add(57413, new Object[] { geometry.Imaging.IncidenceAngle, geometry.Imaging.ViewingAngle, geometry.Imaging.SunAzimuth, geometry.Imaging.SunElevation });
                imageFileDirectory.Add(57417, geometry.Imaging.Bands.Select(band => band.PhysicalGain).Cast <Object>().ToArray());
                imageFileDirectory.Add(57418, geometry.Imaging.Bands.Select(band => band.PhysicalBias).Cast <Object>().ToArray());
                imageFileDirectory.Add(57419, geometry.Imaging.Bands.Select(band => band.SolarIrradiance).Cast <Object>().ToArray());

                Object[] imageLocation = new Object[12];
                for (Int32 coordinateIndex = 0; coordinateIndex < geometry.Imaging.ImageLocation.Count; coordinateIndex++)
                {
                    imageLocation[3 * coordinateIndex]     = geometry.Imaging.ImageLocation[coordinateIndex].Latitude.BaseValue;
                    imageLocation[3 * coordinateIndex + 1] = geometry.Imaging.ImageLocation[coordinateIndex].Longitude.BaseValue;
                    imageLocation[3 * coordinateIndex + 2] = geometry.Imaging.ImageLocation[coordinateIndex].Height.BaseValue;
                }
                imageFileDirectory.Add(57420, imageLocation);
            }

            return(imageFileDirectory);
        }