Commit beedf9a5 authored by Richard Torenvliet's avatar Richard Torenvliet

Moved tests and made them work again, added sphinx documentation and...

Moved tests and made them work again, added sphinx documentation and added/updated documentation, #4, #5
parent 2e70ee46
...@@ -17,21 +17,16 @@ data/pca_shape_model.npy: ...@@ -17,21 +17,16 @@ data/pca_shape_model.npy:
python src/main.py \ python src/main.py \
--save_pca_shape \ --save_pca_shape \
--files `./scripts/imm_train_set.sh` \ --files `./scripts/imm_train_set.sh` \
--model_shape_file data/pca_shape_model --model_shape_file data/pca_shape_model \
--shape_type imm
data/pca_texture_model.npy: data/pca_texture_model.npy:
python src/main.py \ python src/main.py \
--save_pca_texture \ --save_pca_texture \
--files `./scripts/imm_train_set.sh` \ --files `./scripts/imm_train_set.sh` \
--model_texture_file data/pca_texture_model \ --model_texture_file data/pca_texture_model \
--model_shape_file data/pca_shape_model.npy --model_shape_file data/pca_shape_model.npy \
--shape_type imm
show_pca:
python src/main.py \
--show_pca \
--model_texture_file data/pca_texture_model.npy \
--model_shape_file data/pca_shape_model.npy
test_model: test_model:
python src/main.py \ python src/main.py \
...@@ -47,6 +42,7 @@ show_reconstruction: ...@@ -47,6 +42,7 @@ show_reconstruction:
--files data/imm_face_db/*.asf \ --files data/imm_face_db/*.asf \
--model_texture_file data/pca_texture_model.npy \ --model_texture_file data/pca_texture_model.npy \
--model_shape_file data/pca_shape_model.npy \ --model_shape_file data/pca_shape_model.npy \
--shape_type imm \
--n_components 6 --n_components 6
profile_reconstruction: profile_reconstruction:
...@@ -55,6 +51,7 @@ profile_reconstruction: ...@@ -55,6 +51,7 @@ profile_reconstruction:
--files data/imm_face_db/*.asf \ --files data/imm_face_db/*.asf \
--model_texture_file data/pca_texture_model.npy \ --model_texture_file data/pca_texture_model.npy \
--model_shape_file data/pca_shape_model.npy \ --model_shape_file data/pca_shape_model.npy \
--shape_type imm \
--n_components 6 --n_components 6
graph_reconstruction: graph_reconstruction:
...@@ -63,6 +60,7 @@ graph_reconstruction: ...@@ -63,6 +60,7 @@ graph_reconstruction:
--files data/imm_face_db/*.asf \ --files data/imm_face_db/*.asf \
--model_texture_file data/pca_texture_model.npy \ --model_texture_file data/pca_texture_model.npy \
--model_shape_file data/pca_shape_model.npy \ --model_shape_file data/pca_shape_model.npy \
--shape_type imm \
--n_components 6 --n_components 6
show_kivy: show_kivy:
...@@ -74,10 +72,7 @@ show_kivy: ...@@ -74,10 +72,7 @@ show_kivy:
--n_components 6 --n_components 6
test: test:
python -m py.test -f src/*_test.py python -m py.test -f src/test/*_test.py
test_modules:
python -m py.test -f src/*/*_test.py
server: server:
(cd src/; python -m tornado.autoreload server.py) (cd src/; python -m tornado.autoreload server.py)
......
{
"metadata": {
"name": "",
"signature": "sha256:5555a1bfabe8a1649a14d2623b12e662713734bd51ade79d5fadb9e9c2769528"
},
"nbformat": 3,
"nbformat_minor": 0,
"worksheets": [
{
"cells": [
{
"cell_type": "code",
"collapsed": false,
"input": [
"import numpy as np\n",
"import matplotlib.pyplot as plt"
],
"language": "python",
"metadata": {},
"outputs": [],
"prompt_number": 14
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"x = np.array([8, 2, 11, 6, 5, 4, 12, 9, 6, 1])"
],
"language": "python",
"metadata": {},
"outputs": [],
"prompt_number": 19
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"xzm = x - x.mean()"
],
"language": "python",
"metadata": {},
"outputs": [],
"prompt_number": 20
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"y = np.array([3, 10, 3, 6, 8, 12, 1, 4, 9, 14])"
],
"language": "python",
"metadata": {},
"outputs": [],
"prompt_number": 21
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"yzm = y - y.mean()"
],
"language": "python",
"metadata": {},
"outputs": [],
"prompt_number": 22
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"xzm"
],
"language": "python",
"metadata": {},
"outputs": [
{
"metadata": {},
"output_type": "pyout",
"prompt_number": 8,
"text": [
"array([ 1.6, -4.4, 4.6, -0.4, -1.4, -2.4, 5.6, 2.6, -0.4, -5.4])"
]
}
],
"prompt_number": 8
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"yzm"
],
"language": "python",
"metadata": {},
"outputs": [
{
"metadata": {},
"output_type": "pyout",
"prompt_number": 9,
"text": [
"array([-4., 3., -4., -1., 1., 5., -6., -3., 2., 7.])"
]
}
],
"prompt_number": 9
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"covXY = np.dot(xzm, yzm.T)\n",
"print covXY"
],
"language": "python",
"metadata": {},
"outputs": [
{
"output_type": "stream",
"stream": "stdout",
"text": [
"-131.0\n"
]
}
],
"prompt_number": 10
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"covXX = np.dot(xzm, xzm.T)\n",
"print covXX"
],
"language": "python",
"metadata": {},
"outputs": [
{
"output_type": "stream",
"stream": "stdout",
"text": [
"118.4\n"
]
}
],
"prompt_number": 11
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"m = covXY / covXX\n",
"print m"
],
"language": "python",
"metadata": {},
"outputs": [
{
"output_type": "stream",
"stream": "stdout",
"text": [
"-1.10641891892\n"
]
}
],
"prompt_number": 12
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"b = y.mean() - m * x.mean()\n",
"print b"
],
"language": "python",
"metadata": {},
"outputs": [
{
"output_type": "stream",
"stream": "stdout",
"text": [
"14.0810810811\n"
]
}
],
"prompt_number": 13
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"plt.clf()"
],
"language": "python",
"metadata": {},
"outputs": [],
"prompt_number": 24
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"plt.plot(x, y)"
],
"language": "python",
"metadata": {},
"outputs": [
{
"metadata": {},
"output_type": "pyout",
"prompt_number": 25,
"text": [
"[<matplotlib.lines.Line2D at 0x10fb1de50>]"
]
}
],
"prompt_number": 25
},
{
"cell_type": "code",
"collapsed": false,
"input": [],
"language": "python",
"metadata": {},
"outputs": []
}
],
"metadata": {}
}
]
}
\ No newline at end of file
{
"metadata": {
"name": "",
"signature": "sha256:4d1638c5bd5d0bb970509b7736b284687ebe625a76ca590a65d730ddd8f0b58d"
},
"nbformat": 3,
"nbformat_minor": 0,
"worksheets": [
{
"cells": [
{
"cell_type": "code",
"collapsed": false,
"input": [
"import numpy as np"
],
"language": "python",
"metadata": {},
"outputs": [],
"prompt_number": 1
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"U = np.array([[1,2,3]])"
],
"language": "python",
"metadata": {},
"outputs": [],
"prompt_number": 2
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"s = np.array([4,5,6])"
],
"language": "python",
"metadata": {},
"outputs": [],
"prompt_number": 3
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"Vt = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])"
],
"language": "python",
"metadata": {},
"outputs": [],
"prompt_number": 5
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"S = np.diag(s)"
],
"language": "python",
"metadata": {},
"outputs": [],
"prompt_number": 6
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"Us = np.dot(U, S)"
],
"language": "python",
"metadata": {},
"outputs": [],
"prompt_number": 10
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"print Us"
],
"language": "python",
"metadata": {},
"outputs": [
{
"output_type": "stream",
"stream": "stdout",
"text": [
"[[ 4 10 18]]\n"
]
}
],
"prompt_number": 11
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"np.dot(Us, Vt)"
],
"language": "python",
"metadata": {},
"outputs": [
{
"metadata": {},
"output_type": "pyout",
"prompt_number": 12,
"text": [
"array([[170, 202, 234]])"
]
}
],
"prompt_number": 12
},
{
"cell_type": "code",
"collapsed": false,
"input": [],
"language": "python",
"metadata": {},
"outputs": []
}
],
"metadata": {}
}
]
}
\ No newline at end of file
{
"metadata": {
"name": "",
"signature": "sha256:d27d22a739b636b950ada4fb7cf213caec94cb5a1741efc7de5ff6ede09615ca"
},
"nbformat": 3,
"nbformat_minor": 0,
"worksheets": [
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": []
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"import numpy as np\n",
"import matplotlib.pyplot as plt"
],
"language": "python",
"metadata": {},
"outputs": []
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"x = np.array([8, 2, 11, 6, 5, 4, 12, 9, 6, 1])"
],
"language": "python",
"metadata": {},
"outputs": []
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"xzm = x - x.mean()"
],
"language": "python",
"metadata": {},
"outputs": [],
"prompt_number": 3
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"y = np.array([3, 10, 3, 6, 8, 12, 1, 4, 9, 14])"
],
"language": "python",
"metadata": {},
"outputs": [],
"prompt_number": 4
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"yzm = y - y.mean()"
],
"language": "python",
"metadata": {},
"outputs": [],
"prompt_number": 5
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"xzm"
],
"language": "python",
"metadata": {},
"outputs": [
{
"metadata": {},
"output_type": "pyout",
"prompt_number": 12,
"text": [
"array([ 1.6, -4.4, 4.6, -0.4, -1.4, -2.4, 5.6, 2.6, -0.4, -5.4])"
]
}
],
"prompt_number": 12
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"yzm"
],
"language": "python",
"metadata": {},
"outputs": [
{
"metadata": {},
"output_type": "pyout",
"prompt_number": 13,
"text": [
"array([-4., 3., -4., -1., 1., 5., -6., -3., 2., 7.])"
]
}
],
"prompt_number": 13
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"covXY = np.dot(xzm, yzm.T)\n",
"print covXY"
],
"language": "python",
"metadata": {},
"outputs": [
{
"output_type": "stream",
"stream": "stdout",
"text": [
"-131.0\n"
]
}
],
"prompt_number": 14
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"covXX = np.dot(xzm, xzm.T)\n",
"print covXX"
],
"language": "python",
"metadata": {},
"outputs": [
{
"output_type": "stream",
"stream": "stdout",
"text": [
"118.4\n"
]
}
],
"prompt_number": 15
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"m = covXY / covXX\n",
"print m"
],
"language": "python",
"metadata": {},
"outputs": [
{
"output_type": "stream",
"stream": "stdout",
"text": [
"-1.10641891892\n"
]
}
],
"prompt_number": 16
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"b = y.mean() - m * x.mean()\n",
"print b"
],
"language": "python",
"metadata": {},
"outputs": [
{
"output_type": "stream",
"stream": "stdout",
"text": [
"14.0810810811\n"
]
}
],
"prompt_number": 82
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"plt.clf()\n",
"plt.grid()"
],
"language": "python",
"metadata": {},
"outputs": [],
"prompt_number": 97
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"plt.scatter(x, y)"
],
"language": "python",
"metadata": {},
"outputs": [
{
"metadata": {},
"output_type": "pyout",
"prompt_number": 104,
"text": [
"<matplotlib.collections.PathCollection at 0x1106a3710>"
]
}
],
"prompt_number": 104
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"range_x = np.arange(0, 14, 0.5)\n",
"print range_x"
],
"language": "python",
"metadata": {},
"outputs": [
{
"output_type": "stream",
"stream": "stdout",
"text": [
"[ 0. 0.5 1. 1.5 2. 2.5 3. 3.5 4. 4.5 5. 5.5\n",
" 6. 6.5 7. 7.5 8. 8.5 9. 9.5 10. 10.5 11. 11.5\n",
" 12. 12.5 13. 13.5]\n"
]
}
],
"prompt_number": 105
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"y_lstq = [m * i + b for i in x]\n",
"print y_lstq"
],
"language": "python",
"metadata": {},
"outputs": [
{
"output_type": "stream",
"stream": "stdout",
"text": [
"[5.2297297297297298, 11.868243243243242, 1.9104729729729737, 7.4425675675675675, 8.548986486486486, 9.6554054054054053, 0.80405405405405439, 4.1233108108108105, 7.4425675675675675, 12.974662162162161]\n"
]
}
],
"prompt_number": 106
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"plt.plot(x, y_lstq)"
],
"language": "python",
"metadata": {},
"outputs": [
{
"metadata": {},
"output_type": "pyout",
"prompt_number": 107,
"text": [
"[<matplotlib.lines.Line2D at 0x1106ae150>]"
]
}
],
"prompt_number": 107
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"plt.scatter(x.mean(), y.mean(), marker='s', color='b')"
],
"language": "python",
"metadata": {},
"outputs": [
{
"metadata": {},
"output_type": "pyout",
"prompt_number": 108,
"text": [
"<matplotlib.collections.PathCollection at 0x110659390>"
]
}
],
"prompt_number": 108
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"plt.scatter(xzm, yzm, color='r')"
],
"language": "python",
"metadata": {},
"outputs": [
{
"metadata": {},
"output_type": "pyout",
"prompt_number": 110,
"text": [
"<matplotlib.collections.PathCollection at 0x110b91650>"
]
}
],
"prompt_number": 110
},
{
"cell_type": "code",
"collapsed": false,
"input": [],
"language": "python",
"metadata": {},
"outputs": []
}
],
"metadata": {}
}
]
}
\ No newline at end of file
{
"metadata": {
"name": "",
"signature": "sha256:af47a85b07d8fc08044dd1a67d590e6a134aa886a474ba8bb3a5e92356912e01"
},
"nbformat": 3,
"nbformat_minor": 0,
"worksheets": [
{
"cells": [
{
"cell_type": "code",
"collapsed": false,
"input": [
"import numpy as np"
],
"language": "python",
"metadata": {},
"outputs": [],
"prompt_number": 1
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"U = np.array([1,2,3])"
],
"language": "python",
"metadata": {},
"outputs": [],
"prompt_number": 20
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"print U"
],
"language": "python",
"metadata": {},
"outputs": [
{
"output_type": "stream",
"stream": "stdout",
"text": [
"[1 2 3]\n"
]
}
],
"prompt_number": 21
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"s = np.array([4,5,6])"
],
"language": "python",
"metadata": {},
"outputs": [],
"prompt_number": 22
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"Vt = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])"
],
"language": "python",
"metadata": {},
"outputs": [],
"prompt_number": 23
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"S = np.diag(s)"
],
"language": "python",
"metadata": {},
"outputs": [],
"prompt_number": 24
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"Us = np.dot(U, S)"
],
"language": "python",
"metadata": {},
"outputs": [],
"prompt_number": 25
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"print Us"
],
"language": "python",
"metadata": {},
"outputs": [
{
"output_type": "stream",
"stream": "stdout",
"text": [
"[ 4 10 18]\n"
]
}
],
"prompt_number": 26
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"np.dot(Us, Vt)"
],
"language": "python",
"metadata": {},
"outputs": [
{
"metadata": {},
"output_type": "pyout",
"prompt_number": 27,
"text": [
"array([170, 202, 234])"
]
}
],
"prompt_number": 27
},
{
"cell_type": "code",
"collapsed": false,
"input": [],
"language": "python",
"metadata": {},
"outputs": []
}
],
"metadata": {}
}
]
}
\ No newline at end of file
"""
.. module:: active_appearance_model
:platform: Unix, Windows
:synopsis: Contains the aam data format abstraction layer
"""
import logging import logging
import numpy as np import numpy as np
from matplotlib.tri import Triangulation from matplotlib.tri import Triangulation
...@@ -26,8 +33,8 @@ class AAMPoints(): ...@@ -26,8 +33,8 @@ class AAMPoints():
normalized_flattened_points_list(ndarray): flattened list of points. normalized_flattened_points_list(ndarray): flattened list of points.
This means that if the points consist of x,y coordinates, then all this This means that if the points consist of x,y coordinates, then all this
list will be: [x1, y1, x2, y2, ... xi, yi] list will be: [x1, y1, x2, y2, ... xi, yi]
points_list(ndarray): this list is the same points but then not points_list(ndarray): this list is the same points but then not
flattened, [[x1, y1], [x2, y2], ... [xi, yi]]. You either create flattened, [[x1, y1], [x2, y2], ... [xi, yi]]. You either create
this object with this argument or the normalized_flattened_points_list this object with this argument or the normalized_flattened_points_list
actual_shape(tuple): this is important if you want to reconstruct actual_shape(tuple): this is important if you want to reconstruct
the original list, see get_scaled_points() for usage. the original list, see get_scaled_points() for usage.
...@@ -55,11 +62,13 @@ class AAMPoints(): ...@@ -55,11 +62,13 @@ class AAMPoints():
""" """
Scale the normalized flattened points list to a scale given by 'shape'. Scale the normalized flattened points list to a scale given by 'shape'.
The x and y values should be scaled to the width and height of the image. The x and y values should be scaled to the width and height of the image.
Args: Args:
shape(tuple): (height, width) shape(tuple): (height, width)
rescal(boolean): flag if we should rescale or not because if we rescale(boolean): flag if we should rescale or not because if we
already scaled, we are not going to do it again by already scaled, we are not going to do it again by
default. default.
Returns: Returns:
ndarray scaled to 'shape' width and height. ndarray scaled to 'shape' width and height.
""" """
...@@ -106,14 +115,16 @@ class AAMPoints(): ...@@ -106,14 +115,16 @@ class AAMPoints():
#return cv2.boundingRect() #return cv2.boundingRect()
def get_mean(vector): def get_mean(vector):
""" construct a mean from a matrix of x,y values """
Construct a mean from a matrix of x,y values
Args: Args:
points(numpy array) that follows the following structure: points(numpy array) that follows the following structure:
Returns: Returns:
mean_values (numpy array) mean_values (numpy array)
Examples: Example:
Input observations: Input observations:
0. [[x_0_0, y_0_0], ... , [x_0_m, y_0_m]], 0. [[x_0_0, y_0_0], ... , [x_0_m, y_0_m]],
1. [[x_1_0, y_1_0], ... , [x_1_m, y_1_m]], 1. [[x_1_0, y_1_0], ... , [x_1_m, y_1_m]],
...@@ -140,7 +151,13 @@ def get_mean(vector): ...@@ -140,7 +151,13 @@ def get_mean(vector):
def get_triangles(x_vector, y_vector): def get_triangles(x_vector, y_vector):
""" perform triangulation between two 2d vectors""" """
Perform triangulation between two 2d vectors
Args:
x_vector(ndarray): list of x locations
y_vector(ndarray): list of y locations
"""
return Triangulation(x_vector, y_vector).triangles return Triangulation(x_vector, y_vector).triangles
...@@ -151,9 +168,11 @@ def build_shape_feature_vectors(files, get_points, flattened=False): ...@@ -151,9 +168,11 @@ def build_shape_feature_vectors(files, get_points, flattened=False):
Args: Args:
files (list): list files files (list): list files
get_points(function): function that gets the points/landmarks given
a list of files.
return: Returns:
list: list of feature vectors list. List of feature vectors
""" """
points = get_points(files) points = get_points(files)
...@@ -166,10 +185,14 @@ def build_shape_feature_vectors(files, get_points, flattened=False): ...@@ -166,10 +185,14 @@ def build_shape_feature_vectors(files, get_points, flattened=False):
def sample_from_triangles(src, points2d_src, points2d_dst, triangles, dst): def sample_from_triangles(src, points2d_src, points2d_dst, triangles, dst):
""" """
Get pixels from within the triangles [[p1, p2, p3]_0, .. [p1, p2, p3]_n]. Get pixels from within the triangles [[p1, p2, p3]_0, .. [p1, p2, p3]_n].
Args: Args:
src(ndarray, dtype=uint8): input image src(ndarray, dtype=uint8): input image
points2d_src(ndarray, dtype=np.int32): shape array [[x, y], ... [x, y]] points2d_src(ndarray, dtype=np.int32): shape array [[x, y], ... [x, y]]
points2d_dst(ndarray, dtype=np.int32): shape array [[x, y], ... [x, y]] points2d_dst(ndarray, dtype=np.int32): shape array [[x, y], ... [x, y]]
triangles(ndarray, ndim=3, dtype=np.int32): shape array [[p1, p2, p3]_0, .. [p1, p2, p3]_n]. triangles(ndarray, ndim=3, dtype=np.int32): shape array [[p1, p2, p3]_0, .. [p1, p2, p3]_n].
""" """
...@@ -193,7 +216,7 @@ def build_texture_feature_vectors(files, get_image_with_points, mean_points, tri ...@@ -193,7 +216,7 @@ def build_texture_feature_vectors(files, get_image_with_points, mean_points, tri
Args: Args:
files (list): list files files (list): list files
get_image_with_points (function): That can return the image together get_image_with_points (function): That can return the image together
with the location. with the location.
mean_points(AAMPoints): AAMPoints object mean_points(AAMPoints): AAMPoints object
Returns: Returns:
...@@ -233,7 +256,7 @@ def build_texture_feature_vectors(files, get_image_with_points, mean_points, tri ...@@ -233,7 +256,7 @@ def build_texture_feature_vectors(files, get_image_with_points, mean_points, tri
def get_pixel_values(image, points): def get_pixel_values(image, points):
""" docstring """ """ deprecated """
h, w, c = image.shape h, w, c = image.shape
points[:, 0] = points[:, 0] * w points[:, 0] = points[:, 0] * w
......
"""
.. module:: datasets
:platform: Unix, Windows
:synopsis: Contains imm dataset abstraction layer
"""
from matplotlib.tri import Triangulation from matplotlib.tri import Triangulation
import cv2 import cv2
...@@ -30,12 +38,46 @@ class IMMPoints(aam.AAMPoints): ...@@ -30,12 +38,46 @@ class IMMPoints(aam.AAMPoints):
) )
def get_points(self): def get_points(self):
"""
Get the flattened list of points
Returns:
ndarray. flattened array of points, see AAMPoints for more
information.
"""
return self.normalized_flattened_points_list return self.normalized_flattened_points_list
def __get_image(self):
"""
Get the image corresponding to the self.image_file
Returns:
ndarray image
"""
assert hasattr(self, 'image_file'), 'image_file name should be set, \
import file must be invoked first'
self.image = cv2.imread(self.image_file)
def get_image(self): def get_image(self):
return cv2.imread(self.image_file) """
Get the image corresponding to the filename
If filename == image_1.asf, then we read image_1.jpg from disk
and return this to the user.
Returns:
ndarray image
"""
return self.image
def import_file(self, filename): def import_file(self, filename):
"""
Import an .asf filename. Load the points into a list of points and
store the relative path to image file.
Returns:
ndarray(float). Numpy array of landmark locations as stated in the
.asf files.
"""
points_list = [] points_list = []
with open(filename, 'r') as f: with open(filename, 'r') as f:
...@@ -43,6 +85,7 @@ class IMMPoints(aam.AAMPoints): ...@@ -43,6 +85,7 @@ class IMMPoints(aam.AAMPoints):
data = lines[16:74] data = lines[16:74]
dir_name = os.path.dirname(filename) dir_name = os.path.dirname(filename)
self.image_file = "{}/{}".format(dir_name, lines[-1].strip()) self.image_file = "{}/{}".format(dir_name, lines[-1].strip())
self.__get_image()
for d in data: for d in data:
points_list.append(d.split()[2:4]) points_list.append(d.split()[2:4])
...@@ -85,6 +128,16 @@ class IMMPoints(aam.AAMPoints): ...@@ -85,6 +128,16 @@ class IMMPoints(aam.AAMPoints):
def get_imm_points(files): def get_imm_points(files):
"""
This function does something.
Args:
files (array): Array of .asf full or relative path to .asf files.
Returns:
ndarray. Array of landmarks.
"""
points = [] points = []
for f in files: for f in files:
...@@ -95,6 +148,15 @@ def get_imm_points(files): ...@@ -95,6 +148,15 @@ def get_imm_points(files):
def get_imm_image_with_landmarks(filename): def get_imm_image_with_landmarks(filename):
"""
Get Points with image and landmarks/points
Args:
filename(fullpath): .asf file
Returns:
image, points
"""
imm = IMMPoints(filename=filename) imm = IMMPoints(filename=filename)
return imm.get_image(), imm.get_points() return imm.get_image(), imm.get_points()
......
# Makefile for Sphinx documentation
#
# You can set these variables from the command line.
SPHINXOPTS =
SPHINXBUILD = sphinx-build
PAPER =
BUILDDIR = build
# Internal variables.
PAPEROPT_a4 = -D latex_paper_size=a4
PAPEROPT_letter = -D latex_paper_size=letter
ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source
# the i18n builder cannot share the environment and doctrees with the others
I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source
.PHONY: help
help:
@echo "Please use \`make <target>' where <target> is one of"
@echo " html to make standalone HTML files"
@echo " dirhtml to make HTML files named index.html in directories"
@echo " singlehtml to make a single large HTML file"
@echo " pickle to make pickle files"
@echo " json to make JSON files"
@echo " htmlhelp to make HTML files and a HTML help project"
@echo " qthelp to make HTML files and a qthelp project"
@echo " applehelp to make an Apple Help Book"
@echo " devhelp to make HTML files and a Devhelp project"
@echo " epub to make an epub"
@echo " epub3 to make an epub3"
@echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
@echo " latexpdf to make LaTeX files and run them through pdflatex"
@echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx"
@echo " text to make text files"
@echo " man to make manual pages"
@echo " texinfo to make Texinfo files"
@echo " info to make Texinfo files and run them through makeinfo"
@echo " gettext to make PO message catalogs"
@echo " changes to make an overview of all changed/added/deprecated items"
@echo " xml to make Docutils-native XML files"
@echo " pseudoxml to make pseudoxml-XML files for display purposes"
@echo " linkcheck to check all external links for integrity"
@echo " doctest to run all doctests embedded in the documentation (if enabled)"
@echo " coverage to run coverage check of the documentation (if enabled)"
@echo " dummy to check syntax errors of document sources"
.PHONY: clean
clean:
rm -rf $(BUILDDIR)/*
.PHONY: html
html:
$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
@echo
@echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
.PHONY: dirhtml
dirhtml:
$(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
@echo
@echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
.PHONY: singlehtml
singlehtml:
$(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
@echo
@echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
.PHONY: pickle
pickle:
$(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
@echo
@echo "Build finished; now you can process the pickle files."
.PHONY: json
json:
$(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
@echo
@echo "Build finished; now you can process the JSON files."
.PHONY: htmlhelp
htmlhelp:
$(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
@echo
@echo "Build finished; now you can run HTML Help Workshop with the" \
".hhp project file in $(BUILDDIR)/htmlhelp."
.PHONY: qthelp
qthelp:
$(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
@echo
@echo "Build finished; now you can run "qcollectiongenerator" with the" \
".qhcp project file in $(BUILDDIR)/qthelp, like this:"
@echo "# qcollectiongenerator $(BUILDDIR)/qthelp/3DFaceReconstruction.qhcp"
@echo "To view the help file:"
@echo "# assistant -collectionFile $(BUILDDIR)/qthelp/3DFaceReconstruction.qhc"
.PHONY: applehelp
applehelp:
$(SPHINXBUILD) -b applehelp $(ALLSPHINXOPTS) $(BUILDDIR)/applehelp
@echo
@echo "Build finished. The help book is in $(BUILDDIR)/applehelp."
@echo "N.B. You won't be able to view it unless you put it in" \
"~/Library/Documentation/Help or install it in your application" \
"bundle."
.PHONY: devhelp
devhelp:
$(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
@echo
@echo "Build finished."
@echo "To view the help file:"
@echo "# mkdir -p $$HOME/.local/share/devhelp/3DFaceReconstruction"
@echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/3DFaceReconstruction"
@echo "# devhelp"
.PHONY: epub
epub:
$(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
@echo
@echo "Build finished. The epub file is in $(BUILDDIR)/epub."
.PHONY: epub3
epub3:
$(SPHINXBUILD) -b epub3 $(ALLSPHINXOPTS) $(BUILDDIR)/epub3
@echo
@echo "Build finished. The epub3 file is in $(BUILDDIR)/epub3."
.PHONY: latex
latex:
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
@echo
@echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
@echo "Run \`make' in that directory to run these through (pdf)latex" \
"(use \`make latexpdf' here to do that automatically)."
.PHONY: latexpdf
latexpdf:
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
@echo "Running LaTeX files through pdflatex..."
$(MAKE) -C $(BUILDDIR)/latex all-pdf
@echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
.PHONY: latexpdfja
latexpdfja:
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
@echo "Running LaTeX files through platex and dvipdfmx..."
$(MAKE) -C $(BUILDDIR)/latex all-pdf-ja
@echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
.PHONY: text
text:
$(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
@echo
@echo "Build finished. The text files are in $(BUILDDIR)/text."
.PHONY: man
man:
$(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
@echo
@echo "Build finished. The manual pages are in $(BUILDDIR)/man."
.PHONY: texinfo
texinfo:
$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
@echo
@echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo."
@echo "Run \`make' in that directory to run these through makeinfo" \
"(use \`make info' here to do that automatically)."
.PHONY: info
info:
$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
@echo "Running Texinfo files through makeinfo..."
make -C $(BUILDDIR)/texinfo info
@echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo."
.PHONY: gettext
gettext:
$(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale
@echo
@echo "Build finished. The message catalogs are in $(BUILDDIR)/locale."
.PHONY: changes
changes:
$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
@echo
@echo "The overview file is in $(BUILDDIR)/changes."
.PHONY: linkcheck
linkcheck:
$(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
@echo
@echo "Link check complete; look for any errors in the above output " \
"or in $(BUILDDIR)/linkcheck/output.txt."
.PHONY: doctest
doctest:
$(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
@echo "Testing of doctests in the sources finished, look at the " \
"results in $(BUILDDIR)/doctest/output.txt."
.PHONY: coverage
coverage:
$(SPHINXBUILD) -b coverage $(ALLSPHINXOPTS) $(BUILDDIR)/coverage
@echo "Testing of coverage in the sources finished, look at the " \
"results in $(BUILDDIR)/coverage/python.txt."
.PHONY: xml
xml:
$(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml
@echo
@echo "Build finished. The XML files are in $(BUILDDIR)/xml."
.PHONY: pseudoxml
pseudoxml:
$(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml
@echo
@echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml."
.PHONY: dummy
dummy:
$(SPHINXBUILD) -b dummy $(ALLSPHINXOPTS) $(BUILDDIR)/dummy
@echo
@echo "Build finished. Dummy builder generates no files."
# Sphinx build info version 1
# This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done.
config: 5cb3bb1ca45c047aaa96eb2e455b081b
tags: 645f666f9bcd5a90fca523b33c5a78b7
This diff is collapsed.
This diff is collapsed.
<!DOCTYPE html>
<!--[if IE 8]><html class="no-js lt-ie9" lang="en" > <![endif]-->
<!--[if gt IE 8]><!--> <html class="no-js" lang="en" > <!--<![endif]-->
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Overview: module code &mdash; 3D Face Reconstruction 0.1 documentation</title>
<link rel="stylesheet" href="../_static/css/theme.css" type="text/css" />
<link rel="top" title="3D Face Reconstruction 0.1 documentation" href="../index.html"/>
<script src="../_static/js/modernizr.min.js"></script>
</head>
<body class="wy-body-for-nav" role="document">
<div class="wy-grid-for-nav">
<nav data-toggle="wy-nav-shift" class="wy-nav-side">
<div class="wy-side-scroll">
<div class="wy-side-nav-search">
<a href="../index.html" class="icon icon-home"> 3D Face Reconstruction
</a>
<div class="version">
0.1
</div>
<div role="search">
<form id="rtd-search-form" class="wy-form" action="../search.html" method="get">
<input type="text" name="q" placeholder="Search docs" />
<input type="hidden" name="check_keywords" value="yes" />
<input type="hidden" name="area" value="default" />
</form>
</div>
</div>
<div class="wy-menu wy-menu-vertical" data-spy="affix" role="navigation" aria-label="main navigation">
<p class="caption"><span class="caption-text">Table of Contents</span></p>
<ul>
<li class="toctree-l1"><a class="reference internal" href="../datasets.html">Datasets</a></li>
<li class="toctree-l1"><a class="reference internal" href="../aam.html">AAM Module</a></li>
<li class="toctree-l1"><a class="reference internal" href="../pca.html">PCA Module</a></li>
<li class="toctree-l1"><a class="reference internal" href="../reconstruction/reconstruction.html">Reconstruction Module</a></li>
<li class="toctree-l1"><a class="reference internal" href="../reconstruction/texture.html">Texture Module</a></li>
</ul>
</div>
</div>
</nav>
<section data-toggle="wy-nav-shift" class="wy-nav-content-wrap">
<nav class="wy-nav-top" role="navigation" aria-label="top navigation">
<i data-toggle="wy-nav-top" class="fa fa-bars"></i>
<a href="../index.html">3D Face Reconstruction</a>
</nav>
<div class="wy-nav-content">
<div class="rst-content">
<div role="navigation" aria-label="breadcrumbs navigation">
<ul class="wy-breadcrumbs">
<li><a href="../index.html">Docs</a> &raquo;</li>
<li>Overview: module code</li>
<li class="wy-breadcrumbs-aside">
</li>
</ul>
<hr/>
</div>
<div role="main" class="document" itemscope="itemscope" itemtype="http://schema.org/Article">
<div itemprop="articleBody">
<h1>All modules for which code is available</h1>
<ul><li><a href="aam.html">aam</a></li>
<li><a href="datasets/imm.html">datasets.imm</a></li>
<li><a href="main.html">main</a></li>
<li><a href="pca.html">pca</a></li>
<li><a href="reconstruction/reconstruction.html">reconstruction.reconstruction</a></li>
<li><a href="reconstruction/texture.html">reconstruction.texture</a></li>
</ul>
</div>
</div>
<footer>
<hr/>
<div role="contentinfo">
<p>
&copy; Copyright 2016, Richard Torenvliet.
</p>
</div>
Built with <a href="http://sphinx-doc.org/">Sphinx</a> using a <a href="https://github.com/snide/sphinx_rtd_theme">theme</a> provided by <a href="https://readthedocs.org">Read the Docs</a>.
</footer>
</div>
</div>
</section>
</div>
<script type="text/javascript">
var DOCUMENTATION_OPTIONS = {
URL_ROOT:'../',
VERSION:'0.1',
COLLAPSE_INDEX:false,
FILE_SUFFIX:'.html',
HAS_SOURCE: true
};
</script>
<script type="text/javascript" src="../_static/jquery.js"></script>
<script type="text/javascript" src="../_static/underscore.js"></script>
<script type="text/javascript" src="../_static/doctools.js"></script>
<script type="text/javascript" src="https://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML"></script>
<script type="text/javascript" src="../_static/js/theme.js"></script>
<script type="text/javascript">
jQuery(function () {
SphinxRtdTheme.StickyNav.enable();
});
</script>
</body>
</html>
\ No newline at end of file
This diff is collapsed.
This diff is collapsed.
AAM Module
==========
.. automodule:: aam
:members:
Datasets
========
.. automodule:: datasets.imm
:members:
.. 3D Face Reconstruction documentation master file, created by
sphinx-quickstart on Mon Aug 1 16:41:23 2016.
You can adapt this file completely to your liking, but it should at least
contain the root `toctree` directive.
Welcome to 3D Face Reconstruction's documentation!
==================================================
.. toctree::
:maxdepth: 2
:caption: Table of Contents
:name: mastertoc
datasets
aam
pca
reconstruction/reconstruction
reconstruction/texture
!!!Work in progress!!!
======================
PCA reconstruction
==================
Principle Component Analysis is one of the most used methods in the field of statistics, it is used for dimension reduction of data and is capable of removing outliers which ultimately improves learning algorithms. In this case we use PCA for both shape and texture reconstruction. Given an image of person's face we would be able to reconstruct it using a PCA Model. The motivation for using PCA is that we can fill in missing data and remove outliers given one image of person. If for some reason the image is very cluttered, we would still be able to 'predict' how this person would look like, given all the faces we have used to train the PCA Model.
For the PCA reconstruction method has a couple of prerequisites are required. First off, the PCA Model itself. For those who are familiar with PCA know that we need to have a flattened feature vector. Both the dimensions and the content of this feature vector may be arbitrary, but have to be exactly the same from subject to subject, (i.e., there can be no difference in the number of annotated landmarks or order, landmark 1 in subject A, is landmark 1 in subject B). In this case we use it for the shape and texture. The shape feature vector contains the following data:
```
[[x_1, y_1], [x_2, y_2], ..., [x_n, y_n]] -> (flattened) [x_1, y_1, x_2, y_2, x_n, y_n]
```
The x,y values are the location of landmarks in an image. Such a cluster of annotated locations in an image construct a shape we call Active Appearance Model(AAM)[1]. For a serie of annotated pictures with landmark location we can build mean AAM. For this particular implementation we started with supporting the Imm Dataset[^imm_dataset], for the simple reason that it is open for usage without any license agreement before hand (make sure we are correct about this). This is what we call the mean face, which is very important for the construction of the PCA Model, any PCA Model for that matter.
The texture PCA data is somewhat more difficult and depends on a given shape. In our case this given shape is the mean AAM that we have built previously. We need to add extra information to this AAM mean shape, namely a unique set of triangles that can be constructed from the set of landmarks. For this we use the Delaunay algorithm which does exactly this. The triangles help us find corresponding pixels in shape A and B. This solves the problem of pixel correspondences and is important for constructing a mean texture for the reasons explained previously about how a feature vector should look like. Pixel 1 in triangle 1 in subject A needs to correspond to exactly the same pixel (relatively) to pixel 1 in triangle 1 in subject B. This of course is sensitive to noise, but the pixels in the nose region must correspond from subject to subject, this prevents that we reconstruct an eye with a nose for instance (Note: remove this last sentence in a serious text).
References
==========
[1]: Cootes, T. F., Edwards, G. J., & Taylor, C. J. (1998, June). Active appearance models. In European conference on computer vision (pp. 484-498). Springer Berlin Heidelberg.
Links
=====
[^imm_dataset]: http://www.imm.dtu.dk/~aam/datasets/datasets.html "Imm dataset"
Indices and tables
==================
* :ref:`genindex`
* :ref:`modindex`
* :ref:`search`
PCA Module
==========
.. automodule:: pca
:members:
Reconstruction Module
=====================
As explained in [PCA Reconstruction](home) we need a flattened feature vector to able to build a PCA Model. This holds for both shape and texture model. Currently we implement the independent AAM model where we keep the feature vector separate. Note that we could also choose to combine the shape and appearance in a single flattened feature vector (TODO: elaborate our choice more about this, if possible).
We use the imm dataset[^imm_dataset] for this. We first need to build the mean shape of the all the images. The dataset has a .asf file and an equally named .jpg file. The .asf file contains the locations of the landmars (normalized by the width and height of the image). In `src/imm_points.py` we find the ImmPoints class that implements all functions needed to read this file.
[^imm_dataset]: http://www.imm.dtu.dk/~aam/datasets/datasets.html "Imm dataset"
.. automodule:: reconstruction.reconstruction
:members:
Texture Module
==============
.. automodule:: reconstruction.texture
:members:
This diff is collapsed.
This diff is collapsed.
.fa:before{-webkit-font-smoothing:antialiased}.clearfix{*zoom:1}.clearfix:before,.clearfix:after{display:table;content:""}.clearfix:after{clear:both}@font-face{font-family:FontAwesome;font-weight:normal;font-style:normal;src:url("../font/fontawesome_webfont.eot");src:url("../font/fontawesome_webfont.eot?#iefix") format("embedded-opentype"),url("../font/fontawesome_webfont.woff") format("woff"),url("../font/fontawesome_webfont.ttf") format("truetype"),url("../font/fontawesome_webfont.svg#FontAwesome") format("svg")}.fa:before{display:inline-block;font-family:FontAwesome;font-style:normal;font-weight:normal;line-height:1;text-decoration:inherit}a .fa{display:inline-block;text-decoration:inherit}li .fa{display:inline-block}li .fa-large:before,li .fa-large:before{width:1.875em}ul.fas{list-style-type:none;margin-left:2em;text-indent:-0.8em}ul.fas li .fa{width:0.8em}ul.fas li .fa-large:before,ul.fas li .fa-large:before{vertical-align:baseline}.fa-book:before{content:""}.icon-book:before{content:""}.fa-caret-down:before{content:""}.icon-caret-down:before{content:""}.fa-caret-up:before{content:""}.icon-caret-up:before{content:""}.fa-caret-left:before{content:""}.icon-caret-left:before{content:""}.fa-caret-right:before{content:""}.icon-caret-right:before{content:""}.rst-versions{position:fixed;bottom:0;left:0;width:300px;color:#fcfcfc;background:#1f1d1d;border-top:solid 10px #343131;font-family:"Lato","proxima-nova","Helvetica Neue",Arial,sans-serif;z-index:400}.rst-versions a{color:#2980B9;text-decoration:none}.rst-versions .rst-badge-small{display:none}.rst-versions .rst-current-version{padding:12px;background-color:#272525;display:block;text-align:right;font-size:90%;cursor:pointer;color:#27AE60;*zoom:1}.rst-versions .rst-current-version:before,.rst-versions .rst-current-version:after{display:table;content:""}.rst-versions .rst-current-version:after{clear:both}.rst-versions .rst-current-version .fa{color:#fcfcfc}.rst-versions .rst-current-version .fa-book{float:left}.rst-versions .rst-current-version .icon-book{float:left}.rst-versions .rst-current-version.rst-out-of-date{background-color:#E74C3C;color:#fff}.rst-versions .rst-current-version.rst-active-old-version{background-color:#F1C40F;color:#000}.rst-versions.shift-up .rst-other-versions{display:block}.rst-versions .rst-other-versions{font-size:90%;padding:12px;color:gray;display:none}.rst-versions .rst-other-versions hr{display:block;height:1px;border:0;margin:20px 0;padding:0;border-top:solid 1px #413d3d}.rst-versions .rst-other-versions dd{display:inline-block;margin:0}.rst-versions .rst-other-versions dd a{display:inline-block;padding:6px;color:#fcfcfc}.rst-versions.rst-badge{width:auto;bottom:20px;right:20px;left:auto;border:none;max-width:300px}.rst-versions.rst-badge .icon-book{float:none}.rst-versions.rst-badge .fa-book{float:none}.rst-versions.rst-badge.shift-up .rst-current-version{text-align:right}.rst-versions.rst-badge.shift-up .rst-current-version .fa-book{float:left}.rst-versions.rst-badge.shift-up .rst-current-version .icon-book{float:left}.rst-versions.rst-badge .rst-current-version{width:auto;height:30px;line-height:30px;padding:0 6px;display:block;text-align:center}@media screen and (max-width: 768px){.rst-versions{width:85%;display:none}.rst-versions.shift{display:block}img{width:100%;height:auto}}
/*# sourceMappingURL=badge_only.css.map */
This diff is collapsed.
/* This file intentionally left blank. */
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
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