Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
E
eos
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Analytics
Analytics
CI / CD
Repository
Value Stream
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Richard Torenvliet
eos
Commits
ddcce250
Commit
ddcce250
authored
Jan 25, 2017
by
Richard Torenvliet
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add tests for projection error - not realy tests but accuracy measure tests
parent
df1e1286
Changes
6
Hide whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
329 additions
and
20 deletions
+329
-20
tests/CMakeLists.txt
tests/CMakeLists.txt
+9
-12
tests/data/image_0010.pts
tests/data/image_0010.pts
+72
-0
tests/fitting/fitting_test.cpp
tests/fitting/fitting_test.cpp
+0
-8
tests/fitting/fitting_test.hpp
tests/fitting/fitting_test.hpp
+235
-0
tests/fitting/linear_shape_fitting_test.hpp
tests/fitting/linear_shape_fitting_test.hpp
+6
-0
tests/main_tests.cpp
tests/main_tests.cpp
+7
-0
No files found.
tests/CMakeLists.txt
View file @
ddcce250
...
...
@@ -42,19 +42,16 @@ set(CATCH_INCLUDE_DIR ${CMAKE_SOURCE_DIR}/3rdparty/Catch/include CACHE INTERNAL
add_library
(
Catch INTERFACE
)
target_include_directories
(
Catch INTERFACE
${
CATCH_INCLUDE_DIR
}
)
add_executable
(
fitting-test fitting/fitting_test.cpp
)
target_link_libraries
(
fitting-test Catch
${
OPENCV_LIBS
}
${
BOOST_LIBRARIES
}
)
set
(
${
EOS_TESTS_DIRS
}
${
CMAKE_SOURCE_DIR
}
/tests/fitting
"Paths to test include directories"
)
add_
test
(
NAME fitting-test COMMAND fitting-test
)
install
(
TARGETS fitting-test DESTINATION tests
)
add_
executable
(
main-tests main_tests.cpp
)
target_link_libraries
(
main-tests Catch
${
EOS_TESTS_DIRS
}
${
OpenCV_LIBS
}
${
Boost_LIBRARIES
}
)
install
(
TARGETS main-tests DESTINATION tests
)
install
(
DIRECTORY
${
CMAKE_SOURCE_DIR
}
/tests/data DESTINATION tests
)
# Simple model fitting (orthographic camera & shape to landmarks) example:
#add_executable(fit-model-simple fit-model-simple.cpp)
#add_executable(fitting-tests )
#target_link_libraries(fit-model-simple ${OpenCV_LIBS} ${Boost_LIBRARIES})
# install target:
#install(TARGETS fit-model-simple DESTINATION bin)
#install(DIRECTORY ${CMAKE_SOURCE_DIR}/examples/data DESTINATION bin)
# add tests
enable_testing
()
add_test
(
NAME fitting-test COMMAND fitting-test
)
add_test
(
NAME linear-shape-fitting-test COMMAND linear-shape-fitting-test
)
tests/data/image_0010.pts
0 → 100644
View file @
ddcce250
version: 1
n_points: 68
{
611.284152 272.773913
607.899810 304.319120
613.094672 339.255590
622.808910 372.005502
632.669412 404.678824
643.511765 432.387059
651.944706 460.095294
665.273062 490.435795
695.836418 505.268304
732.882854 507.601124
771.140919 496.682178
814.580000 468.528235
852.378512 442.776782
879.780380 411.071469
894.313442 365.675605
905.385571 315.920470
912.766990 265.622269
608.920127 214.000560
624.612439 205.921886
641.092550 213.028614
656.643518 218.496370
675.118172 229.895972
714.507903 232.709448
746.296501 221.279247
780.463683 216.098001
814.843700 219.577695
843.144930 231.216340
691.116694 258.293884
686.687460 284.649159
678.837382 314.952208
671.575170 341.518858
662.787059 357.695294
672.577890 363.872007
684.099935 368.436761
701.177972 366.977129
716.657204 364.432781
629.308705 253.419965
641.102353 239.634118
656.763529 242.043529
675.896682 261.234513
657.320778 266.311140
639.427321 264.662791
746.963287 268.769913
763.151112 249.645230
792.895294 251.681176
810.965882 264.932941
793.647370 274.870034
770.089168 277.392535
650.466760 402.919129
662.802218 396.521655
674.655327 393.178631
685.548727 399.199281
699.727862 394.238444
720.617079 403.327949
762.033016 412.238881
727.622851 431.747041
706.539636 437.383098
691.346572 438.212545
679.045609 436.837854
665.204535 428.934293
657.444159 406.740225
677.532790 409.255983
688.027122 410.142694
701.031914 411.571227
750.088005 413.800864
701.031914 411.571227
688.027122 410.142694
677.532790 409.255983
}
\ No newline at end of file
tests/fitting/fitting_test.cpp
deleted
100644 → 0
View file @
df1e1286
#define CATCH_CONFIG_MAIN
#include "catch.hpp"
TEST_CASE
(
"stupid/1=1"
,
"Prove that one equals two"
){
int
one
=
1
;
REQUIRE
(
one
==
1
);
}
tests/fitting/fitting_test.hpp
0 → 100644
View file @
ddcce250
#include "catch.hpp"
#include "glm/ext.hpp"
#include "glm/gtc/matrix_transform.hpp"
#include "glm/gtx/transform.hpp"
#include "eos/core/Landmark.hpp"
#include "eos/core/LandmarkMapper.hpp"
#include "eos/fitting/orthographic_camera_estimation_linear.hpp"
#include "eos/fitting/RenderingParameters.hpp"
#include "eos/fitting/linear_shape_fitting.hpp"
#include "eos/render/utils.hpp"
#include "eos/render/texture_extraction.hpp"
#include "eos/render/render.hpp"
#include "eos/render/detail/render_detail.hpp"
#include "opencv2/core/core.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "boost/program_options.hpp"
#include "boost/filesystem.hpp"
#include <vector>
#include <iostream>
#include <fstream>
#include <tuple>
#include <random>
#include <cmath>
#include <numeric>
using
namespace
eos
;
namespace
po
=
boost
::
program_options
;
namespace
fs
=
boost
::
filesystem
;
using
eos
::
core
::
Landmark
;
using
eos
::
core
::
LandmarkCollection
;
using
cv
::
Mat
;
using
cv
::
Vec2f
;
using
cv
::
Vec3f
;
using
cv
::
Vec4f
;
using
std
::
cout
;
using
std
::
endl
;
using
std
::
vector
;
using
std
::
string
;
using
std
::
tuple
;
/**
* Reads an ibug .pts landmark file and returns an ordered vector with
* the 68 2D landmark coordinates.
*
* @param[in] filename Path to a .pts file.
* @return An ordered vector with the 68 ibug landmarks.
*/
LandmarkCollection
<
cv
::
Vec2f
>
read_pts_landmarks
(
std
::
string
filename
)
{
using
std
::
getline
;
using
cv
::
Vec2f
;
using
std
::
string
;
LandmarkCollection
<
Vec2f
>
landmarks
;
landmarks
.
reserve
(
68
);
std
::
ifstream
file
(
filename
);
if
(
!
file
.
is_open
())
{
throw
std
::
runtime_error
(
string
(
"Could not open landmark file: "
+
filename
));
}
string
line
;
// Skip the first 3 lines, they're header lines:
getline
(
file
,
line
);
// 'version: 1'
getline
(
file
,
line
);
// 'n_points : 68'
getline
(
file
,
line
);
// '{'
int
ibugId
=
1
;
while
(
getline
(
file
,
line
))
{
if
(
line
==
"}"
)
{
// end of the file
break
;
}
std
::
stringstream
lineStream
(
line
);
Landmark
<
Vec2f
>
landmark
;
landmark
.
name
=
std
::
to_string
(
ibugId
);
if
(
!
(
lineStream
>>
landmark
.
coordinates
[
0
]
>>
landmark
.
coordinates
[
1
]))
{
throw
std
::
runtime_error
(
string
(
"Landmark format error while parsing the line: "
+
line
));
}
// From the iBug website:
// "Please note that the re-annotated data for this challenge are saved in the Matlab convention of 1 being
// the first index, i.e. the coordinates of the top left pixel in an image are x=1, y=1."
// ==> So we shift every point by 1:
landmark
.
coordinates
[
0
]
-=
1.0
f
;
landmark
.
coordinates
[
1
]
-=
1.0
f
;
landmarks
.
emplace_back
(
landmark
);
++
ibugId
;
}
return
landmarks
;
};
morphablemodel
::
MorphableModel
loadTestModel
()
{
return
morphablemodel
::
load_model
(
"share/sfm_shape_3448.bin"
);
}
/**
* Loads test data. Returns
* @param landmarks
* @param landmark_mapper
* @return
*/
std
::
tuple
<
morphablemodel
::
MorphableModel
,
vector
<
Vec4f
>
,
vector
<
int
>
,
vector
<
Vec2f
>>
loadTestData
(
LandmarkCollection
<
cv
::
Vec2f
>
landmarks
,
core
::
LandmarkMapper
landmark_mapper
)
{
morphablemodel
::
MorphableModel
morphable_model
=
loadTestModel
();
// These will be the final 2D and 3D points used for the fitting:
vector
<
Vec4f
>
model_points
;
// the points in the 3D shape model
vector
<
int
>
vertex_indices
;
// their vertex indices
vector
<
Vec2f
>
image_points
;
// the corresponding 2D landmark points
// Sub-select all the landmarks which we have a mapping for (i.e. that are defined in the 3DMM):
for
(
int
i
=
0
;
i
<
landmarks
.
size
();
++
i
)
{
auto
converted_name
=
landmark_mapper
.
convert
(
landmarks
[
i
].
name
);
// no mapping defined for the current landmark
if
(
!
converted_name
)
{
continue
;
}
int
vertex_idx
=
std
::
stoi
(
converted_name
.
get
());
Vec4f
vertex
=
morphable_model
.
get_shape_model
().
get_mean_at_point
(
vertex_idx
);
model_points
.
emplace_back
(
vertex
);
vertex_indices
.
emplace_back
(
vertex_idx
);
image_points
.
emplace_back
(
landmarks
[
i
].
coordinates
);
}
return
std
::
make_tuple
(
morphable_model
,
model_points
,
vertex_indices
,
image_points
);
}
/**
* Helper function to calculate the euclidean distance between the landmark and a projected
* point. Nothing more than Pythogas.
*
* @param landmark
* @param vertex_screen_coords
* @return
*/
inline
float
euclidean_distance
(
cv
::
Vec2f
landmark
,
cv
::
Mat
vertex_screen_coords
)
{
float
screen_x
=
vertex_screen_coords
.
at
<
float
>
(
0
,
0
);
float
screen_y
=
vertex_screen_coords
.
at
<
float
>
(
1
,
0
);
// Calc squared differences, ready for use in Pythagoras.
float
landmark_diff_x_sq
=
std
::
fabs
(
landmark
[
0
]
-
screen_x
)
*
std
::
fabs
(
landmark
[
0
]
-
screen_x
);
float
landmark_diff_y_sq
=
std
::
fabs
(
landmark
[
1
]
-
screen_y
)
*
std
::
fabs
(
landmark
[
0
]
-
screen_x
);
return
std
::
sqrt
(
landmark_diff_x_sq
+
landmark_diff_y_sq
);
}
TEST_CASE
(
"Test ortographic projection"
,
"[projection]"
){
// ======== begin setup ============
Mat
image
=
cv
::
imread
(
"tests/data/image_0010.png"
);
LandmarkCollection
<
cv
::
Vec2f
>
landmarks
;
landmarks
=
read_pts_landmarks
(
"tests/data/image_0010.pts"
);
core
::
LandmarkMapper
landmark_mapper
=
core
::
LandmarkMapper
(
"share/ibug2did.txt"
);
vector
<
Vec4f
>
model_points
;
// the points in the 3D shape model
vector
<
int
>
vertex_indices
;
// their vertex indices
vector
<
Vec2f
>
image_points
;
// the corresponding 2D landmark points
morphablemodel
::
MorphableModel
morphable_model
;
std
::
tie
(
morphable_model
,
model_points
,
vertex_indices
,
image_points
)
=
loadTestData
(
landmarks
,
landmark_mapper
);
// Estimate the camera (pose) from the 2D - 3D point correspondences
fitting
::
ScaledOrthoProjectionParameters
pose
=
fitting
::
estimate_orthographic_projection_linear
(
image_points
,
model_points
,
true
,
image
.
rows
);
fitting
::
RenderingParameters
rendering_params
(
pose
,
image
.
cols
,
image
.
rows
);
// Estimate the shape coefficients by fitting the shape to the landmarks:
Mat
affine_from_ortho
=
fitting
::
get_3x4_affine_camera_matrix
(
rendering_params
,
image
.
cols
,
image
.
rows
);
vector
<
float
>
fitted_coeffs
=
fitting
::
fit_shape_to_landmarks_linear
(
morphable_model
,
affine_from_ortho
,
image_points
,
vertex_indices
);
// Obtain the full mesh with the estimated coefficients:
render
::
Mesh
mesh
=
morphable_model
.
draw_sample
(
fitted_coeffs
,
vector
<
float
>
());
// ======== end setup ============
SECTION
(
"Landmark projection error"
)
{
vector
<
float
>
total_error
;
// Sub-select all the landmarks which we have a mapping for (i.e. that are defined in the 3DMM):
for
(
int
i
=
0
;
i
<
landmarks
.
size
();
++
i
)
{
auto
converted_name
=
landmark_mapper
.
convert
(
landmarks
[
i
].
name
);
// no mapping defined for the current landmark
if
(
!
converted_name
)
{
continue
;
}
int
vertex_idx
=
std
::
stoi
(
converted_name
.
get
());
// The vertex_idx should be around the value of the original coordinates after we have
// projected it with the affine_from_ortho that is obtained earlier.
Mat
vertex_screen_coords
=
affine_from_ortho
*
Mat
(
cv
::
Vec4f
(
mesh
.
vertices
[
vertex_idx
].
x
,
mesh
.
vertices
[
vertex_idx
].
y
,
mesh
.
vertices
[
vertex_idx
].
z
,
mesh
.
vertices
[
vertex_idx
].
w
)
);
// using euclidean distance here, but should look at other ways too.
total_error
.
push_back
(
euclidean_distance
(
landmarks
[
i
].
coordinates
,
vertex_screen_coords
));
}
// Caculate mean error and stddev.
float
accum
=
0.0
;
float
mean_error
=
std
::
accumulate
(
total_error
.
begin
(),
total_error
.
end
(),
0
)
/
landmarks
.
size
();
// cacl. standardeviation
std
::
for_each
(
std
::
begin
(
total_error
),
std
::
end
(
total_error
),
[
&
](
const
float
d
)
{
accum
+=
(
d
-
mean_error
)
*
(
d
-
mean_error
);
});
float
stddev
=
std
::
sqrt
(
accum
/
(
total_error
.
size
()
-
1
));
CAPTURE
(
mean_error
);
CAPTURE
(
stddev
);
// TODO: make better requirements / tests for these values.
// These are just based on the current output of the tests, it however make sure that we do
// not go over these values while altering eos code.
REQUIRE
(
mean_error
<
4.0
f
);
REQUIRE
(
stddev
<
5.0
f
);
}
}
tests/fitting/linear_shape_fitting_test.hpp
0 → 100644
View file @
ddcce250
// Example file, show to use Catch with multiple files through one main_tests file.
#include "catch.hpp"
TEST_CASE
(
"Test 1 == 1"
,
"[fitting]"
)
{
REQUIRE
(
1
==
1
);
}
\ No newline at end of file
tests/main_tests.cpp
0 → 100644
View file @
ddcce250
#define CATCH_CONFIG_MAIN
#include "catch.hpp"
// Include all tests here and don't forget to alter the CMakeLists.txt in the tests folder,
// that means use add_test to add the a new test file.
#include "fitting/fitting_test.hpp"
#include "fitting/linear_shape_fitting_test.hpp"
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment