CHAPTER
14
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>
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
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