6.2 Methods
6.2.2 Phage orientation function
Microscopy experiments of two kinds were undertaken. Five image stacks recording the response to flow of M13-aCol-TRITC attached to collagen IV coated slides were obtained. Only one image stack for M13-WGA-TRITC attached to a GEnCs coated flow slide was collected. These image stacks were converted to AVI files using the program Fiji [167]. All subsequent data processing was performed in MATLAB (MATLAB R2014b, The MathWorks Inc., Natick, MA) with code written for the purpose.
The first step was to read in to MATLAB the .avi file containing the microscopy image stack. Following this, the number of frames and the frame rate was retrieved from the file, from which the time parameter was calculated.
cellObj = VideoReader(’PhageMicroscopyMovie.avi’); vidFrames = read(cellObj);
numFrames = get(cellObj, ’NumberOfFrames’);
frameRate = get(cellObj, ’FrameRate’);
Time = ((1:numFrames)/frameRate)’;
The method used to distinguish the M13 bacteriophage particles from the background is thresholding, where the image is segmented based on the fact that regions where the M13 is present have considerably higher pixel intensity than where it is absent [168]. To perform this action, the RGB images (from the RGB colour model) need to be converted to grayscale, and MATLAB requires the image intensity values to be converted to double precision format.
for k = 1 : numFrames img(k).gray = rgb2gray(vidFrames(:,:,:,k)); end for k = 1 : numFrames img(k).double = im2double(img(k).gray); end
To display an example image from the greyscale stack, the ‘imshow’ command is used. An example of this is given in Figure 6.2 to show one greyscale image of a fluorescent M13-aCol-TRITC particle. This frame of the image stack is used throughout this section to show the effect of the image processing commands.
figure(1)
Figure 6.2: Example of a greyscale image produced after reading a microcopy image stack in to MATLAB. This image shows a M13-aCol-TRITC particle, anchored to a collagen IV coated flow slide.
The next step was to compute threshold values that will be used to convert the greyscale images to binary images. MATLAB has a global function for this, based on Otsu’s method [169]. Once the threshold value was computed, it was used to binarise the images, so that pixels with an original value greater than the threshold were assigned a value of one, and those below are set a value of zero. A duplicate binary image stack was also created to be used for background subtraction.
level=zeros(numFrames,1); for k=1:numFrames level(k)=graythresh(img(k).double); end for k = 1 : numFrames img(k).binary = im2bw(img(k).double,level(k)); end for k = 1 : numFrames
img(k).binary1 = img(k).binary;
end
After thresholding, the frame of the image stack shown above is now displayed in Figure 6.3.
figure(2)
imshow(img(45).binary1, ’InitialMagnification’,’fit’)
Figure 6.3: The figure shows a product of the thresholding process, using the same image as is shown in Figure 6.2. The white region has a uniform pixel intensity of one, and all other pixels have a value of zero.
At this point the images were binary, with the main region of ones being the M13 bacteriophage complex. This region was then defined as the region of interest by finding and grouping the connected ones in the image. To clean up any residual background regions of ones that made it through the thresholding process, the largest region (the M13) was deleted from the first image stack, and then this whole stack was subtracted from the duplicate made above.
for k = 1 : numFrames regions(k) = bwconncomp(img(k).binary); end for k = 1 : numFrames numPixels = cellfun(@numel,regions(k).PixelIdxList); [biggest,idx] = max(numPixels); img(k).binary(regions(k).PixelIdxListidx) = 0; end for k = 1 : numFrames img(k).subtracted = imsubtract(img(k).binary1,img(k).binary); end
The final piece of image processing that was performed was to find the convex hull of the region, which fits a convex polygon within the M13 bacteriophage region of the binary image. This was included to deal with the potential problem of a group of pixels within the phage body having a pixel intensity below the threshold value (and hence would now have a intensity of zero), and also to help smooth the edges of the phage, as both of these issues may distort the information on the phage orientation and length.
for k = 1 : numFrames
img(k).convhull = bwconvhull(img(k).subtracted);
end
figure(3)
imshow(img(45).convhull,’InitialMagnification’,’fit’)
Figure 6.4: The figure displays and example of the processed images used to obtain orientation and major axis length data. It is once again the same image as was used in Figure 6.2, however this a convex polygon has been fit within the boundaries of this particle, and so the edges are considerably smoother.
The function ‘regionprops’ was used to obtain the M13-aCol-TRITC and M13-WGA-TRITC orientation and major axis length from each image in the stack. The function was used to find the major axis of the M13 complexes by finding the longest straight line that can be drawn within the region of interest, in pixels. The function is then used to find the particle’s orientation by calculating the angle between the major axis and the horizontal, or x axis of the image, in degrees.
for k = 1 : numFrames
Orientation(k).Orientation=regionprops(img(k).convhull,’Orientation’);
for k = 1 : numFrames
Majoraxis(k).Majoraxis=regionprops(img(k).convhull,’MajorAxisLength’);
end
At this point, all of the processed data were stored in structure arrays. To use the data for subsequent calculations more easily, and also to plot it, the relevant data needed to be extracted into vectors. This was done using the ‘extractfield’ commands. Here, the M13 length data was also converted from pixels toµm, which in this case required multiplication by 0.0693.
Angle=zeros(numFrames,1); for k = 1 : numFrames Angle(k)=extractfield(Orientation(k).Orientation,’Orientation’); end Length=zeros(numFrames,1); for k = 1 : numFrames Length(k)=extractfield(Majoraxis(k).Majoraxis,’MajorAxisLength’); end LengthNM = Length*0.0693;
The next problem that must be dealt with was to find a way of defining the times at which the flow was applied to the M13-aCol-TRITC, and when it was turned off. The defining characteristic of the flow being on is that the movement of the particle is restricted, and the variance in its angle relative to the horizontal axis is reduced. It is this property that we used to distinguish between the flow being on or off. First, a moving standard deviation of the orientation data was calculated. The minima of this vector corresponds to the times at which flow was applied. The
’findpeaks’ function finds the local maxima of a vector, and so we simply calculated the inverse of the moving standard deviation vector and used this function to find the times at when flow is applied. These data were also smoothed prior to using the findpeaks function to ensure the maxima were found precisely.
MovStanDev = movingstd(Angle,51,’c’); InverseSmooth=-1*MovStanDev; SmoothedData = smooth(InverseSmooth,0.1,’rloess’); findthepeaks=findpeaks(SmoothedData, ’MINPEAKHEIGHT’, -30); for k=1:length(findthepeaks) indvalues(k)=find(SmoothedData==findthepeaks(k)); end
The smoothed moving standard deviation data is displayed in Figure 6.5. It shows that there are nine peaks, and the modulus of their maximum values are trending to an increased magnitude as time increases, which tells us that the flow rates being applied are getting progressively weaker.
figure(4);
plot(Time, SmoothedData) xlabel(’Time / s’)
0 100 200 300 400 500 600 700 800 900 1000 Time / s -60 -55 -50 -45 -40 -35 -30 -25 -20 -15 -10 Smoothed Data
Figure 6.5: The inverse moving standard deviation of the orientation data, obtained from one of the microscopy data sets using M13-aCol-TRITC bound to a collagen IV coated flow slide. The data has been smoothed so that the maxima are better defined.
The maxima values from Figure 6.5 were then used to set a time frame over which flow was applied. Twenty seconds after the maxima and ten seconds prior was selected, so to allow the M13 bacteriophage complexes some time to settle following the application of shear flow. The average orientation and major axis length of the M13 particles over these time frames were calculated, in addition to the standard deviation of these properties.
LOW=49; HIGH=101; indvalues=[indvalues, numFrames-(HIGH+5)]; for k=1:length(indvalues) StdAngle(k) = std(Angle(indvalues(k)-LOW:indvalues(k)+HIGH)); end for k=1:length(indvalues)
StdLength(k) = std(LengthNM(indvalues(k)-LOW:indvalues(k)+HIGH)); end for k=1:length(indvalues) Average_Angle(k) = mean(Angle(indvalues(k)-LOW:indvalues(k)+HIGH)); end for k=1:length(indvalues) Average_Length(k) = mean(LengthNM(indvalues(k)-LOW:indvalues(k)+HIGH)); end for k=1:length(indvalues) TimeBars(k)=Time(ceil((indvalues(k)-LOW+indvalues(k)+HIGH)/2)); end findthepeaks=findpeaks(SmoothedData, ’MINPEAKHEIGHT’, -30); for k=1:length(findthepeaks) indvalues(k)=find(SmoothedData==findthepeaks(k)); end
Finally, the processed data were grouped and exported to text files, so that they can be analysed and plotted in R [170].
DataMatrix=cat(2,Time,Angle,LengthNM);
header = ’Time’, ’Angle’, ’Length’;
RawDataOut = dataset(DataMatrix,header:);
export(RawDataOut,’file’,’RawDataOut.txt’,’Delimiter’,’,’)
DataMatrix=cat(2,TimeBars’,Average_Angle’,StdAngle’,Average_Length’,... StdLength’); ProHeader = (’TimeBars’,’AverageAngle’,’StdAngle’,’AverageLength’,... ’StdLength’); ProDataOut = dataset(DataMatrix,ProHeader:); export(ProDataOut,’file’,’ProDataOut.txt’,’Delimiter’,’,’)
% To export ’processed’ data to a text file