0

我正在开发一个将文本放入背景图像的应用程序。文本块作为子项放置在画布控件上,我将画布渲染为 PNG。然后我使用 BlendEffect 将它与背景图像混合。

更新 这篇文章专门介绍了使用 Lumia Imaging SDK 的链接效果。不幸的是,其中一位评论者的视野狭隘,并坚持认为我必须了解有损图像和无损图像之间的区别。在我的上下文中告诉孩子如何在 Microsoft Paint 中保存图像一样有用的意见。在其他一些评论中,他甚至傲慢地希望我的应用程序中一定有大量错误并且有一些心理问题

我是来学习 Lumia Imaging 的,显然遇到了一个对那个 SDK 毫无经验的人,还坚持要炫耀。然后对这个帖子投了反对票,让自己感觉更好。

话虽如此,@Martin Liversage 指出这不仅仅是 JPEG 伪影,这很有帮助。我尝试在另存为 JPEG 的同时使用几个链接步骤和选项,并且确实图像的显示方式有所不同。虽然保存为 PNG 确实提高了质量,但它仍然不是我所期望的。因此,我在这里向任何有使用 SDK 的个人经验的人询问我可以在我的代码中做些什么来改进我的结果。

这是我的代码

private async void saveChainedEffects(StorageFile file)
{
    var displayInformation = DisplayInformation.GetForCurrentView();
    var renderTargetBitmap = new RenderTargetBitmap();
    await renderTargetBitmap.RenderAsync(Textify.CanvasControl);
    var width = renderTargetBitmap.PixelWidth;
    var height = renderTargetBitmap.PixelHeight;
    IBuffer textBuffer = await renderTargetBitmap.GetPixelsAsync();
    byte[] pixels = textBuffer.ToArray();

    using (InMemoryRandomAccessStream memoryRas = new InMemoryRandomAccessStream())
    {
        //Encode foregroundtext to PNG
        var encoder = await BitmapEncoder.CreateAsync(BitmapEncoder.PngEncoderId, memoryRas);

        encoder.SetPixelData(BitmapPixelFormat.Bgra8,
                             BitmapAlphaMode.Straight,
                             (uint)width,
                             (uint)height,
                             displayInformation.LogicalDpi,
                             displayInformation.LogicalDpi,
                             pixels);

        await encoder.FlushAsync();

        IImageProvider effectBackground;

        if (SelectedEffect.Name == "No Effect")
        {
            effectBackground = imageProcessorRenderer.M_Source;
        }
        else
        {
            effectBackground = (SelectedEffect.GetEffectAsync(imageProcessorRenderer.M_Source, new Size(), new Size())).Result;
        }

        StreamImageSource streamForeground = new StreamImageSource(memoryRas.AsStream());

        //Sharpening the text has unintended consequences to I set to 0d
        using (SharpnessEffect sharpnessEffect = new SharpnessEffect(streamForeground, 0d) )
        using (BlendEffect blendEffect = new BlendEffect(effectBackground, sharpnessEffect, BlendFunction.Normal, 1.0f))
        {
            string errorMessage = null;
            Debug.WriteLine("M_SourceSize (Normalized) {0}", imageProcessorRenderer.M_SourceSize);
            Debug.WriteLine("PreviewSize {0}", imageProcessorRenderer.PreviewSize);

            try
            {
                using (var filestream = await file.OpenAsync(FileAccessMode.ReadWrite))
                using (var jpegRenderer = new JpegRenderer(blendEffect) { Size = imageProcessorRenderer.M_SourceSize, Quality = 1.0, RenderOptions = RenderOptions.Mixed })
                {
                    IBuffer jpegBuffer = await jpegRenderer.RenderAsync().AsTask().ConfigureAwait(false);
                    await filestream.WriteAsync(jpegBuffer);
                    await filestream.FlushAsync();
                }
            }
            catch (Exception exception)
            {
                errorMessage = exception.Message;
            }

            if (!string.IsNullOrEmpty(errorMessage))
            {
                var dialog = new MessageDialog(errorMessage);
                await dialog.ShowAsync();
            }
        }
    }
}

这是保存为 JPEG 之前在我的 PC 屏幕上看到的图像

在此处输入图像描述

然后,当图像保存为 JPG 时,质量会明显降低,如下所示。放大图像并注意字体的边缘。

在此处输入图像描述

那么,如果我想尽可能接近原始图像质量,我有哪些选择?

4

1 回答 1

0

根据@Martin Liversage 和@Hans Passant 的反馈,我决定避免使用JPEG 并保存为PNG。字体边缘的伪影大多消失了。我现在做客我只需要使用图像锐化来获得我想要的结果。

更新 我仍然没有得到我想要的质量水平,所以不仅仅是不使用 JPEG,一切都会完美。因此,问题是如何提高 BlendEffect 质量。

这是我的代码

private async void saveChainedEffects(StorageFile file)
{
    var displayInformation = DisplayInformation.GetForCurrentView();
    var renderTargetBitmap = new RenderTargetBitmap();
    await renderTargetBitmap.RenderAsync(Textify.CanvasControl);
    var width = renderTargetBitmap.PixelWidth;
    var height = renderTargetBitmap.PixelHeight;
    IBuffer textBuffer = await renderTargetBitmap.GetPixelsAsync();
    byte[] pixels = textBuffer.ToArray();

    using (InMemoryRandomAccessStream memoryRas = new InMemoryRandomAccessStream())
    {
        //Encode foregroundtext to PNG
        var encoder = await BitmapEncoder.CreateAsync(BitmapEncoder.PngEncoderId, memoryRas);

        encoder.SetPixelData(BitmapPixelFormat.Bgra8,
                             BitmapAlphaMode.Straight,
                             (uint)width,
                             (uint)height,
                             displayInformation.LogicalDpi,
                             displayInformation.LogicalDpi,
                             pixels);

        await encoder.FlushAsync(); 

        IImageProvider effectBackground;

        if (SelectedEffect.Name == "No Effect")
        {
            effectBackground = imageProcessorRenderer.M_Source;
        }
        else
        {
            effectBackground = (SelectedEffect.GetEffectAsync(imageProcessorRenderer.M_Source, new Size(), new Size())).Result;
        }

        StreamImageSource streamForeground = new StreamImageSource(memoryRas.AsStream());

        //Sharpen the text
        //using (SharpnessEffect sharpnessEffect = new SharpnessEffect(streamForeground, 0.3d) )
        using (BlendEffect blendEffect = new BlendEffect(effectBackground, streamForeground, BlendFunction.Normal, 1.0f))
        using (BitmapRenderer bitmapRenderer = new BitmapRenderer(blendEffect, ColorMode.Bgra8888))
        {


            Bitmap bitmap = await bitmapRenderer.RenderAsync();
            byte[] pixelBuffer = bitmap.Buffers[0].Buffer.ToArray();

            using (var stream = new InMemoryRandomAccessStream())
            {
                var pngEncoder = await BitmapEncoder.CreateAsync(BitmapEncoder.PngEncoderId, stream).AsTask().ConfigureAwait(false);

                pngEncoder.SetPixelData(BitmapPixelFormat.Bgra8, 
                    BitmapAlphaMode.Straight, 
                    (uint)bitmap.Dimensions.Width, 
                    (uint)bitmap.Dimensions.Height, 
                    displayInformation.LogicalDpi, 
                    displayInformation.LogicalDpi, 
                    pixelBuffer);

                await pngEncoder.FlushAsync().AsTask().ConfigureAwait(false);

                //Need an IBuffer, here is how to get one:
                using (var memoryStream = new MemoryStream())
                {
                    memoryStream.Capacity = (int)stream.Size;
                    var ibuffer = memoryStream.GetWindowsRuntimeBuffer();
                    await stream.ReadAsync(ibuffer, (uint)stream.Size, InputStreamOptions.None).AsTask().ConfigureAwait(false);
                    string errorMessage = null;

                    try
                    {
                        using (var filestream = await file.OpenAsync(FileAccessMode.ReadWrite))
                        {                                 
                            await filestream.WriteAsync(ibuffer);
                            await filestream.FlushAsync();
                        }
                    }
                    catch (Exception exception)
                    {
                        errorMessage = exception.Message;
                    }

                    if (!string.IsNullOrEmpty(errorMessage))
                    {
                        var dialog = new MessageDialog(errorMessage);
                        await dialog.ShowAsync();
                    }
                }
            }                
        }
    }
}

更新 2

我已经解决了这个问题,并想分享我的发现,以防有人遇到类似情况。事实证明,保存为 JPEG 或 PNG 并不是那么重要。虽然PNG确实改善了最终图像,但您必须注意以下几点

  1. 确保前景和背景图像具有相似的尺寸。默认情况下,Lumia Imaging 会拉伸前景以覆盖背景,并且不会考虑纵横比。

  2. 如果您需要调整前景的大小,请renderTargetBitmap.RenderAsync使用允许您指定大小的重载。

  3. 避免调整前景的大小,SoftwareBitmapRenderer尤其是在混合之前使用透明图像时。对我来说,这影响了我的图像保真度并出于某种原因减少了颜色范围。

这就是预先渲染前景与背景完美融合的方式。

private async Task<SoftwareBitmap> renderForeground()
{
    RenderTargetBitmap renderTargetBitmap = new RenderTargetBitmap();

    await renderTargetBitmap.RenderAsync(gridToRender, (int)imageProcessorRenderer.M_SourceSize.Width, (int)imageProcessorRenderer.M_SourceSize.Height);
    var width = renderTargetBitmap.PixelWidth;
    var height = renderTargetBitmap.PixelHeight;
    IBuffer textBuffer = await renderTargetBitmap.GetPixelsAsync();

    //Get the output into a software bitmap.
     var outputBitmap = new SoftwareBitmap(
         BitmapPixelFormat.Bgra8,
         width,
         height,
         BitmapAlphaMode.Premultiplied);

    outputBitmap.CopyFromBuffer(textBuffer);

    return outputBitmap;
}
于 2016-09-08T08:29:15.177 回答