Project 4
Neural Radiance Fields (NeRF)
Neural Radiance Fields (NeRF)
This project implements a Neural Radiance Field (NeRF) to represent and render 3D scenes from multi-view 2D images. Starting with camera calibration and 2D neural fields, we progressively build up to full 3D volumetric rendering.
Part 0: Camera Calibration and 3D Scanning
0.1: Camera Calibration
Camera calibration recovers the intrinsic parameters (focal length, principal point) and distortion coefficients of the camera using ArUco markers.
Calibration Process
- Capture Calibration Images: 30-50 images of ArUco calibration tags from various angles and distances
- Detect ArUco Markers: Use OpenCV’s ArUco detector to find tag corners in each image
- Collect Correspondences: Match 2D image points to 3D world coordinates
- Compute Intrinsics: Use
cv2.calibrateCamera()to recover camera matrix and distortion coefficients
Calibration Results
Camera Intrinsics:
\[K = \begin{bmatrix} f_x & 0 & c_x \\ 0 & f_y & c_y \\ 0 & 0 & 1 \end{bmatrix}\]0.2: 3D Object Scanning
Captured 30-50 images of a chosen object with a single ArUco tag for pose estimation.
0.3: Camera Pose Estimation
Using the calibrated intrinsics, we estimate camera pose for each image using Perspective-n-Point (PnP).
PnP Algorithm
Given 3D-2D correspondences and camera intrinsics, cv2.solvePnP() recovers the rotation and translation. The algorithm converts the axis-angle rotation vector to a rotation matrix using cv2.Rodrigues(), then constructs the camera-to-world transformation matrix by inverting the world-to-camera transformation.
0.4: Image Undistortion and Dataset Creation
Removed lens distortion using cv2.undistort() and packaged data for NeRF training. Used cv2.getOptimalNewCameraMatrix() to handle black boundaries from undistortion, cropping images to the valid region of interest and updating the principal point accordingly. The final dataset is saved in .npz format containing training/validation images, camera poses, and focal length.
Part 1: 2D Neural Field
Before tackling 3D NeRF, we start with a simpler 2D case: representing an image as a continuous neural field (f(x, y) \rightarrow (r, g, b)).
1.1: Network Architecture
Multilayer Perceptron (MLP) with Positional Encoding:
- Input: 2D pixel coordinates ((x, y)) normalized to ([0, 1])
- Positional Encoding: Expands input dimensionality using sinusoidal functions
- Output: RGB color values in ([0, 1])
where (L) is the maximum frequency level.
Network Structure:
The network uses three fully connected layers with ReLU activations, followed by a Sigmoid activation to constrain outputs to valid color range [0, 1].
1.2: Training Process
Hyperparameters:
- Learning Rate: 1e-2
- Optimizer: Adam
- Batch Size: 10,000 pixels per iteration
- Iterations: 1000-3000
- Loss: MSE between predicted and ground truth colors
PSNR Metric:
\[\text{PSNR} = -10 \cdot \log_{10}(\text{MSE})\]1.3: Training Results
PSNR improves steadily as the network learns the image
Training Results on Another Image (Dog):
1.4: Hyperparameter Analysis
Effect of Positional Encoding Frequency (L):
Different L values affect the network's ability to capture high-frequency details. Lower L values produce blurry results, while higher L values enable sharp reconstruction.
Effect of Network Width:
Network capacity affects reconstruction quality. Narrow networks struggle with complex patterns, while wider networks provide better representation capacity.
Part 2: 3D Neural Radiance Field
2.1: Ray Generation
Coordinate Transformations
Camera to World Transformation:
The transformation between camera space and world space is:
\[\begin{bmatrix} \mathbf{x}_w \\ 1 \end{bmatrix} = \begin{bmatrix} R & \mathbf{t} \\ \mathbf{0}^T & 1 \end{bmatrix} \begin{bmatrix} \mathbf{x}_c \\ 1 \end{bmatrix}\]where the camera-to-world (c2w) transformation matrix is a 4×4 matrix containing rotation (R) (3×3) and translation (\mathbf{t}) (3×1).
Pixel to Camera Coordinates:
Given intrinsic matrix (K) and pixel coordinates ((u, v)):
\[\mathbf{x}_c = s \cdot K^{-1} \begin{bmatrix} u \\ v \\ 1 \end{bmatrix}\]Ray Generation:
For each pixel, we compute:
- Ray Origin: \(\mathbf{o} = \mathbf{t}\) (camera position in world space)
- Ray Direction: \(\mathbf{d} = \frac{R \cdot K^{-1} [u, v, 1]^T}{|R \cdot K^{-1} [u, v, 1]^T|}\)
The implementation adds 0.5 to pixel coordinates to account for pixel centers, transforms from pixel to camera coordinates using the inverse intrinsic matrix, then to world coordinates using the camera-to-world transformation.
2.2: Sampling Strategy
Sampling Rays: Randomly sample 10,000 rays per iteration from all training images.
Sampling Points Along Rays:
For each ray, sample (N) points between near and far planes:
\[\mathbf{p}_i = \mathbf{o} + t_i \mathbf{d}, \quad t_i \in [t_{\text{near}}, t_{\text{far}}]\]Stratified sampling with random perturbation during training ensures every location along the ray is explored, preventing overfitting to discrete sample locations.
2.3: Rays and Samples Visualization
100 rays sampled from training cameras
Points sampled along rays for volume rendering
2.4: NeRF Network Architecture
Input:
- 3D position (\mathbf{x}) with positional encoding ((L=10))
- View direction (\mathbf{d}) with positional encoding ((L=4))
Output:
- Density (\sigma \geq 0) (ReLU activation)
- RGB color (\mathbf{c} \in [0, 1]^3) (Sigmoid activation)
Network Structure:
The network processes position encoding through multiple layers with a skip connection to inject input features in the middle. The density branch uses ReLU activation for non-negative values, while the color branch conditions on view direction and uses Sigmoid for valid RGB range.
2.5: Volume Rendering
Continuous Volume Rendering Equation:
\[C(\mathbf{r}) = \int_{t_n}^{t_f} T(t) \sigma(\mathbf{r}(t)) \mathbf{c}(\mathbf{r}(t), \mathbf{d}) dt\]where transmittance (T(t) = \exp\left(-\int_{t_n}^{t} \sigma(\mathbf{r}(s)) ds\right))
Discrete Approximation:
\[\hat{C}(\mathbf{r}) = \sum_{i=1}^{N} T_i (1 - \exp(-\sigma_i \delta_i)) \mathbf{c}_i\]where:
- \(T_i = \exp\left(-\sum_{j=1}^{i-1} \sigma_j \delta_j\right)\) is the accumulated transmittance
- \(\delta_i = t_{i+1} - t_i\) is the distance between samples
- \(\alpha_i = 1 - \exp(-\sigma_i \delta_i)\) is the opacity
The implementation computes opacity from density, calculates accumulated transmittance using cumulative product, and performs weighted sum of colors to produce the final rendered pixel color.
2.5: Training Results on Lego Dataset
Training Configuration:
- Dataset: Lego (200×200 resolution, 100 training views)
- Batch Size: 10,000 rays per iteration
- Learning Rate: 5e-4 (Adam optimizer)
- Iterations: 1000-5000
- Near/Far Planes: 2.0 / 6.0
- Samples per Ray: 64
Training Loss Curve
Steady convergence with adjusted hyperparameters
Novel View Synthesis
Spherical camera trajectory around the lego bulldozer
Part 2.6: Custom Object NeRF
Dataset: Custom Captured Object
Trained NeRF on a custom object captured using the calibration pipeline from Part 0.
Dataset Statistics:
- Training Images: 35
- Validation Images: 5
- Image Resolution: 400×300 (resized from original)
- Near/Far Planes: 0.02 / 0.5 (adjusted for object scale)
- Samples per Ray: 64
Training Modifications:
- Adjusted near/far bounds based on object distance
- Increased samples per ray from 32 to 64 for better quality
- Resized images to manage training time
- Updated intrinsics matrix after resizing
Training Loss Curve
Steady convergence with adjusted hyperparameters
Novel View Synthesis
Camera circling around the captured object
Circular Camera Path Generation:
The novel view synthesis uses a circular camera trajectory that looks at the object origin. For each angle, a camera-to-world matrix is computed using the look-at transformation, then rotated around the object. The trained NeRF model renders each view by sampling rays through the scene and applying volume rendering to produce the final images.
Key Learnings
Practical Considerations
-
Near/Far Bounds: Must be carefully tuned for each scene. Too wide wastes samples in empty space; too narrow clips the object.
-
Image Resolution: Higher resolution requires more memory and training time. Resizing images is often necessary for practical training.
-
Training Time: NeRF training is computationally intensive. GPU acceleration is essential, and training can take hours even for small scenes.
-
View-Dependent Effects: The direction-conditioned color prediction allows NeRF to model specular reflections and view-dependent appearance.