예제 #1
0
        private Task ExportTexture(ITexture texture, ExportDescription desc, LayerMipmapRange lm, CancellationToken ct)
        {
            Debug.Assert(desc.StagingFormat.DxgiFormat == texture.Format);

            int nMipmaps = lm.IsSingleMipmap ? 1 : texture.NumMipmaps;
            int nLayer   = lm.IsSingleLayer ? 1 : texture.NumLayers;

            var img = IO.CreateImage(desc.StagingFormat, texture.Size.GetMip(lm.FirstMipmap), new LayerMipmapCount(nLayer, nMipmaps));

            // fill with data
            foreach (var dstLm in img.LayerMipmap.Range)
            {
                var mip = img.GetMipmap(dstLm);
                // transfer image data
                texture.CopyPixels(new LayerMipmapSlice(lm.FirstLayer + dstLm.Layer, lm.FirstMipmap + dstLm.Mipmap), mip.Bytes, mip.ByteSize);
            }

            return(Task.Run(() =>
            {
                using (img)
                {
                    IO.SaveImage(img, desc.Filename, desc.Extension, desc.FileFormat, desc.Quality);
                }
            }, ct));
        }
예제 #2
0
        //public bool HasAlpha => !(Min.Alpha == 1.0f && Max.Alpha == 1.0f);

        internal float GetStats(ITexture texture, LayerMipmapRange lm, StatisticsShader statShader, ReduceShader redShader, bool normalize)
        {
            Debug.Assert(lm.IsSingleMipmap);

            // obtain a buffer that is big enough
            int numElements = texture.Size.GetMip(lm.FirstMipmap).Product;

            if (lm.AllLayer)
            {
                numElements *= texture.LayerMipmap.Layers;
            }

            // allocate buffer that is big enough
            GetBuffer(numElements);

            // copy all values into buffer
            statShader.CopyToBuffer(texture, buffer, lm);

            // execute reduce
            redShader.Run(buffer, numElements);

            shared.Download.CopyFrom(buffer, sizeof(float));

            var res = shared.Download.GetData <float>();

            if (normalize)
            {
                res /= numElements;
            }
            return(res);
        }
예제 #3
0
 internal DefaultStatistics(StatisticsModel parent, ITexture texture, LayerMipmapRange lm)
 {
     Luminance = new DefaultStatisticsType(parent.LuminanceShader, texture, parent, lm);
     Average   = new DefaultStatisticsType(parent.UniformShader, texture, parent, lm);
     Luma      = new DefaultStatisticsType(parent.LumaShader, texture, parent, lm);
     Lightness = new DefaultStatisticsType(parent.LightnessShader, texture, parent, lm);
     Alpha     = new DefaultStatisticsType(parent.AlphaShader, texture, parent, lm);
 }
예제 #4
0
 internal DefaultStatisticsType(StatisticsShader shader, ITexture texture, StatisticsModel parent, LayerMipmapRange lm)
 {
     this.shader  = shader;
     this.texture = texture;
     this.parent  = parent;
     this.lm      = lm;
     minValue     = null;
     maxValue     = null;
     avgValue     = null;
 }
예제 #5
0
        /// <summary>
        /// puts statistic data of all pixels into the buffer
        /// </summary>
        /// <param name="lm">range with single mipmap</param>
        /// <param name="offset">offset in each direction</param>
        /// <param name="source"></param>
        /// <param name="buffer"></param>
        internal void CopyToBuffer(ITexture source, GpuBuffer buffer, LayerMipmapRange lm, Size3 offset)
        {
            Debug.Assert(lm.IsSingleMipmap);

            // copy pixels from the source image into a texture from the texture cache
            var dev = Device.Get();

            if (source.Is3D)
            {
                dev.Compute.Set(shader3d.Compute);
            }
            else
            {
                dev.Compute.Set(shader.Compute);
            }

            var dim = source.Size.GetMip(lm.Mipmap);

            AdjustDim(ref dim, ref offset, source.Is3D);

            var numLayers = source.LayerMipmap.Layers;
            var curData   = new BufferData
            {
                Level    = lm.Mipmap,
                TrueBool = true,
                Offset   = offset,
                Size     = dim
            };

            if (lm.AllLayer)
            {
                dev.Compute.SetShaderResource(0, source.View);
            }
            else
            {
                // single layer
                dev.Compute.SetShaderResource(0, source.GetSrView(lm.Single));
                curData.Level = 0; // view with single level
                numLayers     = 1;
            }
            cbuffer.SetData(curData);

            // buffer big enough?
            Debug.Assert(buffer.ElementCount >= dim.Product * numLayers);

            dev.Compute.SetUnorderedAccessView(0, buffer.View);
            dev.Compute.SetConstantBuffer(0, cbuffer.Handle);

            dev.Dispatch(Utility.Utility.DivideRoundUp(dim.Width, LocalSizeX),
                         Utility.Utility.DivideRoundUp(dim.Height, LocalSizeY),
                         Math.Max(dim.Depth, numLayers));

            dev.Compute.SetUnorderedAccessView(0, null);
            dev.Compute.SetShaderResource(0, null);
        }
예제 #6
0
        /// <summary>
        /// calculates the number of buffer elements required with the current configuation
        /// </summary>
        public int GetRequiredElementCount(ITexture tex, LayerMipmapRange lm, Size3 offset)
        {
            var dim = tex.Size.GetMip(lm.SingleMipmap);

            AdjustDim(ref dim, ref offset, tex.Is3D);
            var res = dim.Product;

            if (lm.AllLayer)
            {
                res *= tex.NumLayers;
            }
            return(res);
        }
예제 #7
0
        private float GetAveragedValue(ITexture tex, LayerMipmapRange lm, bool noBorders)
        {
            var offset = noBorders ? new Size3(5) : Size3.Zero; // don't get values from blur borders

            // obtain gpu buffer that is big enough to hold all elements
            var numElements = copyToBufferShader.GetRequiredElementCount(tex, lm, offset);

            var buffer = models.Stats.GetBuffer(numElements);
            var reduce = models.Stats.AvgReduce;

            // copy values into buffer for scan
            copyToBufferShader.CopyToBuffer(tex, buffer, lm, offset);
            reduce.Run(buffer, numElements);
            models.SharedModel.Download.CopyFrom(buffer, sizeof(float));
            return(models.SharedModel.Download.GetData <float>() / numElements);
        }
예제 #8
0
 public DefaultStatistics GetStatisticsFor(ITexture texture, LayerMipmapRange lm)
 {
     return(new DefaultStatistics(this, texture, lm));
 }
예제 #9
0
 public Stats GetStats(ITexture image1, ITexture image2, LayerMipmapRange lmRange)
 => GetStats(image1, image2, lmRange, new Settings());
예제 #10
0
        public Stats GetStats(ITexture image1, ITexture image2, LayerMipmapRange lmRange, Settings s)
        {
            Debug.Assert(image1.HasSameDimensions(image2));
            Debug.Assert(lmRange.IsSingleMipmap);
            var cache = GetCache(image1);

            var lumTex   = cache.GetTexture();
            var contTex  = cache.GetTexture();
            var strucTex = cache.GetTexture();
            var ssimTex  = cache.GetTexture();

            using (var data = new ImagesCorrelationStats(cache))
            {
                if (!s.Multiscale)
                {
                    foreach (var lm in image1.LayerMipmap.RangeOf(lmRange))
                    {
                        // determine expected value, variance, correlation
                        RenderImagesCorrelation(image1, image2, data, lm);

                        // calc the three components
                        RenderLuminance(data, lumTex, lm);
                        RenderContrast(data, contTex, lm);
                        RenderStructure(data, strucTex, lm);

                        // build ssim
                        //RenderSSIM(data, ssimTex, lm);
                        RenderSSIM(lumTex, strucTex, contTex, ssimTex, lm);
                    }
                }
                else // multiscale
                {
                    int endMipmap = Math.Min(lmRange.Mipmap + 5, image1.NumMipmaps);
                    for (int curMip = lmRange.Mipmap; curMip < endMipmap; ++curMip)
                    {
                        foreach (var lm in image1.LayerMipmap.RangeOf(new LayerMipmapRange(lmRange.Layer, curMip)))
                        {
                            // determine expected value, variance, correlation
                            RenderImagesCorrelation(image1, image2, data, lm);

                            // calc components
                            if (curMip == endMipmap - 1) // luminance only for last mipmap
                            {
                                RenderLuminance(data, lumTex, lm);
                            }
                            RenderContrast(data, contTex, lm);
                            RenderStructure(data, strucTex, lm);
                        }
                    }

                    // combine values of different scales to compute ssim
                    foreach (var lm in image1.LayerMipmap.RangeOf(lmRange))
                    {
                        // determine appropriate scale scores
                        RenderLuminanceMultiscale(lumTex, lm);
                        RenderContrastStructureMultiscale(contTex, lm);
                        RenderContrastStructureMultiscale(strucTex, lm);

                        // build ssim
                        RenderSSIM(lumTex, strucTex, contTex, ssimTex, lm);
                    }
                }
            }

            var stats = new Stats
            {
                Luminance = GetAveragedValue(lumTex, lmRange, s.ExcludeBorders),
                Contrast  = GetAveragedValue(contTex, lmRange, s.ExcludeBorders),
                Structure = GetAveragedValue(strucTex, lmRange, s.ExcludeBorders),
                SSIM      = GetAveragedValue(ssimTex, lmRange, s.ExcludeBorders)
            };

            cache.StoreTexture(lumTex);
            cache.StoreTexture(contTex);
            cache.StoreTexture(strucTex);
            cache.StoreTexture(ssimTex);

            return(stats);
        }
예제 #11
0
 internal void CopyToBuffer(ITexture source, GpuBuffer buffer, LayerMipmapRange lm)
 => CopyToBuffer(source, buffer, lm, Size3.Zero);
예제 #12
0
        /// <summary>
        /// converts the texture into another format and performs cropping if requested
        /// </summary>
        /// <param name="texture">source texture</param>
        /// <param name="dstFormat">destination format</param>
        /// <param name="srcLm">layer/mipmap to export</param>
        /// <param name="multiplier">rgb channels will be multiplied by this value</param>
        /// <param name="crop">indicates if the image should be cropped, only works with 1 mipmap to export</param>
        /// <param name="offset">if crop: offset in source image</param>
        /// <param name="size">if crop: size of the destination image</param>
        /// <param name="align">if nonzero: texture width will be aligned to this (rounded down)</param>
        /// <param name="scaling">required for regenerating mipmaps after cropping.</param>
        /// <param name="overlay">overlay</param>
        /// <param name="scale">scales the destination image by this factor</param>
        /// <returns></returns>
        public ITexture Convert(ITexture texture, SharpDX.DXGI.Format dstFormat, LayerMipmapRange srcLm, float multiplier, bool crop,
                                Size3 offset, Size3 size, Size3 align, ScalingModel scaling, ITexture overlay = null, int scale = 1)
        {
            Debug.Assert(ImageFormat.IsSupported(dstFormat));
            Debug.Assert(ImageFormat.IsSupported(texture.Format));
            Debug.Assert(overlay == null || texture.HasSameDimensions(overlay));
            Debug.Assert(scale >= 1);

            // set width, height mipmap
            int nMipmaps = srcLm.IsSingleMipmap ? 1: texture.NumMipmaps;
            int nLayer   = srcLm.IsSingleLayer ? 1 : texture.NumLayers;

            // set correct width, height, offsets
            if (!crop)
            {
                size   = texture.Size.GetMip(srcLm.FirstMipmap);
                offset = Size3.Zero;
            }

            if (scale != 1)
            {
                size.X *= scale;
                size.Y *= scale;
                if (texture.Is3D)
                {
                    size.Z *= scale;
                }
                offset.X *= scale;
                offset.Y *= scale;
                if (texture.Is3D)
                {
                    offset.Z *= scale;
                }
            }

            // adjust alignments
            for (int i = 0; i < 3; ++i)
            {
                if (align[i] != 0)
                {
                    if (size[i] % align[i] != 0)
                    {
                        if (size[i] < align[i])
                        {
                            throw new Exception($"image needs to be aligned to {align[i]} but one axis is only {size[i]}. Axis should be at least {align[i]}");
                        }

                        crop = true;
                        var remainder = size[i] % align[i];
                        offset[i] = offset[i] + remainder / 2;
                        size[i]   = size[i] - remainder;
                    }
                }
            }

            bool recomputeMips = nMipmaps > 1 && (crop || scale != 1);

            if (recomputeMips)
            {
                // number of mipmaps might have changed
                nMipmaps      = size.MaxMipLevels;
                recomputeMips = nMipmaps > 1;
            }

            var res = texture.Create(new LayerMipmapCount(nLayer, nMipmaps), size, dstFormat, false);

            var dev = DirectX.Device.Get();

            quad.Bind(texture.Is3D);
            if (texture.Is3D)
            {
                dev.Pixel.Set(convert3D.Pixel);
            }
            else
            {
                dev.Pixel.Set(convert2D.Pixel);
            }

            dev.Pixel.SetShaderResource(0, texture.View);
            if (overlay != null)
            {
                dev.Pixel.SetShaderResource(1, overlay.View);
            }
            else
            {
                dev.Pixel.SetShaderResource(1, null);
            }

            foreach (var dstLm in res.LayerMipmap.Range)
            {
                cbuffer.SetData(new LayerLevelOffsetData
                {
                    Layer      = dstLm.Layer + srcLm.FirstLayer + offset.Z,
                    Level      = dstLm.Mipmap + srcLm.FirstMipmap,
                    Xoffset    = offset.X,
                    Yoffset    = offset.Y,
                    Multiplier = multiplier,
                    UseOverlay = overlay != null ? 1 : 0,
                    Scale      = scale
                });

                var dim = res.Size.GetMip(dstLm.Mipmap);
                dev.Pixel.SetConstantBuffer(0, cbuffer.Handle);
                dev.OutputMerger.SetRenderTargets(res.GetRtView(dstLm));
                dev.SetViewScissors(dim.Width, dim.Height);
                dev.DrawFullscreenTriangle(dim.Depth);

                if (recomputeMips && dstLm.Mipmap > 0)
                {
                    break;                                    // only write most detailed mipmap
                }
            }

            // remove bindings
            dev.Pixel.SetShaderResource(0, null);
            dev.Pixel.SetShaderResource(1, null);
            dev.OutputMerger.SetRenderTargets((RenderTargetView)null);
            quad.Unbind();

            if (recomputeMips)
            {
                scaling.WriteMipmaps(res);
            }

            return(res);
        }