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)); }
//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); }
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); }
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; }
/// <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); }
/// <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); }
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); }
public DefaultStatistics GetStatisticsFor(ITexture texture, LayerMipmapRange lm) { return(new DefaultStatistics(this, texture, lm)); }
public Stats GetStats(ITexture image1, ITexture image2, LayerMipmapRange lmRange) => GetStats(image1, image2, lmRange, new Settings());
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); }
internal void CopyToBuffer(ITexture source, GpuBuffer buffer, LayerMipmapRange lm) => CopyToBuffer(source, buffer, lm, Size3.Zero);
/// <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); }