CHAPTER 14

Experimental Components

 

This chapter discusses the software implementation of experimental components directly related to a protein crystal diffraction experiment, such as image, detector, goniometer.  These software components help achieve better object-oriented design of software for diffraction pattern analysis.  These components do not depend on crystallographic symmetry discussed in Chapter 5.  This chapter does not include those components of a synchrotron beamline.  They will be discussed elsewhere.

 

14.1 Digitized Image

 

In modern protein crystallography, a diffraction pattern is almost always in form of a digitized image.  A digitized image may have various types for the pixel values.  An abstract base class ccl::imageBase is independent of these types and handles some common tasks.  A base class is necessary also because various image processing routines need is common type name.  The template class ccl::image<T> is derived from the base class, and can be used independently.  However, all kinds of proprietary image formats specific to detector manufacture and operation mode etc. are once again derived from ccl::image<T>.  The two sections below discuss the base class and the template class, respectively.  The other sections contain the specific implementation of individual image formats.

 

14.1.1 Abstract base class

 

The abstract base class does not store the actual pixel values, since the pixel type may vary.  It only stores the dimensions of image, and handles some range checking.  Most of the other member functions related to pixel type are pure virtual.

 

14.1.1.1 ccl::imageBase

imageBase(const int = 0, const int = 0);

Constructs an image base with the given horizontal and vertical dimensions.

 

virtual ~imageBase();

Virtual destructor.

 

bool operator!() const;

Returns true if the image has 0 dimensions.

 

bool imageLoaded() const;

Returns true if pixel values are loaded from a file.  Returns false by a newly constructed blank image.

 

int getSizeX() const;

int getSizeY() const;

std::complex<double> getSize() const;

Return the dimensions.

 

bool inside(const size_t, const size_t) const;

bool inside(const int,    const int)    const;

bool inside(const double, const double) const;

bool inside(const std::complex<double>&) const;

Return true if the given coordinates are within the valid range of the image.  All integer types shall range from 1 to the dimension of a specific edge, that is, a 1-offset subscript.  Floating point types shall range from 0.5 to 0.5 pass the dimension.

 

bool outside(const size_t, const size_t) const;

bool outside(const int,    const int)    const;

bool outside(const double, const double) const;

bool outside(const std::complex<double>&) const;

Return true if the given coordinates are outside the valid range of the image.

 

virtual pair<std::complex<double>,

             std::complex<double> >

   activeArea(const int = 0) const;

This virtual member function provides a default implementation of active area of a detector and returns a pair of complexes that contain the coordinates of two corners of the active area.  It returns corners (1, 1) and (x, y), where x and y are the dimensions of the image.  It is expected to be overridden by derived classes to fit the detector characteristic better.  If the derived class does not override this function, the entire image is considered active area.  The optional integer may indicate a specific area of the image, for example, in case of a multi-chip CCD detector.  This function may be moved to detector class in future release.

 

virtual pair<std::complex<double>,

             std::complex<double> >

   activeRadius() const;

This is a default implementation of active radius for a circular detector.  It returns a pair of complexes that contain the coordinates of the center and maximum and minimum radii of the active area, the center of the image (x/2, y/2) and (, 0), where x and y are the dimensions of the image.  The second complex contains outer and inner radii of the active area.  It is expected to be overridden by derived classes to fit the detector characteristic better.  This function may be moved to detector class in future release.

 

virtual bool insideActiveArea(

   const double, const double) const;

virtual bool insideActiveRadius(

   const double, const double) const;

virtual bool insideActive(

   const double, const double) const;

Return true if the given coordinates are inside the active area.  These are default function names that a derived class may provide specific implementations.  The third function may be implemented as a combined checking function.

 

friend ostream &operator<<(ostream&,

                           const imageBase&);

Prints the dimensions of the image.

 

14.1.1.2 cpl.ccl.exp.imageBase

img is an object of cpl.ccl.exp.imageTYP (see below) in this section.  c is a complex.

 

img.is_zero()

Returns true if img has 0 dimensions.

 

img.nonzero()

Returns true if none of the dimensions are zero.

 

img.imageLoaded()

Returns true if pixel values are loaded from a file.

 

img.getSizeX()

img.getSizeY()

Return the dimensions.

 

img.getSize()

Return the dimensions in a complex.

 

img.inside(x, y)

img.inside(c)

Return true if the given coordinates are within the valid range of the image.  x and y can be either integer or float.

 

img.outside(x, y)

img.outside(c)

Return true if the given coordinates are outside the valid range of the image.  x and y can be either integer or float.

 

img.activeArea(chip = 0)

Returns a tuple of two complexes being corners (1, 1) and (x, y), where x and y are the dimensions of the image.

 

img.activeRadius()

Returns a tuple of two complexes being the center of the image (x/2, y/2) and (, 0), where x and y are the dimensions of the image.

 

img.insideActiveArea(x, y)

img.insideActiveRadius(x, y)

img.insideActive(x, y)

Return true if the given coordinates are inside the active area.

 

img.__str__()

Prints the dimensions of the image.

 

14.1.2 Template classes ccl::image<T> and cpl.ccl.exp.imageTYP

 

A digitized image is essentially a 2-dimensional array of type T.  Each pixel is associated with an uncertainty value s.  If type T is of some integer types, the pixel value can be used as an index of a lookup table.

 

14.1.2.1 ccl::image<T>

Constructors and destructor

image(const int = 0, const int = 0);

The general constructor makes an image of the specified dimensions, horizontal and vertical.  The default constructor returns a 0-dimension image.  The newly constructed image contains 0 at all pixels.  The standard deviation s of every pixel is set to 1.  No lookup table is configured.

 

~image();

The destructor releases the memory once occupied by the image, its s-image, and the lookup table.

 

image(const image&);

The copy constructor returns a new image identical to the given one.

 

Assignment and conversion

const image<T> &operator=(const image<T>&);

The assignment operator destroys the current image, and copies the given image to itself.

 

operator ccl::image<float>() const;

Returns an image<float>.  The image value is converted to float, and its s-image is copied.  Lookup table is set to 0-length.

 

operator ccl::image<unsigned short>() const;

Returns an image<unsigned short>.  The maximum and minimum values in the original image are mapped to 65535 and 0.  The s-image is scaled accordingly.  Lookup table is set to 0-length.

 

Supported formats

static const std::string

   format_ESRFImageIntensifiedCCD;

static const std::string

   format_FujiImagingPlate;

static const std::string

   format_KodakStoragePhosphor;

static const std::string

   format_MAR3450Packed;

static const std::string

   format_MARCCD165;

These strings are defined as the names of the supported formats.

 

static std::string ESRFImageIntensifiedCCD();

static std::string FujiImagingPlate();

static std::string KodakStoragePhosphor();

static std::string MAR3450Packed();

static std::string MARCCD165();

Return the names of the supported formats.  The purpose of these functions are mostly due to the inability of SWIG to expose static const.

 

std::string getFormat();

Returns the format of this image.  The format is only set by the derived class of the proprietary image classes.  If it is not set, an empty string will be returned.

 

Intensity

double intensity(const int, const int) const;

Returns the intensity of the image at the given coordinates if inside the image, or 0 if outside the image and an error will be generated.  The returned value is always double.  It can be directly from the image array if no lookup table available.  Or it is a value through the lookup table.

 

double intensity(const double,

                 const double) const;

double intensity(

   const std::complex<double>) const;

Return the intensity of the image at the given coordinates if inside the image, or 0 and generate an error if outside.  See 14.1.1.5 for definition of the valid range.  The returned value is an interpolation between the nearest integer coordinates.  The interpolation has a discontinuity at 0.5 past an integer.  It is a good idea to avoid such coordinates and use coordinates such as 1/3, 2/3; 1/5, 2/5, 3/5, 4/5 past an integer.  Same as the other function intensity(), the lookup table is used if it is available.

 

double operator()(const int, const int) const;

double operator()(

   const std::complex<double>) const;

The operator () returns intensity at the given coordinates too.  See above for detail.

 

Uncertainty of the intensity

double sigmaIntensity(

   const int,    const int)    const;

double sigmaIntensity(

   const double, const double) const;

double sigmaIntensity(

   const std::complex<double>) const;

Return the uncertainty of the intensity at the given coordinates.  The given coordinates is also range-checked.  If out of range, 0 is returned and error is generated.  If the coordinates are floating point, the nearest integer coordinates are used.  No interpolation will occur.

 

Setting or modifying the image

T& operator()(const int, const int);              

T& setPixel(const int, const int);

The non-const version of the operator () and the function setPixel() return modifiable references to the pixel values at the given coordinates.  The returned reference can be used as an l-value.  The lookup table, if available, will not be altered in any way by using these functions.

 

void setPixel(const int, const int, const T);

Sets the pixel value at the given coordinates to the specified value.  Again, the lookup table will not modified.

 

Setting or modifying the uncertainty

These functions do not change the outcome of imageLoaded().

 

float& setSigma(const int, const int);            

Returns a modifiable reference to the s value at the given coordinates.  The returned reference can be an l-value.

 

void setSigma(const int, const int, const T);

Sets the s value at the given coordinates to the specified value.

 

Output

void write(ofstream*) const;

Given a ready-to-use ofstream, this function writes the image to it.  The saved values are not through lookup table even if it is available.  The s-image and the lookup table are not saved.

 

#include <assert.h>

#include <math.h>

#include <mac/utility.h>

#include <ccl/image.h>

 

main()

{

   typedef ccl::image<> IMG;

 

   IMG img;       // Default constructor

   assert (!img); // Null image

   assert (img.getFormat() == "");

   assert (!img.imageLoaded());

 

   const int x = 100;

   const int y = 60;

   img = IMG(x, y);

   assert (img.getSizeX() == x);

   assert (img.getSizeY() == y);

   assert (img.inside(1, 1));

   assert (img.inside(0.5, 0.5));

   assert (img.outside(std::complex<double>()));

   cout << img << endl;

   for (int i = 1; i <= x; i++)

   for (int j = 1; j <= y; j++)

   {

      if (fmod(i, 3) == 0) img(i, j) = i * j;

      if (fmod(i, 3) == 1) img.setPixel(i, j, i * j);

      if (fmod(i, 3) == 2) img.setPixel(i, j) = i * j;

   }

 

   for (int i = 1; i < x; i++)

   for (int j = 1; j < y; j++)

   for (int k = 0; k < 5; k++)

   for (int l = 0; l < 5; l++)

   {

      const double u = i + k / 5.;

      const double v = j + l / 5.;

      assert (mac::approx(img(std::complex<double>(u, v)),

                          img.intensity(u, v)));

   }

 

 

   return 0;

}

 

Listing 14.1.2.1.1 Testing ccl::imageBase and ccl::image<T>.

 

14.1.2.2 cpl.ccl.exp.imageTYP

Three shadow classes imageFLT, imageSIN, imageSUI are instantiated in the current release.  Other types may be added easily in the future releases.  In this section, img is an image.  See 14.1.2.1 for detail.

 

Constructors

imageTYP(x = 0, y = 0)

Returns an image of the specified size.

 

imageTYP(img)

Returns a new image identical to img.

 

Conversion

img.to_imageFLT()

Returns an imageFLT.  The image value is converted to float, and its s-image is copied.  Lookup table is set to 0-length.

 

img.to_imageSUI()

Returns an imageSUI.  The maximum and minimum values in the original image are mapped to 65535 and 0.  The s-image is scaled accordingly.  Lookup table is set to 0-length.

 

Supported formats

imageTYP_ESRFImageIntensifiedCCD()

imageTYP_FujiImagingPlate()

imageTYP_KodakStoragePhosphor()

imageTYP_MAR3450Packed()

imageTYP_MARCCD165()

These static functions return the names of the supported formats.

 

img.getFormat()

Returns the format of the image img.  The format is only set by the derived class of the proprietary image classes.  If it is not set, an empty string will be returned.

 

Intensity

img.intensity(x, y)

img.intensity(c)

img(x, y)

img(c)

Return intensity of the image at the given coordinates if inside the image, or 0 if outside the image and an error will be generated.  The coordinates x and y can be integers or floats.  c is a complex.

 

Uncertainty of the intensity

img.sigmaIntensity(x, y)

img.sigmaIntensity(c)

Returns uncertainty of the intensity at the given coordinates if inside the valid range.  The coordinates x and y can be integers or floats.  c is a complex.

 

Setting or modifying the image and its uncertainty

img.setPixel(x, y, z)

Sets the pixel value at the given coordinates x, y to the specified value z.  x, y must be integers inside the valid range.

 

img.setSigma(x, y, z)

Sets the s value at the given coordinates x, y to the specified value z.  x, y must be integers inside the valid range.

 

#!/usr/local/bin/python

 

import cpl

 

IMG = cpl.ccl.exp.imageSUI

 

img = IMG()          # Default constructor

assert img.is_zero() # Null image

assert img.getFormat() == ""

assert not img.imageLoaded()

 

x = 100

y = 60

img = IMG(x, y)

assert img.getSizeX() == x

assert img.getSizeY() == y

assert img.inside(1, 1)

assert img.inside(0.5, 0.5)

assert img.outside(complex())

 

print img.activeArea()

print img.getSize()

print img

for i in range(1, x + 1):

   for j in range(1, y + 1):

      img.setPixel(i, j, i * j)

 

for i in range(1, x):

   for j in range(1, y):

      for k in range(5):

         for l in range(5):

            u = i + k / 5.

            v = j + l / 5.

            assert cpl.mac.utility.approx(img(complex(u, v)),

                                          img.intensity(u, v))

 

Listing 14.1.2.2.1 Testing cpl.ccl.exp.imageTYP.

 

14.1.3 ESRF Image-intensified CCD

 

The distortion and flood-field corrected image is a simple 16-bit image.

 

14.1.4 Fuji Imaging Plate

 

A logarithmic scale image.

 

14.1.5 Kodak Storage Phosphor

 

This is an obsolete detector, may never be used again.  It is a 16-bit image.

 

14.1.6 MAR 3450 Packed Image

 

A large image with compressed image format.

 

14.1.7 MAR CCD 165

 

This is a CCD detector with a circular active area.  The image format is 16 bits per pixel.  There are 2048 ´ 2048 pixels per image and 4096 bytes of header.  The total file size is 8,392,704 bytes.  A derived class ccl::MARCCD165 inherits from ccl::image<unsigned short>, and it is shadowed into Python as cpl.ccl.exp.MARCCD165.

 

14.1.7.1 Derived class ccl::MARCCD165

MARCCD165(const std::string);

Given a filename with full path, constructs a MAR CCD 165 image, loads the file in, and configures the s-image.  This image does not need lookup table.  The s value of a pixel at x, y is estimated as follow, where x and y are 1-offset subscripts.

 

,

 

where p is the linear dimension of a pixel, 80 mm.  sp is the standard deviation of the point spread function,  mm if i2 + j2 < 22, and  mm if i2 + j2³ 22.

~MARCCD165();

Nothing extra to do in destructor.

 

virtual pair<std::complex<double>,

             std::complex<double> >

   activeRadius() const;

The first complex returned in the pair is the image center.  The second is 1024, that is, the active area is a circular area of radius of half the dimension.

 

virtual bool insideActive(const double,

                          const double) const;

Same as insideActiveRatius().  See 14.1.1.1.

 

#include <assert.h>

#include <string.h>

#include <ccl/image_prop.h>

#include <tui/fileName.h>

 

main()

{

   ccl::MARCCD165 img("BM1_1_001.img", 1);

 

   assert (img.getFormat() == "MARCCD165");

   assert (img.inside(0.5, 0.5));

   assert (img.outside(2049, 2048));

   assert (real(img.activeRadius().second) <= 1024);

   assert (img.intensity(10, 10) == img(10, 10));

   assert (img.intensity(100.3, 100.3) ==

           img(std::complex<double>(100.3, 100.3)));

   img(10, 10) = 1000;

   assert (img.intensity(std::complex<double>(10., 10.)) == 1000);

   assert (img.sigmaIntensity(10, 10) ==

           img.sigmaIntensity(std::complex<double>(10, 10)));

 

   assert (img.getSizeX() == img.getSizeY());

   assert (img.imageLoaded());

 

   ccl::image<float> flt = img;

   fileName<ccl::image<float> > fn1("", "", "", "", "float.img");

   bool fileNameSelected = 1;

   while (fn1.write(&flt, &fileNameSelected, 1));

 

   ccl::image<unsigned short> sht = img;

   fileName<ccl::image<unsigned short> > fn2("", "", "", "", "short.img");

   fileNameSelected = 1;

   while (fn2.write(&sht, &fileNameSelected, 1));

 

   return 0;

}

 

Listing 14.1.7.1.1 Testing ccl::MARCCD165.

 

14.1.7.2 Shadow class cpl.ccl.exp.MARCCD165

MARCCD165(fn)

Constructs a MAR CCD 165 image, and loads the image from the file fn.  The uncertainty image is also constructed.

 

img.activeRadius()

Returns a tuple of two complexes that define the active area.  See 14.1.7.1 for detail.

 

img.insideActive(x, y)

Same as insideActiveRatius().  See 14.1.1.1.

 

#!/usr/bin/python

 

import cpl

 

CCD = cpl.ccl.exp.MARCCD165

img = CCD("BM1_1_001.img")

print img

assert img.getFormat() == "MARCCD165"

assert img.inside(0.5, 0.5)

assert img.outside(2049, 2048)

assert img.activeRadius()[1].real <= 1024

assert img.intensity(10, 10) == img(10, 10)

assert img.sigmaIntensity(10, 10) == 1

assert img.getSizeX() == img.getSizeY()

assert img.imageLoaded()

 

img.setPixel(10, 10, 1000)

assert img(10, 10) == 1000

 

flt = img.to_imageFLT()

assert img.intensity(10, 10) == flt.intensity(10, 10)

 

sht = flt.to_imageSUI()

assert flt.intensity(10, 10) == sht.intensity(10, 10)

 

Listing 14.1.7.2.1 Testing cpl.ccl.exp.MARCCD165.

 

14.1.8 Quantum 4