如何将UWP InkCanvas的分辨率降低至28x28像素?
UWP InkCanvas手写数字转为28x28像素位图解决方案
问题背景
我正在开发手写数字识别的神经网络程序,需要在UWP的InkCanvas上捕获手写数字。目前将InkCanvas保存为Bitmap时分辨率远大于28x28,但神经网络输入层是784个节点(对应28x28像素),原分辨率会导致输入节点数超标。
现在我通过MS Paint在28x28画布绘制数字并保存为BMP供程序处理,需要实现将UWP InkCanvas的输出直接转为实际像素数为28x28的位图(不是只修改宽高参数)。
解决方案
要实现目标,核心是对InkCanvas渲染后的图像进行高质量缩放,步骤如下:
- 先将InkCanvas渲染到
RenderTargetBitmap获取原始像素数据; - 通过
BitmapDecoder解码原始图像,再用BitmapEncoder结合BitmapTransform将图像缩放到28x28; - 使用高质量插值模式保证缩放后笔迹的清晰度,避免变形。
修改后的完整代码
public sealed partial class MainPage : Page { public MainPage() { this.InitializeComponent(); // 设置初始窗口大小 ApplicationView.PreferredLaunchViewSize = new Windows.Foundation.Size(1000, 800); ApplicationView.PreferredLaunchWindowingMode = ApplicationViewWindowingMode.PreferredLaunchViewSize; // 设置支持的输入设备类型 inkCanvas.InkPresenter.InputDeviceTypes = Windows.UI.Core.CoreInputDeviceTypes.Mouse; // 设置初始笔迹属性 InkDrawingAttributes drawingAttributes = new InkDrawingAttributes(); drawingAttributes.Color = Windows.UI.Colors.White; drawingAttributes.Size = new Windows.Foundation.Size(2, 2); drawingAttributes.IgnorePressure = false; drawingAttributes.FitToCurve = true; inkCanvas.InkPresenter.UpdateDefaultDrawingAttributes(drawingAttributes); } private async void saveInkCanvasBitmap() { RenderTargetBitmap bitmap = new RenderTargetBitmap(); await bitmap.RenderAsync(grid); // 将原始渲染像素写入内存流,用于解码 var pixelBuffer = await bitmap.GetPixelsAsync(); using (var inputStream = new InMemoryRandomAccessStream()) { var encoder = await BitmapEncoder.CreateAsync(BitmapEncoder.BmpEncoderId, inputStream); encoder.SetPixelData( BitmapPixelFormat.Bgra8, BitmapAlphaMode.Ignore, (uint)bitmap.PixelWidth, (uint)bitmap.PixelHeight, DisplayInformation.GetForCurrentView().RawDpiX, DisplayInformation.GetForCurrentView().RawDpiY, pixelBuffer.ToArray()); await encoder.FlushAsync(); inputStream.Seek(0); // 解码图像并执行缩放操作 var decoder = await BitmapDecoder.CreateAsync(inputStream); using (var outputStream = new InMemoryRandomAccessStream()) { var scaledEncoder = await BitmapEncoder.CreateAsync(BitmapEncoder.BmpEncoderId, outputStream); // 设置目标尺寸为28x28 scaledEncoder.BitmapTransform.ScaledWidth = 28; scaledEncoder.BitmapTransform.ScaledHeight = 28; // 使用高质量三次插值保证笔迹清晰 scaledEncoder.BitmapTransform.InterpolationMode = BitmapInterpolationMode.HighQualityCubic; scaledEncoder.IsThumbnailGenerated = false; // 获取缩放后的像素数据并写入编码器 var pixelData = await decoder.GetPixelDataAsync( BitmapPixelFormat.Bgra8, BitmapAlphaMode.Ignore, scaledEncoder.BitmapTransform, ExifOrientationMode.IgnoreExifOrientation, ColorManagementMode.DoNotColorManage); scaledEncoder.SetPixelData( BitmapPixelFormat.Bgra8, BitmapAlphaMode.Ignore, 28, 28, decoder.DpiX, decoder.DpiY, pixelData.DetachPixelData()); await scaledEncoder.FlushAsync(); outputStream.Seek(0); // 保存28x28的图像到文件 StorageFolder pictureFolder = KnownFolders.SavedPictures; var file = await pictureFolder.CreateFileAsync("image.bmp", CreationCollisionOption.ReplaceExisting); using (var fileStream = await file.OpenAsync(FileAccessMode.ReadWrite)) { await RandomAccessStream.CopyAsync(outputStream, fileStream); } } } } private void clearButton_Click(object sender, RoutedEventArgs e) { // 清空InkCanvas所有笔迹 inkCanvas.InkPresenter.StrokeContainer.Clear(); } private void submitButton_Click(object sender, RoutedEventArgs e) { saveInkCanvasBitmap(); // 生成28x28分辨率图像 // 后续神经网络处理逻辑 } }
关键说明
- 缩放时采用
HighQualityCubic插值模式,能最大程度保留手写笔迹的细节,避免缩放后模糊或变形; - 整个流程基于UWP原生的
BitmapEncoder和BitmapDecoder实现,无需第三方库,性能稳定; - 最终保存的文件是实际像素为28x28的BMP,直接匹配神经网络784节点的输入要求。
内容的提问来源于stack exchange,提问作者Cason
相关产品推荐
相关产品推荐

