26 int *frow,
int *fcol,
int *fwidth,
int *fheight,
30 std::array<quint32,UCHAR_MAX + 1> counter {};
32 QByteArray fname =
filename.toLocal8Bit();
33 FILE *fp = fopen(fname.constData(),
"r");
38 auto close_fp = [&](
FILE *fp2) {
41 LOG(VB_COMMFLAG, LOG_ERR, QString(
"Error closing %1: %2")
44 std::unique_ptr<
FILE,
decltype(close_fp)>
cleanup { fp, close_fp };
46 for (
long long frameno = 0; frameno < nframes; frameno++)
48 int monochromaticval = 0;
55 float stddevval = NAN;
56 int nitems = fscanf(fp,
"%20d %20f %20d %20f %20d %20d %20d %20d",
57 &monochromaticval, &meanval, &medianval, &stddevval,
58 &widthval, &heightval, &colval, &rowval);
61 LOG(VB_COMMFLAG, LOG_ERR,
62 QString(
"Not enough data in %1: frame %2")
66 if (monochromaticval < 0 || monochromaticval > 1 ||
67 medianval < 0 || (
uint)medianval > UCHAR_MAX ||
68 widthval < 0 || heightval < 0 || colval < 0 || rowval < 0)
70 LOG(VB_COMMFLAG, LOG_ERR,
71 QString(
"Data out of range in %1: frame %2")
75 for (
uint & ctr : counter)
77 if (fscanf(fp,
"%20x", &ctr) != 1)
79 LOG(VB_COMMFLAG, LOG_ERR,
80 QString(
"Not enough data in %1: frame %2")
86 LOG(VB_COMMFLAG, LOG_ERR,
87 QString(
"Data out of range in %1: frame %2")
92 mean[frameno] = meanval;
93 median[frameno] = medianval;
94 stddev[frameno] = stddevval;
95 frow[frameno] = rowval;
96 fcol[frameno] = colval;
97 fwidth[frameno] = widthval;
98 fheight[frameno] = heightval;
99 for (
size_t ii = 0; ii < counter.size(); ii++)
100 histogram[frameno][ii] = counter[ii];
101 monochromatic[frameno] = !widthval || !heightval ? 1 : 0;
112 int *frow,
int *fcol,
int *fwidth,
int *fheight,
116 FILE *fp = fopen(
filename.toLocal8Bit().constData(),
"w");
119 for (
long long frameno = 0; frameno < nframes; frameno++)
121 (void)fprintf(fp,
"%3u %10.6f %3u %10.6f %5d %5d %5d %5d",
122 monochromatic[frameno],
123 static_cast<double>(mean[frameno]), median[frameno],
124 static_cast<double>(stddev[frameno]),
125 fwidth[frameno], fheight[frameno],
126 fcol[frameno], frow[frameno]);
127 for (
unsigned int ii = 0; ii < UCHAR_MAX + 1; ii++)
128 (
void)fprintf(fp,
" %02x", histogram[frameno][ii]);
129 (void)fprintf(fp,
"\n");
132 LOG(VB_COMMFLAG, LOG_ERR, QString(
"Error closing %1: %2")
140 std::shared_ptr<BorderDetector> bd,
141 const QString& debugdir)
142 : m_pgmConverter(std::move(pgmc))
143 , m_borderDetector(std::move(bd))
145 , m_debugdata(debugdir +
"/HistogramAnalyzer-pgm.txt")
147 , m_debugdata(debugdir +
"/HistogramAnalyzer-yuv.txt")
160 QString(
"HistogramAnalyzer debugLevel %1").arg(
m_debugLevel));
189 unsigned int width = buf_dim.width();
190 unsigned int height = buf_dim.height();
202 QString details =
m_logo ? QString(
"logo %1x%2@(%3,%4)")
206 LOG(VB_COMMFLAG, LOG_INFO,
207 QString(
"HistogramAnalyzer::MythPlayerInited %1x%2: %3")
208 .arg(width).arg(height).arg(details));
219 LOG(VB_COMMFLAG, LOG_INFO,
220 QString(
"HistogramAnalyzer::MythPlayerInited nframes %1, allocating %2")
221 .arg(nframes).arg(nframes+128));
223 m_mean =
new float[nframes];
224 m_median =
new unsigned char[nframes];
226 m_fRow =
new int[nframes];
227 m_fCol =
new int[nframes];
243 unsigned int npixels = width * height;
244 m_buf =
new unsigned char[npixels];
251 LOG(VB_COMMFLAG, LOG_INFO,
252 QString(
"HistogramAnalyzer::MythPlayerInited read %1")
268static constexpr int ROUNDUP(
int a,
int b) {
return (a + b - 1) / b * b; }
277 static constexpr int kDefaultColor = 0;
287 static constexpr int kRInc = 4;
288 static constexpr int kCInc = 4;
292 bool ismonochromatic =
false;
297 unsigned int borderpixels = 0;
298 unsigned int livepixels = 0;
299 unsigned int npixels = 0;
300 unsigned int halfnpixels = 0;
301 unsigned char *
pp =
nullptr;
302 unsigned char bordercolor = 0;
303 unsigned long long sumval = 0;
304 unsigned long long sumsquares = 0;
311 std::chrono::microseconds start {0us};
312 std::chrono::microseconds end {0us};
320 LOG(VB_COMMFLAG, LOG_ERR,
321 QString(
"HistogramAnalyzer::analyzeFrame error at frame %1")
328 &croprow, &cropcol, &cropwidth, &cropheight) != 0;
330 start = nowAsDuration<std::chrono::microseconds>();
332 m_fRow[frameno] = croprow;
333 m_fCol[frameno] = cropcol;
340 croprow = pgmheight * 3 / 8;
341 cropheight = pgmheight / 4;
342 cropcol = pgmwidth * 3 / 8;
343 cropwidth = pgmwidth / 4;
348 rr2 =
ROUNDUP(croprow + cropheight, kRInc);
349 cc2 =
ROUNDUP(cropcol + cropwidth, kCInc);
350 rr3 =
ROUNDUP(pgmheight, kRInc);
351 cc3 =
ROUNDUP(pgmwidth, kCInc);
353 borderpixels = ((rr1 / kRInc) * (cc3 / kCInc)) +
354 (((rr2 - rr1) / kRInc) * (cc1 / kCInc)) +
355 (((rr2 - rr1) / kRInc) * ((cc3 - cc2) / kCInc)) +
356 (((rr3 - rr2) / kRInc) * (cc3 / kCInc));
360 m_histVal[kDefaultColor] += borderpixels;
361 for (
int rr = rr1; rr < rr2; rr += kRInc)
363 int rroffset = rr * pgmwidth;
365 for (
int cc = cc1; cc < cc2; cc += kCInc)
371 unsigned char val = pgm->data[0][rroffset + cc];
374 sumsquares += 1ULL * val * val;
379 npixels = borderpixels + livepixels;
382 halfnpixels = npixels / 2;
383 for (
unsigned int color = 0; color < UCHAR_MAX + 1; color++)
385 ((
m_histVal[color] * UCHAR_MAX) + halfnpixels) / npixels;
388 if (ismonochromatic && livepixels)
394 bordercolor = (sumval + livepixels - 1) / livepixels;
395 sumval += 1ULL * borderpixels * bordercolor;
396 sumsquares += 1ULL * borderpixels * bordercolor * bordercolor;
399 memset(
m_buf, bordercolor, borderpixels *
sizeof(*
m_buf));
401 m_mean[frameno] = (float)sumval / npixels;
402 m_median[frameno] = quick_select_median<uint8_t>(
m_buf, npixels);
404 sqrt((sumsquares - ((
float)sumval * sumval / npixels)) / (npixels - 1)) :
407 end = nowAsDuration<std::chrono::microseconds>();
423 LOG(VB_COMMFLAG, LOG_INFO,
424 QString(
"HistogramAnalyzer::finished wrote %1")
442 LOG(VB_COMMFLAG, LOG_INFO, QString(
"HA Time: analyze=%1s")
static constexpr int ROUNDUP(int a, int b)
#define PGM_CONVERT_GREYSCALE
static const long long kUncached
HistogramAnalyzer(std::shared_ptr< PGMConverter > pgmc, std::shared_ptr< BorderDetector > bd, const QString &debugdir)
TemplateFinder * m_logoFinder
unsigned char * m_monochromatic
enum FrameAnalyzer::analyzeFrameResult MythPlayerInited(MythPlayer *player, long long nframes)
const struct AVFrame * m_logo
std::array< int, UCHAR_MAX+1 > m_histVal
std::array< uint8_t, UCHAR_MAX+1 > Histogram
int finished(long long nframes, bool final)
std::shared_ptr< BorderDetector > m_borderDetector
void setLogoState(TemplateFinder *finder)
std::chrono::microseconds m_analyzeTime
int reportTime(void) const
enum FrameAnalyzer::analyzeFrameResult analyzeFrame(const MythVideoFrame *frame, long long frameno)
std::shared_ptr< PGMConverter > m_pgmConverter
int GetNumSetting(const QString &key, int defaultval=0)
QSize GetVideoBufferSize(void) const
const struct AVFrame * getTemplate(int *prow, int *pcol, int *pwidth, int *pheight) const
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
bool writeData(const QString &filename, float *mean, unsigned char *median, float *stddev, int *frow, int *fcol, int *fwidth, int *fheight, HistogramAnalyzer::Histogram *histogram, unsigned char *monochromatic, long long nframes)
bool readData(const QString &filename, float *mean, unsigned char *median, float *stddev, int *frow, int *fcol, int *fwidth, int *fheight, HistogramAnalyzer::Histogram *histogram, unsigned char *monochromatic, long long nframes)
void createDebugDirectory(const QString &dirname, const QString &comment)
QString strftimeval(std::chrono::microseconds usecs)
static QString cleanup(const QString &str)