FFmpeg coverage


Directory: ../../../ffmpeg/
File: src/libavfilter/vf_blockdetect.c
Date: 2024-11-20 23:03:26
Exec Total Coverage
Lines: 113 114 99.1%
Functions: 5 5 100.0%
Branches: 59 66 89.4%

Line Branch Exec Source
1 /*
2 * Copyright (c) 2021 Thilo Borgmann <thilo.borgmann _at_ mail.de>
3 *
4 * This file is part of FFmpeg.
5 *
6 * FFmpeg is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * FFmpeg 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 GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with FFmpeg; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 */
20
21 /**
22 * @file
23 * No-reference blockdetect filter
24 *
25 * Implementing:
26 * Remco Muijs and Ihor Kirenko: "A no-reference blocking artifact measure for adaptive video processing." 2005 13th European signal processing conference. IEEE, 2005.
27 * http://www.eurasip.org/Proceedings/Eusipco/Eusipco2005/defevent/papers/cr1042.pdf
28 *
29 * @author Thilo Borgmann <thilo.borgmann _at_ mail.de>
30 */
31
32 #include "libavutil/mem.h"
33 #include "libavutil/opt.h"
34 #include "libavutil/pixdesc.h"
35
36 #include "filters.h"
37 #include "video.h"
38
39 typedef struct BLKContext {
40 const AVClass *class;
41
42 int hsub, vsub;
43 int nb_planes;
44
45 int period_min; // minimum period to search for
46 int period_max; // maximum period to search for
47 int planes; // number of planes to filter
48
49 double block_total;
50 uint64_t nb_frames;
51
52 float *gradients;
53 } BLKContext;
54
55 #define OFFSET(x) offsetof(BLKContext, x)
56 #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
57 static const AVOption blockdetect_options[] = {
58 { "period_min", "Minimum period to search for", OFFSET(period_min), AV_OPT_TYPE_INT, {.i64=3}, 2, 32, FLAGS},
59 { "period_max", "Maximum period to search for", OFFSET(period_max), AV_OPT_TYPE_INT, {.i64=24}, 2, 64, FLAGS},
60 { "planes", "set planes to filter", OFFSET(planes), AV_OPT_TYPE_INT, {.i64=1}, 0, 15, FLAGS },
61 { NULL }
62 };
63
64 AVFILTER_DEFINE_CLASS(blockdetect);
65
66 1 static int blockdetect_config_input(AVFilterLink *inlink)
67 {
68 1 AVFilterContext *ctx = inlink->dst;
69 1 BLKContext *s = ctx->priv;
70 1 const int bufsize = inlink->w * inlink->h;
71 const AVPixFmtDescriptor *pix_desc;
72
73 1 pix_desc = av_pix_fmt_desc_get(inlink->format);
74 1 s->hsub = pix_desc->log2_chroma_w;
75 1 s->vsub = pix_desc->log2_chroma_h;
76 1 s->nb_planes = av_pix_fmt_count_planes(inlink->format);
77
78 1 s->gradients = av_calloc(bufsize, sizeof(*s->gradients));
79
80
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (!s->gradients)
81 return AVERROR(ENOMEM);
82
83 1 return 0;
84 }
85
86 5 static float calculate_blockiness(BLKContext *s, int w, int h,
87 float *grad, int grad_linesize,
88 uint8_t* src, int src_linesize)
89 {
90 5 float block = 0.0f;
91 5 float nonblock = 0.0f;
92 5 int block_count = 0;
93 5 int nonblock_count = 0;
94 5 float ret = 0;
95
96 // Calculate BS in horizontal and vertical directions according to (1)(2)(3).
97 // Also try to find integer pixel periods (grids) even for scaled images.
98 // In case of fractional periods, FFMAX of current and neighbor pixels
99 // can help improve the correlation with MQS.
100 // Skip linear correction term (4)(5), as it appears only valid for their own test samples.
101
102 // horizontal blockiness (fixed width)
103
2/2
✓ Branch 0 taken 995 times.
✓ Branch 1 taken 5 times.
1000 for (int j = 1; j < h; j++) {
104
2/2
✓ Branch 0 taken 291535 times.
✓ Branch 1 taken 995 times.
292530 for (int i = 3; i < w - 4; i++) {
105 291535 float temp = 0.0f;
106 291535 grad[j * grad_linesize + i] =
107 291535 abs(src[j * src_linesize + i + 0] - src[j * src_linesize + i + 1]);
108 291535 temp += abs(src[j * src_linesize + i + 1] - src[j * src_linesize + i + 2]);
109 291535 temp += abs(src[j * src_linesize + i + 2] - src[j * src_linesize + i + 3]);
110 291535 temp += abs(src[j * src_linesize + i + 3] - src[j * src_linesize + i + 4]);
111 291535 temp += abs(src[j * src_linesize + i - 0] - src[j * src_linesize + i - 1]);
112 291535 temp += abs(src[j * src_linesize + i - 1] - src[j * src_linesize + i - 2]);
113 291535 temp += abs(src[j * src_linesize + i - 2] - src[j * src_linesize + i - 3]);
114
2/2
✓ Branch 0 taken 220354 times.
✓ Branch 1 taken 71181 times.
291535 temp = FFMAX(1, temp);
115 291535 grad[j * grad_linesize + i] /= temp;
116
117 // use first row to store acculated results
118 291535 grad[i] += grad[j * grad_linesize + i];
119 }
120 }
121
122 // find horizontal period
123
2/2
✓ Branch 0 taken 110 times.
✓ Branch 1 taken 5 times.
115 for (int period = s->period_min; period < s->period_max + 1; period++) {
124 float temp;
125 110 block = 0;
126 110 nonblock = 0;
127 110 block_count = 0;
128 110 nonblock_count = 0;
129
2/2
✓ Branch 0 taken 32230 times.
✓ Branch 1 taken 110 times.
32340 for (int i = 3; i < w - 4; i++) {
130
2/2
✓ Branch 0 taken 3310 times.
✓ Branch 1 taken 28920 times.
32230 if ((i % period) == (period - 1)) {
131
6/6
✓ Branch 0 taken 2395 times.
✓ Branch 1 taken 915 times.
✓ Branch 2 taken 2751 times.
✓ Branch 3 taken 559 times.
✓ Branch 4 taken 2385 times.
✓ Branch 5 taken 366 times.
3310 block += FFMAX(FFMAX(grad[i + 0], grad[i + 1]), grad[i - 1]);
132 3310 block_count++;
133 } else {
134 28920 nonblock += grad[i];
135 28920 nonblock_count++;
136 }
137 }
138
2/4
✓ Branch 0 taken 110 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 110 times.
✗ Branch 3 not taken.
110 if (block_count && nonblock_count) {
139 110 temp = (block / block_count) / (nonblock / nonblock_count);
140
2/2
✓ Branch 0 taken 95 times.
✓ Branch 1 taken 15 times.
110 ret = FFMAX(ret, temp);
141 }
142 }
143
144 // vertical blockiness (fixed height)
145 5 block_count = 0;
146
2/2
✓ Branch 0 taken 965 times.
✓ Branch 1 taken 5 times.
970 for (int j = 3; j < h - 4; j++) {
147
2/2
✓ Branch 0 taken 288535 times.
✓ Branch 1 taken 965 times.
289500 for (int i = 1; i < w; i++) {
148 288535 float temp = 0.0f;
149 288535 grad[j * grad_linesize + i] =
150 288535 abs(src[(j + 0) * src_linesize + i] - src[(j + 1) * src_linesize + i]);
151 288535 temp += abs(src[(j + 1) * src_linesize + i] - src[(j + 2) * src_linesize + i]);
152 288535 temp += abs(src[(j + 2) * src_linesize + i] - src[(j + 3) * src_linesize + i]);
153 288535 temp += abs(src[(j + 3) * src_linesize + i] - src[(j + 4) * src_linesize + i]);
154 288535 temp += abs(src[(j - 0) * src_linesize + i] - src[(j - 1) * src_linesize + i]);
155 288535 temp += abs(src[(j - 1) * src_linesize + i] - src[(j - 2) * src_linesize + i]);
156 288535 temp += abs(src[(j - 2) * src_linesize + i] - src[(j - 3) * src_linesize + i]);
157
2/2
✓ Branch 0 taken 246901 times.
✓ Branch 1 taken 41634 times.
288535 temp = FFMAX(1, temp);
158 288535 grad[j * grad_linesize + i] /= temp;
159
160 // use first column to store accumulated results
161 288535 grad[j * grad_linesize] += grad[j * grad_linesize + i];
162 }
163 }
164
165 // find vertical period
166
2/2
✓ Branch 0 taken 110 times.
✓ Branch 1 taken 5 times.
115 for (int period = s->period_min; period < s->period_max + 1; period++) {
167 float temp;
168 110 block = 0;
169 110 nonblock = 0;
170 110 block_count = 0;
171 110 nonblock_count = 0;
172
2/2
✓ Branch 0 taken 21230 times.
✓ Branch 1 taken 110 times.
21340 for (int j = 3; j < h - 4; j++) {
173
2/2
✓ Branch 0 taken 2180 times.
✓ Branch 1 taken 19050 times.
21230 if ((j % period) == (period - 1)) {
174
6/6
✓ Branch 0 taken 1496 times.
✓ Branch 1 taken 684 times.
✓ Branch 2 taken 1758 times.
✓ Branch 3 taken 422 times.
✓ Branch 4 taken 1441 times.
✓ Branch 5 taken 317 times.
2180 block += FFMAX(FFMAX(grad[(j + 0) * grad_linesize],
175 grad[(j + 1) * grad_linesize]),
176 grad[(j - 1) * grad_linesize]);
177 2180 block_count++;
178 } else {
179 19050 nonblock += grad[j * grad_linesize];
180 19050 nonblock_count++;
181 }
182 }
183
2/4
✓ Branch 0 taken 110 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 110 times.
✗ Branch 3 not taken.
110 if (block_count && nonblock_count) {
184 110 temp = (block / block_count) / (nonblock / nonblock_count);
185
1/2
✓ Branch 0 taken 110 times.
✗ Branch 1 not taken.
110 ret = FFMAX(ret, temp);
186 }
187 }
188
189 // return highest value of horz||vert
190 5 return ret;
191 }
192
193 5 static void set_meta(AVDictionary **metadata, const char *key, float d)
194 {
195 char value[128];
196 5 snprintf(value, sizeof(value), "%f", d);
197 5 av_dict_set(metadata, key, value, 0);
198 5 }
199
200 5 static int blockdetect_filter_frame(AVFilterLink *inlink, AVFrame *in)
201 {
202 5 FilterLink *inl = ff_filter_link(inlink);
203 5 AVFilterContext *ctx = inlink->dst;
204 5 BLKContext *s = ctx->priv;
205 5 AVFilterLink *outlink = ctx->outputs[0];
206
207 5 const int inw = inlink->w;
208 5 const int inh = inlink->h;
209
210 5 float *gradients = s->gradients;
211
212 5 float block = 0.0f;
213 5 int nplanes = 0;
214 AVDictionary **metadata;
215 5 metadata = &in->metadata;
216
217
2/2
✓ Branch 0 taken 15 times.
✓ Branch 1 taken 5 times.
20 for (int plane = 0; plane < s->nb_planes; plane++) {
218
4/4
✓ Branch 0 taken 10 times.
✓ Branch 1 taken 5 times.
✓ Branch 2 taken 5 times.
✓ Branch 3 taken 5 times.
15 int hsub = plane == 1 || plane == 2 ? s->hsub : 0;
219
4/4
✓ Branch 0 taken 10 times.
✓ Branch 1 taken 5 times.
✓ Branch 2 taken 5 times.
✓ Branch 3 taken 5 times.
15 int vsub = plane == 1 || plane == 2 ? s->vsub : 0;
220 15 int w = AV_CEIL_RSHIFT(inw, hsub);
221 15 int h = AV_CEIL_RSHIFT(inh, vsub);
222
223
2/2
✓ Branch 0 taken 10 times.
✓ Branch 1 taken 5 times.
15 if (!((1 << plane) & s->planes))
224 10 continue;
225
226 5 nplanes++;
227
228 5 block += calculate_blockiness(s, w, h, gradients, w, in->data[plane], in->linesize[plane]);
229 }
230
231
1/2
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
5 if (nplanes)
232 5 block /= nplanes;
233
234 5 s->block_total += block;
235
236 // write stats
237 5 av_log(ctx, AV_LOG_VERBOSE, "block: %.7f\n", block);
238
239 5 set_meta(metadata, "lavfi.block", block);
240
241 5 s->nb_frames = inl->frame_count_in;
242
243 5 return ff_filter_frame(outlink, in);
244 }
245
246 2 static av_cold void blockdetect_uninit(AVFilterContext *ctx)
247 {
248 2 BLKContext *s = ctx->priv;
249
250
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
2 if (s->nb_frames > 0) {
251 1 av_log(ctx, AV_LOG_INFO, "block mean: %.7f\n",
252 1 s->block_total / s->nb_frames);
253 }
254
255 2 av_freep(&s->gradients);
256 2 }
257
258 static const enum AVPixelFormat pix_fmts[] = {
259 AV_PIX_FMT_GRAY8,
260 AV_PIX_FMT_GBRP, AV_PIX_FMT_GBRAP,
261 AV_PIX_FMT_YUV422P, AV_PIX_FMT_YUV420P,
262 AV_PIX_FMT_YUV444P, AV_PIX_FMT_YUV440P,
263 AV_PIX_FMT_YUV411P, AV_PIX_FMT_YUV410P,
264 AV_PIX_FMT_YUVJ440P, AV_PIX_FMT_YUVJ411P, AV_PIX_FMT_YUVJ420P,
265 AV_PIX_FMT_YUVJ422P, AV_PIX_FMT_YUVJ444P,
266 AV_PIX_FMT_YUVA444P, AV_PIX_FMT_YUVA422P, AV_PIX_FMT_YUVA420P,
267 AV_PIX_FMT_NONE
268 };
269
270 static const AVFilterPad blockdetect_inputs[] = {
271 {
272 .name = "default",
273 .type = AVMEDIA_TYPE_VIDEO,
274 .config_props = blockdetect_config_input,
275 .filter_frame = blockdetect_filter_frame,
276 },
277 };
278
279 const AVFilter ff_vf_blockdetect = {
280 .name = "blockdetect",
281 .description = NULL_IF_CONFIG_SMALL("Blockdetect filter."),
282 .priv_size = sizeof(BLKContext),
283 .uninit = blockdetect_uninit,
284 FILTER_PIXFMTS_ARRAY(pix_fmts),
285 FILTER_INPUTS(blockdetect_inputs),
286 FILTER_OUTPUTS(ff_video_default_filterpad),
287 .priv_class = &blockdetect_class,
288 .flags = AVFILTER_FLAG_METADATA_ONLY,
289 };
290