Ejemplo n.º 1
0
    def testDdsCopyCreate(self):
        """
        Test the :func:`mango.copy` method.
        """

        dds0 = mango.ones(shape=self.shape, dtype="uint8")
        dds1 = mango.copy(dds0, dtype="uint16")
        self.assertEqual(dds0[0], dds1[0])
        self.assertEqual(dds0[0, 0, 0], dds1[0, 0, 0])
        self.assertEqual(dds0[(0, 0, 0)], dds1[(0, 0, 0)])
Ejemplo n.º 2
0
    def testDdsSetGlobalOrigin(self):
        """
        Test the :attr:`mango.Dds.origin` attribute for read/write access.
        """
        origin = (5, 3, 1)
        dds0 = mango.ones(shape=self.shape, dtype="uint8", origin=origin)
        self.assertTrue(sp.all(origin == dds0.origin))
        dds1 = mango.copy(dds0, dtype="uint16")
        self.assertTrue(sp.all(origin == dds1.origin))

        newOrigin = sp.array((8, 16, 2), dtype="int32")
        dds0.origin = newOrigin
        self.assertTrue(sp.all(newOrigin == dds0.origin))
        self.assertTrue(
            sp.all(dds1.subd.origin +
                   (newOrigin - origin) == dds0.subd.origin))
Ejemplo n.º 3
0
    def testDdsResizeHalo(self):

        for ddsH in [(0, 0, 0), (4, 4, 4), [2, 1, 0],
                     sp.array([3, 6, 7]), [6, 9, 8]]:
            for h in [1, sp.array([1, 1, 1]), sp.array([2, 1, 0]), [3, 6, 7]]:
                for s in [False, True]:
                    dds = \
                        mango.data.gaussian_noise(
                            shape=2*sp.array(self.shape),
                            mean = 32000.0,
                            stdd = 100.0,
                            mtype="tomo",
                            origin=(5,-11,7),
                            halo=ddsH
                        )

                    ddsResz = mango.copy(dds)
                    dds.updateHaloRegions()
                    dds.mirrorOuterLayersToBorder(True)
                    ddsResz.updateOverlapRegions()
                    ddsResz.mirrorOuterLayersToBorder(True)

                    ddsResz.resizeHalo(h, s)
                    if (not hasattr(h, "__len__")):
                        h = sp.array([
                            h,
                        ] * 3)
                    h = sp.array(h)
                    self.assertListEqual(list(ddsResz.halo), list(h))
                    self.assertListEqual(list(dds.shape), list(ddsResz.shape))
                    self.assertListEqual(list(dds.origin),
                                         list(ddsResz.origin))

                    self.assertListEqual(list(dds.subd.shape),
                                         list(ddsResz.subd.shape))
                    self.assertListEqual(list(dds.subd.origin),
                                         list(ddsResz.subd.origin))

                    self.assertListEqual(
                        list(dds.subd_h.shape - 2 * (dds.subd_h.halo - h)),
                        list(ddsResz.subd_h.shape))
                    self.assertListEqual(
                        list(dds.subd_h.origin + dds.subd_h.halo - h),
                        list(ddsResz.subd_h.origin))

                    self.assertTrue(
                        sp.all(dds.subd.asarray() == ddsResz.subd.asarray()))

                    slc = []
                    slcResz = []
                    for i in range(0, 3):
                        if (dds.halo[i] > h[i]):
                            slc.append(
                                slice(dds.halo[i] - h[i],
                                      -(dds.halo[i] - h[i])))
                            slcResz.append(slice(None))
                        elif (dds.halo[i] < h[i]):
                            slc.append(slice(None))
                            slcResz.append(
                                slice(h[i] - dds.halo[i],
                                      -(h[i] - dds.halo[i])))
                        else:
                            slc.append(slice(None))
                            slcResz.append(slice(None))

                    self.assertListEqual(
                        list(dds.subd_h.asarray()[slc].shape),
                        list(ddsResz.subd_h.asarray()[slcResz].shape))
                    self.assertTrue(
                        sp.all(dds.subd_h.asarray()[slc] ==
                               ddsResz.subd_h.asarray()[slcResz]))
Ejemplo n.º 4
0
    def testDdsSetGlobalFaceValue(self):
        """
        Test the :meth:`mango.Dds.setFacesToValue` method.
        """
        outDir = self.createTmpDir("testDdsSetGlobalFaceValue")
        ddsorig = mango.zeros(shape=self.shape,
                              mtype="tomo",
                              origin=(-8, 5, -32))
        for depth in (1, int(np.min(ddsorig.shape) // 2)):
            for axis in (0, 1, 2):
                dds = mango.copy(ddsorig)
                vallo = 16 + axis
                rootLogger.info("LO: axis=%s, val=%s, depth=%s" %
                                (axis, vallo, depth))
                dds.setFacesToValue(vallo, axislo=axis, depth=depth)
                #mango.io.writeDds(os.path.join(outDir, "tomoLoFaceAxis%s.nc" % axis), dds)
                shpFace = dds.shape
                shpFace[axis] = depth
                mpidimsFace = [0, 0, 0]
                mpidimsFace[axis] = 1
                rootLogger.info("LO: shpFace=%s, mpidimsFace=%s" %
                                (shpFace, mpidimsFace))
                ddsFace = mango.copy(dds, shape=shpFace, mpidims=mpidimsFace)
                self.assertTrue(sp.all(ddsFace.subd.asarray() == vallo))
                shpNotFace = dds.shape
                shpNotFace[axis] = dds.shape[axis] - depth
                orgNotFace = dds.origin
                orgNotFace[axis] = dds.origin[axis] + depth

                mpidimsNotFace = [0, 0, 0]
                mpidimsNotFace[axis] = 1
                rootLogger.info(
                    "LO: shpNotFace=%s, mpidimsNotFace=%s, orgNotFace=%s" %
                    (shpNotFace, mpidimsNotFace, orgNotFace))
                ddsNotFace = mango.copy(dds,
                                        origin=orgNotFace,
                                        shape=shpNotFace,
                                        mpidims=mpidimsNotFace)
                self.assertTrue(sp.all(ddsNotFace.subd.asarray() == 0))

                dds = mango.copy(ddsorig)
                valhi = 2 * vallo
                rootLogger.info("HI: axis=%s, val=%s, depth=%s" %
                                (axis, valhi, depth))
                dds.setFacesToValue(valhi, axishi=axis, depth=depth)
                #mango.io.writeDds(os.path.join(outDir, "tomoLoFaceAxis%s.nc" % axis), dds)
                shpFace = dds.shape
                shpFace[axis] = depth
                mpidimsFace = [0, 0, 0]
                mpidimsFace[axis] = 1
                orgFace = dds.origin
                orgFace[axis] = dds.origin[axis] + dds.shape[axis] - depth
                rootLogger.info("HI: shpFace=%s, mpidimsFace=%s, orgFace=%s" %
                                (shpFace, mpidimsFace, orgFace))
                ddsFace = mango.copy(dds,
                                     origin=orgFace,
                                     shape=shpFace,
                                     mpidims=mpidimsFace)
                self.assertTrue(sp.all(ddsFace.subd.asarray() == valhi))
                shpNotFace = dds.shape
                shpNotFace[axis] = dds.shape[axis] - depth
                mpidimsNotFace = [0, 0, 0]
                mpidimsNotFace[axis] = 1
                rootLogger.info(
                    "HI: shpNotFace=%s, mpidimsNotFace=%s, orgNotFace=%s" %
                    (shpNotFace, mpidimsNotFace, orgNotFace))
                ddsNotFace = mango.copy(dds,
                                        shape=shpNotFace,
                                        mpidims=mpidimsNotFace)
                self.assertTrue(sp.all(ddsNotFace.subd.asarray() == 0))
Ejemplo n.º 5
0
    def calcSphericalCavityLabels(self, dds):

        mpidims = dds.mpi.shape
        if (dds.mpi.comm != None):
            mpidims = (dds.mpi.comm.Get_size(), 1, 1)
        dds = mango.copy(dds, mpidims=mpidims)
        rootLogger.info("Calculating neighbourhood mean image...")
        fltDds = mango.copy(dds,
                            mtype="tomo_float",
                            halo=self.se.getHaloSize())
        nMeanDds = mango.image.mean_filter(fltDds, self.se)
        nMeanDds = mango.copy(nMeanDds, halo=(0, 0, 0))
        rootLogger.info("Calculating neighbourhood stdd image...")
        nStddDds = mango.image.stdd_filter(fltDds, self.se)
        nStddDds = mango.copy(nStddDds, halo=(0, 0, 0))
        del fltDds
        self.maskLowStddVoxels(dds, nMeanDds, nStddDds)
        rootLogger.info("Calculating mean vs stdd histogram...")
        h2d, edges = mango.image.histogramdd([nMeanDds, nStddDds],
                                             bins=(1024, 8192))
        h2d = h2d[:, :-2]
        rootLogger.info(
            "h2d.shape = %s, edges[0].shape=%s, edges[1].shape=%s" %
            (h2d.shape, edges[0].shape, edges[1].shape))
        rootLogger.info("Done calculating mean vs stdd histogram...")
        maxIdx = np.unravel_index(np.argmax(h2d), h2d.shape)
        rootLogger.info("np.argmax(h2d) = %s" % (maxIdx, ))
        backgroundMean = 0.5 * (edges[0][maxIdx[0]] + edges[0][maxIdx[0] + 1])
        backgroundStdd = 0.5 * (edges[1][maxIdx[1]] + edges[1][maxIdx[1] + 1])

        mskDds = mango.copy(dds)
        rootLogger.info("Background (mean,stdd) = (%s, %s)." %
                        (backgroundMean, backgroundStdd))
        mskDds.subd.asarray()[...] = \
            sp.where(
                sp.logical_or(
                    nStddDds.subd.asarray() < (8 * backgroundStdd),
                    nMeanDds.subd.asarray() < (backgroundMean + 3 * backgroundStdd),
                ),
                mskDds.mtype.maskValue(),
                mskDds.subd.asarray()
            )
        del nMeanDds, nStddDds

        self.writeIntermediateDds("_AaaPrePercentileTailMask", mskDds)
        self.eliminatePercentileTails(mskDds, 1.0, 92.5)
        rootLogger.info("Calculating neighbourhood stdd image...")
        nStddDds = mango.image.stdd_filter(mskDds, self.se)
        self.eliminatePercentileTails(nStddDds, 33.0, 100.0)
        rootLogger.info("Copying stdd percentile tail mask to mskDds...")
        mango.copy_masked(nStddDds, mskDds)
        rootLogger.info("Done copying stdd percentile tail mask to mskDds.")
        self.writeIntermediateDds("_AaaPstPercentileTailMask", mskDds)
        rootLogger.info("Eliminating small clusters from mskDds...")
        self.eliminateSmallClusters(mskDds, 0.1)
        rootLogger.info("Done eliminating small clusters from mskDds.")
        self.writeIntermediateDds("_AaaPstSmallClusterMask", mskDds)
        del nStddDds
        segEdtDds = mango.zeros_like(mskDds, mtype="segmented")
        segEdtDds.asarray()[...] = sp.where(
            mskDds.asarray() == mskDds.mtype.maskValue(), 0, 1)
        self.writeIntermediateDds("_AaaPreCvxHullMask", segEdtDds)
        rootLogger.info("Calculating convex hull...")
        cvxHullMsk = mango.image.convex_hull_3d(
            segEdtDds,
            inputmsk=0,
            outhull=segEdtDds.mtype.maskValue(),
            inhull=1)
        segEdtDds.asarray()[...] = sp.where(
            cvxHullMsk.asarray() == cvxHullMsk.mtype.maskValue(), 1,
            segEdtDds.asarray())
        rootLogger.info("Done calculating convex hull.")

        self.writeIntermediateDds("_AaaPstCvxHullMask", segEdtDds)
        segEdtDds.setFacesToValue(1)
        rootLogger.info("Calculating EDT image...")
        edtDds = mango.image.distance_transform_edt(segEdtDds, val=0)
        self.writeIntermediateDds("_AaaPstCvxHullMaskEdt", edtDds)
        rootLogger.info("Calculating MCR image...")
        mcrDds = mango.image.max_covering_radius(edtDds,
                                                 maxdist=0.5 *
                                                 (np.min(edtDds.shape)),
                                                 filecache=True)
        mango.copy_masked(cvxHullMsk, mcrDds)
        rootLogger.info("Calculating (min,max) MCR values...")
        mcrMin, mcrMax = mango.minmax(mcrDds)
        rootLogger.info("Masking small MCR values...")
        mcrDds.asarray()[...] = sp.where(mcrDds.asarray() >= 0.05 * mcrMax,
                                         mcrDds.asarray(),
                                         mcrDds.mtype.maskValue())
        self.writeIntermediateDds("_AaaPstCvxHullMaskMcr", mcrDds)
        del cvxHullMsk, edtDds
        #
        # Normalise the intensities so that a relative-gradient is computed for the largest
        # MCR radii.
        rootLogger.info("Normalising MCR image...")
        mnmx = mango.minmax(mcrDds)
        tmpDds = mango.copy(mcrDds)
        tmpDds.asarray()[...] -= mnmx[1]
        tmpDds.asarray()[...] *= tmpDds.asarray()
        tmpDds.asarray()[...] = 1 + mnmx[1] * np.exp(-(tmpDds.asarray()) /
                                                     (2 * 0.133 * 0.133 *
                                                      (mnmx[1] * mnmx[1])))
        mcrDds.asarray()[...] = sp.where(mcrDds.asarray() > 0,
                                         mcrDds.asarray() / tmpDds.asarray(),
                                         mcrDds.asarray())
        rootLogger.info("Calculating MCR image gradient...")
        grdMcrDds = mango.image.discrete_gaussian_gradient_magnitude(
            mcrDds, 0.65, errtol=0.01)
        grdMcrDds.asarray()[...] = sp.where(grdMcrDds.asarray() <= 3.0e-2,
                                            mcrDds.asarray(),
                                            mcrDds.mtype.maskValue())
        rootLogger.info("Calculating unique MCR low-gradient values...")
        u = mango.unique(grdMcrDds)
        rootLogger.info(
            "Converting low gradient MCR image to binary segmentation...")
        segDds = mango.map_element_values(grdMcrDds,
                                          lambda x: x in u,
                                          mtype="segmented")
        rootLogger.info("Labeling low gradient MCR regions...")
        mango.copy_masked(mcrDds, segDds)
        lblDds = mango.image.label(segDds, val=1, connectivity=26, dosort=True)
        self.writeIntermediateDds("_AaaPstCvxHullMaskMcrGrdLbl", lblDds)
        del segDds, grdMcrDds

        rootLogger.info("Calculating Principal Moment of Inertia...")
        self.pmoi, self.pmoi_axes, self.com = mango.image.moment_of_inertia(
            mskDds)
        rootLogger.info("Done calculating Principal Moment of Inertia.")
        return lblDds, mcrDds, segEdtDds
Ejemplo n.º 6
0
    def calcSphericalCavityLabels(self, dds):
    
        mpidims = dds.mpi.shape
        if (dds.mpi.comm != None):
            mpidims = (dds.mpi.comm.Get_size(), 1, 1)
        dds = mango.copy(dds, mpidims=mpidims)
        rootLogger.info("Calculating neighbourhood mean image...")
        fltDds = mango.copy(dds, mtype="tomo_float", halo=self.se.getHaloSize())
        nMeanDds = mango.image.mean_filter(fltDds, self.se)
        nMeanDds = mango.copy(nMeanDds, halo=(0,0,0))
        rootLogger.info("Calculating neighbourhood stdd image...")
        nStddDds = mango.image.stdd_filter(fltDds, self.se)
        nStddDds = mango.copy(nStddDds, halo=(0,0,0))
        del fltDds
        self.maskLowStddVoxels(dds, nMeanDds, nStddDds)
        rootLogger.info("Calculating mean vs stdd histogram...")
        h2d, edges = mango.image.histogramdd([nMeanDds, nStddDds], bins=(1024, 8192))
        h2d = h2d[:, :-2]
        rootLogger.info("h2d.shape = %s, edges[0].shape=%s, edges[1].shape=%s" % (h2d.shape, edges[0].shape, edges[1].shape))
        rootLogger.info("Done calculating mean vs stdd histogram...")
        maxIdx = np.unravel_index(np.argmax(h2d), h2d.shape)
        rootLogger.info("np.argmax(h2d) = %s" % (maxIdx,))
        backgroundMean = 0.5 * (edges[0][maxIdx[0]] + edges[0][maxIdx[0] + 1])
        backgroundStdd = 0.5 * (edges[1][maxIdx[1]] + edges[1][maxIdx[1] + 1])

        mskDds = mango.copy(dds)
        rootLogger.info("Background (mean,stdd) = (%s, %s)." % (backgroundMean, backgroundStdd))
        mskDds.subd.asarray()[...] = \
            sp.where(
                sp.logical_or(
                    nStddDds.subd.asarray() < (8 * backgroundStdd),
                    nMeanDds.subd.asarray() < (backgroundMean + 3 * backgroundStdd),
                ),
                mskDds.mtype.maskValue(),
                mskDds.subd.asarray()
            )
        del nMeanDds, nStddDds

        self.writeIntermediateDds("_AaaPrePercentileTailMask", mskDds)
        self.eliminatePercentileTails(mskDds, 1.0, 92.5)
        rootLogger.info("Calculating neighbourhood stdd image...")
        nStddDds = mango.image.stdd_filter(mskDds, self.se)
        self.eliminatePercentileTails(nStddDds, 33.0, 100.0)
        rootLogger.info("Copying stdd percentile tail mask to mskDds...")
        mango.copy_masked(nStddDds, mskDds)
        rootLogger.info("Done copying stdd percentile tail mask to mskDds.")
        self.writeIntermediateDds("_AaaPstPercentileTailMask", mskDds)
        rootLogger.info("Eliminating small clusters from mskDds...")
        self.eliminateSmallClusters(mskDds, 0.1)
        rootLogger.info("Done eliminating small clusters from mskDds.")
        self.writeIntermediateDds("_AaaPstSmallClusterMask", mskDds)
        del nStddDds
        segEdtDds = mango.zeros_like(mskDds, mtype="segmented")
        segEdtDds.asarray()[...] = sp.where(mskDds.asarray() == mskDds.mtype.maskValue(), 0, 1)
        self.writeIntermediateDds("_AaaPreCvxHullMask", segEdtDds)
        rootLogger.info("Calculating convex hull...")
        cvxHullMsk = mango.image.convex_hull_3d(segEdtDds, inputmsk=0, outhull=segEdtDds.mtype.maskValue(), inhull=1)
        segEdtDds.asarray()[...] = sp.where(cvxHullMsk.asarray() == cvxHullMsk.mtype.maskValue(), 1, segEdtDds.asarray())
        rootLogger.info("Done calculating convex hull.")

        self.writeIntermediateDds("_AaaPstCvxHullMask", segEdtDds)
        segEdtDds.setFacesToValue(1)
        rootLogger.info("Calculating EDT image...")
        edtDds = mango.image.distance_transform_edt(segEdtDds, val=0)
        self.writeIntermediateDds("_AaaPstCvxHullMaskEdt", edtDds)
        rootLogger.info("Calculating MCR image...")
        mcrDds = mango.image.max_covering_radius(edtDds, maxdist=0.5*(np.min(edtDds.shape)), filecache=True)
        mango.copy_masked(cvxHullMsk, mcrDds)
        rootLogger.info("Calculating (min,max) MCR values...")
        mcrMin, mcrMax = mango.minmax(mcrDds)
        rootLogger.info("Masking small MCR values...")
        mcrDds.asarray()[...] = sp.where(mcrDds.asarray() >= 0.05*mcrMax, mcrDds.asarray(), mcrDds.mtype.maskValue())
        self.writeIntermediateDds("_AaaPstCvxHullMaskMcr", mcrDds)
        del cvxHullMsk, edtDds
        #
        # Normalise the intensities so that a relative-gradient is computed for the largest
        # MCR radii.
        rootLogger.info("Normalising MCR image...")
        mnmx = mango.minmax(mcrDds)
        tmpDds = mango.copy(mcrDds)
        tmpDds.asarray()[...] -= mnmx[1]
        tmpDds.asarray()[...] *= tmpDds.asarray()
        tmpDds.asarray()[...] = 1+mnmx[1]*np.exp(-(tmpDds.asarray())/(2*0.133*0.133*(mnmx[1]*mnmx[1])))
        mcrDds.asarray()[...] = sp.where(mcrDds.asarray() > 0, mcrDds.asarray()/tmpDds.asarray(), mcrDds.asarray())
        rootLogger.info("Calculating MCR image gradient...")
        grdMcrDds = mango.image.discrete_gaussian_gradient_magnitude(mcrDds, 0.65, errtol=0.01)
        grdMcrDds.asarray()[...] = sp.where(grdMcrDds.asarray() <= 3.0e-2, mcrDds.asarray(), mcrDds.mtype.maskValue())
        rootLogger.info("Calculating unique MCR low-gradient values...")
        u = mango.unique(grdMcrDds)
        rootLogger.info("Converting low gradient MCR image to binary segmentation...")
        segDds = mango.map_element_values(grdMcrDds, lambda x: x in u, mtype="segmented")
        rootLogger.info("Labeling low gradient MCR regions...")
        mango.copy_masked(mcrDds, segDds)
        lblDds = mango.image.label(segDds, val=1, connectivity=26, dosort=True)
        self.writeIntermediateDds("_AaaPstCvxHullMaskMcrGrdLbl", lblDds)
        del segDds, grdMcrDds

        rootLogger.info("Calculating Principal Moment of Inertia...")
        self.pmoi, self.pmoi_axes, self.com = mango.image.moment_of_inertia(mskDds)
        rootLogger.info("Done calculating Principal Moment of Inertia.")
        return lblDds, mcrDds, segEdtDds
Ejemplo n.º 7
0
def cylinder_fit_multi_res(
    input,
    numcyl,
    retcylimg=False,
    resolutions=[128, 256, 1024],
    metric = None,
    radius_range_percent = (50.0, 100.0),
    centre_range_percent = (-8.0,   8.0),
    metricThreshRelTol   = 0.25
):
    """
    Multi-resolution version of :func:`cylinder_fit`.
    
    :type input: :obj:`mango.Dds`
    :param input: Search for cylinder boundaries in the gradient-vector
       image of this :samp:`{input}` image.
    :type numcyl: :obj:`mango.Dds`
    :param numcyl: Number of cylinders to find.
    :type retcylimg: :obj:`bool`
    :param retcylimg: If :samp:`True`, additionally returns a segmented image
       with the boundary of the cylinder fits labeled from :samp:`0` to :samp:`{numcyl}-1`.
    :type resolutions: sequence of :obj:`int`
    :param resolutions: The sequence of image shapes for each cylinder fit resolution.
        Exhaustive search and refinement is performed at the lowest resolution,
        just refinement at the higher resolutions.
    :type metric: :obj:`CylinderFitGradientMetric`
    :param metric: Metric object used to identify cylinder parameters
       which indicate the presence of a cylindrical boundary in the :samp:`input` image.
    :type radius_range_percent: 2 or 3 sequence of :obj:`float`
    :param radius_range_percent: Indicates the cylinder-radius exhaustive search grid
       as :samp:`(min_percent, max_percent, percent_step)`. This is a percentage
       of the minimum :samp:`{input}.shape` component (i.e. :samp:`numpy.min({input}.shape)`). 
       If the percent step size is omitted, then a percent equivalent of 2-voxels is chosen
       as the step-size.
    :type centre_range_percent: 2 or 3 sequence of :obj:`float`
    :param centre_range_percent: Indicates the cylinder-centre exhaustive search grid
       as :samp:`(min_percent, max_percent, percent_step)`. This is a percentage
       of the minimum :samp:`{input}.shape` component (i.e. :samp:`numpy.min({input}.shape)`). 
       If the percent step size is omitted, then a percent equivalent of 2-voxels is chosen
       as the step-size.
    :type metricThreshRelTol: :obj:`float`
    :param metricThreshRelTol: Relative tolerance for truncating the
       exhaustive search metric values. Ignore exhaustive search parameters
       which give metric values that have relative difference greater
       than :samp:`{metricThreshRelTol}` of the best (lowest) metric value in
       the entire search.
    :rtype: :obj:`list` of :samp:`[metricVal, cylinderParameters]` pairs
    :return: List of pairs :samp:`pairList = [[metricVal0, cylPrms0], [metricVal1, cylPrms1], ...]`
       where :samp:`cylPrms0=[[centrez0, centrey0, centrex0], radius0, axisLength0, [axisz0, axisy0, axisx0]]`, :samp:`cylPrms1=[[centrez1, centrey1, centrex1], radius1, axisLength1, [axisz1, axisy1, axisx1]]`, etc.
       If  :samp:`{retcylimg}` is :samp:`True`, additionally returns segmented :obj:`mango.Dds`
       image labeled with cylinder boundaries (i.e. returns :samp:`pairList, segDds`).

    """
    resolutions = list(resolutions)
    resolutions.sort()
    dsFactors = np.min(input.shape)/sp.array(resolutions, dtype="float64")
    if (dsFactors[-1] < 1):
        dsFactors[-1] = 1.0
    dsFactors = sp.array([dsFactor for dsFactor in dsFactors if dsFactor >= 1], dtype=dsFactors.dtype)
    i = 0
    rootLogger.info("Downsample factors = %s." % (dsFactors,))
    rootLogger.info("Downsampling (resolution=%s) input.shape=%s to shape=%s..." % (resolutions[i], input.shape, input.shape/dsFactors[i]))
    input.updateOverlapRegions()
    input.mirrorOuterLayersToBorder(False)
    dsInput = mango.image.gaussian_downsample(input, factor=[1.0/dsFactors[0],]*3)
    dsInput.updateHaloRegions()
    dsInput.mirrorOuterLayersToBorder(False)
    
    # Do data-parallel parallelism for all resolutions, having a copy of
    # the global downsampled gradient-vector image on all processes tends
    # to be too memory hungary.
    distributedMetricEvaluation = True
    rootLogger.info("Done downsampling, new shape=%s." % (dsInput.shape, ))
    cylPrmList = \
        cylinder_fit(
            input = dsInput,
            numcyl=numcyl,
            retcylimg = False,
            distributedMetricEvaluation = distributedMetricEvaluation,
            metric = metric,
            radius_range_percent = radius_range_percent,
            centre_range_percent = centre_range_percent,
            metricThreshRelTol = metricThreshRelTol
        )
    rootLogger.info("Lowest resolution fit cylinders=:\n%s" % ("\n".join(map(str, cylPrmList))))
    del dsInput
    dsInput = None
    fitter = None
    distributedMetricEvaluation = True
    i = 1
    if (metric == None):
        metric = cylinder_fit_gradient_metric()

    for dsFactor in dsFactors[1:]:
        metric.setNumSlices(metric.getNumSlices() + 10)
        metric.setNumPointsPerSlice(int(metric.getNumPointsPerSlice()*1.25))
        if (dsFactor > 1):
            rootLogger.info("Downsampling (resolution=%s) input.shape=%s to shape=%s..." % (resolutions[i], input.shape, input.shape/dsFactor))
            dsInput = mango.image.gaussian_downsample(input, factor=[1.0/dsFactor,]*3)
            rootLogger.info("Done downsampling, new shape=%s." % (dsInput.shape, ))
        else:
            rootLogger.info("Full resolution refinement input.shape=%s..." % (input.shape,))
            dsInput = input
        if (input.mpi.comm != None):
            input.mpi.comm.barrier()
        dsInput.updateHaloRegions()
        dsInput.mirrorOuterLayersToBorder(False)
        rootLogger.info("Cylinder-fit refinement for resolution image shape=%s." % (dsInput.shape,))
        fitter = \
            _CylinderFitter(
                input                       = dsInput,
                numcyl                      = numcyl,
                distributedMetricEvaluation = distributedMetricEvaluation,
                metric                      = metric,
                radius_range_percent        = radius_range_percent,
                centre_range_percent        = centre_range_percent,
                axis_range_degrees          = None,
                metricThreshRelTol          = metricThreshRelTol
            )
        if (i >= (len(dsFactors)-1)):
            fitter.minimize_options['xtol'] = 1.0e-4
            fitter.minimize_options['ftol'] = (fitter.minimize_options['xtol'])**2
        cylPrmList = fitter.refineFit(cylPrmList)
        rootLogger.info("Done cylinder-fit refinement for resolution image shape=%s." % (dsInput.shape,))
        rootLogger.info("Fit cylinders=:\n%s" % ("\n".join(map(str, cylPrmList))))
        i+=1
    
        del dsInput, fitter
        dsInput = None
        fitter = None

    retVal = cylPrmList
    if (retcylimg):
        cylPrmList = [cylfit[1] for cylfit in cylPrmList]
        cylImg = mango.copy(input, mtype="segmented")
        cylImg.setAllToValue(cylImg.mtype.maskValue())
        cylImg = fill_cylinder_boundaries(cylPrmList, cylImg)

        retVal = [retVal, cylImg]
    
    return retVal