Seam Carving in Julia
"fox.jpg"
Functions
xxxxxxxxxx
1
1
using Images, ImageView, ImageFiltering, Statistics, ImageMagick, PlutoUI
brightness (generic function with 1 method)
x
1
# Convert RGB to brigthness
2
function brightness(img_element::AbstractRGB)
3
return mean((img_element.r + img_element.g + img_element.b))
4
end
find_energy (generic function with 1 method)
xxxxxxxxxx
1
# Find energy, how important a pixel is to the image.
2
function find_energy(img)
3
energy_x = imfilter(brightness.(img), Kernel.sobel()[2])
4
energy_y = imfilter(brightness.(img), Kernel.sobel()[1])
5
6
return sqrt.(energy_x.^2 + energy_y.^2)
7
end
normalize_greyness (generic function with 1 method)
xxxxxxxxxx
5
1
# To help view energy image, normalize values to 0-1
2
function normalize_greyness(array)
3
max, _ = findmax(array)
4
array = array./max
5
return array
6
end
grey_to_rgb (generic function with 1 method)
x
1
# Convert a value to RGB to view arrays as images
2
function grey_to_rgb(brigthness)::RGB
3
return RGB(brigthness, brigthness, brigthness)
4
end
find_seam_at (generic function with 1 method)
xxxxxxxxxx
10
1
# Finds the seam of least energy starting at given position
2
function find_seam_at(next_elements, element)
3
seam = zeros(Int, size(next_elements)[1])
4
seam[1] = element
5
6
for i = 2 :length(seam)
7
seam[i] = seam[i-1] + next_elements[i, seam[i-1]]
8
end
9
10
return seam
11
end
find_seam (generic function with 1 method)
1
# Finds the best starting point and the seam from there
2
function find_seam(energy)
3
energy_map, next_elements = find_energy_map(energy)
4
5
_, min_element = findmin(energy_map[1, :])
6
7
return find_seam_at(next_elements, min_element)
8
end
find_energy_map (generic function with 1 method)
1
# Calculate the combined energy needed for each pixel from bottom to top
2
function find_energy_map(energy)
3
energy_map = zeros(size(energy))
4
energy_map[ end, : ] = energy[ end, : ]
5
6
next_elements = zeros(Int, size(energy))
7
8
for i = size(energy)[1]-1 :-1:1
9
for j = 1 :size(energy)[2]
10
left = max(1, j-1)
11
right = min(j+1, size(energy)[2])
12
13
local_energy, next_element = findmin(energy_map[i+1, left:right])
14
energy_map[i,j] = local_energy + energy[i,j]
15
16
next_elements[i,j] = next_element - 2
17
18
if left == 1
19
next_elements[i,j] += 1
20
end
21
end
22
end
23
24
return energy_map, next_elements
25
end
draw_seam (generic function with 1 method)
xxxxxxxxxx
8
1
function draw_seam(image, seam)
2
image_with_seam = copy(image)
3
for i = 1 : size(image_with_seam)[1]
4
image_with_seam[i, seam[i]] = RGB(1, 0, 0)
5
end
6
7
return image_with_seam
8
end
remove_seam (generic function with 1 method)
xxxxxxxxxx
16
1
function remove_seam(img, seam)
2
img_res = (size(img)[1], size(img)[2]-1)
3
4
new_img = Array{RGB}(undef, img_res)
5
6
for i = 1 :length(seam)
7
if seam[i] > 1 && seam[i] < size(img)[2]
8
new_img[i, :] .= vcat(img[i, 1:seam[i]-1], img[i, seam[i]+1:end])
9
elseif seam[i] == 1
10
new_img[i, :] .= img[i, 2:end]
11
elseif seam[i] == size(img)[2]
12
new_img[i, :] .= img[i, 1:end-1]
13
end
14
end
15
return new_img
16
end
seam_carving (generic function with 1 method)
xxxxxxxxxx
12
1
function seam_carving(img, res)
2
if res < 0 || res > size(img)[2]
3
error("resolution not acceptable")
4
end
5
6
for i = (1:size(img)[2] - res)
7
energy = find_energy(img)
8
seam = find_seam(energy)
9
img = remove_seam(img, seam)
10
end
11
return img
12
end
get_all_carved (generic function with 1 method)
1
# Make all carved variants of the original image
2
function get_all_carved(img, amount)
3
4
if(amount < 0 || amount > size(img)[2])
5
error("amount not acceptable")
6
end
7
8
all_images = []
9
10
for i = (1:amount)
11
energy = find_energy(img)
12
seam = find_seam(energy)
13
img = remove_seam(img, seam)
14
push!(all_images, img)
15
end
16
return all_images
17
end
Examples
The original image
xxxxxxxxxx
1
begin
2
test_image = imresize(load(path_to_image), ratio=1/4.5)
3
test_image = RGB{Float32}.(test_image)
4
end
459
675
xxxxxxxxxx
1
1
size(test_image)
459×675 Array{Float64,2}:
0.08230626182890809 0.03924633733582069 … 0.12013708121153048
0.08204016971261124 0.03615509192693736 0.1452321975682269
0.06117055721409237 0.07197040239680258 0.08148767262441961
0.1579527126513256 0.13794121945113755 0.05316306337434861
0.14057949647072956 0.07945411130085152 0.03812833195152889
0.21019070310238272 0.12611102359351106 … 0.16439778998489007
0.15806071985941766 0.05101811349756884 0.09896866880767466
⋮ ⋱
0.030956177996254306 0.03615507091560697 0.062391724730204975
0.06907769979945161 0.03448138488543452 0.0576724554581597
0.023529440164585358 0.02977717948065177 … 0.04000427991459951
0.04742507232365957 0.05030185791220897 0.057107305327577386
0.02630661388029809 0.03553835112719368 0.027116291064753127
0.024956750583578716 0.037203320918664774 0.04185369598012464
xxxxxxxxxx
1
1
energy = find_energy(test_image)
This image below shows the "energy" or how important each pixel is
xxxxxxxxxx
1
1
energy_image = grey_to_rgb.(energy)
459×675 Array{Float64,2}: 13.4415 13.3322 13.4144 13.4028 … 5.53075 5.49632 5.52851 13.4258 13.3592 13.293 13.3202 5.44132 5.40837 5.46554 13.3532 13.3438 13.323 13.2763 5.42115 5.32031 5.36379 13.3927 13.292 13.2718 13.2658 5.30259 5.32483 5.28231 13.4433 13.2347 13.1541 13.1492 5.29658 5.22914 5.23736 13.4577 13.3027 13.1553 13.1291 … 5.16706 5.19923 5.22769 13.3546 13.2475 13.1766 13.0791 5.03396 5.06329 5.20093 ⋮ ⋱ 0.14147 0.136973 0.145926 0.113835 0.13022 0.143386 0.195931 0.15812 0.110514 0.100818 0.103997 0.0959613 0.133539 0.176819 0.109384 0.0890423 0.0760325 0.0661278 … 0.0894212 0.119147 0.137979 0.0961168 0.0858545 0.0592651 0.0642783 0.0850369 0.0979748 0.0989295 0.0512634 0.0486917 0.0355526 0.0694598 0.0518985 0.0478337 0.0418222 0.0249568 0.0372033 0.0131534 0.0587131 0.00623912 0.0147059 0.0418537
459×675 Array{Int64,2}: 1 2 0 -1 0 -1 1 0 1 1 1 … 0 -1 0 -1 1 0 -1 1 0 -1 1 2 1 0 1 0 1 0 -1 1 1 -1 1 1 1 0 1 0 -1 0 -1 1 2 1 1 1 0 -1 -1 -1 1 1 -1 -1 1 1 0 -1 1 0 1 0 1 2 1 0 1 1 0 -1 -1 -1 1 -1 1 0 -1 -1 0 -1 1 0 -1 1 2 1 0 -1 1 1 0 -1 -1 1 -1 0 -1 -1 1 1 0 -1 -1 -1 1 2 1 0 1 1 1 1 1 0 -1 … 1 0 -1 -1 1 1 1 0 -1 -1 1 1 1 1 0 1 1 0 -1 1 1 0 -1 -1 0 1 1 0 -1 -1 -1 ⋮ ⋮ ⋮ ⋱ ⋮ ⋮ 1 2 0 1 0 -1 1 0 1 1 0 0 -1 -1 -1 0 -1 0 -1 -1 -1 1 2 1 0 -1 1 0 -1 1 0 -1 -1 -1 -1 -1 -1 0 -1 0 -1 -1 1 2 0 -1 1 0 -1 -1 1 0 -1 … 0 -1 -1 -1 -1 0 -1 0 -1 -1 1 2 0 -1 1 1 0 -1 1 1 0 -1 -1 -1 -1 -1 1 0 1 1 0 0 2 0 -1 -1 1 0 -1 -1 1 0 -1 0 -1 0 -1 1 1 0 -1 -1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
xxxxxxxxxx
1
1
energy_map, next_elements = find_energy_map(energy)
This image below shows the the combined energy needed for a pixel at that position.
x
1
begin
2
normalized = normalize_greyness(energy_map)
3
energy_map_img = grey_to_rgb.(normalized)
4
end
647
647
648
647
648
648
647
647
647
647
647
646
647
648
648
649
650
650
650
651
652
651
651
652
652
653
654
653
654
655
656
657
656
655
655
655
655
656
657
658
664
663
663
663
663
664
664
665
664
664
xxxxxxxxxx
1
1
best_seam = find_seam(energy)
Here the first best seam marked in red
xxxxxxxxxx
1
1
image_with_best_seam = draw_seam(test_image, best_seam)
xxxxxxxxxx
4
1
begin
2
#max_reduction = size(test_image)[2] * 0.0
3
#all_carved_images = get_all_carved(test_image, max_reduction)
4
end
xxxxxxxxxx
4
1
begin
2
# width = length(all_carved_images)
3
#@bind new_width Slider(1:width, default=1, show_value=true)
4
end
With all the carved variants we can preview each one in series.
Commented out to keep the html preview smaller. Refer to video on my website
xxxxxxxxxx
1
1
Text("With all the carved variants we can preview each one in series.
2
Commented out to keep the html preview smaller. Refer to video on my website")
x
1
#all_carved_images[new_width]
xxxxxxxxxx
1
1
test_image