ShapeFromShading.jl

The following documents the functionality currently available in ShapeFormShading.jl.

Synthetic data generation:

ShapeFromShading.generate_surfaceMethod
img = generate_surface(shape::AbstractSyntheticShape, albedo::Real = 1.0, illumination_direction::Vector{T} where T <: Real = [0.0, 0.0, 1.0]; img_size::Int=151, noise::Real = 0, relative_noise::Bool = true)

Creates a synthetic image of a given shape with the given illumination properties.

Output

Returns a grayscale image with dimensions img_size in both axes.

Details

Uses the passed properties to calculate the partial differentials of the shape reflectance and uses these to generate a synthetic image of the shape to be used as a consistent test image for SFS testing.

Arguments

The function arguments are described in more detail below.

shape

A AbstractSyntheticShape that specifies the type of shape to be generated. Available shapes are; SynthSphere, Prism, Ripple, Ripple2, Cake, Cake2, SynthVase, Tent, Dem, SynthGaussian and SuperGaussian. Each has a parameter radius, a Real that specifies the radius of the desired shape or its scale within the image. This can acts as either an actual radius (such as for SynthSphere) or rather as a scale factor (such as for Ripple). If left unspecified a default value is used for each.

albedo

A Real that specifies the albedo (amount of light reflected) of the image. If left unspecified a default value of 1.0 is used.

illumination_direction

A Vector{T} where T <: Real that specifies the tilt value to be used by the algorithm. The illumination_direction should be a vector of the form [x,y,z] where x,y,z are in the range [0,1]. If left unspecified a default value of [0.0,0.0,1.0] is used.

img_size

A Int that specifies the dimentions of the final image should be. Defults to 151.

noise

A Real that specifies the standard deviation of noise to be added to the image. Defults to 0.0.

relative_noise

A Bool that specifies if the noise added to the image should be relative to the gradient of the object displayed in the image at that point.

Example

Compute the heightmap for a synthetic image generated by generate_surface.

using Images, ShapeFromShading

#generate synthetic image
img = generate_surface(SynthSphere(50), 1.0, [0.5,0.1,0.7])

Reference

  1. S. Elhabian, "Hands on Shape from Shading", Computer Vision and Image Processing, 2008.
source

Normal Integration:

ShapeFromShading.AbstractIntegratorType
(scheme::AbstractIntegrator)(p::AbstractArray, q::AbstractArray)

Integrates the gradient field defined by p and q using various methods as described in the following sections.

Output

Returns a heightmap Z which defines the reconstructed surface.

Details

Using the method defined in each type of integrator, each algorithm is run using parameters passed to the struct when creating the integrator. Results can vary wildly depending on the chosen integrator. Available methods are; Frankot, Path, SplitPath, Horn, Durou, Quadratic, TotalVariation, NonConvex1, NonConvex2, AnisotropicDiffusion and MumfordShah. See individual documentation for each method for more detials.

Parameters

Each algorithm has its own set of parameters which are discussed in each the section for each method.

Arguments

p and q:

AbstractArray's which define the gradient field to be integrated.

Example

The following example demonstrates the use of the Frankot integrator.

using ShapeFromShading, Makie

# Generate synthetic gradients
p, q = synthetic_gradient(SynthSphere(38), img_size = 151)

# Create a Frankot() integrator
frankot = Frankot()

# Calculate the heightmap from the gradients
Z = frankot(p, q)

# Normalize to maximum of 1 (not necessary but makes displaying easier)
Z = Z./maximum(Z)

# Display using Makie (Note: Makie can often take several minutes first time)
r = 0.0:0.1:4
surface(r, r, Z)
source
ShapeFromShading.FrankotMethod
Frankot()

Defines the Frankot integrator which contians the Frankot-Chellappa method of integration. The Frankot-Chellappa method is a fast, reliable method for integration surface normals while enforcing integrability using Fourier methods.

Output

Frankot() returns a Frankot integrator which can then be called to run the Frankot-Chellappa method on a gradient field.

Details

Frankot-Chellappa method uses Fourier methods to attempt to solve the Poission equation $\nabla^2z = \partial_up + \partial_vq$. By taking the Fourier transform of both sides we get:

\[−(\omega^2_u + \omega^2_v)\hat{z}(\omega_u, \omega_v) = \imath \omega_u\hat{p} (\omega_u, \omega_v) + \imath \omega_v\hat{q}(\omega_u, \omega_v)\]

By rearranging the above equation we arrive at an equation for $\hat{z}$;

\[\hat{z}(\omega_u, \omega_v) = \frac{\omega_u\hat{p}(\omega_u, \omega_v) + \omega_v\hat{q}(\omega_u, \omega_v)}{\imath(\omega^2_u + \omega^2_v)}\]

From which the final surface can be found by taking the inverse Fourier transform of $\hat{z}$.

Due to the way $(\omega_u, \omega_v)$ is defined the algorithm works best when the input dimensions are odd in length. To accommodate this the integrator will pad the edge of the inputs if they are even before running the algorithm. This padding will be removed before returning a value hence output size will be unaffected.

Parameters

Frankot integrator take no parameters.

Example

The following example demonstrates the use of the Frankot integrator.

using ShapeFromShading, Makie

# Generate synthetic gradients
p, q = synthetic_gradient(SynthSphere(38), img_size = 151)

# Create a Frankot() integrator
frankot = Frankot()

# Calculate the heightmap from the gradients
Z = frankot(p, q)

# Normalize to maximum of 1 (not necessary but makes displaying easier)
Z = Z./maximum(Z)

# Display using Makie (Note: Makie can often take several minutes first time)
r = 0.0:0.1:4
surface(r, r, Z)

Reference

[1] R. T. Frankot and R. Chellappa, "A method for enforcing integrability in shape from shading algorithms," in IEEE Transactions on Pattern Analysis and Machine Intelligence, vol. 10, no. 4, pp. 439-451, July 1988. doi: 10.1109/34.3909

source
ShapeFromShading.PathMethod
Path()

Creates a Path() integrator which utilises the average of two path integrals along varying paths. Each path integral reconstructs the surface with accumulating error along the path, hence averaging two different paths can minimise this error, although the method still suffers if the gradient field is not integrable at some points.

Output

Path() returns a Path integrator which can then be called to integrate a gradient field.

Details

Under the assumption that the surface normals are approximately integrable everywhere ($\frac{\partial p}{\partial y}\approx\frac{\partial q}{\partial x}$), then surface can be reconstructed using the path integral defined as:

\[z(x,y)=\oint_c\left(\frac{\partial z}{\partial x},\frac{\partial z}{\partial y}\right)\cdot dl\]

Which can be broken into two integrals representing the value at each point on the surface as shown below for a path which integrates along the first column then along the row.

\[z(u,v)=\int_0^v\frac{\partial z}{\partial y}(0,y)dy + \int_0^u\frac{\partial z}{\partial x}(x,v)dx\]

The second path used in the algorithm is simply the transpose of the first, integrating along the first row then down the column represented mathematically as:

\[z(u,v)=\int_0^u\frac{\partial z}{\partial x}(x,0)dx + \int_0^v\frac{\partial z}{\partial y}(u,y)dy\]

The algorithm can be written, then discretised as shown below:

\[\begin{gathered} z(u,v)=\frac{1}{2}\left(\int_0^v\frac{\partial z}{\partial y}(0,y)dy + \int_0^u\frac{\partial z}{\partial x}(x,v)dx + \int_0^u\frac{\partial z}{\partial x}(x,0)dx + \int_0^v\frac{\partial z}{\partial y}(u,y)dy\right)\\ z(u,v)=\frac{1}{2}\left(\sum_{i=0}^vq(0,i) + \sum_{j=0}^up(j,v) + \sum_{j=0}^up(j,0) + \sum_{i=0}^vq(u,i)\right)\\ z(u,v)=\frac{1}{2}\left(\sum_{i=0}^v(q(0,i) + q(u,i)) + \sum_{j=0}^u(p(j,0) + p(j,v))\right)\\ \end{gathered}\]

It is important to note as mentioned above if there are non-integrable points in the normal field then artefacts can appear in the reconstruction. This is seen in the example below where the otherwise smooth sphere appears "spiky". This can be corrected post reconstruction by smoothing but ideally a different integrator should be used.

Parameters

Path integrator take no parameters.

Example

The following example demonstrates the use of the Path integrator.

using ShapeFromShading, Makie

# Generate synthetic gradients
p, q = synthetic_gradient(SynthSphere(38), img_size = 151)

# Create a Path() integrator
path = Path()

# Calculate the heightmap from the gradients
Z = path(p, q)

# Normalize to maximum of 1 (not necessary but makes displaying easier)
Z = Z./maximum(Z)

# Display using Makie (Note: Makie can often take several minutes first time)
r = 0.0:0.1:4
surface(r, r, Z)

Reference

[1] D. Forsyth and J. Ponce, Computer vision: a modern approach. Upper Saddle River, N.J: Prentice Hall, 2003, pp. 84-86.

source
ShapeFromShading.SplitPathMethod
SplitPath()

Creates a SplitPath() integrator which utilizes the average of two path integrals along varying paths averaging the value at each step. Each path integral reconstructs the surface with accumlating error along the path, hence averaging two different paths at each step reduces the global error at the cost of local error, although the method still suffers if the gradient field is not integrable at some points it does less so the Path() from which it extends.

Output

SplitPath() returns a SplitPath integrator which can then be called to integrate a gradient field.

Details

Under the assumption that the surface normals are approximately integrable everywhere ($\frac{\partial p}{\partial y}\approx\frac{\partial q}{\partial x}$), then surface can be reconstructed using the path integral defined as:

\[z(x,y)=\oint_c\left(\frac{\partial z}{\partial x},\frac{\partial z}{\partial y}\right)\cdot dl\]

By expanding on this principle and the discreate summation from Path() we can arrive at the discreate expresion for the value at each point, assuming all values prior to that point have been calculated, as follows:

\[z_{u,v} = \frac{1}{2}(z_{u-1,v}+p_{u-1,v}+z_{u,v-1}+q_{u,v-1})\]

As with other similar methods (see Horn()) care must be taken with regards to boundaries which can be calculated, to a constant value $z(0,0)$ which is assumed to be the zero point, using:

\[\begin{gathered} z_{u,0} = z_{u-1,0}+p_{u-1,0}\\ z_{0,v} = z_{0,v-1}+q_{0,v-1} \end{gathered}\]

It is important to note as mentioned above if there are non-integrable points in the normal field then artefacts can appear in the reconstruction. These errors gradually average out but will lead to "streaks" appearing in the reconstruction. This is seen in the example below where the otherwise smooth sphere appears has ripple like structures pointing toward to top right corner. This can be corrected post reconstruction by smoothing but ideally a different integrator should be used. It is also interesting to note the parallels between this method and the Horn and Brooks method, with this method being effectively the forward component of Horn's method. As such this algorithm provided a useful middle ground between direct integration algorithms and iterative algorithms such as the Horn and Brooks method.

Parameters

SplitPath integrator take no parameters.

Example

The following example demonstrates the use of the SplitPath integrator.

using ShapeFromShading, Makie

# Generate synthetic gradients
p, q = synthetic_gradient(SynthSphere(38), img_size = 151)

# Create a Path() integrator
splitPath = SplitPath()

# Calculate the heightmap from the gradients
Z = splitPath(p, q)

# Normalize to maximum of 1 (not necessary but makes displaying easier)
Z = Z./maximum(Z)

# Display using Makie (Note: Makie can often take several minutes first time)
r = 0.0:0.1:4
surface(r, r, Z)

Reference

[1] D. Forsyth and J. Ponce, Computer vision: a modern approach. Upper Saddle River, N.J: Prentice Hall, 2003, pp. 84-86. [2] B. Horn and M. Brooks, "The variational approach to shape from shading", Computer Vision, Graphics, and Image Processing, vol. 33, no. 2, pp. 174-208, 1986. doi: 10.1016/0734-189x(86)90114-3

source
ShapeFromShading.HornMethod
Horn(ϵ::Real = 1.0, max_iter::Real = 10000)

Implements the Horn and Brook's method of integrating surface normals. This algorithm offers an iterative solution to the Poisson equation describing the surface providing good reconstructions under most conditions.

Output

Horn() returns a Horn integrator which can then be called to integrate a gradient field.

Details

The Horn and Brook's method attempts to solve the Poisson equation $\nabla^2z = \partial_up + \partial_vq$ by the discretization given below:

\[z_{u+1,v}+z_{u,v+1}+z_{u-1,v}+z_{u,v-1}-4z_{u,v}=\frac{p_{u+1,v}-p_{u-1,v}}{2}+\frac{q_{u,v+1}-q_{u,v-1}}{2}\]

Which can be rearranged to give the iterative scheme provided by:

\[z_{u,v}^{k+1}= \frac{z_{u+1,v}^k + z_{u,v+1}^k + z_{u-1,v}^k + z_{u,v-1}^k}{4} - \frac{p_{u+1,v}-p_{u-1,v}}{8} - \frac{q_{u,v+1}-q_{u,v-1}}{8}\]

This scheme will always converge to a solution however the rate of convergence may depend upon the initial solution. This implementation will initilize with a zero solution. Neumann boundary conditions are imposed at the edges where the scheme would otherwise go out of bounds.

Parameters

The function parameters are described in more detail below.

Max_iter:

An Int which controls the number of iterations the algorithm will run for.

ϵ:

A Real representing the distance between pixels. This will Control how tall the final reconstruction is relative the array grid.

Example

The following example demonstrates the use of the Horn integrator.

using ShapeFromShading, Makie

# Generate synthetic gradients
p, q = synthetic_gradient(SynthSphere(38), img_size = 151)

# Create a Horn() integrator
horn = Horn(ϵ = 0.03, max_iter = 10000)

# Calculate the heightmap from the gradients
Z = horn(p, q)

# Display using Makie (Note: Makie can often take several minutes first time)
r = 0.0:0.1:4
surface(r, r, Z)

Reference

[1] B. Horn and M. Brooks, "The variational approach to shape from shading", Computer Vision, Graphics, and Image Processing, vol. 33, no. 2, pp. 174-208, 1986. doi: 10.1016/0734-189x(86)90114-3

source
ShapeFromShading.DurouMethod
Durou(ϵ::Real = 1.0, max_iter::Real = 1000)

Implements the Durou and Courteille method of integrating surface normals. This algorithm offers an iterative solution to the Poisson equation describing the surface extending Horn and Brook's method by improving the boundary approximation and providing good reconstructions under most conditions.

Output

Durou() returns a Durou integrator which can then be called to integrate a gradient field.

Details

TheDurou and Courteille's method attempts to solve the Poisson equation $\nabla^2z = \partial_up + \partial_vq$ by the discretization given below:

\[z_{u+1,v}+z_{u,v+1}-2z_{u,v}=\frac{p_{u+1,v}+p_{u,v}}{2}+\frac{q_{u,v+1}+q_{u,v}}{2}\]

Which can be rearranged to give the iterative scheme provided by:

\[z_{u,v}^{k+1}= \frac{z_{u+1,v}^k + z_{u,v+1}^k}{2} - \frac{p_{u+1,v}+p_{u,v}}{4} - \frac{q_{u,v+1}+q_{u,v}}{4}\]

This scheme will always converge to a solution however the rate of convergence may depend upon the initial solution. This implementation will initialize with a zero solution. Natural boundary conditions are imposed at the edges using the condition $\partial_uz-p+\partial_v-q=0$. Although faster then the Horn and Brook's method and better at handling boundaries, it can generate a worse solution under some conditions.

Parameters

The function parameters are described in more detail below.

Max_iter:

An Int which controls the number of iterations the algorithm will run for. the range [0,1].

ϵ:

A Real representing the distance between pixels. This will Control how tall the final reconstruction is relative the array grid.

Example

The following example demonstrates the use of the Durou integrator.

using ShapeFromShading, Makie

# Generate synthetic gradients
p, q = synthetic_gradient(SynthSphere(38), img_size = 151)

# Create a Durou() integrator
durou = Durou(ϵ = 0.03, max_iter = 10000)

# Calculate the heightmap from the gradients
Z = durou(p, q)

# Display using Makie (Note: Makie can often take several minutes first time)
r = 0.0:0.1:4
surface(r, r, Z)

Reference

[1] Y. Quéau, J. Durou and J. Aujol, "Normal Integration: A Survey", Journal of Mathematical Imaging and Vision, vol. 60, no. 4, pp. 576-593, 2017. doi: 10.1007/s10851-017-0773-x

source
ShapeFromShading.QuadraticMethod
Quadratic(z::AbstractArray, λ::AbstractArray = fill(10.0^-6, size(z)), mask::AbstractArray = fill(1.0, size(z)))

Implements the quadratic variational least squared method proposed by Aujol, Durou and Quéau. The algorithm solves the least squares system generated from minimizing a fidelity term $\mathcal{F}(z) = \iint_{(u,v)\in \Omega}\Phi(||\nabla z(u,v) - g(u,v)||)dudv$ and a regularization term $\mathcal{R}(z) = \iint_{(u,v)\in \Omega}\lambda \left[z(u,v)-z^0(u,v)\right]^2dudv$ where $\Phi(s) = s^2$. This method is able to quickly produce good solutions on a smooth surface and can easily handle non-rectangular domains or sub-divided domains and can provide a good starting solution for other algorithms.

Output

Quadratic() returns a Quadratic integrator which can then be called to run the quadratic method on a gradient field.

Details

As mentioned above the quadratic variational least squared method aims to minimize a fidelity term $\mathcal{F}(z) = \iint_{(u,v)\in \Omega}\Phi(||\nabla z(u,v) - g(u,v)||)dudv$ and a regulization term $\mathcal{R}(z) = \iint_{(u,v)\in \Omega}\lambda \left[z(u,v)-z^0(u,v)\right]^2dudv$ where $\Phi(s) = s^2$.

This leads to the minimization problem given by:

\[min\iint_{(u,v)\in \Omega}||\nabla z(u,v)-\mathbf{g}(u,v)||^2+\lambda(u,v)\left[z(u,v)-z^0(u,v)\right]^2dudv\]

By discretizing the problem we can arrive at the functional:

\[\begin{aligned} E(z) = \frac{1}{2}\Big(\sum_{(u,v)\in \Omega_u^+}[\partial_u^+z_{u,v}-p_{u,v}]^2 \\+ \sum_{(u,v)\in \Omega_u^-}[\partial_u^-z_{u,v}-p_{u,v}]^2 \\+ \sum_{(u,v)\in \Omega_v^+}[\partial_v^+z_{u,v}-q_{u,v}]^2 \\+ \sum_{(u,v)\in \Omega_v^-}[\partial_v^-z_{u,v}-q_{u,v}]^2\Big) \\+ \sum_{(u,v)\in \Omega}[z_{u,v}-z^0_{u,v}]^2 \end{aligned}\]

where $\Omega_u^+$ represents the domain where $(u,v)\in\{(u,v)\in\Omega|(u,v)(u+1,v)\in\Omega\}$ etc.. Using this definition the discrete differences can be converted into matrix form, where $p_{u,v}=z_{u+1,v}-z_{u,v}$ is the forward difference in the u direction etc. This data is then stacked into three vectors; $\mathbf{z},\mathbf{p},\mathbf{q}\in\R^{|\Omega|}$. Thus the matrix reresenting each of these is defined as below where m(i) is the mapping of the ith element of this vector to its corresponding point in $(u,v)$ and $D_u^+$ is a $|\Omega|\times|\Omega|$ matrix.

\[D_u^+[i,j]=\begin{cases} 0 &\text{if } m(i)\notin\Omega_u^+ \text{or } j \ne i \text{or } j \ne i+1\\ -1 &\text{if } j = i \text {and } m(i)\in\Omega_u^+\\ 1 &\text{if } j = i+1 \text {and } m(i)\in\Omega_u^+ \end{cases}\]

For a 2X2 domain this looks like:

\[D_u^+ = \begin{bmatrix} -1 & 1 & 0 & 0 \\ 0 & 0 & 0 & 0 \\ 0 & 0 & -1 & 1 \\ 0 & 0 & 0 & 0 \end{bmatrix}\]

The other three discrete differences matrices are similarly defined from there definitions to be $D_u^-$, $D_v^+$ and $D_v^-$. These can then be used to redefine the minimization problem to be in the form:

\[E(\mathbf{z})=\frac{1}{2}\left(||D_u^+\mathbf{z}-\mathbf{p}||^2+||D_u^-\mathbf{z}-\mathbf{p}||^2+||D_v^+\mathbf{z}-\mathbf{q}||^2+||D_v^-\mathbf{z}-\mathbf{q}||^2\right)+||\Lambda(\mathbf{z}-\mathbf{z}^0)||^2\]

Where $\Lambda$ is the $|\Omega|\times|\Omega|$ diagonal matrix containing the values of $\sqrt{\lambda_{u,v}}$. Using the above definitions the negative Laplacian matrix can then be defined as:

\[L=\frac{1}{2}[D_u^{+\top}D_u^++D_u^{-\top}D_u^-+D_v^{+\top}D_v^++D_v^{-\top}D_v^-]\]

Finally the least minimization problem can be represented in the form of a least squares problem of the form $A\mathbf{z}=\mathbf{b}$ where:

\[\begin{gathered} A=L+\Lambda^2\\ \mathbf{b}=\frac{1}{2}\left[D_u^{+\top}+D_u^{-\top}\right]\mathbf{p}+\frac{1}{2}\left[D_v^{+\top}+D_v^{-\top}\right]\mathbf{q}+\Lambda^2\mathbf{z}^0\\ =D_u\mathbf{p}+D_v\mathbf{q}+\Lambda^2\mathbf{z}^0 \end{gathered}\]

This system is then solved using a standard conjugate gradient algorithm where the initialization has only a slight impact on the runtime and no impact on the final solution. The algorithm provides good results on smooth surfaces but struggles in the presence of discontinuities.

Parameters

z:

An AbstractArray which defines the value of $z^0$ the initial solution and prior to be used in the regularization term. Must be provided.

λ:

An AbstractArray the same size as z, defaulting to $10.0^{-6}$ everywhere. This defines theregulization weight at each point. Large values will force the algorithm to keep the solution near to $z^0$ at that position. Can be used to keep the solution near the initial solution or guide the solution to a certain known value at points (i.e. known maxima and minima). This value should be set uniformly small otherwise.

mask:

An AbstractArray the same size as z, which guides the algorithm as to where the valid domain is. Values of 1 will be in the domain $\Omega$ while other values will be ignored and set to $z^0$. This can be used to integrate over sub-domain or to segment the domain into parts. The gen_mask() funtion can be used to generate a mask which will remove non-integrable regions dramatically improving the solution under most condition at the cost of not integrating the entire solution.

Example

The following example demonstrates the use of the Quadratic integrator.

using ShapeFromShading, Makie

# Generate synthetic gradients
p, q = synthetic_gradient(Prism(75), img_size = 151)

# Create a Quadratic() integrator
quadratic = Quadratic(z=zeros(size(p)))
quadraticMasked = Quadratic(z=zeros(size(p)), mask=gen_mask(p,q,1.0)[:,:,1])

# Calculate the heightmap from the gradients
Z = quadratic(p, q)
Z2 = quadraticMasked(p, q)

# Normalize to maximum of 1 (not necessary but makes displaying easier)
Z = Z./maximum(Z)
Z2 = Z2./maximum(Z2)

# Display using Makie (Note: Makie can often take several minutes first time)
r = 0.0:0.1:4
vbox(surface(r, r, Z), surface(r, r, Z2))

Reference

[1] Y. Quéau, J. Durou and J. Aujol, "Variational Methods for Normal Integration", Journal of Mathematical Imaging and Vision, vol. 60, no. 4, pp. 609-632, 2017. doi: 10.1007/s10851-017-0777-6

source
ShapeFromShading.TotalVariationMethod
TotalVariation(z::AbstractArray, α::Real = 1.0, λ::AbstractArray = fill(10.0^-6, size(z)), mask::AbstractArray = fill(1.0, size(z)), max_iter::Int = 100)

Implements the total variational method proposed by Aujol, Durou and Quéau. The algorithm solves the same minimization problem as th Quadraticmethod exept the fidelity terms function $\Phi(s)=||s||_{L_1}$. This method is able to produce good solutions on a smooth and piecewise smooth surface and can easily handle non-rectangular domains or sub-divided domains.

Output

TotalVariation() returns a TotalVariation integrator which can then be called to run the quadratic method on a gradient field.

Details

As discussed above this algorithm used the same fidelity and regularization terms as the Quadratic method exept the $\Phi(s)=s^2$ term is replaced with $\Phi(s)=||s||_{L_1}$. This leads to the minimization problem defined by:

\[min\iint_{(u,v)\in \Omega}||\nabla z(u,v)-\mathbf{g}(u,v)||+\lambda(u,v)\left[z(u,v)-z^0(u,v)\right]^2dudv\]

By considering the four posible discreatisations of $∇z(u,v)$ we can generate four posible domains to consider given by; $\Omega^{UV}=\Omega_u^U\cup\Omega_v^V, (U,V)\in\{+,-\}^2$ where $\{+,-\}^2$ refers to all posible combinations of +,-. Using these the following discreate functional can be generated:

\[\begin{aligned} E(\mathbf{z})=\frac{1}{4}\Big(&\sum_{(u,v)\in\Omega^{++}}\sqrt{[\partial_u^+z_{u,v}-p_{u,v}]^2+[\partial_v^+z_{u,v}-q_{u,v}]^2}\\ +&\sum_{(u,v)\in\Omega^{+-}}\sqrt{[\partial_u^+z_{u,v}-p_{u,v}]^2+[\partial_v^-z_{u,v}-q_{u,v}]^2}\\ +&\sum_{(u,v)\in\Omega^{-+}}\sqrt{[\partial_u^-z_{u,v}-p_{u,v}]^2+[\partial_v^+z_{u,v}-q_{u,v}]^2}\\ +&\sum_{(u,v)\in\Omega^{--}}\sqrt{[\partial_u^-z_{u,v}-p_{u,v}]^2+[\partial_v^-z_{u,v}-q_{u,v}]^2}\Big)\\ +&\sum_{(u,v)\in\Omega}\lambda_{u,v}\left[z_{u,v}-z^0_{u,v}\right]^2 \end{aligned}\]

Which simplifies to the minimization problem:

\[\begin{gathered} min\frac{1}{4}\sum_{(U,V)\in\{+,-\}^2}\sum_{(u,v)\in\Omega^{UV}}||\mathbf{r}_{(u,v)}^{UV}||+\sum_{(u,v)\in\Omega}\lambda_{u,v}\left[z_{u,v}-z^0_{u,v}\right]^2\\ \mathbf{r}_(u,v)^{UV}=\nabla^{UV}z_{u,v}-\mathbf{g}_{u,v} \end{gathered}\]

This leads to the optimization scheme using an ADMM algorithm defined by:

\[\begin{aligned} z^{(k+1)}=&min\frac{\alpha}{8}\sum_{(U,V)\in\{+,-\}^2}\sum_{(u,v)\in\Omega^{UV}}||\nabla^{UV}z_{u,v}-(\mathbf{g}_{u,v}+\mathbf{r}_{(u,v)}^{UV^{(k)}}-\mathbf{b}_{(u,v)}^{UV^{(k)}})||\\&+\sum_{(u,v)\in\Omega}\lambda_{u,v}\left[z_{u,v}-z^0_{u,v}\right]^2\\ \mathbf{r}_{(u,v)}^{UV^{(k+1)}}=&min\frac{\alpha}{8}||\mathbf{r}-(\nabla^{UV}z_{u,v}-\mathbf{g}_{u,v}+\mathbf{b}_{(u,v)}^{UV^{(k)}})||+||\mathbf{r}||\\ \mathbf{b}_{(u,v)}^{UV^{(k+1)}}=&\mathbf{b}_{(u,v)}^{UV^{(k)}})+\nabla^{UV}z_{u,v}-\mathbf{g}_{u,v}-\mathbf{r}_{(u,v)}^{UV^{(k+1)}} \end{aligned}\]

The z update then can be solved using the linear system defined below, where D_{u,v}^{U,V} and $\Lambda$ are the same at those defined in Quadratic.

\[\begin{aligned} A_{TV}&\mathbf{z}^{(k+1)}=b_{TV}^{(k)}\\ A_{TV}&=\frac{\alpha}{8}\sum_{(U,V)\in\{+,-\}^2}\left[D_u^{U\top}D_u^{U}+D_v^{V\top}D_v^{V}\right] + \Lambda^2\\ b_{TV}^{(k)}&=\frac{\alpha}{8}\sum_{(U,V)\in\{+,-\}^2}\left[D_u^{U\top}\mathbf{P}^{UV^{(k)}} + D_v^{V\top}\mathbf{Q}^{UV^{(k)}}\right]+ \Lambda^2\mathbf{z}^0\\ \end{aligned}\]

Where $\mathbf{P}^{UV^{(k)}}, \mathbf{Q}^{UV^{(k)}}$ are the u and v components of $\mathbf{g}+\mathbf{r}^{UV^{(k)}}-\mathbf{b}^{UV^{(k)}}$. This can be solved using conjugate gradient. Finally the update to $\mathbf{r}^{UV}$ can be computed as:

\[\begin{aligned} \mathbf{r}^{UV^{(k+1)}}&=max\Big\{||\mathbf{s}_{u,v}^{UV^{(k+1)}}||-\frac{4}{\alpha},0\Big\}\frac{\mathbf{s}_{u,v}^{UV^{(k+1)}}}{||\mathbf{s}_{u,v}^{UV^{(k+1)}}||}\\ \text{Where:}&\\ \mathbf{s}^{UV^{(k+1)}}&=\nabla^{UV}z_{u,v}^{(k+1)}-\mathbf{g}_{u,v}+\mathbf{b}_{u,v}^{UV^{(k)}} \end{aligned}\]

Parameters

z:

An AbstractArray which defines the value of $z^0$ the initial solution and prior to be used in the regulization term. Must be provided.

α:

A Real with defult value of 1.0 which controls the step size. In theory this value should have no impact on final solution but in practice larger values can lead to worse solutions while values which are two small may lead non-convergance in the least square update step, causing the algorithm to hang for long periods of time. Values below 0.25 are not recomended but may work depending on domain size and inputs.

λ:

An AbstractArray the same size as z defulting to $10.0^{-6}$ everywhere. This defines theregulization weight at each point. Large valueas will force the algorithm to keep the solution near to $z^0$ at that position. Can be used to keep the solution near the initial solution or guide the solution to a certian known value at points (i.e. known maxima and minima). This value should be set uniformly small otherwise.

mask:

An AbstractArray the same size as z, which guides the algorithm as to where the valid domain is. Values of 1 will be in the domain $\Omega$ while other values will be ignored and set to $z^0$. This can be used to integrate over sub-domain or to segment the domain into parts. The gen_mask() funtion can be used to generate a mask which will remove non-integrable regions dramatically improving the solution under most condition at the cost of not integrating the entire solution.

Example

The following example demonstrates the use of the TotalVariation integrator.

using ShapeFromShading, Makie

# Generate synthetic gradients
p, q = synthetic_gradient(Prism(75), img_size = 151)

# Create a TotalVariation() integrator
totalVariation = TotalVariation(z=zeros(size(p)), α=1.0)
totalVariationMasked = TotalVariation(z=zeros(size(p)), α=0.5, mask=gen_mask(p,q,1.0)[:,:,1])

# Calculate the heightmap from the gradients
Z = totalVariation(p, q)
Z2 = totalVariationMasked(p, q)

# Normalize to maximum of 1 (not necessary but makes displaying easier)
Z = Z./maximum(Z)
Z2 = Z2./maximum(Z2)

# Display using Makie (Note: Makie can often take several minutes first time)
r = 0.0:0.1:4
vbox(surface(r, r, Z), surface(r, r, Z2))

Reference

[1] Y. Quéau, J. Durou and J. Aujol, "Variational Methods for Normal Integration", Journal of Mathematical Imaging and Vision, vol. 60, no. 4, pp. 609-632, 2017. doi: 10.1007/s10851-017-0777-6

source
ShapeFromShading.NonConvex1Method
NonConvex1(z::AbstractArray, β::Real = 0.5, λ::AbstractArray = fill(10.0^-6, size(z)), mask::AbstractArray = fill(1.0, size(z)), max_iter::Int = 100)

The first of two non-convex regularization methods proposed by Aujol, Durou and Quéau. The same fidelity and regularization terms to minimize as Quadratic are used, but the convexity of $\Phi$ is sacrificed in order to gain better behaviour around discontinuities and outliers.

Output

NonConvex1() returns a NonConvex1 integrator which can then be called to run the non-convex regularization method method on a gradient field.

Details

The initial minimization problem is the same as in TotalVariation as given below exept $Phi$ is defined using the given function instead of the $L_1$ norm.

\[\begin{aligned} &min\iint_{(u,v)\in \Omega}||\nabla z(u,v)-\mathbf{g}(u,v)||+\lambda(u,v)\left[z(u,v)-z^0(u,v)\right]^2dudv\\ &\text{where:}\\ &\Phi(s)=log(s^2+\beta^2) \end{aligned}\]

The problem can then be discretized to form the following functional where $D_{u,v}^{UV}$ is a $2\times|\Omega|$ matrix formed from stacking the vectors of the finite diferences.

\[\begin{aligned} E(\mathbf{z})&=\frac{1}{4}\sum_{(U,V)\in\{+,-\}^2}\sum_{(u,v)\in\Omega^{UV}}\Phi(||\nabla z_{u,v}-\mathbf{g}_{u,v}||)+\sum_{(u,v)\in\Omega}\lambda_{u,v}\left[z_{u,v}-z^0_{u,v}\right]^2\\ &=\frac{1}{4}\sum_{(U,V)\in\{+,-\}^2}\sum_{(u,v)\in\Omega^{UV}}\Phi(||D_{u,v}^{UV}\mathbf{z}-\mathbf{g}_{u,v}||)+||\Lambda\left(\mathbf{z}-\mathbf{z}^0\right)||^2\\ &=f(\mathbf{z})+g(\mathbf{z}) \end{aligned}\]

As $f(\mathbf{z})$ is smooth but not convex and $g(\mathbf{z})$ is convex the iPiano algorithm is then used to iterativly solve the minimization of the functional such that:

\[\mathbf{z}^{(k+1)}=(I+\alpha_1\partial g)^{-1}(\mathbf{z}^{(k)}-\alpha_1\nabla f(\mathbf{z}^{(k)})+\alpha_2(\mathbf{z}^{(k)}-z^{(k+1)}))\]

where $(I+\alpha_1\partial g)^{-1}$ is a proximal operator defined as:

\[(I+\alpha_1\partial g)^{-1}(\mathbf{\hat{x}})=(I+2\alpha_1\Lambda^2)^{-1}(\mathbf{\hat{x}}+2\alpha_1\Lambda\mathbf{z}^0)\]

Using the definition of $\Phi$ given above, the derivative of $f(\mathbf{z})$ can be computed as below:

\[\nabla f(\mathbf{z})=\frac{1}{4}\sum_{(U,V)\in\{+,-\}^2}\sum_{(u,v)\in\Omega^{UV}}\frac{D_{u,v}^{UV^\top}(D_{u,v}^{UV}\mathbf{z}-\mathbf{g}_{u,v})}{||D_{u,v}^{UV}\mathbf{z}-\mathbf{g}_{u,v}||^2+\beta^2}\]

The values of $\alpha_1$ and $\aplha_2$ control the step size of the algorith and are chosen such that $\alpha_2$ is fixed at 0.8 while $\alpha_1$ is chosen using the lazy backtracking method, which uses a Lipschitz constant to caculate a suitably small step size at each step using the below relationship where $\eta>1$ is a constant:

\[\begin{aligned} &\alpha_1 < 2(1-\alpha_2)/L_n\\ &\text{where:}\\ &L_k\in\{L_{k-1},\eta L_{k-1},\eta^2L_{k-1},...\}\\ &\text{such that it is minimal and satisfies:}\\ &f(x^{(k+1)})\le f(x^{(k)}) + \left\langle\nabla f(x^{(k)}), x^{(k+1)}-x^{(k)}\right\rangle+\frac{L_k}{2}||x^{(k+1)}-x^{(k)}||^2 \end{aligned}\]

This method, being non-convex, highly relies on a good initial solution and will often only provide a minimal improvment to the solution. A bad initial solution will produce a final solution which does not resemble the surface under most conditions.

Parameters

z:

An AbstractArray which defines the value of $z^0$ the initial solution and prior to be used in the regulization term.

β:

A Real which acts as a hyper-parameter to the function. Large values for β will produce smoother functions but loose discontinuities. Smaller value will preserve discontinuities but lead to staircassing in the solution. Defults to 0.5.

λ:

An AbstractArray the same size as z defulting to $10.0^-6$ everywhere. This defines theregulization weight at each point. Large valueas will force the algorithm to keep the solution near to $z^0$ at that position. Can be used to keep the solution near the initial solution or guide the solution to a certian known value at points (i.e. known maxima and minima). This value should be set uniformly small otherwise.

mask:

An AbstractArray the same size as z, which guides the algorithm as to where the valid domain is. Values of 1 will be in the domain $\Omega$ while other values will be ignored and set to $z^0$. This can be used to integrate over sub-domain or to segment the domain into parts. The gen_mask() funtion can be used to generate a mask which will remove non-integrable regions dramatically improving the solution under most condition at the cost of not integrating the entire solution.

Example

The following example demonstrates the use of the NonConvex1 integrator.

using ShapeFromShading, Makie

# Generate synthetic gradients
p, q = synthetic_gradient(Prism(75), img_size = 151)

# Create a NonConvex1() integrator
nonConvex1 = NonConvex1(z=zeros(size(p)), β=0.5)
nonConvex1Init = NonConvex1(z=Horn()(p,q), β=0.5)

# Calculate the heightmap from the gradients
Z = nonConvex1(p, q)
Z2 = nonConvex1Init(p, q)

# Normalize to maximum of 1 (not necessary but makes displaying easier)
Z = Z./maximum(Z)
Z2 = Z2./maximum(Z2)

# Display using Makie (Note: Makie can often take several minutes first time)
r = 0.0:0.1:4
vbox(surface(r, r, Z), surface(r, r, Z2))

Reference

[1] Y. Quéau, J. Durou and J. Aujol, "Variational Methods for Normal Integration", Journal of Mathematical Imaging and Vision, vol. 60, no. 4, pp. 609-632, 2017. doi: 10.1007/s10851-017-0777-6

source
ShapeFromShading.NonConvex2Method
NonConvex2(z::AbstractArray, γ::Real = 1.0, λ::AbstractArray = fill(10.0^-6, size(z, mask::AbstractArray = fill(1.0, size(z)), max_iter::Int = 100)

The second of two non-convex regularization methods proposed by Aujol, Durou and Quéau. The same as NonConvex1 exept $\Phi$ has been replaced with a different non-convex function.

Output

NonConvex2() returns a NonConvex2 integrator which can then be called to run the non-convex regularization method method on a gradient field.

Details

The implimentation is the same as the one in TotalVariation as given below exept $Phi$ is defined as below:

\[\Phi(s)=\frac{s^2}{s^2+\gamma^2}\]

Using the definition of $\Phi$ given above, the derivative of $f(\mathbf{z})$ can be computed as below:

\[\nabla f(\mathbf{z})=\frac{1}{4}\sum_{(U,V)\in\{+,-\}^2}\sum_{(u,v)\in\Omega^{UV}}\frac{\gamma^2D_{u,v}^{UV^\top}(D_{u,v}^{UV}\mathbf{z}-\mathbf{g}_{u,v})}{()||D_{u,v}^{UV}\mathbf{z}-\mathbf{g}_{u,v}||^2+\gamma^2)^2}\]

This method, being non-convex, highly relies on a good initial solution and will often only provide a minimal improvment to the solution. A bad initial solution will produce a final solution which does not resemble the surface under most conditions.

Parameters

z:

An AbstractArray which defines the value of $z^0$ the initial solution and prior to be used in the regulization term.

γ:

A Real which acts as a hyper-parameter to the function. Large values for γ will produce smoother functions but loose discontinuities. Smaller value will preserve discontinuities but lead to staircassing in the solution. Defults to 1.0.

λ:

An AbstractArray the same size as z defulting to $10.0^-6$ everywhere. This defines theregulization weight at each point. Large valueas will force the algorithm to keep the solution near to $z^0$ at that position. Can be used to keep the solution near the initial solution or guide the solution to a certian known value at points (i.e. known maxima and minima). This value should be set uniformly small otherwise.

mask:

An AbstractArray the same size as z, which guides the algorithm as to where the valid domain is. Values of 1 will be in the domain $\Omega$ while other values will be ignored and set to $z^0$. This can be used to integrate over sub-domain or to segment the domain into parts. The gen_mask() funtion can be used to generate a mask which will remove non-integrable regions dramatically improving the solution under most condition at the cost of not integrating the entire solution.

Example

The following example demonstrates the use of the NonConvex2 integrator.

using ShapeFromShading, Makie

# Generate synthetic gradients
p, q = synthetic_gradient(Prism(75), img_size = 151)

# Create a NonConvex2() integrator
nonConvex2 = NonConvex2(z=zeros(size(p)), γ=1.0)
nonConvex2Init = NonConvex2(z=Horn()(p,q), γ=1.0)

# Calculate the heightmap from the gradients
Z = nonConvex2(p, q)
Z2 = nonConvex2Init(p, q)

# Normalize to maximum of 1 (not necessary but makes displaying easier)
Z = Z./maximum(Z)
Z2 = Z2./maximum(Z2)

# Display using Makie (Note: Makie can often take several minutes first time)
r = 0.0:0.1:4
vbox(surface(r, r, Z), surface(r, r, Z2))

Reference

[1] Y. Quéau, J. Durou and J. Aujol, "Variational Methods for Normal Integration", Journal of Mathematical Imaging and Vision, vol. 60, no. 4, pp. 609-632, 2017. doi: 10.1007/s10851-017-0777-6

source
ShapeFromShading.AnisotropicDiffusionMethod
AnisotropicDiffusion(z::AbstractArray, μ::Real = 5.0, ν::Real = 10., λ::AbstractArray = fill(10.0^-6, size(z)), mask::AbstractArray = fill(1.0, size(z)), max_iter::Int = 10)

Defines the anisotropic diffusion method as proposed by Aujol, Durou and Quéau. It utilizes an anisotropic diffusion like process to create a weighted version of the least squares problem solved in the Quadratic method.

Output

AnisotropicDiffusion() returns a AnisotropicDiffusion integrator which can then be called to run the anisotropic diffusion method method on a gradient field.

Details

The minimization problem from Quadratic is modified with the addition weighting term $W(u,v)$ to reach the following minimization problem.

\[min\iint_{(u,v)\in \Omega}||W(u,v)[\nabla z(u,v)-\mathbf{g}(u,v)]||^2+\lambda(u,v)\left[z(u,v)-z^0(u,v)\right]^2dudv\]

The weighting term can be defined as below where $\mu$ and $\nu$ are parameters which control the impact of gradient of the reconstruction at each iteration and the input gradients.

\[W(u,v)=\frac{1}{\sqrt{\left(\frac{||\nabla z(u,v)||}{\mu}\right)^2+1}}\begin{bmatrix}\frac{1}{\sqrt{1+\left(\frac{p(u,v)}{\nu}\right)^2}} &0\\ 0 &\frac{1}{\sqrt{1+\left(\frac{q(u,v)}{\nu}\right)^2}}\end{bmatrix}\]

This lends itself to the creation of the terms $A^{UV}$ and $B^{UV}$ which are $|\Omega|\times|\Omega|$ diagonal matrices, respectively, contianing the values of;

\[ \frac{1}{\sqrt{1+\left(\frac{p(u,v)}{\nu}\right)^2}\sqrt{\frac{(\partial^U_uz_{u,v})^2+(\partial^V_vz_{u,v})^2}{\mu^2}+1}}\]

and

\[ \frac{1}{\sqrt{1+\left(\frac{q(u,v)}{\nu}\right)^2}\sqrt{\frac{(\partial^U_uz_{u,v})^2+(\partial^V_vz_{u,v})^2}{\mu^2}+1}}\]

where $(U,V)\in\{+,-\}^2$. Using these definititions the original minimization problem can be rewriten as the following iterative scheme:

\[\begin{aligned} z^{(k+1)}=&min\frac{1}{4}\sum_{(U,V)\in\{+,-\}^2}\left\{||A^{UV}(D^U_u\mathbf{z}-\mathbf{p})||^2+||B^{UV}(D^V_v\mathbf{z}-\mathbf{q})||^2\right\}\\ &+||\Lambda(\mathbf{z}-\mathbf{z}^0)|| \end{aligned}\]

which is then solved using Cholesky factorization.

Parameters

z:

An AbstractArray which defines the value of $z^0$ the initial solution and prior to be used in the regulization term.

μ:

A Real which acts as a hyper-parameter to the function. Allows for the tuning of discontinuities. Defults to 5.0.

ν:

A Real which acts as a hyper-parameter to the function. Allows for the tuning of discontinuities. Unlike μ is has a minimal impact on the solution and generally does not need adjusting unless an extreme value for μ is used where it can help to balance out the two terms. Defults to 10.0.

λ:

An AbstractArray the same size as z defulting to $10.0^-6$ everywhere. This defines theregulization weight at each point. Large valueas will force the algorithm to keep the solution near to $z^0$ at that position. Can be used to keep the solution near the initial solution or guide the solution to a certian known value at points (i.e. known maxima and minima). This value should be set uniformly small otherwise.

mask:

An AbstractArray the same size as z, which guides the algorithm as to where the valid domain is. Values of 1 will be in the domain $\Omega$ while other values will be ignored and set to $z^0$. This can be used to integrate over sub-domain or to segment the domain into parts. The gen_mask() funtion can be used to generate a mask which will remove non-integrable regions dramatically improving the solution under most condition at the cost of not integrating the entire solution.

Example

The following example demonstrates the use of the AnisotropicDiffusion integrator.

using ShapeFromShading, Makie

# Generate synthetic gradients
p, q = synthetic_gradient(Prism(75), img_size = 151)

# Create a AnisotropicDiffusion() integrator
anisotropicDiffusion = AnisotropicDiffusion(z=zeros(size(p)), γ=1.0)
anisotropicDiffusionInit = AnisotropicDiffusion(z=Horn()(p,q), γ=1.0)

# Calculate the heightmap from the gradients
Z = anisotropicDiffusion(p, q)
Z2 = anisotropicDiffusionInit(p, q)

# Normalize to maximum of 1 (not necessary but makes displaying easier)
Z = Z./maximum(Z)
Z2 = Z2./maximum(Z2)

# Display using Makie (Note: Makie can often take several minutes first time)
r = 0.0:0.1:4
vbox(surface(r, r, Z), surface(r, r, Z2))

Reference

[1] Y. Quéau, J. Durou and J. Aujol, "Variational Methods for Normal Integration", Journal of Mathematical Imaging and Vision, vol. 60, no. 4, pp. 609-632, 2017. doi: 10.1007/s10851-017-0777-6

source
ShapeFromShading.MumfordShahMethod
MumfordShah(z::AbstractArray, μ::Real = 10.0, ϵ::Real = 0.1, λ::AbstractArray = fill(10.0^-6, size(z, mask::AbstractArray = fill(1.0, size(z)), max_iter::Int = 50)

Defines the Mumford-Shah method, the final method proposed by Aujol, Durou and Quéau. It utilizes an adepted version of Mumford and Shah functional to provide a good reconstruction in the presence of discontinuities at the const of a longer runtime then other methods.

Warning

This function can take several minutes to run on grids larger then 64X64.

Output

MumfordShah() returns a MumfordShah integrator which can then be called to run the Mumford-Shah method method on a gradient field.

Details

This method involves modifying the minimization problem given by the Mumford and Shah functional given below:

\[min\;\mu\iint_{(u,v)\in\Omega\backslash K}||\nabla z(u,v)||dudv+\int_Kd\sigma\\+\lambda\iint_{(u,v)\in\Omega\backslash K}[z(u,v)-z^0(u,v)]^2dudv\]

Where $K$ is the set of discontinuities. This can then be adapted to our problem by utilizing a Ambrosio-Tortelli approximation to achaive the following functional where $\epsilon\to0$.

\[\begin{aligned} E(z)=\mu&\iint_{(u,v)\in\Omega}w(u,v)^2||\nabla z(u,v)-\mathbf{g}(u,v)||^2dudv\\ +&\iint_{(u,v)}\left[\epsilon||\nabla w(u,v)||^2+\frac{1}{4\epsilon}(w(u,v)-1)^2\right]dudv\\ +&\iint_{(u,v)}\lambda(u,v)\left[z(u,v)-z^0(u,v)\right]dudv \end{aligned}\]

This leads to the following discretization of the functional where $\mathbf{w}^{+/-}_{u/v}$ are thevecotr of weights in each forward and backward direction and the matrix $W^{+/-}_{u/v}$ is the diagonal matrix formed from this vector.

\[\begin{aligned} E(\mathbf{z},\mathbf{w}^{+}_{u},\mathbf{w}^{-}_{u},\mathbf{w}^{+}_{v},\mathbf{w}^{-}_{v})&=\frac{\mu}{2}\Big(||W^+_u(D^+_u\mathbf{z}-\mathbf{p})||^2+||W^-_u(D^-_u\mathbf{z}-\mathbf{p})||^2\\ &+||W^+_v(D^+_v\mathbf{z}-\mathbf{q})||^2+||W^-_v(D^-_v\mathbf{z}-\mathbf{q})||^2\Big)\\ &+\frac{\epsilon}{2}\Big(||D^+_u\mathbf{w}^{+}_{u}||^2+||D^-_u\mathbf{w}^{-}_{u}||^2+||D^+_v\mathbf{w}^{+}_{v}||^2\\ &+||D^-_v\mathbf{w}^{-}_{v}||^2\Big)+\frac{1}{8\epsilon}\Big(||\mathbf{w}^{+}_{u}-\mathbf{1}||^2+||\mathbf{w}^{-}_{u}-\mathbf{1}||^2\\ &+||\mathbf{w}^{+}_{v}-\mathbf{1}||^2+||\mathbf{w}^{-}_{v}-\mathbf{1}||^2\Big)+||\Lambda(\mathbf{z}-\mathbf{z}^0)||^2 \end{aligned}\]

This is then solved with a conjugate gradient algorithm and an alternating optimization scheme at each step where the updates are found using the relationships below:

\[\begin{gathered} \mathbf{z}^{(k+1)}=min\;E(\mathbf{z}^{(k)},\mathbf{w}^{+(k)}_{u},\mathbf{w}^{-(k)}_{u},\mathbf{w}^{+(k)}_{v},\mathbf{w}^{-(k)}_{v})\\ \mathbf{w}^{+/-(k+1)}_{u/v}=min\;E(\mathbf{z}^{(k+1},\mathbf{w}^{+(k)}_{u},\mathbf{w}^{-(k)}_{u},\mathbf{w}^{+(k)}_{v},\mathbf{w}^{-(k)}_{v})\\ \end{gathered}\]

This method can produce good results if the parameters are appropriotly tuned. If the parameters are too large the solution will suffer heavily from staircasing artifacts while setting it too small will result in a smooth solution. Even if the value is chosen correctly the algorithm tends to overfit the final solution to the discontinuities and they will extend into parts of the solution which are actually smooth.

Parameters

z:

An AbstractArray which defines the value of $z^0$ the initial solution and prior to be used in the regulization term.

ϵ:

A Real which acts as a hyper-parameter to the function. Controls how the final solution will converge. Large values will lead to staircasing while small values will over-smooth the surface. Must be relativly small to achieve convergence to a solution. Defults to 0.1.

μ:

A Real which acts as a hyper-parameter to the function. This value controls the smoothness of the final solution. Large values will lead to staircasing while small values will lead to over-smoothed solutions. Defults to 10.0.

λ:

An AbstractArray the same size as z defulting to $10.0^-6$ everywhere. This defines the regulization weight at each point. Large values will force the algorithm to keep the solution near to $z^0$ at that position. Can be used to keep the solution near the initial solution or guide the solution to a certian known value at points (i.e. known maxima and minima). This value should be set uniformly small otherwise.

mask:

An AbstractArray the same size as z, which guides the algorithm as to where the valid domain is. Values of 1 will be in the domain $\Omega$ while other values will be ignored and set to $z^0$. This can be used to integrate over sub-domain or to segment the domain into parts. The gen_mask() funtion can be used to generate a mask which will remove non-integrable regions dramatically improving the solution under most condition at the cost of not integrating the entire solution.

Example

The following example demonstrates the use of the MumfordShah integrator.

using ShapeFromShading, Makie

# Generate synthetic gradients
p, q = synthetic_gradient(Prism(75), img_size = 151)

# Create a MumfordShah() integrator
mumfordShah = MumfordShah(z=zeros(size(p)), γ=1.0)
mumfordShahInit = MumfordShah(z=Horn()(p,q), γ=1.0)

# Calculate the heightmap from the gradients
Z = mumfordShah(p, q)
Z2 = mumfordShahInit(p, q)

# Normalize to maximum of 1 (not necessary but makes displaying easier)
Z = Z./maximum(Z)
Z2 = Z2./maximum(Z2)

# Display using Makie (Note: Makie can often take several minutes first time)
r = 0.0:0.1:4
vbox(surface(r, r, Z), surface(r, r, Z2))

Reference

[1] Y. Quéau, J. Durou and J. Aujol, "Variational Methods for Normal Integration", Journal of Mathematical Imaging and Vision, vol. 60, no. 4, pp. 609-632, 2017. doi: 10.1007/s10851-017-0777-6

source

Shape From Shading:

ShapeFromShading.DiscreteShapeMethod
DiscreteShape(iterations::Int = 2000, smoothness::Int = 1000, albedo::Real = Inf, illumination_direction::Vector{T} where T <: Real = [Inf, Inf, Inf])

Contins DiscreteShape algorithm which ttempts to produce a heightmap from a grayscale image by minimization of a set of Euler-Lagrange equations. This is done discretely at each point in the image utilizing the second derivates of the surface normals and their Fourier Transforms.

Output

Returns a DiscreteShape functor which can be run on an image to attempt reconstruction of the surface contianed in the image.

Details

The algorithm attempts to minimise the brightness deviation $ϵ$ through the brightness constraint $\epsilon_1$ and smoothness constraint $\epsilon_2$ as defined bellow:

\[\epsilon=\iint((\epsilon_1+\lambda\epsilon_2))dxdy=\iint((E(x,y)-R(p,q))^2+ \lambda(p_x^2+q_x^2+p_y^2+q_y^2))\]

This is minimised using the Euler-Lagrange equations defined as;

\[\dfrac{\delta\epsilon}{\delta p}-\dfrac{\delta}{\delta x}\dfrac{\delta\epsilon}{ \delta p_x}-\dfrac{\delta}{\delta y}\dfrac{\delta\epsilon}{\delta p_y}=0\]

and

\[\dfrac{\delta \epsilon}{\delta q}-\dfrac{\delta}{\delta x}\dfrac{\delta\epsilon}{\delta q_x}- \dfrac{\delta}{\delta y}\dfrac{\delta\epsilon}{\delta q_y}=0\]

which become:

\[\begin{gathered} -2(E-R)\dfrac{\delta R}{\delta p}-2\lambda p_{xx}-2\lambda p_{yy}=0\\ -2(E-R)\dfrac{\delta R}{\delta p}-2\lambda q_{xx}-2\lambda q_{yy}=0 \end{gathered}\]

which can be further simplified to give:

\[\begin{gathered} \nabla^2p=\dfrac{1}{\lambda}(R-E)\dfrac{\delta R}{\delta p}\\ \nabla^2q=\dfrac{1}{\lambda}(R-E)\dfrac{\delta R}{\delta q} \end{gathered}\]

where $\nabla^2p=p_{xx}+p_{yy}$ and $\nabla^2q=q_{xx}+q_{yy}$ are Laplacians of p and q.

However, for computation we are dealing with a discreate case of these equations which can be defined as below:

\[\begin{gathered} p_{i,j}=\bar{p}_{i,j}+\dfrac{1}{4\lambda}(E-R)\dfrac{\delta R}{\delta p}\\q_{i,j}= \bar{q}_{i,j}+\dfrac{1}{4\lambda}(E-R)\dfrac{\delta R}{\delta q}\\ \end{gathered}\]

where

\[\bar{p}_{i,j}=\dfrac{p_{i+1,j}+p_{i-1,j}+p_{i,j+1}+p_{i,j-1}}{4}\]

and

\[\bar{q}_{i,j}=\dfrac{q_{i+1,j}+q_{i-1,j}+q_{i,j+1}+q_{i,j-1}}{4}\]

Finally, the algorithm needs to enforce integrability on p and q and retrieve the surface Z. This can be done by taking the Fast Fourier Transform of p and q ()$c_p(\omega_x,\omega_y)$ and $c_q(\omega_x,\omega_y)$) and then using the Inverse Fast Fourier Transform to recover Z and update p and q as per bellow:

\[\begin{gathered} p=\sum c_p(\omega_x,\omega_y)e^{j(\omega_xx+\omega_yy)}\\ q=\sum c_q(\omega_x,\omega_y)e^{j(\omega_xx+\omega_yy)}\\Z=\sum c(\omega_x,\omega_y) e^{j(\omega_xx+\omega_yy)}\\ \end{gathered}\]

where

\[c(\omega_x,\omega_y)=\dfrac{-j(\omega_xc_p(\omega_x,\omega_y)+\omega_yc_q(\omega_x ,\omega_y))}{\omega_x^2+\omega_y^2}\]

Arguments

The arguments are described in more detail below.

albedo

A Real that specifies the albedo (amount of light reflected) of the image. If albedo is specified to must the illumination_direction. Defults to Inf which will trigger the algorithm to run estimate_img_properties.

illumination_direction

A Vector{T} where T <: Real that specifies the tilt value to be used by the algorithm. The illumination_direction should be a vector of the form [x,y,z] where x,y,z are int he range [0,1]. If illumination_direction is specified to must the albedo. Defults to [Inf, Inf, Inf] which will trigger the algorithm to run estimate_img_properties.

iterations

An Int that specifies the number of iterations the algorithm is to perform. If left unspecified a default value of 2000 is used.

smoothness

An Int that specifies the strength of the smoothness constraint in the minimised function. Defults to 1000.

Note

If albedo and illumination_direction are not defined (i.e. have defulted to Inf) they will be calculated at runtime using estimate_img_properties.

Example

Compute the heightmap for a synthetic image generated by generate_surface.

using Images, Makie, ShapeFromShading

#generate synthetic image
img = generate_surface(SynthSphere(50), 1.0, [0.2,0,0.9])

#calculate the heightmap (using 500 iterations)
discreteShape = DiscreteShape(iterations = 500, albedo = 1.0, illumination_direction = [0.2,0,0.9])
Z,p,q = discreteShape(img)

#normalize to maximum of 1 (not necessary but makes displaying easier)
Z = Z./maximum(Z)

#display using Makie (Note: Makie can often take several minutes first time)
r = 0.0:0.1:2
surface(r, r, Z)

Reference

  1. S. Elhabian, "Hands on Shape from Shading", Computer Vision and Image Processing, 2008.
source
ShapeFromShading.DiscreteShapeBoundMethod
DiscreteShapeBound(iterations::Int = 2000, smoothness::Int = 1000, albedo::Real = Inf, illumination_direction::Vector{T} where T <: Real = [Inf, Inf, Inf])

Same as DiscreteShape except it has its initial conditions bound by the image as per bellow where $E$ is the brightness of the image:

\[\begin{gathered} Z_{i,j}=\begin{cases}-100E_{i,j} &\text{if } E_{i,j}>0.75\\0 &\text{otherwise}\\ \end{cases}\\\\p,q=\nabla E \end{gathered}\]

Example

Compute the heightmap for a synthetic image generated by generate_surface.

using Images, Makie, ShapeFromShading

#generate synthetic image
img = generate_surface(SynthSphere(50), 1.0, [0.2,0,0.9])

#calculate the heightmap (using 500 iterations)
discreteShapeBound = DiscreteShapeBound(iterations = 500, 1.0, [0.2,0,0.9])
Z,p,q = discreteShapeBound(img)

#normalize to maximum of 1 (not necessary but makes displaying easier)
Z = Z./maximum(Z)

#display using Makie (Note: Makie can often take several minutes first time)
r = 0.0:0.1:2
surface(r, r, Z)

Reference

  1. S. Elhabian, "Hands on Shape from Shading", Computer Vision and Image Processing, 2008.
source
ShapeFromShading.PentlandMethod
Pentland(slant::Real = Inf, tilt::Real = Inf)

Creates a Pentland fucntor which attempts to produce a heightmap from a grayscale image using Pentland's algorithm. Under the assumptions of Lambertian surface, orthographic projections, the surface being illuminated by distant light sources, the surface is not self-shadowing and the surface has constant albedo, hence it can be ignored. The algorithm employs Tayler series expansion and Fourier transforms to compute a non-iterative solution.

Output

Returns a Pentland functor which can be used to run the Pentland algorithm to reconstruct a surface form a grayscale image.

Details

In Pentlands algorithm the image irradiance is defined as:

\[E(x,y)=R(p,q)=\dfrac{\rho(i_xp+i_yq-i_z)}{\sqrt{1+p^2+q^2}}=\dfrac{p\sin\sigma\cos \tau+q\sin\sigma\sin\tau+\cos\sigma}{\sqrt{1+p^2+q^2}}\]

This can be reduced using the Taylor series expansion about $p=p_0$ and $p=p_0$ and ignoring the higher order terms becomes:

\[E(x,y)=R(p,q)\approx R(p_0,q_0)+(p-p_0)\dfrac{\delta R}{\delta p}(p_0,q_0)+(q-q_0) \dfrac{\delta R}{\delta q}(p_0,q_0)\]

which for $p_0=q_0=0$ further reduces to:

\[E(x,y)\approx\cos\sigma+p\cos\tau\sin\sigma+q\sin\tau\sin\sigma\]

This gives the following transform identities:

\[\begin{gathered} p=\dfrac{\delta}{\delta x}Z(x,y)\xleftrightarrow{\Im}(-j\omega_x)F_z( \omega_x,\omega_y)\\q=\dfrac{\delta}{\delta y}Z(x,y)\xleftrightarrow{\Im}(-j \omega_y)F_z(\omega_x,\omega_y) \end{gathered}\]

By taking the Fourier transform of both sides if $E(x,y)$ yields the following:

\[F_E=(-j\omega_x)F_z(\omega_x,\omega_y)\cos\tau\sin\sigma+(-j\omega_y)F_z(\omega_x ,\omega_y)\sin\tau\sin\sigma\]

where $F_z$ is the Fourier transform of $Z(x,y)$.

These can be rearranged, and the Inverse Fourier transform used to recover the surface $Z(x,y)$ as per the following:

\[\begin{gathered} F_E=F_z(\omega_x,\omega_y)[-j\omega_x\cos\tau\sin\sigma-j\omega_y\sin\tau\sin \sigma]\\\Rightarrow F_z(\omega_x,\omega_y)=\dfrac{F_E}{-j\omega_x\cos\tau\sin \sigma-j\omega_y\sin\tau\sin\sigma}\\Z(x,y)=\Im^{-1}\{F_z(\omega_x,\omega_y)\} \end{gathered}\]

Arguments

The function arguments are described in more detail below.

slant

A Real that specifies the slant value to be used by the algorithm. The slant should be a value in the range [0,π/2]. If slant is specified to must the tilt. Will defult to Inf which will trigger estimate_img_properties to be run.

tilt

A Real that specifies the tilt value to be used by the algorithm. The tilt should be a value in the range [0,2π]. If tilt is specified to must the slant. Will defult to Inf which will trigger estimate_img_properties to be run.

Note

If slant and tilt are not defined (i.e. have defulted to Inf) they will be calculated at runtime using estimate_img_properties.

Example

Compute the heightmap for a synthetic image generated by generate_surface.

using Images, Makie, ShapeFromShading

#generate synthetic image
img = generate_surface(SynthSphere(50), 1.0, [0.1,0,0.9])

#calculate the heightmap
pentland = Pentland(slant = 0.1106572211739, tilt = 0.0)
Z = pentland(img)

#normalize to maximum of 1 (not necessary but makes displaying easier)
Z = Z./maximum(Z)

#display using Makie (Note: Makie can often take several minutes first time)
r = 0.0:0.1:2
surface(r, r, Z)

Reference

  1. A. Pentland, "Shape Information From Shading: A Theory About Human Perception," [1988 Proceedings] Second International Conference on Computer Vision, Tampa, FL, USA, 1988, pp. 404-413. doi: 10.1109/CCV.1988.590017
source
ShapeFromShading.PhotometricMethod
Photometric(illumination_direction1::Vector{T} where T <: Real = [Inf, Inf, Inf], illumination_direction2::Vector{T} where T <: Real = [Inf, Inf, Inf], illumination_direction3::Vector{T} where T <: Real = [Inf, Inf, Inf], integration_scheme::AbstractIntegrator = Horn())

Creates a Photometric functor which attempts to produce the gradients and integrate them to retrieve a height map using three-point photometric stereo.

This algorithm solves a system of linear equation formed by varying the lighting conditions on an object while holding the viewing angle constant. Note that the ilumination direction id defined negative of most other algorithms and as such inouts may need to be corrected (see example).

Output

Returns an M by N array (matching dimensions of original image) of Float Z that represents the reconstructed height at the point and the gradients in M by N arrays of Float p and q.

Details

Let ⁠ñ₁, ñ₂ and ñ₃ define the ilumination direction let N be a 3 X 3 matrix whos rows are formed by the row vectors of the illumination direction as per:

\[N=\begin{bmatrix}&n_{11}&n_{12}&n_{13}\\&n_{21}&n_{22}&n_{23}\\&n_{31}&n_{32}&n_{33}\end{bmatrix}\]

and I₁, I₂ and I₃ be the intensity values at a point (x,y) and let Ĩ=[I₁,I₂,I₃]′ be the column vector formed by these values.

The surface normal at the point (x,y) can now be directly calculated using:

\[ñ=N^{-1}Ĩ\]

where n is the surface normal.

Arguments

The function arguments are described in more detail below.

illumination_direction1

A Vector{T} where T <: Real that specifies the first illumination vector to be used by the algorithm. The illumination_direction should be a vector of the form [x,y,z] where x,y,z are int he range [0,1]. Defults to [Inf,Inf,Inf] which triggers estimate_img_properties to be called.

illumination_direction2

A Vector{T} where T <: Real that specifies the second illumination vector to be used by the algorithm. The illumination_direction should be a vector of the form [x,y,z] where x,y,z are int he range [0,1]. Defults to [Inf,Inf,Inf] which triggers estimate_img_properties to be called.

illumination_direction3

A Vector{T} where T <: Real that specifies the third illumination vector to be used by the algorithm. The illumination_direction should be a vector of the form [x,y,z] where x,y,z are int he range [0,1]. Defults to [Inf,Inf,Inf] which triggers estimate_img_properties to be called.

integration_scheme

A keywork argument of type AbstractIntegrator which specifies which integration scheme is to be used to reconstruct the surface from the calculated normals. Defutls to Horn().

Note

If any illumination_direction are not defined (i.e. have defulted to Inf) they will be calculated at runtime using estimate_img_properties.

using Images, Makie, ShapeFromShading

#generate synthetic images
n₁ = [0,0,1.0]
n₂ = [1.0,0.2,1.0]
n₃ = [0.2,0.9,1.0]
img1, img2, img3 = generate_photometric(SynthSphere(50), 1.0, n₁, n₂, n₃)

#calculate the heightmap
photometric = Photometric(-n₁, -n₂, -n₃, Horn())
Z,p,q = photometric(img1, img2, img3)

#normalize to maximum of 1 (not necessary but makes displaying easier)
Z = Z./maximum(Z)

#display using Makie (Note: Makie can often take several minutes first time)
r = 0.0:0.1:2
surface(r, r, Z)

Reference

  1. R. Woodham, "Photometric Method For Determining Surface Orientation From Multiple Images", Optical Engineering, vol. 19, no. 1, 1980. doi:10.1117/12.7972479
source
ShapeFromShading.ShahMethod
Shah(slant::Real = Inf, tilt::Real = Inf, iterations::Int = 200)

Creates a Shah fucntor which attempts to produce a heightmap from a grayscale image using Shah's algorithm.

Under the assumption that the albedo is constant, and the surface is Lambertian, the algorithm employs discrete approximations of p and q using finite differences in order to linearize the reflectance map in terms of Z by taking the first term of its Taylor series expansion.

Output

Returns an M by N array (matching dimensions of original image) of Float Z that represents the reconstructed height at the point and the gradients in M by N arrays of Float 'p' and 'q'.

Details

Given the reflectance map for the surface defined as bellow:

\[R(x,y)=\dfrac{-i_xp-i_yq+i_z}{\sqrt{1+p^2+q^2}}=\dfrac{\cos\sigma+p\cos\tau\sin \sigma+q\sin\tau\sin\sigma}{\sqrt{1+p^2+q^2}}\]

where $i_x=\frac{I(1)}{I(3)}=\frac{\cos\tau\sin\sigma}{\cos\sigma}=\cos\tau \tan\sigma$ and $i_y=\frac{I(2)}{I(3)}=\frac{\sin\tau\sin\sigma}{\cos\sigma} =\sin\tau\tan\sigma$.

and p and q are discretely approximated as:

\[\begin{gathered} p=Z(x,y)-Z(x-1,y)\\ q=Z(x,y)-Z(x,y-1)\\ \end{gathered}\]

Shah linearized the function $f=E-R=0$ in terms of $Z$ in the vicinity of $Z^{k-1}$ by crating a system of linear equations which can be solved iteratively using the Jacobi iterative scheme, simplifying the Taylor series expansion to the first order to get the following:

\[f(Z(x,y))=0\approx f(Z^{n-1}(x,y))+(Z(x,y)-Z^{n-1}(x,y))\dfrac{df(Z^{n-1}(x,y))} {dZ(x,y)}\]

which by letting $Z^n(x,y)=Z(x,y)$ gives:

\[Z^n(x,y=Z^{n-1}(x,y)-\dfrac{f(Z^{n-1}(x,y))}{\dfrac{df(Z^{n-1}(x,y))}{dZ(x,y)}}\]

where,

\[\dfrac{df(Z^{n-1}(x,y))}{dZ(x,y)}=\dfrac{(p+q)(pi_x+qi_y+1)}{\sqrt{(1+p^2+q^2)^3} \sqrt{1+i_x+i_y}}-\dfrac{i_x+i_y}{\sqrt{1+p^2+q^2}\sqrt{1+i_x+i_y}}\]

which as $Z^0(x,y)=0$, allows the algorithm to iteratively solve for $Z(x,y)$.

Arguments

The function arguments are described in more detail below.

iterations

An Int that specifies the number of iterations the algorithm is to perform. If left unspecified a default value of 200 is used.

slant

A Real that specifies the slant value to be used by the algorithm. The slant should be a value in the range [0,π/2]. If slant is specified to must the tilt. Will defult to Inf which will trigger estimate_img_properties to be run.

tilt

A Real that specifies the tilt value to be used by the algorithm. The tilt should be a value in the range [0,2π]. If tilt is specified to must the slant. Will defult to Inf which will trigger estimate_img_properties to be run.

Note

If slant and tilt are not defined (i.e. have defulted to Inf) they will be calculated at runtime using estimate_img_properties.

Example

Compute the heightmap for a synthetic image generated by generate_surface.

using Images, Makie, ShapeFromShading

#generate synthetic image
img = generate_surface(SynthSphere(50), 1.0, [0.1,0,0.9])

#calculate the heightmap
shah = Shah(slant = 0.1106572211739, tilt = 0.0)
Z = shah(img)

#normalize to maximum of 1 (not necessary but makes displaying easier)
Z = Z./maximum(Z)

#display using Makie (Note: Makie can often take several minutes first time)
r = 0.0:0.1:2
surface(r, r, Z)

Reference

  1. T. Ping-Sing and M. Shah, "Shape from shading using linear approximation", Image and Vision Computing, vol. 12, no. 8, pp. 487-498, 1994. doi:10.1016/0262-8856(94)90002-7
source
ShapeFromShading.DeterministicMethod
Deterministic(p::AbstractArray, q::AbstractArray, integration_factor::Real = 10, smoothness::Real = 20, β::Real = 1.0, integration_scheme::AbstractIntegrator = Horn())

Impliments a deterministic method for reconstructing the surface using a gradient descent method. This version makes use of a Optim.jl solver.

Output

Returns a Deterministic functor which can be called on an image to reconstruct the surface normals from the image.

Details

Under the assumption that the object is illuminated from directly above ([0,0,1]) and that the surface is Lambertian, the eikonal equation, shown below, can be used to describe the surface in an image.

\[E_{i,j}=\frac{E_{max}}{\sqrt{p_{i,j}^2+q_{i,j}^2+1}}\]

As the surface must be integrable, the integrability condition; $p_y=q_x$ must hold on the surface. Using this a discreate itegrability function can be derived in the form:

\[p_{i,j+1}-p_{i,j}=q_{i+1,j}-q_{i,j}\]

A discreate functional for the energy can now be derived from these in the form:

\[\begin{aligned} \epsilon&=\sum_{i,j\in\Omega}\left[\frac{E_{max}}{\sqrt{p_{i,j}^2+q_{i,j}^2+1}}-E_{i,j}\right]^2\\ &+\lambda_i\sum_{i,j\in\Omega}[(p_{i,j+1}-p_{i,j})-(q_{i+1,j}-q_{i,j})]^2 \end{aligned}\]

Finally a smoothness term can be introduced as below. This both aids in the quality of the reconstruction and reduces the number of minima in the functional helping to ensure a good solution is found.

\[\begin{aligned} \epsilon&=\sum_{i,j\in\Omega}\left[\frac{E_{max}}{\sqrt{p_{i,j}^2+q_{i,j}^2+1}}-E_{i,j}\right]^2\\ &+\lambda_i\sum_{i,j\in\Omega}[(p_{i,j+1}-p_{i,j})-(q_{i+1,j}-q_{i,j})]^2\\ &+\lambda_i\sum_{i,j\in\Omega}[(p_{i,j+1}-p_{i,j})^2+(p_{i+1,j}-p_{i,j})^2+(q_{i+1,j}-q_{i,j})^2+(q_{i,j+1}-q_{i,j})^2] \end{aligned}\]

The problem is then minimized using gradient decsent method to attampt to find a solution minimizing the energy function. Gradient descent is terminated when $|\nabla\epsilon|<\beta\sqrt{2|\Omega|}$.

Parameters

p

An AbstractArray which contains initial solution for p, the x gradient of the surface. Better inital solution will produce better results.

q

An AbstractArray which contains initial solution for q, the y gradient of the surface. Better inital solution will produce better results.

integration_factor

A Real which controls the impact of the integration term on the energy function. Can be used to tune the final solution. Defults to 10.

smoothness

A Real which controls the impact of the smoothness term on the energy function. Can be used to tune the final solution. Defults to 20.

β

A Real which controls the value of $|\nabla\epsilon|$ which will terminate the algorithm. If algorithm terminates on first iteration value will need reducing and if algorithm fails to converge the value will need increasing. Defults to 1.0.

integration_scheme

A AbstractIntegrator which will be used to integrate the calculated gradients. Defults to Horn().

Example

The following example demonstrates the use of the Deterministic method.

using ShapeFromShading, Makie, Images

#generate synthetic image and initial solution
img = generate_surface(SynthSphere(38))
pin, qin = synthetic_gradient(SynthSphere(38))

#calculate the heightmap
deterministic = Deterministic(p=pin,q=qin, integration_factor = 10, smoothness = 50, β = 1.0, integration_scheme = Horn())
Z,p,q = deterministic(img)

#normalize to maximum of 1 (not necessary but makes displaying easier)
Z = Z./maximum(abs.(Z))

#display using Makie (Note: Makie can often take several minutes first time)
r = 0.0:0.1:4
surface(r, r, Z)

Reference

[1] A. Crouzil, X. Descombes and J. Durou, "A multiresolution approach for shape from shading coupling deterministic and stochastic optimization," in IEEE Transactions on Pattern Analysis and Machine Intelligence, vol. 25, no. 11, pp. 1416-1421, Nov. 2003. doi: 10.1109/TPAMI.2003.1240116

source
ShapeFromShading.DeterministicCustomMethod
DeterministicCustom(p::AbstractArray, q::AbstractArray, integration_factor::Real = 10, smoothness::Real = 20, β::Real = 1.0, integration_scheme::AbstractIntegrator = Horn())

Same as Deterministic exept impliments a custom gradient descent and line search algorithm.

Example

The following example demonstrates the use of the DeterministicCustom method.

using ShapeFromShading, Makie, Images

#generate synthetic image and initial solution
img = generate_surface(SynthSphere(38))
pin, qin = synthetic_gradient(SynthSphere(38))

#calculate the heightmap
deterministicCustom = DeterministicCustom(p=pin,q=qin, integration_factor = 10, smoothness = 50, β = 1.0, integration_scheme = Horn())
Z,p,q = deterministicCustom(img)

#normalize to maximum of 1 (not necessary but makes displaying easier)
Z = Z./maximum(abs.(Z))

#display using Makie (Note: Makie can often take several minutes first time)
r = 0.0:0.1:4
surface(r, r, Z)

Reference

[1] A. Crouzil, X. Descombes and J. Durou, "A multiresolution approach for shape from shading coupling deterministic and stochastic optimization," in IEEE Transactions on Pattern Analysis and Machine Intelligence, vol. 25, no. 11, pp. 1416-1421, Nov. 2003. doi: 10.1109/TPAMI.2003.1240116

source

Benchmarking:

Miscellaneous: