×
Namespaces

Variants
Actions

如何使用Nokia Imaging SDK的卡通和裁剪滤镜

From Nokia Developer Wiki
Jump to: navigation, search

本篇文章讲解如何使用Nokia Imaging SDK创建图片的卡通和裁剪滤镜,以及使用WriteableBitmapEx拼接图片。

WP Metro Icon Multimedia.png
SignpostIcon XAML 40.png
WP Metro Icon WP8.png
Article Metadata

代码示例
测试基于
SDK: Windows Phone 8.0 SDK
设备:: Nokia Lumia 920

兼容于
Dependencies: Nokia Imaging SDK

文章
翻译:
highcedar
最后由 hamishwillee 在 02 Sep 2013 编辑

Contents

Introduction

使用Nokia Imaging SDK可高效的创建令人惊奇的图片滤镜效果。将自己的肖像照片增加卡通的滤镜效果是很有趣的事,至少我喜欢这么做。本文提供使用FilterFactory.CreateCartoonFilter的方法可将图像转换为卡通样式,本文也介绍如何在应用中使用FilterFactory.CreateCropFilter方法裁剪图片,文章的最后一个内容就是创建如下面右侧图片的宽图块的磁贴图像。下面的宽图块的图片其实由五张图片拼接而成。 本篇文档的英文版Using Nokia Imaging SDK Create Cartoon Photo参加了Nokia Imaging Wiki Competition 2013Q3

Pre-requisites

安装Nokia Imaging SDK请参考以下的文档:

使用 Nokia Imaging SDK 请特别注意编译的条件:ARM编译模式可使程序在手机中运行,X86的编译模式可使程序在模拟器中运行。 因此“All CPU”的编译配置必需在配置管理器中删除,只保留“ARM”和“X86”。打开配置管理器的方法:BUILD – Configuration Manager…

在配置管理器中,打开“Active Solution platform:”列表,选择“<Edit…>”。在解决方案平台的窗体中,选中“Any CPU”然后将其删除,最后确保“ARM”和“X86”仍在列表中。

安装WriteableBitmapEx库

启动 NuGet 库管理:Tools 菜单 > Library Package Manager > Manage NuGet Packages for Solution...

在搜索栏中键入“WriteableBitmap”并搜索,则WriteableBitmapEX的SDK会在搜索结果中显示,点击安装按钮即可。

"Cartoon Me" 应用

“Cartoon Me”应用是如何使用Nokia Imaging SDK的示例,在此示例中使用拍照取景器获取照片,然后使用Imaging SDK为照片增加卡通的滤镜效果,并可将滤镜处理的图片保存至相机的camera roll相册;最近浏览的列表则保存您在应用中最近处理的原始图片。

Cartoon Me 应用还是用 Imaging SDK 的 FilterFactory.CreateCropFilter 方法裁剪图片,并将裁剪后的图片与另外四个图片拼接为宽图块的图像,最后生成动态磁贴显示在手机的开始屏幕中。

捕获照片

在 MainPage 类中捕获照片,使用 UIFramework 库实现增加照片的 UI 。当点击增加按钮,则执行以下的代码。如果 PhotoChooserTask 返回为OK,则应用加图片流,并且应用导航至EditPage。

private void AddButton_Click(object sender, RoutedEventArgs e)
{
if (isChoosing)
{
return;
}
 
isChoosing = true;
 
PhotoChooserTask photoChoose = new PhotoChooserTask();
photoChoose.ShowCamera = true;
photoChoose.Completed += (s, res) =>
{
isChoosing = false;
 
if (res.TaskResult == TaskResult.Cancel && res.Error is InvalidOperationException)
{
MessageBox.Show(CartoonMe.Resources.AppResources.message_duplicate_add);
return;
}
 
if (res.TaskResult == TaskResult.OK)
{
chosenPhoto = res.ChosenPhoto;
 
if (null != res.ChosenPhoto)
{
DataContext dataContext = CartoonMe.DataContext.Singleton;
 
// Reset the streams
dataContext.CreateStreams();
 
// Use the largest possible dimensions
WriteableBitmap bitmap = new WriteableBitmap(3552, 2448);
try
{
// Jpeg images can be used as such.
bitmap.LoadJpeg(chosenPhoto);
chosenPhoto.Position = 0;
chosenPhoto.CopyTo(dataContext.ImageStream);
}
catch (Exception /*ex*/)
{
// Image format is not jpeg. Can be anything, so first
// load it into a bitmap image and then write as jpeg.
BitmapImage image = new BitmapImage();
image.SetSource(chosenPhoto);
 
bitmap = new WriteableBitmap(image);
bitmap.SaveJpeg(dataContext.ImageStream, image.PixelWidth, image.PixelHeight, 0, 100);
}
}
 
// Since PhotoChooserTask is not completed yet to front, NavigationService.Navigate is not allowed here.
// This is a workaround by one time navigation event subscribed, and deplayed the navigation to another page.
NavigationService.Navigated += new NavigatedEventHandler(NavigationService_Navigated);
}
};
photoChoose.Show();
}

请注意应声明程序具有 ID_CAP_ISV_CAMERA 的能力。

卡通滤镜处理图片

在 EditPage 类中创建卡通图像:

/// <summary>
/// Takes the captured image and applies filters to it.
/// </summary>
private async void CreateCartoonImage()
{
DataContext dataContext = CartoonMe.DataContext.Singleton;
 
if (dataContext.ThumbStream == null)
{
// No captured image available!
NavigationService.GoBack();
return;
}
 
// Create a session that we will use with all the filters
IBuffer buffer = dataContext.ImageStream.GetWindowsRuntimeBuffer();
EditingSession session = new EditingSession(buffer);
 
// Perform the filtering
using (session)
{
foreach (AbstractFilter filter in _filters)
{
filter.SetBuffer(buffer);
filter.DefineFilter(session);
 
await filter.RenderToBitmapAsync(session);
//session.UndoAll();
}
}
}

卡通滤镜的实现:在 CartoonFilter.cs 中实现虚函数 DefineFilter()。

public override void DefineFilter(EditingSession session)
{
session.AddFilter(FilterFactory.CreateCartoonFilter(_distinctEdges));
}

RenderToBitmapAsync()在CartoonFilter 的父类 AbstractFilter 中实现(AbstractFilter.cs):

public async Task RenderToBitmapAsync(EditingSession session)
{
await session.RenderToBitmapAsync(_filteredBitmap, OutputOption.PreserveAspectRatio);
_previewBitmap.Invalidate(); // Force a redraw
}

保存滤镜处理后的图片至媒体库:

private async void OnSaveButtonClicked(object sender, RoutedEventArgs e)
{
_progressIndicator.Text = AppResources.SavingText;
_progressIndicator.IsVisible = true;
SystemTray.SetProgressIndicator(this, _progressIndicator);
 
MediaLibrary library = new MediaLibrary();
 
DataContext dataContext = CartoonMe.DataContext.Singleton;
 
try
{
AbstractFilter filter = _filters[0]; // cartoon filter
EditingSession session = new EditingSession(dataContext.ImageStream.GetWindowsRuntimeBuffer());
 
using (session)
{
filter.DefineFilter(session);
IBuffer data = await session.RenderToJpegAsync();
 
// Save the rendered image into a file
library.SavePictureToCameraRoll(FileNamePrefix
+ DateTime.UtcNow.ToString(Constant.DateTimeFormat) + Constant.ImageFileSuffix, data.AsStream());
 
}
 
}
catch (Exception ex)
{
Debug.WriteLine("EditPage::SaveButton_Click(): Failed to save the image to camera roll: "
+ ex.ToString());
}
 
_progressIndicator.IsVisible = false;
SystemTray.SetProgressIndicator(this, _progressIndicator);
 
NavigationService.GoBack();
}

请注意应声明程序具有 ID_CAP_MEDIALIB_PHOTO 的能力。

裁剪滤镜

我们将使用 FilterFactory.CreateCropFilter 方法裁剪图片,裁剪后的图片将作为动态磁贴的背景图片。 为了实现裁剪的滤镜效果,在 AbstractFilter 类中增加 CreateCropImage 方法,在EditPage中裁剪图片:

DataContext dataContext = CartoonMe.DataContext.Singleton;
// Create a session that we will use with all the filters
IBuffer buffer = dataContext.ImageStream.GetWindowsRuntimeBuffer();
EditingSession session = new EditingSession(buffer);
 
// Perform the filtering
using (session)
{
foreach (AbstractFilter filter in _filters)
{
filter.SetBuffer(buffer);
filter.CropFilter(session);
 
await filter.CropRenderToBitmapAsync(session);
session.UndoAll();
}
}

在 AbstractFilter 类中实现 CreateCropImage 方法:

public abstract void CropFilter(EditingSession session);
/// <summary>
/// Creates a filtered crip image from the given buffer.
/// </summary>
public async void CreateCropImage()
{
if (_buffer == null)
{
return;
}
 
EditingSession session = new EditingSession(_buffer);
 
// Perform the filtering
using (session)
{
CropFilter(session);
await CropRenderToBitmapAsync(session);
}
}
 
/// <summary>
/// Creates the preview buffers or resize them if needed.
/// </summary>
/// <param name="session">The session initialized with the desired jpeg.</param>
public async Task CropRenderToBitmapAsync(EditingSession session)
{
await session.RenderToBitmapAsync(_cropFilteredBitmap, OutputOption.PreserveAspectRatio);
_cropBitmap.Invalidate(); // Force a redraw
}

在 AbstractFilter 类中输出裁剪滤镜的图片流:

private WriteableBitmap _cropBitmap;
 
private Bitmap _cropFilteredBitmap;
 
/// <summary>
/// Creates the PreviewBuffers or resize them if needed.
/// </summary>
/// <param name="session">The session initialized with the desired jpeg.</param>
public void SetOutputResolution(int width, int height)
{
......
 
_cropBitmap = new WriteableBitmap(355, 355);
_cropFilteredBitmap = _cropBitmap.AsBitmap();
CropImage.Source = _cropBitmap; // Force a redraw
}

在 CartoonFilter 类中冲在虚函数 CropFilter ,设置裁剪图片的矩形区域。

public override void CropFilter(EditingSession session)
{
Windows.Foundation.Rect cropArea = new Windows.Foundation.Rect(0, 0, 355, 355);
session.AddFilter(FilterFactory.CreateCropFilter(cropArea));
}

拼接图片

曾在一个项目中遇到过拼接图片是由服务器端完成,还是有Windows Phone客户端完成的问题。抛开这个问题不谈,我很感兴趣于在Windows Phone中实现拼接图片的方法,最终图片拼接完成的效果如下图所示。您也在此方法之上将其改进,应用于您自己的项目。书的目标为读者提供有用内容,Wiki也是这样。

WriteableBitmapEx 是 WriteableBitmap 的扩展,这里所讲的是其组合图片的功能。 691X336的宽图块的图片尺寸和拼接点坐标如下图所示,左侧为一张大图,右侧由四张小图组成。如果微软以后更新图块的尺寸和样式,那么这个图片拼接的处理方法也要相应改变。

在 EditPage 类中我们使用 Blit 方法组成图片:

WriteableBitmap wbmp = new WriteableBitmap(_filters[0].CropImage.Source as BitmapSource);
 
using (var stream = store.OpenFile(backgoundImagePath, System.IO.FileMode.OpenOrCreate))
{
System.Windows.Media.Imaging.Extensions.SaveJpeg(wbmp, stream, MediumTilePixelWidth, MediumTilePixelHeight, 0, 100);
//wbmp.SaveJpeg(stream, bitmap.PixelWidth, bitmap.PixelHeight, 0, 100);
}
 
WriteableBitmap wideBitmap = new WriteableBitmap(WideTilePixelWidth, WideTilePixelHeight);
 
System.IO.Stream stream1 = Application.GetResourceStream(new Uri(@"Assets/Images/01_imaging_sdk_text.png", UriKind.Relative)).Stream;
BitmapImage image1 = new BitmapImage();
image1.SetSource(stream1);
 
System.IO.Stream stream2 = Application.GetResourceStream(new Uri(@"Assets/Images/02_imaging_sdk_icon.png", UriKind.Relative)).Stream;
BitmapImage image2 = new BitmapImage();
image2.SetSource(stream2);
 
System.IO.Stream stream3 = Application.GetResourceStream(new Uri(@"Assets/Images/03_nokia_future_icon.png", UriKind.Relative)).Stream;
BitmapImage image3 = new BitmapImage();
image3.SetSource(stream3);
 
System.IO.Stream stream4 = Application.GetResourceStream(new Uri(@"Assets/Images/04_nokia_future_text.png", UriKind.Relative)).Stream;
BitmapImage image4 = new BitmapImage();
image4.SetSource(stream4);
 
int leftImageWidth = (wbmp.PixelWidth > 355) ? 355 : wbmp.PixelWidth;
int leftImageHeight = (wbmp.PixelHeight > 336) ? 336 : wbmp.PixelHeight;
 
wideBitmap.Blit(new Rect(0, 0, 355, 336), new WriteableBitmap(wbmp), new Rect(0, 0, wbmp.PixelWidth, wbmp.PixelHeight), WriteableBitmapExtensions.BlendMode.Additive);
wideBitmap.Blit(new Rect(355, 0, 168, 168), new WriteableBitmap(image1), new Rect(0, 0, image1.PixelWidth, image1.PixelHeight), WriteableBitmapExtensions.BlendMode.Additive);
wideBitmap.Blit(new Rect(523, 0, 168, 168), new WriteableBitmap(image2), new Rect(0, 0, image4.PixelWidth, image4.PixelHeight), WriteableBitmapExtensions.BlendMode.Additive);
wideBitmap.Blit(new Rect(355, 168, 168, 168), new WriteableBitmap(image3), new Rect(0, 0, image3.PixelWidth, image3.PixelHeight), WriteableBitmapExtensions.BlendMode.Additive);
wideBitmap.Blit(new Rect(523, 168, 168, 168), new WriteableBitmap(image4), new Rect(0, 0, image4.PixelWidth, image4.PixelHeight), WriteableBitmapExtensions.BlendMode.Additive);

参考

This page was last modified on 2 September 2013, at 08:25.
175 page views in the last 30 days.
×