Skip to content

replace current seam carving implementation (#334) - #801

Open
Rynoxx wants to merge 2 commits into
image-rs:mainfrom
Rynoxx:feature/improved-seam-carving
Open

replace current seam carving implementation (#334)#801
Rynoxx wants to merge 2 commits into
image-rs:mainfrom
Rynoxx:feature/improved-seam-carving

Conversation

@Rynoxx

@Rynoxx Rynoxx commented Jul 16, 2026

Copy link
Copy Markdown

Fixes #334

For the past few months I've been working on-and-off on a seam carving implementation based on the suggested paper
https://web.archive.org/web/20120404113111/https://users.cs.cf.ac.uk/Paul.Rosin/resources/papers/seam-carving-ChinaF.pdf

Since the new algorithm allows for finding and removing all seams simultanioulsy I chose to adjust the public API to deprecate the old find_vertical_seam and remove_vertical_seam, while adding two new equivalents (find_vertical_seams, remove_vertical_seams) for VerticalSeams. It might make sense to make remove_vertical_seam non-deprecated, because I've kept VerticalSeam and draw_vertical_seam in case people want to do anything with the individual seams.
The signature for shrink_width stays the same.

Most design choices here have been led by an intention to optimize performance while keeping accuracy.
Where benchmarks found it beneficial sections have been made into branchless and zip:ed iterators instead of indexing into a vec/slice.
This has also led to some parts which might be slightly overkill/overengineered.

AI Disclaimer:
I've been using AI quite extensively to help me understand the research paper, how to implement the algorithms described and create tests.
All of the generated code has been reviewed by me, refactored, tested and benchmarked.

Benchmarks:

Benchmarks were ran on Linux with a Ryzen 9 5900XT and 32GB of RAM.

Compared to imagemagick it's significantly faster (37x?) when removing 500px from a 4080x3072, and 7.5x faster when removing 50px from a 300x188 image.
Almost unnecessary to compare to old implementation for larger sizes.

The cat bench suite is using the same image as my manual runs, which is the comparison image attached below.

# Shrinking a picture of my cat by 500px
> time magick tests/data/test-cat.jpg \
            -liquid-rescale "3580x3072!" \
            ./output/imagemagick_shrunk_r500.jpg
________________________________________________________
Executed in    9.90 secs    fish           external
   usr time    6.88 secs    0.00 micros    6.88 secs
   sys time    2.98 secs  664.00 micros    2.98 secs

# New impl
> time target/release/examples/seam_carving tests/data/test-cat.jpg ./output 500 true
________________________________________________________
Executed in  261.24 millis    fish           external
   usr time  191.02 millis  431.00 micros  190.59 millis
   sys time   47.77 millis  117.00 micros   47.65 millis

# Old impl
> time target/release/examples/seam_carving tests/data/test-cat.jpg ./output-old
Removing seam 0
....
Removing seam 499

________________________________________________________
Executed in  231.45 secs    fish           external
   usr time  223.28 secs    0.00 millis  223.28 secs
   sys time    7.63 secs    1.05 millis    7.63 secs

# Shrinking elephant from the test-data by 50px
> time magick tests/data/elephant.png \
            -liquid-rescale "250x188!" \
            ./output-elephant/imagemagick_shrunk_r50.jpg
________________________________________________________
Executed in   27.00 millis    fish           external
   usr time   14.69 millis  679.00 micros   14.01 millis
   sys time    6.00 millis    0.00 micros    6.00 millis

# New impl
> time target/release/examples/seam_carving tests/data/elephant.png ./output-elephant 50 true
________________________________________________________
Executed in    3.59 millis    fish           external
   usr time    1.05 millis    0.00 micros    1.05 millis
   sys time    2.70 millis  590.00 micros    2.11 millis

# Old impl
> time target/release/examples/seam_carving tests/data/elephant.png ./output-old-elephant
Removing seam 0
....
Removing seam 49

________________________________________________________
Executed in  114.71 millis    fish           external
   usr time  100.72 millis  460.00 micros  100.26 millis
   sys time   14.04 millis    0.00 micros   14.04 millis
> cargo +nightly bench seam_carving
test seam_carving::benches::cat::bench_compute_bottom_up                              ... bench:   9,247,930.85 ns/iter (+/- 1,385,202.38)
test seam_carving::benches::cat::bench_compute_energy                                 ... bench:   2,550,779.65 ns/iter (+/- 595,896.05)
test seam_carving::benches::cat::bench_compute_intensity                              ... bench:  17,371,486.70 ns/iter (+/- 749,429.94)
test seam_carving::benches::cat::bench_compute_vertical_seams                         ... bench:  36,042,803.30 ns/iter (+/- 1,306,838.10)
test seam_carving::benches::cat::shrink_width_r500::bench_remove_seams                ... bench:  12,670,289.20 ns/iter (+/- 1,513,824.25)
test seam_carving::benches::cat::shrink_width_r500::bench_shrink_width                ... bench:  82,125,760.20 ns/iter (+/- 2,896,338.02)
test seam_carving::benches::elephant::bench_compute_bottom_up                         ... bench:      16,898.20 ns/iter (+/- 169.55)
test seam_carving::benches::elephant::bench_compute_energy                            ... bench:       8,820.93 ns/iter (+/- 29.08)
test seam_carving::benches::elephant::bench_compute_intensity                         ... bench:      77,555.00 ns/iter (+/- 2,685.68)
test seam_carving::benches::elephant::bench_compute_vertical_seams                    ... bench:     141,221.28 ns/iter (+/- 482.54)
test seam_carving::benches::elephant::shrink_width_r100::bench_remove_seams           ... bench:     108,148.74 ns/iter (+/- 1,421.44)
test seam_carving::benches::elephant::shrink_width_r100::bench_shrink_width           ... bench:     363,649.15 ns/iter (+/- 2,363.43)
test seam_carving::benches::grey_1000x1000::bench_compute_bottom_up                   ... bench:     321,438.32 ns/iter (+/- 4,023.82)
test seam_carving::benches::grey_1000x1000::bench_compute_energy                      ... bench:     146,044.76 ns/iter (+/- 1,428.16)
test seam_carving::benches::grey_1000x1000::bench_compute_intensity                   ... bench:      27,080.03 ns/iter (+/- 3,032.40)
test seam_carving::benches::grey_1000x1000::bench_compute_vertical_seams              ... bench:   1,318,086.32 ns/iter (+/- 8,336.71)
test seam_carving::benches::grey_1000x1000::shrink_width_r100::bench_remove_seams     ... bench:     429,013.41 ns/iter (+/- 69,223.39)
test seam_carving::benches::grey_1000x1000::shrink_width_r100::bench_shrink_width     ... bench:   3,954,277.25 ns/iter (+/- 130,535.51)
test seam_carving::benches::grey_100x100::bench_compute_bottom_up                     ... bench:       3,503.05 ns/iter (+/- 37.68)
test seam_carving::benches::grey_100x100::bench_compute_energy                        ... bench:       1,854.12 ns/iter (+/- 21.47)
test seam_carving::benches::grey_100x100::bench_compute_intensity                     ... bench:         224.20 ns/iter (+/- 1.91)
test seam_carving::benches::grey_100x100::bench_compute_vertical_seams                ... bench:      12,992.38 ns/iter (+/- 278.93)
test seam_carving::benches::grey_100x100::shrink_width_r1::bench_remove_seams         ... bench:         872.26 ns/iter (+/- 6.95)
test seam_carving::benches::grey_100x100::shrink_width_r1::bench_shrink_width         ... bench:      19,485.48 ns/iter (+/- 187.63)
test seam_carving::benches::grey_100x100::shrink_width_r25::bench_remove_seams        ... bench:      10,717.75 ns/iter (+/- 171.62)
test seam_carving::benches::grey_100x100::shrink_width_r25::bench_shrink_width        ... bench:      28,514.71 ns/iter (+/- 287.51)
test seam_carving::benches::grey_100x100::shrink_width_r4::bench_remove_seams         ... bench:       2,161.34 ns/iter (+/- 17.36)
test seam_carving::benches::grey_100x100::shrink_width_r4::bench_shrink_width         ... bench:      20,821.92 ns/iter (+/- 387.65)
test seam_carving::benches::grey_100x100::shrink_width_r8::bench_remove_seams         ... bench:       3,485.43 ns/iter (+/- 26.79)
test seam_carving::benches::grey_100x100::shrink_width_r8::bench_shrink_width         ... bench:      22,179.99 ns/iter (+/- 353.98)
test seam_carving::benches::grey_3000x3000::bench_compute_bottom_up                   ... bench:   6,057,317.90 ns/iter (+/- 220,219.47)
test seam_carving::benches::grey_3000x3000::bench_compute_energy                      ... bench:   1,346,268.89 ns/iter (+/- 122,741.10)
test seam_carving::benches::grey_3000x3000::bench_compute_intensity                   ... bench:     244,019.40 ns/iter (+/- 23,569.68)
test seam_carving::benches::grey_3000x3000::bench_compute_vertical_seams              ... bench:  12,544,676.60 ns/iter (+/- 145,571.47)
test seam_carving::benches::grey_3000x3000::shrink_width_r300::bench_remove_seams     ... bench:   3,728,934.00 ns/iter (+/- 113,276.71)
test seam_carving::benches::grey_3000x3000::shrink_width_r300::bench_shrink_width     ... bench:  27,504,949.10 ns/iter (+/- 489,655.02)
test seam_carving::benches::grey_500x500::bench_compute_bottom_up                     ... bench:      81,724.02 ns/iter (+/- 3,103.73)
test seam_carving::benches::grey_500x500::bench_compute_energy                        ... bench:      36,527.41 ns/iter (+/- 257.49)
test seam_carving::benches::grey_500x500::bench_compute_intensity                     ... bench:       5,689.85 ns/iter (+/- 86.30)
test seam_carving::benches::grey_500x500::bench_compute_vertical_seams                ... bench:     329,857.90 ns/iter (+/- 1,954.25)
test seam_carving::benches::grey_500x500::shrink_width_r50::bench_remove_seams        ... bench:     102,315.32 ns/iter (+/- 1,940.58)
test seam_carving::benches::grey_500x500::shrink_width_r50::bench_shrink_width        ... bench:     560,578.10 ns/iter (+/- 6,314.49)
test seam_carving::benches::robin::bench_compute_bottom_up                            ... bench:      69,457.01 ns/iter (+/- 2,326.24)
test seam_carving::benches::robin::bench_compute_energy                               ... bench:      31,663.81 ns/iter (+/- 160.69)
test seam_carving::benches::robin::bench_compute_intensity                            ... bench:     288,458.30 ns/iter (+/- 2,850.31)
test seam_carving::benches::robin::bench_compute_vertical_seams                       ... bench:     575,608.30 ns/iter (+/- 8,339.79)
test seam_carving::benches::robin::shrink_width_r100::bench_remove_seams              ... bench:     317,979.00 ns/iter (+/- 10,825.21)
test seam_carving::benches::robin::shrink_width_r100::bench_shrink_width              ... bench:   1,298,966.10 ns/iter (+/- 11,470.22)
test seam_carving::benches::zebra::bench_compute_bottom_up                            ... bench:      12,794.13 ns/iter (+/- 130.77)
test seam_carving::benches::zebra::bench_compute_energy                               ... bench:       6,329.63 ns/iter (+/- 22.65)
test seam_carving::benches::zebra::bench_compute_intensity                            ... bench:      52,548.54 ns/iter (+/- 405.30)
test seam_carving::benches::zebra::bench_compute_vertical_seams                       ... bench:      90,921.94 ns/iter (+/- 2,385.26)
test seam_carving::benches::zebra::shrink_width_r100::bench_remove_seams              ... bench:      88,044.78 ns/iter (+/- 6,396.56)
test seam_carving::benches::zebra::shrink_width_r100::bench_shrink_width              ... bench:     268,827.75 ns/iter (+/- 7,150.61)
test seam_carving::old::benches::bench_shrink_width_elephant_r100                     ... bench: 176,691,215.60 ns/iter (+/- 4,208,874.93)
test seam_carving::old::benches::bench_shrink_width_s100_r1                           ... bench:     193,372.80 ns/iter (+/- 2,805.91)
test seam_carving::old::benches::bench_shrink_width_s100_r4                           ... bench:     759,016.00 ns/iter (+/- 5,353.67)
test seam_carving::old::benches::bench_shrink_width_s100_r8                           ... bench:   1,492,431.90 ns/iter (+/- 6,733.60)

Comparison images:
Original:
test-cat

Imagemagick:
imagemagick_shrunk_500
Old imageproc implementation:
shrunk-old
This implementation:
shrunk-new

@Rynoxx
Rynoxx force-pushed the feature/improved-seam-carving branch from f02e286 to e338cee Compare July 16, 2026 15:50
@Rynoxx
Rynoxx force-pushed the feature/improved-seam-carving branch from e338cee to c47bfc8 Compare July 17, 2026 21:13
@Rynoxx
Rynoxx force-pushed the feature/improved-seam-carving branch from 4544151 to 7b51446 Compare July 18, 2026 16:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Alternative seam carving implementation

1 participant