|
MythTV
0.26-pre
|
00001 #include <sys/time.h> 00002 00003 extern "C" { 00004 #include "libavcodec/avcodec.h" /* AVPicture */ 00005 } 00006 #include "mythcorecontext.h" /* gContext */ 00007 #include "compat.h" 00008 00009 #include "CommDetector2.h" 00010 #include "FrameAnalyzer.h" 00011 #include "TemplateFinder.h" 00012 #include "BorderDetector.h" 00013 00014 using namespace frameAnalyzer; 00015 using namespace commDetector2; 00016 00017 BorderDetector::BorderDetector(void) 00018 : logoFinder(NULL), 00019 logo(NULL), 00020 logorow(-1), 00021 logocol(-1), 00022 logowidth(-1), 00023 logoheight(-1), 00024 frameno(-1), 00025 row(-1), 00026 col(-1), 00027 width(-1), 00028 height(-1), 00029 ismonochromatic(false), 00030 debugLevel(0), 00031 time_reported(false) 00032 { 00033 memset(&analyze_time, 0, sizeof(analyze_time)); 00034 debugLevel = gCoreContext->GetNumSetting("BorderDetectorDebugLevel", 0); 00035 00036 if (debugLevel >= 1) 00037 LOG(VB_COMMFLAG, LOG_INFO, 00038 QString("BorderDetector debugLevel %1").arg(debugLevel)); 00039 } 00040 00041 int 00042 BorderDetector::MythPlayerInited(const MythPlayer *player) 00043 { 00044 (void)player; /* gcc */ 00045 time_reported = false; 00046 memset(&analyze_time, 0, sizeof(analyze_time)); 00047 return 0; 00048 } 00049 00050 void 00051 BorderDetector::setLogoState(TemplateFinder *finder) 00052 { 00053 if ((logoFinder = finder) && (logo = logoFinder->getTemplate( 00054 &logorow, &logocol, &logowidth, &logoheight))) 00055 { 00056 LOG(VB_COMMFLAG, LOG_INFO, 00057 QString("BorderDetector::setLogoState: %1x%2@(%3,%4)") 00058 .arg(logowidth).arg(logoheight).arg(logocol).arg(logorow)); 00059 } 00060 } 00061 00062 int 00063 BorderDetector::getDimensions(const AVPicture *pgm, int pgmheight, 00064 long long _frameno, int *prow, int *pcol, int *pwidth, int *pheight) 00065 { 00066 /* 00067 * The basic algorithm is to look for pixels of the same color along all 00068 * four borders of the frame, working inwards until the pixels cease to be 00069 * of uniform color. This way, letterboxing/pillarboxing bars can be of any 00070 * color (varying shades of black-grey). 00071 * 00072 * If there is a logo, exclude its area from border detection. 00073 * 00074 * Return 0 for normal frames; non-zero for monochromatic frames. 00075 */ 00076 00077 /* 00078 * TUNABLES: 00079 * 00080 * Higher values mean more tolerance for noise (e.g., analog recordings). 00081 * However, in the absence of noise, content/logos can be cropped away from 00082 * analysis. 00083 * 00084 * Lower values mean less tolerance for noise. In a noisy recording, the 00085 * transition between pillarbox/letterbox black to content color will be 00086 * detected as an edge, and thwart logo edge detection. In the absence of 00087 * noise, content/logos will be more faithfully analyzed. 00088 */ 00089 00090 /* 00091 * TUNABLE: The maximum range of values allowed for 00092 * letterboxing/pillarboxing bars. Usually the bars are black (0x00), but 00093 * sometimes they are grey (0x71). Sometimes the letterboxing and 00094 * pillarboxing (when one is embedded inside the other) are different 00095 * colors. 00096 */ 00097 static const unsigned char MAXRANGE = 32; 00098 00099 /* 00100 * TUNABLE: The maximum number of consecutive rows or columns with too many 00101 * outlier points that may be scanned before declaring the existence of a 00102 * border. 00103 */ 00104 static const int MAXLINES = 2; 00105 00106 const int pgmwidth = pgm->linesize[0]; 00107 00108 /* 00109 * TUNABLE: The maximum number of outlier points in a single row or column 00110 * with grey values outside of MAXRANGE before declaring the existence of a 00111 * border. 00112 */ 00113 const int MAXOUTLIERS = pgmwidth * 12 / 1000; 00114 00115 /* 00116 * TUNABLE: Margins to avoid noise at the extreme edges of the signal 00117 * (VBI?). (Really, just a special case of VERTSLOP and HORIZSLOP, below.) 00118 */ 00119 const int VERTMARGIN = max(2, pgmheight * 1 / 60); 00120 const int HORIZMARGIN = max(2, pgmwidth * 1 / 80); 00121 00122 /* 00123 * TUNABLE: Slop to accommodate any jagged letterboxing/pillarboxing edges, 00124 * or noise between edges and content. (Really, a more general case of 00125 * VERTMARGIN and HORIZMARGIN, above.) 00126 */ 00127 const int VERTSLOP = max(MAXLINES, pgmheight * 1 / 120); 00128 const int HORIZSLOP = max(MAXLINES, pgmwidth * 1 / 160); 00129 00130 struct timeval start, end, elapsed; 00131 unsigned char minval, maxval, val; 00132 int rr, cc, minrow, mincol, maxrow1, maxcol1, saved; 00133 int newrow, newcol, newwidth, newheight; 00134 bool top, bottom, left, right, inrange; 00135 int range, outliers, lines; 00136 00137 (void)gettimeofday(&start, NULL); 00138 00139 if (_frameno != UNCACHED && _frameno == frameno) 00140 goto done; 00141 00142 top = false; 00143 bottom = false; 00144 left = false; 00145 right = false; 00146 00147 minrow = VERTMARGIN; 00148 maxrow1 = pgmheight - VERTMARGIN; /* maxrow + 1 */ 00149 00150 mincol = HORIZMARGIN; 00151 maxcol1 = pgmwidth - HORIZMARGIN; /* maxcol + 1 */ 00152 00153 newrow = minrow - 1; 00154 newcol = mincol - 1; 00155 newwidth = maxcol1 + 1 - mincol; 00156 newheight = maxrow1 + 1 - minrow; 00157 00158 for (;;) 00159 { 00160 /* Find left edge. */ 00161 left = false; 00162 minval = UCHAR_MAX; 00163 maxval = 0; 00164 lines = 0; 00165 saved = mincol; 00166 for (cc = mincol; cc < maxcol1; cc++) 00167 { 00168 outliers = 0; 00169 inrange = true; 00170 for (rr = minrow; rr < maxrow1; rr++) 00171 { 00172 if (logo && rrccinrect(rr, cc, logorow, logocol, 00173 logowidth, logoheight)) 00174 continue; /* Exclude logo area from analysis. */ 00175 00176 val = pgm->data[0][rr * pgmwidth + cc]; 00177 range = max(maxval, val) - min(minval, val) + 1; 00178 if (range > MAXRANGE) 00179 { 00180 if (outliers++ < MAXOUTLIERS) 00181 continue; /* Next row. */ 00182 inrange = false; 00183 if (lines++ < MAXLINES) 00184 break; /* Next column. */ 00185 goto found_left; 00186 } 00187 if (val < minval) 00188 minval = val; 00189 if (val > maxval) 00190 maxval = val; 00191 } 00192 if (inrange) 00193 { 00194 saved = cc; 00195 lines = 0; 00196 } 00197 } 00198 found_left: 00199 if (newcol != saved + 1 + HORIZSLOP) 00200 { 00201 newcol = min(maxcol1, saved + 1 + HORIZSLOP); 00202 newwidth = max(0, maxcol1 - newcol); 00203 left = true; 00204 } 00205 00206 if (!newwidth) 00207 goto monochromatic_frame; 00208 00209 mincol = newcol; 00210 00211 /* 00212 * Find right edge. Keep same minval/maxval (pillarboxing colors) as 00213 * left edge. 00214 */ 00215 right = false; 00216 lines = 0; 00217 saved = maxcol1 - 1; 00218 for (cc = maxcol1 - 1; cc >= mincol; cc--) 00219 { 00220 outliers = 0; 00221 inrange = true; 00222 for (rr = minrow; rr < maxrow1; rr++) 00223 { 00224 if (logo && rrccinrect(rr, cc, logorow, logocol, 00225 logowidth, logoheight)) 00226 continue; /* Exclude logo area from analysis. */ 00227 00228 val = pgm->data[0][rr * pgmwidth + cc]; 00229 range = max(maxval, val) - min(minval, val) + 1; 00230 if (range > MAXRANGE) 00231 { 00232 if (outliers++ < MAXOUTLIERS) 00233 continue; /* Next row. */ 00234 inrange = false; 00235 if (lines++ < MAXLINES) 00236 break; /* Next column. */ 00237 goto found_right; 00238 } 00239 if (val < minval) 00240 minval = val; 00241 if (val > maxval) 00242 maxval = val; 00243 } 00244 if (inrange) 00245 { 00246 saved = cc; 00247 lines = 0; 00248 } 00249 } 00250 found_right: 00251 if (newwidth != saved - mincol - HORIZSLOP) 00252 { 00253 newwidth = max(0, saved - mincol - HORIZSLOP); 00254 right = true; 00255 } 00256 00257 if (!newwidth) 00258 goto monochromatic_frame; 00259 00260 if (top || bottom) 00261 break; /* Do not repeat letterboxing check. */ 00262 00263 maxcol1 = mincol + newwidth; 00264 00265 /* Find top edge. */ 00266 top = false; 00267 minval = UCHAR_MAX; 00268 maxval = 0; 00269 lines = 0; 00270 saved = minrow; 00271 for (rr = minrow; rr < maxrow1; rr++) 00272 { 00273 outliers = 0; 00274 inrange = true; 00275 for (cc = mincol; cc < maxcol1; cc++) 00276 { 00277 if (logo && rrccinrect(rr, cc, logorow, logocol, 00278 logowidth, logoheight)) 00279 continue; /* Exclude logo area from analysis. */ 00280 00281 val = pgm->data[0][rr * pgmwidth + cc]; 00282 range = max(maxval, val) - min(minval, val) + 1; 00283 if (range > MAXRANGE) 00284 { 00285 if (outliers++ < MAXOUTLIERS) 00286 continue; /* Next column. */ 00287 inrange = false; 00288 if (lines++ < MAXLINES) 00289 break; /* Next row. */ 00290 goto found_top; 00291 } 00292 if (val < minval) 00293 minval = val; 00294 if (val > maxval) 00295 maxval = val; 00296 } 00297 if (inrange) 00298 { 00299 saved = rr; 00300 lines = 0; 00301 } 00302 } 00303 found_top: 00304 if (newrow != saved + 1 + VERTSLOP) 00305 { 00306 newrow = min(maxrow1, saved + 1 + VERTSLOP); 00307 newheight = max(0, maxrow1 - newrow); 00308 top = true; 00309 } 00310 00311 if (!newheight) 00312 goto monochromatic_frame; 00313 00314 minrow = newrow; 00315 00316 /* Find bottom edge. Keep same minval/maxval as top edge. */ 00317 bottom = false; 00318 lines = 0; 00319 saved = maxrow1 - 1; 00320 for (rr = maxrow1 - 1; rr >= minrow; rr--) 00321 { 00322 outliers = 0; 00323 inrange = true; 00324 for (cc = mincol; cc < maxcol1; cc++) 00325 { 00326 if (logo && rrccinrect(rr, cc, logorow, logocol, 00327 logowidth, logoheight)) 00328 continue; /* Exclude logo area from analysis. */ 00329 00330 val = pgm->data[0][rr * pgmwidth + cc]; 00331 range = max(maxval, val) - min(minval, val) + 1; 00332 if (range > MAXRANGE) 00333 { 00334 if (outliers++ < MAXOUTLIERS) 00335 continue; /* Next column. */ 00336 inrange = false; 00337 if (lines++ < MAXLINES) 00338 break; /* Next row. */ 00339 goto found_bottom; 00340 } 00341 if (val < minval) 00342 minval = val; 00343 if (val > maxval) 00344 maxval = val; 00345 } 00346 if (inrange) 00347 { 00348 saved = rr; 00349 lines = 0; 00350 } 00351 } 00352 found_bottom: 00353 if (newheight != saved - minrow - VERTSLOP) 00354 { 00355 newheight = max(0, saved - minrow - VERTSLOP); 00356 bottom = true; 00357 } 00358 00359 if (!newheight) 00360 goto monochromatic_frame; 00361 00362 if (left || right) 00363 break; /* Do not repeat pillarboxing check. */ 00364 00365 maxrow1 = minrow + newheight; 00366 } 00367 00368 frameno = _frameno; 00369 row = newrow; 00370 col = newcol; 00371 width = newwidth; 00372 height = newheight; 00373 ismonochromatic = false; 00374 goto done; 00375 00376 monochromatic_frame: 00377 frameno = _frameno; 00378 row = newrow; 00379 col = newcol; 00380 width = newwidth; 00381 height = newheight; 00382 ismonochromatic = true; 00383 00384 done: 00385 *prow = row; 00386 *pcol = col; 00387 *pwidth = width; 00388 *pheight = height; 00389 00390 (void)gettimeofday(&end, NULL); 00391 timersub(&end, &start, &elapsed); 00392 timeradd(&analyze_time, &elapsed, &analyze_time); 00393 00394 return ismonochromatic ? -1 : 0; 00395 } 00396 00397 int 00398 BorderDetector::reportTime(void) 00399 { 00400 if (!time_reported) 00401 { 00402 LOG(VB_COMMFLAG, LOG_INFO, QString("BD Time: analyze=%1s") 00403 .arg(strftimeval(&analyze_time))); 00404 time_reported = true; 00405 } 00406 return 0; 00407 } 00408 00409 /* vim: set expandtab tabstop=4 shiftwidth=4: */
1.7.6.1