Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] Python3 port #8

Merged
merged 2 commits into from
Oct 16, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -52,14 +52,15 @@ $(tmpdirbuild)/package_creation: $(tmpdirbuild)
@echo "********"
@echo "********"
@echo "\033[0m"
@virtualenv --system-site-packages $(venv_dir)
@virtualenv --system-site-packages $(venv_dir) -p python3
@ . $(activate) && pip install --upgrade pip
@ . $(activate) && pip install --upgrade virtualenv
@ . $(activate) && pip install nose2
@ . $(activate) && echo `which pip` && pip -V
@ . $(activate) && pip install --upgrade setuptools && echo `which pip` && pip -V
@ . $(activate) && pip install --upgrade wheel && echo `which pip` && pip -V
@ . $(activate) && pip install numpy scipy pyopengl pillow pyzmq pyyaml && echo `which pip` && pip -V
@ . $(activate) && pip install numpy
@ . $(activate) && pip install scipy pyopengl pillow pyzmq pyyaml && echo `which pip` && pip -V
####### PACKAGE: creating SDIST target
@echo "\033[0;33m----- [" ${package_name} "] Creating the source distribution\033[0m"
@ . $(activate) && python setup.py sdist
Expand Down
2 changes: 1 addition & 1 deletion mesh/colors.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ def main():
g = int(reg.group(2)) / 255.
b = int(reg.group(3)) / 255.
d = reg.group(4)
print "'%s': np.array([%.2f, %.2f, %.2f])," % (d, r, g, b)
print("'%s': np.array([%.2f, %.2f, %.2f])," % (d, r, g, b))
line = fp.readline()


Expand Down
4 changes: 2 additions & 2 deletions mesh/landmarks.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ def landm_xyz(self, ordering=None):
def recompute_landmark_indices(self, landmark_fname=None, safe_mode=True):
filtered_landmarks = dict(filter(lambda e, : e[1] != [0.0, 0.0, 0.0], self.landm_raw_xyz.items()) if (landmark_fname and safe_mode) else self.landm_raw_xyz.items())
if len(filtered_landmarks) != len(self.landm_raw_xyz):
print "WARNING: %d landmarks in file %s are positioned at (0.0, 0.0, 0.0) and were ignored" % (len(self.landm_raw_xyz) - len(filtered_landmarks), landmark_fname)
print("WARNING: %d landmarks in file %s are positioned at (0.0, 0.0, 0.0) and were ignored" % (len(self.landm_raw_xyz) - len(filtered_landmarks), landmark_fname))

self.landm = {}
self.landm_regressors = {}
Expand Down Expand Up @@ -85,7 +85,7 @@ def set_landmarks_from_raw(self, landmarks):
if np.all(map(lambda x: hasattr(x, "__iter__") and len(x) == 3, landmarks.values())):
landmarks = dict((i, np.array(l)) for i, l in landmarks.items())
self.set_landmarks_from_xyz(landmarks)
elif np.all(map(lambda x: isinstance(x, (int, long)), landmarks.values())):
elif np.all(map(lambda x: isinstance(x, int), landmarks.values())):
self.landm = landmarks
self.recompute_landmark_xyz()
else:
Expand Down
2 changes: 1 addition & 1 deletion mesh/lines.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
# See file LICENSE.txt for full license details.

import numpy as np
import colors
from . import colors


class Lines(object):
Expand Down
8 changes: 5 additions & 3 deletions mesh/mesh.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,12 @@


import os
from functools import reduce

import numpy as np

import colors
import search
from . import colors
from . import search

try:
from .serialization import serialization
Expand Down Expand Up @@ -153,7 +155,7 @@ def jet(v):
result[result < 0.0] = 0.0
return row(result)
color = col(color)
color = np.concatenate([jet(color[i]) for i in xrange(color.size)], axis=0)
color = np.concatenate([jet(color[i]) for i in range(color.size)], axis=0)

return np.ones_like(arr) * color

Expand Down
34 changes: 17 additions & 17 deletions mesh/meshviewer.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,8 +90,8 @@

# this block is below the previous one to make my linter happy
if __package__ is None:
print "this file cannot be executed as a standalone python module"
print "python -m psbody.mesh.%s arguments" % (os.path.splitext(os.path.basename(__file__))[0])
print("this file cannot be executed as a standalone python module")
print("python -m psbody.mesh.%s arguments" % (os.path.splitext(os.path.basename(__file__))[0]))
sys.exit(-1)


Expand All @@ -110,10 +110,10 @@ def _test_for_opengl():
# from OpenGL.GLUT import glutInit
glutInit()
except Exception as e:
print >> sys.stderr, e
print 'failure'
print(e, file=sys.stderr)
print('failure')
else:
print 'success'
print('success')


test_for_opengl_cached = None
Expand All @@ -129,12 +129,12 @@ def test_for_opengl():
if test_for_opengl_cached is None:
p = _run_self(["TEST_FOR_OPENGL"], stderr=subprocess.PIPE)
p.wait()
line = '\n'.join(p.stdout.readlines())
line = ''.join(str(p.stdout.readlines()))
test_for_opengl_cached = 'success' in line
if not test_for_opengl_cached:
print 'OpenGL test failed: '
print '\tstdout:', line
print '\tstderr:', '\n'.join(p.stderr.readlines())
print('OpenGL test failed: ')
print('\tstdout:', line)
print('\tstderr:', '\n'.join(p.stderr.readlines()))
return test_for_opengl_cached


Expand Down Expand Up @@ -311,7 +311,7 @@ class MeshViewerLocal(object):
managed = {}

def __new__(cls, titlebar, uid, shape, keepalive, window_width, window_height):
assert(uid is None or isinstance(uid, str) or isinstance(uid, unicode))
assert(uid is None or isinstance(uid, str))

if uid == 'stack':
uid = ''.join(traceback.format_list(traceback.extract_stack()))
Expand All @@ -325,7 +325,7 @@ def __new__(cls, titlebar, uid, shape, keepalive, window_width, window_height):
result.p = _run_self([titlebar, str(shape[0]), str(shape[1]), str(window_width), str(window_height)])

line = result.p.stdout.readline()
current_port = re.match('<PORT>(.*?)</PORT>', line)
current_port = re.match('<PORT>(.*?)</PORT>', line.decode(sys.stdout.encoding))
if not current_port:
raise Exception("MeshViewer remote appears to have failed to launch")
current_port = int(current_port.group(1))
Expand Down Expand Up @@ -870,7 +870,7 @@ def __init__(self,

# Print out our port so that our client can connect to us with it. Flush stdout immediately; otherwise
# our client could wait forever.
print '<PORT>%d</PORT>\n' % (port,)
print('<PORT>%d</PORT>\n' % (port,))
sys.stdout.flush()

self.arcball = ArcBallT(width, height)
Expand Down Expand Up @@ -1109,7 +1109,7 @@ def handle_request(self, request):
mv.autorecenter = obj
self.need_redraw = True
elif label == 'titlebar':
assert(isinstance(obj, str) or isinstance(obj, unicode))
assert(isinstance(obj, str))
self.titlebar = obj
glutSetWindowTitle(obj)
elif label == 'lighting_on':
Expand All @@ -1119,7 +1119,7 @@ def handle_request(self, request):
glClearColor(obj[0], obj[1], obj[2], 1.0)
self.need_redraw = True
elif label == 'save_snapshot': # redraws for itself
assert(isinstance(obj, str) or isinstance(obj, unicode))
assert(isinstance(obj, str))
self.snapshot(obj)
elif label == 'get_keypress':
self.keypress_port = obj
Expand Down Expand Up @@ -1213,7 +1213,7 @@ def init_opengl(self):
height=int(sys.argv[5]))

else:
print "#" * 10
print 'Usage:'
print "python -m %s.%s arguments" % (__package__, os.path.splitext(os.path.basename(__file__))[0])
print("#" * 10)
print('Usage:')
print("python -m %s.%s arguments" % (__package__, os.path.splitext(os.path.basename(__file__))[0]))
sys.exit(-1)
17 changes: 9 additions & 8 deletions mesh/search.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,17 +21,18 @@
class AabbTree(object):
"""Encapsulates an AABB (Axis Aligned Bounding Box) Tree"""
def __init__(self, m):
import spatialsearch
from . import spatialsearch
# this shit return NULL
self.cpp_handle = spatialsearch.aabbtree_compute(m.v.astype(np.float64).copy(order='C'), m.f.astype(np.uint32).copy(order='C'))

def nearest(self, v_samples, nearest_part=False):
"nearest_part tells you whether the closest point in triangle abc is in the interior (0), on an edge (ab:1,bc:2,ca:3), or a vertex (a:4,b:5,c:6)"
import spatialsearch
from . import spatialsearch
f_idxs, f_part, v = spatialsearch.aabbtree_nearest(self.cpp_handle, np.array(v_samples, dtype=np.float64, order='C'))
return (f_idxs, f_part, v) if nearest_part else (f_idxs, v)

def nearest_alongnormal(self, points, normals):
import spatialsearch
from . import spatialsearch
distances, f_idxs, v = spatialsearch.aabbtree_nearest_alongnormal(self.cpp_handle,
points.astype(np.float64),
normals.astype(np.float64))
Expand All @@ -57,20 +58,20 @@ def nearest_vertices(self, v_samples):
class CGALClosestPointTree(object):
"""Encapsulates an AABB (Axis Aligned Bounding Box) Tree """
def __init__(self, m):
import spatialsearch
from . import spatialsearch
self.v = m.v
n = m.v.shape[0]
faces = np.vstack([np.array(range(n)), np.array(range(n)) + n, np.array(range(n)) + 2 * n]).T
eps = 0.000000000001
self.cpp_handle = spatialsearch.aabbtree_compute(np.vstack([m.v + eps * np.array([1.0, 0.0, 0.0]), m.v + eps * np.array([0.0, 1.0, 0.0]), m.v - eps * np.array([1.0, 1.0, 0.0])]).astype(np.float64).copy(order='C'), faces.astype(np.uint32).copy(order='C'))

def nearest(self, v_samples):
import spatialsearch
from . import spatialsearch
f_idxs, f_part, v = spatialsearch.aabbtree_nearest(self.cpp_handle, np.array(v_samples, dtype=np.float64, order='C'))
return (f_idxs.flatten(), (np.sum(((self.v[f_idxs.flatten()] - v_samples) ** 2.0), axis=1) ** 0.5).flatten())

def nearest_vertices(self, v_samples):
import spatialsearch
from . import spatialsearch
f_idxs, f_part, v = spatialsearch.aabbtree_nearest(self.cpp_handle, np.array(v_samples, dtype=np.float64, order='C'))
return self.v[f_idxs.flatten()]

Expand All @@ -79,11 +80,11 @@ class AabbNormalsTree(object):
def __init__(self, m):
# the weight of the normals cosine is proportional to the std of the vertices
# the best point can be translated up to 2*eps because of the normals
import aabb_normals
from . import aabb_normals
eps = 0.1 # np.std(m.v)#0
self.tree_handle = aabb_normals.aabbtree_n_compute(m.v, m.f.astype(np.uint32).copy(), eps)

def nearest(self, v_samples, n_samples):
import aabb_normals
from . import aabb_normals
closest_tri, closest_p = aabb_normals.aabbtree_n_nearest(self.tree_handle, v_samples, n_samples)
return (closest_tri, closest_p)
23 changes: 16 additions & 7 deletions mesh/serialization/serialization.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@

from ..errors import SerializationError


"""
serialization.py

Expand All @@ -26,6 +25,7 @@
'set_landmark_indices_from_ppfile', 'set_landmark_indices_from_lmrkfile',
'load_from_ply', 'load_from_file']


# import os.path


Expand Down Expand Up @@ -74,7 +74,7 @@ def load_from_obj(self, filename):
currLandm = line[1]
elif line[0] == 'mtllib':
self.materials_filepath = os.path.join(os.path.dirname(filename), line[1])
self.materials_file = file(self.materials_filepath, 'r').readlines()
self.materials_file = open(self.materials_filepath, 'r').readlines()

self.v = np.array(v)
self.f = np.array(f) - 1
Expand Down Expand Up @@ -149,7 +149,8 @@ def write_face_to_obj_file(face_index, obj_file):
if not hasattr(self, 'fn'):
self.reset_face_normals()
normal_indices = self.fn[face_index][::ff] + 1
obj_file.write('f %d/%d/%d %d/%d/%d %d/%d/%d\n' % tuple(np.array([vertex_indices, texture_indices, normal_indices]).T.flatten()))
obj_file.write('f %d/%d/%d %d/%d/%d %d/%d/%d\n' % tuple(
np.array([vertex_indices, texture_indices, normal_indices]).T.flatten()))
elif hasattr(self, 'fn'):
normal_indices = self.fn[face_index][::ff] + 1
obj_file.write('f %d//%d %d//%d %d//%d\n' % tuple(np.array([vertex_indices, normal_indices]).T.flatten()))
Expand Down Expand Up @@ -272,7 +273,9 @@ def write_three_json(self, filename, name=""):
mesh_data["vertices"] = self.v.flatten().tolist()
mesh_data["normals"] = self.vn.flatten().tolist()
mesh_data["uvs"] = [np.array([[vt[0], vt[1]] for vt in self.vt]).flatten().tolist()]
mesh_data["faces"] = np.array([[42, self.f[i][0], self.f[i][1], self.f[i][2], 0, self.ft[i][0], self.ft[i][1], self.ft[i][2], self.fn[i][0], self.fn[i][1], self.fn[i][2]] for i in range(len(self.f))]).flatten().tolist()
mesh_data["faces"] = np.array([[42, self.f[i][0], self.f[i][1], self.f[i][2], 0, self.ft[i][0], self.ft[i][1],
self.ft[i][2], self.fn[i][0], self.fn[i][1], self.fn[i][2]] for i in
range(len(self.f))]).flatten().tolist()

json_or_js_file = open(filename, 'w')
json_or_js_file.write(json.dumps(mesh_data, indent=4))
Expand Down Expand Up @@ -422,13 +425,19 @@ def load_from_file(self, filename, use_cpp=True):


def load_from_ply(self, filename):
import plyutils
from os.path import abspath, dirname, join

test_data_folder = abspath(join(dirname(__file__), '..', 'data', 'unittest'))

from psbody.mesh.serialization import plyutils
try:
res = plyutils.read(filename)
except plyutils.error, e:
raise SerializationError(e.message)
except plyutils.error as e:
raise SerializationError(e)

self.v = np.array(res['pts']).T.copy()
self.f = np.array(res['tri']).T.copy()

if 'color' in res:
self.set_vertex_colors(np.array(res['color']).T.copy() / 255)
if 'normals' in res:
Expand Down
29 changes: 19 additions & 10 deletions mesh/src/aabb_normals.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,18 +35,27 @@ static PyMethodDef SpatialsearchMethods[] = {
{NULL, NULL, 0, NULL} /* Sentinel */
};

static struct PyModuleDef moduleDef =
{
PyModuleDef_HEAD_INIT,
"aabb_normals", /* name of module */
"", /* module documentation, may be NULL */
-1, /* size of per-interpreter state of the module, or -1 if the module keeps state in global variables. */
SpatialsearchMethods
};


PyMODINIT_FUNC
initaabb_normals(void)
PyMODINIT_FUNC PyInit_aabb_normals(void)
{
(void) Py_InitModule("aabb_normals", SpatialsearchMethods);
PyObject *module = PyModule_Create(&moduleDef);

import_array();

return module;
}

void aabb_tree_destructor(void *ptr)
void aabb_tree_destructor(PyObject *ptr)
{
TreeAndTri* search = (TreeAndTri*)ptr;
TreeAndTri* search = (TreeAndTri*) PyCapsule_GetPointer(ptr, NULL);
delete search;
}

Expand Down Expand Up @@ -90,8 +99,8 @@ aabbtree_normals_compute(PyObject *self, PyObject *args)
{
TreeAndTri* search = new TreeAndTri(m_mesh_tri,m_mesh_points,eps,T,P);

PyObject* result = PyCObject_FromVoidPtr((void*)search, aabb_tree_destructor);
return Py_BuildValue("N", result);
PyObject* result = PyCapsule_New((void*)search, NULL, aabb_tree_destructor);
return result;
}
catch (mesh_aabb_tree_error&)
{
Expand All @@ -107,7 +116,7 @@ aabbtree_normals_nearest(PyObject *self, PyObject *args)
if (!PyArg_ParseTuple(args, "OOO", &py_tree, &py_v, &py_n))
return NULL;

TreeAndTri *search = (TreeAndTri *)PyCObject_AsVoidPtr(py_tree);
TreeAndTri *search = (TreeAndTri *) PyCapsule_GetPointer(py_tree, NULL);

npy_intp* v_dims = PyArray_DIMS(py_v);
npy_intp* n_dims = PyArray_DIMS(py_n);
Expand Down Expand Up @@ -174,7 +183,7 @@ aabbtree_normals_selfintersects(PyObject *self, PyObject *args)
if (!PyArg_ParseTuple(args, "O", &py_tree))
return NULL;

TreeAndTri *search = (TreeAndTri *)PyCObject_AsVoidPtr(py_tree);
TreeAndTri *search = (TreeAndTri *) PyCapsule_GetPointer(py_tree, NULL);

for(Iterator it=search->triangles.begin();it!=search->triangles.end();++it)
if(search->tree.do_intersect(*it))
Expand Down
Loading