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