Description

To better illustrate the differences in cell movement between different strains, we have visualized the capability of cell motility by displaying cell trajectories directly on images.


Toward this goal, firstly, the recorded images of cyanobacteria through time-lapse microscopy need to be processed to get the center positions of each cell at each frame, and to track them by linking their positions at different time points. This was done using a developed program from a published study (Sci. Adv. 10, eadd9485 (2024)). Secondly, after getting the positions of tracked cells, we wrote a new python program (AddTraj2Img.py) to draw a mark (a circle used in our case) at each cell position on the current displaying frame for a period from the beginning of the recording to the time point of the current frame. The detailed description of the program AddTraj2Img.py is below:


- Function of AddTraj2Img.py: Program used to add cyanobacterial trajectories on images captured by time-lapse microscopy


- Input data required: The excel data file that contains the positions of tracked cyanobacterial cells; The time-lapse image files used to get the cell-tracking data files.


- Output results: The modified image files that contain marks at the center positions of tracked bacterial cells for a period from the first image (i.e. the earliest one among the whole images) to the current examined image.


Figure 1 shows the flowchart of AddTraj2Img.py. Briefly, the program starts with reading input data to get tracked cell data, then execute a for loop to process each image found under the image-file folder; For each image, read the image file and get the corresponding frame number of the image, then select the cell data that match the frame number from the whole tracked cell data; For each matched cell, get the center positions and set a color value based on its id name, next append the center positions and color values to an accumulation array and draw circles at the center positions with corresponding colors; Up to now, only the positions of cells at the current frame are marked by circles. If the current frame is the first image among the whole image files, then just save the modified image (the original image overlayed by circles drawn at the center positions of tracked cells) and continue to process the next frame. If the current frame is not the first image, then the cell positions in those images that are recorded before the current frame need also to be drawn, which is then followed by saving the modified image and continue the for loop. After the end of for loop, all the images under the image-file folder will be processed and modified images will be saved. Finally, modified images can be converted to an video using software such as ImageJ to realize the visualization of bacterial movement.


Figure 1 The flowchart of AddTraj2Img.py


Results

Original movies (played at a 50x speed):


An example of WT movement


An example of PilO mutant movement


Movies with cell trajectories marked (played at a 50x speed):


An example of WT movement


An example of PilO mutant movement

 

 

 

Code

import os

import xlrd

import numpy as np

import matplotlib.pyplot as plt

import cv2 as cv

import pandas as pd

 

directory_img = r"D:\python_code\datafolder\P16Simg"#image directory

imgfile_prefix = r"P16S-20fps"#prefix of image file name

saveimg_directory = r"D:\python_code\datafolder\resultdata" #directory used to save processed images

datafile_loc = r"D:\python_code\datafolder/P16S.xlsx" #the path of data file, which store x,y,frame,id

#read data file to get the coordinates of center of mass, and frame number

get_alldata =  pd.read_excel(datafile_loc,sheet_name='Sheet1',header=0)

frame_only = get_alldata.iloc[:,2::4].values #get the frame column, first is 2, then every 4 columns get one, to avoid the same number in x,y

#print(type(frame_only))

sum_data_forplot = [[0,0,(0,0,0)]] #array used to save the center position and color accumulated from each frame

#read image file to add the center locations

frame_count = 0

for filename in os.listdir(directory_img):

    print(filename)

    frame_count = frame_count+1

    framenumber = filename[len(imgfile_prefix):len(filename)-1-3]#get the framenumber of this image,-3 is to exclude the file extension

    #print(framenumber) #in excel file, the frame number is started from 1

    img = cv.imread(directory_img+"/"+filename)

    #next need to find the location of cells in this frame

    get_ff_frame = np.nonzero(frame_only == int(framenumber)+1) #get the line number for the  specific frame number

    #note frame_only only constains frame columns, so the line number would be correct for get_alldata

    #the column number need convert by *4+2, refer to iloc[:,2::4]

    #print(get_ff_frame)

    #print(type(get_ff_frame))

    org_index=(get_ff_frame[0],get_ff_frame[1]*4+2)

    org_index_arr=np.transpose(np.array(org_index))    

    for eachcell in org_index_arr:

        #print(eachcell)

        cmposition = (get_alldata.iloc[[int(eachcell[0])],[int(eachcell[1])-2]].values[0][0],get_alldata.iloc[[int(eachcell[0])],[int(eachcell[1])-1]].values[0][0])

        #print(cmposition)

        cmcolor_str = get_alldata.iloc[[int(eachcell[0])],[int(eachcell[1])+1]].values[0][0]

        #print(cmcolor_str)

        match int(cmcolor_str[2:]):

            case 1:

                cmcolor = (0,0,255)

            case 2:

                cmcolor = (0,255,0)

            case 3:

                cmcolor = (255,0,0)

            case 4:

                cmcolor = (0,255,255)

            case 5:

                cmcolor = (255,255,0)

            case 6:

                cmcolor = (255,0,255)

            case 7:

                cmcolor = (50,50,200)

            case 8:

                cmcolor = (50,200,50)

            case 9:

                cmcolor = (200,50,50)

            case 10:

                cmcolor = (50,200,200)

            case 11:

                cmcolor = (200,200,50)

            case 12:

                cmcolor = (200,50,200)

            case 13:

                cmcolor = (100,100,150)

            case 14:

                cmcolor = (100,150,100)

            case 15:

                cmcolor = (0,0,0)

            case _:

                cmcolor= (255,255,255)

        #print(cmcolor)

        sum_data_forplot.append([int(cmposition[0]),int(cmposition[1]),cmcolor])

       

        cv.circle(img, (int(cmposition[0]),int(cmposition[1])),1,cmcolor,thickness=1)

    if frame_count >= 2: #indicating that cells have been shown up for >=2 frames

        iparticle = 1

        while iparticle < len(sum_data_forplot) :            

            cv.circle(img, (sum_data_forplot[iparticle][0],sum_data_forplot[iparticle][1]),1,sum_data_forplot[iparticle][2],thickness=1)

            iparticle=iparticle+1

        #print("add trajectory")

    #cv.circle(img,(int(cm_coordi[0][0]),int(cm_coordi[0][1])),10,(0,0,255),thickness = 1)

    #cv.imshow("wt"+framenumber,img)

    #k = cv.waitKey(0) & 0XFF

    #if k == 27: #wait for Esc key

    #    cv.destroyAllWindows()

    #else:

    cv.imwrite(saveimg_directory+"/"+filename,img)

    #    cv.destroyAllWindows()