27 #include "libavutil/imgutils.h"
37 const int width = pict->linesize[0];
38 const int size = height * width;
41 for (
int ii = 0; ii < size; ii++)
42 if (pict->data[0][ii])
48 int radius,
unsigned short *pscore)
51 const int width = tmpl->linesize[0];
53 if (width != test->linesize[0])
55 LOG(VB_COMMFLAG, LOG_ERR,
56 QString(
"pgm_match widths don't match: %1 != %2")
57 .arg(width).arg(test->linesize[0]));
62 for (
int rr = 0; rr < height; rr++)
64 for (
int cc = 0;
cc < width;
cc++)
66 if (!tmpl->data[0][(rr * width) +
cc])
69 int r2min = std::max(0, rr - radius);
70 int r2max = std::min(height, rr + radius);
72 int c2min = std::max(0,
cc - radius);
73 int c2max = std::min(width,
cc + radius);
75 for (
int r2 = r2min; r2 <= r2max; r2++)
77 for (
int c2 = c2min; c2 <= c2max; c2++)
79 if (test->data[0][(r2 * width) + c2])
97 QByteArray fname =
filename.toLocal8Bit();
98 FILE *fp = fopen(fname.constData(),
"r");
102 for (
long long frameno = 0; frameno < nframes; frameno++)
104 int nitems = fscanf(fp,
"%20hu", &matches[frameno]);
107 LOG(VB_COMMFLAG, LOG_ERR,
108 QString(
"Not enough data in %1: frame %2")
115 LOG(VB_COMMFLAG, LOG_ERR, QString(
"Error closing %1: %2")
121 LOG(VB_COMMFLAG, LOG_ERR, QString(
"Error closing %1: %2")
128 QByteArray fname =
filename.toLocal8Bit();
129 FILE *fp = fopen(fname.constData(),
"w");
133 for (
long long frameno = 0; frameno < nframes; frameno++)
134 (
void)fprintf(fp,
"%hu\n", matches[frameno]);
137 LOG(VB_COMMFLAG, LOG_ERR, QString(
"Error closing %1: %2")
143 const unsigned char *match)
145 ushort score = matches[0];
148 long long startframe = 0;
150 for (
long long frameno = 1; frameno < nframes; frameno++)
152 score = matches[frameno];
153 if (match[frameno - 1] == match[frameno])
155 low = std::min(score, low);
156 high = std::max(score, high);
160 LOG(VB_COMMFLAG, LOG_INFO, QString(
"Frame %1-%2: %3 L-H: %4-%5 (%6)")
161 .arg(startframe, 6).arg(frameno - 1, 6)
162 .arg(match[frameno - 1] ?
"logo " :
" no-logo")
163 .arg(low, 4).arg(high, 4).arg(frameno - startframe, 5));
167 startframe = frameno;
175 return *(
unsigned short*)aa - *(
unsigned short*)bb;
178 long long matchspn(
long long nframes,
const unsigned char *match,
long long frameno,
179 unsigned char acceptval)
185 while (frameno < nframes && match[frameno] == acceptval)
194 const unsigned short width = end - start;
198 for (ushort matchcnt = start; matchcnt < end; matchcnt++)
202 sum +=
freq[matchcnt];
208 return width * sum / nsamples;
234 static constexpr
float kLeftWidth = 0.04;
235 static constexpr
float kMiddleWidth = 0.04;
236 static constexpr
float kRightWidth = 0.04;
238 static constexpr
float kMatchStart = 0.20;
239 static constexpr
float kMatchEnd = 0.80;
241 auto *sorted =
new unsigned short[nframes];
242 memcpy(sorted, matches, nframes *
sizeof(*matches));
244 ushort minmatch = sorted[0];
245 ushort maxmatch = sorted[nframes - 1];
246 ushort matchrange = maxmatch - minmatch;
249 auto leftwidth = (
unsigned short)(kLeftWidth * matchrange);
250 auto middlewidth = (
unsigned short)(kMiddleWidth * matchrange);
251 auto rightwidth = (
unsigned short)(kRightWidth * matchrange);
253 int nfreq = maxmatch + 1;
254 auto *
freq =
new unsigned short[nfreq];
255 memset(
freq, 0, nfreq *
sizeof(*
freq));
256 for (
long long frameno = 0; frameno < nframes; frameno++)
257 freq[matches[frameno]]++;
259 ushort matchstart = minmatch + (
unsigned short)(kMatchStart * matchrange);
260 ushort matchend = minmatch + (
unsigned short)(kMatchEnd * matchrange);
262 int local_minimum = matchstart;
264 for (
int matchcnt = matchstart + leftwidth + (middlewidth / 2);
265 matchcnt < matchend - rightwidth - middlewidth / 2;
268 ushort p0 = matchcnt - leftwidth - (middlewidth / 2);
269 ushort
p1 = p0 + leftwidth;
270 ushort
p2 =
p1 + middlewidth;
271 ushort p3 =
p2 + rightwidth;
276 if (middlescore < leftscore && middlescore < rightscore)
278 unsigned int delta = (leftscore - middlescore) +
279 (rightscore - middlescore);
280 if (delta > maxdelta)
282 local_minimum = matchcnt;
288 LOG(VB_COMMFLAG, LOG_INFO,
289 QString(
"pick_mintmpledges minmatch=%1 maxmatch=%2"
290 " matchstart=%3 matchend=%4 widths=%5,%6,%7 local_minimum=%8")
291 .arg(minmatch).arg(maxmatch).arg(matchstart).arg(matchend)
292 .arg(leftwidth).arg(middlewidth).arg(rightwidth)
293 .arg(local_minimum));
297 return local_minimum;
303 std::shared_ptr<EdgeDetector> ed,
305 m_pgmConverter(std::move(pgmc)),
306 m_edgeDetector(std::move(ed)), m_templateFinder(tf),
307 m_debugDir(debugdir),
309 m_debugData(debugdir +
"/TemplateMatcher-pgm.txt")
311 m_debugData(debugdir +
"/TemplateMatcher-yuv.txt")
325 QString(
"TemplateMatcher debugLevel %1").arg(
m_debugLevel));
336 av_freep(
reinterpret_cast<void*
>(&
m_cropped.data[0]));
350 LOG(VB_COMMFLAG, LOG_ERR,
351 QString(
"TemplateMatcher::MythPlayerInited: no template"));
358 LOG(VB_COMMFLAG, LOG_ERR,
359 QString(
"TemplateMatcher::MythPlayerInited "
360 "av_image_alloc cropped (%1x%2) failed")
371 m_match =
new unsigned char[nframes];
377 LOG(VB_COMMFLAG, LOG_INFO,
378 QString(
"TemplateMatcher::MythPlayerInited read %1")
390 av_freep(
reinterpret_cast<void*
>(&
m_cropped.data[0]));
396 long long *pNextFrame)
413 const int FRAMESGMPCTILE = 70;
431 const int JITTER_RADIUS = 0;
433 const AVFrame *edges =
nullptr;
436 std::chrono::microseconds start {0us};
437 std::chrono::microseconds end {0us};
445 start = nowAsDuration<std::chrono::microseconds>();
452 if (edges ==
nullptr)
458 end = nowAsDuration<std::chrono::microseconds>();
464 LOG(VB_COMMFLAG, LOG_ERR,
465 QString(
"TemplateMatcher::analyzeFrame error at frame %1 of %2")
482 const int MINBREAKLEN = (int)roundf(45 *
m_fps);
483 const int MINSEGLEN = (int)roundf(105 *
m_fps);
493 LOG(VB_COMMFLAG, LOG_INFO,
494 QString(
"TemplateMatcher::finished wrote %1") .arg(
m_debugData));
502 LOG(VB_COMMFLAG, LOG_INFO,
503 QString(
"TemplateMatcher::finished %1x%2@(%3,%4),"
504 " %5 edge pixels, want %6")
506 .arg(tmpledges).arg(mintmpledges));
508 for (
long long ii = 0; ii < nframes; ii++)
522 while (brkb < nframes)
526 long long brklen = brke - brkb;
540 if (minbreaklen <= MINBREAKLEN)
546 if (minseglen <= MINSEGLEN)
552 if (minbreaklen > MINBREAKLEN && minseglen > MINSEGLEN)
575 LOG(VB_COMMFLAG, LOG_INFO, QString(
"TM Time: analyze=%1s")
594 static const int64_t MINBREAKS { nframes * 20 / 100 };
595 static const int64_t MAXBREAKS { nframes * 45 / 100 };
598 const bool good = brklen >= MINBREAKS && brklen <= MAXBREAKS;
604 LOG(VB_COMMFLAG, LOG_ERR,
605 QString(
"TemplateMatcher: no template (wanted %2-%3%)")
606 .arg(100 * MINBREAKS / nframes)
607 .arg(100 * MAXBREAKS / nframes));
611 LOG(VB_COMMFLAG, LOG_ERR,
612 QString(
"TemplateMatcher has %1% breaks (real-time flagging)")
613 .arg(100 * brklen / nframes));
617 LOG(VB_COMMFLAG, LOG_INFO, QString(
"TemplateMatcher has %1% breaks")
618 .arg(100 * brklen / nframes));
622 LOG(VB_COMMFLAG, LOG_INFO,
623 QString(
"TemplateMatcher has %1% breaks (wanted %2-%3%)")
624 .arg(100 * brklen / nframes)
625 .arg(100 * MINBREAKS / nframes)
626 .arg(100 * MAXBREAKS / nframes));
633 if (brklen < MINBREAKS)
635 if (brklen <= MAXBREAKS)
713 const int BLANK_NEARBY = (int)roundf(0.5F *
m_fps);
714 const int TEMPLATE_DISAPPEARS_EARLY = (int)roundf(25 *
m_fps);
715 const int TEMPLATE_DISAPPEARS_LATE = (int)roundf(0 *
m_fps);
716 const int TEMPLATE_REAPPEARS_LATE = (int)roundf(35 *
m_fps);
717 const int TEMPLATE_REAPPEARS_EARLY = (int)roundf(1.5F *
m_fps);
719 LOG(VB_COMMFLAG, LOG_INFO, QString(
"TemplateMatcher adjusting for blanks"));
721 FrameAnalyzer::FrameMap::Iterator ii =
m_breakMap.begin();
722 long long prevbrke = 0;
725 FrameAnalyzer::FrameMap::Iterator iinext = ii;
733 const long long brkb = ii.key();
734 const long long brke = brkb + *ii;
735 FrameAnalyzer::FrameMap::const_iterator jj = blankMap->constEnd();
740 brkb - std::max(BLANK_NEARBY, TEMPLATE_DISAPPEARS_LATE)),
742 brkb + std::max(BLANK_NEARBY, TEMPLATE_DISAPPEARS_EARLY)));
744 long long newbrkb = brkb;
745 if (jj != blankMap->constEnd())
748 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())
777 long long newbrklen = newbrke - newbrkb;
781 if (newbrkb < nframes && newbrklen)
784 else if (newbrke != brke)
816 breaks->insert(bb.key(), *bb);