Vous avez reçu un message "Your GitLab account has been locked ..." ? Pas d'inquiétude : lisez cet article https://docs.gricad-pages.univ-grenoble-alpes.fr/help/unlock/

Commit e3db4f22 authored by Gustavo Pinzon's avatar Gustavo Pinzon
Browse files

removed Spheroid class and changed the functions/test to work with makeBlurryNoisySpheroid

parent e43aac67
Pipeline #62021 passed with stages
in 25 minutes and 45 seconds
......@@ -365,7 +365,13 @@ def directionalErosion(bwIm, vect, a, c, numberOfThreads=1, verbose = False):
qResults.put("STOP")
break
spheroid = spam.label.label.Spheroid(a, c, numpy.asarray(vect[job])).digitize()
maxDim = numpy.max([a,c])
spheroid = spam.kalisphera.makeBlurryNoisySpheroid([maxDim,maxDim,maxDim],
[numpy.floor(maxDim/2), numpy.floor(maxDim/2), numpy.floor(maxDim/2)],
[a,c],
vect[job],
background=0,
foreground=1)
imEroded_i = scipy.ndimage.binary_erosion(bwIm, structure = spheroid)
qResults.put( [imEroded_i])
......
......@@ -1230,80 +1230,80 @@ def meanOrientation(orientations):
return orientations_proj, main_axis, intermediate_axis, minor_axis
class Spheroid:
"""
This class creates at 3D binarised ellipsoid characterised by two semi-axis and
an orientation vector as:
#class Spheroid:
#"""
#This class creates at 3D binarised ellipsoid characterised by two semi-axis and
#an orientation vector as:
- a, Secondary semi-axis (contains as well the third semi-axis).
- c, Main semi-axis of rotational symmetry.
- v, Orientation vector.
#- a, Secondary semi-axis (contains as well the third semi-axis).
#- c, Main semi-axis of rotational symmetry.
#- v, Orientation vector.
The binarised ellipsoid can be used as an structuring element for morphological
operations.
#The binarised ellipsoid can be used as an structuring element for morphological
#operations.
Parameters
-----------
a : int or float
Length of the secondary semi-axis, contains as well the third semi-axis
#Parameters
#-----------
#a : int or float
#Length of the secondary semi-axis, contains as well the third semi-axis
c : int or float
Lenght of the principal semi-axis
#c : int or float
#Lenght of the principal semi-axis
v : 1x3 array
Orientation vector of the ellipsoid
#v : 1x3 array
#Orientation vector of the ellipsoid
Returns
--------
Spheroid : 3D boolean array
Boolean array with the spheroid
#Returns
#--------
#Spheroid : 3D boolean array
#Boolean array with the spheroid
Note
-----
If c>a, a prolate (rice-like) is generated; while a>c yields an oblate (lentil-like).
#Note
#-----
#If c>a, a prolate (rice-like) is generated; while a>c yields an oblate (lentil-like).
Taken from https://sbrisard.github.io/posts/20150930-orientation_correlations_among_rice_grains-06.html
#Taken from https://sbrisard.github.io/posts/20150930-orientation_correlations_among_rice_grains-06.html
"""
def __init__(self, a, c, d=None, dim=None):
if ((d is None) + (dim is None)) != 1:
raise ValueError('d and dim cannot be specified simultaneously')
self.a = a
self.c = c
if d is None:
self.d = numpy.zeros((dim,), dtype=numpy.float64)
self.d[-1] = 1.
else:
self.d = numpy.asarray(d)
dim = len(d)
p = numpy.outer(self.d, self.d)
q = numpy.eye(dim, dtype=numpy.float64) - p
self.Q = c**2*p+a**2*q
self.invQ = p/c**2+q/a**2
def __str__(self):
return ('spheroid: a = {}, c = {3}, d = {}, ').format(tuple(self.d),
self.a,
self.c)
def bounding_box(self):
return numpy.sqrt(numpy.diag(self.Q))
def criterion(self, x):
"""Ordering of points: ``x[i, ...]`` is the i-th coordinate"""
y = numpy.tensordot(self.invQ, x, axes=([-1], [0]))
numpy.multiply(x, y, y)
return numpy.sum(y, axis=0)
def digitize(self, h=1.0):
bb = self.bounding_box()
i_max = numpy.ceil(bb/h-0.5)
bb = i_max*h
shape = 2*i_max+1
slices = [slice(-x, x, i*1j) for (x, i) in zip(bb, shape)]
x = numpy.mgrid[slices]
return self.criterion(x)<=1.0
#"""
#def __init__(self, a, c, d=None, dim=None):
#if ((d is None) + (dim is None)) != 1:
#raise ValueError('d and dim cannot be specified simultaneously')
#self.a = a
#self.c = c
#if d is None:
#self.d = numpy.zeros((dim,), dtype=numpy.float64)
#self.d[-1] = 1.
#else:
#self.d = numpy.asarray(d)
#dim = len(d)
#p = numpy.outer(self.d, self.d)
#q = numpy.eye(dim, dtype=numpy.float64) - p
#self.Q = c**2*p+a**2*q
#self.invQ = p/c**2+q/a**2
#def __str__(self):
#return ('spheroid: a = {}, c = {3}, d = {}, ').format(tuple(self.d),
#self.a,
#self.c)
#def bounding_box(self):
#return numpy.sqrt(numpy.diag(self.Q))
#def criterion(self, x):
#"""Ordering of points: ``x[i, ...]`` is the i-th coordinate"""
#y = numpy.tensordot(self.invQ, x, axes=([-1], [0]))
#numpy.multiply(x, y, y)
#return numpy.sum(y, axis=0)
#def digitize(self, h=1.0):
#bb = self.bounding_box()
#i_max = numpy.ceil(bb/h-0.5)
#bb = i_max*h
#shape = 2*i_max+1
#slices = [slice(-x, x, i*1j) for (x, i) in zip(bb, shape)]
#x = numpy.mgrid[slices]
#return self.criterion(x)<=1.0
#def _fixUndersegmentation(imLab, imGrey, listLabels, a, c, numVect=100, vect=None, boundingBoxes=None, centresOfMass=None, numberOfThreads=1, verbose=False):
#"""
......
......@@ -10,7 +10,7 @@ import spam.mesh.structured as smesh
import random
import spam.label.label
import spam.label.ITKwatershed as ws
import scipy.ndimage
import scipy.ndimage
import spam.kalisphera
class testAll(unittest.TestCase):
......@@ -68,7 +68,13 @@ class testAll(unittest.TestCase):
a = random.randrange(1,10,1)
c = random.randrange(1,10,1)
if numpy.abs(a-c)<2:a = a*2
imTest = spam.label.label.Spheroid(a, c, numpy.asarray(vect)).digitize()
maxDim = numpy.max([a,c])
imTest = spam.kalisphera.makeBlurryNoisySpheroid([maxDim,maxDim,maxDim],
[numpy.floor(maxDim/2), numpy.floor(maxDim/2), numpy.floor(maxDim/2)],
[a,c],
vect,
background=0,
foreground=1)
#Check that a non-list entry of vect generate error
imEroded = morph.directionalErosion(imTest, vect, a, c)
self.assertIs(imEroded, None)
......
......@@ -82,11 +82,7 @@ class testAll(unittest.TestCase):
self.assertEqual(res,None)
# Check that it does not fail when the image is too small and gaussianFit = True
im = spam.label.label.Spheroid(10, 10, numpy.asarray([0, 0, 1])).digitize()
#im = spam.kalisphera.kalisphera.makeBlurryNoisySphere([10,10,10], [5,5,5], 4, 0.5, 0.05, background=0.25, foreground=0.75)
im = im.astype(int)
im = (im*0.5)+0.25
im = spam.kalisphera.makeBlurryNoisySphere([30,30,30],[15,15,15], [15])
res = spam.helpers.histogramTools.findHistogramPeaks(im, valley1 = 0.5, greyRange = [0, 1], gaussianFit = False)
self.assertIsNot(res, None)
......
......@@ -439,138 +439,127 @@ class TestFunctionLabel(unittest.TestCase):
normVectors = numpy.sum(numpy.linalg.norm(projectedVectors, axis = 1)) / len(projectedVectors)
self.assertAlmostEqual(normVectors, 1, places = 3)
def test_Spheroid(self):
# Test for Oblate (Lentil-shaped)
# Generate random direction
theta = numpy.radians(random.randrange(0,360,1))
phi = numpy.radians(random.randrange(0,90,1))
vect = numpy.array([numpy.cos(phi), numpy.sin(phi)*numpy.sin(theta), numpy.sin(phi)*numpy.cos(theta)])
if vect[0]<0: vect[:]=-1*vect[:]
# Generate two random semi-axis values
a = random.randrange(20,40,1)
c = random.randrange(10,20,1)
#Generate the spheroid
spheroid = spam.label.label.Spheroid(a, c, numpy.asarray(vect)).digitize()
# Compute its semi-axis
semiAxis = spam.label.ellipseAxes(spheroid)
# Compare the semi-axis
self.assertLess(numpy.abs(numpy.max(semiAxis[1])-numpy.maximum(a,c)),2)
self.assertLess(numpy.abs(numpy.min(semiAxis[1])-numpy.minimum(a,c)),2)
# Compute the orientation
eigenVal, eigenVect = spam.label.momentOfInertia(spheroid)
eigenVal = eigenVal / numpy.max(eigenVal)
# Get main orientation
mainVect = eigenVect[1][0:3]
if mainVect[0]<0: mainVect[:]=-1*mainVect[:]
# Compute the angle between them
c = numpy.dot(vect,mainVect)/numpy.linalg.norm(vect)/numpy.linalg.norm(mainVect)
angle = numpy.degrees(numpy.arccos(numpy.clip(c, -1, 1)))
# Check angle less than 2 degree
self.assertLess(angle,2)
# Test for Prolate (Rice-shaped)
# Generate random direction
theta = numpy.radians(random.randrange(0,360,1))
phi = numpy.radians(random.randrange(0,90,1))
vect = numpy.array([numpy.cos(phi), numpy.sin(phi)*numpy.sin(theta), numpy.sin(phi)*numpy.cos(theta)])
if vect[0]<0: vect[:]=-1*vect[:]
# Generate two random semi-axis values
a = random.randrange(10,20,1)
b = random.randrange(20,40,1)
# Generate the spheroid
spheroid = spam.label.label.Spheroid(a, b, numpy.asarray(vect)).digitize()
# Compute its semi-axis
semiAxis = spam.label.ellipseAxes(spheroid)
# Compare the semi-axis
self.assertLess(numpy.abs(numpy.max(semiAxis[1])-numpy.maximum(a,b)),2)
self.assertLess(numpy.abs(numpy.min(semiAxis[1])-numpy.minimum(a,b)),2)
# Compute the orientation
eigenVal, eigenVect = spam.label.momentOfInertia(spheroid)
eigenVal = eigenVal / numpy.max(eigenVal)
# Get main orientation
mainVect = eigenVect[1][6:9]
if mainVect[0]<0: mainVect[:]=-1*mainVect[:]
# Compute the angle between them
c = numpy.dot(vect,mainVect)/numpy.linalg.norm(vect)/numpy.linalg.norm(mainVect)
angle = numpy.degrees(numpy.arccos(numpy.clip(c, -1, 1)))
# Check angle less than 2 degree
self.assertLess(angle,2)
#def _test_Spheroid(self):
## Test for Oblate (Lentil-shaped)
## Generate random direction
#theta = numpy.radians(random.randrange(0,360,1))
#phi = numpy.radians(random.randrange(0,90,1))
#vect = numpy.array([numpy.cos(phi), numpy.sin(phi)*numpy.sin(theta), numpy.sin(phi)*numpy.cos(theta)])
#if vect[0]<0: vect[:]=-1*vect[:]
## Generate two random semi-axis values
#a = random.randrange(20,40,1)
#c = random.randrange(10,20,1)
##Generate the spheroid
#spheroid = spam.label.label.Spheroid(a, c, numpy.asarray(vect)).digitize()
## Compute its semi-axis
#semiAxis = spam.label.ellipseAxes(spheroid)
## Compare the semi-axis
#self.assertLess(numpy.abs(numpy.max(semiAxis[1])-numpy.maximum(a,c)),2)
#self.assertLess(numpy.abs(numpy.min(semiAxis[1])-numpy.minimum(a,c)),2)
## Compute the orientation
#eigenVal, eigenVect = spam.label.momentOfInertia(spheroid)
#eigenVal = eigenVal / numpy.max(eigenVal)
## Get main orientation
#mainVect = eigenVect[1][0:3]
#if mainVect[0]<0: mainVect[:]=-1*mainVect[:]
## Compute the angle between them
#c = numpy.dot(vect,mainVect)/numpy.linalg.norm(vect)/numpy.linalg.norm(mainVect)
#angle = numpy.degrees(numpy.arccos(numpy.clip(c, -1, 1)))
## Check angle less than 2 degree
#self.assertLess(angle,2)
## Test for Prolate (Rice-shaped)
## Generate random direction
#theta = numpy.radians(random.randrange(0,360,1))
#phi = numpy.radians(random.randrange(0,90,1))
#vect = numpy.array([numpy.cos(phi), numpy.sin(phi)*numpy.sin(theta), numpy.sin(phi)*numpy.cos(theta)])
#if vect[0]<0: vect[:]=-1*vect[:]
## Generate two random semi-axis values
#a = random.randrange(10,20,1)
#b = random.randrange(20,40,1)
## Generate the spheroid
#spheroid = spam.label.label.Spheroid(a, b, numpy.asarray(vect)).digitize()
## Compute its semi-axis
#semiAxis = spam.label.ellipseAxes(spheroid)
## Compare the semi-axis
#self.assertLess(numpy.abs(numpy.max(semiAxis[1])-numpy.maximum(a,b)),2)
#self.assertLess(numpy.abs(numpy.min(semiAxis[1])-numpy.minimum(a,b)),2)
## Compute the orientation
#eigenVal, eigenVect = spam.label.momentOfInertia(spheroid)
#eigenVal = eigenVal / numpy.max(eigenVal)
## Get main orientation
#mainVect = eigenVect[1][6:9]
#if mainVect[0]<0: mainVect[:]=-1*mainVect[:]
## Compute the angle between them
#c = numpy.dot(vect,mainVect)/numpy.linalg.norm(vect)/numpy.linalg.norm(mainVect)
#angle = numpy.degrees(numpy.arccos(numpy.clip(c, -1, 1)))
## Check angle less than 2 degree
#self.assertLess(angle,2)
# Check that raises an error when the vector and dim are passed along
with self.assertRaises(ValueError): spam.label.label.Spheroid(10, 20, numpy.asarray([0,1,0]), dim = 1).digitize()
# Check that it runs even without a vector
res = spam.label.label.Spheroid(10, 20, dim = 3).digitize()
self.assertIsNotNone(res)
def _test_FixUnderSegmentation(self):
# Generate two prolate grains (rice-like)
grain1 = spam.label.label.Spheroid(10, 20, numpy.asarray([0,1,0])).digitize()
grain2 = spam.label.label.Spheroid(10, 20, numpy.asarray([0,1,0])).digitize()
# Create the bigger labelled image
grainIm = numpy.concatenate((grain1,grain2))
grainIm = numpy.zeros(grainIm.shape)
# Add the grains to the bigger image
grainIm[:grain1.shape[0]-1,:,:] = grain1[:grain1.shape[0]-1,:,:]
grainIm[grain2.shape[0]-5:-5,:,:] = grainIm[grain2.shape[0]-5:-5,:,:] + grain1[:,:,:]
# Set all the labels to 1
grainIm = numpy.where(grainIm >= 1, 3, grainIm)
# Pad a border
grainIm = numpy.pad(grainIm, pad_width=10, mode='constant', constant_values = 0)
# Create the 'greyScale' image
greyIm = numpy.where(grainIm == 3, 30000, 10000)
# Check that the greyscale image is normalized
res1 = spam.label.label.fixUndersegmentation(grainIm, greyIm, [3], 10*0.5, 20*0.5)
self.assertEqual(res1, None)
# Check that a or c is valid
res2 = spam.label.label.fixUndersegmentation(grainIm, greyIm, [3], numpy.nan, numpy.nan)
self.assertEqual(res2, None)
# Check that a or c are positive
res3 = spam.label.label.fixUndersegmentation(grainIm, greyIm, [3], -1, 10)
self.assertEqual(res3, None)
# Run fixUnderSegmentation
greyIm = numpy.where(grainIm == 3, 0.75, 0.25)
res4 = spam.label.label.fixUndersegmentation(grainIm, greyIm, [3], 10*0.8, 20*0.8, vect = [[0,1,0]])
# Check that there are two grains
self.assertEqual(numpy.max(numpy.unique(res4)), 2)
# Check that it runs even if the label does not exist
res5 = spam.label.label.fixUndersegmentation(grainIm, greyIm, [3], 10*0.8, 20*0.8, vect = [[0,1,0]])
self.assertIsNotNone(res5)
# Check for a vect that is not a list
res6 = spam.label.label.fixUndersegmentation(grainIm, greyIm, [3], 10*0.8, 20*0.8, vect = (0,1,0))
self.assertIsNone(res6)
# Check that it works without the input vect
res7 = spam.label.label.fixUndersegmentation(grainIm, greyIm, [3], 10*0.8, 20*0.8, numVect = 1)
self.assertIsNotNone(res7)
# Check that it works with verbose = True
res8 = spam.label.label.fixUndersegmentation(grainIm, greyIm, [3], 10*0.8, 20*0.8, numVect = 1, verbose = True)
self.assertIsNotNone(res8)
# Check that it works even for a non-existing label
res9 = spam.label.label.fixUndersegmentation(grainIm, greyIm, [5], 10*0.8, 20*0.8, numVect = 1)
self.assertIsNotNone(res9)
## Check that raises an error when the vector and dim are passed along
#with self.assertRaises(ValueError): spam.label.label.Spheroid(10, 20, numpy.asarray([0,1,0]), dim = 1).digitize()
## Check that it runs even without a vector
#res = spam.label.label.Spheroid(10, 20, dim = 3).digitize()
#self.assertIsNotNone(res)
#def _test_FixUnderSegmentation(self):
## Generate two prolate grains (rice-like)
#grain1 = spam.label.label.Spheroid(10, 20, numpy.asarray([0,1,0])).digitize()
#grain2 = spam.label.label.Spheroid(10, 20, numpy.asarray([0,1,0])).digitize()
## Create the bigger labelled image
#grainIm = numpy.concatenate((grain1,grain2))
#grainIm = numpy.zeros(grainIm.shape)
## Add the grains to the bigger image
#grainIm[:grain1.shape[0]-1,:,:] = grain1[:grain1.shape[0]-1,:,:]
#grainIm[grain2.shape[0]-5:-5,:,:] = grainIm[grain2.shape[0]-5:-5,:,:] + grain1[:,:,:]
## Set all the labels to 1
#grainIm = numpy.where(grainIm >= 1, 3, grainIm)
## Pad a border
#grainIm = numpy.pad(grainIm, pad_width=10, mode='constant', constant_values = 0)
## Create the 'greyScale' image
#greyIm = numpy.where(grainIm == 3, 30000, 10000)
## Check that the greyscale image is normalized
#res1 = spam.label.label.fixUndersegmentation(grainIm, greyIm, [3], 10*0.5, 20*0.5)
#self.assertEqual(res1, None)
## Check that a or c is valid
#res2 = spam.label.label.fixUndersegmentation(grainIm, greyIm, [3], numpy.nan, numpy.nan)
#self.assertEqual(res2, None)
## Check that a or c are positive
#res3 = spam.label.label.fixUndersegmentation(grainIm, greyIm, [3], -1, 10)
#self.assertEqual(res3, None)
## Run fixUnderSegmentation
#greyIm = numpy.where(grainIm == 3, 0.75, 0.25)
#res4 = spam.label.label.fixUndersegmentation(grainIm, greyIm, [3], 10*0.8, 20*0.8, vect = [[0,1,0]])
## Check that there are two grains
#self.assertEqual(numpy.max(numpy.unique(res4)), 2)
## Check that it runs even if the label does not exist
#res5 = spam.label.label.fixUndersegmentation(grainIm, greyIm, [3], 10*0.8, 20*0.8, vect = [[0,1,0]])
#self.assertIsNotNone(res5)
## Check for a vect that is not a list
#res6 = spam.label.label.fixUndersegmentation(grainIm, greyIm, [3], 10*0.8, 20*0.8, vect = (0,1,0))
#self.assertIsNone(res6)
## Check that it works without the input vect
#res7 = spam.label.label.fixUndersegmentation(grainIm, greyIm, [3], 10*0.8, 20*0.8, numVect = 1)
#self.assertIsNotNone(res7)
## Check that it works with verbose = True
#res8 = spam.label.label.fixUndersegmentation(grainIm, greyIm, [3], 10*0.8, 20*0.8, numVect = 1, verbose = True)
#self.assertIsNotNone(res8)
## Check that it works even for a non-existing label
#res9 = spam.label.label.fixUndersegmentation(grainIm, greyIm, [5], 10*0.8, 20*0.8, numVect = 1)
#self.assertIsNotNone(res9)
def test_contactOrientations(self):
# Generate two oblate grains
grain1 = spam.label.label.Spheroid(7, 15, numpy.asarray([0,1,0])).digitize()
grain2 = spam.label.label.Spheroid(7, 15, numpy.asarray([0,1,0])).digitize()
grainIm = numpy.concatenate((grain1,grain2))
grainIm = numpy.zeros(grainIm.shape)
grainIm[:grain1.shape[0]-1,:,:] = grain1[:grain1.shape[0]-1,:,:]
grainIm[grain2.shape[0]-5:-5,:,:] = grainIm[grain2.shape[0]-5:-5,:,:] + grain2[:,:,:]
grainIm = numpy.where(grainIm >1,1,grainIm)
# Generate a new grain and create contact
grain3 = 2*spam.label.label.Spheroid(7, 15, numpy.asarray([0,1,0])).digitize()
imLab = numpy.concatenate((grain3,grainIm))
imLab = numpy.zeros(imLab.shape)
imLab[:grain3.shape[0]-1,:,:] = grain3[:grain3.shape[0]-1,:,:]
imLab[grain3.shape[0]-3:-3,:,:] = imLab[grain3.shape[0]-3:-3,:,:] + grainIm[:,:,:]
imLab = numpy.where(imLab >2, 2, imLab)
# Create bin image
imBin = numpy.where(imLab >0, 1, 0)
# Create two rice-like grain
imGrey = spam.kalisphera.makeBlurryNoisySpheroid([40,80,40],[[11,40,20],[29,40,20]],[[10,20],[10,20]], [[0,1,0],[0,1,0]])
# Binary
imBin = imGrey > 0.5
#Label
imLab = spam.label.watershed(imBin)
# Run ITK and check angle of contact
contactNormal, intervox, NotTreatedContact = con.contactOrientations(imBin, imLab, watershed="ITK")
if contactNormal[0]<0: contactNormal = contactNormal*-1
......@@ -751,18 +740,10 @@ class TestFunctionLabel(unittest.TestCase):
def test_convexFillHoles(self):
# Generate two spheres
grain1 = spam.label.label.Spheroid(15, 15, numpy.asarray([0,1,0])).digitize()
grain1 = numpy.pad(grain1, (5), 'constant', constant_values=(0))
grain1 = numpy.where(grain1, 1, 0)
grain2 = spam.label.label.Spheroid(15, 15, numpy.asarray([0,1,0])).digitize()
grain2 = numpy.pad(grain2, (5), 'constant', constant_values=(0))
grain2 = numpy.where(grain2, 2, 0)
grainIm = numpy.concatenate((grain1,grain2))
grainIm = numpy.zeros(grainIm.shape)
grainIm[:grain1.shape[0]-1,:,:] = grain1[:grain1.shape[0]-1,:,:]
grainIm[grain2.shape[0]-5:-5,:,:] = grainIm[grain2.shape[0]-5:-5,:,:] + grain2[:,:,:]
grainIm = grainIm.astype(int)
# Generate two spheres with Kalisphera
grainIm = spam.kalisphera.makeBlurryNoisySphere([60,60,60],[[15,30,30],[45,30,30]], [10,10])
# Label
grainIm = spam.label.watershed(grainIm > 0.5)
# Compute initial volume & COM
COM = spam.label.centresOfMass(grainIm)
iniVol = spam.label.volumes(grainIm).astype(int)
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment