Digitale Linse

Lens-Apps für Windows Phone 8 entwickeln
Kommentare

Nokia lebt seit jeher von der Leistungsfähigkeit seiner Kameras, die berühmt-berüchtigte Partnerschaft mit Zeiss ist auch technisch uninteressierten Personen hinreichend bekannt. Die Kameraapplikation des Betriebssystems wiederum kann durch verschiedene „Lens-Apps“ ergänzt werden.

Fotofilterapplikationen sind seit dem enormen Erfolg von Instagram mehr als populär. Viele Benutzer haben zehn oder mehr Kamera-Apps auf ihrem Telefon – eine Situation, die mit Microsofts Vision von Einfachheit und Intuition nicht wirklich kompatibel ist. Windows Phone 8 versucht, diesen Wildwuchs durch einen neuen Applikationstyp einzudämmen. Die Kameraapplikation des Betriebssystems enthält den in Abbildung 1 gezeigten Button (mit zwei entgegengesetzten Pfeilen), der eine Liste so genannter „Lens-Apps“ einblendet. Das Antippen einer der Kacheln sorgt dafür, dass der Sucherbildschirm durch die Linse ersetzt wird.

Abb. 1: Der Lens-Knopf ist prominent platziert

Linse, was ist das?

Nach dem Erscheinen von Windows Phone 8 gingen einige Analysten davon aus, dass Linsen als eine Art Plug-in realisiert werden und sich diskret in den Bilddatenstrom einbinden würden. Dies hat sich in der Retrospektive als falsch erwiesen. Eine gestartete Linse benimmt sich wie eine aus einem Contract heraus gestartete App. Die von Microsoft für die Default-Kamera-App implementierte Pipeline arbeitet zu diesem Zeitpunkt nicht, der Bildschirm wird mit dem als Handler vorgesehenen Formular befüllt. Fortgeschrittene Linsen ermöglichen das nachträgliche Bearbeiten der mit ihnen aufgenommenen Bilddaten. Eine Rich-Media-fähige Linse meldet sich bei der Bildgalerie an. Ein von ihr erstellter Inhalt wird mit einem zusätzlichen Menüeintrag eingeblendet, der den Start der Applikation ermöglicht. Wenn der Nutzer diese Option anklickt, wird die Applikation abermals mit einem Contract gestartet. Dieser bekommt einen im Rahmen der Erstellung des Fotos zugewiesenen Parameter mit, der die Korrelation zwischen der angeforderten Szene und den im lokalen Ordner der Applikation gespeicherten Daten erlaubt.

Dieser auf den ersten Blick unnötig komplexe Workflow lässt sich am einfachsten anhand eines kleinen Beispiels visualisieren. Ein auf die Erstellung von Texteinblendungen spezialisiertes Programm möchte dem Benutzer das nachträgliche Ändern des Labels ermöglichen. Dazu speichert die App im Isolated Storage das Rohbild und den zuletzt verwendeten Text – in das Fotoalbum des Geräts wandert ein „fertiges“ JPG. Wenn der Benutzer das Label ändern will, klickt er den diesbezüglichen Button in der Bildergalerie an. Windows Phone sendet der Applikation daraufhin ein Ereignis zu, das die ID des neu anzulegenden Bilds enthält.

Linsenstruktur

Nach diesen einleitenden Überlegungen ist es nun an der Zeit, mit der Erstellung einer Beispiel-Lens zu beginnen. Aus Bequemlichkeitsgründen werden wir uns mit einer simplen C#-Lens begnügen, die wir auf Basis eines vorgegebenen Projektskeletts erstellen. Aus didaktischen Gründen wollen wir aber vorher noch einen Blick auf die Erstellung einer Basislinse werfen. Nutzen Sie hierfür den Projekt-Wizard zur Erstellung einer neuen Windows-Phone-Applikation. Als Zielbetriebssystem muss Windows Phone 8 ausgewählt werden, da frühere Versionen der Plattform mit Linsen nichts anzufangen wissen. Als Nächstes müssen wir unser Manifest um eine Extension-Deklaration erweitern. Visual Studio kennt das dazu notwendige Attribut noch nicht, weshalb wir die Manifestdatei rechts anklicken und im XML-Text-Editor öffnen. Die relevante Passage beginnt mit dem Tag , und wird unter dem schon vorhandenen Tokens-Tag platziert. Übernehmen Sie den Code aus Listing 1 einfach eins zu eins in Ihr Projekt – Tippfehler im GUID führen dazu, dass die Applikation nicht erkannt wird.


http://schemas.microsoft.com/windowsphone/2012/deployment" AppPlatformVersion="8.0">
  
  
    
      
    
    . . .
  

Die Kameraapplikation setzt zudem das Vorhandensein der in Tabelle 1 gezeigten Symbole voraus. Erstellen Sie die Dateien mit einem Grafikprogramm Ihrer Wahl und speichern Sie sie danach im /Assets/-Ordner ihres Projekts ab.

Auflösung des Bildschirms Größe der Datei Name der Datei
WVGA 173^2 Lens.Screen-WVGA.png
HD, 720p 259^2 Lens.Screen-720p.png
WXGA 277^2 Lens.Screen-WXGA.png

Lens-Apps tauchen normalerweise auch im Programmstarter auf. Microsoft lässt Ihnen hier freie Wahl: Starts aus dem Launcher können den Benutzer entweder in den Aufnahmebildschirm oder in ein „stationäres“ Formular bringen. Aus didaktischen Gründen wollen wir hier die zweite Option realisieren, weshalb wir unsere Applikation um ein weiteres Formular namens „Viewfinder“ ergänzen.

Windows Phone betrachtet Apps als eine Art Webseite auf Steroiden. Für Sie bedeutet das, dass ihr Programm vom Betriebssystem eine URL mit weiteren Informationen über die anstehende Aufgabe erhält. Ihre Entgegennahme erfolgt in einem URI-Mapper, der im Falle unseres Projekts aussieht wie in Listing 2.

class SUSUriMapper : UriMapperBase
{
  private string tempUri;
  public override Uri MapUri(Uri uri)
  {
    tempUri = uri.ToString();
    if (tempUri.Contains("ViewfinderLaunch"))
    {
      return new Uri("/Viewfinder.xaml", UriKind.Relative);
    }
    return uri;
  }
}

An sich gibt es hier nicht viel zu sehen. Wir prüfen, ob der eingehende URL einen Verweis auf den Viewfinder mitbringt. Wenn dies der Fall ist, wird das entsprechende Formular geladen, ansonsten erfolgt der Start wie gewohnt. Die Anmeldung der Klasse erfolgt im Rahmen der InitializePhoneApplication-Methode. Diese ist normalerweise schon vorgegeben und muss nur um die Zeile mit dem UriMapper ergänzt werden (Listing 3).

private void InitializePhoneApplication()
{
  if (phoneApplicationInitialized)
  return;
  // Create the frame but don't set it as RootVisual yet; this allows the splash
  // screen to remain active until the application is ready to render.
  RootFrame = new PhoneApplicationFrame();
  RootFrame.Navigated += CompleteInitializePhoneApplication;
  RootFrame.UriMapper = new SUSUriMapper();

Damit ist unsere Lens einsatzbereit. Installieren Sie sie auf einem Windows-Phone-Gerät Ihrer Wahl und öffnen Sie die Kamera-App. Das Symbol ihres Programms wird in der Liste der Linsen erscheinen. Klicken Sie auf Wunsch anschließend noch auf die Kachel, um die Funktion unseres URL-Handlers zu testen.Im nächsten Schritt geht es daran, den Viewfinder zu modifizieren. Wir wollen unseren Benutzer mit einem Gitter versorgen, das ihm die Ausrichtung der Szene erleichtert. Als weiteres kleines Ärgernis wollen wir den Bildern zu einem kräftigen Rotstich verhelfen, indem wir die restlichen Farbkomponenten ausfiltern.

Der Zugriff auf die Kamera des Telefons lässt sich zur Realisierung von Spionageapplikationen missbrauchen. Aus diesem Grund setzt Microsoft die Deklaration einer Capability (ID_CAP_ISV_CAMERA) voraus, das Requirement ID_REQ_REARCAMERA sorgt dafür, dass die App nur auf Telefonen mit einer rückseitigen Kamera lebensfähig ist. Beide Änderungen lassen sich ohne größere Probleme mit dem visuellen Manifesteditor erledigen. Während der Initialisierung des Viewfinder-Formulars müssen wir die Kamera zum Aufnehmen von Bildern animieren. Den dazu notwendigen Code zeigt Listing 4.

private void PhoneApplicationPage_Loaded(object sender, RoutedEventArgs e)
{
  asyncSetup();
}
private async void asyncSetup()
{
  Windows.Foundation.Size previewSize = PhotoCaptureDevice.GetAvailablePreviewResolutions(CameraSensorLocation.Back)[1];
  Windows.Foundation.Size captureSize = PhotoCaptureDevice.GetAvailableCaptureResolutions(CameraSensorLocation.Back)[0];
  myBitmap = new WriteableBitmap((int)previewSize.Width, (int)previewSize.Height);
  myPixelData=new int[(int)(previewSize.Height*previewSize.Width)];
  ImgP.Width = previewSize.Width/2;
  ImgP.Height = previewSize.Height/2;
  myCaptureDevice = await PhotoCaptureDevice.OpenAsync(CameraSensorLocation.Back, captureSize);
  myCaptureDevice.PreviewFrameAvailable += previewFrameReady;
}

Die erste Amtshandlung unseres Programms ist das Ermitteln der Preview- und der Bildgröße. An dieser Stelle treffen wir auf eine Eigenart des Betriebssystems: manche Geräte erlauben das Einstellen der Vorschaugröße nicht. In diesem Fall müssen Sie einfach einen Frame anfordern und diesen danach zur Ermittlung der Größe verwenden. In unserem Beispiel nutzen wir einen Erfahrungswert des Autors – das Samsung ATIV S verwendet die im zweiten Eintrag gespeicherte Bildgröße.

Kameraströme liefern Rohdaten zurück. Für uns ist das insofern von Relevanz, als wir zu ihrer Darstellung auf ein WriteableBitmap zurückgreifen. Dabei handelt es sich um eine stark abgespeckte Variante der vom Desktop hinreichend bekannten Klasse, die – unter anderem beim Verarbeiten von Pixeldaten brilliert. Danach schreiben wir einen Event Handler ein und beginnen die Abarbeitung der anfallenden Frames.

Aufmacherbild: The diaphragm of a camera lens aperture. von Shutterstock / Urheberrecht: Akirbs

[ header = Seite 2: Strom herbei ]

Strom herbei

Wir haben soeben festgestellt, dass PhotoCaptureDevices Previewframes im ARGB-Format zurückliefern. Dabei handelt es sich um einen „primitiven“ Datenstrom, in dem die Helligkeits- und Farbwerte hintereinander angeordnet sind. Zur Darstellung der darin enthaltenen Daten verwenden wir ein Image-Steuerelement, das per Drag and Drop im Korpus des Formulars abgelegt wird. Der resultierende Event Handler präsentiert sich dann wie in Listing 5.

private void previewFrameReady(ICameraCaptureDevice sender, object args)
{
  if (mySkipCounter > 10)
  {
    mySkipCounter++;
    sender.GetPreviewBufferArgb(myPixelData);
    return;
  }
  mySkipCounter = 0;

Wer sich irgendwann einmal mit der Kinect befasst hat, kennt das Problem des „Steckenbleibens“ mit Sicherheit. Die meisten nach dem Push-Prinzip arbeitenden Lösungen aus dem Hause Microsoft stellen die weitere Arbeit ein, wenn zu viele unverarbeitete Frames vorliegen. Da unser Code für eine Echtzeitverarbeitung viel zu langsam ist, umgehen wir das Problem, indem wir nur jeden zehnten Frame verarbeiten.Im nächsten Schritt folgt das Abrufen der vom Sensor angelieferten Daten. GetPreviewBufferARGB schreibt die eingehenden Informationen in ein als Parameter übergebenes Array von Integern:

sender.GetPreviewBufferArgb(myPixelData);
int ph = myBitmap.PixelHeight;
int pw = myBitmap.PixelWidth;
int ph1 = ph - 1;
int pw1 = pw - 1;

Beim Realisieren von „engen“ Schleifen ist es ratsam, Grenzwerte nach Möglichkeit nicht bei jedem Durchlauf zu berechnen. Aus diesem Grund legen wir zuerst einige Werte fest, bevor wir uns mit der eigentlichen Verarbeitung der Pixeldaten befassen (Listing 6).

for (int h = 0; h < ph; h++)
{
  int hStor = h * myBitmap.PixelWidth;
  int myH = ph1 - h;
  int myHStor = myH * pw;
  for (int w = 0; w < pw; w++)
  {
    //Invert height and width
    int myW = pw1 - w;
    long valA = myPixelData[myHStor + myW] & 0xFFFF0000;
    myBitmap.Pixels[hStor + w] = (int)valA;
  }
}

Windows Phone liefert die Kameradaten aus unerkennbaren Gründen sowohl horizontal als auch vertikal gespiegelt aus. Der Großteil unserer Routine befasst sich damit – die Reduktion auf die rote Farbkomponente erfolgt durch die Maskierung mit 0xFFFF0000.

Das auf den ersten Blick übertrieben defensiv erscheinende Vermeiden von Multiplikationen erweist sich in der Praxis als höchst sinnvoll .Heutige Windows Phones haben 720p-Bildschirme, was für einige zehntausend Operationen pro Frame sorgt. Multiplikationen sind aufgrund ihrer mathematischen Beschaffenheit aufwändiger als Additionen – manche Prozessoren schaffen zehn andere Operationen in der für eine Integer-Multiplikation erforderlichen Zeit.

Zu guter Letzt müssen wir das Image-Steuerelement noch über den neuen Inhalt informieren. Da die Abarbeitung unseres Event Handlers in einem zur Kamera gehörenden Thread erfolgt, greifen wir auf die Dienste des Dispatchers zurück:

if (Dispatcher.CheckAccess() == false)
{
  Dispatcher.BeginInvoke(() =

Damit ist die „verbesserte“ Version der Linse betriebsbereit. Das Ruckeln und gelegentliche „Zerreißen“ des Bilds liegt daran, dass die Übergabe zwischen den Threads nicht perfekt synchronisiert ist – auch das ist ein Problem, dem wir uns im nächsten Teil der Serie zuwenden wollen. Abbildung 2 zeigt das Programm im praktischen Einsatz.

Abb. 2: Die Linse liefert rotstichige Bilder zurück

Linse, effizient

Trotz der vergleichsweise hohen Rechenleistung von Windows-Phone-8-Hardware ist die hier beschriebene Vorgehensweise nicht ideal – die Anzeige der Kamera läuft alles andere als flüssig, das Abspeichern von Bildern nimmt noch mehr Zeit in Anspruch. Die Lösung dieses Problems ist, wie so oft, im Grafikchip zu finden. Wenn Ihre Routinen in HLSL vorliegen, können Sie die zu verarbeitenden Bilddaten in Form einer Textur an die GPU schicken. Dort erfolgt dann die eigentliche Bearbeitung, der Hauptprozessor ist nur mehr für das Entgegennehmen und Verarbeiten der Bilddaten zuständig. Nokia illustriert die hierzu notwendige Vorgehensweise in einem lesenswerten Wiki-Eintrag. Wir werden im nächsten Teil der Serie darauf zurückkommen und einen Schärfungsfilter realisieren – die Vorgehensweise ist im Großen und Ganzen logisch.

Praktische Linsengenerierung

Es ist in höchstem Maße empfehlenswert, sich so stark wie möglich am Aussehen der im Betriebssystem enthaltenen Sucherapplikation zu orientieren. Theoretisch spricht zwar nichts dagegen, das GUI von Hand neu zu erfinden. Leider ist das aber alles andere als effektiv. Aus Gründen der Bequemlichkeit wollen wir uns eines von Microsoft bereitgestellten Formulars ermächtigen. Laden Sie das hier bereitstehende Basic Lens Sample herunter und extrahieren Sie es an einem für Sie geeigneten Ort im Dateisystem. Öffnen Sie es danach wie jedes andere Projektbeispiel – eventuelle Warnungen über unsichere Herkunftsverhältnisse werden ignoriert.

sdkBasicLense ist, genauso wie die meisten anderen MSDN-Codebeispiele, vergleichsweise komplex aufgebaut. Das für uns wichtigste Formular ist MainPage.xaml, da sich dort die für die Realisierung des Viewfinders notwendige Intelligenz befindet. Die eigentliche Verwaltung der Kamera findet sich in LensViewModel.cs, das in Zusammenarbeit mit CameraEngine.cs für die Kommunikation mit der Hardware zuständig ist. MediaViewer ist ein kleines Add-on, das einen primitiven Bilderviewer realisiert. Linsen sollten dem Benutzer gemäß Microsofts Spezifikation Einblick in die in der aktuellen Sitzung angefertigten Bilder gewähren – das wird durch das Einbinden dieses Projekts realisiert.

Der erste Schritt zu Ihrer eigenen Linse wäre das Anpassen der Symbole und der im Manifest deklarierten Konstanten: QA-Assessoren reagieren nicht sonderlich positiv, wenn sich eine Drittanbieterapplikation als Angebot aus dem Hause Microsoft ausgibt. Im nächsten Schritt sollten Sie die Bildverarbeitungspipeline „optimieren“. Die von Haus aus installierte Variante mit dem VideoBrush ist zwar ressourcensparend, erlaubt Ihnen aber keinen Zugriff auf die im Datenstrom angelieferten Inhalte.

Linse zu Geld

Der Sucherbildschirm einer Linsenapplikation ist ein denkbar schlechter Aufenthaltsort für Banner: Da der User normalerweise voll auf das Geschehen konzentriert ist, wird er die Werbung mit relativer Sicherheit nicht wahrnehmen. Neben dem „Zuspammen“ diverser Einstellungsdialoge bietet sich das Einsperren der Benutzerdaten an. Wenn die Testversion Ihrer Linse Bilder nur in beschränkter Auflösung (beispielsweise XGA oder HD) ausgibt, können Sie den Benutzer zum Kauf der Vollversion animieren.

Besonders bösartige Entwickler nutzen die schon aus Zeiten von Palm OS bekannte Methode der progressiven Degeneration. Die Linse liefert anfangs Daten in Full HD, um die Auflösung im Laufe der Zeit stillschweigend zu beschränken. Perfiderweise erscheint der Warndialog hierzu nur nach dem ersten Start, weshalb der Nutzer im Laufe der Zeit einen nur durch den Kauf der Vollversion rettbaren Datenbestand aufbaut.

Fazit

Wer fotobezogene Applikationen für Windows Phone anbietet, sollte sich über kurz oder lang mit dem Gedanken anfreunden, diese ins Lens-Framework zu verfrachten. Das zahlt sich schon alleine aus dem Grund aus, dass Microsoft „normale“ Apps über kurz oder lang nicht mehr oder nur mehr sehr reduziert promoten wird.Unser kleines Beispiel zeigt, wie Sie Ihr Produkt auf den Weg der Linse bringen. Die Erstellung der diversen Bildverbesserungsalgorithmen ist ein Thema für sich, das wir in den nächsten Ausgaben abhandeln wollen. Bis dahin wünscht der Autor viel Spaß mit Ihrer Linse.

Unsere Redaktion empfiehlt:

Relevante Beiträge

Meinungen zu diesem Beitrag

X
- Gib Deinen Standort ein -
- or -