00001
00002 #include <sys/time.h>
00003
00004
00005 #include <cmath>
00006
00007
00008 #include "mythcorecontext.h"
00009 #include "mythplayer.h"
00010 #include "mythlogging.h"
00011
00012
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
00092
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 };
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
00165 , debugdata(debugdir + "/HistogramAnalyzer-yuv.txt")
00166 #endif
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
00175
00176
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
00295
00296
00297 static const int DEFAULT_COLOR = 0;
00298
00299
00300
00301
00302
00303
00304
00305
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
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) +
00354 ((rr2 - rr1) / RINC) * (cc1 / CINC) +
00355 ((rr2 - rr1) / RINC) * ((cc3 - cc2) / CINC) +
00356 ((rr3 - rr2) / RINC) * (cc3 / CINC);
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;
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
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
00395
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