A utility for simple printf-style
debugging of images in Win32 C/C++ applications.
"The Image Debugger" is a programmer's utility to
make debugging of Win32 applications that use images and grid data
easier. Examples of such applications include 3D games and
visualization applications, as well as grid-based numerical PDE
simulation codes. "The Image Debugger" could even conceivably be used to
debug dense matrices. The code is for Windows only, and actually
probably only works on Win NT or higher.
Often when creating graphical applications you have a chunk of memory
that you know is supposed to look some certain way. Unfortunately
debuggers don't typically have a way to view that memory as an image,
and so it's a real pain to figure out whether the image is correct or
not. That's where "The Image Debugger" comes in. The
typical solution -- if looking at the raw numbers in a debugger is not
sufficient -- is to write a bit of code that will dump the data to a
file, which can then be looked at using something like Matlab, or if you already have a
graphical app, you can write some code to splat some images up on the
screen on top of the actual graphical output you're trying to generate.
Either way it takes some effort. And often it's a real
hassle if you're working with double-buffered graphics, because if you
want to see drawing as it's happening you have to switch to drawing on
the front buffer, and even then if you're stopped at a breakpoint in the
debugger and your window gets partially covered, you lose the image.
With "The Image Debugger" your data pops up in a separate window with
none of those problems and with just a single line of code.
That's all it takes! You can go from a bare bones "hello world"
console application to visual output with just one line of code.
Basically you can think of this utility as a "printf" for images. So
aside from debugging images, one thing you could use imdebug for, for
example, would be as a really quick-and-dirty front end for a ray
tracer. You write the ray-tracer, then you can make one call to
imdebug to show the results.
But debugging is the primary purpose for imdebug. To debug a simple
variable, x, one way is to toss a 'printf("%d\n", x) '
at a strategic location in your code. With The Image Debugger if
you have an 128x128 RGB image in img, you can simply
"printf" it with a call like 'imdebug("rgb w=128 h=128 %p", img) '
. For more details see the Usage section
below.
Here's a screen capture of The Image Debugger's
output viewer application. The image you're viewing was a
randomly-generated RGBA image. You can see from the gray and white
checker board background that The Image Debugger understands and
handles the alpha channel if present. Here for illustrative
purposes we're looking at a pretty extreme zoom of the data.
Probably more zoom than you'd ever want or need to do with real
data.
These next few annotated screen shots explain some of the status
messages and UI elements.
First download either the src
or bin distribution. Then to install, just unzip the files into a
temp directory. Then copy these files into the following
locations:
FILE
|
INSTALL
LOCATION
|
imdebug.h |
Put someplace where your compiler
knows to look for include files. |
imdebug.lib |
Put someplace where your compiler
knows to look for library files. |
imdebug.dll
imdbgdisplay.exe |
Put someplace on your path, or in
the same dir as the app you're debugging.
|
If you download the src package you'll find precompiled versions of the
binaries in the "lib" directory.
That's all there is to installation!
There are two parts to usage: calling imdebug from
your code, and using the displayer to look at the output from your
application.
1. Using The Image Debugger in your code
The first thing to do is put this in the file you want to debug:
#include <imdebug.h>
The next step is to call it. The one function exported from the
imdebug DLL has this prototype:
void imdebug(const char *format, ...);
That doesn't tell you a whole lot. But basically that's the same
argument list as printf(): a format string followed by a variable number
of arguments. The operation of imdebug is similar to printf.
The format string contains all the information about how to parse
the rest of the arguments passed to the function. The format
string is basically a snippet of code in a mini-language that I
devised.
Before going into the format syntax ins and outs, let's just take a
look at a few usage examples (from testapp.cpp distributed in the source
zip above). Here are the declarations of the images we're going
to try to display:
unsigned char testRGB[16*17*3]; // 16x17 3 8-bit channels unsigned char testRGBA[16*17*4]; // 16x17 4 8-bit channels unsigned short testRGB16[16*17*3];// 16x17 3 16-bit channels float testRGBf[16*17*3]; // 16x17 3 32-bit float channels unsigned char testBigRGBA[512*512*4]; // 512x512 4 8-bit chan
Now here's a bunch of different examples of ways to call imdebug.
Basically the minimum requirement is that you specify the width
and height of your image, how many channels it has, and how many bits
are in each channel.
The first example does just that:
imdebug("rgb w=%d h=%d %p", 16, 17, testRGB);
The 'rgb' says this is a 3 channel, RGB format image. You
could also say 'bgr' if that's the order of your images channels.
8 bits per channel is assumed if nothing is specified. The
'w=' and 'h=' statements tell imdebug the width and height of the
image. The actual values could be entered directly in the format
string, like 'w=16 h=17' but it's also possible to use the '%d'
specifier to tell imdebug to get the value from an input argument
instead. Finally the '%p' indicates the image pointer argument.
Here's another example
imdebug("rgb rbga=__gg w=%d h=%d %p", 16, 17, testRGB);
This is just like the last one exept for the swizzling statement
"rbga=__gg". This tells imdebug to remap the input channels when
displaying the image. An '_' indicates a channel should be
discarded. In this case we see the green input value will be used
both as an alpha channel and as the green channel, while red and blue
channels will just be ignored. Note that that's "rBGa" not
"rGBa". Originally when I typed it I made a typo, but
Imdebug can interpret the statement either way. If I had typed the
latter, then imdebug would map the green input to the blue and alpha
channels instead of green and alpha.
To specify the bit-depth of your channels use the 'b=' statement:
imdebug("rgb rbga=gggg b=32f w=%d h=%d %p", 16, 17, testRGBf);
The 'f' indicates floating point values instead of integer values.
Using '*auto' tells imdebug to autoscale (and bias) the values in the
floating point image to a [0,1) range for output. Useful if you're
looking at non-image floating point data which isn't in the [0,1)
range:
imdebug("rgb rbga=gggg *auto b=32f w=%d h=%d %p", 16, 17, testRGBf);
Here are some other examples:
imdebug("rgb rbga=__gg b=16 w=%d h=%d %p", 16, 17, testRGB16); imdebug("rgb rbga=gggg w=%d h=%d %p", 16, 17, testRGB); imdebug("lum b=32f w=%d h=%d %p", 16*3, 17, testRGBf); imdebug("rgba w=%d h=%d %p", 512, 512, testBigRGBA);
And finally here's the syntax description from the imdebug.h.
It's not necessarily all 100% correct or 100% tested. If
something doesn't work let me know.
VARIABLE SPEC:
%d - int
%f - float
%s - string
%p - picture (pointer to raw image data)
INPUT FORMAT SPEC:
rgb
bgr
abgr
rgba... - specify input image channel order
lum - 1-channel image:
lumninance
luma - 2-channel image: lumninance
+ alpha
#7 - Generic 7-channel
image
---> (default is rgb)
b=8 - size of all channels
is 8 bits
b=5,6,5 - size of channels is 5bits(R) 6bits(G)
5bits(B)
b=32f - size of all channels is 32 bits,
float format
---> (default is b=8)
OUTPUT FORMAT SPEC:
rgba=rg__ - just display the red and green chanels
rgba=aaa_ - display alpha as grayscale
lum=g - display green as grayscale
rgba=#0512 - map channels by number
---> (default is 1-1 mapping with no translation or swizzling)
ATTRIBUTE SPEC:
w=23 - width is 23 (default
0)
h=17 - height is 17 (default 0)
rs=1 - skip 1 row after every
row (default 0)
cs=2 - skip 2 columns after every
column (default 0)
t='img2' - title to put in displayer titlebar
SCALE AND BIAS:
*1.2 - scale by 1.2
/1.2 - scale by 1/1.2
+128 - bias by 128
-0.5 - bias by -0.5
*auto - automatically scale & bias
based on max & min values
--> Default is scale=1 and bias=0
--> Output value is computed by first scaling, then biasing,
i.e.
out =
(in*scale)+bias,
the same order as with OpenGL
glPixelTransfer functions.
Order of specifiers is mostly not important, but channel
swizzeling should come after input format specifier.
(i.e. do "rgb bgr=rgb", not "bgr=rgb rgb")
If no image is specified (with '%p'), then the previous
image data is used.
If you have installed the header file and .lib file where your compiler
can find them, then compiling your app with imdebug should be just like
compiling without. You shouldn't have to do anything special to
get it to link. The header file uses a special "#pragma comment"
to tell the linker to link with the DLL, so you don't have to add it
explicitly to your link command. (Recent versions of GLUT use
this technique too).
Using imdebug with OpenGL
Here's an example of some utility functions for looking at OpenGL
textures using imdebug: imdebuggl.h. This
header defines a couple of functions that you can call like this:
dumpTexture{f}(myTextureID);
with a valid OpenGL texture ID, to spit out that texture using imdebug.
Mark Harris
put together his own imdebug wrapper functions for OpenGL that
may be a bit more complete than mine: mh_imdebuggl.h.
The main imdebug function in this header is :
imdebugTexImage{f}(target,myTextureID,level,format).
Mark's functions have a few more arguments to allow you to dump
different mipmap levels, and to override the default imdebug
format string, etc. He also provides functions that read from
the frame buffer then dump those pixels to the imdebug window.
Both of the example GL headers above are C++. Also both
will give you multiply defined symbols if you include them in more than
one source file. But you're smart. You'll figure that out.
2. Using the Output Viewer
When you call the imdebug() function, the
imdebug.dll launches the output viewer, imdbgdisplay.exe (show in the
screenshots above) and sends it a copy of the image data that you
passed in as the '%p' argument. Once the output viewer app has
the data, the app can make whatever changes it wants; the viewer's local
copy will not change. (For the curious, this is inter-process
communication is accomplished using a memory-mapped file).
Repeated calls to imdebug() cause the viewer to buffer up the images.
You can use the GUI to navigate and inspect the 10 most recently
output images.
The main GUI functions are as follows:
Mouse drag |
translate image around |
Mouse wheel |
zoom image in and out |
Ctrl+Mouse wheel |
cycle back and forth in the history buffer |
PgUp PgDn
Alt+Left Alt+Right |
also for navigating back and forth in history buffer |
G |
toggle the grid lines on and off |
F |
toggle flipping in the Y direction |
B
|
toggle background type (solid gray or checkerboard) |
Ctrl+I |
Block new images coming from the app (ignore
output from app) |
-
|
zoom out
|
=
|
zoom in
|
/ |
rescale image to fit in window |
I had to leave a few bugs in to keep life
interesting. There are also many features that could make things
even more groovy.
Top on the list are:
Known Bugs
- Save As... doesn't work
- The
row stride and column stride settings (rs=,cs=) need work. I
think they're currently broken. You'll have to use densely packed data
for now.
- Can't handle non-native endian data. For 8-bit
channels, of course you can just call your RGBA image AGBR and you're
set. But for 16-, 32-, and 64- bit channels there's trouble.
- I'm probably not handling the distinction between signed
and unsigned data types correctly.
Desiderata
- The
ability to control the channel selection and swizzling from the GUI
would be handy.
- A way to change to other backgrounds in the viewer.
Either provide a larger selection of built-in backgrounds or a way
to load an external image to use as a background. Often that
checkboard background can look way too much like the data you're trying
to see.
- Allow OpenGL texture IDs as the %p argument and have the
imdebug() function automatically snarf the data for you with an
apropriate glGetTexImage. Maybe same thing for glReadPixels too
so you can imdebug() the usually non-visible depth and stencil
buffers easily.
- A new version of Visual C++ that just has this sort of
image viewing built in!
Apr 4, 2003
|
Version 0.954b.
Fixed "alpha" on "channels" menu. Now alpha displays as grayscale.
|
Apr 2, 2003
|
Version 0.953b.
Added a "channels" menu, basically to allow turning off the alpha
channel interactively. If alpha is all 0, you can't see the other
data, which is annoying. Of course you can specify "rgba=rgb_" in
your format string, but you'd like to be able to get the same effect on
the fly, and now you can.
|
Mar 7, 2003
|
Version 0.952b.
Fixed aligment of images in the window when "flip" is checked.
Fixed auto-scaling of images that are all one color.
|
Feb 23, 2003
|
Version 0.951b.
Added option to automatically copy the scale & bias of a tagged
image to the next image with the same tag. Currently it's overridden by
any specific scale or bias set on the image itself using the format
string.
|
Feb 18, 2003
|
Version 0.95b.
Added button to set scale and bias back to identity.
Changed "Ready" message to give info on image (resolution, bit depth,
etc.)
|
Feb 17, 2003
|
Version 0.942b.
Fixed parsing of bias syntax in format string.
Added some UI to allow interactive munging of scale and bias.
|
Jan 26, 2003
|
Version 0.94b.
-Added somewhat meaningful error status messages for some cases.
-Increased memory mapped file limit be able to handle RGBA images of
32f 2048x2048.
-Added dialog to change number of images saved in history buffer.
-Fixed bug with flipped images not getting the status correct.
-Fixed error trying to allocate negative memory when window made too
small.
-Made it so status message isn't set to "Ready" so often when it could
say something useful.
-Made status message indicate which values are mapped to which output
channels.
-Added option for reusing/sharing the display window so you don't end
up with like 23 "Image Debugger Display" windows hanging around all the
time.
-Added t='label' to format string so you can give images intelligible
labels that will make it easier to distinguish which of 10 all black
images is the one you're looking for.
-Added big red "BLOCK" status indicator to let you know when the
displayer is not listening (in case you accidentally hit Ctrl-I or
something)
|
Sept 19, 2002
|
Version 0.931b.
I think I fixed the clipboard Edit->Copy problem. Maybe.
It worked for me, anyway, though I can't say why for sure.
|
Sept 15, 2002
|
Version 0.93b.
Added state save and restore using windows registry. (remebers window
position and checked menu items)
|
Sept 13, 2002
|
Version 0.921b.
Reports errors when imdbgdisplay not found.
Added alt-left and alt-right keys for navigating images.
|
Sept 11, 2002
|
Version 0.92b.
Added support for packed bit formats like 5,6,5. Will work as
long as the total bits are a multiple of 8, which most real formats are.
Added support for independent scaling and biasing on a per-chanel
basis, like OpenGL allows.
|
Sept 10, 2002 |
Version 0.91b.
Added the '#8' type format specifications for dealing with data having
more than 4 channels. Just something that I had forgotten to
include in the parser. |
Sept 9, 2002 |
Version 0.9b
First release. |
Thanks to Jeffrey Richter and Charles Petzold,
without whose books (this
one and that
one) there's no way I could have figured out how to do this (i.e.
how write a straight Win32 C program that uses memory mapped files to
communicate inter-process data). Thanks also to Willem van Schaik
who wrote "VisualPNG". The image display code in
imdbgdisplay.exe was based initally off of his source code. More info
at Willem's website here.
Thanks to the person over at http://www.codeproject.com
(I think!) whose comment about an image debugger got me thinking about
this idea. I got excited because when I saw him use "image" and
"debug" in the same sentence, the first thought I had was that he had
written something like the utility I ended up writing. It turned
out, though, that his program was of a somewhat more limited scope.
I'm not sure what his name was, and I haven't been able to find
the link again since then.
Thanks to Andrew Z. whose one followup to my post on unccs.graphics was
all the convincing I needed to go and write this, and thanks to Mark
Harris for his copious and useful suggestions.
The Image Debugger version 0.952b can be downloaded
from the links below:
- imdebug.h - header to #include in your
app
- imdebug.dll - dll to link with your app
(note this happens automatically)
- imdebug.lib - export library necessary
for compiling your app agains the dll.
- imdbgdisplay.exe - the output viewer
application. (Launched automatically when you call
imdebug() )
testapp.exe - a very basic test program
made with OpenGL/GLUT that spits out a few images in its display
function and draws a lame-looking teapot in its GL window. You may
need to grab the
GLUT dlls to get it to run.
- Binaries + full source code: imdebug-0.954b-src.zip
This has everything the binary only distribution has, plus all the
source code. It also includes project files for MSVC++ 6.0, as
well as a makefile that works with Microsoft's NMAKE.
The Image Debugger was developed by William Baxter. I'm
currently a PhD student at the University
of North Carolina at Chapel Hill working on graphics
and interactive techniques. If you're interested, have a
look at the
other projects I've worked on.
If you find this software useful, or make any improvements, please let
me know. If you have patches you'd like to contribute by all means
please send 'em in!
<EMAIL ADDRESS> You can contact me by by sending email to my last
name @cs.unc.edu.
Happy Image Debugging!
|