如何提高 BlendEffect 的质量? Lumia 成像 3.0 和 UWP
How to improve BlendEffect quality? Lumia Imaging 3.0 & UWP
我正在开发一个将文本放入背景图像的应用程序。文本块作为 children 放置在 canvas 控件上,我将 canvas 渲染为 PNG。然后我使用 BlendEffect 将它与背景图像混合。
更新
此 post 专门针对 Lumia Imaging SDK 的链接效果。不幸的是,其中一位发表评论的人目光短浅,坚持认为我必须了解有损图像和无损图像之间的区别。在我的上下文中,这种意见就像告诉孩子如何在 Microsoft Paint 中保存图像一样有用。在其他评论中,he even arrogantly wish that I must be having tons of bugs in my app and am having some mental problem.
我是来学习Lumia Imaging的,明明遇到了一个没有那个SDK经验的人非要炫耀。然后给这个投了反对票post让自己心情好一点。
话虽如此,@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 之前在我的电脑屏幕上看到的图像
然后,当图像保存为 JPG 时,质量明显下降,如下所示。放大图片,注意字体边缘。
如果我想尽可能接近原始图像质量,我有哪些选择?
根据@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 确实改善了最终图像,但您必须注意以下几点
确保前景和背景图像具有相似的尺寸。默认情况下,Lumia Imaging 会拉伸前景以覆盖背景,并且不会考虑纵横比。
如果您需要调整前景大小,请在 renderTargetBitmap.RenderAsync
中使用允许您指定大小的重载。
避免使用 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;
}
我正在开发一个将文本放入背景图像的应用程序。文本块作为 children 放置在 canvas 控件上,我将 canvas 渲染为 PNG。然后我使用 BlendEffect 将它与背景图像混合。
更新 此 post 专门针对 Lumia Imaging SDK 的链接效果。不幸的是,其中一位发表评论的人目光短浅,坚持认为我必须了解有损图像和无损图像之间的区别。在我的上下文中,这种意见就像告诉孩子如何在 Microsoft Paint 中保存图像一样有用。在其他评论中,he even arrogantly wish that I must be having tons of bugs in my app and am having some mental problem.
我是来学习Lumia Imaging的,明明遇到了一个没有那个SDK经验的人非要炫耀。然后给这个投了反对票post让自己心情好一点。
话虽如此,@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 之前在我的电脑屏幕上看到的图像
然后,当图像保存为 JPG 时,质量明显下降,如下所示。放大图片,注意字体边缘。
如果我想尽可能接近原始图像质量,我有哪些选择?
根据@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 确实改善了最终图像,但您必须注意以下几点
确保前景和背景图像具有相似的尺寸。默认情况下,Lumia Imaging 会拉伸前景以覆盖背景,并且不会考虑纵横比。
如果您需要调整前景大小,请在
renderTargetBitmap.RenderAsync
中使用允许您指定大小的重载。避免使用
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;
}