Skip to content
GitLab
Explore
Sign in
Primary navigation
Search or go to…
Project
S
surfseg
Manage
Activity
Members
Labels
Plan
Issues
Issue boards
Milestones
Wiki
Requirements
Code
Merge requests
Repository
Branches
Commits
Tags
Repository graph
Compare revisions
Snippets
Locked files
Build
Pipelines
Jobs
Pipeline schedules
Test cases
Artifacts
Deploy
Releases
Package registry
Container registry
Model registry
Operate
Environments
Terraform modules
Monitor
Incidents
Analyze
Value stream analytics
Contributor analytics
CI/CD analytics
Repository analytics
Code review analytics
Issue analytics
Insights
Model experiments
Help
Help
Support
GitLab documentation
Compare GitLab plans
GitLab community forum
Contribute to GitLab
Provide feedback
Keyboard shortcuts
?
Snippets
Groups
Projects
Show more breadcrumbs
patmjen
surfseg
Commits
5bb31c73
Commit
5bb31c73
authored
Sep 2, 2019
by
Patrick M. Jensen
Browse files
Options
Downloads
Patches
Plain Diff
Add support for ELF sample columns
parent
cc4b6a7e
Branches
Branches containing commit
No related tags found
No related merge requests found
Changes
2
Show whitespace changes
Inline
Side-by-side
Showing
2 changed files
surfseg/matlab/mex_hyperplane_intersect.cpp
+27
-2
27 additions, 2 deletions
surfseg/matlab/mex_hyperplane_intersect.cpp
surfseg/src/surface_segment_4d.cpp
+260
-2
260 additions, 2 deletions
surfseg/src/surface_segment_4d.cpp
with
287 additions
and
4 deletions
surfseg/matlab/mex_hyperplane_intersect.cpp
+
27
−
2
View file @
5bb31c73
...
...
@@ -62,8 +62,8 @@ void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
ensureOrError
(
3
<=
nrhs
&&
nrhs
<=
5
,
"Must supply between 3 and 5 inputs"
);
ensureOrError
(
isSize
(
prhs
[
0
],
{
-
1
,
4
}),
"Tets must be an N x 4 array"
);
ensureOrError
(
isSize
(
prhs
[
1
],
{
-
1
,
4
}),
"Vertices must be an M x 4 array"
);
const
Volume
<
int
>
tetVol
=
getVolumeChecked
<
int
>
(
prhs
[
0
],
"Tets"
);
const
Volume
<
float
>
vertVol
=
getVolumeChecked
<
float
>
(
prhs
[
1
],
"Vertices"
);
const
Volume
<
int
>
tetVol
=
get
Cast
VolumeChecked
<
int
>
(
prhs
[
0
],
"Tets"
);
const
Volume
<
float
>
vertVol
=
get
Cast
VolumeChecked
<
float
>
(
prhs
[
1
],
"Vertices"
);
const
float
time
=
getCastScalarChecked
<
float
>
(
prhs
[
2
],
"time"
);
const
Vec4f
normal
=
nrhs
<
4
?
Vec4f
(
0
,
0
,
0
,
1
)
:
getCastVectorChecked
<
Vec4f
>
(
prhs
[
3
],
"Normal"
);
const
bool
triMesh
=
nrhs
<
5
?
false
:
getCastScalarChecked
<
bool
>
(
prhs
[
4
],
"triMesh"
);
...
...
@@ -74,6 +74,7 @@ void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
std
::
vector
<
Vec4f
>
vertices
;
std
::
vector
<
Vec4i
>
faces
;
// For triangle meshes, we just ignore the last entry
std
::
vector
<
int
>
intTets
;
std
::
vector
<
int
>
faceTetIdxs
;
// Index of tet. each face "came from"
auto
addVertex
=
[
&
](
int
vk
,
Vec4f
v
)
{
...
...
@@ -150,6 +151,11 @@ void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
faces
.
push_back
(
Vec4i
(
nvk0
,
nvk1
,
nvk3
,
-
1
));
faces
.
push_back
(
Vec4i
(
nvk0
,
nvk2
,
nvk3
,
-
1
));
faces
.
push_back
(
Vec4i
(
nvk1
,
nvk2
,
nvk3
,
-
1
));
faceTetIdxs
.
push_back
(
ti
);
faceTetIdxs
.
push_back
(
ti
);
faceTetIdxs
.
push_back
(
ti
);
faceTetIdxs
.
push_back
(
ti
);
}
else
if
(
npos
==
2
&&
nneg
==
2
)
{
// Intersection is a quad
const
auto
idxs
=
sortIdx4
(
s0
,
s1
,
s2
,
s3
);
...
...
@@ -188,8 +194,13 @@ void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
if
(
triMesh
)
{
faces
.
push_back
(
Vec4i
(
nvk0
,
nvk1
,
nvk2
,
-
1
));
faces
.
push_back
(
Vec4i
(
nvk0
,
nvk2
,
nvk3
,
-
1
));
faceTetIdxs
.
push_back
(
ti
);
faceTetIdxs
.
push_back
(
ti
);
}
else
{
faces
.
push_back
(
Vec4i
(
nvk0
,
nvk1
,
nvk2
,
nvk3
));
faceTetIdxs
.
push_back
(
ti
);
}
}
else
if
(
nzer
==
1
)
{
// Intersection is a triangular tet face
...
...
@@ -202,6 +213,8 @@ void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
newKeys
[
i
]
=
addVertex
(
tetKeys
[
idxs
[
i
]],
tetVerts
[
idxs
[
i
]]);
}
faces
.
push_back
(
Vec4i
(
newKeys
[
0
],
newKeys
[
1
],
newKeys
[
2
],
-
1
));
faceTetIdxs
.
push_back
(
ti
);
}
else
if
(
npos
==
1
||
nneg
==
1
)
{
// Intersection is a triangle
const
auto
idxs
=
sortIdx4
(
s0
,
s1
,
s2
,
s3
);
...
...
@@ -237,6 +250,8 @@ void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
int
nvk2
=
addTetIntersection
(
idxA
,
idxB3
);
faces
.
push_back
(
Vec4i
(
nvk0
,
nvk1
,
nvk2
,
-
1
));
faceTetIdxs
.
push_back
(
ti
);
}
else
if
(
nzer
==
2
&&
(
npos
==
2
||
nneg
==
2
))
{
// Intersection is line segment
const
auto
idxs
=
sortIdx4
(
abs
(
s0
),
abs
(
s1
),
abs
(
s2
),
abs
(
s3
));
// We don't care about above or below
...
...
@@ -248,6 +263,8 @@ void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
int
nvk1
=
addVertex
(
tetKeys
[
idxs
[
1
]],
tetVerts
[
idxs
[
1
]]);
faces
.
push_back
(
Vec4i
(
nvk0
,
nvk1
,
-
1
,
-
1
));
faceTetIdxs
.
push_back
(
ti
);
}
else
if
(
nzer
==
1
&&
(
npos
==
3
||
nneg
==
3
))
{
// Intersection is a point
const
auto
idxs
=
sortIdx4
(
abs
(
s0
),
abs
(
s1
),
abs
(
s2
),
abs
(
s3
));
// We don't care about above or below
...
...
@@ -298,5 +315,13 @@ void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
intTetData
[
i
]
=
intTets
[
i
]
+
1
;
}
plhs
[
2
]
=
mxIntTets
;
mxArray
*
mxFaceTetIdxs
=
mxCreateNumericMatrix
(
numFaces
,
1
,
mxDOUBLE_CLASS
,
mxREAL
);
double
*
faceTetIdxsData
=
static_cast
<
double
*>
(
mxGetData
(
mxFaceTetIdxs
));
for
(
int
i
=
0
;
i
<
numFaces
;
++
i
)
{
// Add 1 since MATLAB uses 1-indexing
faceTetIdxsData
[
i
]
=
faceTetIdxs
[
i
]
+
1
;
}
plhs
[
3
]
=
mxFaceTetIdxs
;
}
}
\ No newline at end of file
This diff is collapsed.
Click to expand it.
surfseg/src/surface_segment_4d.cpp
+
260
−
2
View file @
5bb31c73
#include
"surface_segment.h"
#include
<tuple>
#include
<algorithm>
#include
<thread>
#include
<limits>
#include
<GEL/CGLA/Mat3x3f.h>
#include
<GEL/CGLA/Vec4d.h>
#include
"util.h"
static
float
float_identity
(
const
Volume4d
<
float
>&
vol
,
Vec4f
pos
)
...
...
@@ -7,6 +12,26 @@ static float float_identity(const Volume4d<float>& vol, Vec4f pos)
return
vol
.
interp
(
pos
);
}
static
Vec4d
f2dVec
(
Vec4f
v
)
{
return
Vec4d
(
static_cast
<
double
>
(
v
[
0
]),
static_cast
<
double
>
(
v
[
1
]),
static_cast
<
double
>
(
v
[
2
]),
static_cast
<
double
>
(
v
[
3
])
);
}
static
Vec4f
d2fVec
(
Vec4d
v
)
{
return
Vec4f
(
static_cast
<
float
>
(
v
[
0
]),
static_cast
<
float
>
(
v
[
1
]),
static_cast
<
float
>
(
v
[
2
]),
static_cast
<
float
>
(
v
[
3
])
);
}
FloatGraph
&
buildSurfaceGraph4d
(
FloatGraph
&
graph
,
const
Volume
<
float
>&
costSamples
,
TetMesh4d
&
mesh
,
int
maxDiff
,
CostType
costType
,
const
std
::
vector
<
int
>&
frozenVerts
,
size_t
k
=
0
,
size_t
offset
=
0
);
...
...
@@ -14,10 +39,18 @@ FloatGraph& buildSurfaceGraph4d(FloatGraph& graph, const Volume<float>& costSamp
TetMesh4d
&
updateVertices4d
(
const
FloatGraph
&
graph
,
const
Volume
<
float
>&
costSamples
,
TetMesh4d
&
mesh
,
float
sampleStep
,
size_t
k
=
0
,
size_t
offset
=
0
);
TetMesh4d
&
updateVertices4d
(
const
FloatGraph
&
graph
,
const
Volume
<
float
>&
costSamples
,
TetMesh4d
&
mesh
,
const
std
::
vector
<
Vec4f
>&
samplePositions
,
size_t
k
=
0
,
size_t
offset
=
0
);
template
<
class
Func
>
Volume
<
float
>&
extractCostSamples4d
(
const
Volume4d
<
float
>&
cost
,
const
TetMesh4d
&
mesh
,
Volume
<
float
>&
samples
,
int
numSamples
,
float
sampleStep
,
Func
costFunc
,
size_t
k
=
0
);
template
<
class
Func
>
void
extractCostSamples4dElf
(
const
Volume4d
<
float
>&
vol
,
TetMesh4d
&
mesh
,
Volume
<
float
>&
samples
,
std
::
vector
<
Vec4f
>&
samplePositions
,
int
numSamples
,
double
sampleStep
,
Func
costFunc
,
size_t
k
=
0
,
double
tolP
=
1e-4
);
TetMesh4d
surfaceCut4d
(
const
Volume4d
<
float
>&
cost
,
TetMesh4d
mesh
,
int
numSamples
,
float
sampleStep
,
int
maxDiff
,
CostType
costType
,
const
std
::
vector
<
int
>&
frozenVerts
)
{
...
...
@@ -34,8 +67,9 @@ TetMesh4d surfaceCut4d(const Volume4d<float>& vol, TetMesh4d mesh,
// Make cost sample volume
Volume
<
float
>
costSamples
(
numVerts
,
numSamples
,
1
);
std
::
vector
<
Vec4f
>
samplePositions
(
numVerts
*
numSamples
);
costSamples
.
alloc
();
extractCostSamples4d
(
vol
,
mesh
,
costSamples
,
numSamples
,
sampleStep
,
costFunc
);
extractCostSamples4d
Elf
(
vol
,
mesh
,
costSamples
,
samplePositions
,
numSamples
,
sampleStep
,
costFunc
);
costSamples
.
saveToBin
(
"costSamples.bin"
);
// Debug: save cost samples so we can look at them afterwards
...
...
@@ -52,7 +86,7 @@ TetMesh4d surfaceCut4d(const Volume4d<float>& vol, TetMesh4d mesh,
graph
.
maxflow
();
// Update mesh vertex positions
updateVertices4d
(
graph
,
costSamples
,
mesh
,
sample
Step
);
updateVertices4d
(
graph
,
costSamples
,
mesh
,
sample
Positions
);
return
mesh
;
}
...
...
@@ -132,6 +166,23 @@ TetMesh4d& updateVertices4d(const FloatGraph& graph, const Volume<float>& costSa
return
mesh
;
}
TetMesh4d
&
updateVertices4d
(
const
FloatGraph
&
graph
,
const
Volume
<
float
>&
costSamples
,
TetMesh4d
&
mesh
,
const
std
::
vector
<
Vec4f
>&
samplePositions
,
size_t
k
,
size_t
offset
)
{
for
(
auto
&
v
:
mesh
.
vertices
)
{
// Find upper position for this vertex
for
(
int
i
=
costSamples
.
ny
-
1
;
i
>=
0
;
--
i
)
{
size_t
ni
=
costSamples
.
idx
(
v
.
self
,
i
,
k
)
+
offset
;
if
(
graph
.
what_segment
(
ni
)
==
SOURCE
)
{
v
.
pos
=
samplePositions
[
ni
];
break
;
}
}
}
return
mesh
;
}
template
<
class
Func
>
Volume
<
float
>&
extractCostSamples4d
(
const
Volume4d
<
float
>&
vol
,
const
TetMesh4d
&
mesh
,
Volume
<
float
>&
samples
,
int
numSamples
,
float
sampleStep
,
Func
costFunc
,
size_t
k
)
...
...
@@ -151,3 +202,210 @@ Volume<float>& extractCostSamples4d(const Volume4d<float>& vol, const TetMesh4d&
return
samples
;
}
template
<
class
Func
>
void
extractCostSamples4dElf
(
const
Volume4d
<
float
>&
vol
,
TetMesh4d
&
mesh
,
Volume
<
float
>&
samples
,
std
::
vector
<
Vec4f
>&
samplePositions
,
int
numSamples
,
double
sampleStep
,
Func
costFunc
,
size_t
k
,
double
tolP
)
{
// Use sampling method from:
// Yin et al., LOGISMOS-layered optimal graph image segmentation of multiple objects and surfaces...
// 2010, IEEE Transactions on Medical Imaging
using
TetKey
=
TetMesh4d
::
TetKey
;
using
VertKey
=
TetMesh4d
::
VertKey
;
using
Vertex
=
TetMesh4d
::
Vertex
;
constexpr
float
div6
=
1.0f
/
6.0f
;
if
(
numSamples
==
0
)
{
// Nothing to do, so just return now
return
;
}
// Precompute tet. volumes
std
::
vector
<
float
>
tetVolumes
;
tetVolumes
.
reserve
(
mesh
.
tets
.
size
());
for
(
const
auto
&
tet
:
mesh
.
tets
)
{
// Compute volume via determinant of Gram matrix
VertKey
vk0
=
tet
.
verts
[
0
];
VertKey
vk1
=
tet
.
verts
[
1
];
VertKey
vk2
=
tet
.
verts
[
2
];
VertKey
vk3
=
tet
.
verts
[
3
];
Vec4f
u1
=
mesh
.
vertices
[
vk1
].
pos
-
mesh
.
vertices
[
vk0
].
pos
;
Vec4f
u2
=
mesh
.
vertices
[
vk2
].
pos
-
mesh
.
vertices
[
vk0
].
pos
;
Vec4f
u3
=
mesh
.
vertices
[
vk3
].
pos
-
mesh
.
vertices
[
vk0
].
pos
;
Mat3x3f
G
=
Mat3x3f
(
Vec3f
(
dot
(
u1
,
u1
),
dot
(
u1
,
u2
),
dot
(
u1
,
u3
)),
Vec3f
(
dot
(
u2
,
u1
),
dot
(
u2
,
u2
),
dot
(
u2
,
u3
)),
Vec3f
(
dot
(
u3
,
u1
),
dot
(
u3
,
u2
),
dot
(
u3
,
u3
))
);
tetVolumes
.
push_back
(
div6
*
sqrt
(
determinant
(
G
)));
}
// Precompute charge for each vertex
std
::
vector
<
float
>
charges
;
charges
.
reserve
(
mesh
.
vertices
.
size
());
for
(
const
auto
&
v
:
mesh
.
vertices
)
{
std
::
vector
<
TetKey
>
nborTets
;
std
::
tie
(
std
::
ignore
,
std
::
ignore
,
nborTets
)
=
mesh
.
getVertCoboundary
(
v
.
self
);
float
q
=
0
;
for
(
auto
tk
:
nborTets
)
{
q
+=
tetVolumes
[
tk
];
}
charges
.
push_back
(
q
);
}
// Make lambda function for evaluating field
auto
evalE
=
[
&
](
Vec4d
p
)
->
Vec4d
{
Vec4f
pf
=
d2fVec
(
p
);
Vec4f
e
(
0
);
for
(
const
auto
&
v
:
mesh
.
vertices
)
{
Vec4f
rv
=
pf
-
v
.
pos
;
// NOTE: Below method is faster than calling sqr_length
float
r2
=
rv
[
0
]
*
rv
[
0
]
+
rv
[
1
]
*
rv
[
1
]
+
rv
[
2
]
*
rv
[
2
]
+
rv
[
3
]
*
rv
[
3
];
float
r
=
sqrtf
(
r2
);
float
r5
=
r2
*
r2
;
r5
*=
r
;
e
+=
(
charges
[
v
.
self
]
/
r5
)
*
rv
;
// divide by r^(4 + 1)
}
return
f2dVec
(
e
);
};
Volume
<
double
>
samplePositionsVol
(
samples
.
nx
,
samples
.
ny
,
4
);
// DEBUG
samplePositionsVol
.
alloc
();
// Sample volume along electrical field lines (EFLs)
auto
sampleSubset
=
[
&
](
int
begin
,
int
end
)
{
for
(
int
vi
=
begin
;
vi
<
end
;
++
vi
)
{
// We use doubles in this loop for extra precision
const
Vertex
&
v
=
mesh
.
vertices
[
vi
];
Vec4d
nrm
=
f2dVec
(
v
.
normal
);
Vec4d
p
=
f2dVec
(
v
.
pos
);
Vec4f
pf
=
v
.
pos
;
// DEBUG
samplePositionsVol
.
at
(
vi
,
0
,
0
)
=
p
[
0
];
samplePositionsVol
.
at
(
vi
,
0
,
1
)
=
p
[
1
];
samplePositionsVol
.
at
(
vi
,
0
,
2
)
=
p
[
2
];
samplePositionsVol
.
at
(
vi
,
0
,
3
)
=
p
[
3
];
// Take first sample now, before perturbing p
samples
.
at
(
v
.
self
,
0
,
k
)
=
vol
.
contains
(
pf
)
?
costFunc
(
vol
,
pf
)
:
infOrMax
<
float
>
;
samplePositions
[
samples
.
idx
(
v
.
self
,
0
,
k
)]
=
pf
;
// Perturp position along the normal direction so the EFL points in the right direction
const
double
perturb
=
sampleStep
*
0.002
;
p
+=
nrm
*
perturb
;
// Initialize h so the first step will have length close to 0.001 * sampleStep
// Empirically, this allows us to accept a step in 1-2 attempts
double
perturb4
=
perturb
*
perturb
;
perturb4
*=
perturb4
;
double
h
=
2
*
0.001
*
sampleStep
*
perturb4
/
charges
[
v
.
self
];
const
double
minStep
=
h
*
6e-8
;
// We allow approx. 24 halvings from the initial step
for
(
int
i
=
1
;
i
<
numSamples
;
++
i
)
{
double
crntLen
=
0
;
// Integrate field equations using RKF45 with adaptive step (step doubling)
while
(
true
)
{
// Define all RKF45 coefficients
constexpr
double
a21
=
0.25
;
constexpr
double
a31
=
3.0
/
32.0
;
constexpr
double
a32
=
9.0
/
32.0
;
constexpr
double
a41
=
1932.0
/
2197.0
;
constexpr
double
a42
=
-
7200.0
/
2197.0
;
constexpr
double
a43
=
7296.0
/
2197.0
;
constexpr
double
a51
=
439.0
/
216.0
;
constexpr
double
a52
=
-
8.0
;
constexpr
double
a53
=
3680.0
/
513.0
;
constexpr
double
a54
=
-
845.0
/
4104.0
;
constexpr
double
a61
=
-
8.0
/
27.0
;
constexpr
double
a62
=
2.0
;
constexpr
double
a63
=
-
3544.0
/
2565.0
;
constexpr
double
a64
=
1859.0
/
4104.0
;
constexpr
double
a65
=
-
11.0
/
40.0
;
constexpr
double
s41
=
25.0
/
216.0
;
constexpr
double
s43
=
1408.0
/
2565.0
;
constexpr
double
s44
=
2197.0
/
4104.0
;
constexpr
double
s45
=
-
1.0
/
5.0
;
constexpr
double
s51
=
16.0
/
135.0
;
constexpr
double
s53
=
6656.0
/
12825.0
;
constexpr
double
s54
=
28561.0
/
56430.0
;
constexpr
double
s55
=
-
9.0
/
50.0
;
constexpr
double
s56
=
2.0
/
55.0
;
// Compute 4th and 5th order steps (dp4 and dp5, respectively)
const
Vec4d
k1
=
h
*
evalE
(
p
);
const
Vec4d
k2
=
h
*
evalE
(
p
+
a21
*
k1
);
const
Vec4d
k3
=
h
*
evalE
(
p
+
a31
*
k1
+
a32
*
k2
);
const
Vec4d
k4
=
h
*
evalE
(
p
+
a41
*
k1
+
a42
*
k2
+
a43
*
k3
);
const
Vec4d
k5
=
h
*
evalE
(
p
+
a51
*
k1
+
a52
*
k2
+
a53
*
k3
+
a54
*
k4
);
const
Vec4d
k6
=
h
*
evalE
(
p
+
a61
*
k1
+
a62
*
k2
+
a63
*
k3
+
a64
*
k4
+
a65
*
k5
);
const
Vec4d
dp4
=
s41
*
k1
+
s43
*
k3
+
s44
*
k4
+
s45
*
k5
;
const
Vec4d
dp5
=
s51
*
k1
+
s53
*
k3
+
s54
*
k4
+
s55
*
k5
+
s56
*
k6
;
// If the estimated error is too large we halve the step and try again
if
(
length
(
dp4
-
dp5
)
>
tolP
&&
h
>
minStep
)
{
h
*=
0.5
;
continue
;
}
const
double
dpLen
=
length
(
dp4
);
if
(
crntLen
+
dpLen
>=
sampleStep
)
{
// Truncate step so we don't go too far
// TODO: Maybe find better way to acheive this
double
a
=
(
sampleStep
-
crntLen
)
/
dpLen
;
p
+=
a
*
dp4
;
// We are now at the next sample position, so stop iterations and reset
break
;
}
p
+=
dp4
;
crntLen
+=
dpLen
;
// Step worked so double for next iteration.
h
*=
2
;
}
samplePositionsVol
.
at
(
vi
,
i
,
0
)
=
p
[
0
];
samplePositionsVol
.
at
(
vi
,
i
,
1
)
=
p
[
1
];
samplePositionsVol
.
at
(
vi
,
i
,
2
)
=
p
[
2
];
samplePositionsVol
.
at
(
vi
,
i
,
3
)
=
p
[
3
];
pf
=
d2fVec
(
p
);
samples
.
at
(
v
.
self
,
i
,
k
)
=
vol
.
contains
(
pf
)
?
costFunc
(
vol
,
pf
)
:
infOrMax
<
float
>
;
samplePositions
[
samples
.
idx
(
v
.
self
,
i
,
k
)]
=
pf
;
}
}
};
int
numThreads
=
4
;
// TODO: Let this be an input to the function
int
numVert
=
mesh
.
vertices
.
size
();
if
(
numThreads
==
1
)
{
// Just run on this thread
sampleSubset
(
0
,
numVert
);
}
else
{
// Split sampling among threads
int
verticesPerThread
=
(
numVert
+
(
numVert
%
numThreads
==
0
?
0
:
numThreads
))
/
numThreads
;
std
::
vector
<
std
::
thread
>
threads
;
for
(
int
i
=
0
;
i
<
numThreads
;
++
i
)
{
int
begin
=
i
*
verticesPerThread
;
int
end
=
std
::
min
((
i
+
1
)
*
verticesPerThread
,
numVert
);
threads
.
push_back
(
std
::
thread
(
sampleSubset
,
begin
,
end
));
}
// Wait for all threads to finish
for
(
auto
&
th
:
threads
)
{
if
(
th
.
joinable
())
{
th
.
join
();
}
}
}
samplePositionsVol
.
saveToBin
(
"samplePositionsVol.bin"
);
}
This diff is collapsed.
Click to expand it.
Preview
0%
Loading
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Save comment
Cancel
Please
register
or
sign in
to comment