From: Carl Hetherington Date: Sat, 25 Dec 2021 23:16:45 +0000 (+0100) Subject: Add guess_crop(). X-Git-Tag: v2.16.10~26 X-Git-Url: https://git.carlh.net/gitweb/?p=dcpomatic.git;a=commitdiff_plain;h=af85c3bb844599895eda780083fb9466cdfc8e76 Add guess_crop(). --- diff --git a/src/lib/guess_crop.cc b/src/lib/guess_crop.cc new file mode 100644 index 000000000..5deb0e480 --- /dev/null +++ b/src/lib/guess_crop.cc @@ -0,0 +1,133 @@ +/* + Copyright (C) 2021 Carl Hetherington + + This file is part of DCP-o-matic. + + DCP-o-matic is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + DCP-o-matic is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with DCP-o-matic. If not, see . + +*/ + + +#include "decoder.h" +#include "decoder_factory.h" +#include "image_proxy.h" +#include "guess_crop.h" +#include "image.h" +#include "video_decoder.h" + + +using std::shared_ptr; +using namespace dcpomatic; + + +Crop +guess_crop (shared_ptr image, double threshold) +{ + auto const width = image->size().width; + auto const height = image->size().height; + + auto image_in_line = [image, threshold](int start_x, int start_y, int pixels, bool rows) { + double brightest = 0; + + switch (image->pixel_format()) { + case AV_PIX_FMT_RGB24: + { + uint8_t const* data = image->data()[0] + start_x * 3 + start_y * image->stride()[0]; + for (int p = 0; p < pixels; ++p) { + /* Averaging R, G and B */ + brightest = std::max(brightest, static_cast(data[0] + data[1] + data[2]) / (3 * 256)); + data += rows ? 3 : image->stride()[0]; + } + break; + } + case AV_PIX_FMT_YUV420P: + { + uint8_t const* data = image->data()[0] + start_x + start_y * image->stride()[0]; + for (int p = 0; p < pixels; ++p) { + /* Just using Y */ + brightest = std::max(brightest, static_cast(*data) / 256); + data += rows ? 1 : image->stride()[0]; + } + break; + } + default: + DCPOMATIC_ASSERT (false); + } + + return brightest > threshold; + }; + + auto crop = Crop{}; + + for (auto y = 0; y < height; ++y) { + if (image_in_line(0, y, width, true)) { + crop.top = y; + break; + } + } + + for (auto y = height - 1; y >= 0; --y) { + if (image_in_line(0, y, width, true)) { + crop.bottom = height - 1 - y; + break; + } + } + + for (auto x = 0; x < width; ++x) { + if (image_in_line(x, 0, height, false)) { + crop.left = x; + break; + } + } + + for (auto x = width - 1; x >= 0; --x) { + if (image_in_line(x, 0, height, false)) { + crop.right = width - 1 - x; + break; + } + } + + return crop; +} + + +/** @param position Time within the content to get a video frame from when guessing the crop */ +Crop +guess_crop (shared_ptr film, shared_ptr content, double threshold, ContentTime position) +{ + DCPOMATIC_ASSERT (content->video); + + auto decoder = decoder_factory (film, content, false, false, {}); + DCPOMATIC_ASSERT (decoder->video); + + bool done = false; + auto crop = Crop{}; + + auto handle_video = [&done, &crop, threshold](ContentVideo video) { + crop = guess_crop(video.image->image(Image::Alignment::COMPACT).image, threshold); + done = true; + }; + + decoder->video->Data.connect (handle_video); + decoder->seek (position, false); + + int tries_left = 50; + while (!done && tries_left >= 0) { + decoder->pass(); + --tries_left; + } + + return crop; +} + diff --git a/src/lib/guess_crop.h b/src/lib/guess_crop.h new file mode 100644 index 000000000..d74912042 --- /dev/null +++ b/src/lib/guess_crop.h @@ -0,0 +1,34 @@ +/* + Copyright (C) 2021 Carl Hetherington + + This file is part of DCP-o-matic. + + DCP-o-matic is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + DCP-o-matic is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with DCP-o-matic. If not, see . + +*/ + + +#include "dcpomatic_time.h" +#include "types.h" +#include + + +class Content; +class Film; +class Image; + + +Crop guess_crop (std::shared_ptr image, double threshold); +Crop guess_crop (std::shared_ptr fillm, std::shared_ptr content, double threshold, dcpomatic::ContentTime position); + diff --git a/src/lib/wscript b/src/lib/wscript index d1eef8e33..30240cd11 100644 --- a/src/lib/wscript +++ b/src/lib/wscript @@ -115,6 +115,7 @@ sources = """ font_data.cc frame_interval_checker.cc frame_rate_change.cc + guess_crop.cc hints.cc internet.cc image.cc diff --git a/test/guess_crop_test.cc b/test/guess_crop_test.cc new file mode 100644 index 000000000..c26fc5ee4 --- /dev/null +++ b/test/guess_crop_test.cc @@ -0,0 +1,69 @@ +/* + Copyright (C) 2021 Carl Hetherington + + This file is part of DCP-o-matic. + + DCP-o-matic is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + DCP-o-matic is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with DCP-o-matic. If not, see . + +*/ + + +#include "lib/content.h" +#include "lib/content_factory.h" +#include "lib/guess_crop.h" +#include "lib/types.h" +#include "lib/video_content.h" +#include "test.h" +#include +#include +#include + + +using namespace dcpomatic; + + +BOOST_AUTO_TEST_CASE (guess_crop_image_test1) +{ + auto content = content_factory(TestPaths::private_data() / "arrietty_724.tiff").front(); + auto film = new_test_film2 ("guess_crop_image_test1", { content }); + + BOOST_CHECK ( + guess_crop(film, content, 0.1, ContentTime::from_frames(content->video->length(), content->video_frame_rate().get())) + == Crop(0, 0, 11, 11) + ); +} + + +BOOST_AUTO_TEST_CASE (guess_crop_image_test2) +{ + auto content = content_factory(TestPaths::private_data() / "prophet_frame.tiff").front(); + auto film = new_test_film2 ("guess_crop_image_test2", { content }); + + BOOST_CHECK ( + guess_crop(film, content, 0.1, ContentTime::from_frames(content->video->length(), content->video_frame_rate().get())) + == Crop(0, 0, 22, 22) + ); +} + + +BOOST_AUTO_TEST_CASE (guess_crop_image_test3) +{ + auto content = content_factory(TestPaths::private_data() / "pillarbox.png").front(); + auto film = new_test_film2 ("guess_crop_image_test3", { content }); + + BOOST_CHECK ( + guess_crop(film, content, 0.1, ContentTime::from_frames(content->video->length(), content->video_frame_rate().get())) + == Crop(113, 262, 0, 0) + ); +} diff --git a/test/wscript b/test/wscript index 1178264f0..4e28f1740 100644 --- a/test/wscript +++ b/test/wscript @@ -91,6 +91,7 @@ def build(bld): film_metadata_test.cc frame_interval_checker_test.cc frame_rate_test.cc + guess_crop_test.cc hints_test.cc image_content_fade_test.cc image_filename_sorter_test.cc