@doc raw"""
```
Z,p,q = retrieve_surface(algorithm::DiscreteShapeBound, img::AbstractArray, iterations::Int=2000)
```

Same as [`DiscreteShape`](@ref retrieve_surface(::DiscreteShape, ::AbstractArray, ::iterations))
except it has its initial conditions bound by the image as per bellow where
``E`` is the brightness of the image:
```math
\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`.
```julia
using Images, Makie, ShapeFromShading

#generate synthetic image
img = generate_surface(SynthSphere(), 1, [0.2,0,0.9], radius = 5)

#calculate the heightmap (using 500 iterations)
Z,p,q = retrieve_surface(DiscreteShapeBound(), img, 500)

#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.
"""
function retrieve_surface(algorithm::DiscreteShapeBound, img::AbstractArray, iterations::Int=2000; smoothness::Int=1000)
    ρ,I,σ,τ = estimate_img_properties(img)
    λ = smoothness
    return retrieve_surface(DiscreteShapeBound(), img, ρ, I, iterations, smoothness=λ)
end

function retrieve_surface(algorithm::DiscreteShapeBound, img::AbstractArray, albedo::Real, illumination_direction::Vector{T} where T <: Real, iterations::Int=2000; smoothness::Int=1000)
    ρ = albedo
    I = illumination_direction
    E = Array{Float64}(img)

    #initialize variables
    p = zeros(Complex{Float64},axes(E))
    q = zeros(Complex{Float64},axes(E))
    R = zeros(Complex{Float64},axes(E))
    Z = zeros(axes(E))

    #apply bounding conditions
    for i in CartesianIndices(E)
        if E[i] > 0.75
            Z[i] = -100*E[i]
        else
            Z[i] = 0.0
        end
    end
    q, p = Array{Complex{Float64}}.(imgradients(Z, KernelFactors.sobel, "replicate"))
    return solve_EulerLagrange(ρ, I, iterations, p, q, R, smoothness, E, Z)
end
