×
Namespaces

Variants
Actions

(译)Nokia Imaging SDK — 获取和保存照片

From Nokia Developer Wiki
Jump to: navigation, search

本文为翻译内容,原文链接在底部

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

测试基于
SDK: Windows Phone 8.0 SDK, Nokia Imaging SDK Beta 1

兼容于
文章
段博琼 在 11 Oct 2013 创建
最后由 hamishwillee 在 17 Oct 2013 编辑

Contents

获取和保存照片

有一些处理高分辨率图片的应用程序会遇到的问题。例如,由于应用程序可以使用使用 PhotoChooserTask 和 MediaLibrary APIs 从图片库获取图片,用户可能遭遇意想不到的像内存占用过高甚至用尽了内存。因此,下面为 在应用程序间分享图片制定了一些规则:

—应用程序应该把高分辨率的照片保存到应用的本地存储里,低分辨率的图片保存到图片库。

—当应用程序从图片库中打开一些图片时,可以匹配图片库中的图片和本地存储中的高分辨率照片,比如,根据 照片文件的文件名

—应用程序这么做就必须保证适时的清理应用程序本地存储,以避免没用的高分辨率图片占用磁盘空间。


关于保存图片的低分辨率版本,500万像素是比较恰当的,既可以在 A3 的纸上进行清晰打印,尺寸又足够小可以适应 各种分享的场合。

因为你在图片库中只保存了低分辨率的图片,所以强烈建议你为你的应用程序添加富媒体支持。从而让用户方便的到你 的应用程序中查看原图,从而有增加了你的应用程序的使用频率。

上面描述的规则和富媒体扩展,你的应用都应该支持,例如,示例应用 Photo Inspector 和 Nokia Pro Camera 应用程序。另外,高分辨率的图片也不能被其他第三方应用程序获得。


双保存之保存到本地存储和图片库

下面的示例演示了你如何保存高分辨率版本图片到应用的本地存储,低分辨率的版本保持到照片库以供其它 应用使用。特别是当照片在 1000+万 以上的大尺寸时,需要考虑压缩图片并且使用双保存策略。如果图片 超过了 500-800万的范围双保存通常是有意义的。有些像使用 870万分辨率的手机拍出的照片不需要做额 外的工作去压缩图片。除非你考虑到其它特殊的场合,比如分享到一些社交网络上。确保图片没有超过 Windows Phone 8 的 4096X 4096 像素最大纹理尺寸是比较合理的,因为这可能导致应用程序出现意想不到 的问题,从而可以适当避免处理这么大分辨率的图片。

下面的代码片段演示了缩小图片到 500万(保存到图片库的推荐尺寸)。注意这里演示的双保存是依赖 文件的名称—保持不同分辨率版本的文件使用相同的文件名称。

 using Microsoft.Xna.Framework.Media;
using Microsoft.Xna.Framework.Media.PhoneExtensions;
using Nokia.Graphics.Imaging; using Windows.Foundation;
 
...
 
public class Utilities
{
...
 
/// <summary>
/// Asynchronously saves a low resolution version of given photo to MediaLibrary. If the photo is too
/// large to be saved to MediaLibrary as is, also saves the original high resolution photo to application's
/// local storage so that the high resolution version is not lost.
/// <param name="image">Photo to save</param>
/// <returns>Path to the saved file in MediaLibrary</returns>
/// </summary>
public static async Task<string> SaveAsync(IBuffer image)
{
var savedPath = "";
 
if (image != null && image.Length > 0)
{
uint maxBytes = 2 * 1024 * 1024; // 2 megabytes
var maxPixels = 5 * 1024 * 1024; // 5 megapixels
var maxSize = new Size(4096, 4096); // Maximum texture size on WP8 is 4096x4096
 
AutoResizeConfiguration resizeConfiguration = null;
 
using (var editingSession = new EditingSession(image))
{
if (editingSession.Dimensions.Width * editingSession.Dimensions.Height > maxPixels)
{
var compactedSize = CalculateSize(editingSession.Dimensions, maxSize, maxPixels);
 
resizeConfiguration = new AutoResizeConfiguration(maxBytes, compactedSize,
new Size(0, 0), AutoResizeMode.Automatic, 0, ColorSpace.Yuv420);
}
}
 
var filenameBase = "myphotoapp_" + DateTime.UtcNow.Ticks.ToString();
 
if (resizeConfiguration != null)
{
// Store high resolution original to application local storage
 
using (var store = IsolatedStorageFile.GetUserStoreForApplication())
{
var localPath = @"\LocalImages";
 
if (!store.DirectoryExists(localPath ))
{
store.CreateDirectory(localPath );
}
 
using (var file = store.CreateFile(localPath + @"\" + filenameBase + @".jpg"))
{
using (var localImage = image.AsStream())
{
localImage.CopyTo(file);
 
file.Flush();
}
}
}
 
// Compact the image for saving to the library
 
image = await Nokia.Graphics.Imaging.JpegTools.AutoResizeAsync(image, resizeConfiguration);
}
 
 
using (var library = new MediaLibrary())
{
using (var libraryImage = image.AsStream())
{
using (var picture = library.SavePictureToCameraRoll(filenameBase, libraryImage))
{
savedPath = picture.GetPath();
}
}
}
}
 
return savedPath;
}
 
 
/// <summary>
/// Calculates a new size from originalSize so that the maximum area is maxArea
/// and maximum size is maxSize. Aspect ratio is preserved.
/// </summary>
/// <param name="originalSize">Original size</param>
/// <param name="maxArea">Maximum area</param>
/// <param name="maxSize">Maximum size</param>
/// <returns>Area in same aspect ratio fits the limits set in maxArea and maxSize</returns>
private static Size CalculateSize(Size originalSize, Size maxSize, double maxArea)
{
// Make sure that the image does not exceed the maximum size
 
var width = originalSize.Width;
var height = originalSize.Height;
 
if (width > maxSize.Width)
{
var scale = maxSize.Width / width;
 
width = width * scale;
height = height * scale;
}
 
if (height > maxSize.Height)
{
var scale = maxSize.Height / height;
 
width = width * scale;
height = height * scale;
}
 
// Make sure that the image does not exceed the maximum area
 
var originalPixels = width * height;
 
if (originalPixels > maxArea)
{
var scale = Math.Sqrt(maxArea / originalPixels);
 
width = originalSize.Width * scale;
height = originalSize.Height * scale;
}
 
return new Size(width, height);
}
...
}


匹配图片库里和本地保存的文件

PhotoChooserTask 和 MediaLibrary APIs

现在你有两个分辨率版本的图片,分别在图片库中的分辨率版本和应用本地存储中的高分辨率原图,如果在应用中使用 PhotoChooserTask 或者 Medialibrary APIs 打开图片,下面演示了你如何检查选择的图片,与之匹配的本地高分辨率是 否可用。

using Microsoft.Phone.Tasks;
 
...
 
public partial class PreviewPage : PhoneApplicationPage
{
private PhotoChooserTask _photoChooserTask = new PhotoChooserTask();
 
...
 
public PreviewPage()
{
...
 
_photoChooserTask.Completed += PhotoChooserTask_Completed;
}
 
...
 
private void PhotoChooserTask_Completed(object sender, PhotoResult e)
{
if (e.TaskResult == TaskResult.OK)
{
if (e.ChosenPhoto.CanRead && e.ChosenPhoto.Length > 0)
{
var localStream = Utilities.LocalPhotoFromLibraryPath(e.OriginalFileName);
 
if (localStream != null)
{
e.ChosenPhoto.Close();
 
localStream.Position = 0;
 
// Note that while we use the high resolution original here, the BitmapImage
// will scale it down for display due to the texture size limit. Therefore
// using the high resolution photo would only make sense if we would also be
// doing something really high resolution dependent with it
InitializePreview(localStream);
}
else
{
InitializePreview(e.ChosenPhoto);
}
}
}
}
 
...
}
...
 
public class Utilities
{
...
 
/// <summary>
/// Takes a MediaLibrary photo path and tries to find a local high resolution copy of the same photo.
/// </summary>
/// <param name="libraryPath">Path to a photo in MediaLibrary</param>
/// <returns>Stream to a local copy of the same photo</returns>
public static Stream LocalPhotoFromLibraryPath(string libraryPath)
{
var localPathCandidate = @"\LocalImages\" + FilenameFromPath(libraryPath);
 
using (var store = IsolatedStorageFile.GetUserStoreForApplication())
{
if (store.FileExists(localPathCandidate))
{
return store.OpenFile(localPathCandidate, FileMode.Open);
}
}
 
return null;
}
 
/// <summary>
/// Takes a local high resolution photo path and tries to find MediaLibrary copy of the same photo.
/// </summary>
/// <param name="localPath">Path to a locally saved photo</param>
/// <returns>Stream to a MediaLibrary copy of the same photo</returns>
public static Stream LibraryPhotoFromLocalPath(string localPath)
{
var localFilename = FilenameFromPath(localPath);
 
using (var library = new MediaLibrary())
{
using (var pictures = library.Pictures)
{
for (int i = 0; i < pictures.Count; i++)
{
using (var picture = pictures[i])
{
var libraryFilename = FilenameFromPath(picture.GetPath());
 
if (localFilename == libraryFilename)
{
return picture.GetImage();
}
}
}
}
}
 
return null;
}
 
/// <summary>
/// Takes a full path to a file and returns the last path component.
/// </summary>
/// <param name="path">Path</param>
/// <returns>Last component of the given path</returns>
private static string FilenameFromPath(string path)
{
var pathParts = path.Split('\\');
return pathParts[pathParts.Length - 1];
}
 
...
}


富媒体和照片编辑选择器扩展

一个很实用的方式就是你的应用和媒体库建立链接,可以参考  ​Rich media extensibility (MSDN) 

和 ​Photo edit picker extensibility (MSDN) 进行集成。 从而鼓励用户用你的应用程序打开图片。

通过富媒体扩展,你保存到媒体库中的图片会显示一个链接,用来启动你的应用,当用户单击这个链接打开你的应用时, 会在导航路径中同时传递一个特殊的 token ,你可以使用相应的 api 利用这个 token 来打开照片。

通过照片编辑选择器扩展,任何保存在图片库中的图片都可以通过编辑菜单中的编辑选项在你的应用中打开。和富媒体扩展的 方式一样,你的应用也是通过导航的 URI 中的 token 来获取相应的图片。

下面的示例演示你怎样通过一个照片的 token 匹配到本地保存的高分辨率版本的照片

public class Utilities
{
...
 
/// <summary>
/// Takes a MediaLibrary photo token and tries to find a local high resolution copy of the same photo.
/// </summary>
/// <param name="token">Photo token</param>
public static Stream LocalPhotoFromLibraryToken(string token)
{
using (var library = new MediaLibrary())
{
using (var picture = library.GetPictureFromToken(token))
{
var libraryPath = picture.GetPath();
 
return LocalPhotoFromLibraryPath(libraryPath );
}
}
}
 
...
}


从图片库中获取图片的缩略图

如果你的应用程序使用的双保存,意味着你保存在本地的图片在图片库中还有相应的副本,你可以很轻松的从 图片库获取相应的缩略图,而不需要自己在应用程序中另外保存副本。

下面的代码演示了如何从图片库中获取你本地保存图片的缩略图。

using Microsoft.Xna.Framework.Media.PhoneExtensions;
 
...
 
public class Utilities
{
...
 
public struct Photo
{
public string Filename = null;
public Stream Thumbnail = null;
}
 
/// <summary>
/// Attempts to get a thumbnail for given photo filenames from MediaLibrary.
/// </summary>
/// <param name="localFilenames">Photo filenames</param>
/// <returns>List of photo items for which a thumbnail was found</returns>
private static List<Photo> GetLibraryThumbnails(List<string> localFilenames)
{
var photos = new List<Photo>();
 
using (var library = new MediaLibrary())
{
using (var pictures = library.Pictures)
{
foreach (var localFilename in localFilenames)
{
for (int i = 0; i < pictures.Count; i++)
{
using (var picture = pictures[i])
{
var libraryPath = picture.GetPath();
var libraryFilename = FilenameFromPath(libraryPath);
 
if (localFilename == libraryFilename)
{
var thumbnail = picture.GetThumbnail();
 
var photo = new Photo()
{
Filename = localFilename,
Thumbnail = thumbnail
};
 
photos.Add(photo);
 
break;
}
}
}
}
}
}
 
return photos;
}
 
...
}


保持应用的本地存储整洁

第三个关于保存和加载照片需要注意的就是应用的本地存储避免保存不需要的照片。

下面的方法演示了通过遍历本地保存的图片并且检查媒体库中是否还有相应的副本, 如果没有,则删除本地的文件。

using Microsoft.Xna.Framework.Media.PhoneExtensions;
 
...
 
public class Utilities
{
...
 
/// <summary>
/// Goes through all the locally saved photos and tries to find a match for them in the MediaLibrary.
/// If a match is not found (photo has been deleted from the MediaLibrary) this routine deletes
/// also the locally saved photo.
/// </summary>
public void CleanLocalPhotos()
{
using (var store = IsolatedStorageFile.GetUserStoreForApplication())
{
var localPath = @"\LocalImages";
 
if (store.DirectoryExists(localPath ))
{
var array = store.GetFileNames(localPath + @"\*");
 
using (var library = new MediaLibrary())
{
using (var pictures = library.Pictures)
{
foreach (var localFilename in array)
{
var found = false;
 
for (int i = 0; i < pictures.Count && !found; i++)
{
using (var picture = pictures[i])
{
var libraryFilename = FilenameFromPath(picture.GetPath());
 
if (localFilename == libraryFilename)
{
found = true;
}
}
}
 
if (!found)
{
store.DeleteFile(localPath + @"\" + localFilename);
}
}
}
}
}
}
}
 
...
}

Nokia Wiki 原文链接http://developer.nokia.com/Resources/Library/Lumia/#!imaging/working-with-high-resolution-photos/accessing-and-saving-photos.html


Version Hint

Windows Phone: [[Category:Windows Phone]]
[[Category:Windows Phone 7.5]]
[[Category:Windows Phone 8]]

Nokia Asha: [[Category:Nokia Asha]]
[[Category:Nokia Asha Platform 1.0]]

Series 40: [[Category:Series 40]]
[[Category:Series 40 1st Edition]] [[Category:Series 40 2nd Edition]]
[[Category:Series 40 3rd Edition (initial release)]] [[Category:Series 40 3rd Edition FP1]] [[Category:Series 40 3rd Edition FP2]]
[[Category:Series 40 5th Edition (initial release)]] [[Category:Series 40 5th Edition FP1]]
[[Category:Series 40 6th Edition (initial release)]] [[Category:Series 40 6th Edition FP1]] [[Category:Series 40 Developer Platform 1.0]] [[Category:Series 40 Developer Platform 1.1]] [[Category:Series 40 Developer Platform 2.0]]

Symbian: [[Category:Symbian]]
[[Category:S60 1st Edition]] [[Category:S60 2nd Edition (initial release)]] [[Category:S60 2nd Edition FP1]] [[Category:S60 2nd Edition FP2]] [[Category:S60 2nd Edition FP3]]
[[Category:S60 3rd Edition (initial release)]] [[Category:S60 3rd Edition FP1]] [[Category:S60 3rd Edition FP2]]
[[Category:S60 5th Edition]]
[[Category:Symbian^3]] [[Category:Symbian Anna]] [[Category:Nokia Belle]]

This page was last modified on 17 October 2013, at 03:43.
114 page views in the last 30 days.
×