00001
00002 #include <sys/time.h>
00003
00004
00005 #include <cmath>
00006 #include <cstdlib>
00007
00008
00009 #include <QFile>
00010 #include <QFileInfo>
00011 #include <QTextStream>
00012
00013
00014 #include "mythplayer.h"
00015 #include "mythcorecontext.h"
00016 #include "frame.h"
00017 #include "mythmiscutil.h"
00018 #include "mythsystem.h"
00019 #include "exitcodes.h"
00020
00021
00022 #include "CommDetector2.h"
00023 #include "pgm.h"
00024 #include "PGMConverter.h"
00025 #include "BorderDetector.h"
00026 #include "EdgeDetector.h"
00027 #include "TemplateFinder.h"
00028
00029 using namespace commDetector2;
00030
00031 namespace {
00032
00033 int writeJPG(QString prefix, const AVPicture *img, int imgheight)
00034 {
00035 const int imgwidth = img->linesize[0];
00036 QFileInfo jpgfi(prefix + ".jpg");
00037 if (!jpgfi.exists())
00038 {
00039 QFile pgmfile(prefix + ".pgm");
00040 if (!pgmfile.exists())
00041 {
00042 QByteArray pfname = pgmfile.fileName().toLocal8Bit();
00043 if (pgm_write(img->data[0], imgwidth, imgheight,
00044 pfname.constData()))
00045 {
00046 return -1;
00047 }
00048 }
00049
00050 QString cmd = QString("convert -quality 50 -resize 192x144 %1 %2")
00051 .arg(pgmfile.fileName()).arg(jpgfi.filePath());
00052 if (myth_system(cmd) != GENERIC_EXIT_OK)
00053 return -1;
00054
00055 if (!pgmfile.remove())
00056 {
00057 LOG(VB_COMMFLAG, LOG_ERR,
00058 QString("TemplateFinder.writeJPG error removing %1 (%2)")
00059 .arg(pgmfile.fileName()).arg(strerror(errno)));
00060 return -1;
00061 }
00062 }
00063 return 0;
00064 }
00065
00066 int
00067 pgm_scorepixels(unsigned int *scores, int width, int row, int col,
00068 const AVPicture *src, int srcheight)
00069 {
00070
00071 const int srcwidth = src->linesize[0];
00072 int rr, cc;
00073
00074 for (rr = 0; rr < srcheight; rr++)
00075 {
00076 for (cc = 0; cc < srcwidth; cc++)
00077 {
00078 if (src->data[0][rr * srcwidth + cc])
00079 scores[(row + rr) * width + col + cc]++;
00080 }
00081 }
00082
00083 return 0;
00084 }
00085
00086 int
00087 sort_ascending(const void *aa, const void *bb)
00088 {
00089 return *(unsigned int*)aa - *(unsigned int*)bb;
00090 }
00091
00092 float
00093 bounding_score(const AVPicture *img, int row, int col, int width, int height)
00094 {
00095
00096 const int imgwidth = img->linesize[0];
00097 unsigned int score;
00098 int rr, cc, rr2, cc2;
00099
00100 score = 0;
00101 rr2 = row + height;
00102 cc2 = col + width;
00103 for (rr = row; rr < rr2; rr++)
00104 {
00105 for (cc = col; cc < cc2; cc++)
00106 {
00107 if (img->data[0][rr * imgwidth + cc])
00108 score++;
00109 }
00110 }
00111 return (float)score / (width * height);
00112 }
00113
00114 bool
00115 rowisempty(const AVPicture *img, int row, int col, int width)
00116 {
00117 const int imgwidth = img->linesize[0];
00118 for (int cc = col; cc < col + width; cc++)
00119 if (img->data[0][row * imgwidth + cc])
00120 return false;
00121 return true;
00122 }
00123
00124 bool
00125 colisempty(const AVPicture *img, int col, int row, int height)
00126 {
00127 const int imgwidth = img->linesize[0];
00128 for (int rr = row; rr < row + height; rr++)
00129 if (img->data[0][rr * imgwidth + col])
00130 return false;
00131 return true;
00132 }
00133
00134 int
00135 bounding_box(const AVPicture *img, int imgheight,
00136 int minrow, int mincol, int maxrow1, int maxcol1,
00137 int *prow, int *pcol, int *pwidth, int *pheight)
00138 {
00139 const int imgwidth = img->linesize[0];
00140
00141
00142
00143
00144
00145
00146 static const int MAXWIDTHPCT = 20;
00147 static const int MAXHEIGHTPCT = 20;
00148
00149
00150
00151
00152
00153
00154
00155
00156 const int VERTSLOP = max(4, imgheight * 1 / 15);
00157 const int HORIZSLOP = max(4, imgwidth * 1 / 20);
00158
00159 int maxwidth, maxheight;
00160 int width, height, row, col;
00161 int newrow, newcol, newright, newbottom;
00162
00163 maxwidth = (maxcol1 - mincol) * MAXWIDTHPCT / 100;
00164 maxheight = (maxrow1 - minrow) * MAXHEIGHTPCT / 100;
00165
00166 row = minrow;
00167 col = mincol;
00168 width = maxcol1 - mincol;
00169 height = maxrow1 - minrow;
00170
00171 for (;;)
00172 {
00173 float score, newscore;
00174 int ii;
00175 bool improved = false;
00176
00177 LOG(VB_COMMFLAG, LOG_INFO, QString("bounding_box %1x%2@(%3,%4)")
00178 .arg(width).arg(height).arg(col).arg(row));
00179
00180
00181 score = bounding_score(img, row, col, width, height);
00182 newrow = row;
00183 for (ii = 1; ii < height; ii++)
00184 {
00185 if ((newscore = bounding_score(img, row + ii, col,
00186 width, height - ii)) < score)
00187 break;
00188 score = newscore;
00189 newrow = row + ii;
00190 improved = true;
00191 }
00192
00193
00194 score = bounding_score(img, row, col, width, height);
00195 newcol = col;
00196 for (ii = 1; ii < width; ii++)
00197 {
00198 if ((newscore = bounding_score(img, row, col + ii,
00199 width - ii, height)) < score)
00200 break;
00201 score = newscore;
00202 newcol = col + ii;
00203 improved = true;
00204 }
00205
00206
00207 score = bounding_score(img, row, col, width, height);
00208 newbottom = row + height;
00209 for (ii = 1; ii < height; ii++)
00210 {
00211 if ((newscore = bounding_score(img, row, col,
00212 width, height - ii)) < score)
00213 break;
00214 score = newscore;
00215 newbottom = row + height - ii;
00216 improved = true;
00217 }
00218
00219
00220 score = bounding_score(img, row, col, width, height);
00221 newright = col + width;
00222 for (ii = 1; ii < width; ii++)
00223 {
00224 if ((newscore = bounding_score(img, row, col,
00225 width - ii, height)) < score)
00226 break;
00227 score = newscore;
00228 newright = col + width - ii;
00229 improved = true;
00230 }
00231
00232 if (!improved)
00233 break;
00234
00235 row = newrow;
00236 col = newcol;
00237 width = newright - newcol;
00238 height = newbottom - newrow;
00239
00240
00241
00242
00243
00244
00245
00246
00247
00248
00249
00250
00251
00252
00253
00254
00255
00256
00257
00258
00259 if (width > maxwidth)
00260 {
00261
00262 int chop = width / 3;
00263 int chopwidth = width - chop;
00264 float left, right, minscore, maxscore;
00265
00266 left = bounding_score(img, row, col, chopwidth, height);
00267 right = bounding_score(img, row, col + chop, chopwidth, height);
00268 LOG(VB_COMMFLAG, LOG_INFO,
00269 QString("bounding_box too wide (%1 > %2); left=%3, right=%4")
00270 .arg(width).arg(maxwidth)
00271 .arg(left, 0, 'f', 3).arg(right, 0, 'f', 3));
00272 minscore = min(left, right);
00273 maxscore = max(left, right);
00274 if (maxscore < 3 * minscore / 2)
00275 {
00276
00277
00278
00279
00280
00281 LOG(VB_COMMFLAG, LOG_ERR, "bounding_box giving up (edge "
00282 "pixels distributed too uniformly)");
00283 return -1;
00284 }
00285
00286 if (left < right)
00287 col += chop;
00288 width -= chop;
00289 continue;
00290 }
00291
00292 if (height > maxheight)
00293 {
00294
00295 int chop = height / 3;
00296 int chopheight = height - chop;
00297 float upper, lower, minscore, maxscore;
00298
00299 upper = bounding_score(img, row, col, width, chopheight);
00300 lower = bounding_score(img, row + chop, col, width, chopheight);
00301 LOG(VB_COMMFLAG, LOG_INFO,
00302 QString("bounding_box too tall (%1 > %2); upper=%3, lower=%4")
00303 .arg(height).arg(maxheight)
00304 .arg(upper, 0, 'f', 3).arg(lower, 0, 'f', 3));
00305 minscore = min(upper, lower);
00306 maxscore = max(upper, lower);
00307 if (maxscore < 3 * minscore / 2)
00308 {
00309
00310
00311
00312
00313
00314 LOG(VB_COMMFLAG, LOG_ERR, "bounding_box giving up (edge "
00315 "pixel distribution too uniform)");
00316 return -1;
00317 }
00318
00319 if (upper < lower)
00320 row += chop;
00321 height -= chop;
00322 continue;
00323 }
00324
00325 break;
00326 }
00327
00328
00329
00330
00331
00332
00333
00334
00335 LOG(VB_COMMFLAG, LOG_INFO,
00336 QString("bounding_box %1x%2@(%3,%4); horizslop=%5,vertslop=%6")
00337 .arg(width).arg(height).arg(col).arg(row)
00338 .arg(HORIZSLOP).arg(VERTSLOP));
00339
00340
00341 newrow = row - 1;
00342 for (;;)
00343 {
00344 if (newrow <= minrow)
00345 {
00346 newrow = minrow;
00347 break;
00348 }
00349 if (row - newrow >= VERTSLOP)
00350 {
00351 newrow = row - VERTSLOP;
00352 break;
00353 }
00354 if (rowisempty(img, newrow, col, width))
00355 {
00356 newrow++;
00357 break;
00358 }
00359 newrow--;
00360 }
00361 newrow = max(minrow, newrow - 1);
00362
00363
00364 newcol = col - 1;
00365 for (;;)
00366 {
00367 if (newcol <= mincol)
00368 {
00369 newcol = mincol;
00370 break;
00371 }
00372 if (col - newcol >= HORIZSLOP)
00373 {
00374 newcol = col - HORIZSLOP;
00375 break;
00376 }
00377 if (colisempty(img, newcol, row, height))
00378 {
00379 newcol++;
00380 break;
00381 }
00382 newcol--;
00383 }
00384 newcol = max(mincol, newcol - 1);
00385
00386
00387 newright = col + width;
00388 for (;;)
00389 {
00390 if (newright >= maxcol1)
00391 {
00392 newright = maxcol1;
00393 break;
00394 }
00395 if (newright - (col + width) >= HORIZSLOP)
00396 {
00397 newright = col + width + HORIZSLOP;
00398 break;
00399 }
00400 if (colisempty(img, newright, row, height))
00401 break;
00402 newright++;
00403 }
00404 newright = min(maxcol1, newright + 1);
00405
00406
00407 newbottom = row + height;
00408 for (;;)
00409 {
00410 if (newbottom >= maxrow1)
00411 {
00412 newbottom = maxrow1;
00413 break;
00414 }
00415 if (newbottom - (row + height) >= VERTSLOP)
00416 {
00417 newbottom = row + height + VERTSLOP;
00418 break;
00419 }
00420 if (rowisempty(img, newbottom, col, width))
00421 break;
00422 newbottom++;
00423 }
00424 newbottom = min(maxrow1, newbottom + 1);
00425
00426 row = newrow;
00427 col = newcol;
00428 width = newright - newcol;
00429 height = newbottom - newrow;
00430
00431 LOG(VB_COMMFLAG, LOG_INFO, QString("bounding_box %1x%2@(%3,%4)")
00432 .arg(width).arg(height).arg(col).arg(row));
00433
00434 *prow = row;
00435 *pcol = col;
00436 *pwidth = width;
00437 *pheight = height;
00438 return 0;
00439 }
00440
00441 int
00442 template_alloc(const unsigned int *scores, int width, int height,
00443 int minrow, int mincol, int maxrow1, int maxcol1, AVPicture *tmpl,
00444 int *ptmplrow, int *ptmplcol, int *ptmplwidth, int *ptmplheight,
00445 bool debug_edgecounts, QString debugdir)
00446 {
00447
00448
00449
00450
00451
00452
00453
00454
00455
00456 static const float MINSCOREPCTILE = 0.998;
00457
00458 const int nn = width * height;
00459 int ii, first, last;
00460 unsigned int *sortedscores, threshscore;
00461 AVPicture thresh;
00462
00463 if (avpicture_alloc(&thresh, PIX_FMT_GRAY8, width, height))
00464 {
00465 LOG(VB_COMMFLAG, LOG_ERR,
00466 QString("template_alloc avpicture_alloc thresh (%1x%2) failed")
00467 .arg(width).arg(height));
00468 return -1;
00469 }
00470
00471 sortedscores = new unsigned int[nn];
00472 memcpy(sortedscores, scores, nn * sizeof(*sortedscores));
00473 qsort(sortedscores, nn, sizeof(*sortedscores), sort_ascending);
00474
00475 if (sortedscores[0] == sortedscores[nn - 1])
00476 {
00477
00478 LOG(VB_COMMFLAG, LOG_ERR,
00479 QString("template_alloc: %1x%2 pixels all identical!")
00480 .arg(width).arg(height));
00481 goto free_thresh;
00482 }
00483
00484
00485
00486 ii = (int)roundf(nn * MINSCOREPCTILE);
00487 threshscore = sortedscores[ii];
00488 for (first = ii; first > 0 && sortedscores[first] == threshscore; first--)
00489 ;
00490 if (sortedscores[first] != threshscore)
00491 first++;
00492 for (last = ii; last < nn - 1 && sortedscores[last] == threshscore; last++)
00493 ;
00494 if (sortedscores[last] != threshscore)
00495 last--;
00496
00497 LOG(VB_COMMFLAG, LOG_INFO, QString("template_alloc wanted %1, got %2-%3")
00498 .arg(MINSCOREPCTILE, 0, 'f', 6)
00499 .arg((float)first / nn, 0, 'f', 6)
00500 .arg((float)last / nn, 0, 'f', 6));
00501
00502 for (ii = 0; ii < nn; ii++)
00503 thresh.data[0][ii] = scores[ii] >= threshscore ? UCHAR_MAX : 0;
00504
00505 if (debug_edgecounts)
00506 {
00507
00508 AVPicture scored;
00509 if (avpicture_alloc(&scored, PIX_FMT_GRAY8, width, height))
00510 {
00511 LOG(VB_COMMFLAG, LOG_ERR,
00512 QString("template_alloc avpicture_alloc scored (%1x%2) failed")
00513 .arg(width).arg(height));
00514 goto free_thresh;
00515 }
00516 unsigned int maxscore = sortedscores[nn - 1];
00517 for (ii = 0; ii < nn; ii++)
00518 scored.data[0][ii] = scores[ii] * UCHAR_MAX / maxscore;
00519 int error = writeJPG(debugdir + "/TemplateFinder-scores", &scored,
00520 height);
00521 avpicture_free(&scored);
00522 if (error)
00523 goto free_thresh;
00524
00525
00526 if (writeJPG(debugdir + "/TemplateFinder-edgecounts", &thresh, height))
00527 goto free_thresh;
00528 }
00529
00530
00531
00532 if (bounding_box(&thresh, height, minrow, mincol, maxrow1, maxcol1,
00533 ptmplrow, ptmplcol, ptmplwidth, ptmplheight))
00534 goto free_thresh;
00535
00536 if ((uint)(*ptmplwidth * *ptmplheight) > USHRT_MAX)
00537 {
00538
00539 LOG(VB_COMMFLAG, LOG_ERR,
00540 QString("template_alloc bounding_box too big (%1x%2)")
00541 .arg(*ptmplwidth).arg(*ptmplheight));
00542 goto free_thresh;
00543 }
00544
00545 if (avpicture_alloc(tmpl, PIX_FMT_GRAY8, *ptmplwidth, *ptmplheight))
00546 {
00547 LOG(VB_COMMFLAG, LOG_ERR,
00548 QString("template_alloc avpicture_alloc tmpl (%1x%2) failed")
00549 .arg(*ptmplwidth).arg(*ptmplheight));
00550 goto free_thresh;
00551 }
00552
00553 if (pgm_crop(tmpl, &thresh, height, *ptmplrow, *ptmplcol,
00554 *ptmplwidth, *ptmplheight))
00555 goto free_thresh;
00556
00557 delete []sortedscores;
00558 avpicture_free(&thresh);
00559 return 0;
00560
00561 free_thresh:
00562 delete []sortedscores;
00563 avpicture_free(&thresh);
00564 return -1;
00565 }
00566
00567 int
00568 analyzeFrameDebug(long long frameno, const AVPicture *pgm, int pgmheight,
00569 const AVPicture *cropped, const AVPicture *edges, int cropheight,
00570 int croprow, int cropcol, bool debug_frames, QString debugdir)
00571 {
00572 static const int delta = 24;
00573 static int lastrow, lastcol, lastwidth, lastheight;
00574 const int cropwidth = cropped->linesize[0];
00575 int rowsame, colsame, widthsame, heightsame;
00576
00577 rowsame = abs(lastrow - croprow) <= delta ? 1 : 0;
00578 colsame = abs(lastcol - cropcol) <= delta ? 1 : 0;
00579 widthsame = abs(lastwidth - cropwidth) <= delta ? 1 : 0;
00580 heightsame = abs(lastheight - cropheight) <= delta ? 1 : 0;
00581
00582 if (frameno > 0 && rowsame + colsame + widthsame + heightsame >= 3)
00583 return 0;
00584
00585 LOG(VB_COMMFLAG, LOG_INFO,
00586 QString("TemplateFinder Frame %1: %2x%3@(%4,%5)")
00587 .arg(frameno, 5)
00588 .arg(cropwidth).arg(cropheight)
00589 .arg(cropcol).arg(croprow));
00590
00591 lastrow = croprow;
00592 lastcol = cropcol;
00593 lastwidth = cropwidth;
00594 lastheight = cropheight;
00595
00596 if (debug_frames)
00597 {
00598 QString base = QString("%1/TemplateFinder-%2")
00599 .arg(debugdir).arg(frameno, 5, 10, QChar('0'));
00600
00601
00602 if (writeJPG(base, pgm, pgmheight))
00603 goto error;
00604
00605
00606 if (writeJPG(base + "-cropped", cropped, cropheight))
00607 goto error;
00608
00609
00610 if (writeJPG(base + "-edges", edges, cropheight))
00611 goto error;
00612 }
00613
00614 return 0;
00615
00616 error:
00617 return -1;
00618 }
00619
00620 bool
00621 readTemplate(QString datafile, int *prow, int *pcol, int *pwidth, int *pheight,
00622 QString tmplfile, AVPicture *tmpl, bool *pvalid)
00623 {
00624 QFile dfile(datafile);
00625 QFileInfo dfileinfo(dfile);
00626
00627 if (!dfile.open(QIODevice::ReadOnly))
00628 return false;
00629
00630 if (!dfileinfo.size())
00631 {
00632
00633 *pvalid = false;
00634 return true;
00635 }
00636
00637 QTextStream stream(&dfile);
00638 stream >> *prow >> *pcol >> *pwidth >> *pheight;
00639 dfile.close();
00640
00641 if (avpicture_alloc(tmpl, PIX_FMT_GRAY8, *pwidth, *pheight))
00642 {
00643 LOG(VB_COMMFLAG, LOG_ERR,
00644 QString("readTemplate avpicture_alloc %1 (%2x%3) failed")
00645 .arg(tmplfile).arg(*pwidth).arg(*pheight));
00646 return false;
00647 }
00648
00649 QByteArray tmfile = tmplfile.toAscii();
00650 if (pgm_read(tmpl->data[0], *pwidth, *pheight, tmfile.constData()))
00651 {
00652 avpicture_free(tmpl);
00653 return false;
00654 }
00655
00656 *pvalid = true;
00657 return true;
00658 }
00659
00660 void
00661 writeDummyTemplate(QString datafile)
00662 {
00663
00664 QFile dfile(datafile);
00665
00666 if (!dfile.open(QIODevice::WriteOnly | QIODevice::Truncate) &&
00667 dfile.exists())
00668 (void)dfile.remove();
00669 }
00670
00671 bool
00672 writeTemplate(QString tmplfile, const AVPicture *tmpl, QString datafile,
00673 int row, int col, int width, int height)
00674 {
00675 QFile tfile(tmplfile);
00676
00677 QByteArray tmfile = tmplfile.toAscii();
00678 if (pgm_write(tmpl->data[0], width, height, tmfile.constData()))
00679 return false;
00680
00681 QFile dfile(datafile);
00682 if (!dfile.open(QIODevice::WriteOnly))
00683 return false;
00684
00685 QTextStream stream(&dfile);
00686 stream << row << " " << col << "\n" << width << " " << height << "\n";
00687 dfile.close();
00688 return true;
00689 }
00690
00691 };
00692
00693 TemplateFinder::TemplateFinder(PGMConverter *pgmc, BorderDetector *bd,
00694 EdgeDetector *ed, MythPlayer *player, int proglen,
00695 QString debugdir)
00696 : FrameAnalyzer()
00697 , pgmConverter(pgmc)
00698 , borderDetector(bd)
00699 , edgeDetector(ed)
00700 , nextFrame(0)
00701 , width(-1)
00702 , height(-1)
00703 , scores(NULL)
00704 , mincontentrow(INT_MAX)
00705 , mincontentcol(INT_MAX)
00706 , maxcontentrow1(INT_MIN)
00707 , maxcontentcol1(INT_MIN)
00708 , tmplrow(-1)
00709 , tmplcol(-1)
00710 , tmplwidth(-1)
00711 , tmplheight(-1)
00712 , cwidth(-1)
00713 , cheight(-1)
00714 , debugLevel(0)
00715 , debugdir(debugdir)
00716 , debugdata(debugdir + "/TemplateFinder.txt")
00717 , debugtmpl(debugdir + "/TemplateFinder.pgm")
00718 , debug_template(false)
00719 , debug_edgecounts(false)
00720 , debug_frames(false)
00721 , tmpl_valid(false)
00722 , tmpl_done(false)
00723 {
00724
00725
00726
00727
00728
00729
00730
00731
00732 unsigned int samplesNeeded = 300;
00733
00734
00735
00736
00737
00738
00739
00740
00741
00742
00743
00744 sampleTime = min(proglen / 2, 20 * 60);
00745
00746 const float fps = player->GetFrameRate();
00747
00748 if (samplesNeeded > UINT_MAX)
00749 {
00750
00751 samplesNeeded = UINT_MAX;
00752 }
00753
00754 frameInterval = (int)roundf(sampleTime * fps / samplesNeeded);
00755 endFrame = 0 + frameInterval * samplesNeeded - 1;
00756
00757 LOG(VB_COMMFLAG, LOG_INFO,
00758 QString("TemplateFinder: sampleTime=%1s, samplesNeeded=%2, endFrame=%3")
00759 .arg(sampleTime).arg(samplesNeeded).arg(endFrame));
00760
00761 memset(&cropped, 0, sizeof(cropped));
00762 memset(&tmpl, 0, sizeof(tmpl));
00763 memset(&analyze_time, 0, sizeof(analyze_time));
00764
00765
00766
00767
00768
00769
00770
00771
00772 debugLevel = gCoreContext->GetNumSetting("TemplateFinderDebugLevel", 0);
00773
00774 if (debugLevel >= 1)
00775 {
00776 createDebugDirectory(debugdir,
00777 QString("TemplateFinder debugLevel %1").arg(debugLevel));
00778
00779 debug_template = true;
00780 debug_edgecounts = true;
00781
00782 if (debugLevel >= 3)
00783 debug_frames = true;
00784 }
00785 }
00786
00787 TemplateFinder::~TemplateFinder(void)
00788 {
00789 if (scores)
00790 delete []scores;
00791 avpicture_free(&tmpl);
00792 avpicture_free(&cropped);
00793 }
00794
00795 enum FrameAnalyzer::analyzeFrameResult
00796 TemplateFinder::MythPlayerInited(MythPlayer *player, long long nframes)
00797 {
00798
00799
00800
00801
00802
00803
00804
00805
00806
00807 QString tmpldims, playerdims;
00808
00809 (void)nframes;
00810 QSize buf_dim = player->GetVideoBufferSize();
00811 width = buf_dim.width();
00812 height = buf_dim.height();
00813 playerdims = QString("%1x%2").arg(width).arg(height);
00814
00815 if (debug_template)
00816 {
00817 if ((tmpl_done = readTemplate(debugdata, &tmplrow, &tmplcol,
00818 &tmplwidth, &tmplheight, debugtmpl, &tmpl,
00819 &tmpl_valid)))
00820 {
00821 tmpldims = tmpl_valid ? QString("%1x%2@(%3,%4)")
00822 .arg(tmplwidth).arg(tmplheight).arg(tmplcol).arg(tmplrow) :
00823 "no template";
00824
00825 LOG(VB_COMMFLAG, LOG_INFO,
00826 QString("TemplateFinder::MythPlayerInited read %1: %2")
00827 .arg(debugtmpl)
00828 .arg(tmpldims));
00829 }
00830 }
00831
00832 if (pgmConverter->MythPlayerInited(player))
00833 goto free_tmpl;
00834
00835 if (borderDetector->MythPlayerInited(player))
00836 goto free_tmpl;
00837
00838 if (tmpl_done)
00839 {
00840 if (tmpl_valid)
00841 {
00842 LOG(VB_COMMFLAG, LOG_INFO,
00843 QString("TemplateFinder::MythPlayerInited %1 of %2 (%3)")
00844 .arg(tmpldims).arg(playerdims).arg(debugtmpl));
00845 }
00846 return ANALYZE_FINISHED;
00847 }
00848
00849 LOG(VB_COMMFLAG, LOG_INFO,
00850 QString("TemplateFinder::MythPlayerInited framesize %1")
00851 .arg(playerdims));
00852 scores = new unsigned int[width * height];
00853
00854 return ANALYZE_OK;
00855
00856 free_tmpl:
00857 avpicture_free(&tmpl);
00858 return ANALYZE_FATAL;
00859 }
00860
00861 int
00862 TemplateFinder::resetBuffers(int newwidth, int newheight)
00863 {
00864 if (cwidth == newwidth && cheight == newheight)
00865 return 0;
00866
00867 avpicture_free(&cropped);
00868
00869 if (avpicture_alloc(&cropped, PIX_FMT_GRAY8, newwidth, newheight))
00870 {
00871 LOG(VB_COMMFLAG, LOG_ERR,
00872 QString("TemplateFinder::resetBuffers "
00873 "avpicture_alloc cropped (%1x%2) failed")
00874 .arg(newwidth).arg(newheight));
00875 return -1;
00876 }
00877
00878 cwidth = newwidth;
00879 cheight = newheight;
00880 return 0;
00881 }
00882
00883 enum FrameAnalyzer::analyzeFrameResult
00884 TemplateFinder::analyzeFrame(const VideoFrame *frame, long long frameno,
00885 long long *pNextFrame)
00886 {
00887
00888
00889
00890
00891
00892
00893
00894
00895
00896
00897
00898
00899
00900
00901 const int FRAMESGMPCTILE = 90;
00902
00903
00904
00905
00906
00907
00908
00909
00910
00911
00912
00913
00914 static const float EXCLUDEWIDTH = 0.5;
00915 static const float EXCLUDEHEIGHT = 0.5;
00916
00917 const AVPicture *pgm, *edges;
00918 int pgmwidth, pgmheight;
00919 int croprow, cropcol, cropwidth, cropheight;
00920 int excluderow, excludecol, excludewidth, excludeheight;
00921 struct timeval start, end, elapsed;
00922
00923 if (frameno < nextFrame)
00924 {
00925 *pNextFrame = nextFrame;
00926 return ANALYZE_OK;
00927 }
00928
00929 nextFrame = frameno + frameInterval;
00930 *pNextFrame = min(endFrame, nextFrame);
00931
00932 if (!(pgm = pgmConverter->getImage(frame, frameno, &pgmwidth, &pgmheight)))
00933 goto error;
00934
00935 if (!borderDetector->getDimensions(pgm, pgmheight, frameno,
00936 &croprow, &cropcol, &cropwidth, &cropheight))
00937 {
00938
00939
00940 (void)gettimeofday(&start, NULL);
00941
00942 if (croprow < mincontentrow)
00943 mincontentrow = croprow;
00944 if (cropcol < mincontentcol)
00945 mincontentcol = cropcol;
00946 if (cropcol + cropwidth > maxcontentcol1)
00947 maxcontentcol1 = cropcol + cropwidth;
00948 if (croprow + cropheight > maxcontentrow1)
00949 maxcontentrow1 = croprow + cropheight;
00950
00951 if (resetBuffers(cropwidth, cropheight))
00952 goto error;
00953
00954 if (pgm_crop(&cropped, pgm, pgmheight, croprow, cropcol,
00955 cropwidth, cropheight))
00956 goto error;
00957
00958
00959
00960
00961
00962 excludewidth = (int)(pgmwidth * EXCLUDEWIDTH);
00963 excludeheight = (int)(pgmheight * EXCLUDEHEIGHT);
00964 excluderow = (pgmheight - excludeheight) / 2 - croprow;
00965 excludecol = (pgmwidth - excludewidth) / 2 - cropcol;
00966 (void)edgeDetector->setExcludeArea(excluderow, excludecol,
00967 excludewidth, excludeheight);
00968
00969 if (!(edges = edgeDetector->detectEdges(&cropped, cropheight,
00970 FRAMESGMPCTILE)))
00971 goto error;
00972
00973 if (pgm_scorepixels(scores, pgmwidth, croprow, cropcol,
00974 edges, cropheight))
00975 goto error;
00976
00977 if (debugLevel >= 2)
00978 {
00979 if (analyzeFrameDebug(frameno, pgm, pgmheight, &cropped, edges,
00980 cropheight, croprow, cropcol, debug_frames, debugdir))
00981 goto error;
00982 }
00983
00984 (void)gettimeofday(&end, NULL);
00985 timersub(&end, &start, &elapsed);
00986 timeradd(&analyze_time, &elapsed, &analyze_time);
00987 }
00988
00989 if (nextFrame > endFrame)
00990 return ANALYZE_FINISHED;
00991
00992 return ANALYZE_OK;
00993
00994 error:
00995 LOG(VB_COMMFLAG, LOG_ERR,
00996 QString("TemplateFinder::analyzeFrame error at frame %1")
00997 .arg(frameno));
00998
00999 if (nextFrame > endFrame)
01000 return ANALYZE_FINISHED;
01001
01002 return ANALYZE_ERROR;
01003 }
01004
01005 int
01006 TemplateFinder::finished(long long nframes, bool final)
01007 {
01008 (void)nframes;
01009 if (!tmpl_done)
01010 {
01011 if (template_alloc(scores, width, height,
01012 mincontentrow, mincontentcol,
01013 maxcontentrow1, maxcontentcol1,
01014 &tmpl, &tmplrow, &tmplcol, &tmplwidth, &tmplheight,
01015 debug_edgecounts, debugdir))
01016 {
01017 if (final)
01018 writeDummyTemplate(debugdata);
01019 }
01020 else
01021 {
01022 if (final && debug_template)
01023 {
01024 if (!(tmpl_valid = writeTemplate(debugtmpl, &tmpl, debugdata,
01025 tmplrow, tmplcol, tmplwidth, tmplheight)))
01026 goto free_tmpl;
01027
01028 LOG(VB_COMMFLAG, LOG_INFO,
01029 QString("TemplateFinder::finished wrote %1"
01030 " and %2 [%3x%4@(%5,%6)]")
01031 .arg(debugtmpl).arg(debugdata)
01032 .arg(tmplwidth).arg(tmplheight)
01033 .arg(tmplcol).arg(tmplrow));
01034 }
01035 }
01036
01037 if (final)
01038 tmpl_done = true;
01039 }
01040
01041 borderDetector->setLogoState(this);
01042
01043 return 0;
01044
01045 free_tmpl:
01046 avpicture_free(&tmpl);
01047 return -1;
01048 }
01049
01050 int
01051 TemplateFinder::reportTime(void) const
01052 {
01053 if (pgmConverter->reportTime())
01054 return -1;
01055
01056 if (borderDetector->reportTime())
01057 return -1;
01058
01059 LOG(VB_COMMFLAG, LOG_INFO, QString("TF Time: analyze=%1s")
01060 .arg(strftimeval(&analyze_time)));
01061 return 0;
01062 }
01063
01064 const struct AVPicture *
01065 TemplateFinder::getTemplate(int *prow, int *pcol, int *pwidth, int *pheight)
01066 const
01067 {
01068 if (tmpl_valid)
01069 {
01070 *prow = tmplrow;
01071 *pcol = tmplcol;
01072 *pwidth = tmplwidth;
01073 *pheight = tmplheight;
01074 return &tmpl;
01075 }
01076 return NULL;
01077 }
01078
01079