summaryrefslogtreecommitdiff
path: root/camera.py
diff options
context:
space:
mode:
Diffstat (limited to 'camera.py')
-rw-r--r--camera.py261
1 files changed, 166 insertions, 95 deletions
diff --git a/camera.py b/camera.py
index 2feafd7..18203c0 100644
--- a/camera.py
+++ b/camera.py
@@ -2,8 +2,10 @@ import numpy as np
from itertools import product, count
from threading import Thread, Lock
import time
+import datetime
import os
import sys
+from color import map_wavelength
from transform import rotate
@@ -14,6 +16,32 @@ from pycuda import gpuarray
from pycuda.characterize import sizeof
import pycuda.driver as cuda
+from subprocess import call
+import shutil
+import tempfile
+
+def timeit(func):
+ def f(*args, **kwargs):
+ t0 = time.time()
+ func(*args, **kwargs)
+ elapsed = time.time() - t0
+ print '%s elapsed in %s().' % (datetime.timedelta(seconds=elapsed), func.__name__)
+ return f
+
+def encode_movie(dir):
+ root, ext = 'movie', 'avi'
+ for i in count():
+ filename = '.'.join([root + str(i).zfill(5), ext])
+
+ if not os.path.exists(filename):
+ break
+
+ call(['mencoder', 'mf://' + dir + '/*.png', '-mf', 'fps=10', '-o', filename, '-ovc', 'xvid', '-xvidencopts', 'bitrate=3000'])
+
+ shutil.rmtree(dir)
+
+ print 'movie saved to %s.' % filename
+
def get_rays(position, size = (800, 600), film_size = (0.035, 0.024), focal_length=0.05):
"""
Generate ray positions and directions from a pinhole camera facing the negative y direction.
@@ -62,9 +90,11 @@ class Camera(Thread):
self.context.push()
self.ray_trace_kernel = self.module.get_function('ray_trace')
self.rotate_kernel = self.module.get_function('rotate')
+ self.rotate_around_point_kernel = self.module.get_function('rotate_around_point')
self.translate_kernel = self.module.get_function('translate')
- self.build_rgb_lookup_kernel = self.module.get_function('build_rgb_lookup')
- self.render_kernel = self.module.get_function('render')
+ self.update_xyz_lookup_kernel = self.module.get_function('update_xyz_lookup')
+ self.update_xyz_image_kernel = self.module.get_function('update_xyz_image')
+ self.process_image_kernel = self.module.get_function('process_image')
self.init_rng_kernel = self.module.get_function('init_rng')
self.context.pop()
@@ -77,7 +107,7 @@ class Camera(Thread):
self.axis1 = np.array([1,0,0], float)
self.axis2 = np.array([0,0,1], float)
- origins, directions = get_rays(self.point)
+ origins, directions = get_rays(self.point, self.size)
with self.lock:
self.context.push()
@@ -86,85 +116,84 @@ class Camera(Thread):
self.pixels_gpu = gpuarray.zeros(self.width*self.height, dtype=np.int32)
self.context.pop()
+ self.movie = False
+ self.movie_index = 0
+ self.movie_dir = None
self.render = False
- def build_rgb_lookup(self, source_position=(0,0,0)):
+ @timeit
+ def initialize_render(self):
with self.lock:
self.context.push()
- print '\ninitializing random number states.'
self.rng_states_gpu = cuda.mem_alloc(self.width*self.height*sizeof('curandStateXORWOW', '#include <curand_kernel.h>'))
self.init_rng_kernel(np.int32(self.width*self.height), self.rng_states_gpu, np.int32(0), np.int32(0), block=(self.nblocks,1,1), grid=(self.width*self.height//self.nblocks+1,1))
+ self.xyz_lookup1_gpu = gpuarray.zeros(len(self.geometry.mesh.triangles), dtype=gpuarray.vec.float3)
+ self.xyz_lookup2_gpu = gpuarray.zeros(len(self.geometry.mesh.triangles), dtype=gpuarray.vec.float3)
+ self.image_gpu = gpuarray.zeros(self.width*self.height, dtype=gpuarray.vec.float3)
self.context.synchronize()
- print 'done.'
-
- self.source_positions_gpu = gpuarray.empty(len(geometry.mesh.triangles), dtype=gpuarray.vec.float3)
- self.source_positions_gpu.fill(gpuarray.vec.make_float3(*source_position))
+ self.context.pop()
- source_directions = np.mean(self.geometry.mesh[:], axis=1) - source_position
- self.source_directions_gpu = gpuarray.to_gpu(source_directions.astype(np.float32).view(gpuarray.vec.float3))
+ self.source_position = self.point
- self.rgb_lookup1_gpu = gpuarray.zeros(self.source_positions_gpu.size, dtype=gpuarray.vec.float3)
- self.rgb_lookup2_gpu = gpuarray.zeros(self.source_positions_gpu.size, dtype=gpuarray.vec.float3)
+ self.nimages = 0
+ self.nlookup_calls = 0
+ self.max_steps = 10
- self.max_steps = 10
- rgb_runs = 100
-
- print 'building rgb lookup.'
- self.build_rgb_lookup_kernel(np.int32(self.source_positions_gpu.size), self.source_positions_gpu, self.source_directions_gpu, self.rgb_lookup1_gpu, self.rgb_lookup2_gpu, np.uint64(np.random.randint(np.iinfo(np.int64).max)), np.int32(rgb_runs), np.int32(self.max_steps), block=(self.nblocks,1,1), grid=(self.source_positions_gpu.size//self.nblocks+1,1))
- self.context.synchronize()
- print 'done.'
+ def clear_xyz_lookup(self):
+ with self.lock:
+ self.context.push()
+ self.xyz_lookup1_gpu.fill(gpuarray.vec.make_float3(0.0,0.0,0.0))
+ self.xyz_lookup2_gpu.fill(gpuarray.vec.make_float3(0.0,0.0,0.0))
+ self.context.pop()
- rgb_lookup1 = self.rgb_lookup1_gpu.get().view(np.float32)
- rgb_lookup1 /= rgb_runs
- rgb_lookup1[rgb_lookup1 > 1.0] = 1.0
- self.rgb_lookup1_gpu.set(rgb_lookup1.view(gpuarray.vec.float3))
+ self.nlookup_calls = 0
- rgb_lookup2 = self.rgb_lookup2_gpu.get().view(np.float32)
- rgb_lookup2 /= rgb_runs
- rgb_lookup2[rgb_lookup2 > 1.0] = 1.0
- self.rgb_lookup2_gpu.set(rgb_lookup2.view(gpuarray.vec.float3))
+ def update_xyz_lookup(self, source_position):
+ with self.lock:
+ self.context.push()
+ for i in range(self.xyz_lookup1_gpu.size//(self.width*self.height)+1):
+ self.update_xyz_lookup_kernel(np.int32(self.width*self.height), np.int32(self.xyz_lookup1_gpu.size), np.int32(i*self.width*self.height), gpuarray.vec.make_float3(*source_position), self.rng_states_gpu, np.float32(685.0), gpuarray.vec.make_float3(1.0,0.0,0.0), self.xyz_lookup1_gpu, self.xyz_lookup2_gpu, np.int32(self.max_steps), block=(self.nblocks,1,1), grid=(self.width*self.height//self.nblocks+1,1))
- self.nimages = 0
- self.rgb_image = np.zeros((self.pixels_gpu.size, 3), float)
- self.r_gpu = gpuarray.zeros(self.pixels_gpu.size, dtype=np.float32)
- self.g_gpu = gpuarray.zeros(self.pixels_gpu.size, dtype=np.float32)
- self.b_gpu = gpuarray.zeros(self.pixels_gpu.size, dtype=np.float32)
+ for i in range(self.xyz_lookup1_gpu.size//(self.width*self.height)+1):
+ self.update_xyz_lookup_kernel(np.int32(self.width*self.height), np.int32(self.xyz_lookup1_gpu.size), np.int32(i*self.width*self.height), gpuarray.vec.make_float3(*source_position), self.rng_states_gpu, np.float32(545.0), gpuarray.vec.make_float3(0.0,1.0,0.0), self.xyz_lookup1_gpu, self.xyz_lookup2_gpu, np.int32(self.max_steps), block=(self.nblocks,1,1), grid=(self.width*self.height//self.nblocks+1,1))
+ for i in range(self.xyz_lookup1_gpu.size//(self.width*self.height)+1):
+ self.update_xyz_lookup_kernel(np.int32(self.width*self.height), np.int32(self.xyz_lookup1_gpu.size), np.int32(i*self.width*self.height), gpuarray.vec.make_float3(*source_position), self.rng_states_gpu, np.float32(445.0), gpuarray.vec.make_float3(0.0,0.0,1.0), self.xyz_lookup1_gpu, self.xyz_lookup2_gpu, np.int32(self.max_steps), block=(self.nblocks,1,1), grid=(self.width*self.height//self.nblocks+1,1))
self.context.pop()
+ self.nlookup_calls += 1
+
def clear_image(self):
- try:
- self.rgb_image.fill(0.0)
- self.nimages = 0
- except AttributeError:
- pass
+ with self.lock:
+ self.context.push()
+ self.image_gpu.fill(gpuarray.vec.make_float3(0.0,0.0,0.0))
+ self.context.pop()
- def acquire_image(self):
- if not hasattr(self, 'rng_states_gpu'):
- self.build_rgb_lookup()
+ self.nimages = 0
+ def update_image(self):
with self.lock:
self.context.push()
- self.render_kernel(np.int32(self.pixels_gpu.size), self.rng_states_gpu, self.origins_gpu, self.directions_gpu, self.r_gpu, self.g_gpu, self.b_gpu, self.rgb_lookup1_gpu, self.rgb_lookup2_gpu, np.int32(self.max_steps), block=(self.nblocks,1,1), grid=(self.pixels_gpu.size//self.nblocks+1,1))
- self.rgb_image[:,0] += self.r_gpu.get()
- self.rgb_image[:,1] += self.g_gpu.get()
- self.rgb_image[:,2] += self.b_gpu.get()
- self.nimages += 1
- self.context.pop()
+ self.update_xyz_image_kernel(np.int32(self.width*self.height), self.rng_states_gpu, self.origins_gpu, self.directions_gpu, np.float32(685.0), gpuarray.vec.make_float3(1.0,0.0,0.0), self.xyz_lookup1_gpu, self.xyz_lookup2_gpu, self.image_gpu, np.int32(self.nlookup_calls), np.int32(self.max_steps), block=(self.nblocks,1,1), grid=(self.width*self.height//self.nblocks+1,1))
+
+ self.update_xyz_image_kernel(np.int32(self.width*self.height), self.rng_states_gpu, self.origins_gpu, self.directions_gpu, np.float32(545.0), gpuarray.vec.make_float3(0.0,1.0,0.0), self.xyz_lookup1_gpu, self.xyz_lookup2_gpu, self.image_gpu, np.int32(self.nlookup_calls), np.int32(self.max_steps), block=(self.nblocks,1,1), grid=(self.width*self.height//self.nblocks+1,1))
- def render_image(self):
- scaled_rgb_image = ((self.rgb_image/self.nimages)*255).astype(np.int32)
+ self.update_xyz_image_kernel(np.int32(self.width*self.height), self.rng_states_gpu, self.origins_gpu, self.directions_gpu, np.float32(445.0), gpuarray.vec.make_float3(0.0,0.0,1.0), self.xyz_lookup1_gpu, self.xyz_lookup2_gpu, self.image_gpu, np.int32(self.nlookup_calls), np.int32(self.max_steps), block=(self.nblocks,1,1), grid=(self.width*self.height//self.nblocks+1,1))
+ self.context.pop()
- image = scaled_rgb_image[:,0] << 16 | scaled_rgb_image[:,1] << 8 | scaled_rgb_image[:,2]
+ self.nimages += 1
- pygame.surfarray.blit_array(self.screen, image.reshape(self.size))
- pygame.display.flip()
+ def process_image(self):
+ with self.lock:
+ self.context.push()
+ self.process_image_kernel(np.int32(self.width*self.height), self.image_gpu, self.pixels_gpu, np.int32(self.nimages), block=(self.nblocks,1,1), grid=((self.width*self.height)//self.nblocks+1,1))
+ self.context.pop()
- def screenshot(self, dir=''):
+ def screenshot(self, dir='', start=0):
root, ext = 'screenshot', 'png'
- for i in count():
- filename = os.path.join(dir, '.'.join([root + str(i).zfill(4), ext]))
+ for i in count(start):
+ filename = os.path.join(dir, '.'.join([root + str(i).zfill(5), ext]))
if not os.path.exists(filename):
break
@@ -175,14 +204,34 @@ class Camera(Thread):
def rotate(self, phi, n):
with self.lock:
self.context.push()
- self.rotate_kernel(np.int32(self.pixels_gpu.size), self.origins_gpu, phi, gpuarray.vec.make_float3(*n), block=(self.nblocks,1,1), grid=(self.pixels_gpu.size//self.nblocks+1,1))
- self.rotate_kernel(np.int32(self.pixels_gpu.size), self.directions_gpu, phi, gpuarray.vec.make_float3(*n), block=(self.nblocks,1,1), grid=(self.pixels_gpu.size//self.nblocks+1,1))
+ self.rotate_kernel(np.int32(self.pixels_gpu.size), self.origins_gpu, np.float32(phi), gpuarray.vec.make_float3(*n), block=(self.nblocks,1,1), grid=(self.pixels_gpu.size//self.nblocks+1,1))
+ self.rotate_kernel(np.int32(self.pixels_gpu.size), self.directions_gpu, np.float32(phi), gpuarray.vec.make_float3(*n), block=(self.nblocks,1,1), grid=(self.pixels_gpu.size//self.nblocks+1,1))
self.point = rotate(self.point, phi, n)
self.axis1 = rotate(self.axis1, phi, n)
self.axis2 = rotate(self.axis2, phi, n)
self.context.pop()
+ if self.render:
+ self.clear_image()
+
+ self.update()
+
+ def rotate_around_point(self, phi, n, point):
+ with self.lock:
+ self.context.push()
+ self.rotate_around_point_kernel(np.int32(self.origins_gpu.size), self.origins_gpu, np.float32(phi), gpuarray.vec.make_float3(*n), gpuarray.vec.make_float3(*point), block=(self.nblocks,1,1), grid=(self.origins_gpu.size//self.nblocks+1,1))
+ self.rotate_kernel(np.int32(self.directions_gpu.size), self.directions_gpu, np.float32(phi), gpuarray.vec.make_float3(*n), block=(self.nblocks,1,1), grid=(self.directions_gpu.size//self.nblocks+1,1))
+ self.context.pop()
+
+ self.axis1 = rotate(self.axis1, phi, n)
+ self.axis2 = rotate(self.axis2, phi, n)
+
+ if self.render:
+ self.clear_image()
+
+ self.update()
+
def translate(self, v):
with self.lock:
self.context.push()
@@ -191,55 +240,58 @@ class Camera(Thread):
self.point += v
self.context.pop()
+ if self.render:
+ self.clear_image()
+
+ self.update()
+
def update(self):
if self.render:
- self.acquire_image()
- self.render_image()
- return
+ while self.nlookup_calls < 10:
+ self.update_xyz_lookup(self.source_position)
+ self.update_image()
+ self.process_image()
+ else:
+ with self.lock:
+ self.context.push()
+ self.ray_trace_kernel(np.int32(self.pixels_gpu.size), self.origins_gpu, self.directions_gpu, self.pixels_gpu, block=(self.nblocks,1,1), grid=(self.pixels_gpu.size//self.nblocks+1,1))
+ self.context.pop()
with self.lock:
self.context.push()
- t0 = time.time()
- self.ray_trace_kernel(np.int32(self.pixels_gpu.size), self.origins_gpu, self.directions_gpu, self.pixels_gpu, block=(self.nblocks,1,1), grid=(self.pixels_gpu.size//self.nblocks+1,1))
- self.context.synchronize()
- elapsed = time.time() - t0
-
- print '\relapsed %f sec.' % elapsed,
- sys.stdout.flush()
-
pygame.surfarray.blit_array(self.screen, self.pixels_gpu.get().reshape(self.size))
pygame.display.flip()
self.context.pop()
+ if self.movie:
+ self.screenshot(self.movie_dir, self.movie_index)
+ self.movie_index += 1
+
def run(self):
self.update()
done = False
clicked = False
shift = False
+ ctrl = False
- current_layer = 0
+ #current_layer = 0
while not done:
self.clock.tick(20)
if self.render and not clicked and not pygame.event.peek(KEYDOWN):
- self.acquire_image()
- self.render_image()
+ self.update()
for event in pygame.event.get():
if event.type == MOUSEBUTTONDOWN:
if event.button == 4:
v = self.scale*np.cross(self.axis1,self.axis2)/10.0
self.translate(v)
- self.clear_image()
- self.update()
if event.button == 5:
v = -self.scale*np.cross(self.axis1,self.axis2)/10.0
self.translate(v)
- self.clear_image()
- self.update()
if event.button == 1:
clicked = True
@@ -263,56 +315,56 @@ class Camera(Thread):
if shift:
v = mouse_direction*self.scale*length/float(self.width)
self.translate(v)
- self.clear_image()
- self.update()
else:
phi = np.float32(2*np.pi*length/float(self.width))
n = rotate(mouse_direction, np.pi/2, -np.cross(self.axis1,self.axis2))
- self.rotate(phi, n)
- self.clear_image()
- self.update()
+ if ctrl:
+ self.rotate_around_point(phi, n, self.point)
+ else:
+ self.rotate(phi, n)
if event.type == KEYDOWN:
if event.key == K_a:
v = self.scale*self.axis1/10.0
self.translate(v)
- self.clear_image()
- self.update()
if event.key == K_d:
v = -self.scale*self.axis1/10.0
self.translate(v)
- self.clear_image()
- self.update()
if event.key == K_w:
v = self.scale*np.cross(self.axis1,self.axis2)/10.0
self.translate(v)
- self.clear_image()
- self.update()
if event.key == K_s:
v = -self.scale*np.cross(self.axis1,self.axis2)/10.0
self.translate(v)
- self.clear_image()
- self.update()
if event.key == K_SPACE:
v = self.scale*self.axis2/10.0
self.translate(v)
- self.clear_image()
- self.update()
- if event.key == K_LCTRL:
- v = -self.scale*self.axis2/10.0
- self.translate(v)
+ # if event.key == K_LCTRL:
+ # v = -self.scale*self.axis2/10.0
+ # self.translate(v)
+
+ if event.key == K_F6:
+ self.clear_xyz_lookup()
self.clear_image()
- self.update()
+ self.source_position = self.point
+
+ if event.key == K_p:
+ for i in range(10):
+ self.update_xyz_lookup(self.point)
+ self.source_position = self.point
if event.key == K_LSHIFT or event.key == K_RSHIFT:
shift = True
+ if event.key == K_LCTRL or event.key == K_RCTRL:
+ ctrl = True
+
if event.key == K_ESCAPE:
done = True
break
@@ -347,18 +399,37 @@ class Camera(Thread):
self.screenshot()
if event.key == K_F5:
+ if not hasattr(self, 'rng_states_gpu'):
+ self.initialize_render()
+
self.render = not self.render
+ self.clear_image()
self.update()
+ if event.key == K_m:
+ if self.movie:
+ encode_movie(self.movie_dir)
+ self.movie_dir = None
+ self.movie = False
+ else:
+ self.movie_index = 0
+ self.movie_dir = tempfile.mkdtemp()
+ self.movie = True
+
if event.type == KEYUP:
if event.key == K_LSHIFT or event.key == K_RSHIFT:
shift = False
+ if event.key == K_LCTRL or event.key == K_RCTRL:
+ ctrl = False
+
if event.type == pygame.QUIT:
done = True
break
- print
+ if self.movie:
+ encode_movie(self.movie_dir)
+
pygame.display.quit()
if __name__ == '__main__':