Making the project

By far the easiest way to work on the sequencer is to use Qt Designer as the IDE and use the command-line utilities from MS Visual Studio for the actual compilation. Open the project grabber.pro, set all the project information (include paths, required libraries etc.) in the Project Options dialogue window from within the Qt Designer IDE, then go to a DOS console and type:

qmake -o Makefile grabber.pro
nmake

The other option is to generate a MS Visual Studio project from the qmake project file grabber.pro. This way, you will have to set all the project-relevant information (include paths, libraries, preprocessor flags etc) in the project file itself - through the Qt Designer GUI, or by editing grabber.pro by hand. You want to create a clean workspace from within the Visual C IDE (say, grabber.dsw, with a project grabber.dsp), then issue the following command:

qmake -t vcapp -o grabber.dsp grabber.pro

This will create an MS VC project which you can open from within the Visual Studio environment. Of course, every time something changes in the project or in the Qt-dependent classes, the qmake command will have to be re-run.

Using the grabber class

grabber.h, grabber.cpp

The grabber class serves as the interface to the framegrabber. Although it does all the dirty work internally, some of its variables have to be set explicitly, depending on the application. In general, you will want to set the number of frames to grab, the number of buffers in the memory for storing temporary images, and the path to the camera files (on the KOGS windows PCs used for grabbing, this should be in c:/genesis/dcf/free running/dxd5013hs_slow_freerunning_nobinning.dcf). After the internal variables have been set, you have to call grabber::init(). This function will allocate all the required memory and initialise the framegrabber. After calling init(), you can start grabbing.

Here is an example of a simple program which takes one image:

#include <grabber.h>

void main()
{
unsigned char *image;
Grabber grabber;

grabber.setNumFrames( 1 );
grabber.setNumBuffers( 1 );
grabber.setDcfFilename( "c:/genesis/dcf/free running/dxd5013hs_slow_freerunning_nobinning.dcf" );
grabber.init();

image = grabber.grabSingleImage(); // image now points to the grabbed grayscale image

...

}

If you want to grab a sequence, it will be automatically written to two raw files on two separate disks, for performance reasons. This is the normal way the grabber class is used within the sequencer. In this case, you probably want to initialise the two disk drives (D and E are the defaults) and a basic filename for the raw files. After this, one uses the grabber::grabSequence() function instead of grabber::grabSingleImage(), but the rest is the same. The main.cpp file in the sequencer project implements sequence grabbing.

A quick example for storing the raw data from 1024 frames into two raw files is shown below. The setDeleteRaw function sets a flag which determines whether the destructor will delete the raw files afterwards (for example, if we have already processed the raw files and turned them into separate images, like in the sequencer program).

#include <grabber.h>

void main()
{
grabber Grabber;

Grabber.setNumFrames( 1024 ); // 1024 frames
Grabber.setNumBuffers( 144 ); // 144 buffers in memory (maximum, to keep it smooth)
Grabber.setDcfFilename( "c:/genesis/dcf/free running/dxd5013hs_slow_freerunning_nobinning.dcf" );
Grabber.setOutputFilename( "out" );
Grabber.setFirstDrive( "d" );
Grabber.setSecondDrive( "e" );
Grabber.setDeleteRaw( false ); // Leave the files after the program finishes

Grabber.init();

Grabber.grabSequence(); // Now there will be two raw files on D: and E: with the data

...

}

The grabber class is a Q_OBJECT, and as such needs the Qt library to be present. The good point is that it can generate signals and have slots. Throughout the grabbing sequence, it will generate the grabber::updateProgress( int ) signal, which can be connected to other objects via slots. In the sequencer, this is used to control the progress dialogue.

All open files will be closed by the destructor. It should be possible to implement a function which holds the whole sequence in the buffer and can return the pointer to an arbitrary frame, but it hasn't been implemented yet.

Image export - the filesplitter class

filesplitter.h, filesplitter.cpp

The filesplitter class operates on the raw files which the grabber class produces. Basically, it is a class which can take a large file, and split it into smaller chunks. The sequencer uses it to get single images out of the raw files, operate on them, and export them as finished image files.

The information it needs is the filename, and the width and height of the images. These can be set through the constructor, or by setting the individual variables manually and calling fileSplitter::init(). After initialisation, the individual images can be obtained by calling fileSplitter::currentImage(). Example code:

#include <filesplitter.h>

void main()
{
fileSplitter spl;
unsigned char *image;

...

spl.setFilename( "output1.raw" );
spl.setX( 1024 );
spl.setY( 768 );
spl.init(); // Open the file and initialise all pointers...

image = spl.currentImage(); // Get the first image...
image = spl.currentImage(); // Get the second image...
image = spl.currentImage(); // ...and third...
...

}

All open files will be automatically closed by the destructor. The sequencer uses two filesplitters, one for the odd images an one for the even ones (because they are stored in two different files). Maybe it would be more elegant to store the filesplitter class inside the grabber class, and provide seamless access to all the individual images from within the grabber class in the future.

Colour generation

The colour generation part of the sequencer is based on the code written by Yildirim Karal, which was only adapted to use QImage instead of Vigra. This code can be found in avgcolor.h and bayer2color.h. The code in avgcolor.h calculates the colour coefficients from a white rectangle in the image, and the code in bayer2color.h generates a 24-bit QImage from the greyscale bayer images using those coefficients. The original code can be found in ~karal/sa/src/.

Other than these classes and functions, the rest of the sequencer is mostly dialogue windows and user interaction which doesn't really need much explanation. Maybe just a word on how the colour calibration works (the code for this is inside advdia.ui.h in the function AdvDialog::calibrate() ). A grabber class is initialised to only take one image (1 frame and only one buffer). It first takes a black image (lens should be covered), and then an image containing a large white area, by calling calibGrabber->grabSingleImage().

This second image is given to the calibration dialog (calibdia.h, calibdia.ui.h), which scales it, displays it and prompts the user to select a white rectangle (the code for this is inside calibdia.ui.h). From these four coordinates, we can calculate the colour coefficients (by calling avgColor(...) from avgcolor.h).


Kasim Terzic, 21.10.2003.