# import a bunch of stuff that we'll use to manipulate our images...
from skimage.io import imread
from skimage import filters
import numpy as np

from bokeh.plotting import figure, show
from bokeh.io import output_notebook

output_notebook()

"""
Explanation: Image Processing
Math with Pictures
End of explanation
"""


test_image = imread("img/frame-0.jpg", as_grey=True)

test_image.shape # how big is the image?

p = figure(plot_width=480, plot_height=320, x_range=(0, 10), y_range=(0, 10))
p.image(image=[test_image[::-1]], x=[0], y=[0], dw=[10], dh=[10])

show(p)

"""
Explanation: Load in an image that we've already captured with our camera:
End of explanation
"""


test_image2 = imread("img/frame-1.jpg", as_grey=True)

p2 = figure(plot_width=480, plot_height=320, x_range=(0, 10), y_range=(0, 10))
p2.image(image=[test_image2[::-1]], x=[0], y=[0], dw=[10], dh=[10])

show(p2)

test_difference = test_image - test_image2

p3 = figure(plot_width=480, plot_height=320, x_range=(0, 10), y_range=(0, 10))
p3.image(image=[test_difference[::-1]], x=[0], y=[0], dw=[10], dh=[10])

show(p3)

"""
Explanation: So, let's compare this to another image:
End of explanation
"""


image_roberts = filters.edges.roberts(test_image)

p_roberts = figure(plot_width=480, plot_height=320, x_range=(0, 10), y_range=(0, 10))
p_roberts.image(image=[image_roberts[::-1]], x=[0], y=[0], dw=[10], dh=[10])

show(p_roberts)

image_wiener = filters.frangi(test_image)

p_wiener = figure(plot_width=480, plot_height=320, x_range=(0, 10), y_range=(0, 10))
p_wiener.image(image=[image_wiener[::-1]], x=[0], y=[0], dw=[10], dh=[10])

show(p_wiener)

"""
Explanation: How about some more complicated math?
Start with edge detection:
End of explanation
"""


from scipy import ndimage as ndi
from skimage.morphology import watershed, disk
from bokeh.palettes import Spectral6

# denoise image
denoised = filters.rank.median(test_image, disk(2))

# find continuous region (low gradient -
# where less than 10 for this image) --> markers
# disk(5) is used here to get a more smooth image
markers = filters.rank.gradient(denoised, disk(5)) < 10
markers = ndi.label(markers)[0]

# local gradient (disk(2) is used to keep edges thin)
gradient = filters.rank.gradient(denoised, disk(2))

# process the watershed
labels = watershed(gradient, markers)


p_gradient = figure(plot_width=480, plot_height=320, x_range=(0, 10), y_range=(0, 10))
p_gradient.image(image=[gradient[::-1]], x=[0], y=[0], dw=[10], dh=[10], palette=Spectral6, alpha=.7)

show(p_gradient)

p_labels = figure(plot_width=480, plot_height=320, x_range=(0, 10), y_range=(0, 10))
p_labels.image(image=[labels[::-1]], x=[0], y=[0], dw=[10], dh=[10], palette=Spectral6, alpha=.7)

show(p_labels)

"""
Explanation: Watershed algorithm
The watershed is a classical algorithm used for segmentation, that is, for separating different objects in an image.
Here a marker image is built from the region of low gradient inside the image. In a gradient image, the areas of high values provide barriers that help to segment the image. Using markers on the lower values will ensure that the segmented objects are found.  It's like flooding the image with water according to the gradient of the color changes.
End of explanation
"""


