29 #include "libavutil/imgutils.h"
37 int pgm_set(
const AVFrame *pict,
int height)
39 const int width = pict->linesize[0];
40 const int size = height * width;
43 for (
int ii = 0; ii < size; ii++)
44 if (pict->data[0][ii])
49 int pgm_match(
const AVFrame *tmpl,
const AVFrame *test,
int height,
50 int radius,
unsigned short *pscore)
53 const int width = tmpl->linesize[0];
55 if (width != test->linesize[0])
57 LOG(VB_COMMFLAG, LOG_ERR,
58 QString(
"pgm_match widths don't match: %1 != %2")
59 .
arg(width).
arg(test->linesize[0]));
64 for (
int rr = 0; rr < height; rr++)
66 for (
int cc = 0;
cc < width;
cc++)
68 if (!tmpl->data[0][rr * width +
cc])
71 int r2min = std::max(0, rr - radius);
72 int r2max = std::min(height, rr + radius);
74 int c2min = std::max(0,
cc - radius);
75 int c2max = std::min(width,
cc + radius);
77 for (
int r2 = r2min; r2 <= r2max; r2++)
79 for (
int c2 = c2min; c2 <= c2max; c2++)
81 if (test->data[0][r2 * width + c2])
97 bool readMatches(
const QString&
filename,
unsigned short *matches,
long long nframes)
99 QByteArray fname =
filename.toLocal8Bit();
100 FILE *fp = fopen(fname.constData(),
"r");
104 for (
long long frameno = 0; frameno < nframes; frameno++)
106 int nitems = fscanf(fp,
"%20hu", &matches[frameno]);
109 LOG(VB_COMMFLAG, LOG_ERR,
110 QString(
"Not enough data in %1: frame %2")
117 LOG(VB_COMMFLAG, LOG_ERR, QString(
"Error closing %1: %2")
123 LOG(VB_COMMFLAG, LOG_ERR, QString(
"Error closing %1: %2")
128 bool writeMatches(
const QString&
filename,
unsigned short *matches,
long long nframes)
130 QByteArray fname =
filename.toLocal8Bit();
131 FILE *fp = fopen(fname.constData(),
"w");
135 for (
long long frameno = 0; frameno < nframes; frameno++)
136 (
void)fprintf(fp,
"%hu\n", matches[frameno]);
139 LOG(VB_COMMFLAG, LOG_ERR, QString(
"Error closing %1: %2")
144 int finishedDebug(
long long nframes,
const unsigned short *matches,
145 const unsigned char *match)
147 ushort score = matches[0];
150 long long startframe = 0;
152 for (
long long frameno = 1; frameno < nframes; frameno++)
154 score = matches[frameno];
155 if (match[frameno - 1] == match[frameno])
164 LOG(VB_COMMFLAG, LOG_INFO, QString(
"Frame %1-%2: %3 L-H: %4-%5 (%6)")
165 .
arg(startframe, 6).
arg(frameno - 1, 6)
166 .
arg(match[frameno - 1] ?
"logo " :
" no-logo")
167 .
arg(low, 4).
arg(high, 4).
arg(frameno - startframe, 5));
171 startframe = frameno;
179 return *(
unsigned short*)aa - *(
unsigned short*)bb;
182 long long matchspn(
long long nframes,
const unsigned char *match,
long long frameno,
183 unsigned char acceptval)
189 while (frameno < nframes && match[frameno] == acceptval)
194 unsigned int range_area(
const unsigned short *
freq,
unsigned short start,
198 const unsigned short width = end - start;
202 for (ushort matchcnt = start; matchcnt < end; matchcnt++)
206 sum +=
freq[matchcnt];
212 return width * sum / nsamples;
215 unsigned short pick_mintmpledges(
const unsigned short *matches,
238 static constexpr
float kLeftWidth = 0.04;
239 static constexpr
float kMiddleWidth = 0.04;
240 static constexpr
float kRightWidth = 0.04;
242 static constexpr
float kMatchStart = 0.20;
243 static constexpr
float kMatchEnd = 0.80;
245 auto *sorted =
new unsigned short[nframes];
246 memcpy(sorted, matches, nframes *
sizeof(*matches));
248 ushort minmatch = sorted[0];
249 ushort maxmatch = sorted[nframes - 1];
250 ushort matchrange = maxmatch - minmatch;
253 auto leftwidth = (
unsigned short)(kLeftWidth * matchrange);
254 auto middlewidth = (
unsigned short)(kMiddleWidth * matchrange);
255 auto rightwidth = (
unsigned short)(kRightWidth * matchrange);
257 int nfreq = maxmatch + 1;
258 auto *
freq =
new unsigned short[nfreq];
259 memset(
freq, 0, nfreq *
sizeof(*
freq));
260 for (
long long frameno = 0; frameno < nframes; frameno++)
261 freq[matches[frameno]]++;
263 ushort matchstart = minmatch + (
unsigned short)(kMatchStart * matchrange);
264 ushort matchend = minmatch + (
unsigned short)(kMatchEnd * matchrange);
266 int local_minimum = matchstart;
268 for (
int matchcnt = matchstart + leftwidth + middlewidth / 2;
269 matchcnt < matchend - rightwidth - middlewidth / 2;
272 ushort p0 = matchcnt - leftwidth - middlewidth / 2;
273 ushort
p1 = p0 + leftwidth;
274 ushort
p2 =
p1 + middlewidth;
275 ushort p3 =
p2 + rightwidth;
280 if (middlescore < leftscore && middlescore < rightscore)
282 unsigned int delta = (leftscore - middlescore) +
283 (rightscore - middlescore);
284 if (delta > maxdelta)
286 local_minimum = matchcnt;
292 LOG(VB_COMMFLAG, LOG_INFO,
293 QString(
"pick_mintmpledges minmatch=%1 maxmatch=%2"
294 " matchstart=%3 matchend=%4 widths=%5,%6,%7 local_minimum=%8")
295 .
arg(minmatch).
arg(maxmatch).
arg(matchstart).
arg(matchend)
296 .
arg(leftwidth).
arg(middlewidth).
arg(rightwidth)
297 .
arg(local_minimum));
301 return local_minimum;
307 std::shared_ptr<EdgeDetector> ed,
309 m_pgmConverter(
std::move(pgmc)),
310 m_edgeDetector(
std::move(ed)), m_templateFinder(tf),
311 m_debugDir(debugdir),
313 m_debugData(debugdir +
"/TemplateMatcher-pgm.txt")
315 m_debugData(debugdir +
"/TemplateMatcher-yuv.txt")
353 LOG(VB_COMMFLAG, LOG_ERR,
354 QString(
"TemplateMatcher::MythPlayerInited: no template"));
361 LOG(VB_COMMFLAG, LOG_ERR,
362 QString(
"TemplateMatcher::MythPlayerInited "
363 "av_image_alloc cropped (%1x%2) failed")
374 m_match =
new unsigned char[nframes];
380 LOG(VB_COMMFLAG, LOG_INFO,
381 QString(
"TemplateMatcher::MythPlayerInited read %1")
399 long long *pNextFrame)
416 const int FRAMESGMPCTILE = 70;
434 const int JITTER_RADIUS = 0;
436 const AVFrame *edges =
nullptr;
439 std::chrono::microseconds start {0us};
440 std::chrono::microseconds end {0us};
448 start = nowAsDuration<std::chrono::microseconds>();
461 end = nowAsDuration<std::chrono::microseconds>();
467 LOG(VB_COMMFLAG, LOG_ERR,
468 QString(
"TemplateMatcher::analyzeFrame error at frame %1 of %2")
485 const int MINBREAKLEN = (int)roundf(45 *
m_fps);
486 const int MINSEGLEN = (int)roundf(105 *
m_fps);
496 LOG(VB_COMMFLAG, LOG_INFO,
503 int mintmpledges = pick_mintmpledges(
m_matches, nframes);
505 LOG(VB_COMMFLAG, LOG_INFO,
506 QString(
"TemplateMatcher::finished %1x%2@(%3,%4),"
507 " %5 edge pixels, want %6")
509 .
arg(tmpledges).
arg(mintmpledges));
511 for (
long long ii = 0; ii < nframes; ii++)
525 while (brkb < nframes)
529 long long brklen = brke - brkb;
543 if (minbreaklen <= MINBREAKLEN)
549 if (minseglen <= MINSEGLEN)
555 if (minbreaklen > MINBREAKLEN && minseglen > MINSEGLEN)
578 LOG(VB_COMMFLAG, LOG_INFO, QString(
"TM Time: analyze=%1s")
597 const int MINBREAKS = nframes * 20 / 100;
598 const int MAXBREAKS = nframes * 45 / 100;
601 const bool good = brklen >= MINBREAKS && brklen <= MAXBREAKS;
607 LOG(VB_COMMFLAG, LOG_ERR,
608 QString(
"TemplateMatcher: no template (wanted %2-%3%)")
609 .
arg(100 * MINBREAKS / nframes)
610 .
arg(100 * MAXBREAKS / nframes));
614 LOG(VB_COMMFLAG, LOG_ERR,
615 QString(
"TemplateMatcher has %1% breaks (real-time flagging)")
616 .
arg(100 * brklen / nframes));
620 LOG(VB_COMMFLAG, LOG_INFO, QString(
"TemplateMatcher has %1% breaks")
621 .
arg(100 * brklen / nframes));
625 LOG(VB_COMMFLAG, LOG_INFO,
626 QString(
"TemplateMatcher has %1% breaks (wanted %2-%3%)")
627 .
arg(100 * brklen / nframes)
628 .
arg(100 * MINBREAKS / nframes)
629 .
arg(100 * MAXBREAKS / nframes));
636 return brklen < MINBREAKS ? 1 : brklen <= MAXBREAKS ? 0 : -1;
712 const int BLANK_NEARBY = (int)roundf(0.5F *
m_fps);
713 const int TEMPLATE_DISAPPEARS_EARLY = (int)roundf(25 *
m_fps);
714 const int TEMPLATE_DISAPPEARS_LATE = (int)roundf(0 *
m_fps);
715 const int TEMPLATE_REAPPEARS_LATE = (int)roundf(35 *
m_fps);
716 const int TEMPLATE_REAPPEARS_EARLY = (int)roundf(1.5F *
m_fps);
718 LOG(VB_COMMFLAG, LOG_INFO, QString(
"TemplateMatcher adjusting for blanks"));
720 FrameAnalyzer::FrameMap::Iterator ii =
m_breakMap.begin();
721 long long prevbrke = 0;
724 FrameAnalyzer::FrameMap::Iterator iinext = ii;
732 const long long brkb = ii.key();
733 const long long brke = brkb + *ii;
734 FrameAnalyzer::FrameMap::const_iterator jj = blankMap->constEnd();
739 brkb - std::max(BLANK_NEARBY, TEMPLATE_DISAPPEARS_LATE)),
741 brkb + std::max(BLANK_NEARBY, TEMPLATE_DISAPPEARS_EARLY)));
743 long long newbrkb = brkb;
744 if (jj != blankMap->constEnd())
747 long long adj = *jj / 2;
760 brke - std::max(BLANK_NEARBY, TEMPLATE_REAPPEARS_LATE)),
761 std::min(iinext ==
m_breakMap.end() ? nframes : iinext.key(),
762 brke + std::max(BLANK_NEARBY, TEMPLATE_REAPPEARS_EARLY)));
763 long long newbrke = brke;
764 if (kk != blankMap->constEnd())
778 long long newbrklen = newbrke - newbrkb;
782 if (newbrkb < nframes && newbrklen)
785 else if (newbrke != brke)
815 breaks->insert(bb.key(), *bb);