示例#1
0
            public override Image <TPixel> GetImage()
            {
                var result = new Image <TPixel>(this.Configuration, this.Width, this.Height);

                TPixel topLeftColor    = Color.Red.ToPixel <TPixel>();
                TPixel topRightColor   = Color.Green.ToPixel <TPixel>();
                TPixel bottomLeftColor = Color.Blue.ToPixel <TPixel>();

                // Transparent purple:
                TPixel bottomRightColor = default;

                bottomRightColor.FromVector4(new Vector4(1f, 0f, 1f, 0.5f));

                int midY = this.Height / 2;
                int midX = this.Width / 2;
                Buffer2D <TPixel> imageBuffer = result.GetRootFramePixelBuffer();

                for (int y = 0; y < midY; y++)
                {
                    Span <TPixel> row = imageBuffer.DangerousGetRowSpan(y);

                    row.Slice(0, midX).Fill(topLeftColor);
                    row.Slice(midX, this.Width - midX).Fill(topRightColor);
                }

                for (int y = midY; y < this.Height; y++)
                {
                    Span <TPixel> row = imageBuffer.DangerousGetRowSpan(y);

                    row.Slice(0, midX).Fill(bottomLeftColor);
                    row.Slice(midX, this.Width - midX).Fill(bottomRightColor);
                }

                return(result);
            }
        /// <summary>
        /// Apply an image integral. <See href="https://en.wikipedia.org/wiki/Summed-area_table"/>
        /// </summary>
        /// <param name="source">The image on which to apply the integral.</param>
        /// <typeparam name="TPixel">The type of the pixel.</typeparam>
        /// <returns>The <see cref="Buffer2D{T}"/> containing all the sums.</returns>
        public static Buffer2D <ulong> CalculateIntegralImage <TPixel>(this Image <TPixel> source)
            where TPixel : unmanaged, IPixel <TPixel>
        {
            Configuration configuration = source.GetConfiguration();

            int endY = source.Height;
            int endX = source.Width;

            Buffer2D <ulong>  intImage     = configuration.MemoryAllocator.Allocate2D <ulong>(source.Width, source.Height);
            ulong             sumX0        = 0;
            Buffer2D <TPixel> sourceBuffer = source.Frames.RootFrame.PixelBuffer;

            using (IMemoryOwner <L8> tempRow = configuration.MemoryAllocator.Allocate <L8>(source.Width))
            {
                Span <L8>     tempSpan  = tempRow.GetSpan();
                Span <TPixel> sourceRow = sourceBuffer.DangerousGetRowSpan(0);
                Span <ulong>  destRow   = intImage.DangerousGetRowSpan(0);

                PixelOperations <TPixel> .Instance.ToL8(configuration, sourceRow, tempSpan);

                // First row
                for (int x = 0; x < endX; x++)
                {
                    sumX0     += tempSpan[x].PackedValue;
                    destRow[x] = sumX0;
                }

                Span <ulong> previousDestRow = destRow;

                // All other rows
                for (int y = 1; y < endY; y++)
                {
                    sourceRow = sourceBuffer.DangerousGetRowSpan(y);
                    destRow   = intImage.DangerousGetRowSpan(y);

                    PixelOperations <TPixel> .Instance.ToL8(configuration, sourceRow, tempSpan);

                    // Process first column
                    sumX0      = tempSpan[0].PackedValue;
                    destRow[0] = sumX0 + previousDestRow[0];

                    // Process all other colmns
                    for (int x = 1; x < endX; x++)
                    {
                        sumX0     += tempSpan[x].PackedValue;
                        destRow[x] = sumX0 + previousDestRow[x];
                    }

                    previousDestRow = destRow;
                }
            }

            return(intImage);
        }
示例#3
0
        /// <summary>
        /// Reads a uncompressed TGA image where each pixels has 16 bit.
        /// </summary>
        /// <typeparam name="TPixel">The pixel type.</typeparam>
        /// <param name="width">The width of the image.</param>
        /// <param name="height">The height of the image.</param>
        /// <param name="pixels">The <see cref="Buffer2D{TPixel}"/> to assign the palette to.</param>
        /// <param name="origin">The image origin.</param>
        private void ReadBgra16 <TPixel>(int width, int height, Buffer2D <TPixel> pixels, TgaImageOrigin origin)
            where TPixel : unmanaged, IPixel <TPixel>
        {
            TPixel color   = default;
            bool   invertX = InvertX(origin);

            using IMemoryOwner <byte> row = this.memoryAllocator.AllocatePaddedPixelRowBuffer(width, 2, 0);
            Span <byte> rowSpan           = row.GetSpan();

            for (int y = 0; y < height; y++)
            {
                int           newY      = InvertY(y, height, origin);
                Span <TPixel> pixelSpan = pixels.DangerousGetRowSpan(newY);

                if (invertX)
                {
                    for (int x = width - 1; x >= 0; x--)
                    {
                        this.currentStream.Read(this.scratchBuffer, 0, 2);
                        if (!this.hasAlpha)
                        {
                            this.scratchBuffer[1] |= 1 << 7;
                        }

                        if (this.fileHeader.ImageType == TgaImageType.BlackAndWhite)
                        {
                            color.FromLa16(Unsafe.As <byte, La16>(ref this.scratchBuffer[0]));
                        }
                        else
                        {
                            color.FromBgra5551(Unsafe.As <byte, Bgra5551>(ref this.scratchBuffer[0]));
                        }

                        pixelSpan[x] = color;
                    }
                }
                else
                {
                    this.currentStream.Read(rowSpan);

                    if (!this.hasAlpha)
                    {
                        // We need to set the alpha component value to fully opaque.
                        for (int x = 1; x < rowSpan.Length; x += 2)
                        {
                            rowSpan[x] |= 1 << 7;
                        }
                    }

                    if (this.fileHeader.ImageType == TgaImageType.BlackAndWhite)
                    {
                        PixelOperations <TPixel> .Instance.FromLa16Bytes(this.Configuration, rowSpan, pixelSpan, width);
                    }
                    else
                    {
                        PixelOperations <TPixel> .Instance.FromBgra5551Bytes(this.Configuration, rowSpan, pixelSpan, width);
                    }
                }
            }
        }
示例#4
0
        public void ApplyQuantizationDither <TFrameQuantizer, TPixel>(
            ref TFrameQuantizer quantizer,
            ImageFrame <TPixel> source,
            IndexedImageFrame <TPixel> destination,
            Rectangle bounds)
            where TFrameQuantizer : struct, IQuantizer <TPixel>
            where TPixel : unmanaged, IPixel <TPixel>
        {
            if (this == default)
            {
                ThrowDefaultInstance();
            }

            int               spread       = CalculatePaletteSpread(destination.Palette.Length);
            float             scale        = quantizer.Options.DitherScale;
            Buffer2D <TPixel> sourceBuffer = source.PixelBuffer;

            for (int y = bounds.Top; y < bounds.Bottom; y++)
            {
                ReadOnlySpan <TPixel> sourceRow = sourceBuffer.DangerousGetRowSpan(y).Slice(bounds.X, bounds.Width);
                Span <byte>           destRow   = destination.GetWritablePixelRowSpanUnsafe(y - bounds.Y).Slice(0, sourceRow.Length);

                for (int x = 0; x < sourceRow.Length; x++)
                {
                    TPixel dithered = this.Dither(sourceRow[x], x, y, spread, scale);
                    destRow[x] = quantizer.GetQuantizedColor(dithered, out TPixel _);
                }
            }
        }
示例#5
0
        private static void WriteGrayscale <TPixel>(Configuration configuration, Stream stream, ImageFrame <TPixel> image)
            where TPixel : unmanaged, IPixel <TPixel>
        {
            int width  = image.Width;
            int height = image.Height;
            Buffer2D <TPixel> pixelBuffer = image.PixelBuffer;
            MemoryAllocator   allocator   = configuration.MemoryAllocator;

            using IMemoryOwner <L8> row = allocator.Allocate <L8>(width);
            Span <L8> rowSpan           = row.GetSpan();

            using IMemoryOwner <byte> plainMemory = allocator.Allocate <byte>(width * MaxCharsPerPixelGrayscale);
            Span <byte> plainSpan = plainMemory.GetSpan();

            for (int y = 0; y < height; y++)
            {
                Span <TPixel> pixelSpan = pixelBuffer.DangerousGetRowSpan(y);
                PixelOperations <TPixel> .Instance.ToL8(
                    configuration,
                    pixelSpan,
                    rowSpan);

                int written = 0;
                for (int x = 0; x < width; x++)
                {
                    Utf8Formatter.TryFormat(rowSpan[x].PackedValue, plainSpan.Slice(written), out int bytesWritten, DecimalFormat);
                    written += bytesWritten;
                    plainSpan[written++] = Space;
                }

                plainSpan[written - 1] = NewLine;
                stream.Write(plainSpan, 0, written);
            }
        }
示例#6
0
        /// <summary>
        /// Extract the alpha data of the image.
        /// </summary>
        /// <typeparam name="TPixel">The pixel format.</typeparam>
        /// <param name="image">The <see cref="ImageFrame{TPixel}"/> to encode from.</param>
        /// <param name="configuration">The global configuration.</param>
        /// <param name="memoryAllocator">The memory manager.</param>
        /// <returns>A byte sequence of length width * height, containing all the 8-bit transparency values in scan order.</returns>
        private static IMemoryOwner <byte> ExtractAlphaChannel <TPixel>(Image <TPixel> image, Configuration configuration, MemoryAllocator memoryAllocator)
            where TPixel : unmanaged, IPixel <TPixel>
        {
            Buffer2D <TPixel> imageBuffer = image.Frames.RootFrame.PixelBuffer;
            int height = image.Height;
            int width  = image.Width;
            IMemoryOwner <byte> alphaDataBuffer = memoryAllocator.Allocate <byte>(width * height);
            Span <byte>         alphaData       = alphaDataBuffer.GetSpan();

            using IMemoryOwner <Rgba32> rowBuffer = memoryAllocator.Allocate <Rgba32>(width);
            Span <Rgba32> rgbaRow = rowBuffer.GetSpan();

            for (int y = 0; y < height; y++)
            {
                Span <TPixel> rowSpan = imageBuffer.DangerousGetRowSpan(y);
                PixelOperations <TPixel> .Instance.ToRgba32(configuration, rowSpan, rgbaRow);

                int offset = y * width;
                for (int x = 0; x < width; x++)
                {
                    alphaData[offset + x] = rgbaRow[x].A;
                }
            }

            return(alphaDataBuffer);
        }
        /// <inheritdoc />
        protected override void OnFrameApply(ImageFrame <TPixel> source)
        {
            var interest = Rectangle.Intersect(source.Bounds(), this.SourceRectangle);

            Configuration configuration = this.Configuration;

            using IQuantizer <TPixel> frameQuantizer   = this.quantizer.CreatePixelSpecificQuantizer <TPixel>(configuration);
            using IndexedImageFrame <TPixel> quantized = frameQuantizer.BuildPaletteAndQuantizeFrame(source, interest);

            ReadOnlySpan <TPixel> paletteSpan = quantized.Palette.Span;
            int offsetY = interest.Top;
            int offsetX = interest.Left;
            Buffer2D <TPixel> sourceBuffer = source.PixelBuffer;

            for (int y = interest.Y; y < interest.Height; y++)
            {
                Span <TPixel>       row          = sourceBuffer.DangerousGetRowSpan(y);
                ReadOnlySpan <byte> quantizedRow = quantized.DangerousGetRowSpan(y - offsetY);

                for (int x = interest.Left; x < interest.Right; x++)
                {
                    row[x] = paletteSpan[quantizedRow[x - offsetX]];
                }
            }
        }
示例#8
0
        /// <summary>
        /// Writes an 8 bit gray image with a color palette. The color palette has 256 entry's with 4 bytes for each entry.
        /// </summary>
        /// <typeparam name="TPixel">The type of the pixel.</typeparam>
        /// <param name="stream">The <see cref="Stream"/> to write to.</param>
        /// <param name="image"> The <see cref="ImageFrame{TPixel}"/> containing pixel data.</param>
        /// <param name="colorPalette">A byte span of size 1024 for the color palette.</param>
        private void Write8BitGray <TPixel>(Stream stream, ImageFrame <TPixel> image, Span <byte> colorPalette)
            where TPixel : unmanaged, IPixel <TPixel>
        {
            // Create a color palette with 256 different gray values.
            for (int i = 0; i <= 255; i++)
            {
                int  idx       = i * 4;
                byte grayValue = (byte)i;
                colorPalette[idx]     = grayValue;
                colorPalette[idx + 1] = grayValue;
                colorPalette[idx + 2] = grayValue;

                // Padding byte, always 0.
                colorPalette[idx + 3] = 0;
            }

            stream.Write(colorPalette);
            Buffer2D <TPixel> imageBuffer = image.PixelBuffer;

            for (int y = image.Height - 1; y >= 0; y--)
            {
                ReadOnlySpan <TPixel> inputPixelRow  = imageBuffer.DangerousGetRowSpan(y);
                ReadOnlySpan <byte>   outputPixelRow = MemoryMarshal.AsBytes(inputPixelRow);
                stream.Write(outputPixelRow);

                for (int i = 0; i < this.padding; i++)
                {
                    stream.WriteByte(0);
                }
            }
        }
        /// <inheritdoc/>
        public override void Decode(IMemoryOwner <byte>[] data, Buffer2D <TPixel> pixels, int left, int top, int width, int height)
        {
            Span <byte> yData  = data[0].GetSpan();
            Span <byte> cbData = data[1].GetSpan();
            Span <byte> crData = data[2].GetSpan();

            if (this.ycbcrSubSampling != null && !(this.ycbcrSubSampling[0] == 1 && this.ycbcrSubSampling[1] == 1))
            {
                ReverseChromaSubSampling(width, height, this.ycbcrSubSampling[0], this.ycbcrSubSampling[1], cbData, crData);
            }

            var color        = default(TPixel);
            int offset       = 0;
            int widthPadding = 0;

            if (this.ycbcrSubSampling != null)
            {
                // Round to the next integer multiple of horizontalSubSampling.
                widthPadding = TiffUtils.PaddingToNextInteger(width, this.ycbcrSubSampling[0]);
            }

            for (int y = top; y < top + height; y++)
            {
                Span <TPixel> pixelRow = pixels.DangerousGetRowSpan(y).Slice(left, width);
                for (int x = 0; x < pixelRow.Length; x++)
                {
                    Rgba32 rgba = this.converter.ConvertToRgba32(yData[offset], cbData[offset], crData[offset]);
                    color.FromRgba32(rgba);
                    pixelRow[x] = color;
                    offset++;
                }

                offset += widthPadding;
            }
        }
示例#10
0
        private static void WriteWideGrayscale <TPixel>(Configuration configuration, Stream stream, ImageFrame <TPixel> image)
            where TPixel : unmanaged, IPixel <TPixel>
        {
            const int         bytesPerPixel = 2;
            int               width         = image.Width;
            int               height        = image.Height;
            Buffer2D <TPixel> pixelBuffer   = image.PixelBuffer;
            MemoryAllocator   allocator     = configuration.MemoryAllocator;

            using IMemoryOwner <byte> row = allocator.Allocate <byte>(width * bytesPerPixel);
            Span <byte> rowSpan           = row.GetSpan();

            for (int y = 0; y < height; y++)
            {
                Span <TPixel> pixelSpan = pixelBuffer.DangerousGetRowSpan(y);

                PixelOperations <TPixel> .Instance.ToL16Bytes(
                    configuration,
                    pixelSpan,
                    rowSpan,
                    width);

                stream.Write(rowSpan);
            }
        }
示例#11
0
        private static void WriteBlackAndWhite <TPixel>(Configuration configuration, Stream stream, ImageFrame <TPixel> image)
            where TPixel : unmanaged, IPixel <TPixel>
        {
            int width  = image.Width;
            int height = image.Height;
            Buffer2D <TPixel> pixelBuffer = image.PixelBuffer;
            MemoryAllocator   allocator   = configuration.MemoryAllocator;

            using IMemoryOwner <L8> row = allocator.Allocate <L8>(width);
            Span <L8> rowSpan           = row.GetSpan();

            using IMemoryOwner <byte> plainMemory = allocator.Allocate <byte>(width * MaxCharsPerPixelBlackAndWhite);
            Span <byte> plainSpan = plainMemory.GetSpan();

            for (int y = 0; y < height; y++)
            {
                Span <TPixel> pixelSpan = pixelBuffer.DangerousGetRowSpan(y);
                PixelOperations <TPixel> .Instance.ToL8(
                    configuration,
                    pixelSpan,
                    rowSpan);

                int written = 0;
                for (int x = 0; x < width; x++)
                {
                    byte value = (rowSpan[x].PackedValue < 128) ? One : Zero;
                    plainSpan[written++] = value;
                    plainSpan[written++] = Space;
                }

                plainSpan[written - 1] = NewLine;
                stream.Write(plainSpan, 0, written);
            }
        }
示例#12
0
        public void WriteImagePixels_SingleImage()
        {
            using var image = new Image <L16>(256, 256);
            this.ProcessPixelRowsImpl(image, accessor =>
            {
                for (int y = 0; y < accessor.Height; y++)
                {
                    Span <L16> row = accessor.GetRowSpan(y);
                    for (int x = 0; x < row.Length; x++)
                    {
                        row[x] = new L16((ushort)(x * y));
                    }
                }
            });

            Buffer2D <L16> buffer = image.Frames.RootFrame.PixelBuffer;

            for (int y = 0; y < 256; y++)
            {
                Span <L16> row = buffer.DangerousGetRowSpan(y);
                for (int x = 0; x < 256; x++)
                {
                    int actual = row[x].PackedValue;
                    Assert.Equal(x * y, actual);
                }
            }
        }
示例#13
0
        /// <summary>
        /// Decodes pixel data using the current photometric interpretation.
        /// </summary>
        /// <param name="data">The buffers to read image data from.</param>
        /// <param name="pixels">The image buffer to write pixels to.</param>
        /// <param name="left">The x-coordinate of the left-hand side of the image block.</param>
        /// <param name="top">The y-coordinate of the  top of the image block.</param>
        /// <param name="width">The width of the image block.</param>
        /// <param name="height">The height of the image block.</param>
        public override void Decode(IMemoryOwner <byte>[] data, Buffer2D <TPixel> pixels, int left, int top, int width, int height)
        {
            var color = default(TPixel);

            var rBitReader = new BitReader(data[0].GetSpan());
            var gBitReader = new BitReader(data[1].GetSpan());
            var bBitReader = new BitReader(data[2].GetSpan());

            for (int y = top; y < top + height; y++)
            {
                Span <TPixel> pixelRow = pixels.DangerousGetRowSpan(y).Slice(left, width);
                for (int x = 0; x < pixelRow.Length; x++)
                {
                    float r = rBitReader.ReadBits(this.bitsPerSampleR) / this.rFactor;
                    float g = gBitReader.ReadBits(this.bitsPerSampleG) / this.gFactor;
                    float b = bBitReader.ReadBits(this.bitsPerSampleB) / this.bFactor;

                    color.FromVector4(new Vector4(r, g, b, 1.0f));
                    pixelRow[x] = color;
                }

                rBitReader.NextRow();
                gBitReader.NextRow();
                bBitReader.NextRow();
            }
        }
示例#14
0
        /// <summary>
        /// Swaps the image at the X-axis, which goes horizontally through the middle at half the height of the image.
        /// </summary>
        /// <param name="source">The source image to apply the process to.</param>
        /// <param name="configuration">The configuration.</param>
        private void FlipX(Buffer2D <TPixel> source, Configuration configuration)
        {
            int height = source.Height;

            using IMemoryOwner <TPixel> tempBuffer = configuration.MemoryAllocator.Allocate <TPixel>(source.Width);
            Span <TPixel> temp = tempBuffer.Memory.Span;

            for (int yTop = 0; yTop < height / 2; yTop++)
            {
                int           yBottom   = height - yTop - 1;
                Span <TPixel> topRow    = source.DangerousGetRowSpan(yBottom);
                Span <TPixel> bottomRow = source.DangerousGetRowSpan(yTop);
                topRow.CopyTo(temp);
                bottomRow.CopyTo(topRow);
                temp.CopyTo(bottomRow);
            }
        }
示例#15
0
        private void ReadBgra32Row <TPixel>(int width, Buffer2D <TPixel> pixels, Span <byte> row, int y)
            where TPixel : unmanaged, IPixel <TPixel>
        {
            this.currentStream.Read(row);
            Span <TPixel> pixelSpan = pixels.DangerousGetRowSpan(y);

            PixelOperations <TPixel> .Instance.FromBgra32Bytes(this.Configuration, row, pixelSpan, width);
        }
示例#16
0
 private void CopyPixelRowFast(
     Buffer2D <TPixel> source,
     Span <Vector4> rowPixels,
     int x,
     int y,
     int tileWidth,
     Configuration configuration)
 => PixelOperations <TPixel> .Instance.ToVector4(configuration, source.DangerousGetRowSpan(y).Slice(start: x, length: tileWidth), rowPixels);
示例#17
0
        public void WriteImagePixels_MultiImage3()
        {
            using var img1 = new Image <L16>(256, 256);
            Buffer2D <L16> buffer2 = img1.Frames.RootFrame.PixelBuffer;

            for (int y = 0; y < 256; y++)
            {
                Span <L16> row = buffer2.DangerousGetRowSpan(y);
                for (int x = 0; x < 256; x++)
                {
                    row[x] = new L16((ushort)(x * y));
                }
            }

            using var img2 = new Image <L16>(256, 256);
            using var img3 = new Image <L16>(256, 256);

            this.ProcessPixelRowsImpl(img1, img2, img3, (accessor1, accessor2, accessor3) =>
            {
                for (int y = 0; y < accessor1.Height; y++)
                {
                    Span <L16> row1 = accessor1.GetRowSpan(y);
                    Span <L16> row2 = accessor2.GetRowSpan(accessor2.Height - y - 1);
                    Span <L16> row3 = accessor3.GetRowSpan(y);
                    row1.CopyTo(row2);
                    row1.CopyTo(row3);
                }
            });

            buffer2 = img2.Frames.RootFrame.PixelBuffer;
            Buffer2D <L16> buffer3 = img3.Frames.RootFrame.PixelBuffer;

            for (int y = 0; y < 256; y++)
            {
                Span <L16> row2 = buffer2.DangerousGetRowSpan(y);
                Span <L16> row3 = buffer3.DangerousGetRowSpan(y);
                for (int x = 0; x < 256; x++)
                {
                    int actual2 = row2[x].PackedValue;
                    int actual3 = row3[x].PackedValue;
                    Assert.Equal(x * (256 - y - 1), actual2);
                    Assert.Equal(x * y, actual3);
                }
            }
        }
示例#18
0
        public void ClearSpectralBuffers()
        {
            Buffer2D <Block8x8> spectralBlocks = this.component.SpectralBlocks;

            for (int i = 0; i < spectralBlocks.Height; i++)
            {
                spectralBlocks.DangerousGetRowSpan(i).Clear();
            }
        }
        /// <inheritdoc/>
        public void AddPaletteColors(Buffer2DRegion <TPixel> pixelRegion)
        {
            Rectangle         bounds = pixelRegion.Rectangle;
            Buffer2D <TPixel> source = pixelRegion.Buffer;

            using (IMemoryOwner <Rgba32> buffer = this.Configuration.MemoryAllocator.Allocate <Rgba32>(bounds.Width))
            {
                Span <Rgba32> bufferSpan = buffer.GetSpan();

                // Loop through each row
                for (int y = bounds.Top; y < bounds.Bottom; y++)
                {
                    Span <TPixel> row = source.DangerousGetRowSpan(y).Slice(bounds.Left, bounds.Width);
                    PixelOperations <TPixel> .Instance.ToRgba32(this.Configuration, row, bufferSpan);

                    for (int x = 0; x < bufferSpan.Length; x++)
                    {
                        Rgba32 rgba = bufferSpan[x];

                        // Add the color to the Octree
                        this.octree.AddColor(rgba);
                    }
                }
            }

            int           paletteIndex = 0;
            Span <TPixel> paletteSpan  = this.paletteOwner.GetSpan();

            // On very rare occasions, (blur.png), the quantizer does not preserve a
            // transparent entry when palletizing the captured colors.
            // To workaround this we ensure the palette ends with the default color
            // for higher bit depths. Lower bit depths will correctly reduce the palette.
            // TODO: Investigate more evenly reduced palette reduction.
            int max = this.maxColors;

            if (this.bitDepth == 8)
            {
                max--;
            }

            this.octree.Palletize(paletteSpan, max, ref paletteIndex);
            ReadOnlyMemory <TPixel> result = this.paletteOwner.Memory.Slice(0, paletteSpan.Length);

            // When called multiple times by QuantizerUtilities.BuildPalette
            // this prevents memory churn caused by reallocation.
            if (this.pixelMap is null)
            {
                this.pixelMap = new EuclideanPixelMap <TPixel>(this.Configuration, result);
            }
            else
            {
                this.pixelMap.Clear(result);
            }

            this.palette = result;
        }
示例#20
0
        public override ImageSimilarityReport <TPixelA, TPixelB> CompareImagesOrFrames <TPixelA, TPixelB>(ImageFrame <TPixelA> expected, ImageFrame <TPixelB> actual)
        {
            if (expected.Size() != actual.Size())
            {
                throw new InvalidOperationException("Calling ImageComparer is invalid when dimensions mismatch!");
            }

            int width = actual.Width;

            // TODO: Comparing through Rgba64 may not robust enough because of the existence of super high precision pixel types.
            var aBuffer = new Rgba64[width];
            var bBuffer = new Rgba64[width];

            float totalDifference = 0F;

            var                differences    = new List <PixelDifference>();
            Configuration      configuration  = expected.GetConfiguration();
            Buffer2D <TPixelA> expectedBuffer = expected.PixelBuffer;
            Buffer2D <TPixelB> actualBuffer   = actual.PixelBuffer;

            for (int y = 0; y < actual.Height; y++)
            {
                Span <TPixelA> aSpan = expectedBuffer.DangerousGetRowSpan(y);
                Span <TPixelB> bSpan = actualBuffer.DangerousGetRowSpan(y);

                PixelOperations <TPixelA> .Instance.ToRgba64(configuration, aSpan, aBuffer);

                PixelOperations <TPixelB> .Instance.ToRgba64(configuration, bSpan, bBuffer);

                for (int x = 0; x < width; x++)
                {
                    int d = GetManhattanDistanceInRgbaSpace(ref aBuffer[x], ref bBuffer[x]);

                    if (d > this.PerPixelManhattanThreshold)
                    {
                        var diff = new PixelDifference(new Point(x, y), aBuffer[x], bBuffer[x]);
                        differences.Add(diff);

                        totalDifference += d;
                    }
                }
            }

            float normalizedDifference = totalDifference / (actual.Width * (float)actual.Height);

            normalizedDifference /= 4F * 65535F;

            if (normalizedDifference > this.ImageThreshold)
            {
                return(new ImageSimilarityReport <TPixelA, TPixelB>(expected, actual, differences, normalizedDifference));
            }
            else
            {
                return(ImageSimilarityReport <TPixelA, TPixelB> .Empty);
            }
        }
示例#21
0
        private static void CopyImageBytesToBuffer(Span <byte> buffer, Buffer2D <Rgb24> pixelBuffer)
        {
            int offset = 0;

            for (int y = 0; y < pixelBuffer.Height; y++)
            {
                Span <Rgb24> pixelRowSpan = pixelBuffer.DangerousGetRowSpan(y);
                Span <byte>  rgbBytes     = MemoryMarshal.AsBytes(pixelRowSpan);
                rgbBytes.CopyTo(buffer.Slice(offset));
                offset += rgbBytes.Length;
            }
        }
示例#22
0
            public void LoadSpectral(JpegComponent c)
            {
                Buffer2D <Block8x8> data = c.SpectralBlocks;

                for (int y = 0; y < this.HeightInBlocks; y++)
                {
                    Span <Block8x8> blockRow = data.DangerousGetRowSpan(y);
                    for (int x = 0; x < this.WidthInBlocks; x++)
                    {
                        this.MakeBlock(blockRow[x], y, x);
                    }
                }
            }
示例#23
0
        /// <summary>
        /// Returns an image from the given System.Drawing bitmap.
        /// </summary>
        /// <typeparam name="TPixel">The pixel format.</typeparam>
        /// <param name="bmp">The input bitmap.</param>
        /// <exception cref="ArgumentException">Thrown if the image pixel format is not of type <see cref="PixelFormat.Format24bppRgb"/></exception>
        internal static unsafe Image <TPixel> From24bppRgbSystemDrawingBitmap <TPixel>(Bitmap bmp)
            where TPixel : unmanaged, IPixel <TPixel>
        {
            int w = bmp.Width;
            int h = bmp.Height;

            var fullRect = new System.Drawing.Rectangle(0, 0, w, h);

            if (bmp.PixelFormat != PixelFormat.Format24bppRgb)
            {
                throw new ArgumentException(
                          $"{nameof(From24bppRgbSystemDrawingBitmap)}: pixel format should be {PixelFormat.Format24bppRgb}!",
                          nameof(bmp));
            }

            BitmapData data  = bmp.LockBits(fullRect, ImageLockMode.ReadWrite, bmp.PixelFormat);
            var        image = new Image <TPixel>(w, h);

            try
            {
                byte *sourcePtrBase = (byte *)data.Scan0;

                long sourceRowByteCount = data.Stride;
                long destRowByteCount   = w * sizeof(Bgr24);

                Configuration     configuration = image.GetConfiguration();
                Buffer2D <TPixel> imageBuffer   = image.Frames.RootFrame.PixelBuffer;

                using (IMemoryOwner <Bgr24> workBuffer = Configuration.Default.MemoryAllocator.Allocate <Bgr24>(w))
                {
                    fixed(Bgr24 *destPtr = &workBuffer.GetReference())
                    {
                        for (int y = 0; y < h; y++)
                        {
                            Span <TPixel> row = imageBuffer.DangerousGetRowSpan(y);

                            byte *sourcePtr = sourcePtrBase + (data.Stride * y);

                            Buffer.MemoryCopy(sourcePtr, destPtr, destRowByteCount, sourceRowByteCount);
                            PixelOperations <TPixel> .Instance.FromBgr24(configuration, workBuffer.GetSpan().Slice(0, w), row);
                        }
                    }
                }
            }
            finally
            {
                bmp.UnlockBits(data);
            }

            return(image);
        }
示例#24
0
        private static void WriteBlackAndWhite <TPixel>(Configuration configuration, Stream stream, ImageFrame <TPixel> image)
            where TPixel : unmanaged, IPixel <TPixel>
        {
            int width  = image.Width;
            int height = image.Height;
            Buffer2D <TPixel> pixelBuffer = image.PixelBuffer;
            MemoryAllocator   allocator   = configuration.MemoryAllocator;

            using IMemoryOwner <L8> row = allocator.Allocate <L8>(width);
            Span <L8> rowSpan           = row.GetSpan();

            int previousValue = 0;
            int startBit      = 0;

            for (int y = 0; y < height; y++)
            {
                Span <TPixel> pixelSpan = pixelBuffer.DangerousGetRowSpan(y);

                PixelOperations <TPixel> .Instance.ToL8(
                    configuration,
                    pixelSpan,
                    rowSpan);

                for (int x = 0; x < width;)
                {
                    int value = previousValue;
                    for (int i = startBit; i < 8; i++)
                    {
                        if (rowSpan[x].PackedValue < 128)
                        {
                            value |= 0x80 >> i;
                        }

                        x++;
                        if (x == width)
                        {
                            previousValue = value;
                            startBit      = (i + 1) & 7; // Round off to below 8.
                            break;
                        }
                    }

                    if (startBit == 0)
                    {
                        stream.WriteByte((byte)value);
                        previousValue = 0;
                    }
                }
            }
        }
示例#25
0
        /// <summary>
        /// Reads a uncompressed TGA image where each pixels has 32 bit.
        /// </summary>
        /// <typeparam name="TPixel">The pixel type.</typeparam>
        /// <param name="width">The width of the image.</param>
        /// <param name="height">The height of the image.</param>
        /// <param name="pixels">The <see cref="Buffer2D{TPixel}"/> to assign the palette to.</param>
        /// <param name="origin">The image origin.</param>
        private void ReadBgra32 <TPixel>(int width, int height, Buffer2D <TPixel> pixels, TgaImageOrigin origin)
            where TPixel : unmanaged, IPixel <TPixel>
        {
            TPixel color   = default;
            bool   invertX = InvertX(origin);

            if (this.tgaMetadata.AlphaChannelBits == 8 && !invertX)
            {
                using IMemoryOwner <byte> row = this.memoryAllocator.AllocatePaddedPixelRowBuffer(width, 4, 0);
                Span <byte> rowSpan           = row.GetSpan();

                if (InvertY(origin))
                {
                    for (int y = height - 1; y >= 0; y--)
                    {
                        this.ReadBgra32Row(width, pixels, rowSpan, y);
                    }
                }
                else
                {
                    for (int y = 0; y < height; y++)
                    {
                        this.ReadBgra32Row(width, pixels, rowSpan, y);
                    }
                }

                return;
            }

            for (int y = 0; y < height; y++)
            {
                int           newY     = InvertY(y, height, origin);
                Span <TPixel> pixelRow = pixels.DangerousGetRowSpan(newY);
                if (invertX)
                {
                    for (int x = width - 1; x >= 0; x--)
                    {
                        this.ReadBgra32Pixel(x, color, pixelRow);
                    }
                }
                else
                {
                    for (int x = 0; x < width; x++)
                    {
                        this.ReadBgra32Pixel(x, color, pixelRow);
                    }
                }
            }
        }
示例#26
0
            public void LoadSpectralStride(Buffer2D <Block8x8> data, int strideIndex)
            {
                int startIndex = strideIndex * data.Height;

                int endIndex = Math.Min(this.HeightInBlocks, startIndex + data.Height);

                for (int y = startIndex; y < endIndex; y++)
                {
                    Span <Block8x8> blockRow = data.DangerousGetRowSpan(y - startIndex);
                    for (int x = 0; x < this.WidthInBlocks; x++)
                    {
                        this.MakeBlock(blockRow[x], y, x);
                    }
                }
            }
示例#27
0
            public void SwapOrCopyContent_WhenDestinationIsOwned_ShouldNotSwapInDisposedSourceBuffer()
            {
                using var destData = MemoryGroup <int> .Wrap(new int[100]);

                using var dest = new Buffer2D <int>(destData, 10, 10);

                using (Buffer2D <int> source = this.MemoryAllocator.Allocate2D <int>(10, 10, AllocationOptions.Clean))
                {
                    source[0, 0] = 1;
                    dest[0, 0]   = 2;

                    Buffer2D <int> .SwapOrCopyContent(dest, source);
                }

                int actual1 = dest.DangerousGetRowSpan(0)[0];
                int actual2 = dest.DangerousGetRowSpan(0)[0];
                int actual3 = dest.GetSafeRowMemory(0).Span[0];
                int actual5 = dest[0, 0];

                Assert.Equal(1, actual1);
                Assert.Equal(1, actual2);
                Assert.Equal(1, actual3);
                Assert.Equal(1, actual5);
            }
示例#28
0
        public Size BulkPixelConvert()
        {
            using var image = new Image <Rgba32>(800, 800);
            using IMemoryOwner <float> amounts = Configuration.Default.MemoryAllocator.Allocate <float>(image.Width);
            amounts.GetSpan().Fill(1);
            Buffer2D <Rgba32> pixels = image.GetRootFramePixelBuffer();

            for (int y = 0; y < image.Height; y++)
            {
                Span <Rgba32> span = pixels.DangerousGetRowSpan(y);
                this.BulkPixelConvert(span, span, span, amounts.GetSpan());
            }

            return(new Size(image.Width, image.Height));
        }
        /// <inheritdoc/>
        protected override void OnFrameApply(ImageFrame <TPixel> source)
        {
            var intersect = Rectangle.Intersect(this.SourceRectangle, source.Bounds());

            Configuration configuration  = this.Configuration;
            TPixel        upper          = this.definition.Upper.ToPixel <TPixel>();
            TPixel        lower          = this.definition.Lower.ToPixel <TPixel>();
            float         thresholdLimit = this.definition.ThresholdLimit;

            int startY = intersect.Y;
            int endY   = intersect.Bottom;
            int startX = intersect.X;
            int endX   = intersect.Right;

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

            // ClusterSize defines the size of cluster to used to check for average. Tweaked to support up to 4k wide pixels and not more. 4096 / 16 is 256 thus the '-1'
            byte clusterSize = (byte)Math.Truncate((width / 16f) - 1);

            Buffer2D <TPixel> sourceBuffer = source.PixelBuffer;

            // Using pooled 2d buffer for integer image table and temp memory to hold Rgb24 converted pixel data.
            using (Buffer2D <ulong> intImage = this.Configuration.MemoryAllocator.Allocate2D <ulong>(width, height))
            {
                Rgba32 rgb = default;
                for (int x = startX; x < endX; x++)
                {
                    ulong sum = 0;
                    for (int y = startY; y < endY; y++)
                    {
                        Span <TPixel> row    = sourceBuffer.DangerousGetRowSpan(y);
                        ref TPixel    rowRef = ref MemoryMarshal.GetReference(row);
                        ref TPixel    color  = ref Unsafe.Add(ref rowRef, x);
                        color.ToRgba32(ref rgb);

                        sum += (ulong)(rgb.R + rgb.G + rgb.B);

                        if (x - startX != 0)
                        {
                            intImage[x - startX, y - startY] = intImage[x - startX - 1, y - startY] + sum;
                        }
                        else
                        {
                            intImage[x - startX, y - startY] = sum;
                        }
                    }
                }
示例#30
0
        /// <summary>
        /// Convert raw spectral DCT data to color data and copy it to the color buffer <see cref="ColorBuffer"/>.
        /// </summary>
        public void CopyBlocksToColorBuffer(int spectralStep)
        {
            Buffer2D <Block8x8> spectralBuffer = this.component.SpectralBlocks;

            float maximumValue = this.frame.MaxColorChannelValue;

            int destAreaStride = this.ColorBuffer.Width;

            int yBlockStart = spectralStep * this.blockRowsPerStep;

            Size subSamplingDivisors = this.component.SubSamplingDivisors;

            Block8x8F dequantTable   = this.rawJpeg.QuantizationTables[this.component.QuantizationTableIndex];
            Block8x8F workspaceBlock = default;

            for (int y = 0; y < this.blockRowsPerStep; y++)
            {
                int yBuffer = y * this.blockAreaSize.Height;

                Span <float>    colorBufferRow = this.ColorBuffer.DangerousGetRowSpan(yBuffer);
                Span <Block8x8> blockRow       = spectralBuffer.DangerousGetRowSpan(yBlockStart + y);

                for (int xBlock = 0; xBlock < spectralBuffer.Width; xBlock++)
                {
                    // Integer to float
                    workspaceBlock.LoadFrom(ref blockRow[xBlock]);

                    // Dequantize
                    workspaceBlock.MultiplyInPlace(ref dequantTable);

                    // Convert from spectral to color
                    FastFloatingPointDCT.TransformIDCT(ref workspaceBlock);

                    // To conform better to libjpeg we actually NEED TO loose precision here.
                    // This is because they store blocks as Int16 between all the operations.
                    // To be "more accurate", we need to emulate this by rounding!
                    workspaceBlock.NormalizeColorsAndRoundInPlace(maximumValue);

                    // Write to color buffer acording to sampling factors
                    int xColorBufferStart = xBlock * this.blockAreaSize.Width;
                    workspaceBlock.ScaledCopyTo(
                        ref colorBufferRow[xColorBufferStart],
                        destAreaStride,
                        subSamplingDivisors.Width,
                        subSamplingDivisors.Height);
                }
            }
        }