Programmatically Create Deep Zoom Collections

The Expression team have released a new version of Deep Zoom Composer, and at last they’ve supplied a .NET assembly for creating Deep Zoom images. This has always been possible, using the SparseImageTool.exe that was supplied as part of Composer, but that was never a very good solution, particularly for large collections of images.

We have a potential use case where we’d want to create millions of deep zoom images, and expose these as collections, but without necessarily knowing which images were in a collection ahead of time. Or we might want to add new images to a large collection. Until now, that has always been impossible just using SparseImageTool, but using the new DLL it’s easy. Here’s some sample code which I just threw together to see how the API worked. You need two classes. The ImageCreator class creates individual Deep Zoom images, which was always easy with SparseImageTool. But the CollectionCreator can then take these images, and build a composition of all those images.


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using Microsoft.DeepZoomTools;
using Path = System.IO.Path;

namespace DZCtest
{
    /// <summary>
    /// Interaction logic for Window1.xaml
    /// </summary>
    public partial class Window1 : Window
    {
        public Window1()
        {
            InitializeComponent();
        }

        private void Create_Click(object sender, RoutedEventArgs e)
        {
            ImageCreator creator = new ImageCreator();
            creator.TileFormat = ImageFormat.Jpg;
            creator.TileOverlap = 1;
            creator.TileSize = 256;

            List<string> files = new List<string>()
            {
                @"D:\Users\Pictures\DadsArmy\hi000350807.jpg",
                @"D:\Users\Pictures\DadsArmy\hi000350820.jpg",
                @"D:\Users\Pictures\DadsArmy\hi000360009.jpg"
            };
            string root = @"D:\Users\James\Visual Studio 2008\Projects\DeepZoomProjects\DeepZoomTests_Web\ClientBin\GeneratedImages\";
            List<string> dzi = new List<string>();
            foreach (var name in files)
            {
                string output = Path.Combine(root, Path.GetFileNameWithoutExtension(name) + ".dzi");
                dzi.Add(output);
                creator.Create(name, output);
            }

            CollectionCreator ccreator = new CollectionCreator();
            ccreator.TileFormat = ImageFormat.Jpg;
            ccreator.TileOverlap = 1;
            ccreator.TileSize = 256;
            ccreator.Create(dzi, Path.Combine(root, "da.dzc"));
        }
    }
}

You’ll have to add a Reference to DeepZoomTools.dll which is installed as part of Deep Zoom Composer. All this code does is take three hardcoded image paths, generates individual deep zoom images for them, then creates an overall collection of all three.

One thing there doesn’t seem to be is any way to lay out the collection other than having all the images on top of one another, but since any code which makes use of a collection would probably handle its own layout in code, this probably isn’t much of a drawback. And there might well another way to do that – I’ve literally only just thrown this code together, so I haven’t delved very far into what’s exposed.

It’ll be interesting to see what the performance is like. In a perfect world, I’d be able to do a search across millions of images and dynamically generate a collection of a few hundred images in response to a user request, but that might be asking a bit too much.

UPDATE: 9 minutes to process 305 images. 20 seconds to build the collection from those images. 20 seconds is probably a bit more than you’d want for a completely dynamic search, for example, but it’s probably absolutely fine if you’re uploading images to a more static collection like a picture album. 2 seconds to process a typical digital camera image is really rather good. Next test will be some of the really large images that DZC choked on last time I tried it.

19 comments

  1. Hi, I have got the above code to work but how do I use it in my deepzoom test page? The “DeepZoomProjectTestPage.htm” file that I have points to a XAP file, so how do I modify the page to use the files that the above code has generated?

    Thanks in advance
    Andy

  2. The actual image is hard coded in the xap file. You’ll need the source code for the xap file which you get if you get Deep Zoom Composer to output as a project.

    There’s two steps you’ll need – you need to copy the collection files you created into the GeneratedImages folder in the DeepZoomProjectWeb – let’s say the main collection file is called ‘collection.dzc’ (in my sample code it was ‘da.dzc’).

    Next, in the silverlight project, find the MultiScaleImage tag in the Page.xaml file and change the Source attribute from

    Source=”../GeneratedImages/dzc_output.xml”

    to

    Source=”../GeneratedImages/collection.dzc”

    Then if you rebuild the project, it should now show your new images.

    Hope this helps.

    Jim.

  3. I dont want the images to be in the default GeneratedImages but in a user specified directory. See- I want to be able to upload different deepzoom collections for different users and let them see their own collection by (lets say) selecting themselves from a list within deepzoom.

    Is this possible.

    1. My example code just has a hard-coded path, but a proper app would use a SaveFileDialog so you could set the root to anywhere. Silverlight certainly doesn’t require using a GeneratedImages folder at all – all it requires when you’re using the images is the Url of the metadata file (in my example, the .dzi or .dzc file). You can serve this from anywhere, and you can set it programatically in code like this:

      image.Source = new DeepZoomImageTileSource(new Uri(“deepimages/myimage.dzi”, UriKind.Relative));

  4. THANKS A LOT!
    This helps a lot. From some other forum I saw that one HAS to put this directory in the ClientBin. I then tried to for instance put it in the c:\temp\ folder and point the source to there but it doesnt work. Do you have any idea as to how I can set a path which is NOT in the ClientBin?

    Thanks so much for the help.

    1. I find that the usual problem is in the way that Silverlight handles URLs. On the web, it’s possible to fetch data or images from any part of your domain by starting the URL with / but it’s different with Silverlight (presumably for security reasons). A URL like ‘/images/img1.jpg’ should, in normal Web terms, fetch from http://www.yourdomain.com/images/img1.jpg but in Silverlight, if your app is down the ClientBin directory, it will look for http://www.yourdomain.com/ClientBin/images/img1.jpg.

      The solution to this is to use an Absolute URL with a domain. In XAML this would be:

      In code it would be:

      image.Source = new DeepZoomImageTileSource(new Uri(”http://yourdomain.com/deepimages/myimage.dzi”, UriKind.Absolute));

      Now, if you want to generate URLs on the fly rather than hand-code them like that then you can, and it’s only tricky if you don’t know ahead of time what your domain name is. Which, when testing a Silverlight app, is quite common because Visual Studio generates a random port number when you’re testing, so you have to be able to cope with that. You can get the URL of the current application like this:

      string appurl = App.Current.Host.Source.ToString();

      And you can to extract the hostname from that.

  5. I try to create a collection dynamically with DeepZoomTools.DLL but how I can create the content for the dzc_output_files?

    1. The individual deep zoom files that composer usually puts in dzc_output_files are created by these lones in the example code:

      foreach (var name in files)
      {
      string output = Path.Combine(root, Path.GetFileNameWithoutExtension(name) + “.dzi”);
      dzi.Add(output);
      creator.Create(name, output);
      }

      and the collection creator takes all the generated dzi files and makes a collection out of them. They don’t have to be in any specific directory – that’s just simpler for DeepZoom Composer to put them there.

      1. Thanks for your reply, I don’t know why it didn’t work, but now it works without changing the code.

        The main problem is that when I need to change one image I need to elaborate again all images in order to update the dzc_output_files content.
        It seems there is no way to solve it.

        Thanks
        Lorenzo

  6. HI – me again 🙂

    You add great value to the world of DeepZoom 🙂
    I wanted to ask…

    The MDI uses MultiScaleSubImage(s) which holds the image… is there ANY way to overwrite this, so that I can make the msi use a multiscalesubimage WITH an extra canvas ontop (which would then move and scale WITH the subimage)?

    IF you have time for this 🙂
    Thanks
    Marthinus

    1. Not precisely – the MultiScaleImage as a whole is a single UI Element, and you can’t insert other elements ‘inside’ it. What you can do is overlay a canvas or other panel over the whole image and lock its scale and offset to the deep zoom image. I’ve done that before, so I’ve posted some sample code that does it.

      I’ve never tried to lock overlays to individual subimages myself, though, although I’ve written code to do the calculations – have a look at this post.

  7. Hi there

    I read an article in MSDN about Taking Silverlight to the next level.

    I have a question. I have a scenario where I have different versions of a multi-image. What I mean by this is I have, say 3 images (as version one). People can then upload a second version of the 3 images (as version two) and so on.

    For each version I use the DeepZoomTools.dll to create a seperate dzc_output.xml file to serve as the source for the msi.

    What I now need to do is when someone wants to see version 3… version 2 and 1 also needs to show (in order to compare the images). So I needs to merge the 2 different dzc_output.xml files into one to have a source for the msi.

    I wondered if there is no way to add 3 different dzc_output.xml files into the msi.source by overwriting the MultiScaleTileSource’s GetTileLayers?

    Is this at all possible or will I have to write a NEW xml (containing all the info of the seperate 3 XML’s) and then go msi.sources = newXml.xml?

    Is there any examples of this?

    1. I’m not quite sure I understand what you need to acheive here. Are you talking about collections? And you want the two collections merged? If that’s the case, then DeepZoomTools will do what you want – you could merge all the separate images into a single collection, since two collections can share the same images if you want to.

      However, if you’re talking about single images, and you want to see both, you could do it simply by overlaying two MultiScaleImage elements and using Opacity with them. I don’t think writing your own tile source would be necessary if what you’re trying to do is merge multiple images.

  8. what is the best way to stitch or in Deep Zoom Composer terms “Compose” images into a grid placing them side by side? Like in your example the images will have to align into a side by side row of 3 columns.
    Thanks,
    Val

    1. I haven’t tried it, but I doubt it is possible as it’s not a Silverlight DLL and probably references assemblies that Silverlight doesn’t contain.

Leave a reply to Val Cancel reply