stratified sampling in ray tracing - nothing is changed - graphics

I have tried to implement stratified sampling in my ray tracer. But nothing is changed.
First I simply change my sampler from
while (s ++ < 100)
sampling(v, x, y);
to
while (s ++ < 10)
{
t = 0;
while (t ++ < 10)
stratified_sampling(v, x, y, s, t);
}
and in the sampling function,
u = ((double)x + random_double(0, 1))
v = ((double)y + random_double(0, 1))
this code changed to
u = ((double)x + ((double)s + random_double(0, 1)) / 10)
v = ((double)y + ((double)t + random_double(0, 1)) / 10)
so the 100 sampling should be done in the 10 x 10 grid in one pixel as i expected.
but the result image is not changed at all, at least in my opinion. what's the problem? Is it normal?? (first one is before stratified sampling)

Related

Solving equation of motion due to (Lorentz acceleration) using Forward Euler and Runge-Kutta 4th order using Python 3

I am tring to solve the equation of motion of charged particle in planetary magnetic field to see the path of the particle using Forward Euler's and RK5 method in python (as an excercise in learning Numerical methods) I encounter two problems:
The 'for loop' in the RK4 method does not update the new values. It give the values of the first iteration for all iteration.
With the change of the sing of 'β = charge/mass' the path of particle which is expected does not change. It seems the path is unaffected by the nature(sign) of the particle. What does this mean physically or mathematically?
The codes are adapted from :
python two coupled second order ODEs Runge Kutta 4th order
and
Applying Forward Euler Method to a Three-Box Model System of ODEs
I would be immensely grateful if anyone explain to me what is wrong in the code.
thank you.
The Code are as under:
import numpy as np
import matplotlib.pyplot as plt
from math import sin, cos
from scipy.integrate import odeint
scales = np.array([1e7, 0.1, 1, 1e-5, 10, 1e-5])
def LzForce(t,p):
# assigning each ODE to a vector element
r,x,θ,y,ϕ,z = p*scales
# constants
R = 60268e3 # metre
g_20 = 1583e-9
Ω = 9.74e-3 # degree/second
B_θ = (R/r)**4*g_20*cos(θ)*sin(θ)
B_r = 2*(R/r)**4*g_20*(0.5*(3*cos(θ)**2-1))
β = +9.36e10
# defining the ODEs
drdt = x
dxdt = r*(y**2 +(z+Ω)**2*sin(θ)**2-β*z*sin(θ)*B_θ)
dθdt = y
dydt = (-2*x*y +r*(z+Ω)**2*sin(θ)*cos(θ)+β*r*z*sin(θ)*B_r)/r
dϕdt = z
dzdt = (-2*x*(z+Ω)*sin(θ)-2*r*y*(z+Ω)*cos(θ)+β*(x*B_θ-r*y*B_r))/(r*sin(θ))
return np.array([drdt,dxdt,dθdt,dydt,dϕdt,dzdt])/scales
def ForwardEuler(fun,t0,p0,tf,dt):
r0 = 6.6e+07
x0 = 0.
θ0 = 88.
y0 = 0.
ϕ0 = 0.
z0 = 22e-3
p0 = np.array([r0,x0,θ0,y0,ϕ0,z0])
t = np.arange(t0,tf+dt,dt)
p = np.zeros([len(t), len(p0)])
p[0] = p0
for i in range(len(t)-1):
p[i+1,:] = p[i,:] + fun(t[i],p[i,:]) * dt
return t, p
def rk4(fun,t0,p0,tf,dt):
# initial conditions
r0 = 6.6e+07
x0 = 0.
θ0 = 88.
y0 = 0.
ϕ0 = 0.
z0 = 22e-3
p0 = np.array([r0,x0,θ0,y0,ϕ0,z0])
t = np.arange(t0,tf+dt,dt)
p = np.zeros([len(t), len(p0)])
p[0] = p0
for i in range(len(t)-1):
k1 = dt * fun(t[i], p[i])
k2 = dt * fun(t[i] + 0.5*dt, p[i] + 0.5 * k1)
k3 = dt * fun(t[i] + 0.5*dt, p[i] + 0.5 * k2)
k4 = dt * fun(t[i] + dt, p[i] + k3)
p[i+1] = p[i] + (k1 + 2*(k2 + k3) + k4)/6
return t,p
dt = 0.5
tf = 1000
p0 = [6.6e+07,0.0,88.0,0.0,0.0,22e-3]
t0 = 0
#Solution with Forward Euler
t,p_Euler = ForwardEuler(LzForce,t0,p0,tf,dt)
#Solution with RK4
t ,p_RK4 = rk4(LzForce,t0, p0 ,tf,dt)
print(t,p_Euler)
print(t,p_RK4)
# Plot Solutions
r,x,θ,y,ϕ,z = p_Euler.T
fig,ax=plt.subplots(2,3,figsize=(8,4))
plt.xlabel('time in sec')
plt.ylabel('parameters')
for a,s in zip(ax.flatten(),[r,x,θ,y,ϕ,z]):
a.plot(t,s); a.grid()
plt.title("Forward Euler", loc='left')
plt.tight_layout(); plt.show()
r,x,θ,y,ϕ,z = p_RK4.T
fig,ax=plt.subplots(2,3,figsize=(8,4))
plt.xlabel('time in sec')
plt.ylabel('parameters')
for a,q in zip(ax.flatten(),[r,x,θ,y,ϕ,z]):
a.plot(t,q); a.grid()
plt.title("RK4", loc='left')
plt.tight_layout(); plt.show()
[RK4 solution plot][1]
[Euler's solution methods][2]
''''RK4 does not give iterated values.
The path is unaffected by the change of sign which is expected as it is under Lorentz force''''
[1]: https://i.stack.imgur.com/bZdIw.png
[2]: https://i.stack.imgur.com/tuNDp.png
You are not iterating more than once inside the for loop in rk4 because it returns after the first iteration.
for i in range(len(t)-1):
k1 = dt * fun(t[i], p[i])
k2 = dt * fun(t[i] + 0.5*dt, p[i] + 0.5 * k1)
k3 = dt * fun(t[i] + 0.5*dt, p[i] + 0.5 * k2)
k4 = dt * fun(t[i] + dt, p[i] + k3)
p[i+1] = p[i] + (k1 + 2*(k2 + k3) + k4)/6
# This is the problem line, the return was tabbed in, to be inside the for block, so the block executed once and returned.
return t,p
For physics questions please try a different forum.

Optimizing asymmetrically reweighted penalized least squares smoothing (from matlab to python)

I'm trying to apply the method for baselinining vibrational spectra, which is announced as an improvement over asymmetric and iterative re-weighted least-squares algorithms in the 2015 paper (doi:10.1039/c4an01061b), where the following matlab code was provided:
function z = baseline(y, lambda, ratio)
% Estimate baseline with arPLS in Matlab
N = length(y);
D = diff(speye(N), 2);
H = lambda*D'*D;
w = ones(N, 1);
while true
W = spdiags(w, 0, N, N);
% Cholesky decomposition
C = chol(W + H);
z = C \ (C' \ (w.*y) );
d = y - z;
% make d-, and get w^t with m and s
dn = d(d<0);
m = mean(d);
s = std(d);
wt = 1./ (1 + exp( 2* (d-(2*s-m))/s ) );
% check exit condition and backup
if norm(w-wt)/norm(w) < ratio, break; end
end
that I rewrote into python:
def baseline_arPLS(y, lam, ratio):
# Estimate baseline with arPLS
N = len(y)
k = [numpy.ones(N), -2*numpy.ones(N-1), numpy.ones(N-2)]
offset = [0, 1, 2]
D = diags(k, offset).toarray()
H = lam * numpy.matmul(D.T, D)
w_ = numpy.ones(N)
while True:
W = spdiags(w_, 0, N, N, format='csr')
# Cholesky decomposition
C = cholesky(W + H)
z_ = spsolve(C.T, w_ * y)
z = spsolve(C, z_)
d = y - z
# make d- and get w^t with m and s
dn = d[d<0]
m = numpy.mean(dn)
s = numpy.std(dn)
wt = 1. / (1 + numpy.exp(2 * (d - (2*s-m)) / s))
# check exit condition and backup
norm_wt, norm_w = norm(w_-wt), norm(w_)
if (norm_wt / norm_w) < ratio:
break
w_ = wt
return(z)
Except for the input vector y the method requires parameters lam and ratio and it runs ok for values lam<1.e+07 and ratio>1.e-01, but outputs poor results. When values are changed outside this range, for example lam=1e+07, ratio=1e-02 the CPU starts heating up and job never finishes (I interrupted it after 1min). Also in both cases the following warning shows up:
/usr/local/lib/python3.9/site-packages/scipy/sparse/linalg/dsolve/linsolve.py: 144: SparseEfficencyWarning: spsolve requires A to be CSC or CSR matrix format warn('spsolve requires A to be CSC or CSR format',
although I added the recommended format='csr' option to the spdiags call.
And here's some synthetic data (similar to one in the paper) for testing purposes. The noise was added along with a 3rd degree polynomial baseline The method works well for parameters bl_1 and fails to converge for bl_2:
import numpy
from matplotlib import pyplot
from scipy.sparse import spdiags, diags, identity
from scipy.sparse.linalg import spsolve
from numpy.linalg import cholesky, norm
import sys
x = numpy.arange(0, 1000)
noise = numpy.random.uniform(low=0, high = 10, size=len(x))
poly_3rd_degree = numpy.poly1d([1.2e-06, -1.23e-03, .36, -4.e-04])
poly_baseline = poly_3rd_degree(x)
y = 100 * numpy.exp(-((x-300)/15)**2)+\
200 * numpy.exp(-((x-750)/30)**2)+ \
100 * numpy.exp(-((x-800)/15)**2) + noise + poly_baseline
bl_1 = baseline_arPLS(y, 1e+07, 1e-01)
bl_2 = baseline_arPLS(y, 1e+07, 1e-02)
pyplot.figure(1)
pyplot.plot(x, y, 'C0')
pyplot.plot(x, poly_baseline, 'C1')
pyplot.plot(x, bl_1, 'k')
pyplot.show()
sys.exit(0)
All this is telling me that I'm doing something very non-optimal in my python implementation. Since I'm not knowledgeable enough about the intricacies of scipy computations I'm kindly asking for suggestions on how to achieve convergence in this calculations.
(I encountered an issue in running the "straight" matlab version of the code because the line D = diff(speye(N), 2); truncates the last two rows of the matrix, creating dimension mismatch later in the function. Following the description of matrix D's appearance I substituted this line by directly creating a tridiagonal matrix using the diags function.)
Guided by the comment #hpaulj made, and suspecting that the loop exit wasn't coded properly, I re-visited the paper and found out that the authors actually implemented an exit condition that was not featured in their matlab script. Changing the while loop condition provides an exit for any set of parameters; my understanding is that algorithm is not guaranteed to converge in all cases, which is why this condition is necessary but was omitted by error. Here's the edited version of my python code:
def baseline_arPLS(y, lam, ratio):
# Estimate baseline with arPLS
N = len(y)
k = [numpy.ones(N), -2*numpy.ones(N-1), numpy.ones(N-2)]
offset = [0, 1, 2]
D = diags(k, offset).toarray()
H = lam * numpy.matmul(D.T, D)
w_ = numpy.ones(N)
i = 0
N_iterations = 100
while i < N_iterations:
W = spdiags(w_, 0, N, N, format='csr')
# Cholesky decomposition
C = cholesky(W + H)
z_ = spsolve(C.T, w_ * y)
z = spsolve(C, z_)
d = y - z
# make d- and get w^t with m and s
dn = d[d<0]
m = numpy.mean(dn)
s = numpy.std(dn)
wt = 1. / (1 + numpy.exp(2 * (d - (2*s-m)) / s))
# check exit condition and backup
norm_wt, norm_w = norm(w_-wt), norm(w_)
if (norm_wt / norm_w) < ratio:
break
w_ = wt
i += 1
return(z)

cv2.resize() unable to use with (y, x) matrix

I'm trying to calibrate HD Camera, resolution 1280x720.
I need it to be resized after applying undistort function to size 640x480.
So it's changing aspect ratio from 16:9 to 4:3.
In my opencv application anything works flawless besides saving (y, x) positions of calibrated camera. I need it because, binary output file is working with flashplayer application that reads this changed camera (y, x) positions and applyies it to original camera view that is being grabbed by flashplayer.
Binary file with cv2.resize():
def generate_grid(rows, cols):
"""Generate grid filed with y,x values at desired size"""
cc, rr = np.meshgrid(np.arange(cols, dtype=np.uint16), np.arange(rows, dtype=np.uint16))
return np.dstack([rr, cc])
camera_size = (1280, 720)
binary_size = (320, 240)
raw_grid = generate_grid(camera_size[1], camera_size[0])
undist_grid = cv2.undistort(raw_grid, camera_matrix, dist_coeffs)
resized_grid = cv2.resize(undist_grid, (320, 240))
transformed_grid = cv2.warpPerspective(resized_grid, transformed_matrix, binary_size)
with open(path_to_binary_file, "wb") as binary_file:
num_bytes_written = binary_file.write(transformed_grid)
print(transformed_grid.shape)
print("Wrote %d bytes." % num_bytes_written)
Output image read by flashplayer apllication:
Binary file with naive algorithm:
def generate_grid(rows, cols):
"""Generate grid filed with y,x values at desired size"""
cc, rr = np.meshgrid(np.arange(cols, dtype=np.uint16), np.arange(rows, dtype=np.uint16))
return np.dstack([rr, cc])
camera_size = (1280, 720)
binary_size = (320, 240)
raw_grid = generate_grid(camera_size[1], camera_size[0])
undist_grid = cv2.undistort(raw_grid, camera_matrix, dist_coeffs)
resized_grid = generate_grid(480, 640)
_resized_grid = generate_grid(binary_size[1], binary_size[0])
for y in range(undist_grid.shape[0]):
for x in range(undist_grid.shape[1]):
if y % 2 != 0 and x % 2 != 0:
yy = int(y / 2)
xx = int(x / 2)
resized_grid[yy, xx, :] = undist_grid[y, x] / 2
for y in range(resized_grid.shape[0]):
for x in range(resized_grid.shape[1]):
if y % 2 != 0 and x % 2 != 0:
yy = int(y / 2)
xx = int(x / 2)
_resized_grid[yy, xx, :] = resized_grid[y, x] / 2
transformed_grid = cv2.warpPerspective(_resized_grid, transformed_matrix, binary_size)
with open(path_to_binary_file, "wb") as binary_file:
num_bytes_written = binary_file.write(transformed_grid)
print(transformed_grid.shape)
print("Wrote %d bytes." % num_bytes_written)
Output image read by flashplayer application:
So in the naive case, I can see that this transformation from 1280x720 to 640x480 is wrong because normally it would be 640x360.
My questions are:
Am I using the cv2.resize() function in the wrong way?
Is there a better way to generate (y, x) points from undistorted camera view?
How can I implement resize 1280x720 to 640x480 in naive algorithm?
#Edit
My application is working okay.
I'm stuck on transferring, changed camera data to binary file.
I could not get to work numpy 2d array with cv2.resize()
but finally I could get (y, x) 720,1280 array to be "scaled" to 480x640
for y in range(undist_grid.shape[0]):
for x in range(undist_grid.shape[1]):
if y % 3 != 0 and x % 2 != 0:
yy = int((2*y) / 3)
xx = int(x / 2)
resized_grid[yy, xx, :] = (2 * undist_grid[y, x]) / 3

Python cvxpy - reuse some constraints

I'm currently using cvxpy to optimize a really big problem but now facing the current issue.
I run multiple iterations of the solver (every iteration reduces the flexibility of some variables).
Every run has 50 constraints in total, of which only 2 of them are different on every run. The remaining 48 constraints are identical.
During every iteration I rebuild from scratch those 2 constraints, the problem, and the obj function.
If I don't rebuild the remaining (same) 48 constraints, the final solution makes no sense.
I read this post CVXPY: how to efficiently solve a series of similar problems but here in my case, I don't need to change parameters and re-optimize.
I just managed to prepare an example that shows this issue:
x = cvx.Variable(3)
y = cvx.Variable(3)
tc = np.array([1.0, 1.0,1.0])
constraints2 = [x >= 2]
constraints3 = [x <= 4]
constraints4 = [y >= 0]
for i in range(2):
if i == 0:
constraints1 = [x - y >= 0]
else:
x = cvx.Variable(3)
y = cvx.Variable(3)
constraints1 = [x + y == 1,
x - y >= 1,
x - y >= 0,
x >= 0]
constraints = constraints1 + constraints2 + constraints3 + constraints4
# Form objective.
obj = cvx.Minimize( (tc.T # x ) - (tc.T # y ) )
# Form and solve problem.
prob = cvx.Problem(obj, constraints)
prob.solve()
solution_value = prob.value
solution = str(prob.status).lower()
print("\n\n** SOLUTION: {} Value: {} ".format(solution, solution_value))
print("* optimal (x + y == 1) dual variable", constraints[0].dual_value)
print("optimal (x - y >= 1) dual variable", constraints[1].dual_value)
print("x - y value:", (x - y).value)
print("x = {}".format(x.value))
print("y = {}".format(y.value))
As you can see, constraints2 requires all the values in the x vector to be greater than 2. constraints2 is added in both iterations to "constraints" that is used in the solver.
The second solution should give you values of vector x that are less than 2.
Why? How to avoid this issue?
Thank you
You need to use parameters as described in the linked post. Suppose you have the constraint rhs >= lhs which is sometimes used and other times not, where rhs and lhs have dimensions m x n. Write the following code:
param = cp.Parameter((m, n))
slack = cp.Variable((m, n))
param_constraint = [rhs >= lhs + cp.multiply(param, slack)]
Now to turn off the constraint, set param.values = np.ones((m, n)). To turn the constraint on, set param.values = np.zeros((m, n)). You can turn some entries of the constraint off/on by setting some entries of param to be 1 and others to be 0.

Total Possible Paths in a Square Grid when diagonal movement is allowed

How should I answer this-"Compute the total number of possible paths from (0,0) to (7,9) if the steps R (to the right) and U (up) are allowed, along with the diagonal step D:(x,y)→(x +1,y+ 1)"
Edit: added calculation for arbitrary cell, not only diagonal one.
The number of ways on square grid is known as Delannoy number, for (n,n) cell sequence is 1, 3, 13, 63, 321, 1683, 8989...
There is simple natural recurrence
D(m, n) = D(m-1, n) + D(m, n-1) + D(m-1,n-1)
that might be used to calculate values rather quickly for reasonable argument values (table approach, O(nm) operations including long summation).
"Closed formula"
D(m, n) = Sum[k=0..min(n, m)] {C(m + n - k, m) * C(m, k)}
for effective implementations requires table of binomial coefficients
#2D table quadratic approach
def PathsInSqGrid(n, m):
D = [[0 for x in range(m+1)] for y in range(n+1)]
for i in range(n+1):
D[i][0] = 1
for i in range(m+1):
D[0][i] = 1
for i in range(1, n+1):
for j in range(1,m+1):
D[i][j] = D[i][j-1] + D[i-1][j] + D[i-1][j-1]
return D[n][m]
def NCr(n, k):
result = 1
if k > n - k:
k = n - k
for i in range (1, k + 1):
result = (result * (n - i + 1)) // i
return result
#closed formula
def PathsCF(n, m):
#D(m, n) = Sum[k=0..min(n, m)] {C(m + n - k, m) * C(m, k)}
res = 0
for k in range(0, min(n, m) + 1):
res += NCr(m + n - k, m) *NCr(m, k)
return res
print(PathsInSqGrid(7, 9))
print(PathsCF(7, 9))
>>>
224143
224143
Wiki also shows two so-called "closed formulas" for central Delannoy numbers (while I believe that closed formula should be single expression without loop of length n):
D(n) = Sum[k=0..n]{C(n,k)*C(n+k,k)}
D(n) = Sum[k=0..n]{C(n,k)^2 * 2^n}
and recurrence (looks simple, linear complexity, but real implementation requires division of long number by short one)
n*D(n) = 3*(2*n-1) * D(n-1) - (n-1)*D(n-2)
and generating function
Sum[n=0..Inf]{D(n)*x^n} = 1/Sqrt(1 - 6 * x + x^2) = 1 + 3x + 13x^2 + 63x^3 +...
Code
#central Delannoy numbers
#2D table quadratic approach
#linear approach for diagonal cell (n,n)
def PathsInSqGridLin(n):
if n < 2:
return 2 * n + 1
A, B = 1, 3
for i in range(2, n + 1):
B, A = (3 * (2 * i - 1) * B - (i - 1) * A) // i, B
return B
print(PathsInSqGridLin(3))
print(PathsInSqGridLin(100))
>>
63
2053716830872415770228778006271971120334843128349550587141047275840274143041

Resources