In this notebook I want to take an image, break it into regions, and tile it with the maximum color from that region. It is an experiment in computational photography!
In [6]:
using Colors, Images, TestImages;
The one thing I find hard to get used to with Julia is that I don't need to explicitly call functions on module names, like in Python. Just a small detail, but I'll get used to it.
In [8]:
img = testimage("lighthouse");
Here's some documentation about Arrays, which are what images are represented as: http://docs.julialang.org/en/stable/manual/arrays/
In [ ]:
# find the size of the image
println(size(img))
In [11]:
summary(img)
Out[11]:
In [12]:
# create a random dense array the same size as the image
# it gets initialized with random numbers, which makes a really cool pattern!
tiled_img = Array{RGB{N0f8}, 2}(size(img))
Out[12]:
Another way you can initialize an array like another:
In [13]:
tiled_img = similar(img);
Like in IPython, you can add a "?" at the end of a function. In Julia, you put the question mark at the start of a function.
In [114]:
?linspace
Out[114]:
In [29]:
linspace(1, size(img, 1), 8)
Out[29]:
In [44]:
Int16.(linspace(1, size(img, 1), 8))
Out[44]:
In [42]:
Int16[i for i in linspace(1, size(img, 1), 8)]
Out[42]:
You can generate x, y tile indices with an interator-like thing or a generator-like thing.
In [52]:
# or to directly generate ints
1:8:size(img, 1)
Out[52]:
In [54]:
typeof(1:8:size(img, 1))
Out[54]:
And the visualizations for strips of images is awesome!
In [56]:
img[1:8:size(img, 1)]
Out[56]:
If you use these generators to index in both directions, what it basically does is tile the image and downsample.
In [62]:
stepsize = 16;
img[1:stepsize:size(img, 1), 1:stepsize:size(img, 2)]
Out[62]:
In [81]:
maximum(green.(img[1:10]))
Out[81]:
In [83]:
maximum(green.(img[1:16, 1:16]))
Out[83]:
And now let's put it all together to take the max R, G, and B values in a block.
In [99]:
for x in 1:stepsize:size(img, 1)
for y in 1:stepsize:size(img, 2)
x_end = min(x + stepsize, size(img, 1));
y_end = min(y + stepsize, size(img, 2));
# the @view takes a view into the image rather than making a copy
imgv = @view img[x:x_end, y:y_end]
tiled_img[x:x_end, y:y_end] = RGB{N0f8}(
maximum(red.(imgv)), maximum(green.(imgv)), maximum(blue.(imgv)));
end
end
And we can concatenate the images to put them size by side.
In [102]:
[img tiled_img]
Out[102]:
I can even put all this into a function to call separately. Small notes about functions:
stepsize= every time I want to pass stepsize.
In [103]:
function max_tiling(img; stepsize=16)
tiled_img = similar(img);
for x in 1:stepsize:size(img, 1)
for y in 1:stepsize:size(img, 2)
x_end = min(x + stepsize, size(img, 1));
y_end = min(y + stepsize, size(img, 2));
imgv = @view img[x:x_end, y:y_end];
tiled_img[x:x_end, y:y_end] = RGB{N0f8}(
maximum(red.(imgv)), maximum(green.(imgv)), maximum(blue.(imgv)));
end
end
tiled_img
end
Out[103]:
In [104]:
[img max_tiling(img, stepsize=16)]
Out[104]:
In [105]:
[img max_tiling(img, stepsize=8)]
Out[105]:
I am told that the interact.jl package (https://github.com/JuliaGizmos/Interact.jl) can let me make a slider to change the stepsize!
In [106]:
for x in 1:stepsize:size(img, 1)
for y in 1:stepsize:size(img, 2)
x_end = min(x + stepsize, size(img, 1));
y_end = min(y + stepsize, size(img, 2));
imgv = @view img[x:x_end, y:y_end]
tiled_img[x:x_end, y:y_end] = RGB{N0f8}(maximum(red.(imgv)), maximum(green.(imgv)), maximum(blue.(imgv)));
end
end
In [112]:
# Pkg.add("Interact")
In [113]:
using Interact;
The widget lets me manipulate the step size and re-generate images directly in the notebook! Highly recommend this package =)
In [111]:
@manipulate for s=[2^x for x in 0:6]
[img max_tiling(img, stepsize=s)]
end
Out[111]: