def findPeaks(img4D, params): """ img4D: a 4D RandomAccessibleInterval params["frames"]: the number of consecutive time points to average towards detecting peaks with difference of Gaussian. Returns a list of lists of peaks found, one list per time point. """ frames = params["frames"] # Work image: the current sum sum3D = ArrayImgs.unsignedLongs([img4D.dimension(d) for d in [0, 1, 2]]) peaks = [] # Sum of the first set of frames compute(add([Views.hyperSlice(img4D, 3, i) for i in xrange(frames)])).into(sum3D) # Extract nuclei from first sum3D peaks.append(doGPeaks(sum3D, params)) # Running sums: subtract the first and add the last for i in xrange(frames, img4D.dimension(3), 1): compute(add(sub(sum3D, Views.hyperSlice(img4D, 3, i - frames)), Views.hyperSlice(img4D, 3, i))) \ .into(sum3D) # Extract nuclei from sum4D peaks.append(doGPeaks(sum3D, params)) return peaks
def getPixels(self, n): # 'n' is 1-based aimg = ArrayImgs.unsignedShorts(self.dimensions[0:2]) #computeInto(ImgSource(Views.hyperSlice(self.img4d, 2, n-1)), aimg) nZ = self.img4d.dimension(2) fixedT = Views.hyperSlice(self.img4d, 3, int((n - 1) / nZ)) # Z blocks fixedZ = Views.hyperSlice(fixedT, 2, (n - 1) % nZ) w.copy(fixedZ.cursor(), aimg.cursor()) return aimg.update(None).getCurrentStorageArray()
def looping(img, center): for z in xrange(img.dimension(2)): radius = img.dimension(0) * 0.5 / (z + 1) circle = GeomMasks.openSphere(center, radius) # Works, explicit iteration of every pixel for t in Regions.sample(circle, Views.hyperSlice(img, 2, z)): t.setOne()
def findNucleiOverTime(img4D, params, show=True): """ params["frames"]: number of time frames to average params["calibration"]: e.g. [1.0, 1.0, 1.0] params["somaDiameter"]: width of a soma, in pixels params["minPeakValue"]: determine it by hand with e.g. difference of Gaussians sigma=somaDiameter/4 minus sigma=somaDiameter/2 params["sigmaSmaller"]: for difference of Gaussian to detect somas. Recommended somaDiameter / 4.0 -- in pixels params["sigmaLarger"]: for difference of Gaussian to detect somas. Recommended somaDiameter / 2.0 -- in pixels params["searchRadius"]: for finding nearby DoG peaks which are actually the same soma. Recommended somaDiameter / 3.0 -- in pixels parmams["min_count"]: to consider only somas detected in at least min_count time points, i.e. their coordinates are the average of at least min_count independent detections. """ peaks = findPeaks(img4D, params) mergedPeaks = mergePeaks(peaks, params) nuclei = filterNuclei(mergedPeaks, params) # Show as a 3D volume with spheres if show: spheresRAI = virtualPointsRAI(nuclei, params["somaDiameter"] / 2.0, Views.hyperSlice(img4D, 3, 1)) imp = showStack(spheresRAI, title="nuclei (min_count=%i)" % params["min_count"]) return peaks, mergedPeaks, nuclei, spheresRAI, imp return peaks, mergedPeaks, nuclei
def dropSlices(img, nth): """ Drop every nth slice. The calibration is then to be multipled by nth for Z. Counts slices 1-based so as to preserve the first slice (index zero). """ return Views.stack([Views.hyperSlice(img, 2, i) for i in xrange(img.dimension(2)) if 0 == (i+1) % nth])
def MakeMultiChannelPhantom(ops, size): if len(size) > 3: numChannels = size[3] else: numChannels = 1 image = ops.run("create", size, FloatType()) ax = [Axes.X, Axes.Y, Axes.Z, Axes.CHANNEL] imgPlus = ImgPlus(image, "phantom", ax) location = zeros(3, 'i') location[0] = 40 location[1] = size[1] / 2 location[2] = size[2] / 2 #ops.run("addsphere", image, location, radius, 1.0) #ops.run("addassymetricspherel", image, location, 1.0, radius1, radius2) shapes = Add3DShapes(ops, size) def AddShapes(hyperSlice): #shapes.addRandomPointsInROI(hyperSlice, 100.0, 20) shapes.addCenterSphere(hyperSlice, 5.0, 20) if (numChannels > 1): for d in range(0, numChannels): hyperSlice = Views.hyperSlice(image, 3, d) AddShapes(hyperSlice) location[0] += 10 else: AddShapes(image) return imgPlus
def MakeMultiChannelPhantom (ops, size): if len(size)>3: numChannels=size[3] else: numChannels=1 image=ops.run("create", size, FloatType()) ax=[Axes.X, Axes.Y, Axes.Z, Axes.CHANNEL] imgPlus=ImgPlus(image, "phantom", ax) location=zeros(3,'i') location[0]=40; location[1]=size[1]/2; location[2]=size[2]/2; #ops.run("addsphere", image, location, radius, 1.0) #ops.run("addassymetricspherel", image, location, 1.0, radius1, radius2) shapes=Add3DShapes(ops, size) def AddShapes(hyperSlice): #shapes.addRandomPointsInROI(hyperSlice, 100.0, 20) shapes.addCenterSphere(hyperSlice, 5.0, 20) if (numChannels>1): for d in range(0,numChannels): hyperSlice= Views.hyperSlice(image, 3, d) AddShapes(hyperSlice) location[0]+=10 else: AddShapes(image) return imgPlus
def getDoGPeaks(timepoint_index, calibration): img = Views.hyperSlice(vol4d, 3, timepoint_index) dog = createDoG(img, calibration, sigmaSmaller, sigmaLarger, minPeakValue) peaks = dog.getSubpixelPeaks( ) # could also use getPeaks() in integer precision # Return peaks in calibrated units for peak in peaks: for d, cal in enumerate(calibration): peak.setPosition(peak.getFloatPosition(d) * cal, d) return peaks
def updatePixels(self): # Copy interval into pixels view = Views.interval( Views.extendZero(Views.hyperSlice(self.img3D, 2, self.indexZ)), self.interval2D) aimg = ArrayImgs.floats( self.getPixels(), [self.interval2D.dimension(0), self.interval2D.dimension(1)]) ImgUtil.copy(view, aimg)
def getPixels(self, n): # 'n' is 1-based # Target 2D array img to copy data into aimg = ArrayImgs.unsignedShorts(self.dimensions[0:2]) # The number of slices of the 3D volume of a single timepoint nZ = self.img4d.dimension(2) # The slice_index if there was a single channel slice_index = int((n - 1) / 2) # 0-based, of the whole 4D series local_slice_index = slice_index % nZ # 0-based, of the timepoint 3D volume timepoint_index = int(slice_index / nZ) # Z blocks if 1 == n % 2: # Odd slice index: image channel fixedT = Views.hyperSlice(self.img4d, 3, timepoint_index) fixedZ = Views.hyperSlice(fixedT, 2, local_slice_index) w.copy(fixedZ.cursor(), aimg.cursor()) else: # Even slice index: spheres channel sd = SpheresData(self.kdtrees[timepoint_index], radius, inside, outside) volume = Views.interval(Views.raster(sd), self.dimensions3d) plane = Views.hyperSlice(volume, 2, local_slice_index) w.copy(plane.cursor(), aimg.cursor()) # return aimg.update(None).getCurrentStorageArray()
def projectLastDimension(img, showEarly=False): """ Project the last dimension, e.g. a 4D image becomes a 3D image, using the provided reducing function (e.g. min, max, sum). """ last_dimension = img.numDimensions() - 1 # The collapsed image imgC = ArrayImgs.unsignedShorts( [img.dimension(d) for d in xrange(last_dimension)]) if showEarly: showStack( imgC, title="projected") # show it early, will be updated progressively if img.dimension(last_dimension) > 10: # one by one print "One by one" for i in xrange(img.dimension(last_dimension)): print i compute(maximum(imgC, Views.hyperSlice(img, last_dimension, i))).into(imgC) else: # Each sample of img3DV is a virtual vector over all time frames at that 3D coordinate: imgV = Views.collapseReal(img) # Reduce each vector to a single scalar, using a Converter # The Converter class reduce_max = makeCompositeToRealConverter( reducer_class=Math, reducer_method="max", reducer_method_signature="(DD)D") img3DC = convert(imgV, reduce_max.newInstance(), img.randomAccess().get().getClass()) ImgUtil.copy(ImgView.wrap(imgV, img.factory()), imgC) return imgC
"maxEpsilon": somaDiameter * 0.1, # max allowed alignment error in calibrated units (a distance) "minInlierRatio": 0.0000001, # ratio inliers/candidates "minNumInliers": 5, # minimum number of good matches to accept the result "n_iterations": 2000, # for estimating the model "maxTrust": 4, # for rejecting candidates } # Joint dictionary of parameters params = {} params.update(paramsDoG) params.update(paramsFeatures) params.update(paramsModel) calibration = [1.0, 1.0, 1.0] img1 = Views.hyperSlice(unregistered, 3, 0) img2 = Views.hyperSlice(unregistered, 3, 1) peaks1 = getDoGPeaks(img1, calibration, params["sigmaSmaller"], params["sigmaLarger"], params['minPeakValue']) peaks2 = getDoGPeaks(img2, calibration, params["sigmaSmaller"], params["sigmaLarger"], params['minPeakValue']) print "DoG peaks1: %i" % len(peaks1) print "DoG peaks2: %i" % len(peaks2) features1 = ConstellationPlus.extractFeatures(peaks1, makeRadiusSearch(peaks1), params['radius'], params['max_per_peak'], params['min_neighbors'], params['max_neighbors'])
from net.imglib2.converter import Converters, ColorChannelOrder from net.imglib2.img.display.imagej import ImageJFunctions as IL from net.imglib2.view import Views from ij import IJ, ImagePlus # Fetch an RGB image stack (or any RGB image with more than 1 dimension) imp_rgb = IJ.getImage( ) # IJ.openImage("http://imagej.nih.gov/ij/images/flybrain.zip") img = IL.wrap(imp_rgb) # an ARGBType Img red = Converters.argbChannel(img, 1) # a view of the ARGB red channel # Project the last dimension using the max function last_d = red.numDimensions() - 1 op = maximum( [Views.hyperSlice(red, last_d, i) for i in xrange(red.dimension(last_d))]) img_max_red = compute(op).intoArrayImg() IL.wrap(img_max_red, "max projection of the red channel)").show() # Now project all 3 color channels and compose an RGB image last_dim_index = img.numDimensions() - 1 channel_stacks = [[ Views.hyperSlice(Converters.argbChannel(img, channel_index), last_dim_index, slice_index) for slice_index in xrange(img.dimension(last_dim_index)) ] for channel_index in [1, 2, 3]] # 1: red, 2: green, 3: blue channels = Views.stack([maximum(cs).view() for cs in channel_stacks]) max_rgb = Converters.mergeARGB(channels, ColorChannelOrder.RGB)
from net.imglib2.img.display.imagej import ImageJFunctions as IL from net.imglib2.img.array import ArrayImgs from net.imglib2.roi.geom import GeomMasks from net.imglib2.roi import Regions from net.imglib2.view import Views from net.imglib2.type.logic import BitType from collections import deque from itertools import imap # A binary image img = ArrayImgs.bits([512, 512, 50]) # Add some data to it center = img.dimension(0) / 2, img.dimension(1) / 2 for z in xrange(img.dimension(2)): radius = img.dimension(0) * 0.5 / (z + 1) #print radius circle = GeomMasks.openSphere(center, radius) # Works, explicit iteration of every pixel #for t in Regions.sample(circle, Views.hyperSlice(img, 2, z)): # t.setOne() # Works: about twice as fast -- measured with: from time import time .... t0 = time(); ... t1 = time() deque(imap(BitType.setOne, Regions.sample(circle, Views.hyperSlice(img, 2, z))), maxlen=0) IL.wrap(img, "bit img").show()
def hyperSlice(index): return Views.hyperSlice(img, last_dimension, index)
# Convert an ARGB image to a stack of 4 channels: a RandomAccessibleInterval<UnsignedByte> # with one more dimension that before. # The order of channels in the stack can be changed by changing their indices. channels = Converters.argbChannels(img, [0, 1, 2, 3]) impChannels = IL.wrap(channels, imp.getTitle() + " channels") impChannels.show() # Read out a single channel directly red = Converters.argbChannel(img, 1) # Pick a view of the red channel in the channels stack. # Takes the last dimension, which are the channels, # and fixes it, pointing to the index of the red channel (1) in the stack. red = Views.hyperSlice(channels, channels.numDimensions() -1, 1) impRed = IL.wrap(red, imp.getTitle() + " - red channel") impRed.show() # Create an empty image of type FloatType (floating-point values) # Here, the img is used to read out the interval: the dimensions for the new image brightness = ArrayImgFactory(FloatType()).create(img) def iterableChannel(imgARGB, i): return Views.iterable(Converters.argbChannel(imgARGB, i)) zipped_channels = izip(iterableChannel(img, 1), # red iterableChannel(img, 2), # green iterableChannel(img, 3), # blue
def dequeing(img, center): for z in xrange(img.dimension(2)): radius = img.dimension(0) * 0.5 / (z + 1) circle = GeomMasks.openSphere(center, radius) deque(imap(BitType.setOne, Regions.sample(circle, Views.hyperSlice(img, 2, z))), maxlen=0)
def load(self, str_index): return Views.hyperSlice(unregistered, 3, int(str_index))
def measureFluorescence(series_name, img4D, mask=None): csv_fluorescence = os.path.join(srcDir, "%s_fluorescence.csv" % series_name) if not os.path.exists(csv_fluorescence): # Generate projection over time (the img3D) and extract peaks with difference of Gaussian using the params # (Will check if file for projection over time exists and just load it) img3D_filepath = os.path.join( srcDir, "%s_4D-to-3D_max_projection.zip" % series_name) img3D, peaks, spheresRAI, impSpheres = findNucleiByMaxProjection( img4D, params, img3D_filepath, show=True) comp = showAsComposite([wrap(img3D), impSpheres]) # Measure intensity over time, for every peak # by averaging the signal within a radius of each peak. measurement_radius = somaDiameter / 3.0 spheres = [ ClosedWritableSphere([peak.getFloatPosition(d) for d in xrange(3)], measurement_radius) for peak in peaks ] insides = [ Regions.iterable( Views.interval( Views.raster(Masks.toRealRandomAccessible(sphere)), Intervals.largestContainedInterval(sphere))) for sphere in spheres ] count = float(Regions.countTrue(insides[0])) # same for all measurements = [] with open(csv_fluorescence, 'w') as csvfile: w = csv.writer(csvfile, delimiter=",", quotechar='"', quoting=csv.QUOTE_NONNUMERIC) # Header: with peak coordinates w.writerow(["timepoint"] + [ "%.2f::%.2f::%.2f" % tuple(peak.getFloatPosition(d) for d in xrange(3)) for peak in peaks ]) # Each time point for t in xrange(img4D.dimension(3)): img3D = Views.hyperSlice(img4D, 3, t) mean_intensities = array( ((sum(t.get() for t in Regions.sample(inside, img3D)) / count) for inside in insides), 'f') w.writerow([t] + mean_intensities.tolist()) measurements.append(mean_intensities) else: # Parse CSV file with open(csv_fluorescence, 'r') as csvfile: reader = csv.reader(csvfile, delimiter=',', quotechar='"') header = reader.next() # Parse header, containing peak locations peaks = [ RealPoint.wrap(map(float, h.split("::"))) for h in islice(header, 1, None) ] # Parse rows measurements = [map(float, islice(row, 1, None)) for row in reader] return peaks, measurements
array_size = len(pixel_array) print array_size, "<", img_size print "Proportion:", array_size / float( img_size), "AKA", img_size / array_size, "x" # Add some data to it center = img.dimension(0) / 2, img.dimension(1) / 2 for z in xrange(img.dimension(2)): radius = img.dimension(0) * 0.5 / (z + 1) circle = GeomMasks.openSphere(center, radius) # Works, explicit iteration of every pixel #for t in Regions.sample(circle, Views.hyperSlice(img, 2, z)): # t.setOne() # Works: about twice as fast -- measured with: from time import time .... t0 = time(); ... t1 = time() deque(imap(BitType.setOne, Regions.sample(circle, Views.hyperSlice(img, 2, z))), maxlen=0) # Write as TIFF file filepath = os.path.join( tempfile.gettempdir(), "bit-img-" + datetime.now().strftime("%Y-%m-%d-%H:%M:%S") + ".tif") ra = RandomAccessFile(filepath, 'rw') try: # Header: big endian (4D, 4D) or (M, M), magic number (42, 42), and offset of 9 bytes to first IFD # Note: # bin(int("4D", 16))[2:].zfill(8) == '01001101' # int("4D", 16) == 77 ra.write(array([77, 77, 0, 42, 0, 0, 0, 8], 'b')) # A tmp plane img to copy into it each 2D slice for saving later as the pixel data of each IFD plane_img = ArrayImgs.bits([img.dimension(0), img.dimension(1)])
transform = AffineTransform2D() transform.set(scale, 0, 0, 0, scale, 0) # Origins and dimensions (hence, interval) of the target image interval2 = FinalInterval([ int(img1.dimension(0) * scale), int(img1.dimension(1) * scale), img1.dimension(2) ]) # Interval of a single stack slice of the target image sliceInterval = FinalInterval([interval2.dimension(0), interval2.dimension(1)]) slices2 = [] for index in xrange(img1.dimension(2)): # One single 2D RGB slice imgSlice1 = Views.hyperSlice(img1, 2, index) # Views of the 3 color channels, as extended and interpolatable channels = [ Views.interpolate( Views.extendZero(Converters.argbChannel(imgSlice1, i)), NLinearInterpolatorFactory()) for i in [1, 2, 3] ] # ARGBType 2D view of the transformed color channels imgSlice2 = Converters.mergeARGB( Views.stack( Views.interval(RealViews.transform(channel, transform), sliceInterval) for channel in channels), ColorChannelOrder.RGB) slices2.append(imgSlice2) # Transformed view