In this project I use a version of the EditableBitmap class by Justin Dunlap, also published on codeproject.com. Justin's class solved a big performance problem for me, as his idea allowed me to render image tiles 400% faster than is possible with the usual Graphics.DrawImage technique. Thank you, Justin!
This code project was inspired by an interview with Jose Fajardo. In the video, Jose demonstrates his idea that "all documents on the web can be made DeepZoom-able". In my company, we have a regular R&D "free play day", and on my scheduled day, I decided to try out Jose's concept for our document management software. Our software stores scans as TIFF or JPEG images, and I decided to try and make those "DeepZoom-able".
Jose uses a command line tool that is shipped with Microsoft's DeepZoom Composer to convert his documents, but in my experience, relying on a command line tool is not a very stable solution for a server environment, especially if you would want to convert the massive batches of scans that our DMS typically contains. A second reason to use a different approach is that the collection feature of the current DeepZoom Composer beta (June 2008 version) is a bit buggy. If you mix images having different DPI values in one DeepZoom collection, the Composer will render image positions inconsistently. Typically, our customers will mix 200 dpi and 300 dpi, portrait and landscape pages in one scan batch, and I could not get such a DeepZzoom collection rendered correctly by the DeepZoom Composer.
Thus, the idea was born: I wanted to generate a DeepZoom collection, using only my own managed C# code, and not relying on any external tools or non-standard assemblies, to ensure maximum portability of the code. My R&D-project is a simple Windows Forms application, but I strictly separated the user interface and business logic code to allow future code reuse in a server application.
DeepZoom is a popular part of Microsoft's new Silverlight environment. It is a really cool progressive rendering system that allows to -very quickly- zoom in and out by large percentages, using a similar technique as is used in Google Maps.
Like Google Maps, DeepZoom uses multiple image tile sets that are stored in a wide range of zoom percentages. DeepZoom's tiles are always 256 x 256 pixels, and each lower zoom level is scaled down by a factor 2 compared to the level above, until you reach level 0 that is always a 1x1 pixel image. More information is available in this article by Jaime Rodriguez.
There are two distinct DeepZoom image types: DeepZoom composition and DeepZoom collection. A composition is one big image tile set that contains all image data; a collection can be seen as a set of compositions that is combined in one page.
Although a collection looks very similar to a composition in the viewer, it has the added benefit that it is possible to replace one image in the collection without having to recreate image tiles that belong to adjoining images in the composition. Also, the concept of a collection is more suitable to page-oriented image data like document scan batches.
Components of a DeepZoom collection
A DeepZoom collection consists of the following parts:
- A root folder, containing the Silverlight project binary that hosts the DeepZoom control. Usually, the root folder is the Silverlight ClientBin subdirectory of your web application.
- A folder that stores the generated images (tiles). By default, this is the subdirectory GeneratedImages of the root folder.
- For a collection, the DeepZoom Composer places three XML data files in the GeneratedImages folder:
- SparseImageDataSceneGraph.xml: This file contains a description of the image scene, but that description does not appear to be used by the current version of the viewer (remember: everything is still beta for DeepZoom, at least until October, 2008).
- Metadata.xml: This file contains exactly the same data as the Scene Graph XML file, in almost (but not quite) identical format.
- dzc_output.xml: This is the data file that is actually used by the viewer to render a DeepZoom collection.
Unfortunately, an important part of it -the Viewport coordinate system - is currently not documented in the official DeepZoom XSD schema.
- The GeneratedImages/dzc_output_images folder contains a DeepZoom composition tile set for every image in your DeepZoom collection.
- The GeneratedImages/dzc_output_files folder contains a set of thumbnail tiles for the collection. The format and lay-out of these thumbnail tiles is documented here.
Using the code
The code project is a Windows Forms application that is intended to demonstrate the use of the
Decos.DeepZoom.GenerateDeepZoom class. To see what it does, download and try out the compiled runtime (.NET 2.0 assembly). Use the file open button to select the supplied "sample.tif" multi-page TIFF file, or any image file that you like (keep "Scans" selected for TIFF images or "Other image files" for JPG, BMP, GIF, or PNG), then click the [Generate] button. When processing is complete, a window of your default browser should open and display the result in the Silverlight 2 beta 2 version of the DeepZoom viewer. If you do not have Silverlight 2 beta 2 yet, the web page will prompt you to "Get Silverlight".
Location of the generated images
The DeepZoom images are generated in a subdirectory DecosDeepZoom below your %TEMP% path. In this directory, you'll also find copies of DeepZoomProject.xap and DeepZoomProjectTestPage.html, that are linked to the executable assembly as embedded resources. Those two files are a slightly modified version of the test site as generated by the DeepZoom Composer (fixed a small bug in mouse handling and made the control scalable). The Silverlight project itself is also included in the source code download.
Startup code and the main entry point of GenerateDeepZoom
m_gdz = new GenerateDeepZoom();
In the startup code,
CreateTestPage() is responsible for copying the embedded resources, and sets the output path.
m_gdz is the
GenerateDeepZoom object that will do the real work later on.
private void btnGenerate_Click(object sender, EventArgs e)
if (m_gdz.GenerateFromScanFile(txtFilePath.Text, m_sDeepZoomImagesPath))
MessageBox.Show("DeepZoom image generation failed");
The main entry point of the DeepZoom file generator is called from the Generate button click handler. We pass a path to the input scan file and a path to the directory where the output files must be created.
ShowOutput is a simple function that Shell-executes the test HTML page to open it in your browser.
Generation process overview
clsGenerateDeepZoom.cs contains both the public
Decos.DeepZoom.GenerateDeepZoom class and a non-public class
PageBitmap encapsulates the logic to create a DeepZoom image tile set for one page. In the
GenerateFromScanFile function, you'll find a local
alBitmaps, that is used to hold all
PageBitmap objects in the image set.
The generation process follows these steps:
- Read in page bitmaps and create a
PageBitmap object for each page.
PageBitmap.CreateDeepZoomImage for each page to create its full DeepZoom image tile set.
- Create collection thumbnails (call
- Create root XML files describing the collection (call
Color representation and image quality
As our software predominantly handles large quantities of black and white scans, I was interested in the new PNG feature that was introduced in Silverlight 2 beta 2 DeepZoom. Previously, only JPEG tiles could be used, not very suitable for B&W text. Unfortunately, you can only set the "jpg"/"png" property for the entire collection and not per image. My solution was to default a new collection to black and white/png, and fall back to color/jpg when I find at least one color image while reading in scan pages.
To maximize performance, the software uses only 15 bits to represent color, which is OK for "business graphics", but not very suitable for photos. To change this, go to the
Decos.DeepZoom.GenerateDeepZoom.CreateTiles method and change this line:
using (EditableBitmap bmScaled =
new EditableBitmap(bm, PixelFormat.Format16bppRgb555,
iWidth, iHeight, bSmoothScaling))
Use a pixel format having more bits per pixel (e.g.,
Format24bppRgb), to improve color rendering.
Similarly, the down-scaling method used is fast but fairly low quality. The only "smoothing" used is
PixelOffsetMode.Half, that is the most basic anti-aliasing method available in
System.Drawing. If you do not mind the performance reduction, you can improve quality by editing the constructor in EditableBitmap.cs. Look for this code:
g.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.Half;
and replace by something like this:
g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
g.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.HighQuality;
g.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality;
g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
Obviously, if you set everything to "high quality", rendering will be much slower. In particular, the
InterpolationMode.HighQualityBicubic has a big impact on performance (but also visibly improves the result).
Large image rendering test
To do an extreme rendering test, I downloaded a very large NASA image, a Mars panorama that has the extreme JPEG image size of 12,756 by 3487 pixels (40 MB download). It took DecosDeepZoom 15 seconds to convert that on my PC (2.66 GHz Intel Core processor). Interestingly, after conversion, I could zoom in on the DeepZoom image much sooner after loading, and pan around easier and more fluently than I could when I opened the original image using Windows Picture and Fax Viewer, a nice demonstration of the strength of the MultiScaleImage concept . If you compare the two images, you will notice that the color quality is slightly less in the DeepZoom image (due to my use of only 15bpp), but that the resolution did not suffer at all:
Points of interest
The Morton numbers used to determine lay-out of the collection thumbnails (documentation) were an interesting mathematical puzzle. If you want to learn more, this Wikipedia page might be a good start .
html tag is why:
- June 29, 2008: First version created
- June 30, 2008, July 1, 2008: Minor updates.
- July 9, 2008: Improved support of large PNG source images, allows to set JPEG quality and bits per pixel in UI.
- July 12, 2007:
CreateCollectionThumbnails rewritten to support large thumbnail sets.