7ee7a72860c4051bf632841a8b92f4e72f1d89a6
[dcpomatic.git] / src / lib / guess_crop.cc
1 /*
2     Copyright (C) 2021 Carl Hetherington <cth@carlh.net>
3
4     This file is part of DCP-o-matic.
5
6     DCP-o-matic is free software; you can redistribute it and/or modify
7     it under the terms of the GNU General Public License as published by
8     the Free Software Foundation; either version 2 of the License, or
9     (at your option) any later version.
10
11     DCP-o-matic is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14     GNU General Public License for more details.
15
16     You should have received a copy of the GNU General Public License
17     along with DCP-o-matic.  If not, see <http://www.gnu.org/licenses/>.
18
19 */
20
21
22 #include "decoder.h"
23 #include "decoder_factory.h"
24 #include "image_proxy.h"
25 #include "guess_crop.h"
26 #include "image.h"
27 #include "video_decoder.h"
28
29
30 using std::shared_ptr;
31 using namespace dcpomatic;
32
33
34 Crop
35 guess_crop (shared_ptr<const Image> image, double threshold)
36 {
37         auto const width = image->size().width;
38         auto const height = image->size().height;
39
40         auto image_in_line = [image, threshold](int start_x, int start_y, int pixels, bool rows) {
41                 double brightest = 0;
42
43                 switch (image->pixel_format()) {
44                 case AV_PIX_FMT_RGB24:
45                 {
46                         uint8_t const* data = image->data()[0] + start_x * 3 + start_y * image->stride()[0];
47                         for (int p = 0; p < pixels; ++p) {
48                                 /* Averaging R, G and B */
49                                 brightest = std::max(brightest, static_cast<double>(data[0] + data[1] + data[2]) / (3 * 256));
50                                 data += rows ? 3 : image->stride()[0];
51                         }
52                         break;
53                 }
54                 case AV_PIX_FMT_YUV420P:
55                 {
56                         uint8_t const* data = image->data()[0] + start_x + start_y * image->stride()[0];
57                         for (int p = 0; p < pixels; ++p) {
58                                 /* Just using Y */
59                                 brightest = std::max(brightest, static_cast<double>(*data) / 256);
60                                 data += rows ? 1 : image->stride()[0];
61                         }
62                         break;
63                 }
64                 default:
65                         throw PixelFormatError("guess_crop()", image->pixel_format());
66                 }
67
68                 return brightest > threshold;
69         };
70
71         auto crop = Crop{};
72
73         for (auto y = 0; y < height; ++y) {
74                 if (image_in_line(0, y, width, true)) {
75                         crop.top = y;
76                         break;
77                 }
78         }
79
80         for (auto y = height - 1; y >= 0; --y) {
81                 if (image_in_line(0, y, width, true)) {
82                         crop.bottom = height - 1 - y;
83                         break;
84                 }
85         }
86
87         for (auto x = 0; x < width; ++x) {
88                 if (image_in_line(x, 0, height, false)) {
89                         crop.left = x;
90                         break;
91                 }
92         }
93
94         for (auto x = width - 1; x >= 0; --x) {
95                 if (image_in_line(x, 0, height, false)) {
96                         crop.right = width - 1 - x;
97                         break;
98                 }
99         }
100
101         return crop;
102 }
103
104
105 /** @param position Time within the content to get a video frame from when guessing the crop */
106 Crop
107 guess_crop (shared_ptr<const Film> film, shared_ptr<const Content> content, double threshold, ContentTime position)
108 {
109         DCPOMATIC_ASSERT (content->video);
110
111         auto decoder = decoder_factory (film, content, false, false, {});
112         DCPOMATIC_ASSERT (decoder->video);
113
114         bool done = false;
115         auto crop = Crop{};
116
117         auto handle_video = [&done, &crop, threshold](ContentVideo video) {
118                 crop = guess_crop(video.image->image(Image::Alignment::COMPACT).image, threshold);
119                 done = true;
120         };
121
122         decoder->video->Data.connect (handle_video);
123         decoder->seek (position, false);
124
125         int tries_left = 50;
126         while (!done && tries_left >= 0) {
127                 decoder->pass();
128                 --tries_left;
129         }
130
131         return crop;
132 }
133