diff options
| author | Carl Hetherington <cth@carlh.net> | 2026-04-16 22:05:24 +0200 |
|---|---|---|
| committer | Carl Hetherington <cth@carlh.net> | 2026-04-16 22:05:24 +0200 |
| commit | 9bbd8891f9d043fd96a565c74016ee29143899cc (patch) | |
| tree | 98185ef6bd15fd40cc223e01fd1956c63f627b18 | |
| parent | 2fc3908ca3d7a01aee43db7e28f99fb6ec553a68 (diff) | |
Add --fill-crop to create CLI (#3008).
This can be used to extract images from letterboxed/pillarboxed frames.
| -rw-r--r-- | src/lib/create_cli.cc | 32 | ||||
| -rw-r--r-- | src/lib/create_cli.h | 1 | ||||
| -rw-r--r-- | test/create_cli_test.cc | 16 |
3 files changed, 49 insertions, 0 deletions
diff --git a/src/lib/create_cli.cc b/src/lib/create_cli.cc index 32834be23..3ef61fe11 100644 --- a/src/lib/create_cli.cc +++ b/src/lib/create_cli.cc @@ -85,6 +85,8 @@ help() " --left-eye next piece of content is for the left eye\n" " --right-eye next piece of content is for the right eye\n" " --auto-crop next piece of content should be auto-cropped\n" + " --fill-crop next piece of content should be cropped to fit the container\n" + " (e.g. to crop the letterboxing from a scope-in-flat image)\n" " --colourspace next piece of content is in the given colourspace: " + colour_conversions + "\n" " --colorspace same as --colourspace\n" " --channel <channel> next piece of content should be mapped to audio channel L, R, C, Lfe, Ls, Rs, BsL, BsR, HI, VI\n" @@ -184,6 +186,7 @@ CreateCLI::CreateCLI(int argc, char* argv[]) optional<string> next_colour_conversion; auto next_frame_type = VideoFrameType::TWO_D; auto next_auto_crop = false; + auto next_fill_crop = false; optional<dcp::Channel> channel; optional<float> gain; optional<float> fade_in; @@ -225,6 +228,9 @@ CreateCLI::CreateCLI(int argc, char* argv[]) } else if (a == "--auto-crop") { next_auto_crop = true; claimed = true; + } else if (a == "--fill-crop") { + next_fill_crop = true; + claimed = true; } else if (a == "--twok") { _twok = true; claimed = true; @@ -331,6 +337,7 @@ CreateCLI::CreateCLI(int argc, char* argv[]) c.path = a; c.frame_type = next_frame_type; c.auto_crop = next_auto_crop; + c.fill_crop = next_fill_crop; c.colour_conversion = next_colour_conversion; c.channel = channel; c.gain = gain; @@ -341,6 +348,7 @@ CreateCLI::CreateCLI(int argc, char* argv[]) content.push_back(c); next_frame_type = VideoFrameType::TWO_D; next_auto_crop = false; + next_fill_crop = false; next_colour_conversion = {}; channel = {}; gain = {}; @@ -555,6 +563,30 @@ CreateCLI::make_film(function<void (string)> error) const video->set_crop(crop); } + if (cli_content.fill_crop && video->size()) { + auto const source_ratio = video->size()->ratio(); + Crop crop; + if (source_ratio < film->container().ratio()) { + /* Part to extract is wider than the source */ + auto const height = video->size()->width / film->container().ratio(); + crop.top = crop.bottom = (video->size()->height - height) / 2; + } else { + /* Container is wider than the source */ + auto const width = video->size()->height * film->container().ratio(); + crop.left = crop.right = (video->size()->width - width) / 2; + } + + error(fmt::format( + "Cropped {} to {} left, {} right, {} top and {} bottom", + film_content->path(0).string(), + crop.left, + crop.right, + crop.top, + crop.bottom + )); + + video->set_crop(crop); + } if (cli_content.colour_conversion) { video->set_colour_conversion(PresetColourConversion::from_id(*cli_content.colour_conversion).conversion); } diff --git a/src/lib/create_cli.h b/src/lib/create_cli.h index 00abf85e5..cde8598cd 100644 --- a/src/lib/create_cli.h +++ b/src/lib/create_cli.h @@ -42,6 +42,7 @@ public: boost::filesystem::path path; VideoFrameType frame_type = VideoFrameType::TWO_D; bool auto_crop = false; + bool fill_crop = false; boost::optional<std::string> colour_conversion; boost::optional<dcp::Channel> channel; boost::optional<float> gain; diff --git a/test/create_cli_test.cc b/test/create_cli_test.cc index c71031fbc..0f127c018 100644 --- a/test/create_cli_test.cc +++ b/test/create_cli_test.cc @@ -322,6 +322,22 @@ BOOST_AUTO_TEST_CASE (create_cli_test) BOOST_CHECK(film->content()[0]->video->fade_in() == 24); BOOST_CHECK(film->content()[0]->video->fade_out() == 0); BOOST_CHECK(collected_error.empty()); + + /* Extract a 1.85 frame from a 320x240 source */ + cc = run("dcpomatic2_create --container-ratio 185 --fill-crop test/data/red_24.mp4"); + BOOST_CHECK(!cc.error); + film = cc.make_film(error); + BOOST_REQUIRE_EQUAL(film->content().size(), 1U); + BOOST_REQUIRE(film->content()[0]->video); + BOOST_CHECK(film->content()[0]->video->requested_crop() == Crop(0, 0, 33, 33)); + + /* Extract a 1.85 frame from a 2048x858 source */ + cc = run("dcpomatic2_create --container-ratio 185 --fill-crop test/data/scope_dcp"); + BOOST_CHECK(!cc.error); + film = cc.make_film(error); + BOOST_REQUIRE_EQUAL(film->content().size(), 1U); + BOOST_REQUIRE(film->content()[0]->video); + BOOST_CHECK(film->content()[0]->video->requested_crop() == Crop(230, 230, 0, 0)); } |
