Package: simpledots
Simpledots image analysisDot detection and file management for seqFISH-type experiments.By Lincoln Ombelets, 2019-2020Distributed under the MIT license.ClassesWBImageThis is a simple container class for gracefully dealing with multidimensional images in a standardized way.In our research, we commonly encounter 2, 3, or 4 dimensional images. The first 2 dimensions are just the horizontal and vertical dimensions, and each 2D image is a single snap from the microscope camera. But many snaps are often bundled together with changes in either Z (depth) position or fluorescent channel. Finally, rarely you may have multiple time points or "frames", representing a fifth dimension.WBImagefacilitates dealing with any dimensionality image in roughly the same way, by expanding any image to 5D, adding length 1 dimensions that are missing.The only thing you have to specify is the order of the dimensions of the raw image. You can ascertain this by loading in the image (for example, withskimage.io.imread()) and checking theshape:im=skimage.io.imread('C:\path\to\image.tif')print(im.shape)Generally, this will print out something like (depending on the type of experiment and the software that produced the image)(10, 2048, 2048, 4). You know your own experiments best, so from this information you can usually determine the way Python is naively reading the image. In this case, the order would be Z slices - X - Y - Fluorescent channels. We would specify this with the single-letter shorthandzxyc, which is supplied to the constructor:wb_im=WBImage(im,order='zxyc')print(wb_im.shape)>(4,10,1,2048,2048)The WBImage constructor reordered the axes and inserted a length-1framesaxis to bring the image to the final standard shapeczfxy.
Note that if you do not specify an order, WBImage assumes the image is already in the final standard shape.WBImageinstances can be indexed for reading similarly tondarrays:wb_im[1,4].shape# this is the same as wb_im[1, 4, 0]>(2048,2048)This would give the second channel, fifth Z slice image.The image itself is stored as a reshapedndarrayinwb_im.image.They can also be iterated as if they were flat arrays ofc*z*f2D images, but supply adictwith channel, slice and frame information:forainwb_im:print(a.keys())The keys of the iterator arechannel,slice,frame, andimagecontaining the 2Dx*yimage.Finally, theapplymethod loops through the stack in this manner and applies a given function (with optional keyword arguments) to each 2D image, either returning a newWBImageinstance or modifying the current one in place.deffilter_wbimage(im,sigmas):returnskimage.filters.gaussian(im['image'],sigma=sigmas[im['channel']])wb_im_filtered=wb_im.apply(filter_wbimage,inplace=False,sigmas=[4,2,1,1])The above code would apply a different width Gaussian filter to each channel. The applied function receives the samedictas any iterator, so you can use the channel, slice and frame information in your function.DotImageThis is the main class for dealing with smFISH-like images. Its main functions at present are to perform background subtraction, registration to a reference image, and find and quantify dots. All stages of the image processing are stored asWBImages.ConstructorSimilar toWBImage, the only required arguments to the constructor are an image (filename or preloadedndarray) and a string indicating the axes order of the raw image. However, the full constructor looks like this:def__init__(self,source,order,desc='',exclude_channels=None,hybcycle=0,position=0,ref_image=None,z_use=[0],):sourceandorderare the required arguments.desc,hybcycle,positionare metadata that you can add optionally.exclude_channelslets you indicate which channelsnotto process with background subtraction, dot finding etc. Typically the final channel in our images is DAPI, a nuclear stain. You can supply a single integer or a list of integers to exclude them from the analysis.
To exclude DAPI, assuming it's the final channel, you would supplyexclude_channels=-1.z_useallows you to change how Z slices are handled.z_use = 0orz_use = [1, 5, 7]only keeps the specified Z slices in the analysis.z_use = 'max'takes a maximum projection along the Z slices, collapsing the image to a single slice. This can greatly speed up the analysis by reducing the number of 2D images to process, so long as 3D information is not needed.Leavingz_useempty includes all Z slices in the analysis.After calling the constructor, therawimage - with all channels and slices - is found inim.raw_image, and theworkingimage, without excluded channels and after Z processing, is inim.image.im.shape,im.channels,im.slices,im.framesrefer to theworkingimage's shape.Processing methodsdapi_aligndefdapi_align(self,ref_image,channel=-1,z_slice=None,inter=0):This function performs 2D image alignment between the current object and a givenDotImage,ref_image. The channel to perform cross-correlation on is given as -1 by default, because that is typically the DAPI channel - note that thischannelargument refers to theraw_image, containing channels that were excluded in the constructor.z_sliceallows you to specify which slices to use - it can be a tuple in case they are different between the current image and the reference image, or a single integer. If not supplied, themiddle z sliceof each image is used, by reasoning that this may be most likely to have in focus signal.Theinterargument allows you to specify the order of interpolation performed in the shift operation. By default it is 0, which is equivalent to pixel-resolution alignment - no subpixel shift is possible without higher order. Higher orders allow finer shifts, but potentially alter the dot intensities.After running this method, thealign_shiftattribute contains a tuple of the shifts in each direction, andref_dapicontains the 2D DAPI image used as a reference.subtract_backgrounddefsubtract_background(self,radius=3,bg_image=None):This method performs a Gaussian blur on each 2D subimage, then subtracts the blurred image from the original.radiusspecifies the sigma of the Gaussian filter applied - a larger radius retains more lower frequency features while keeping the signal higher, while a smaller radius discards more features at the expense of some real signal intensity. Typically, a radius of a little bigger than the expected size of real dots is used to selectively retain real features.Alternatively, if an actual background image is available, it can be supplied as aWBImagewhich must have the same shape as the current image. Each 2D image of this will be subtracted from the corresponding image in the current object.After running this method, theimage_nobgattribute contains the background-subtractedWBImage, andimage_bgcontains an array ofstandard deviationsof each background (blurred) image. The standard deviation of the background image is used infind_local_maxesas a basis for relative thresholding of dots.Note that the convolutions (actually forward and inverse Fourier Transforms with multiplications) needed for computing this function are expensive for high-dimensional images (such as those with many Z slices). Typically this method runs much longer thanfind_local_maxes.find_local_maxescan run on raw, non-background subtracted images as well if desired; thresholding is somewhat tougher and must be optimized manually. In the future, a faster routine that either averages along Z or only computes the blur periodically could be implemented to reduce the number of convolutions.find_local_maxesdeffind_local_maxes(self,thresh_abs=0,thresh_coeff=3,mask=None,verbose=False):This method identifies local maxima pixels in each 2D subimage of the current object. The most important parameters to optimize are the thresholds.thresh_abs, if supplied,takes precedenceoverthresh_coeff., and specifies the minimum absolute pixel value to call a local maximum.thresh_coeff, which is used by default, is the multiplier applied to one of two measures of 'background':If background subtraction has been performed, each subimage is thresholded onthresh_coefftimes thestandard deviation of its background imageIf no background subtraction was performed, each subimage is thresholded onthresh_coefftimes theminimum value of the raw image(though will likely change this to a quantile-based measure).For both of these, a single number can be supplied,or a list of numbers, one for each channel. This is useful for channels that have different typical backgrounds.maskis a binary 2D image of the same XY dimensions as the current image that can be used to specify where to look or not to look for dots.IfverboseisTrue, messages about the thresholds applied and number of dots identified in each subimage are printed.The outputs offind_local_maxesare:AbinaryWBImagecalledlocal_max_imagewhere identified local maxima areTrueand all other pixels areFalseADataFramecalleddots_dfcontaining the information about every dot identified. This can be quite a long DataFrame. Its columns are:Coords: A length-2 list with the X and Y pixel locationLabel: An integer label that is unique to each dotArea: Area in pixels of the local maximum, should always be 1Intensity: Pixel intensity of the dotfrom the non-background subtracted imageChannel: Channel dot was found in.Slice: Z slice dot was found inFrame: Time frame dot was found inHybCycle:hybcycleattribute of current imagePosition:positionattribute of current imageThus,dots_dfcontains all of the information needed to quantify intensity, position (in 3D) and channel of all identified local maxima.