FFmpeg
Loading...
Searching...
No Matches
vf_signature.c
Go to the documentation of this file.
1/*
2 * Copyright (c) 2017 Gerion Entrup
3 *
4 * This file is part of FFmpeg.
5 *
6 * FFmpeg 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 * 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
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License along
17 * with FFmpeg; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 */
20
21/**
22 * @file
23 * MPEG-7 video signature calculation and lookup filter
24 * @see http://epubs.surrey.ac.uk/531590/1/MPEG-7%20Video%20Signature%20Author%27s%20Copy.pdf
25 */
26
27#include "libavcodec/put_bits.h"
29#include "libavutil/mem.h"
30#include "libavutil/opt.h"
31#include "libavutil/avstring.h"
32#include "libavutil/file_open.h"
33#include "avfilter.h"
34#include "filters.h"
35#include "signature.h"
36#include "signature_lookup.c"
37
38#define OFFSET(x) offsetof(SignatureContext, x)
39#define FLAGS AV_OPT_FLAG_FILTERING_PARAM | AV_OPT_FLAG_VIDEO_PARAM
40#define BLOCK_LCM (int64_t) 476985600
41
42static const AVOption signature_options[] = {
43 { "detectmode", "set the detectmode",
44 OFFSET(mode), AV_OPT_TYPE_INT, {.i64 = MODE_OFF}, 0, NB_LOOKUP_MODE-1, FLAGS, .unit = "mode" },
45 { "off", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = MODE_OFF}, 0, 0, .flags = FLAGS, .unit = "mode" },
46 { "full", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = MODE_FULL}, 0, 0, .flags = FLAGS, .unit = "mode" },
47 { "fast", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = MODE_FAST}, 0, 0, .flags = FLAGS, .unit = "mode" },
48 { "nb_inputs", "number of inputs",
49 OFFSET(nb_inputs), AV_OPT_TYPE_INT, {.i64 = 1}, 1, INT_MAX, FLAGS },
50 { "filename", "filename for output files",
51 OFFSET(filename), AV_OPT_TYPE_STRING, {.str = ""}, 0, NB_FORMATS-1, FLAGS },
52 { "format", "set output format",
53 OFFSET(format), AV_OPT_TYPE_INT, {.i64 = FORMAT_BINARY}, 0, 1, FLAGS , .unit = "format" },
54 { "binary", 0, 0, AV_OPT_TYPE_CONST, {.i64=FORMAT_BINARY}, 0, 0, FLAGS, .unit = "format" },
55 { "xml", 0, 0, AV_OPT_TYPE_CONST, {.i64=FORMAT_XML}, 0, 0, FLAGS, .unit = "format" },
56 { "th_d", "threshold to detect one word as similar",
57 OFFSET(thworddist), AV_OPT_TYPE_INT, {.i64 = 9000}, 1, INT_MAX, FLAGS },
58 { "th_dc", "threshold to detect all words as similar",
59 OFFSET(thcomposdist), AV_OPT_TYPE_INT, {.i64 = 60000}, 1, INT_MAX, FLAGS },
60 { "th_xh", "threshold to detect frames as similar",
61 OFFSET(thl1), AV_OPT_TYPE_INT, {.i64 = 116}, 1, INT_MAX, FLAGS },
62 { "th_di", "minimum length of matching sequence in frames",
63 OFFSET(thdi), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, FLAGS },
64 { "th_it", "threshold for relation of good to all frames",
65 OFFSET(thit), AV_OPT_TYPE_DOUBLE, {.dbl = 0.5}, 0.0, 1.0, FLAGS },
66 { NULL }
67};
68
70
71/* all formats with a separate gray value */
83
84static int config_input(AVFilterLink *inlink)
85{
86 AVFilterContext *ctx = inlink->dst;
87 SignatureContext *sic = ctx->priv;
88 StreamContext *sc = &(sic->streamcontexts[FF_INLINK_IDX(inlink)]);
89
90 sc->time_base = inlink->time_base;
91 /* test for overflow */
92 sc->divide = (((uint64_t) inlink->w/32) * (inlink->w/32 + 1) * (inlink->h/32 * inlink->h/32 + 1) > INT64_MAX / (BLOCK_LCM * 255));
93 if (sc->divide) {
94 av_log(ctx, AV_LOG_WARNING, "Input dimension too high for precise calculation, numbers will be rounded.\n");
95 }
96 sc->w = inlink->w;
97 sc->h = inlink->h;
98 return 0;
99}
100
101static int get_block_size(const Block *b)
102{
103 return (b->to.y - b->up.y + 1) * (b->to.x - b->up.x + 1);
104}
105
106static uint64_t get_block_sum(StreamContext *sc, uint64_t intpic[32][32], const Block *b)
107{
108 uint64_t sum = 0;
109
110 int x0, y0, x1, y1;
111
112 x0 = b->up.x;
113 y0 = b->up.y;
114 x1 = b->to.x;
115 y1 = b->to.y;
116
117 if (x0-1 >= 0 && y0-1 >= 0) {
118 sum = intpic[y1][x1] + intpic[y0-1][x0-1] - intpic[y1][x0-1] - intpic[y0-1][x1];
119 } else if (x0-1 >= 0) {
120 sum = intpic[y1][x1] - intpic[y1][x0-1];
121 } else if (y0-1 >= 0) {
122 sum = intpic[y1][x1] - intpic[y0-1][x1];
123 } else {
124 sum = intpic[y1][x1];
125 }
126 return sum;
127}
128
129static int cmp(const void *x, const void *y)
130{
131 const uint64_t *a = x, *b = y;
132 return *a < *b ? -1 : ( *a > *b ? 1 : 0 );
133}
134
135/**
136 * sets the bit at position pos to 1 in data
137 */
138static void set_bit(uint8_t* data, size_t pos)
139{
140 uint8_t mask = 1 << 7-(pos%8);
141 data[pos/8] |= mask;
142}
143
144static int filter_frame(AVFilterLink *inlink, AVFrame *picref)
145{
146 AVFilterContext *ctx = inlink->dst;
147 SignatureContext *sic = ctx->priv;
148 StreamContext *sc = &(sic->streamcontexts[FF_INLINK_IDX(inlink)]);
150
151 static const uint8_t pot3[5] = { 3*3*3*3, 3*3*3, 3*3, 3, 1 };
152 /* indexes of words : 210,217,219,274,334 44,175,233,270,273 57,70,103,237,269 100,285,295,337,354 101,102,111,275,296
153 s2usw = sorted to unsorted wordvec: 44 is at index 5, 57 at index 10...
154 */
155 static const unsigned int wordvec[25] = {44,57,70,100,101,102,103,111,175,210,217,219,233,237,269,270,273,274,275,285,295,296,334,337,354};
156 static const uint8_t s2usw[25] = { 5,10,11, 15, 20, 21, 12, 22, 6, 0, 1, 2, 7, 13, 14, 8, 9, 3, 23, 16, 17, 24, 4, 18, 19};
157
158 uint8_t wordt2b[5] = { 0, 0, 0, 0, 0 }; /* word ternary to binary */
159 uint64_t intpic[32][32];
160 uint64_t rowcount;
161 uint8_t *p = picref->data[0];
162 int inti, intj;
163 int *intjlut;
164
165 uint64_t conflist[DIFFELEM_SIZE];
166 int f = 0, g = 0, w = 0;
167 int32_t dh1 = 1, dh2 = 1, dw1 = 1, dw2 = 1, a, b;
168 int64_t denom;
169 int i, j, k, ternary;
170 uint64_t blocksum;
171 int blocksize;
172 int64_t th; /* threshold */
173 int64_t sum;
174
175 int64_t precfactor = (sc->divide) ? 65536 : BLOCK_LCM;
176
177 /* initialize fs */
178 if (sc->curfinesig) {
179 fs = av_mallocz(sizeof(FineSignature));
180 if (!fs)
181 return AVERROR(ENOMEM);
182 sc->curfinesig->next = fs;
183 fs->prev = sc->curfinesig;
184 sc->curfinesig = fs;
185 } else {
186 fs = sc->curfinesig = sc->finesiglist;
187 sc->curcoarsesig1->first = fs;
188 }
189
190 fs->pts = picref->pts;
191 fs->index = sc->lastindex++;
192
193 memset(intpic, 0, sizeof(uint64_t)*32*32);
194 intjlut = av_malloc_array(inlink->w, sizeof(int));
195 if (!intjlut)
196 return AVERROR(ENOMEM);
197 for (i = 0; i < inlink->w; i++) {
198 intjlut[i] = (i*32)/inlink->w;
199 }
200
201 for (i = 0; i < inlink->h; i++) {
202 inti = (i*32)/inlink->h;
203 for (j = 0; j < inlink->w; j++) {
204 intj = intjlut[j];
205 intpic[inti][intj] += p[j];
206 }
207 p += picref->linesize[0];
208 }
209 av_freep(&intjlut);
210
211 /* The following calculates a summed area table (intpic) and brings the numbers
212 * in intpic to the same denominator.
213 * So you only have to handle the numinator in the following sections.
214 */
215 dh1 = inlink->h / 32;
216 if (inlink->h % 32)
217 dh2 = dh1 + 1;
218 dw1 = inlink->w / 32;
219 if (inlink->w % 32)
220 dw2 = dw1 + 1;
221 denom = (sc->divide) ? dh1 * (int64_t)dh2 * dw1 * dw2 : 1;
222
223 for (i = 0; i < 32; i++) {
224 rowcount = 0;
225 a = 1;
226 if (dh2 > 1) {
227 a = ((inlink->h*(i+1))%32 == 0) ? (inlink->h*(i+1))/32 - 1 : (inlink->h*(i+1))/32;
228 a -= ((inlink->h*i)%32 == 0) ? (inlink->h*i)/32 - 1 : (inlink->h*i)/32;
229 a = (a == dh1)? dh2 : dh1;
230 }
231 for (j = 0; j < 32; j++) {
232 b = 1;
233 if (dw2 > 1) {
234 b = ((inlink->w*(j+1))%32 == 0) ? (inlink->w*(j+1))/32 - 1 : (inlink->w*(j+1))/32;
235 b -= ((inlink->w*j)%32 == 0) ? (inlink->w*j)/32 - 1 : (inlink->w*j)/32;
236 b = (b == dw1)? dw2 : dw1;
237 }
238 rowcount += intpic[i][j] * a * b * precfactor / denom;
239 if (i > 0) {
240 intpic[i][j] = intpic[i-1][j] + rowcount;
241 } else {
242 intpic[i][j] = rowcount;
243 }
244 }
245 }
246
247 denom = (sc->divide) ? 1 : dh1 * (int64_t)dh2 * dw1 * dw2;
248
249 for (i = 0; i < ELEMENT_COUNT; i++) {
250 const ElemCat* elemcat = elements[i];
251 int64_t* elemsignature;
252 uint64_t* sortsignature;
253
254 elemsignature = av_malloc_array(elemcat->elem_count, 2 * sizeof(int64_t));
255 if (!elemsignature)
256 return AVERROR(ENOMEM);
257 sortsignature = elemsignature + elemcat->elem_count;
258
259 for (j = 0; j < elemcat->elem_count; j++) {
260 blocksum = 0;
261 blocksize = 0;
262 for (k = 0; k < elemcat->left_count; k++) {
263 blocksum += get_block_sum(sc, intpic, &elemcat->blocks[j*elemcat->block_count+k]);
264 blocksize += get_block_size(&elemcat->blocks[j*elemcat->block_count+k]);
265 }
266 sum = blocksum / blocksize;
267 if (elemcat->av_elem) {
268 sum -= 128 * precfactor * denom;
269 } else {
270 blocksum = 0;
271 blocksize = 0;
272 for (; k < elemcat->block_count; k++) {
273 blocksum += get_block_sum(sc, intpic, &elemcat->blocks[j*elemcat->block_count+k]);
274 blocksize += get_block_size(&elemcat->blocks[j*elemcat->block_count+k]);
275 }
276 sum -= blocksum / blocksize;
277 conflist[g++] = FFABS(sum * 8 / (precfactor * denom));
278 }
279
280 elemsignature[j] = sum;
281 sortsignature[j] = FFABS(sum);
282 }
283
284 /* get threshold */
285 qsort(sortsignature, elemcat->elem_count, sizeof(uint64_t), cmp);
286 th = sortsignature[(int) (elemcat->elem_count*0.333)];
287
288 /* ternarize */
289 for (j = 0; j < elemcat->elem_count; j++) {
290 if (elemsignature[j] < -th) {
291 ternary = 0;
292 } else if (elemsignature[j] <= th) {
293 ternary = 1;
294 } else {
295 ternary = 2;
296 }
297 fs->framesig[f/5] += ternary * pot3[f%5];
298
299 if (f == wordvec[w]) {
300 fs->words[s2usw[w]/5] += ternary * pot3[wordt2b[s2usw[w]/5]++];
301 if (w < 24)
302 w++;
303 }
304 f++;
305 }
306 av_freep(&elemsignature);
307 }
308
309 /* confidence */
310 qsort(conflist, DIFFELEM_SIZE, sizeof(uint64_t), cmp);
311 fs->confidence = FFMIN(conflist[DIFFELEM_SIZE/2], 255);
312
313 /* coarsesignature */
314 if (sc->coarsecount == 0) {
315 if (sc->curcoarsesig2) {
317 if (!sc->curcoarsesig1)
318 return AVERROR(ENOMEM);
319 sc->curcoarsesig1->first = fs;
321 sc->coarseend = sc->curcoarsesig1;
322 }
323 }
324 if (sc->coarsecount == 45) {
325 sc->midcoarse = 1;
327 if (!sc->curcoarsesig2)
328 return AVERROR(ENOMEM);
329 sc->curcoarsesig2->first = fs;
331 sc->coarseend = sc->curcoarsesig2;
332 }
333 for (i = 0; i < 5; i++) {
334 set_bit(sc->curcoarsesig1->data[i], fs->words[i]);
335 }
336 /* assuming the actual frame is the last */
337 sc->curcoarsesig1->last = fs;
338 if (sc->midcoarse) {
339 for (i = 0; i < 5; i++) {
340 set_bit(sc->curcoarsesig2->data[i], fs->words[i]);
341 }
342 sc->curcoarsesig2->last = fs;
343 }
344
345 sc->coarsecount = (sc->coarsecount+1)%90;
346
347 /* debug printing finesignature */
349 av_log(ctx, AV_LOG_DEBUG, "input %d, confidence: %d\n", FF_INLINK_IDX(inlink), fs->confidence);
350
351 av_log(ctx, AV_LOG_DEBUG, "words:");
352 for (i = 0; i < 5; i++) {
353 av_log(ctx, AV_LOG_DEBUG, " %d:", fs->words[i] );
354 av_log(ctx, AV_LOG_DEBUG, " %d", fs->words[i] / pot3[0] );
355 for (j = 1; j < 5; j++)
356 av_log(ctx, AV_LOG_DEBUG, ",%d", fs->words[i] % pot3[j-1] / pot3[j] );
357 av_log(ctx, AV_LOG_DEBUG, ";");
358 }
359 av_log(ctx, AV_LOG_DEBUG, "\n");
360
361 av_log(ctx, AV_LOG_DEBUG, "framesignature:");
362 for (i = 0; i < SIGELEM_SIZE/5; i++) {
363 av_log(ctx, AV_LOG_DEBUG, " %d", fs->framesig[i] / pot3[0] );
364 for (j = 1; j < 5; j++)
365 av_log(ctx, AV_LOG_DEBUG, ",%d", fs->framesig[i] % pot3[j-1] / pot3[j] );
366 }
367 av_log(ctx, AV_LOG_DEBUG, "\n");
368 }
369
370 if (FF_INLINK_IDX(inlink) == 0)
371 return ff_filter_frame(inlink->dst->outputs[0], picref);
372 return 1;
373}
374
375static int xml_export(AVFilterContext *ctx, StreamContext *sc, const char* filename)
376{
378 CoarseSignature* cs;
379 int i, j;
380 FILE* f;
381 unsigned int pot3[5] = { 3*3*3*3, 3*3*3, 3*3, 3, 1 };
382
383 if (!sc->coarseend->last)
384 return AVERROR(EINVAL); // No frames ?
385
386 f = avpriv_fopen_utf8(filename, "w");
387 if (!f) {
388 int err = AVERROR(EINVAL);
389 av_log(ctx, AV_LOG_ERROR, "cannot open xml file %s: %s\n", filename, av_err2str(err));
390 return err;
391 }
392
393 /* header */
394 fprintf(f, "<?xml version='1.0' encoding='ASCII' ?>\n");
395 fprintf(f, "<Mpeg7 xmlns=\"urn:mpeg:mpeg7:schema:2001\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"urn:mpeg:mpeg7:schema:2001 schema/Mpeg7-2001.xsd\">\n");
396 fprintf(f, " <DescriptionUnit xsi:type=\"DescriptorCollectionType\">\n");
397 fprintf(f, " <Descriptor xsi:type=\"VideoSignatureType\">\n");
398 fprintf(f, " <VideoSignatureRegion>\n");
399 fprintf(f, " <VideoSignatureSpatialRegion>\n");
400 fprintf(f, " <Pixel>0 0 </Pixel>\n");
401 fprintf(f, " <Pixel>%d %d </Pixel>\n", sc->w - 1, sc->h - 1);
402 fprintf(f, " </VideoSignatureSpatialRegion>\n");
403 fprintf(f, " <StartFrameOfSpatialRegion>0</StartFrameOfSpatialRegion>\n");
404 /* hoping num is 1, other values are vague */
405 fprintf(f, " <MediaTimeUnit>%d</MediaTimeUnit>\n", sc->time_base.den / sc->time_base.num);
406 fprintf(f, " <MediaTimeOfSpatialRegion>\n");
407 fprintf(f, " <StartMediaTimeOfSpatialRegion>0</StartMediaTimeOfSpatialRegion>\n");
408 fprintf(f, " <EndMediaTimeOfSpatialRegion>%" PRIu64 "</EndMediaTimeOfSpatialRegion>\n", sc->coarseend->last->pts);
409 fprintf(f, " </MediaTimeOfSpatialRegion>\n");
410
411 /* coarsesignatures */
412 for (cs = sc->coarsesiglist; cs; cs = cs->next) {
413 fprintf(f, " <VSVideoSegment>\n");
414 fprintf(f, " <StartFrameOfSegment>%" PRIu32 "</StartFrameOfSegment>\n", cs->first->index);
415 fprintf(f, " <EndFrameOfSegment>%" PRIu32 "</EndFrameOfSegment>\n", cs->last->index);
416 fprintf(f, " <MediaTimeOfSegment>\n");
417 fprintf(f, " <StartMediaTimeOfSegment>%" PRIu64 "</StartMediaTimeOfSegment>\n", cs->first->pts);
418 fprintf(f, " <EndMediaTimeOfSegment>%" PRIu64 "</EndMediaTimeOfSegment>\n", cs->last->pts);
419 fprintf(f, " </MediaTimeOfSegment>\n");
420 for (i = 0; i < 5; i++) {
421 fprintf(f, " <BagOfWords>");
422 for (j = 0; j < 31; j++) {
423 uint8_t n = cs->data[i][j];
424 if (j < 30) {
425 fprintf(f, "%d %d %d %d %d %d %d %d ", (n & 0x80) >> 7,
426 (n & 0x40) >> 6,
427 (n & 0x20) >> 5,
428 (n & 0x10) >> 4,
429 (n & 0x08) >> 3,
430 (n & 0x04) >> 2,
431 (n & 0x02) >> 1,
432 (n & 0x01));
433 } else {
434 /* print only 3 bit in last byte */
435 fprintf(f, "%d %d %d ", (n & 0x80) >> 7,
436 (n & 0x40) >> 6,
437 (n & 0x20) >> 5);
438 }
439 }
440 fprintf(f, "</BagOfWords>\n");
441 }
442 fprintf(f, " </VSVideoSegment>\n");
443 }
444
445 /* finesignatures */
446 for (fs = sc->finesiglist; fs; fs = fs->next) {
447 fprintf(f, " <VideoFrame>\n");
448 fprintf(f, " <MediaTimeOfFrame>%" PRIu64 "</MediaTimeOfFrame>\n", fs->pts);
449 /* confidence */
450 fprintf(f, " <FrameConfidence>%d</FrameConfidence>\n", fs->confidence);
451 /* words */
452 fprintf(f, " <Word>");
453 for (i = 0; i < 5; i++) {
454 fprintf(f, "%d ", fs->words[i]);
455 if (i < 4) {
456 fprintf(f, " ");
457 }
458 }
459 fprintf(f, "</Word>\n");
460 /* framesignature */
461 fprintf(f, " <FrameSignature>");
462 for (i = 0; i< SIGELEM_SIZE/5; i++) {
463 if (i > 0) {
464 fprintf(f, " ");
465 }
466 fprintf(f, "%d ", fs->framesig[i] / pot3[0]);
467 for (j = 1; j < 5; j++)
468 fprintf(f, " %d ", fs->framesig[i] % pot3[j-1] / pot3[j] );
469 }
470 fprintf(f, "</FrameSignature>\n");
471 fprintf(f, " </VideoFrame>\n");
472 }
473 fprintf(f, " </VideoSignatureRegion>\n");
474 fprintf(f, " </Descriptor>\n");
475 fprintf(f, " </DescriptionUnit>\n");
476 fprintf(f, "</Mpeg7>\n");
477
478 fclose(f);
479 return 0;
480}
481
482static int binary_export(AVFilterContext *ctx, StreamContext *sc, const char* filename)
483{
484 FILE* f;
486 CoarseSignature* cs;
487 uint32_t numofsegments = (sc->lastindex + 44)/45;
488 int i, j;
489 PutBitContext buf;
490 /* buffer + header + coarsesignatures + finesignature */
491 int len = (512 + 6 * 32 + 3*16 + 2 +
492 numofsegments * (4*32 + 1 + 5*243) +
493 sc->lastindex * (2 + 32 + 6*8 + 608)) / 8;
494 uint8_t* buffer = av_malloc_array(len, sizeof(uint8_t));
495 if (!buffer)
496 return AVERROR(ENOMEM);
497
498 f = avpriv_fopen_utf8(filename, "wb");
499 if (!f) {
500 int err = AVERROR(EINVAL);
501 av_log(ctx, AV_LOG_ERROR, "cannot open file %s: %s\n", filename, av_err2str(err));
503 return err;
504 }
505 init_put_bits(&buf, buffer, len);
506
507 put_bits32(&buf, 1); /* NumOfSpatial Regions, only 1 supported */
508 put_bits(&buf, 1, 1); /* SpatialLocationFlag, always the whole image */
509 put_bits32(&buf, 0); /* PixelX,1 PixelY,1, 0,0 */
510 put_bits(&buf, 16, sc->w-1 & 0xFFFF); /* PixelX,2 */
511 put_bits(&buf, 16, sc->h-1 & 0xFFFF); /* PixelY,2 */
512 put_bits32(&buf, 0); /* StartFrameOfSpatialRegion */
513 put_bits32(&buf, sc->lastindex); /* NumOfFrames */
514 /* hoping num is 1, other values are vague */
515 /* den/num might be greater than 16 bit, so cutting it */
516 put_bits(&buf, 16, 0xFFFF & (sc->time_base.den / sc->time_base.num)); /* MediaTimeUnit */
517 put_bits(&buf, 1, 1); /* MediaTimeFlagOfSpatialRegion */
518 put_bits32(&buf, 0); /* StartMediaTimeOfSpatialRegion */
519 put_bits32(&buf, 0xFFFFFFFF & sc->coarseend->last->pts); /* EndMediaTimeOfSpatialRegion */
520 put_bits32(&buf, numofsegments); /* NumOfSegments */
521 /* coarsesignatures */
522 for (cs = sc->coarsesiglist; cs; cs = cs->next) {
523 put_bits32(&buf, cs->first->index); /* StartFrameOfSegment */
524 put_bits32(&buf, cs->last->index); /* EndFrameOfSegment */
525 put_bits(&buf, 1, 1); /* MediaTimeFlagOfSegment */
526 put_bits32(&buf, 0xFFFFFFFF & cs->first->pts); /* StartMediaTimeOfSegment */
527 put_bits32(&buf, 0xFFFFFFFF & cs->last->pts); /* EndMediaTimeOfSegment */
528 for (i = 0; i < 5; i++) {
529 /* put 243 bits ( = 7 * 32 + 19 = 8 * 28 + 19) into buffer */
530 for (j = 0; j < 30; j++) {
531 put_bits(&buf, 8, cs->data[i][j]);
532 }
533 put_bits(&buf, 3, cs->data[i][30] >> 5);
534 }
535 }
536 /* finesignatures */
537 put_bits(&buf, 1, 0); /* CompressionFlag, only 0 supported */
538 for (fs = sc->finesiglist; fs; fs = fs->next) {
539 put_bits(&buf, 1, 1); /* MediaTimeFlagOfFrame */
540 put_bits32(&buf, 0xFFFFFFFF & fs->pts); /* MediaTimeOfFrame */
541 put_bits(&buf, 8, fs->confidence); /* FrameConfidence */
542 for (i = 0; i < 5; i++) {
543 put_bits(&buf, 8, fs->words[i]); /* Words */
544 }
545 /* framesignature */
546 for (i = 0; i < SIGELEM_SIZE/5; i++) {
547 put_bits(&buf, 8, fs->framesig[i]);
548 }
549 }
550
551 flush_put_bits(&buf);
552 fwrite(buffer, 1, put_bytes_output(&buf), f);
553 fclose(f);
555 return 0;
556}
557
558static int export(AVFilterContext *ctx, StreamContext *sc, int input)
559{
560 SignatureContext* sic = ctx->priv;
561 char filename[1024];
562
563 if (sic->nb_inputs > 1) {
564 /* error already handled */
565 av_assert0(av_get_frame_filename(filename, sizeof(filename), sic->filename, input) == 0);
566 } else {
567 if (av_strlcpy(filename, sic->filename, sizeof(filename)) >= sizeof(filename))
568 return AVERROR(EINVAL);
569 }
570 if (sic->format == FORMAT_XML) {
571 return xml_export(ctx, sc, filename);
572 } else {
573 return binary_export(ctx, sc, filename);
574 }
575}
576
577static int request_frame(AVFilterLink *outlink)
578{
579 AVFilterContext *ctx = outlink->src;
580 SignatureContext *sic = ctx->priv;
581 StreamContext *sc, *sc2;
582 MatchingInfo match;
583 int i, j, ret;
584 int lookup = 1; /* indicates whether EOF of all files is reached */
585
586 /* process all inputs */
587 for (i = 0; i < sic->nb_inputs; i++){
588 sc = &(sic->streamcontexts[i]);
589
590 ret = ff_request_frame(ctx->inputs[i]);
591
592 /* return if unexpected error occurs in input stream */
593 if (ret < 0 && ret != AVERROR_EOF)
594 return ret;
595
596 /* export signature at EOF */
597 if (ret == AVERROR_EOF && !sc->exported) {
598 /* export if wanted */
599 if (strlen(sic->filename) > 0) {
600 if (export(ctx, sc, i) < 0)
601 return ret;
602 }
603 sc->exported = 1;
604 }
605 lookup &= sc->exported;
606 }
607
608 /* signature lookup */
609 if (lookup && sic->mode != MODE_OFF) {
610 /* iterate over every pair */
611 for (i = 0; i < sic->nb_inputs; i++) {
612 sc = &(sic->streamcontexts[i]);
613 for (j = i+1; j < sic->nb_inputs; j++) {
614 sc2 = &(sic->streamcontexts[j]);
615 match = lookup_signatures(ctx, sic, sc, sc2, sic->mode);
616 if (match.score != 0) {
617 av_log(ctx, AV_LOG_INFO, "matching of video %d at %f and %d at %f, %d frames matching\n",
618 i, ((double) match.first->pts * sc->time_base.num) / sc->time_base.den,
619 j, ((double) match.second->pts * sc2->time_base.num) / sc2->time_base.den,
620 match.matchframes);
621 if (match.whole)
622 av_log(ctx, AV_LOG_INFO, "whole video matching\n");
623 } else {
624 av_log(ctx, AV_LOG_INFO, "no matching of video %d and %d\n", i, j);
625 }
626 }
627 }
628 }
629
630 return ret;
631}
632
634{
635
636 SignatureContext *sic = ctx->priv;
637 StreamContext *sc;
638 int i, ret;
639 char tmp[1024];
640
641 sic->streamcontexts = av_mallocz(sic->nb_inputs * sizeof(StreamContext));
642 if (!sic->streamcontexts)
643 return AVERROR(ENOMEM);
644
645 for (i = 0; i < sic->nb_inputs; i++) {
646 AVFilterPad pad = {
647 .type = AVMEDIA_TYPE_VIDEO,
648 .name = av_asprintf("in%d", i),
649 .config_props = config_input,
650 .filter_frame = filter_frame,
651 };
652
653 if (!pad.name)
654 return AVERROR(ENOMEM);
655 if ((ret = ff_append_inpad_free_name(ctx, &pad)) < 0)
656 return ret;
657
658 sc = &(sic->streamcontexts[i]);
659
660 sc->lastindex = 0;
661 sc->finesiglist = av_mallocz(sizeof(FineSignature));
662 if (!sc->finesiglist)
663 return AVERROR(ENOMEM);
664 sc->curfinesig = NULL;
665
667 if (!sc->coarsesiglist)
668 return AVERROR(ENOMEM);
670 sc->coarseend = sc->coarsesiglist;
671 sc->coarsecount = 0;
672 sc->midcoarse = 0;
673 }
674
675 /* check filename */
676 if (sic->nb_inputs > 1 && strlen(sic->filename) > 0 && av_get_frame_filename(tmp, sizeof(tmp), sic->filename, 0) == -1) {
677 av_log(ctx, AV_LOG_ERROR, "The filename must contain %%d or %%0nd, if you have more than one input.\n");
678 return AVERROR(EINVAL);
679 }
680
681 return 0;
682}
683
684
685
687{
688 SignatureContext *sic = ctx->priv;
689 StreamContext *sc;
690 void* tmp;
691 FineSignature* finsig;
692 CoarseSignature* cousig;
693 int i;
694
695
696 /* free the lists */
697 if (sic->streamcontexts != NULL) {
698 for (i = 0; i < sic->nb_inputs; i++) {
699 sc = &(sic->streamcontexts[i]);
700 finsig = sc->finesiglist;
701 cousig = sc->coarsesiglist;
702
703 while (finsig) {
704 tmp = finsig;
705 finsig = finsig->next;
706 av_freep(&tmp);
707 }
708 sc->finesiglist = NULL;
709
710 while (cousig) {
711 tmp = cousig;
712 cousig = cousig->next;
713 av_freep(&tmp);
714 }
715 sc->coarsesiglist = NULL;
716 }
718 }
719}
720
721static int config_output(AVFilterLink *outlink)
722{
723 AVFilterContext *ctx = outlink->src;
724 AVFilterLink *inlink = ctx->inputs[0];
725 FilterLink *il = ff_filter_link(inlink);
726 FilterLink *ol = ff_filter_link(outlink);
727
728 outlink->time_base = inlink->time_base;
729 ol->frame_rate = il->frame_rate;
730 outlink->sample_aspect_ratio = inlink->sample_aspect_ratio;
731 outlink->w = inlink->w;
732 outlink->h = inlink->h;
733
734 return 0;
735}
736
738 {
739 .name = "default",
740 .type = AVMEDIA_TYPE_VIDEO,
741 .request_frame = request_frame,
742 .config_props = config_output,
743 },
744};
745
747 .p.name = "signature",
748 .p.description = NULL_IF_CONFIG_SMALL("Calculate the MPEG-7 video signature"),
749 .p.priv_class = &signature_class,
750 .p.inputs = NULL,
752 .priv_size = sizeof(SignatureContext),
753 .init = init,
754 .uninit = uninit,
757};
static int config_input(AVFilterLink *inlink)
static int request_frame(AVFilterLink *outlink)
Definition af_aecho.c:272
static const char *const format[]
Definition af_aiir.c:444
const FFFilter ff_vf_signature
static AVFormatContext * ctx
int32_t
#define av_assert0(cond)
assert() equivalent, that is always enabled.
Definition avassert.h:42
int ff_append_inpad_free_name(AVFilterContext *f, AVFilterPad *p)
Definition avfilter.c:132
int ff_filter_frame(AVFilterLink *link, AVFrame *frame)
Send a frame of data to the next filter.
Definition avfilter.c:1068
int ff_request_frame(AVFilterLink *link)
Request an input frame from the filter at the other end of the link.
Definition avfilter.c:483
Main libavfilter public API header.
Main libavformat public API header.
char * av_asprintf(const char *fmt,...)
Definition avstring.c:115
#define i(width, name, range_min, range_max)
Definition cbs_h264.c:63
#define f(width, name)
Definition cbs_vp8.c:236
#define fs(width, name, subs,...)
Definition cbs_vp9.c:200
#define FLAGS
Definition cmdutils.c:598
#define FFABS(a)
Absolute value, Note, INT_MIN / INT64_MIN result in undefined behavior as they are not representable ...
Definition common.h:74
#define NULL
Definition coverity.c:32
long long int64_t
Definition coverity.c:34
static int filter_frame(DBEDecodeContext *s, AVFrame *frame)
Definition dolby_e.c:1067
int(* init)(AVBSFContext *ctx)
Definition dts2pts.c:608
@ MODE_FULL
@ AV_OPT_TYPE_CONST
Special option type for declaring named constants.
Definition opt.h:298
@ AV_OPT_TYPE_INT
Underlying C type is int.
Definition opt.h:258
@ AV_OPT_TYPE_DOUBLE
Underlying C type is double.
Definition opt.h:266
@ AV_OPT_TYPE_STRING
Underlying C type is a uint8_t* that is either NULL or points to a C string allocated with the av_mal...
Definition opt.h:275
int av_get_frame_filename(char *buf, int buf_size, const char *path, int number)
Definition utils.c:356
#define AVFILTER_FLAG_DYNAMIC_INPUTS
The number of the filter inputs is not determined just by AVFilter.inputs.
Definition avfilter.h:155
#define AVERROR_EOF
End of file.
Definition error.h:57
#define av_err2str(errnum)
Convenience macro, the return value should be used only directly in function arguments but never stan...
Definition error.h:122
#define AVERROR(e)
Definition error.h:45
#define AV_LOG_DEBUG
Stuff which is only useful for libav* developers.
Definition log.h:231
#define AV_LOG_WARNING
Something somehow does not look correct.
Definition log.h:216
#define AV_LOG_INFO
Standard information.
Definition log.h:221
#define AV_LOG_ERROR
Something went wrong and cannot losslessly be recovered.
Definition log.h:210
int av_log_get_level(void)
Get the current log level.
Definition log.c:471
@ AVMEDIA_TYPE_VIDEO
Definition avutil.h:200
size_t av_strlcpy(char *dst, const char *src, size_t size)
Copy the string src to dst, but no more than size - 1 bytes, and null-terminate dst.
Definition avstring.c:85
int a
#define b
Definition input.c:43
static const char signature[]
Definition ipmovie.c:592
static void put_bits(Jpeg2000EncoderContext *s, int val, int n)
put n times val bit
Definition j2kenc.c:154
static av_cold void uninit(AVBitStreamFilterContext *ctx)
static int config_output(AVBitStreamFilterLink *outlink)
#define FILTER_OUTPUTS(array)
Definition filters.h:265
#define FILTER_PIXFMTS_ARRAY(array)
Definition filters.h:244
static FilterLink * ff_filter_link(AVFilterLink *link)
Definition filters.h:199
#define FF_INLINK_IDX(link)
Find the index of a link.
Definition filters.h:215
#define AVFILTER_DEFINE_CLASS(fname)
Definition filters.h:478
#define av_cold
Definition attributes.h:117
FILE * avpriv_fopen_utf8(const char *path, const char *mode)
Open a file using a UTF-8 filename.
Definition file_open.c:160
#define NULL_IF_CONFIG_SMALL(x)
Return NULL if CONFIG_SMALL is true, otherwise the argument without modification.
Definition internal.h:88
static enum AVPixelFormat pix_fmts[]
Definition libkvazaar.c:296
uint8_t w
Definition llvidencdsp.c:39
static const uint16_t mask[17]
Definition lzw.c:38
#define FFMIN(a, b)
Definition macros.h:49
Memory handling functions.
static av_always_inline int cmp(MPVEncContext *const s, const int x, const int y, const int subx, const int suby, const int size, const int h, int ref_index, int src_index, me_cmp_func cmp_func, me_cmp_func chroma_cmp_func, const int flags)
compares a block (either a full macroblock or a partition thereof) against a proposed motion-compensa...
Definition motion_est.c:263
const char data[16]
Definition mxf.c:149
AVOptions.
AVPixelFormat
Pixel format.
Definition pixfmt.h:71
@ AV_PIX_FMT_NV12
planar YUV 4:2:0, 12bpp, 1 plane for Y and 1 plane for the UV components, which are interleaved (firs...
Definition pixfmt.h:96
@ AV_PIX_FMT_NONE
Definition pixfmt.h:72
@ AV_PIX_FMT_YUV420P
planar YUV 4:2:0, 12bpp, (1 Cr & Cb sample per 2x2 Y samples)
Definition pixfmt.h:73
@ AV_PIX_FMT_YUV440P
planar YUV 4:4:0 (1 Cr & Cb sample per 1x2 Y samples)
Definition pixfmt.h:106
@ AV_PIX_FMT_NV21
as above, but U and V bytes are swapped
Definition pixfmt.h:97
@ AV_PIX_FMT_YUV422P
planar YUV 4:2:2, 16bpp, (1 Cr & Cb sample per 2x1 Y samples)
Definition pixfmt.h:77
@ AV_PIX_FMT_GRAY8
Y , 8bpp.
Definition pixfmt.h:81
@ AV_PIX_FMT_YUVJ440P
planar YUV 4:4:0 full scale (JPEG), deprecated in favor of AV_PIX_FMT_YUV440P and setting color_range
Definition pixfmt.h:107
@ AV_PIX_FMT_YUV410P
planar YUV 4:1:0, 9bpp, (1 Cr & Cb sample per 4x4 Y samples)
Definition pixfmt.h:79
@ AV_PIX_FMT_YUV411P
planar YUV 4:1:1, 12bpp, (1 Cr & Cb sample per 4x1 Y samples)
Definition pixfmt.h:80
@ AV_PIX_FMT_YUV444P
planar YUV 4:4:4, 24bpp, (1 Cr & Cb sample per 1x1 Y samples)
Definition pixfmt.h:78
@ AV_PIX_FMT_YUVJ411P
planar YUV 4:1:1, 12bpp, (1 Cr & Cb sample per 4x1 Y samples) full scale (JPEG), deprecated in favor ...
Definition pixfmt.h:283
@ AV_PIX_FMT_YUVJ422P
planar YUV 4:2:2, 16bpp, full scale (JPEG), deprecated in favor of AV_PIX_FMT_YUV422P and setting col...
Definition pixfmt.h:86
@ AV_PIX_FMT_YUVJ444P
planar YUV 4:4:4, 24bpp, full scale (JPEG), deprecated in favor of AV_PIX_FMT_YUV444P and setting col...
Definition pixfmt.h:87
@ AV_PIX_FMT_YUVJ420P
planar YUV 4:2:0, 12bpp, full scale (JPEG), deprecated in favor of AV_PIX_FMT_YUV420P and setting col...
Definition pixfmt.h:85
bitstream writer API
static void init_put_bits(PutBitContext *s, uint8_t *buffer, int buffer_size)
Initialize the PutBitContext s.
Definition put_bits.h:62
static void flush_put_bits(PutBitContext *s)
Pad the end of the output stream with zeros.
Definition put_bits.h:153
static int put_bytes_output(const PutBitContext *s)
Definition put_bits.h:99
static av_unused void put_bits32(PutBitContext *s, uint32_t value)
Write exactly 32 bits into a bitstream.
Definition put_bits.h:301
MPEG-7 video signature calculation and lookup filter.
@ NB_FORMATS
Definition signature.h:50
@ FORMAT_BINARY
Definition signature.h:48
@ FORMAT_XML
Definition signature.h:49
static const ElemCat * elements[ELEMENT_COUNT]
Definition signature.h:565
#define SIGELEM_SIZE
Definition signature.h:36
#define ELEMENT_COUNT
Definition signature.h:35
#define DIFFELEM_SIZE
Definition signature.h:37
@ MODE_OFF
Definition signature.h:41
@ NB_LOOKUP_MODE
Definition signature.h:44
@ MODE_FAST
Definition signature.h:43
MPEG-7 video signature calculation and lookup filter.
static MatchingInfo lookup_signatures(AVFilterContext *ctx, SignatureContext *sc, StreamContext *first, StreamContext *second, int mode)
unsigned int pos
Definition spdifenc.c:431
An instance of a filter.
Definition avfilter.h:273
AVFilterLink ** outputs
array of pointers to output links
Definition avfilter.h:285
A filter pad used for either input or output.
Definition filters.h:40
const char * name
Pad name.
Definition filters.h:46
This structure describes decoded (raw) audio or video data.
Definition frame.h:472
int64_t pts
Presentation timestamp in time_base units (time when frame should be shown to user).
Definition frame.h:574
uint8_t * data[AV_NUM_DATA_POINTERS]
pointer to the picture/channel planes.
Definition frame.h:493
int linesize[AV_NUM_DATA_POINTERS]
For video, a positive or negative value, which is typically indicating the size in bytes of each pict...
Definition frame.h:517
AVOption.
Definition opt.h:428
int num
Numerator.
Definition rational.h:59
int den
Denominator.
Definition rational.h:60
struct FineSignature * first
Definition signature.h:83
struct CoarseSignature * next
Definition signature.h:85
uint8_t data[5][31]
Definition signature.h:82
struct FineSignature * last
Definition signature.h:84
short left_count
Definition signature.h:65
int av_elem
Definition signature.h:64
short elem_count
Definition signature.h:67
const Block * blocks
Definition signature.h:68
short block_count
Definition signature.h:66
struct FineSignature * next
Definition signature.h:72
uint64_t pts
Definition signature.h:74
uint32_t index
Definition signature.h:75
struct FineSignature * second
Definition signature.h:97
struct FineSignature * first
Definition signature.h:96
int matchframes
Definition signature.h:94
StreamContext * streamcontexts
Definition signature.h:141
FineSignature * finesiglist
Definition signature.h:110
CoarseSignature * curcoarsesig1
Definition signature.h:116
FineSignature * curfinesig
Definition signature.h:111
uint32_t lastindex
Definition signature.h:121
AVRational time_base
Definition signature.h:102
CoarseSignature * coarsesiglist
Definition signature.h:113
CoarseSignature * curcoarsesig2
Definition signature.h:117
CoarseSignature * coarseend
Definition signature.h:114
Definition swscale.c:71
#define av_malloc_array(a, b)
#define av_mallocz(s)
#define av_freep(p)
#define av_log(a,...)
static uint8_t tmp[40]
Definition aes_ctr.c:52
static char buffer[20]
Definition seek.c:32
const char * g
Definition vf_curves.c:128
static int xml_export(AVFilterContext *ctx, StreamContext *sc, const char *filename)
#define BLOCK_LCM
static int export(AVFilterContext *ctx, StreamContext *sc, int input)
static const AVOption signature_options[]
static int config_input(AVFilterLink *inlink)
static int binary_export(AVFilterContext *ctx, StreamContext *sc, const char *filename)
static int request_frame(AVFilterLink *outlink)
static void set_bit(uint8_t *data, size_t pos)
sets the bit at position pos to 1 in data
static int cmp(const void *x, const void *y)
static av_cold void uninit(AVFilterContext *ctx)
static const AVFilterPad signature_outputs[]
static int get_block_size(const Block *b)
#define OFFSET(x)
static int config_output(AVFilterLink *outlink)
static int filter_frame(AVFilterLink *inlink, AVFrame *picref)
static uint64_t get_block_sum(StreamContext *sc, uint64_t intpic[32][32], const Block *b)
int lookup
int len