Minimal 2D Analysis
The following tutorial pertains to reading in a TIFF file containing a image of colloidal particles taken by a confocal microscope and identifying the particle centers in 2D * Created 8-27-07;
Container Classes
There are two main container classes for storing data. PixelArray?2D and PixelArray?3D are designed to store pixel information as either float or boolean. ParticleArray? stores particle positions, centroid data, and moment information. Classes are used, rather than just an array, to allow data to be grouped appropriately and make it easy to include and access data. For example, the PixelArray? classes provide:
- Array for storing pixel infromation
- Variables to hold width, height and/or depth information
- Memory allocation/reallocation routines (resize)
- Routines for printing data to file/screen.
In general, memory for the pixels array is allocated within individual functions.
An instance of the container class is declared as follows:
PixelArray2D <float> *floatData = new PixelArray2D<float>(); PixelArray2D <bool> *boolData = new PixelArray2D<bool>();
Note that the container classes are templated, meaning the type of data they store can be set when the instance is declared. In this case, we used the same class to declare both an instance that stores float data (floatData) and an instance that stores boolean data (boolData).
Reading a TIFF file
Several routines are provided for reading TIFF files. The most common to use in two dimensions is readTiff2Float2D. This accepts three arguments:
- TIFF filename to read in, passed as a character array.
- Class container to store intensity data. This must be a float instance of the PixelArray?2D class.
- Channel to store into the PixelArray?2D class (IP_RED, IP_GREEN, IP_BLUE, or calculate grayscale by averaging r,g,b channels IP_GRAY).
The following code snippet declares an instance of the PixelArray?2D class (floatData), and calls the readTiff2Float2D routine, storing the red channel into the floatData class instance:
PixelArray2D <float> *floatData = new PixelArray2D<float>(); IPGZ::readTiff2Float2D("series2D.011.tiff", floatData, IP_RED);
Apply Filters
To identify the center of the particles, we first must apply a Gaussian Mask to the data. This helps to remove background noise and smooth the data, making the local maximum (center of the particle) more apparent. There are two available Gaussian mask routines in 2D. The first method is essentially "dumb" and should not be used; this routine applies the math without any speed optimization. The second method allows a minimum threshold for calculation to be set; any value less than the minimum threshold is considered to add zero to the mask and is not calculated. At the bare minimum, setting the minimum threshold to 1 will avoid doing any calculations on pixels with value 0, thus recovering the same behavior as the "dumb" routine, but with substantial speed increases. The gaussianMask2D routine takes five arguments:
- Instance of the PixelArray?2D class that holds the "raw" intensity data
- Instance of the PixelArray?2D class where the result of the Gaussian mask will be stored
- The radius of the filter in the width (x) dimension
- The radius of the filter in the height (y) dimension
- The minimum threshold for calculation; calculations are no performed for values less than this.
The following code snippet declares an instance of the PixelArray?2D (floatFilteredData) where the result of the Gaussian mask is stored, and then calls the gaussianMask2D routine:
PixelArray2D <float> *floatFilteredData = new PixelArray2D<float>(); int filter_width = 7; int filter_height = 7; int minimum_threshold = 1; IPGZ::gaussianMask2D(floatData, floatFilteredData, filter_width, filter_height, minimum_threshold);
Note that floatData was previously declared and stores the "raw" intensity data from the TIFF image.
Finding particles
Several routines must be employed to locate the center of the particle. These include:
- localMaximum2D, which indentifies the point of maximum intensity in a local region
- eliminateNeighbors2D, which makes sure that there are no local maximum too close together
- calculateCentroid2D, which calculates the centroid and various moments to allow better indentification of the particle center
These routines have been packed into a single routine called findParticles2D. This routine accepts six arguments:
- Instance of the PixelArray?2D class that corresponds to the result of the Gaussian mask
- Instance of the PixelArray?2D class that is of type bool to store whether a pixel is a local maximum or not
- Instance of the ParticleArray? class to store positions, centroids and moments
- Radius of the filter in the width (x) dimension (used as the characteristic dimension for searching for local maxima)
- Radius of the filter in the height (y) dimension (used as the characteristic dimension for searching for local maxima)
- Minimum threshold for the localMaximum2D routine; anything less than this value is automatically excluded as a local maximum
The following code snippet first creates an instance of the PixelArray?2D class of type bool for storing if a pixel is a local maximim, creates an instance of the PositionArray? class for storing particle positions, centroids, and moments, and calls the findParticles2D routine.
PixelArray2D <bool> *boolLocalMaxData = new PixelArray2D<bool>(); ParticleArray <float> *particleLocation = new ParticleArray<float>(); IPGZ::findParticles2D(floatFilteredData, boolLocalMaxData, particleLocation, filter_width, filter_height, 50);
Note that floatFilteredData, filter_width, and filter_height were previously declared. Additionally, the minimum threshold for local maximum determination is set to 50.
Outputting Particle Analysis
There are several ways to view the results of the particle analysis. We can output the number of particles found as an integer value, we can overlay local maxima data on the original image, or we can output the particle positions as rasmol formatted data.
Particles founds
The particleLocation instance of the ParticleArray? class that was passed to findParticles2D had the total number of particles found set into it. This can be access by calling the size() routine within the ParticleArray? class.
std::cout << "Total number of particles identified " << particleLocation -> size() << std::endl;
Creating An Overlay
The basic routine for creating an overlay is writeOverlay2Tiff. This accepts eight arguments:
- Name of the original TIFF file. We will overlay positions on this data
- Name of the new TIFF file to output.
- Instance of the class PixelArray?2D of type bool that stores whether a pixel is a local maximum.
- Amount of the red channel to express for overlay (this corresponds to the color of the pixel for a local maximum).
- Amount of the green channel to express for overlay (this corresponds to the color of the pixel for a local maximum).
- Amount of the blue channel to express for overlay (this corresponds to the color of the pixel for a local maximum).
- Should compression be used when outputing the new TIFF file ( IP_NONE, IP_COMPRESS).
- Output style, either a single pixel (IP_DOT) or a plus sign (IP_PLUS).
The following code snippet overlays the local maxima (colored white) as plus signs on top of the original image:
IPGZ::writeOverlay2Tiff("series2D.011.tiff", "overlay2D.011.tiff", boolLocalMaxData, 255, 255, 255, IP_COMPRESS, IP_PLUS);
Note that boolLocalMaxData was previously declared. Additionally, any particle that is within the edge and the filter radius (e.g. in this example, the filterWidth = 7 and the overally image is of width = 512, thus we won't find any particles within 0-6 pixels or 505-511).
Result of overlay process:
Output rasmol data
There are several ways to output position data as a rasmol formatted data. We previously created an instance of the class ParticleArray called particleLocation. After passing this to the findParticles2D routine, particleLocation now stores the location (in terms of pixels) of the centers of the particles, the centroid values and moments. We could simply output the location of the pixels that correspond to the centers as x,y and z values (width, height, and depth respectively). This can be accomplished by calling the printRasmol routine that is a part of the ParticleArray? class.
std::ofstream rawRasmol("rawRasmol.txt") particleLocation -> printRasmol(rawRasmol);
However, the pixels may not be all of the same dimension; additionally, we calculated the centroid data to allow us to find the center of the particle with sub-pixel resolution. We can still use the printRasmol routine, but also pass it the characteristic dimensions in the width, height, and depth.
std::ofstream correctRasmol("correctRasmol.txt"); particleLocation -> printRasmol(correctRasmol, 0.069022,0.069022, 0.0);
Where 0.069022 corresponds to length scale in both the width and height dimensions.
Overall Code
#include <iostream> #include <IPGZ.h> int main (int argc, char * const argv[]) { PixelArray2D <float> *floatData = new PixelArray2D<float>(); IPGZ::readTiff2Float2D("series2D.011.tiff", floatData, IP_RED); PixelArray2D <float> *floatFilteredData = new PixelArray2D<float>(); int filter_width = 7; int filter_height = 7; int minimum_threshold = 1; IPGZ::gaussianMask2D(floatData, floatFilteredData, filter_width, filter_height, minimum_threshold); PixelArray2D <bool> *boolLocalMaxData = new PixelArray2D<bool>(); ParticleArray <float> *particleLocation = new ParticleArray<float>(); IPGZ::findParticles2D(floatFilteredData, boolLocalMaxData, particleLocation, filter_width, filter_height, 50); std::cout << "Total number of particles identified " << particleLocation -> size() << std::endl; IPGZ::writeOverlay2Tiff("series2D.011.tiff", "overlay2D.011.tiff", boolLocalMaxData, 255, 255, 255, IP_COMPRESS, IP_PLUS); std::ofstream correctRasmol("correctRasmol.txt"); particleLocation -> printRasmol(correctRasmol, 0.069022,0.069022, 0.0); return 0; }
Related Pages
Attachments
-
overlay2D.011.tiff
(84.2 kB) - added by cri
17 months ago.
Overlay image
-
series2D.011.tiff
(99.7 kB) - added by cri
17 months ago.
Original Data
-
correctRasmol.txt
(3.2 kB) - added by cri
17 months ago.
Rasmol data
-
IdentifyParticles2D.cpp
(1.1 kB) - added by cri
17 months ago.
Overall code to identify particles in 2D
-
filtered2D.011.tiff
(82.1 kB) - added by cri
17 months ago.
Data after being filtered by the Gaussian mask