From ba046c0827a47e504fee47d622f1907a4f1d8f8c Mon Sep 17 00:00:00 2001 From: Jonathan Tompson Date: Sat, 14 Mar 2015 14:08:57 -0400 Subject: [PATCH] refactored code a little. Plotted 3D derivative and checked it using FEM. Everything is now OK. --- Perlin3DDeriv.cpp | 2 +- README.md | 10 +++--- compile_mex.m | 2 +- plot_2D_scalar_and_grad.m | 16 +++++++++ plot_3D_scalar.m | 13 +++++++ plot_3D_scalar_and_grad.m | 29 +++++++++++++++ plot_scalar_and_grad.m | 17 --------- test_mex.m | 76 ++++++++++++++++++++++++++++----------- 8 files changed, 120 insertions(+), 45 deletions(-) create mode 100644 plot_2D_scalar_and_grad.m create mode 100644 plot_3D_scalar.m create mode 100644 plot_3D_scalar_and_grad.m delete mode 100644 plot_scalar_and_grad.m diff --git a/Perlin3DDeriv.cpp b/Perlin3DDeriv.cpp index 75552c1..4b3735d 100644 --- a/Perlin3DDeriv.cpp +++ b/Perlin3DDeriv.cpp @@ -76,7 +76,7 @@ void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) { // data[(m-1)+(n-1)*7+(p-1)*7*8] = 1; for (size_t i = 0; i < npts; i++) { - vec4 s_and_deriv = Perlin3DDeriv(vec2(X[i], Y[i], Z[i])); + vec4 s_and_deriv = Perlin3DDeriv(vec3(X[i], Y[i], Z[i])); out[i] = s_and_deriv.x; dout[i*3] = s_and_deriv.y; dout[i*3+1] = s_and_deriv.z; diff --git a/README.md b/README.md index 4177f71..15fd075 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -**matlabnoise - Jonathan's Matlab noise library (C++ port of GPU-Noise-Lib)** +**matlabnoise - Matlab noise library** --------- --------- ![Image of Perlin Gradient Field](grad_field.jpg) @@ -6,11 +6,11 @@ **Overview** -------- -This is just a mex wrapper around a C++ port of Brian Sharpe's GPU-Noise-Lib: https://github.com/BrianSharpe/GPU-Noise-Lib +This is just a mex wrapper around a C++ port of Brian Sharpe's GPU-Noise-Lib: https://github.com/BrianSharpe/GPU-Noise-Lib (Thanks Brian for putting together such an awesome GLSL library!). -Not everything from the above library has been ported and implemented. ```noise_common.cpp``` contains the GLSL port. +Not everything from the above library has been ported and implemented. ```noise_common.cpp``` contains the GLSL port. -Note that I did not attempt to optimize the code here in any way. In fact, because of the generous use of copy constructors I am relying quite heavily on the optimizing compiler here. Still, it's significantly faster than the same code in Matlab (even properly vectorized). +Note that I did not attempt to optimize the code here in any way. In fact, because of the generous use of copy constructors I am relying quite heavily on the optimizing compiler here. Still, it's significantly faster than the same code in Matlab (even when properly vectorized). The following functions from Brian's work have been exposed: - Perlin2D @@ -19,7 +19,7 @@ The following functions from Brian's work have been exposed: - Perlin2DDeriv - Perlin3DDeriv -Clearly, I'm just using it as a fast Perlin noise implementation in Matlab. However, given that I have already written the vec2, vec3 and vec4 classes it will be quick work porting more functions from GPU-Noise_Lib. +Clearly, I'm just using it as a fast Perlin noise implementation in Matlab. However, given that I have already written the vec2, vec3 and vec4 classes it will be quick work porting more functions from GPU-Noise-Lib. **Compilation** --------------- diff --git a/compile_mex.m b/compile_mex.m index f209830..ab45ccb 100644 --- a/compile_mex.m +++ b/compile_mex.m @@ -1,7 +1,7 @@ mex -v -largeArrayDims -I.\ Perlin4D.cpp noise_common.cpp vec2.cpp vec3.cpp vec4.cpp mex -v -largeArrayDims -I.\ Perlin3D.cpp noise_common.cpp vec2.cpp vec3.cpp vec4.cpp mex -v -largeArrayDims -I.\ Perlin2D.cpp noise_common.cpp vec2.cpp vec3.cpp vec4.cpp -% mex -v -largeArrayDims -I.\ Perlin3DDeriv.cpp noise_common.cpp vec2.cpp vec3.cpp vec4.cpp +mex -v -largeArrayDims -I.\ Perlin3DDeriv.cpp noise_common.cpp vec2.cpp vec3.cpp vec4.cpp mex -v -largeArrayDims -I.\ Perlin2DDeriv.cpp noise_common.cpp vec2.cpp vec3.cpp vec4.cpp diff --git a/plot_2D_scalar_and_grad.m b/plot_2D_scalar_and_grad.m new file mode 100644 index 0000000..cf626de --- /dev/null +++ b/plot_2D_scalar_and_grad.m @@ -0,0 +1,16 @@ +function [ ] = plot_2D_scalar_and_grad( A, gradA ) + + xdim = size(A, 2); + ydim = size(A, 1); + + Ap = A - min(A(:)); + Ap = Ap / max(Ap(:)); + d = ceil(xdim / 15); % decimation ratio + imshow(repmat(Ap,1,1,3)); hold on; + [V, U] = ndgrid(1:ydim, 1:xdim); colormap default; + contour(U, V, Ap, 'LineWidth', 1.5); + [V, U] = ndgrid(1:d:ydim, 1:d:xdim); + quiver(U, V, squeeze(gradA(1,1:d:end,1:d:end)), squeeze(gradA(2,1:d:end,1:d:end))); + +end + diff --git a/plot_3D_scalar.m b/plot_3D_scalar.m new file mode 100644 index 0000000..4ef1eac --- /dev/null +++ b/plot_3D_scalar.m @@ -0,0 +1,13 @@ +function [ ] = plot_3D_scalar( noise ) + + noise = noise - min(noise(:)); + noise = noise / max(noise(:)); + h = contourslice(noise, [], [], [1,10,20,30], 16); + set(h, 'LineWidth', 1.5); + view(3); + axis tight + + grid on; + +end + diff --git a/plot_3D_scalar_and_grad.m b/plot_3D_scalar_and_grad.m new file mode 100644 index 0000000..f4f2552 --- /dev/null +++ b/plot_3D_scalar_and_grad.m @@ -0,0 +1,29 @@ +function [ ] = plot_3D_scalar_and_grad( noise, gradNoise ) + + xdim = size(noise, 3); + ydim = size(noise, 2); + zdim = size(noise, 1); + + nslices = 4; + d = ceil(zdim / nslices); + slices = 1:d:zdim; + + noise = noise - min(noise(:)); + noise = noise / max(noise(:)); + h = contourslice(noise, slices, [], [], 16); + set(h, 'LineWidth', 1.5); + view(3); + axis tight + grid on; + hold on; + + d = ceil(xdim / 5); % decimation ratio + gradNoise = gradNoise(:, slices, 1:d:end, 1:d:end); + dX = squeeze(gradNoise(1,:,:,:)); + dY = squeeze(gradNoise(2,:,:,:)); + dZ = squeeze(gradNoise(3,:,:,:)); + [D, V, U] = ndgrid(slices, 1:d:ydim, 1:d:xdim); + quiver3(D, V, U, dX, dY, dZ); + +end + diff --git a/plot_scalar_and_grad.m b/plot_scalar_and_grad.m deleted file mode 100644 index 47137ba..0000000 --- a/plot_scalar_and_grad.m +++ /dev/null @@ -1,17 +0,0 @@ -function [ ] = plot_scalar_and_grad( A, gradA ) - - figure; - xdim = size(A, 2); - ydim = size(A, 1); - - Ap = A - min(A(:)); - Ap = Ap / max(Ap(:)); - d = ceil(xdim / 15); % decimation ratio - imshow(repmat(Ap,1,1,3)); hold on; - [U, V] = ndgrid(1:xdim, 1:ydim); colormap default; - contour(V, U, Ap, 'LineWidth', 1.5); - [U, V] = ndgrid(1:d:xdim, 1:d:ydim); - quiver(V, U, squeeze(gradA(1,1:d:end,1:d:end)), squeeze(gradA(2,1:d:end,1:d:end))); - -end - diff --git a/test_mex.m b/test_mex.m index b9915b0..70d37aa 100644 --- a/test_mex.m +++ b/test_mex.m @@ -1,7 +1,9 @@ clearvars; clc; close all; +% This script will just plot the scalar fields and check the derivatives +% using FEM (central differences). ydim = 512; -xdim = 512; +xdim = 511; frequency = 1; % 2D Grid @@ -17,18 +19,35 @@ title('Perlin2D'); % 2D Gradient -[noise, gradNoise] = Perlin2DDeriv(X, Y); -plot_scalar_and_grad(noise, gradNoise); -% title('Perlin2DDeriv'); +[noise_deriv, gradNoise] = Perlin2DDeriv(X, Y); +figure; +plot_2D_scalar_and_grad(noise_deriv, gradNoise); +title('Perlin2DDeriv'); + +err = abs(noise_deriv - noise); +assert(max(err(:)) < 1e-6, ... + 'scalar field from Perlin2DDeriv does not match Perlin2D!'); % addpath('C:\Users\IggyMenou\Documents\NYU\detection_nets\matlab\export_fig'); % export_fig('grad_field.jpg', gcf, '-jpg', '-a4'); +% Check the gradient using finite differences (central) +epsilon = 1e-6; +[pos, posGrad] = Perlin2DDeriv(X + epsilon, Y); +[neg, negGrad] = Perlin2DDeriv(X - epsilon, Y); +dx = (pos - neg) / (2 * epsilon); +[pos, posGrad] = Perlin2DDeriv(X, Y + epsilon); +[neg, negGrad] = Perlin2DDeriv(X, Y - epsilon); +dy = (pos - neg) / (2 * epsilon); +gradNoiseFEM = permute(cat(3, dx, dy), [3 1 2]); +err = abs(gradNoiseFEM - gradNoise); +assert(max(err(:)) < 1e-8, 'FEM derivative does not match!'); + % 3D Grid +xdim = 63; ydim = 64; -xdim = 64; -zdim = 64; -[V, U, D] = ndgrid(1:ydim, 1:xdim, 1:zdim); +zdim = 65; +[D, V, U] = ndgrid(1:zdim, 1:ydim, 1:xdim); X = 2 * (U - 1) ./ (xdim - 1) - 1; % [-1, 1] Y = 2 * (V - 1) ./ (ydim - 1) - 1; % [-1, 1] Z = 2 * (D - 1) ./ (zdim - 1) - 1; % [-1, 1] @@ -37,14 +56,35 @@ Z = Z * frequency; figure; noise = Perlin3D(X, Y, Z); -noise = noise - min(noise(:)); -noise = noise / max(noise(:)); -h = contourslice(noise, [], [], [1,10,20,30], 16); -set(h, 'LineWidth', 1.5); -view(3); -axis tight +plot_3D_scalar(noise); title('Perlin3D'); -grid on; + +% 3D Gradient +[noise_deriv, gradNoise] = Perlin3DDeriv(X, Y, Z); +err = abs(noise_deriv - noise); +assert(max(err(:)) < 1e-6, ... + 'scalar field from Perlin3DDeriv does not match Perlin3D!'); + +figure; +plot_3D_scalar_and_grad(noise_deriv, gradNoise); +title('Perlin3DDeriv'); +set(gcf,'Renderer','OpenGL') +title('Perlin3DDeriv'); + +% Check the gradient using finite differences (central) +epsilon = 1e-6; +[pos, posGrad] = Perlin3DDeriv(X + epsilon, Y, Z); +[neg, negGrad] = Perlin3DDeriv(X - epsilon, Y, Z); +dx = (pos - neg) / (2 * epsilon); +[pos, posGrad] = Perlin3DDeriv(X, Y + epsilon, Z); +[neg, negGrad] = Perlin3DDeriv(X, Y - epsilon, Z); +dy = (pos - neg) / (2 * epsilon); +[pos, posGrad] = Perlin3DDeriv(X, Y, Z + epsilon); +[neg, negGrad] = Perlin3DDeriv(X, Y, Z - epsilon); +dz = (pos - neg) / (2 * epsilon); +gradNoiseFEM = permute(cat(4, dx, dy, dz), [4 1 2 3]); +err = abs(gradNoiseFEM - gradNoise); +assert(max(err(:)) < 1e-8, 'FEM derivative does not match!'); % Now just do a time series of a 3D grid (because I can't visualize 4D % noise :-) ) @@ -53,12 +93,6 @@ subplot(2,2,i); W = ones(size(X,1), size(X,2), size(X,3)) * i; noise = Perlin4D(X, Y, Z, W); - noise = noise - min(noise(:)); - noise = noise / max(noise(:)); - h = contourslice(noise, [], [], [1,10,20,30], 16); - set(h, 'LineWidth', 1.5); - view(3); - axis tight + plot_3D_scalar(noise); title(['Perlin4D w=', num2str(i)]); - grid on; end