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