Windows Phone 8

Создание приложения камеры для смартфонов Nokia Lumia

Раджеш Лал

Скачайте Windows Phone SDK прямо сегодня!

Windows Phone SDK включает все средства разработки приложений и игр для Windows Phone.

Продукты и технологии:

Windows Phone 8, Nokia Imaging SDK

В статье рассматриваются:

  • технология PureView, применяемая в продвинутых смартфонах Nokia;
  • технология Random Access JPEG;
  • API в Nokia Imaging SDK;
  • рабочий процесс приложения камеры;
  • создание светофильтра «сдвиг и уклон» (tilt-shift, TS).

В этой статье я научу вас, как разработать приложение для смартфонов Nokia Lumia 1020 с 41-мегапиксельной (Мп) камерой и Nokia Lumia 1520 с 20-мегапиксельной (Мп) камерой. Я сосредоточусь в основном на Nokia Lumia 1020, но эта информация применима ко всем устройствам Lumia Windows Phone 8 с технологией PureView. Сначала я рассмотрю PureView — технологию, используемую в мощных камерах этих смартфонов, а затем поясню доступные вам продвинутые средства, позволяющие улучшать ваши фотоснимки. Я дам обзор Nokia Imaging SDK (в нем масса готовых графических фильтров) и расскажу, как его использовать. Кроме того, мы обсудим типичный рабочий процесс, необходимый для создания приложения камеры, и покажу, как создать светофильтр «сдвиг и уклон» (tilt-shift photo filter) для имитации малой глубины резкости (shallow depth of field). Итак, начнем.

Что представляет собой технология PureView в смартфоне с 41-мегапиксельной камерой

Технология PureView состоит из аппаратного обеспечения и связанного программного обеспечения. Совместно они позволяют захватывать и сохранять высококачественные изображения с высоким разрешением. Три основных аспекта технологии PureView: высокоразрешающая матрица камеры, избыточная дискретизация (oversampling) и приближение без потерь (lossless zoom). Кратко поясню каждый из этих аспектов.

Центральное место в технологии PureView занимает высокоразрешающая матрица, имеющая 7728 пикселей по ширине и 5368 пикселей по высоте, что дает более 41 Мп. Это позволяет камере захватывать большие фотоснимки, в шесть раз большие, чем обычные фотографии с 5 Мп.

На рис. 1 сравниваются картинки с разрешением 41 Мп и 5 Мп. Благодаря разрешению в 41 Мп вы можете делать высококачественные снимки с разрешением 34 Мп в формате 16:9 (7728 × 4354), а также с разрешением 38 Мп в формате 4:3 (7152 × 5368), как показано в проекции матрицы камеры на рис. 2.

Сравнение картинок с разрешением 41 Мп и 5 Мп
Рис. 1. Сравнение картинок с разрешением 41 Мп и 5 Мп

7,728 7728
5,368 5368
3,072 3072
1,728 1728
41MP 41 Мп
5MP 5 Мп

Приближение без потерь
Рис. 2. Приближение без потерь

5MP, 3x Lossless Zoom 5 Мп, трехкратное приближение без потерь
34MP 16:9 Resolution 34 Мп, разрешение 16:9
38MP 4:3 Resolution 38 Мп, разрешение 4:3

Избыточная дискретизация пикселей наблюдается, когда камера делает 41-мегапиксельный снимок и создает высококачественное изображение в 5 Мп. Это картинка, которая показывается на экране смартфона. Более того, процесс избыточной дискретизации сохраняет все богатство деталей в изображении, но отфильтровывает любые визуальные шумы.

Третий аспект технологии PureView — приближение без потерь; это просто означает, что при приближении изображения вы не теряете в его качестве. Это возможно потому, что в любой момент вам доступна исходная 41-мегапиксельная картинка, которая подвергается избыточной дискретизации для вывода фотоснимка 5 Мп. При использовании зума вы на самом деле приближаете часть исходного снимка 41 Мп. При приближении вплоть до трехкратного вы по-прежнему имеете дело с частями исходного снимка 41 Мп. И даже при максимальном зуме качестве вашей фотографии остается на уровне качества снимка 5 Мп. Благодаря избыточной дискретизации качество картинки становится только лучше при приближении. Как это происходит, уже было показано на рис. 2.

Nokia Imaging SDK

При создании приложения камеры снимок с высоким разрешением является исходным материалом, но вам понадобится продвинутый стек программного обеспечения, способного использовать огромные изображения и дать вам возможность операций над ними. Здесь и вступает в игру Nokia Imaging SDK (bit.ly/1hJkmpl). Он предоставляет набор продвинутой функциональности для доступа к снимкам, сделанным высокоразрешающей камерой.

Nokia Imaging SDK включает частичное декодирование JPEG, которое называют технологией Random Access JPEG (RAJPEG). RAJPEG обеспечивает произвольный доступ к JPEG-данным, быстрое уменьшение масштаба (downscaling) изображений и мгновенное частичное декодирование (instant partial decoding). Эти средства RAJPEG поддерживают манипуляцию изображением в реальном времени. В Nokia Imaging SDK содержится более 50 фильтров, эффектов и улучшений. В нем даже поддерживается набор наиболее распространенных операций над изображениями, таких как отсечение (crop), изменение размеров (resize), вращение (rotate), отмена (undo) и многие другие. Эти готовые средства помогают создавать продвинутые приложения камеры, а также программы для работы со снимками, не заботясь о самой распространенной функциональности. Чтобы использовать Nokia Imaging SDK в проекте, следуйте инструкциям на странице Nokia Lumia Developer Library (bit.ly/KzDPNG).

API в Imaging SDK

API, доступные в Imaging SDK, можно легко применять к захваченным камерой данным изображения, написав всего несколько строк кода:

var imageSource = new StreamImageSource(e.ChosenPhoto);
// Определяем применяемые эффекты
var filterEffect = new FilterEffect(imageSource);
filterEffect.Filters = new [] { new FogFilter() };
// Рендеринг изображения с помощью WriteableBitmapRenderer
WriteableBitmapRenderer renderer =
  new WriteableBitmapRenderer(filterEffect, 
  myImageBitmap,OutputOption.PreserveAspectRatio);
await renderer.RenderAsync();

Здесь e.ChosenPhoto используется, чтобы создать StreamImageSource; e.ChosenPhoto может поступать непосредственно со снимка, захваченного камерой, или из любого снимка в альбоме. Затем я создаю эффект на основе фильтров для применения к снимку и добавляю художественный FogFilter к массиву фильтров в своем эффекте. При необходимости этот массив может содержать множество фильтров, которые потом применяются и подвергаются рендерингу в myImageBitmap, используя WriteableBitmapRenderer. Как и предполагает его имя, он осуществляет рендеринг изображения-источника в WriteableBitmap. Наконец, метод RenderAsync помогает сделать рендеринг изображения асинхронной операцией.

Наряду с FogFilter вы можете применять к снимку более 50 других фильтров из Imaging SDK. На рис. 3 показан список корректирующих фильтров (enhancement filters) в бесплатном App Filter Explorer, доступном для разработчиков в готовом виде. Он включает такие фильтры, как AutoEnhanceFilter, AutoLevelsFilter, ColorBoostFilter и ExposureFilter. Это фильтры, которые можно напрямую применять к любому снимку или к изображению, захватываемому камерой в реальном режиме, для улучшения качества.

Корректирующие фильтры в бесплатном App Filter Explorer
Рис. 3. Корректирующие фильтры в бесплатном App Filter Explorer

Существуют другие фильтры для изменения яркости (BrightnessFilter) и контраста (ContrastFilter), которые принимают соответствующие параметры. Есть ряд фильтров исключительно для художественных эффектов, например emboss (выдавливание выпуклого изображения), grayscale (полутоновое изображение) и сепия. Также имеются «фильтры» для редактирования изображений, которые обеспечивают ранее упомянутые часто используемые функции вроде вращения, переворачивания, отражения и др. Их можно применять к одному изображению несколько раз, один поверх другого, что позволяет создавать драматические эффекты.

Рабочий процесс приложения высокоразрешающей камеры

Теперь, когда я рассказал об аппаратных возможностях и средствах Imaging SDK, я покажу типичный рабочий процесс, необходимый для создания мощного приложения камеры. Поскольку я имею дело с огромным изображением, мне придется соблюдать осторожность, чтобы мое приложение не замедлило другие программы и не заняло чрезмерный объем памяти. Этого можно избежать, не пытаясь напрямую манипулировать огромным изображением в камере.

В любой момент времени в камере присутствуют два изображения с размерами 41 Мп и 5 Мп; последний показывается на панели обзора камеры (camera view panel). В своем приложении я буду всегда работать с 5-мегапиксельным снимком, который может быть избыточно дискретизированным изображением всего фотоснимка, приближенной частью картинки или избыточно дискретизированным и частично приближенным фрагментом.

Рабочий процесс этого приложения показан на рис. 4.

Рабочий процесс приложения для работы с изображениями
Рис. 4. Рабочий процесс приложения для работы с изображениями

Acquire Photo Получение снимка
Local Storage Локальное хранилище
Magic Магия
Save image Сохранение изображения
Camera capture Захват камерой
Photo chooser Селектор фотоснимков
Photo capture Захват снимка
Process image Обработка изображения
Apply filters Применение фильтров

Индивидуальные этапы включают:

  • захват изображения с высоким разрешением;
  • его сохранение в вашем локальном хранилище;
  • магические пассы с использованием Imaging SDK;
  • уменьшение масштаба конечного изображения до 5 Мп;
  • сохранение улучшенного 5-мегапиксельного изображения в каталоге, куда помещаются снимки с камеры (camera roll).

Создание светофильтра: сдвиг и уклон

Теперь я пошагово пройду с вами по рабочему процессу в своем приложении камеры. Фотография с использованием сдвига и уклона — это применение движений камеры, особенно уклона, для избирательного фокусирования, чтобы сымитировать пейзаж как на миниатюре (рис. 5). Подробнее о фотографии по методу сдвига и уклона см. bit.ly/1bRYYNK. В этом приложении я моделирую малую глубину резкости с помощью цифровой постобработки, используя несколько фильтров в Nokia Imaging SDK.

Эффект сдвига и уклона
Рис. 5. Эффект сдвига и уклона

Original Оригинал
After tilt shift После сдвига и уклона

Получение снимка Первый шаг в любом приложении камеры — получение изображения от камеры или галереи изображений. Это можно сделать тремя способами. Один из них — использование 5-мегапиксельной камеры по умолчанию с помощью CameraCaptureTask, который вызывает срабатывание обычного видоискателя камеры. Второй — использование «родного» элемента управления для выбора снимков, который называется PhotoChooserTask. Эти два варианта достаточны для большинства приложений камеры, но в продвинутом приложении, где нужно захватывать снимки с высоким разрешением, вы должны создать собственный видоискатель, который вызывает запуск встроенного приложения-видоискателя Pro Camera с высоким разрешением. Для этого применяется объект PhotoCaptureDevice. Список методов и свойств, поддерживаемых PhotoCaptureDevice см. на рис. 6.

Методы и свойства, поддерживаемые PhotoCaptureDevice
 Рис. 6. Методы и свойства, поддерживаемые PhotoCaptureDevice

UI Приложение для сдвига и уклона (tilt-shift app) содержит два объекта: OriginalImage (для отображения реального захваченного или выбранного изображения) и TiltShiftImage (для отображения конечного изображения после применения фильтра сдвига и уклона к оригиналу). SelectButton активизирует «родное» средство выбора снимков, а CameraButton — камеру, как показано в коде на рис. 7.

Рис. 7. XAML-код UI

<Canvas x:Name="LayoutRoot" Background="Transparent">
  <TextBlock Text="" Style="{StaticResource
    PhoneTextNormalStyle}" />
  <Image x:Name="TiltShiftImage" Height="480" Width="728"
    Stretch="UniformToFill" MouseLeftButtonUp
    ="TiltShiftImage_MouseLeftButtonUp"/>
  <Image x:Name="OriginalImage" Height="480" Width="728"
    Stretch="UniformToFill" Canvas.ZIndex="0"
    MouseLeftButtonUp="OriginalImage_MouseLeftButtonUp"
    Source="/Assets/Landscapes.png"/>
  <Rectangle x:Name="TiltshiftRegion" Fill="White" Height="65"
    Stroke="#FF0B7AFF" Canvas.Top="320" Width="728"
    Opacity="0.25" StrokeThickness="5"/>
  <Button x:Name="SelectButton" Content="Select"
    Click="PickAnImageButton_Click" Canvas.Left="4"
    Canvas.Top="398" />
  <Button x:Name="CameraButton" Content="Camera"
    Click="CameraButton_Click" Canvas.Left="123"
    Canvas.Top="398" />
  <Button x:Name="ProButton" Content="Pro Camera"
    Click="ProCameraButton_Click" Canvas.Left="254"
    Canvas.Top="398" />
  <Button x:Name="SaveButton" Content="Save"
    Click="SaveImage_Click" Canvas.Left="630"
    Canvas.Top="398" />
  <Button x:Name="TiltShiftButton" Content="Tilt Shift"
    Click="TiltShiftButton_Click" Canvas.Left="449"
    Canvas.Top="398" />
</Canvas>

Полученный UI камеры представлен на рис. 8.

UI приложения для сдвига и уклона
Рис. 8. UI приложения для сдвига и уклона

Захват изображения с высоким разрешением Первые два способа (работа с обычной камерой и функциональностью селектора снимков) достаточно прямолинейны. Для этих целей в пространстве имен Microsoft.Phone.Tasks есть два объекта задач: CameraCaptureTask и PhotoChooserTask. Просто выберите изображение либо через селектор, либо как захваченные данные от камеры в качестве источника фильтра сдвига и уклона:

private void PickAnImageButton_Click(object sender, RoutedEventArgs e)
{
  PhotoChooserTask chooser = new PhotoChooserTask();
  chooser.Completed += PickImageCallback;
  chooser.Show();  
}
private void CameraButton_Click(object sender, RoutedEventArgs e)
{
  CameraCaptureTask camera = new CameraCaptureTask();
  camera.Show();
  camera.Completed += PickImageCallback;
}

Чтобы захватить снимок высокого разрешения, мне нужно создать собственный видоискатель, используя видеокисть (video brush), источником которой будет изображение от приложения Pro Camera:

<Canvas x:Name="Canvas" Tap="Canvas_Tap" Height="480"
  HorizontalAlignment="Center" VerticalAlignment="Center">
  <Canvas.Background>
    <VideoBrush x:Name="ViewfinderVideoBrush" Stretch="Uniform"/>
  </Canvas.Background>          
  <Border x:Name="FocusBracket" Width="80" Height="80"
    BorderBrush="White" BorderThickness="2" Margin="-40"
    Visibility="Collapsed" CornerRadius="360"/>
  <Image x:Name="FreezeImage" Visibility="Collapsed"
    Stretch="Uniform" Height="480"/>
</Canvas>

Мне также требуется инициализировать приложение Pro Camera правильным разрешением в зависимости от устройства (Lumia 1020 или Lumia 1520) и нужным типом разрешения. Параметры перечислены в табл. 1.

Табл. 1. Параметры для снимка высокого разрешения

Модель Настраиваемые вручную значения Параметры для снимка высокого разрешения
Lumia 1020 RM-875, RM-876, RM-877 7712 x 4352 (16:9), 7136 x 5360 (4:3)
Lumia 1520 RM-937, RM-938, RM-940, RM-939 5376 x 3024 (16:9), 4992 x 3744 (4:3)

На рис. 9 показано, как инициализировать приложение Pro Camera.

Рис. 9. Инициализация приложения Pro Camera

private void InitializeCamera() {
  Windows.Foundation.Size captureResolution;
  var deviceName = DeviceStatus.DeviceName;
  if (deviceName.Contains("RM-875") || deviceName.Contains("RM-876") ||
    deviceName.Contains("RM-877"))
  {
    captureResolution = new Windows.Foundation.Size(7712, 4352); // 16:9
    //captureResolution = new Windows.Foundation.Size(7136, 5360); // 4:3
  }
  else if (deviceName.Contains("RM-937") || deviceName.Contains("RM-938") ||
    deviceName.Contains("RM-939"))  {
      captureResolution = new Windows.Foundation.Size(5376, 3024); // 16:9
      //captureResolution = new Windows.Foundation.Size(4992, 3744); // 4:3
    }
  else {
    captureResolution = PhotoCaptureDevice.GetAvailableCaptureResolutions(
      REAR_CAMERA_SENSOR_LOCATION).First();
  }
  var task = PhotoCaptureDevice.OpenAsync(REAR_CAMERA_SENSOR_LOCATION,
  captureResolution).AsTask();
  task.Wait();
  _device = task.Result;
  _device.SetProperty(
    KnownCameraGeneralProperties.PlayShutterSoundOnCapture, true);
  if (_flashButton != null) {
    SetFlashState(_flashState);
  }
  AdaptToOrientation(); 
  ViewfinderVideoBrush.SetSource(_device);
  if (PhotoCaptureDevice.IsFocusSupported(REAR_CAMERA_SENSOR_LOCATION))  {
    Microsoft.Devices.CameraButtons.ShutterKeyHalfPressed +=
    CameraButtons_ShutterKeyHalfPressed;
  }
  Microsoft.Devices.CameraButtons.ShutterKeyPressed +=
    CameraButtons_ShutterKeyPressed;
}

Захват изображения от приложения Pro Camera включает события ShutterHalfKeyPressed и ShutterKeyPressed, как показано на рис. 10.

Рис. 10. Захват изображения через события нажатия кнопки затвора

private async void CameraButtons_ShutterKeyHalfPressed(
  object sender, EventArgs e) {
    if (!_focusing && !_capturing) {
      _focusing = true;
      await _device.FocusAsync();
      _focusing = false;
    }
}
private async void CameraButtons_ShutterKeyPressed(
  object sender, EventArgs e) {
    if (!_focusing && !_capturing) {
      _capturing = true;
      var stream = new MemoryStream();
      try {
        var sequence = _device.CreateCaptureSequence(1);
        sequence.Frames[0].CaptureStream = stream.AsOutputStream();
        await _device.PrepareCaptureSequenceAsync(sequence);
        await sequence.StartCaptureAsync();
      }
      catch (Exception ex) {
        stream.Close();
      }
      _capturing = false;
      if (stream.CanRead) {
        // Обработка изображения в потоке (stream).
        // Можно сохранить в локальном хранилище.
      }
    }
}

Магические пассы Сначала я использую PickImageCallback для получения снимка, который потом задается как источник OriginalImage. Для доступа к размерам изображения применяется ImageProviderInfo.

Кроме того, я создаю фильтры и применяю их. Чтобы создать эффект сдвига и уклона, используются три фильтра: BlurFilter с KernelSize 15, ColorBoostFilter со значением 0.5 и вновь BlurFilter, но с KernelSize 21. Два фильтра размытия (blur filters) делают передний план и фон изображения слегка расфокусированными, а ColorBoostFilter осветляет область, которую я хочу использовать для создания эффекта миниатюры (miniature effect). Соответствующий код показан на рис. 11.

Рис. 11. Инициализация фильтров для эффекта

public partial class MainPage : PhoneApplicationPage {
  private Stream _img;
  private StreamImageSource imageSource;
  private ImageProviderInfo info;
  private FilterEffect _tiltshiftEffect = null;
  private WriteableBitmap _tiltshiftImageBitmap = null;
  // Конструктор
  public MainPage()  {
    InitializeComponent();
    _tiltshiftImageBitmap =
    new WriteableBitmap((int)TiltShiftImage.Width,(int)TiltShiftImage.Height);
  }
...
private async void PickImageCallback(Object sender, PhotoResult e) {
  if (e.TaskResult != TaskResult.OK) {
    return;
  }
  _img = e.ChosenPhoto;
  imageSource = new StreamImageSource(_img);
  var bitmapImage = new BitmapImage();
  bitmapImage.SetSource(e.ChosenPhoto);
  OriginalImage.Source = bitmapImage;
  info = await imageSource.GetInfoAsync();
  TiltShift();         
...

Чтобы применить фильтр сдвига и уклона, мне нужны три прямоугольника: верхний — для размытия, средний — для осветления и нижний — тоже для размытия (рис. 12). В этом примере используется прямоугольная область TiltshiftRegion, которой пользователь может коснуться, а затем переместить в ту позицию, где должен произойти сдвиг и уклон. Осветленный прямоугольник, показанный на рис. 12, подвергается сдвигу и уклону, а остальная область размывается.

Три прямоугольника, используемые для эффекта «сдвиг и уклон»
Рис. 12. Три прямоугольника, используемые для эффекта «сдвиг и уклон»

Blur top Размытый верх
Color boost Осветление
Blur bottom Размытый низ

Позиция TiltShiftRegion используется в вычислении трех прямоугольников, где применяются фильтры (рис. 13).

Рис. 13. Комбинирование фильтров для эффекта

private async void TiltShift() {
  if (info == null) return;
  try {
    var topLeft = new Windows.Foundation.Point(0, 0);
    var tiltShiftRegionTop = Canvas.GetTop(TiltshiftRegion);
    var delta = info.ImageSize.Height / TiltShiftImage.Height;
    tiltShiftRegionTop = (tiltShiftRegionTop + 10) * delta;
    var tiltShiftRegionBottom =
      (Canvas.GetTop(TiltshiftRegion) + 10) * delta + TiltshiftRegion.Height;
    var topRight =
      new Windows.Foundation.Point(info.ImageSize.Width, tiltShiftRegionTop);
    var bottomLeft = new Windows.Foundation.Point(0, tiltShiftRegionBottom);
    var bottomRight = new Windows.Foundation.Point(
      info.ImageSize.Width, info.ImageSize.Height);
    // Определяем применяемые эффекты
    _tiltshiftEffect = new FilterEffect(imageSource);
    List<IFilter> filters = new List<IFilter>();
    filters.Add(new BlurFilter(15, (
      new Windows.Foundation.Rect(topLeft, topRight)),
      BlurRegionShape.Rectangular));
    filters.Add(new ColorBoostFilter(0.5));
    filters.Add(new BlurFilter(23, (new Windows.Foundation.Rect(bottomLeft,
      bottomRight)), BlurRegionShape.Rectangular));
    _tiltshiftEffect.Filters = filters;
    // Рендеринг изображения с помощью WriteableBitmapRenderer
    WriteableBitmapRenderer renderer =
      new WriteableBitmapRenderer(_tiltshiftEffect,
      _tiltshiftImageBitmap, OutputOption.PreserveAspectRatio);
    _tiltshiftImageBitmap = await renderer.RenderAsync();
    TiltShiftImage.Source = _tiltshiftImageBitmap;
  }
  catch (Exception exception) {
    MessageBox.Show("Exception:" + exception.Message);
    return;
  }
  SaveButton.IsEnabled = true; 
}

Сохранение снимка       Наконец, обработанное изображение нужно сохранить обратно в источнике (рис. 14).

Рис. 14. Сохранение обработанного изображения

private async void SaveImage_Click(object sender, RoutedEventArgs e) {
  SaveButton.IsEnabled = false;
  if (_tiltshiftEffect == null) {
    return;
  }
  var jpegRenderer = new JpegRenderer(_tiltshiftEffect);
  // JPEG-рендер предоставляет исходный буфер
  // для фильтрованного изображения
  IBuffer jpegOutput = await jpegRenderer.RenderAsync();
  // Сохраняем изображение как JPEG в альбом
  MediaLibrary library = new MediaLibrary();
  string fileName = string.Format("TiltShiftImage_{0:G}", DateTime.Now);
  var picture = library.SavePicture(fileName, jpegOutput.AsStream());
  MessageBox.Show("Tilt Shift Image saved!");
  SaveButton.IsEnabled = true;
}

Ну вот и все. Теперь вы знаете, как использовать продвинутую технологию PureView для захвата изображения с камеры и выполнять постобработку картинки с помощью Nokia Imaging SDK. Надеюсь, вы сочтете эту статью полезной, и я с нетерпением жду ваших комментариев и предложений.


Раджеш Лал (Rajesh Lal) работает в Nokia, увлечен технологиями Windows Phone, а также веб-технологиями. Его последняя книга — «Digital Design Essentials: 100 Ways to Create Better Desktop, Web and Mobile Interfaces» (Rockport Publishers, 2013); вы найдете ее на bit.ly/dsgnbk. Подробнее об авторе вы узнаете на его сайте iRajLal.com.

Выражаю благодарность за рецензирование этой статьи экспертам Шевену Кристи (Chevon Christie) из Microsoft и Парасу Вадехре (Paras Wadehra) из Nokia.