(* ::Package:: *) BeginPackage["ParticleTrackingRadialCenter`"] (* ::Section:: *) (*Usage Definitions*) RadialCenter::usage = "Determines the center of a 2D intensity distribution using the method given in Parthasarathy R. Nature Methods(2012) {{x0, y0},{sigma,sigma}} = RadialCenter[Positions,Intensities]; Inputs Positions: A list of x-y coordinate pairs that coorespond to the list of intensity values in Intensities. It is a R x 2 array, where R, the number of rows, is equal to M*N, the dimensions of the rectangular array for which the center is being calculated Intensities: A list of grayscale intensity values that coorespond to the x-y coordinate pairs in Positions. It is an R x 1 array, where R, the number of rows, is equal to M*N, the dimensions of the rectangular array for which the center is being calculated Outputs {x0, y0}: The calculated center of radial symmetry {sigma, sigma}: The approximate width of the object. Both values are the same, but it's listed twice for consistency with other code." Begin["`Private`"] (* ::Section:: *) (*Object Identification Functions*) (*Finds the center of a 2D intensity distribution using the algorithm given in Parthasarathy R. Nature Methods(2012)*) RadialCenter[Positions_,Intensities_]:=Module[ (*Initialize variables*) (*Positions and Intensities are lists of x-y coordinates and grayscale intensities of an M x N array. Positions is an R x 2 list, where R, the number of rows, is equal to M*N, or the total number of pixels. Intensities is an R x 1 list, where each row contains an intensity value that corresponds to the x-y coordinates given in Positions. In the variable initialization, Intensities is reshaped using Partition, into an M x N list. The unusual form of the inputs was necessary to make this function work within a larger code structure*) {Int=Partition[Intensities,Last[Positions][[2]]-Positions[[1,2]]+1] ,ParticleOrigin = Positions[[1,All]], b={},Nx={},Ny={},Xm={},Ym={},dIdu={},dIdv={},fdu={},fdv={},dImag2={},m={},NanElements={},unsmoothm={},sdI2={},xcentroid={},ycentroid={}, w={},wm2p1={},sw={},smw={},smmw={},smbw={},sbw={},det={},xc={},yc={},Isub={},Px={},Py={},xoffset={},yoffset={},r2={}}, (*Set up the grid on which the gradient is going to be calculated. If you picture the pixels as a square grid, the grid points are the inner vertices between each group of four pixels. Therefore, the grid points are offset from the pixel centers by 0.5 pixels in x and y. x coordinates are the columns and they count left to right and y-coordinates are the rows and count top to bottom.*) {Ny,Nx} = Dimensions[Int]; Xm = Table[Range[-(Nx-1)/2+0.5,(Nx-1)/2-0.5,1],{Ny-1}]; Ym = Transpose[Table[Range[-(Ny-1)/2+0.5,(Ny-1)/2-0.5,1],{Nx-1}]]; (*calculate the gradient in the 45 degree rotated coordinates, u and v.*) dIdu = Int[[1;;Ny-1,2;;Nx]]-Int[[2;;Ny,1;;Nx-1]]; dIdv = Int[[1;;Ny-1,1;;Nx-1]]-Int[[2;;Ny,2;;Nx]]; (*Smoothing of the gradient using a 3x3 mean filter*) fdu = MeanFilter[dIdu,1]; fdv = MeanFilter[dIdv,1]; dImag2 = fdu*fdu + fdv*fdv; (*gradient magnitude, squared*) (*The slope of the gradient in x-y coordinates. The minus sign flips the array to account for y increasing downward*) m = -(fdv + fdu) / (fdu-fdv); (*Checks if any values of m are ComplexInfinity or Indeterminate from 0/0 or x/0 operations and if so replaces them with the unsmoothed value. If it is still ComplexInfinity or Indeterminate, replace those elements with 0.*) If[MemberQ[m,ComplexInfinity|Indeterminate,Infinity], NanElements = Position[m,ComplexInfinity|Indeterminate]; unsmoothm = -(dIdv + dIdu) / (dIdu-dIdv); ReplacePart[m,Thread[NanElements -> Extract[unsmoothm,NanElements]]]; If[MemberQ[m,ComplexInfinity|Indeterminate,Infinity], NanElements = Position[m,ComplexInfinity|Indeterminate]; ReplacePart[m,NanElements -> 0]; ]; ]; (*Calculate the y-intercept of the line of slope m that goes through each grid point*) b = Ym - m * Xm; (*Weighting: Weight by the square of the gradient magnitude and the inverse distance to the gradient intensity centroid*) sdI2 = Total[dImag2,Infinity]; xcentroid = Total[dImag2*Xm,Infinity]/sdI2; ycentroid = Total[dImag2*Ym,Infinity]/sdI2; w = dImag2/Sqrt[(Xm-xcentroid)*(Xm-xcentroid)+(Ym-ycentroid)*(Ym-ycentroid)]; (*Least squares solution to determine the translated coordinate system origin [xc,yc] such that lines y=mx+b have the minimal total distance^2 to the origin*) wm2p1 = w /(m * m + 1); sw = Total[wm2p1,Infinity]; smmw = Total[m*m*wm2p1,Infinity]; smw = Total[m*wm2p1,Infinity]; smbw = Total[m*b*wm2p1,Infinity]; sbw = Total[b*wm2p1,Infinity]; det = smw*smw - smmw*sw; xc = (smbw*sw - smw*sbw)/det; yc = (smbw*smw - smmw*sbw)/det; (*find the center with respect to the upper left corner of the matrix of intensities passed to RadialCenter*) xc = xc + (Nx+1)/2.0; yc = yc + (Ny+1)/2.0; (*Calculate an approximate width of the object, which can be used later for other processing or selection*) Isub = Int - Min[Int]; Px = Table[Range[1,Nx,1],{Ny}]; Py = Transpose[Table[Range[1,Ny,1],{Nx}]]; xoffset = Px - xc; yoffset = Py - yc; r2 = xoffset*xoffset + yoffset*yoffset; sigma = Sqrt[Total[Isub*r2,Infinity]/Total[Isub,Infinity]]/2; (*Find the object position with respect to the full image coordinates. ParticleOrigin are the full frame coordinates of the upper left corner of the intensity matrix passed to RadialCenter*) x0 = xc + ParticleOrigin[[2]] - 1; y0 = yc + ParticleOrigin[[1]] - 1; (*Return the final refined position with respect to the full image coordinates and the object width, {{xo,yo},{sigma,sigma}}*) {{x0, y0},{sigma,sigma}} ] (* ::Section::Closed:: *) (*End Package*) End[ ] EndPackage[ ]