|
MythTV
0.26-pre
|
00001 // POSIX headers 00002 #include <sys/time.h> // for gettimeofday 00003 00004 // ANSI C headers 00005 #include <cmath> 00006 00007 // MythTV headers 00008 #include "mythcorecontext.h" 00009 #include "mythplayer.h" 00010 #include "mythlogging.h" 00011 00012 // Commercial Flagging headers 00013 #include "CommDetector2.h" 00014 #include "FrameAnalyzer.h" 00015 #include "PGMConverter.h" 00016 #include "BorderDetector.h" 00017 #include "quickselect.h" 00018 #include "TemplateFinder.h" 00019 #include "HistogramAnalyzer.h" 00020 00021 using namespace commDetector2; 00022 using namespace frameAnalyzer; 00023 00024 namespace { 00025 00026 bool 00027 readData(QString filename, float *mean, unsigned char *median, float *stddev, 00028 int *frow, int *fcol, int *fwidth, int *fheight, 00029 HistogramAnalyzer::Histogram *histogram, unsigned char *monochromatic, 00030 long long nframes) 00031 { 00032 FILE *fp; 00033 long long frameno; 00034 int counter[UCHAR_MAX + 1]; 00035 00036 QByteArray fname = filename.toLocal8Bit(); 00037 if (!(fp = fopen(fname.constData(), "r"))) 00038 return false; 00039 00040 for (frameno = 0; frameno < nframes; frameno++) 00041 { 00042 int monochromaticval, medianval, widthval, heightval, colval, rowval; 00043 float meanval, stddevval; 00044 int nitems = fscanf(fp, "%20d %20f %20d %20f %20d %20d %20d %20d", 00045 &monochromaticval, &meanval, &medianval, &stddevval, 00046 &widthval, &heightval, &colval, &rowval); 00047 if (nitems != 8) 00048 { 00049 LOG(VB_COMMFLAG, LOG_ERR, 00050 QString("Not enough data in %1: frame %2") 00051 .arg(filename).arg(frameno)); 00052 goto error; 00053 } 00054 if (monochromaticval < 0 || monochromaticval > 1 || 00055 medianval < 0 || (uint)medianval > UCHAR_MAX || 00056 widthval < 0 || heightval < 0 || colval < 0 || rowval < 0) 00057 { 00058 LOG(VB_COMMFLAG, LOG_ERR, 00059 QString("Data out of range in %1: frame %2") 00060 .arg(filename).arg(frameno)); 00061 goto error; 00062 } 00063 for (unsigned int ii = 0; ii < sizeof(counter)/sizeof(*counter); ii++) 00064 { 00065 if ((nitems = fscanf(fp, "%20x", &counter[ii])) != 1) 00066 { 00067 LOG(VB_COMMFLAG, LOG_ERR, 00068 QString("Not enough data in %1: frame %2") 00069 .arg(filename).arg(frameno)); 00070 goto error; 00071 } 00072 if (counter[ii] < 0 || (uint)(counter[ii]) > UCHAR_MAX) 00073 { 00074 LOG(VB_COMMFLAG, LOG_ERR, 00075 QString("Data out of range in %1: frame %2") 00076 .arg(filename).arg(frameno)); 00077 goto error; 00078 } 00079 } 00080 mean[frameno] = meanval; 00081 median[frameno] = medianval; 00082 stddev[frameno] = stddevval; 00083 frow[frameno] = rowval; 00084 fcol[frameno] = colval; 00085 fwidth[frameno] = widthval; 00086 fheight[frameno] = heightval; 00087 for (unsigned int ii = 0; ii < sizeof(counter)/sizeof(*counter); ii++) 00088 histogram[frameno][ii] = counter[ii]; 00089 monochromatic[frameno] = !widthval || !heightval ? 1 : 0; 00090 /* 00091 * monochromaticval not used; it's written to file for debugging 00092 * convenience 00093 */ 00094 } 00095 if (fclose(fp)) 00096 LOG(VB_COMMFLAG, LOG_ERR, QString("Error closing %1: %2") 00097 .arg(filename).arg(strerror(errno))); 00098 return true; 00099 00100 error: 00101 if (fclose(fp)) 00102 LOG(VB_COMMFLAG, LOG_ERR, QString("Error closing %1: %2") 00103 .arg(filename).arg(strerror(errno))); 00104 return false; 00105 } 00106 00107 bool 00108 writeData(QString filename, float *mean, unsigned char *median, float *stddev, 00109 int *frow, int *fcol, int *fwidth, int *fheight, 00110 HistogramAnalyzer::Histogram *histogram, unsigned char *monochromatic, 00111 long long nframes) 00112 { 00113 FILE *fp; 00114 long long frameno; 00115 00116 QByteArray fname = filename.toLocal8Bit(); 00117 if (!(fp = fopen(fname, "w"))) 00118 return false; 00119 for (frameno = 0; frameno < nframes; frameno++) 00120 { 00121 (void)fprintf(fp, "%3u %10.6f %3u %10.6f %5u %5u %5u %5u", 00122 monochromatic[frameno], 00123 mean[frameno], median[frameno], stddev[frameno], 00124 fwidth[frameno], fheight[frameno], 00125 fcol[frameno], frow[frameno]); 00126 for (unsigned int ii = 0; ii < UCHAR_MAX + 1; ii++) 00127 (void)fprintf(fp, " %02x", histogram[frameno][ii]); 00128 (void)fprintf(fp, "\n"); 00129 } 00130 if (fclose(fp)) 00131 LOG(VB_COMMFLAG, LOG_ERR, QString("Error closing %1: %2") 00132 .arg(filename).arg(strerror(errno))); 00133 return true; 00134 } 00135 00136 }; /* namespace */ 00137 00138 HistogramAnalyzer::HistogramAnalyzer(PGMConverter *pgmc, BorderDetector *bd, 00139 QString debugdir) 00140 : pgmConverter(pgmc) 00141 , borderDetector(bd) 00142 , logoFinder(NULL) 00143 , logo(NULL) 00144 , logowidth(-1) 00145 , logoheight(-1) 00146 , logorr1(-1) 00147 , logocc1(-1) 00148 , logorr2(-1) 00149 , logocc2(-1) 00150 , mean(NULL) 00151 , median(NULL) 00152 , stddev(NULL) 00153 , frow(NULL) 00154 , fcol(NULL) 00155 , fwidth(NULL) 00156 , fheight(NULL) 00157 , histogram(NULL) 00158 , monochromatic(NULL) 00159 , buf(NULL) 00160 , lastframeno(-1) 00161 , debugLevel(0) 00162 #ifdef PGM_CONVERT_GREYSCALE 00163 , debugdata(debugdir + "/HistogramAnalyzer-pgm.txt") 00164 #else /* !PGM_CONVERT_GREYSCALE */ 00165 , debugdata(debugdir + "/HistogramAnalyzer-yuv.txt") 00166 #endif /* !PGM_CONVERT_GREYSCALE */ 00167 , debug_histval(false) 00168 , histval_done(false) 00169 { 00170 memset(histval, 0, sizeof(int) * (UCHAR_MAX + 1)); 00171 memset(&analyze_time, 0, sizeof(analyze_time)); 00172 00173 /* 00174 * debugLevel: 00175 * 0: no debugging 00176 * 1: cache frame information into debugdata [1 file] 00177 */ 00178 debugLevel = gCoreContext->GetNumSetting("HistogramAnalyzerDebugLevel", 0); 00179 00180 if (debugLevel >= 1) 00181 { 00182 createDebugDirectory(debugdir, 00183 QString("HistogramAnalyzer debugLevel %1").arg(debugLevel)); 00184 debug_histval = true; 00185 } 00186 } 00187 00188 HistogramAnalyzer::~HistogramAnalyzer() 00189 { 00190 if (monochromatic) 00191 delete []monochromatic; 00192 if (mean) 00193 delete []mean; 00194 if (median) 00195 delete []median; 00196 if (stddev) 00197 delete []stddev; 00198 if (frow) 00199 delete []frow; 00200 if (fcol) 00201 delete []fcol; 00202 if (fwidth) 00203 delete []fwidth; 00204 if (fheight) 00205 delete []fheight; 00206 if (histogram) 00207 delete []histogram; 00208 if (buf) 00209 delete []buf; 00210 } 00211 00212 enum FrameAnalyzer::analyzeFrameResult 00213 HistogramAnalyzer::MythPlayerInited(MythPlayer *player, long long nframes) 00214 { 00215 if (histval_done) 00216 return FrameAnalyzer::ANALYZE_FINISHED; 00217 00218 if (monochromatic) 00219 return FrameAnalyzer::ANALYZE_OK; 00220 00221 QSize buf_dim = player->GetVideoBufferSize(); 00222 unsigned int width = buf_dim.width(); 00223 unsigned int height = buf_dim.height(); 00224 00225 if (logoFinder && (logo = logoFinder->getTemplate(&logorr1, &logocc1, 00226 &logowidth, &logoheight))) 00227 { 00228 logorr2 = logorr1 + logoheight - 1; 00229 logocc2 = logocc1 + logowidth - 1; 00230 } 00231 QString details = logo ? QString("logo %1x%2@(%3,%4)") 00232 .arg(logowidth).arg(logoheight).arg(logocc1).arg(logorr1) : 00233 QString("no logo"); 00234 00235 LOG(VB_COMMFLAG, LOG_INFO, 00236 QString("HistogramAnalyzer::MythPlayerInited %1x%2: %3") 00237 .arg(width).arg(height).arg(details)); 00238 00239 if (pgmConverter->MythPlayerInited(player)) 00240 return FrameAnalyzer::ANALYZE_FATAL; 00241 00242 if (borderDetector->MythPlayerInited(player)) 00243 return FrameAnalyzer::ANALYZE_FATAL; 00244 00245 mean = new float[nframes]; 00246 median = new unsigned char[nframes]; 00247 stddev = new float[nframes]; 00248 frow = new int[nframes]; 00249 fcol = new int[nframes]; 00250 fwidth = new int[nframes]; 00251 fheight = new int[nframes]; 00252 histogram = new Histogram[nframes]; 00253 monochromatic = new unsigned char[nframes]; 00254 00255 memset(mean, 0, nframes * sizeof(*mean)); 00256 memset(median, 0, nframes * sizeof(*median)); 00257 memset(stddev, 0, nframes * sizeof(*stddev)); 00258 memset(frow, 0, nframes * sizeof(*frow)); 00259 memset(fcol, 0, nframes * sizeof(*fcol)); 00260 memset(fwidth, 0, nframes * sizeof(*fwidth)); 00261 memset(fheight, 0, nframes * sizeof(*fheight)); 00262 memset(histogram, 0, nframes * sizeof(*histogram)); 00263 memset(monochromatic, 0, nframes * sizeof(*monochromatic)); 00264 00265 unsigned int npixels = width * height; 00266 buf = new unsigned char[npixels]; 00267 00268 if (debug_histval) 00269 { 00270 if (readData(debugdata, mean, median, stddev, frow, fcol, 00271 fwidth, fheight, histogram, monochromatic, nframes)) 00272 { 00273 LOG(VB_COMMFLAG, LOG_INFO, 00274 QString("HistogramAnalyzer::MythPlayerInited read %1") 00275 .arg(debugdata)); 00276 histval_done = true; 00277 return FrameAnalyzer::ANALYZE_FINISHED; 00278 } 00279 } 00280 00281 return FrameAnalyzer::ANALYZE_OK; 00282 } 00283 00284 void 00285 HistogramAnalyzer::setLogoState(TemplateFinder *finder) 00286 { 00287 logoFinder = finder; 00288 } 00289 00290 enum FrameAnalyzer::analyzeFrameResult 00291 HistogramAnalyzer::analyzeFrame(const VideoFrame *frame, long long frameno) 00292 { 00293 /* 00294 * Various statistical computations over pixel values: mean, median, 00295 * (running) standard deviation over sample population. 00296 */ 00297 static const int DEFAULT_COLOR = 0; 00298 00299 /* 00300 * TUNABLE: 00301 * 00302 * Sampling coarseness of each frame. Higher values will allow analysis to 00303 * proceed faster (lower resolution), but might be less accurate. Lower 00304 * values will examine more pixels (higher resolution), but will run 00305 * slower. 00306 */ 00307 static const int RINC = 4; 00308 static const int CINC = 4; 00309 #define ROUNDUP(a,b) (((a) + (b) - 1) / (b) * (b)) 00310 00311 const AVPicture *pgm; 00312 int pgmwidth, pgmheight; 00313 bool ismonochromatic; 00314 int croprow, cropcol, cropwidth, cropheight; 00315 unsigned int borderpixels, livepixels, npixels, halfnpixels; 00316 unsigned char *pp, bordercolor; 00317 unsigned long long sumval, sumsquares; 00318 int rr, cc, rr1, cc1, rr2, cc2, rr3, cc3; 00319 struct timeval start, end, elapsed; 00320 00321 if (lastframeno != UNCACHED && lastframeno == frameno) 00322 return FrameAnalyzer::ANALYZE_OK; 00323 00324 if (!(pgm = pgmConverter->getImage(frame, frameno, &pgmwidth, &pgmheight))) 00325 goto error; 00326 00327 ismonochromatic = borderDetector->getDimensions(pgm, pgmheight, frameno, 00328 &croprow, &cropcol, &cropwidth, &cropheight) != 0; 00329 00330 gettimeofday(&start, NULL); 00331 00332 frow[frameno] = croprow; 00333 fcol[frameno] = cropcol; 00334 fwidth[frameno] = cropwidth; 00335 fheight[frameno] = cropheight; 00336 00337 if (ismonochromatic) 00338 { 00339 /* Optimization for monochromatic frames; just sample center area. */ 00340 croprow = pgmheight * 3 / 8; 00341 cropheight = pgmheight / 4; 00342 cropcol = pgmwidth * 3 / 8; 00343 cropwidth = pgmwidth / 4; 00344 } 00345 00346 rr1 = ROUNDUP(croprow, RINC); 00347 cc1 = ROUNDUP(cropcol, CINC); 00348 rr2 = ROUNDUP(croprow + cropheight, RINC); 00349 cc2 = ROUNDUP(cropcol + cropwidth, CINC); 00350 rr3 = ROUNDUP(pgmheight, RINC); 00351 cc3 = ROUNDUP(pgmwidth, CINC); 00352 00353 borderpixels = (rr1 / RINC) * (cc3 / CINC) + /* top */ 00354 ((rr2 - rr1) / RINC) * (cc1 / CINC) + /* left */ 00355 ((rr2 - rr1) / RINC) * ((cc3 - cc2) / CINC) + /* right */ 00356 ((rr3 - rr2) / RINC) * (cc3 / CINC); /* bottom */ 00357 00358 sumval = 0; 00359 sumsquares = 0; 00360 livepixels = 0; 00361 pp = &buf[borderpixels]; 00362 memset(histval, 0, sizeof(histval)); 00363 histval[DEFAULT_COLOR] += borderpixels; 00364 for (rr = rr1; rr < rr2; rr += RINC) 00365 { 00366 int rroffset = rr * pgmwidth; 00367 00368 for (cc = cc1; cc < cc2; cc += CINC) 00369 { 00370 if (logo && rr >= logorr1 && rr <= logorr2 && 00371 cc >= logocc1 && cc <= logocc2) 00372 continue; /* Exclude logo area from analysis. */ 00373 00374 unsigned char val = pgm->data[0][rroffset + cc]; 00375 *pp++ = val; 00376 sumval += val; 00377 sumsquares += val * val; 00378 livepixels++; 00379 histval[val]++; 00380 } 00381 } 00382 npixels = borderpixels + livepixels; 00383 00384 /* Scale scores down to [0..255]. */ 00385 halfnpixels = npixels / 2; 00386 for (unsigned int color = 0; color < UCHAR_MAX + 1; color++) 00387 histogram[frameno][color] = 00388 (histval[color] * UCHAR_MAX + halfnpixels) / npixels; 00389 00390 bordercolor = 0; 00391 if (ismonochromatic && livepixels) 00392 { 00393 /* 00394 * Fake up the margin pixels to be of the same color as the sampled 00395 * area. 00396 */ 00397 bordercolor = (sumval + livepixels - 1) / livepixels; 00398 sumval += borderpixels * bordercolor; 00399 sumsquares += borderpixels * bordercolor * bordercolor; 00400 } 00401 00402 memset(buf, bordercolor, borderpixels * sizeof(*buf)); 00403 monochromatic[frameno] = ismonochromatic ? 1 : 0; 00404 mean[frameno] = (float)sumval / npixels; 00405 median[frameno] = quick_select_median(buf, npixels); 00406 stddev[frameno] = npixels > 1 ? 00407 sqrt((sumsquares - (float)sumval * sumval / npixels) / (npixels - 1)) : 00408 0; 00409 00410 (void)gettimeofday(&end, NULL); 00411 timersub(&end, &start, &elapsed); 00412 timeradd(&analyze_time, &elapsed, &analyze_time); 00413 00414 lastframeno = frameno; 00415 00416 return FrameAnalyzer::ANALYZE_OK; 00417 00418 error: 00419 LOG(VB_COMMFLAG, LOG_ERR, 00420 QString("HistogramAnalyzer::analyzeFrame error at frame %1") 00421 .arg(frameno)); 00422 00423 return FrameAnalyzer::ANALYZE_ERROR; 00424 } 00425 00426 int 00427 HistogramAnalyzer::finished(long long nframes, bool final) 00428 { 00429 if (!histval_done && debug_histval) 00430 { 00431 if (final && writeData(debugdata, mean, median, stddev, frow, fcol, 00432 fwidth, fheight, histogram, monochromatic, nframes)) 00433 { 00434 LOG(VB_COMMFLAG, LOG_INFO, 00435 QString("HistogramAnalyzer::finished wrote %1") 00436 .arg(debugdata)); 00437 histval_done = true; 00438 } 00439 } 00440 00441 return 0; 00442 } 00443 00444 int 00445 HistogramAnalyzer::reportTime(void) const 00446 { 00447 if (pgmConverter->reportTime()) 00448 return -1; 00449 00450 if (borderDetector->reportTime()) 00451 return -1; 00452 00453 LOG(VB_COMMFLAG, LOG_INFO, QString("HA Time: analyze=%1s") 00454 .arg(strftimeval(&analyze_time))); 00455 return 0; 00456 } 00457 00458 /* vim: set expandtab tabstop=4 shiftwidth=4: */
1.7.6.1