Image not showing for a ray-tracer - python

I wanted to follow a tutorial posted by Omar Aflak on https://medium.com/swlh/ray-tracing-from-scratch-in-python-41670e6a96f9 but I can’t seem to produce an image? I’m using jupyter notebook if that helps!
This is part of the code that he used, the full one is in the link above!
camera = np.array([0, 0, 1])
ratio = float(width) / height
screen = (-1, 1 / ratio, 1, -1 / ratio) # left, top, right, bottom
light = { 'position': np.array([5, 5, 5]), 'ambient': np.array([1, 1, 1]), 'diffuse': np.array([1, 1, 1]), 'specular': np.array([1, 1, 1]) }
objects = [
{ 'center': np.array([-0.2, 0, -1]), 'radius': 0.7, 'ambient': np.array([0.1, 0, 0]), 'diffuse': np.array([0.7, 0, 0]), 'specular': np.array([1, 1, 1]), 'shininess': 100, 'reflection': 0.5 },
{ 'center': np.array([0.1, -0.3, 0]), 'radius': 0.1, 'ambient': np.array([0.1, 0, 0.1]), 'diffuse': np.array([0.7, 0, 0.7]), 'specular': np.array([1, 1, 1]), 'shininess': 100, 'reflection': 0.5 },
{ 'center': np.array([-0.3, 0, 0]), 'radius': 0.15, 'ambient': np.array([0, 0.1, 0]), 'diffuse': np.array([0, 0.6, 0]), 'specular': np.array([1, 1, 1]), 'shininess': 100, 'reflection': 0.5 },
{ 'center': np.array([0, -9000, 0]), 'radius': 9000 - 0.7, 'ambient': np.array([0.1, 0.1, 0.1]), 'diffuse': np.array([0.6, 0.6, 0.6]), 'specular': np.array([1, 1, 1]), 'shininess': 100, 'reflection': 0.5 }
]
image = np.zeros((height, width, 3))
for i, y in enumerate(np.linspace(screen[1], screen[3], height)):
for j, x in enumerate(np.linspace(screen[0], screen[2], width)):
# screen is on origin
pixel = np.array([x, y, 0])
origin = camera
direction = normalize(pixel - origin)
color = np.zeros((3))
reflection = 1
for k in range(max_depth):
# check for intersections
nearest_object, min_distance = nearest_intersected_object(objects, origin, direction)
if nearest_object is None:
break
intersection = origin + min_distance * direction
normal_to_surface = normalize(intersection - nearest_object['center'])
shifted_point = intersection + 1e-5 * normal_to_surface
intersection_to_light = normalize(light['position'] - shifted_point)
_, min_distance = nearest_intersected_object(objects, shifted_point, intersection_to_light)
intersection_to_light_distance = np.linalg.norm(light['position'] - intersection)
is_shadowed = min_distance < intersection_to_light_distance
if is_shadowed:
break
illumination = np.zeros((3))
# ambiant
illumination += nearest_object['ambient'] * light['ambient']
# diffuse
illumination += nearest_object['diffuse'] * light['diffuse'] * np.dot(intersection_to_light, normal_to_surface)
# specular
intersection_to_camera = normalize(camera - intersection)
H = normalize(intersection_to_light + intersection_to_camera)
illumination += nearest_object['specular'] * light['specular'] * np.dot(normal_to_surface, H) ** (nearest_object['shininess'] / 4)
# reflection
color += reflection * illumination
reflection *= nearest_object['reflection']
origin = shifted_point
direction = reflected(direction, normal_to_surface)
image[i, j] = np.clip(color, 0, 1)
print("%d/%d" % (i + 1, height))
plt.imsave('image.png', image)
Instead of producing an image it just gave me a series of fractions from 1/200 to 200/200…does anyone know how to fix this?

Related

Minimizing MSE in Python, Sympy

I have a function. I need to minimize the MSE to 0.75 or less. I was able to get to 6 and don't know what to do next.
def print_points_ands_function1(sympy_function):
def function(x_): return float(sympy_function.subs(x, x_))
points_X = np.array([-2, -1, 0, 1, 2, 3, 3.5, 4, 4.5, 5])
points_Y = np.array([3, 1, -0.5, 1.2, 2.5, 0.8, -2, -3.5, -3, -5])
plt.xlim(-10, 10)
plt.ylim(-5, 5)
plt.scatter(points_X, points_Y, c='r')
x_range = np.linspace(plt.xlim()[0], plt.xlim()[1], num=100)
function_Y = [function(x_) for x_ in x_range]
plt.plot(x_range, function_Y, 'b')
plt.show()
MSE = sum([(points_Y[i] - function(points_X[i]))**2 for i in range(len(points_Y))]) / len(points_Y)
print(f'MSE = {MSE}')
f1 = 2 * x * sin(2 * x + 5) + 5 * sin(2 * x + 5) + 2
print(f1)
print_points_ands_function1(f1)
f1_1 = expand(f1.subs(x, x / 1.4))
print(f1_1)
print_points_ands_function1(f1_1)
f1_2 = expand(f1_1.subs(x, -x))
print(f1_2)
print_points_ands_function1(f1_2)
f1_3 = expand(f1_2.subs(x, x + 2.6))
print(f1_3)
print_points_ands_function1(f1_3)

Optimizing GLViewWidget MeshItem plot

I'm plotting 100K+ cylinders which are added to a GLViewWidget like this:
openGlWidget = GLViewWidget()
points = np.array([
[0, 1, 2],
[0, 1, 5],
[4, 1, 6],
[4, 1, 10]
])
cyl = MeshData.cylinder(6, 6, radius=[.1, .1])
for i in range(0, len(points), 2):
mesh = GLMeshItem(meshdata=cyl)
p1, p2 = points[i], points[i+1]
v = p2 - p1
theta = np.arctan2(v[1], v[0])
phi = np.arctan2(np.linalg.norm(v[:2]), v[2])
tr = Transform3D()
tr.translate(*p1)
tr.rotate(theta * 180 / np.pi, 0, 0, 1)
tr.rotate(phi * 180 / np.pi, 0, 1, 0)
tr.translate(0, 0, 1)
tr.scale(1, 1, np.linalg.norm(v1))
mesh.setTransform(tr)
self.openGlWidget.addItem(mesh)
This of course gives low fps when rotating/paning and I assume a good part of it has to do with adding that many singular items. So, I started thinking about the possibility of combining the cylinders into one item, is this possible? And/or should I be using something else than the GLViewWidget?

3D Plot Order Issues (Lines intersecting spheres)

So I'm having an issue where I want to plot a simple box, with a wire frame, one panel coloured and spheres on the corners. However, whilst I've managed to do this, the spheres are a source of issue in that they obstruct the wire frame in places (which I like) but not in other places.
Any ideas why this is occurring and how to resolve the matter?
import numpy as NP
import os
from matplotlib import pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
from mpl_toolkits.mplot3d.art3d import Poly3DCollection
os.system( 'cls' )
def main( ):
fig = plt.figure( figsize = ( cm2inch( 6 ), cm2inch ( 6 ) ), dpi = 100 )
ax = fig.add_subplot(111, projection='3d')
# Box frame plot
ax.plot( xs = [ 0, 0, 1, 1, 0 ], ys = [ 0, 1, 1, 0, 0 ], zs = [ 0, 0, 0, 0, 0 ], color = 'black', linewidth = 2 ) #<- Base
ax.plot( xs = [ 0, 0, 1, 1, 0 ], ys = [ 0, 1, 1, 0, 0 ], zs = [ 1, 1, 1, 1, 1 ], color = 'black', linewidth = 2 ) #<- Top
ax.plot( xs = [ 0, 0, 1, 1, 0 ], ys = [ 0, 0, 0, 0, 0 ], zs = [ 0, 1, 1, 0, 0 ], color = 'black', linewidth = 2 ) #<- Front
ax.plot( xs = [ 0, 0, 1, 1, 0 ], ys = [ 1, 1, 1, 1, 1 ], zs = [ 0, 1, 1, 0, 0 ], color = 'black', linewidth = 2 ) #<- Back
# Back face plot
xs = [ 0, 0, 1, 1, 0 ]
ys = [ 1, 1, 1, 1, 1 ]
zs = [ 0, 1, 1, 0, 0 ]
verts = [ list( zip( xs, ys, zs ) ) ]
temp = Poly3DCollection( verts, alpha = 0.25 )
temp.set_facecolor( 'blue' )
ax.add_collection3d( temp )
# Corner sphere plots
for x in [ 0, 1 ]:
for y in [ 0, 1 ]:
for z in [ 0, 1 ]:
xs, ys, zs = drawSphere( x, y, z, 0.05 )
ax.plot_surface(xs, ys, zs, color="r", alpha = 1)
#plt.axis('scaled')
plt.axis( 'off' )
plt.savefig( 'Example', pad_inches = 0, transparent = False, dpi = 400 )
plt.show()
plt.close( )
def drawSphere( xCenter, yCenter, zCenter, r ):
#draw sphere
u, v = NP.mgrid[ 0 : 2 * NP.pi : 20j, 0 : NP.pi : 10j ]
x = NP.cos( u ) * NP.sin( v )
y = NP.sin( u ) * NP.sin( v )
z = NP.cos( v )
# shift and scale sphere
x = r * x + xCenter
y = r * y + yCenter
z = r * z + zCenter
return( x, y, z)
def cm2inch( cm ):
inches = cm / 2.54
return inches
if __name__ == '__main__':
main( )

Python: Boundary points of a non-convex grid

I have the following situation as shown in the Figure below:
I want to find out the grid points that surround the red points. The red points are trajectories of moving agents. So in many situations we have a bunch of points, therefore the solution should be as fast as possible.
The grid is plotted as points.
First step, I managed to reduce the number of grid points as shown below (plotted as x):
This is my code:
step = .5
gridX, gridY = np.meshgrid(np.arange(xmin-step, xmax+step, step), np.arange(ymin-step, ymax+step, step))
mask = False * np.empty_like(gridX, dtype=bool)
threshold = 0.5
for (x,y) in zip(df_traj['X'], df_traj['Y']):
pX = x * np.ones_like(gridX)
pY = y * np.ones_like(gridY)
distX = (pX - gridX)**2
distY = (pY - gridY)**2
dist = np.sqrt(distX + distY)
condition = (dist < threshold)
mask = mask | condition
gX = gridX*mask
gY = gridY*mask
Second step, and this is where I need a little help:
How can I efficiently filter out the inner points of the grid and keep only the "x-points" outside the "red area"?
EDIT
In this special case I have 92450 red points.
I think if you just walk around the edge, since its a evenly spaced grid, it should work. No need for far more complicated non-convex-hull to handle pnts that can be anywhere. This isn't adapted to your code and I cheat with my data structures to make the code easy so youll have to handle that but it think as psuedocode it should work.
pnts = <<lists of points>>
edge_pnts = []
fpnt = pnt_with_min_x_then_min_y
cpnt = fpnt
npnt = None
while npnt != fpnt:
if (cpnt[0] + 1, cpnt[1] ) in pnts: npnt = (cpnt[0] + 1, cpnt[1] )
elif (cpnt[0] + 1, cpnt[1] + 1) in pnts: npnt = (cpnt[0] + 1, cpnt[1] + 1)
elif (cpnt[0], cpnt[1] + 1) in pnts: npnt = (cpnt[0] , cpnt[1] + 1)
elif (cpnt[0] - 1, cpnt[1] + 1) in pnts: npnt = (cpnt[0] - 1, cpnt[1] + 1)
elif (cpnt[0] - 1, cpnt[1] ) in pnts: npnt = (cpnt[0] - 1, cpnt[1] )
elif (cpnt[0] - 1, cpnt[1] - 1) in pnts: npnt = (cpnt[0] - 1, cpnt[1] - 1)
elif (cpnt[0] , cpnt[1] - 1) in pnts: npnt = (cpnt[0] , cpnt[1] - 1)
elif (cpnt[0] + 1, cpnt[1] - 1) in pnts: npnt = (cpnt[0] + 1, cpnt[1] - 1)
else: raise ValueError("Oh no!")
edge_pnts.append(npnt)
cpnt = npnt
For non convex polygons, like your example, convex hull is not a solution. My recommendation is that, given you already have a discrete grid, that you simply attribute the value False to a bool grid cell when a sample occurs inside. Something like this:
import numpy as np
import matplotlib.pyplot as plt
# Generic data production
X, Y = np.random.normal(0, 1, 100000), np.random.normal(0, 1, 100000)
ind = np.where((X > 0) & (Y > 0))
X[ind] = 0
Y[ind] = 0
# Generic grid definition
step = 0.5
xmin, xmax = X.min(), X.max()
ymin, ymax = Y.min(), Y.max()
firstx = xmin-step/2
firsty = ymin-step/2
lastx = xmax+2*step/2
lasty = ymax+2*step/2
gridX, gridY = np.meshgrid(np.arange(firstx, lastx, step), np.arange(firsty, lasty, step))
# This is the actual code that computes inside or outside
bool_grid = np.ones(gridX.shape, dtype="bool")
bool_grid[np.int_(0.5+(Y-firsty)/step), np.int_(0.5+(X-firstx)/step)] = False
# Plot code
plt.scatter(gridX.flatten(), gridY.flatten(), marker="+", color="black", alpha=0.3)
plt.scatter(gridX[bool_grid].flatten(), gridY[bool_grid].flatten(), marker="+", s=90, color="green")
plt.scatter(X, Y, s=10, color="red")
plt.show()
, which results in the following (green crosses are True values):
NOTE: This is very fast but it has some limitations. If your data is not compact you'll have True values inside the shape (so holes are possible). It's possible to process the image to remove the holes however (a flood fill or a moving window based algorithm for example). Another possibility is to play with the resolution of the grid.
You just need to pick a point you know is on the hull (let's take the leftmost point among the topmost points), and assume you "got to it" from above (as we know there is no points above it).
now while the next point is not in your list:
Try going CCW from the direction you came from.
The code looks like that:
matrix = [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0],
[0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0],
[0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]
# Find the leftmost topmost point
first_point = None
for i in range(len(matrix)):
if first_point:
break
for j in range(len(matrix[0])):
if matrix[i][j]:
first_point = [i, j]
break
next_point = first_point
prev_direction = 'up'
next_direction_dict = {'up': 'left', 'left': 'down', 'down': 'right', 'right': 'up'}
opposite_direction = {'up': 'down', 'left': 'right', 'down': 'up', 'right': 'left'}
hull_points = []
def go_direction(point, direction):
# Find the point to a given direction of a given point
i = point[0]
j = point[1]
if direction == 'right':
j += 1
elif direction == 'up':
i -= 1
elif direction == 'left':
j -= 1
elif direction == 'down':
i += 1
else:
raise ValueError
return [i, j]
def find_next_point(matrix, point, prev_direction):
next_direction = next_direction_dict[prev_direction]
next_point = go_direction(point, next_direction)
prev_direction = next_direction
while not matrix[next_point[0]][next_point[1]]:
next_direction = next_direction_dict[prev_direction]
next_point = go_direction(point, next_direction)
prev_direction = next_direction
from_direction = opposite_direction[prev_direction]
return next_point, from_direction
next_point, prev_direction = find_next_point(matrix, next_point, prev_direction)
while next_point != first_point:
if next_point not in hull_points:
hull_points.append(next_point)
next_point, prev_direction = find_next_point(matrix, next_point, prev_direction)
Edit:
Now also handles single point 'tentacles' by iterating until returning to the first point
Heres another though that came to my mind --
A flood fill of the space:
pnts = <<lists of points>>
seen = set()
edges = []
stack = (0,0)
while stack:
ele = stack.pop()
if ele in pnts:
edges.append(ele)
else:
seen.add(ele)
if (ele[0] + 1, ele[1]) not in seen:
stack.append(ele[0] + 1, ele[1])
if (ele[0] - 1, ele[1]) not in seen:
stack.append(ele[0] - 1, ele[1])
if (ele[0], ele[1] + 1) not in seen:
stack.append(ele[0], ele[1] + 1)
if (ele[0], ele[1] - 1) not in seen:
stack.append(ele[0], ele[1] - 1)
Then you need to sort the points which shouldn't be too hard.

Python numpy running sum of repeated trues [duplicate]

I have an array like so:
a = np.array([0.1, 0.2, 1.0, 1.0, 1.0, 0.9, 0.6, 1.0, 0.0, 1.0])
I'd like to have a running counter of instances of 1.0 that resets when it encounters a 0.0, so the result would be:
[0, 0, 1, 2, 3, 3, 3, 4, 0, 1]
My initial thought was to use something like b = np.cumsum(a[a==1.0]), but I don't know how to (1) modify this to reset at zeros or (2) quite how to structure it so the output array is the same shape as the input array. Any ideas how to do this without iteration?
I think you could do something like
def rcount(a):
without_reset = (a == 1).cumsum()
reset_at = (a == 0)
overcount = np.maximum.accumulate(without_reset * reset_at)
result = without_reset - overcount
return result
which gives me
>>> a = np.array([0.1, 0.2, 1.0, 1.0, 1.0, 0.9, 0.6, 1.0, 0.0, 1.0])
>>> rcount(a)
array([0, 0, 1, 2, 3, 3, 3, 4, 0, 1])
This works because we can use the cumulative maximum to figure out the "overcount":
>>> without_reset * reset_at
array([0, 0, 0, 0, 0, 0, 0, 0, 4, 0])
>>> np.maximum.accumulate(without_reset * reset_at)
array([0, 0, 0, 0, 0, 0, 0, 0, 4, 4])
Sanity testing:
def manual(arr):
out = []
count = 0
for x in arr:
if x == 1:
count += 1
if x == 0:
count = 0
out.append(count)
return out
def test():
for w in [1, 2, 10, 10**4]:
for trial in range(100):
for vals in [0,1],[0,1,2]:
b = np.random.choice(vals, size=w)
assert (rcount(b) == manual(b)).all()
print("hooray!")
and then
>>> test()
hooray!

Categories