Skip to content

Commit

Permalink
check in pyrender code
Browse files Browse the repository at this point in the history
  • Loading branch information
Gernot Riegler authored and Gernot Riegler committed Oct 11, 2017
0 parents commit 8e5cbcd
Show file tree
Hide file tree
Showing 6 changed files with 353 additions and 0 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
build/
pyrender.cpp
24 changes: 24 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# PyRender

This project contains a Cython version of [librender by Andreas Geiger and Chaohui Wang 2014](http://www.cvlibs.net/software/librender/).
The code enables the efficient rendering of depth maps from 3D triangle meshes.

To use the code, first compile the Cython code via

```bash
python setup.py build-ext --inplace
```

You can then use the rendering function

```python
import pyrender
...
# vertices: a 3xN double numpy array
# faces: a 3xN double array (indices of vertices array)
# cam_intr: (fx, fy, px, py) double vector
# img_size: (width, height) int vector
depth, mask, img = pyrender.render(vertices, faces, cam_intr, img_size)
```

Make sure `pyrender` is in your `$PYTHONPATH`.
192 changes: 192 additions & 0 deletions offscreen.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
#include "offscreen.h"

int OffscreenGL::glutWin = -1;
bool OffscreenGL::glutInitialized = false;

OffscreenGL::OffscreenGL(int maxHeight, int maxWidth) {

if (!glutInitialized) {
int argc = 1;
char *argv = "test";
glutInit(&argc, &argv);
glutInitialized = true;
}

glutInitDisplayMode(GLUT_DEPTH | GLUT_SINGLE | GLUT_RGBA);
glutInitWindowPosition(100, 100);
glutInitWindowSize(maxWidth, maxHeight);

// create or set window & off-screen framebuffer
if (glutWin < 0) {

glutWin = glutCreateWindow("OpenGL");
glutHideWindow();
glewInit();
glGenFramebuffersEXT(1, &fb);

glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fb);
glGenTextures(1, &renderTex);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_RECTANGLE_ARB, renderTex);
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, GL_RGB, maxWidth, maxHeight,
0, GL_RGBA, GL_UNSIGNED_BYTE, 0);

glGenTextures(1, &depthTex);
glBindTexture(GL_TEXTURE_RECTANGLE_ARB, depthTex);
glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_DEPTH_TEXTURE_MODE, GL_INTENSITY);
glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_R_TO_TEXTURE);
glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL);
glTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, GL_DEPTH24_STENCIL8, maxWidth, maxHeight, 0, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, NULL);

glGenFramebuffersEXT(1, &fb);
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fb);
glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_RECTANGLE_ARB, renderTex, 0);
glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_RECTANGLE_ARB, depthTex, 0);
glDrawBuffer(GL_COLOR_ATTACHMENT0_EXT|GL_DEPTH_ATTACHMENT_EXT);
} else {
glutSetWindow(glutWin);
}
}

OffscreenGL::~OffscreenGL() {
}


GLuint createDisplayList(double *fM, int fNum, double *vM, int vNum, double *cM, unsigned int colorModFactor, double linewidth, bool coloring, bool one_offset) {

GLuint theShape;
int i;
unsigned int channelCapacity, channelCapacity2;
double *fp;
int vIndex, fNum2;
fNum2 = fNum*2;

channelCapacity = 256 / colorModFactor;
channelCapacity2 = channelCapacity * channelCapacity;

theShape = glGenLists(1);

glNewList(theShape, GL_COMPILE);

if (linewidth>0.1) {
glPolygonMode(GL_FRONT_AND_BACK,GL_LINE);
glLineWidth(linewidth);
}

glBegin(GL_TRIANGLES);
for (i = 1; i <= fNum; i++) {
fp = fM + i-1;

vIndex = (int)fp[0] - one_offset;
if (coloring) glColor3ub(cM[vIndex], cM[vIndex + vNum], cM[vIndex + 2*vNum]);
glVertex3d(vM[vIndex], vM[vIndex + vNum], vM[vIndex + 2*vNum]);

vIndex = (int)fp[fNum] - one_offset;
if (coloring) glColor3ub(cM[vIndex], cM[vIndex + vNum], cM[vIndex + 2*vNum]);
glVertex3d(vM[vIndex], vM[vIndex + vNum], vM[vIndex + 2*vNum]);

vIndex = (int)fp[fNum2] - one_offset;
if (coloring) glColor3ub(cM[vIndex], cM[vIndex + vNum], cM[vIndex + 2*vNum]);
glVertex3d(vM[vIndex], vM[vIndex + vNum], vM[vIndex + 2*vNum]);
}
glEnd();
if (linewidth>0.1)
glPolygonMode(GL_FRONT_AND_BACK,GL_FILL);
glEndList();

return theShape;
}

void cameraSetup(double zNear, double zFar, double *intrinsics, unsigned int imgHeight, unsigned int imgWidth) {

double viewMat[] = {1, 0, 0, 0, 0, -1, 0, 0, 0, 0, -1, 0, 0, 0, 0, 1};
double fcv[] = {intrinsics[0], intrinsics[1]};
double ccv[] = {intrinsics[2], intrinsics[3]};

glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
glEnable(GL_DEPTH_TEST);
glDisable(GL_TEXTURE_2D);

glMatrixMode(GL_MODELVIEW);
glLoadMatrixd(viewMat);

double left = - ccv[0] / fcv[0] * zNear;
double bottom = (ccv[1] - (double)(imgHeight-1)) / fcv[1] * zNear;
double right = ((double)imgWidth - 1.0 - ccv[0]) / fcv[0] * zNear;
double top = ccv[1] / fcv[1] * zNear;

glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glFrustum(left, right, bottom, top, zNear, zFar);
glViewport(0, 0, imgWidth, imgHeight);
}

void drawPatchToDepthBuffer(GLuint listName, unsigned char *imageBuffer, float *depthBuffer, bool *maskBuffer,
unsigned int imgHeight, unsigned int imgWidth, double *zNearFarV, bool coloring = true) {

glCallList(listName);
glFlush();

// bug fix for Nvidia
unsigned int paddedWidth = imgWidth % 4;
if (paddedWidth != 0) paddedWidth = 4 - paddedWidth + imgWidth;
else paddedWidth = imgWidth;

// Read off of the depth buffer
float *dataBuffer_depth = (float *)malloc(paddedWidth * imgHeight * sizeof(GL_FLOAT));
glReadPixels(0, 0, paddedWidth, imgHeight, GL_DEPTH_COMPONENT, GL_FLOAT, dataBuffer_depth);

// Read off of the color buffer
GLubyte *dataBuffer_rgb = (GLubyte *)malloc(3* paddedWidth * imgHeight * sizeof(GLubyte));
if (coloring)
glReadPixels(0, 0, paddedWidth, imgHeight, GL_RGB, GL_UNSIGNED_BYTE, dataBuffer_rgb);

// reorder the pixel data for the opengl to matlab conversion
unsigned int matlabImgIndex = 0;
unsigned int oglImageIndex = 0;

float n = zNearFarV[0];
float f = zNearFarV[1];
for (int j = 0; j < imgWidth; j++) {
for (int i = 0; i < imgHeight; i++, matlabImgIndex++) {
oglImageIndex = (j + (imgHeight-1-i) * paddedWidth);
float depth = dataBuffer_depth[oglImageIndex];

// render mask: indicating points inside the clipped plane
maskBuffer[matlabImgIndex] = depth<1;

// render depth
depthBuffer[matlabImgIndex] = -f*n/(depth*(f-n)-f);

// render color
if (coloring) {
imageBuffer[matlabImgIndex] = (unsigned char) dataBuffer_rgb[oglImageIndex*3];
imageBuffer[matlabImgIndex+imgWidth*imgHeight] = (unsigned char) dataBuffer_rgb[oglImageIndex*3+1];
imageBuffer[matlabImgIndex+imgWidth*imgHeight*2] = (unsigned char) dataBuffer_rgb[oglImageIndex*3+2];
}
}
}

free(dataBuffer_depth);
free(dataBuffer_rgb);
}

void renderDepthMesh(double *FM, int fNum, double *VM, int vNum, double *CM, double *intrinsics, int *imgSizeV, double *zNearFarV, unsigned char * imgBuffer, float *depthBuffer, bool *maskBuffer, double linewidth, bool coloring, bool one_offset) {
OffscreenGL offscreenGL(imgSizeV[0], imgSizeV[1]);
cameraSetup(zNearFarV[0], zNearFarV[1], intrinsics, imgSizeV[0], imgSizeV[1]);
GLuint list = createDisplayList(FM, fNum, VM, vNum, CM, 1, linewidth, coloring, one_offset);
drawPatchToDepthBuffer(list, imgBuffer, depthBuffer, maskBuffer, imgSizeV[0], imgSizeV[1], zNearFarV, coloring);
if (list) {
glDeleteLists(list, 1);
list = 0;
}
}
31 changes: 31 additions & 0 deletions offscreen.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
#ifndef LIBRENDER_OFFSCREEN_H
#define LIBRENDER_OFFSCREEN_H

#include <GL/glew.h>
#if defined(__APPLE__)
#include <OpenGL/gl.h>
#include <OpenGL/glu.h>
#else
#include <GL/gl.h>
#include <GL/glu.h>
#endif
#include <GL/glut.h>

class OffscreenGL {

public:
OffscreenGL(int maxHeight, int maxWidth);
~OffscreenGL();

private:
static int glutWin;
static bool glutInitialized;
GLuint fb;
GLuint renderTex;
GLuint depthTex;
};


void renderDepthMesh(double *FM, int fNum, double *VM, int vNum, double *CM, double *intrinsics, int *imgSizeV, double *zNearFarV, unsigned char * imgBuffer, float *depthBuffer, bool *maskBuffer, double linewidth, bool coloring, bool one_offset);

#endif
70 changes: 70 additions & 0 deletions pyrender.pyx
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
cimport cython
import numpy as np
cimport numpy as np

from libc.stdlib cimport free, malloc
from libcpp cimport bool
from cpython cimport PyObject, Py_INCREF

CREATE_INIT = True # workaround, so cython builds a init function

np.import_array()


cdef extern from "offscreen.h":
void renderDepthMesh(double *FM, int fNum, double *VM, int vNum, double *CM, double *intrinsics, int *imgSizeV, double *zNearFarV, unsigned char * imgBuffer, float *depthBuffer, bool *maskBuffer, double linewidth, bool coloring, bool one_offset);


def render(double[:,::1] vertices, double[:,::1] faces, double[::1] cam_intr, int[::1] img_size, double linewidth=0, double[:,::1] colors=None):
if vertices.shape[0] != 3:
raise Exception('vertices must be a 3xM double array')
if faces.shape[0] != 3:
raise Exception('faces must be a 3xM double array')
if cam_intr.shape[0] != 4:
raise Exception('cam_intr must be a 4x1 double vector')
if img_size.shape[0] != 2:
raise Exception('img_size must be a 2x1 int vector')

cdef double* VM = &(vertices[0,0])
cdef int vNum = vertices.shape[1]
cdef double* FM = &(faces[0,0])
cdef int fNum = faces.shape[1]
cdef double* intrinsics = &(cam_intr[0])
cdef int* imgSize = &(img_size[0])

cdef bool coloring = True
cdef double* CM = NULL
if colors is not None:
CM = &(colors[0,0])
else:
coloring = False

cdef double znf[2]
znf[0] = 1e10
znf[1] = -1e10
cdef double z
for i in range(vNum):
z = VM[2*vNum+i]
if (z<znf[0]):
znf[0] = z
if (z>znf[1]):
znf[1] = z

znf[0] -= 0.1;
znf[1] += 0.1;
znf[0] = max(znf[0],0.1);
znf[1] = max(znf[1],znf[0]+0.1);

depth = np.empty((img_size[1], img_size[0]), dtype=np.float32)
mask = np.empty((img_size[1], img_size[0]), dtype=np.uint8)
img = np.empty((3, img_size[1], img_size[0]), dtype=np.uint8)
cdef float[:,::1] depth_view = depth
cdef unsigned char[:,::1] mask_view = mask
cdef unsigned char[:,:,::1] img_view = img
cdef float* depthBuffer = &(depth_view[0,0])
cdef bool* maskBuffer = <bool*> &(mask_view[0,0])
cdef unsigned char* imgBuffer = &(img_view[0,0,0])

renderDepthMesh(FM, fNum, VM, vNum, CM, intrinsics, imgSize, znf, imgBuffer, depthBuffer, maskBuffer, linewidth, coloring, False);

return depth.T, mask.T, img.transpose((2,1,0))
34 changes: 34 additions & 0 deletions setup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
from distutils.core import setup
from Cython.Build import cythonize
from distutils.extension import Extension
from Cython.Distutils import build_ext
import numpy as np
import platform

extra_compile_args = []
extra_link_args = ['-lGLEW', '-lglut']

if platform.system() == 'Darwin':
extra_link_args.append('-framework OpenGL')
extra_link_args.append('-framework GLU')
else:
extra_link_args.append('-lGL')
extra_link_args.append('-lGLU')

setup(
name="pyrender",
cmdclass= {'build_ext': build_ext},
ext_modules=[
Extension('pyrender',
['pyrender.pyx',
'offscreen.cpp',
],
language='c++',
include_dirs=[np.get_include(),],
extra_compile_args=extra_compile_args,
extra_link_args=extra_link_args
)
]
)


0 comments on commit 8e5cbcd

Please sign in to comment.