Monthly Archives: February 2007

1 Comment

My app on CodePlex has reached the v0.3.0.0 stadium. It does not sync the metadata yet, but it fetches the metadata from images on Flickr and on a local disk in a form that can easily be compared.

 

Jan kindly donated some C# code using GDI+ and direct file operations for reading and writing EXIF and IPTC information. After digging deeper into the .NET 3.0 documentation, I found out that the Windows Imaging Component that is part of the .NET Framework 3.0 should allow me to do this as well. Because I need to display the images at some point using Windows Presentation Foundation, I would like to take as few dependencies on different image libraries as possible. Image files tend to get large (couple of MB) and it takes some time to read them into memory for processing. I hope to prevent the need for processing them using both WPF/WIC and GDI+ (the pre-WPF way on the Windows platform).

For instance, this is the code to read out the GPS longitude and latitude from a picture on disk:

         using (Stream pictureFileStream = new FileStream(pictureFilename, FileMode.Open, FileAccess.Read))
         {
            JpegBitmapDecoder decoder = new JpegBitmapDecoder(pictureFileStream, BitmapCreateOptions.None, BitmapCacheOption.None);
 
            BitmapMetadata bitmapMetadata = (BitmapMetadata)decoder.Frames[0].Metadata;
 
            ulong[] latitudes = bitmapMetadata.GetQuery("/app1/ifd/gps/subifd:{ulong=2}") as ulong[];
            ulong[] longitudes = bitmapMetadata.GetQuery("/app1/ifd/gps/subifd:{ulong=4}") as ulong[];
         }

It took me quite some time before I found the query strings for these metadata fields through trial-and-error. I could not find them documented anywhere in this form yet.

Unfortunately aiding someone else by testing his application, doing bug reporting and giving suggestions for improvements hasn't turned out to be reciprocal yet ;( So I am looking for other people who are willing to give my app a try and let me know what they think.

You can download the app in binary form from the release page and in source form as well.

The binary form is useful if you just want to look at the UI of the application. The source form is useful if you want to review the code and/or to run the program in the Visual Studio debugger so you can see the metadata that is fetched by setting breakpoints.

If you find any issues or have suggestions, the best way to give feedback is to add them to the Issue Tracker on CodePlex. That way they go directly into Team Foundation Server as work items. Releases and check-ins can be coupled to work items providing you with feedback on how issues are being tackled.

I have published my Flickr Metadata Synchr project on CodePlex that I talked about developing here. You can always find the latest information on the project's Wiki page on CodePlex.

The current version is 0.2.0.0. It's still far from complete, but it already contains some nice features and code that you might want to look into if you are developing a WPF app or an app that wants to connect to Flickr. For instance:

  • Remembering window position and state for an WPF application. Check out my blog entry about this.
  • A managed wrapper around the Select Folder dialog box in shell32.dll (SHBrowseForFolder API) that is more flexible than the standard wrapper FolderBrowserDialog in .NET 2.0 and exposes more of the underlying API.
  • A visually styled declaration for a WPF application, so you don't get an old-fashioned look for things like a FileOpenDialog on Windows XP and Vista. This requires you to embed a custom manifest in the executable. This proved to be a bit more challenging than just calling "EnableVisualStyles()" in your Main method as you can do in a WinForms 2.0 application. Check out this article which explains the visual styling problem.

The nice thing about developing on CodePlex is that you have access to a lot of the power of a hosted Team Foundation Server. You can potentially develop your project from any machine with Visual Studio 2005 and an Internet connection. Of course it also allows a team of people to work on the same application. At the moment it is just me.

The downside of developing on CodePlex is that the whole world can watch your code and all of your check-ins as you go. So you automatically become a bit more careful. In other words, it is not as quick as hacking up a closed source application just for yourself.

Here is a screenshot of version 0.2.0.0 of my app:

1 Comment

[Update 2007-02-24: My Flickr Metadata Synchr project is now live on CodePlex. I will keep the Wiki page on CodePlex updated and will not further update this blog post. If you are interested in the progress, you can subscribe to the Flickr Metadata Synchr project's RSS feed.]

I blogged previously on my digital workflow for photographs here and here. In that last post I described how I violated the golden rule of metadata. Now, I am thinking of writing an application that will fix this for me. I already thought of a name: the Flickr Metadata Synchr. Once I get a skeleton version of the app up-and-running, you will be able to view this project on CodePlex. If I fail miserably, the project will never show up 😉

Here is what I am planning of building:

Name of the application

Flickr Metadata Synchr

Purpose

To synchronize relevant metadata added to images stored on Flickr with the original versions of those images stored locally on your hard drive.

Relevant metadata on Flickr is:

  • Title
  • Description
  • Tags
  • GPS location info

The synchronization will be one-way in the first release. The embedded metadata in EXIF, IPTC and XMP sections in locally stored images can be updated with the metadata of matching pictures on Flickr.

Functionality

  • Ability to authenticate the user with Flickr, i.e., obtain a user token to let the app have read permissions for the user on Flickr.
  • Ability to select a folder with pictures on a local hard disk or network drive. Optionally include subfolders. These pictures will be called the local set.
  • Ability to select a set of pictures on Flickr. This set should belong to the authenticated user. Or the ability to select all pictures of the user tagged with a certain keyword. These pictures will be called the Flickr set.
  • Ability to let the app match pictures between the local and the Flickr set. Match will be done on “date captured”.
  • Ability for the user to cancel the matching process which might be time-consuming.
  • Ability to show the user the match that was made by the application to give the user the opportunity to review for any matching errors.
  • Ability to show the relevant metadata for pictures in both the local and the Flickr set.
  • Ability for the user to deselect matched image pairs that he or she does not want to process.
  • Ability to update the matched local images with metadata from Flickr.
  • Ability for the user to cancel the updating process which might be time-consuming.

Possible future functionality

  • Cache metadata retrieved from Flickr between app restarts to improve performance. Ask the user if it is okay to not update the data. If the user has made significant updates on Flickr, caching will cause more harm than good.
  • Ability to sync Flickr metadata the other way around. Read metadata from local images and update the metadata of matching pictures on Flickr.

Dependencies

Development tools

1 Comment

The December 2006 CTP of the Web Service Software Factory (WSSF) from Microsoft P&P ships with a 2.0 version of the Microsoft Enterprise Library. The Data Accesss factory inside Service Factory generates code which uses the Data Access Application Block from Enterprise Library.

This interface of the Data Access block hasn't changed significantly in the January 2007 CTP of version 3.0 of the Enterprise Library. I wondered if I could use this version of EntLib together with Service Factory.

It turns out that this is really easy. The Service Factory uses a registry key to locate the EntLib binaries. This key is HKEY_LOCAL_MACHINESOFTWAREMicrosoftpatterns and practicesService FactoryEntlibBinaryPath.

It has a default value of C:Program FilesMicrosoft Service FactoryEnterprise Library Binaries. After you have installed EntLib 3.0, change the value to the location of the new binaries. For the January 2007 CTP the default location is C:Program FilesMicrosoft Enterprise Library 3.0 - January 2007 CTPBin.

If you create repository classes in your DataAccess project using the "Create data repository classes from business entities" recipe, the project will reference and use the 3.0 binaries.

On a WPF app we had the need for the application to remember its window position and state (maximized, minimized) across restarts. A short journey on the Internet led me to an interesting article by TimK on CodeProject on how to do this.


As you may know, Windows Presentation Foundation makes heavy use of XAML. As it turns out XAML also allows you to mix-in behavior with your classes without using code. That is a productive way of reusing behavior. Behind the screens the mix-in class uses WPF magic as DependencyProperties to wire things up.


The article contains a WindowSettings class that stores and restores the window position and state. I fixed one bug in this WindowSettings class from TimK. Without the fix an app would restore maximized on the wrong display if it was maximized on a secondary display.


You can find the C# code for our WindowSettings class attached to this post. For your reading convenience I have also copied the code below.


To use it in your WPF window defined in XAML, do something like:

<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="LogicaCMG.Client.MainWindow"
Title="Test Window" Height="700" Width="880"
xmlns:settings="clr-namespace:LogicaCMG.Settings"
settings:WindowSettings.Save="True"
>

Code of the WindowSettings class:

using System;
using System.Diagnostics;
using System.Globalization;
using System.ComponentModel;
using System.Configuration;
using System.Windows;
using System.Windows.Markup;

namespace LogicaCMG.Settings
{
/// <summary>
/// Persists a Window's Size, Location and WindowState to UserScopeSettings
/// </summary>
public class WindowSettings
{
#region WindowApplicationSettings Helper Class
public class WindowApplicationSettings : ApplicationSettingsBase
{
private WindowSettings windowSettings;

public WindowApplicationSettings(WindowSettings windowSettings)
: base(windowSettings.window.PersistId.ToString())
{
this.windowSettings = windowSettings;
}

[UserScopedSetting]
public Rect Location
{
get
{
if (this["Location"] != null)
{
return ((Rect)this["Location"]);
}
return Rect.Empty;
}
set
{
this["Location"] = value;
}
}

[UserScopedSetting]
public WindowState WindowState
{
get
{
if (this["WindowState"] != null)
{
return (WindowState)this["WindowState"];
}
return WindowState.Normal;
}
set
{
this["WindowState"] = value;
}
}

}
#endregion

#region Constructor
private Window window = null;

public WindowSettings(Window window)
{
this.window = window;
}

#endregion

#region Attached "Save" Property Implementation
/// <summary>
/// Register the "Save" attached property and the "OnSaveInvalidated" callback
/// </summary>
public static readonly DependencyProperty SaveProperty
= DependencyProperty.RegisterAttached("Save", typeof(bool), typeof(WindowSettings),
new FrameworkPropertyMetadata(new PropertyChangedCallback(OnSaveInvalidated)));

public static void SetSave(DependencyObject dependencyObject, bool enabled)
{
dependencyObject.SetValue(SaveProperty, enabled);
}

/// <summary>
/// Called when Save is changed on an object.
/// </summary>
private static void OnSaveInvalidated(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
{
Window window = dependencyObject as Window;
if (window != null)
{
if ((bool)e.NewValue)
{
WindowSettings settings = new WindowSettings(window);
settings.Attach();
}
}
}

#endregion

#region Protected Methods
/// <summary>
/// Load the Window Size Location and State from the settings object
/// </summary>
protected virtual void LoadWindowState()
{
this.Settings.Reload();
if (this.Settings.Location != Rect.Empty)
{
this.window.Left = this.Settings.Location.Left;
this.window.Top = this.Settings.Location.Top;
this.window.Width = this.Settings.Location.Width;
this.window.Height = this.Settings.Location.Height;
}

if (this.Settings.WindowState != WindowState.Maximized)
{
this.window.WindowState = this.Settings.WindowState;
}
}

/// <summary>
/// Save the Window Size, Location and State to the settings object
/// </summary>
protected virtual void SaveWindowState()
{
this.Settings.WindowState = this.window.WindowState;
this.Settings.Location = this.window.RestoreBounds;
this.Settings.Save();
}
#endregion

#region Private Methods

private void Attach()
{
if (this.window != null)
{
this.window.Closing += new CancelEventHandler(window_Closing);
this.window.Initialized += new EventHandler(window_Initialized);
this.window.Loaded += new RoutedEventHandler(window_Loaded);
}
}

private void window_Loaded(object sender, RoutedEventArgs e)
{
if (this.Settings.WindowState == WindowState.Maximized)
{
this.window.WindowState = this.Settings.WindowState;
}
}

private void window_Initialized(object sender, EventArgs e)
{
LoadWindowState();
}

private void window_Closing(object sender, CancelEventArgs e)
{
SaveWindowState();
}
#endregion

#region Settings Property Implementation
private WindowApplicationSettings windowApplicationSettings = null;

protected virtual WindowApplicationSettings CreateWindowApplicationSettingsInstance()
{
return new WindowApplicationSettings(this);
}

[Browsable(false)]
public WindowApplicationSettings Settings
{
get
{
if (windowApplicationSettings == null)
{
this.windowApplicationSettings = CreateWindowApplicationSettingsInstance();
}
return this.windowApplicationSettings;
}
}
#endregion
}
}

1 Comment

 


I am still looking for a good solution for my digital workflow for pictures on Windows Vista. I want to be able to easily tag my pictures locally on my hard-drive and on Flickr and keep the metadata in sync.


Let me describe the state of affairs.


Metadata in images


With metadata I mean the info I add manually to a picture, like the title, author, description, tags and GPS info. A digital camera already adds lots of other data to your pictures like aperture, camera model, if the flash fired or not, etc. This info is usually stored in an EXIF section in the JPEG file. For other metadata there are several options: EXIF, IPTC of XMP. IPTC is an older standard. XMP is more flexible (e.g., it allows Unicode) and modern.


The Golden Rule


This page states the golden rule of metadata:



Store the metadata in your images


Adding metadata to pictures


While in Redmond two weeks ago, I uploaded a subset of the pictures that I took to Flickr. I added the metadata through the site. Flickr does not embed this metadata in the file. If you download the original image file, the title, description and tags are gone. So I basically violated the Golden Rule.


A better option is to add the metadata before you upload your images to Flickr. Locally I can embed the metadata as IPTC or XMP in the JPEGs using a variety of tools. This way your metadata flows with your image to Flickr. If you download it again, the metadata is stil there.


The easiest option to add metadata to your images is to use the Windows Photo Gallery built into Windows Vista. This stores metadata in an XMP section in the image file.


Unfortunately, Flickr only imports metadata from EXIF and IPTC and not from the XMP section. So all the images that you tag in Vista show up untagged on Flickr.


Vista Flickr Uploader


My colleague Matthijs (who still won't reboot his blog, not even to market his own app 😉 has written a Vista Flickr Uploader tool to solve this problem.


His Vista Flickr Uploader app is written in C# and uses .NET 3.0, most notably Windows Presentation Foundation and the Windows Imaging Component. He open sourced it through CodePlex. This tool solves one way of the problem. When uploading your pictures, it extracts XMP info from your pictures and adds it to Flickr as metadata.


Microsoft Photo Info


As an alternative to tagging through Windows Vista Photo Gallery, you might want to try out the Microsoft Photo Info tool. This is a free download from Microsoft that works on both XP and Vista. It integrates into the Windows Explorer. The great thing about this tool is that it can read and write both IPTC and XMP info. So your metadata is recognized by Flickr when you upload your images.


Embedding metadata from Flickr


So Vista Flickr Uploader or Microsoft Photo Info solve one way of the problem: getting metadata from your images onto Flickr. The other problem still remains: getting metadata from Flickr into your images. I found one tool (.NET 2.0 based) that is able to download images from Flickr and embed the Flickr metadata as IPTC. It was quite unstable and wasn't really able to find my images so I won't link to it.


Remaining problem


Another problem that this download tool caused was inconsistency between the IPTC and XMP info in the image files. Imagine the following scenario:



  • Tag your images with XMP info in Vista.

  • Upload them with the Vista Flickr Uploader.

  • Flickr now shows the metadata from the XMP.

  • Change the title, description or tags on the Flickr site.

  • Download the image from Flickr with a tool that embeds the Flickr metadata into IPTC.

You end up with an image file with both IPTC and XMP info. The XMP info is out-of-date, yet it takes precedence in Windows Vista ;(


So I am looking for a tool that downloads pictures from the Flickr site that embeds the Flickr metadata as both IPTC and XMP info or just as XMP. I wasn't able to find such a tool, so I will probably have to write my own app to do this.


[Update 2007-08-31: I have now written such a tool: FlickrMetadataSynchr.]


ExifTool


A possible alternative might be to use a really powerful metadata tool and library written in Perl that allows you to extract and embed metadata in almost all known formats. Phil Harvey's ExifTool can even be used to read ID3 info from MP3 files. I could use it to wipe XMP info from images so the IPTC info is used again by Vista.


The problem with this command line tool is that it works really low-level with the potential of damaging the metadata beyond repair. I.e., other programs refuse to load your images because they can't make sense of the metadata.

1 Comment

Bruce Eckel has an interesting post on hybridizing Java with Flash. He notices the trend in the Java world to replace Java-based GUI frameworks (AWT, Swing, etc.) for Java apps by Flash.

Incidently, I came across one such app yesterday evening when I installed PowerSnap. The UI indeed was somewhat snappy. This application claims it can keep your locally stored photographs in sync with those posted on Flickr.

Of course, the major benefit of using Flash over UI technologies as WPF is reach. WPF only runs on a subset of the Windows platforms. PowerSnap is working on a Mac version which should be relatively easy because Java and Flash are available on the Mac.

WPF/E is nice and will be cross-platform but I think it will be confined to the browser in v1. Currently the WPF/E engine can only be scripted using JavaScript.

Maybe in future versions it can be used for the GUI layer of a standalone cross-platform .NET application. Microsoft is quietly working on versions of the CLR that will run on other platforms so that WPF/E can be "scripted" using C#. The work done by the .NET Compact Framework team already proves that this is possible. This CLR runs on devices like SmartPhones, PDAs and the Xbox 360.

Microsoft has hastily released the February 2007 CTP of the Windows Presentation Foundation Everywhere runtime. The previous CTP expired prematurely.

WPF/E is the "light" version of WPF that will run in all major browsers across several platforms (including non-Windows platform). For instance, it also works in Mozilla Firefox. Think of it as the Microsoft competitor to Flash.

WPF/E is also good at displaying video with a light-weight video engine that does not depend on Windows Media Player. Again, comparable to Flash Video, but now using the WMV format.

I noticed that the speed of the WPF/E runtime has increased significantly when compared to the December 2006 CTP.

The WPF/E samples in the Channel 9 Playground have been updated. Go check them out if you are interested in WPF/E. If you don't have WPF/E installed, your browser should prompt you on how to install the runtime.