MythTV  master
SceneChangeDetector.cpp
Go to the documentation of this file.
1 // ANSI C headers
2 #include <cmath>
3 #include <cstdlib>
4 #include <utility>
5 
6 // MythTV headers
7 #include "mythcorecontext.h" /* gContext */
8 #include "mythplayer.h"
9 #include "mythlogging.h"
10 
11 // Commercial Flagging headers
12 #include "CommDetector2.h"
13 #include "FrameAnalyzer.h"
14 #include "quickselect.h"
15 #include "HistogramAnalyzer.h"
16 #include "SceneChangeDetector.h"
17 
18 using namespace commDetector2;
19 using namespace frameAnalyzer;
20 
21 namespace {
22 
23 int
24 scenechange_data_sort_desc_frequency(const void *aa, const void *bb)
25 {
26  /* Descending by frequency, then ascending by color. */
27  const auto *sc1 = (const struct SceneChangeDetector::scenechange_data*)aa;
28  const auto *sc2 = (const struct SceneChangeDetector::scenechange_data*)bb;
29  int freqdiff = sc2->frequency - sc1->frequency;
30  return freqdiff ? freqdiff : sc1->color - sc2->color;
31 }
32 
33 void
34 scenechange_data_init(SceneChangeDetector::SceneChangeData *scdata,
36 {
37  unsigned int ncolors = sizeof(*hh)/sizeof((*hh)[0]);
38 
39  for (unsigned int ii = 0; ii < ncolors; ii++)
40  {
41  (*scdata)[ii].color = ii;
42  (*scdata)[ii].frequency = (*hh)[ii];
43  }
44  qsort(*scdata, sizeof(*scdata)/sizeof((*scdata)[0]), sizeof((*scdata)[0]),
45  scenechange_data_sort_desc_frequency);
46 }
47 
48 unsigned short
49 scenechange_data_diff(const SceneChangeDetector::SceneChangeData *sc1,
51 {
52  /*
53  * Compute a notion of "difference" that takes into account the difference
54  * in relative frequencies of the dominant colors.
55  */
56  unsigned short diff = 0;
57  for (size_t ii = 0; ii < sizeof(*sc1)/sizeof((*sc1)[0]); ii++)
58  diff += abs((*sc1)[ii].frequency - (*sc2)[ii].frequency) +
59  abs((*sc1)[ii].color - (*sc2)[ii].color);
60  return diff;
61 }
62 
63 bool
64 writeData(const QString& filename, const unsigned short *scdiff, long long nframes)
65 {
66  QByteArray fname = filename.toLocal8Bit();
67  FILE *fp = fopen(fname.constData(), "w");
68  if (fp == nullptr)
69  return false;
70  for (long long frameno = 0; frameno < nframes; frameno++)
71  (void)fprintf(fp, "%5u\n", scdiff[frameno]);
72  if (fclose(fp))
73  LOG(VB_COMMFLAG, LOG_ERR, QString("Error closing %1: %2")
74  .arg(filename).arg(strerror(errno)));
75  return true;
76 }
77 
78 void
79 computeChangeMap(FrameAnalyzer::FrameMap *changeMap, long long nframes,
80  const unsigned short *scdiff, unsigned short mindiff)
81 {
82  /*
83  * Look for sudden changes in histogram.
84  */
85  changeMap->clear();
86  for (long long frameno = 0; frameno < nframes; frameno++)
87  {
88  if (scdiff[frameno] > mindiff)
89  changeMap->insert(frameno, 0);
90  }
91 }
92 
93 }; /* namespace */
94 
95 SceneChangeDetector::SceneChangeDetector(std::shared_ptr<HistogramAnalyzer> ha,
96  const QString& debugdir)
97  : m_histogramAnalyzer(std::move(ha))
98  , m_debugData(debugdir + "/SceneChangeDetector.txt")
99 {
100  LOG(VB_COMMFLAG, LOG_INFO, "SceneChangeDetector");
101 
102  /*
103  * debugLevel:
104  * 0: no debugging
105  * 2: extra verbosity [O(nframes)]
106  */
107  m_debugLevel = gCoreContext->GetNumSetting("SceneChangeDetectorDebugLevel", 0);
108 
109  if (m_debugLevel >= 1)
110  {
111  createDebugDirectory(debugdir,
112  QString("SceneChangeDetector debugLevel %1").arg(m_debugLevel));
113  m_debugSceneChange = true;
114  }
115 }
116 
118 {
119  delete []m_scData;
120  delete []m_scDiff;
121 }
122 
125  long long nframes)
126 {
128  m_histogramAnalyzer->MythPlayerInited(player, nframes);
129 
130  m_fps = player->GetFrameRate();
131 
132  m_scData = new SceneChangeData[nframes];
133  memset(m_scData, 0, nframes * sizeof(*m_scData));
134 
135  m_scDiff = new unsigned short[nframes];
136  memset(m_scDiff, 0, nframes * sizeof(*m_scDiff));
137 
138  QSize video_disp_dim = player->GetVideoSize();
139 
140  LOG(VB_COMMFLAG, LOG_INFO,
141  QString("SceneChangeDetector::MythPlayerInited %1x%2")
142  .arg(video_disp_dim.width())
143  .arg(video_disp_dim.height()));
144 
145  return ares;
146 }
147 
149 SceneChangeDetector::analyzeFrame(const VideoFrame *frame, long long frameno,
150  long long *pNextFrame)
151 {
152  *pNextFrame = kNextFrame;
153 
154  if (m_histogramAnalyzer->analyzeFrame(frame, frameno) ==
156  return ANALYZE_OK;
157 
158  LOG(VB_COMMFLAG, LOG_ERR,
159  QString("SceneChangeDetector::analyzeFrame error at frame %1")
160  .arg(frameno));
161  return ANALYZE_ERROR;
162 }
163 
164 int
165 SceneChangeDetector::finished(long long nframes, bool final)
166 {
167  if (m_histogramAnalyzer->finished(nframes, final))
168  return -1;
169 
170  LOG(VB_COMMFLAG, LOG_INFO, QString("SceneChangeDetector::finished(%1)")
171  .arg(nframes));
172 
173  const HistogramAnalyzer::Histogram *histogram =
174  m_histogramAnalyzer->getHistograms();
175  for (long long frameno = 0; frameno < nframes; frameno++)
176  scenechange_data_init(&m_scData[frameno], &histogram[frameno]);
177  m_scDiff[0] = 0;
178  for (long long frameno = 1; frameno < nframes; frameno++)
179  m_scDiff[frameno] = scenechange_data_diff(&m_scData[frameno - 1],
180  &m_scData[frameno]);
181 
183  {
184  if (final && writeData(m_debugData, m_scDiff, nframes))
185  {
186  LOG(VB_COMMFLAG, LOG_INFO,
187  QString("SceneChangeDetector::finished wrote %1")
188  .arg(m_debugData));
189  m_sceneChangeDone = true;
190  }
191  }
192 
193  /* Identify all scene-change frames (changeMap). */
194  auto *scdiffsort = new unsigned short[nframes];
195  memcpy(scdiffsort, m_scDiff, nframes * sizeof(*m_scDiff));
196  unsigned short mindiff = quick_select_ushort(scdiffsort, nframes,
197  (int)(0.979472 * nframes));
198  LOG(VB_COMMFLAG, LOG_INFO,
199  QString("SceneChangeDetector::finished applying threshold value %1")
200  .arg(mindiff));
201  computeChangeMap(&m_changeMap, nframes, m_scDiff, mindiff);
202  delete []scdiffsort;
203  if (m_debugLevel >= 2)
205 
206  return 0;
207 }
208 
209 int
211 {
212  return m_histogramAnalyzer->reportTime();
213 }
214 
215 /* vim: set expandtab tabstop=4 shiftwidth=4: */
HistogramAnalyzer::Histogram
unsigned char[UCHAR_MAX+1] Histogram
Definition: HistogramAnalyzer.h:35
SceneChangeDetector::m_scDiff
unsigned short * m_scDiff
Definition: SceneChangeDetector.h:53
SceneChangeDetector::m_histogramAnalyzer
std::shared_ptr< HistogramAnalyzer > m_histogramAnalyzer
Definition: SceneChangeDetector.h:48
FrameAnalyzer::ANALYZE_OK
@ ANALYZE_OK
Definition: FrameAnalyzer.h:37
CommDetector2.h
FrameAnalyzer::FrameMap
QMap< long long, long long > FrameMap
Definition: FrameAnalyzer.h:45
arg
arg(title).arg(filename).arg(doDelete))
quickselect.h
MythPlayer::GetFrameRate
float GetFrameRate(void) const
Definition: mythplayer.h:219
LOG
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:23
SceneChangeDetector::deleteLater
virtual void deleteLater(void)
Definition: SceneChangeDetector.cpp:117
mythplayer.h
MythPlayer
Definition: mythplayer.h:165
VideoFrame
Definition: mythframe.h:138
SceneChangeDetector::scenechange_data
Definition: SceneChangeDetector.h:38
commDetector2::createDebugDirectory
void createDebugDirectory(const QString &dirname, const QString &comment)
Definition: CommDetector2.cpp:231
mythburn.FILE
int FILE
Definition: mythburn.py:139
SceneChangeDetector::analyzeFrame
enum analyzeFrameResult analyzeFrame(const VideoFrame *frame, long long frameno, long long *pNextFrame) override
Definition: SceneChangeDetector.cpp:149
mythlogging.h
SceneChangeDetector.h
SceneChangeDetector::finished
int finished(long long nframes, bool final) override
Definition: SceneChangeDetector.cpp:165
filename
QString filename
Definition: mythplugins/mytharchive/mytharchivehelper/main.cpp:634
SceneChangeDetector::reportTime
int reportTime(void) const override
Definition: SceneChangeDetector.cpp:210
FrameAnalyzer::kNextFrame
static const long long kNextFrame
Definition: FrameAnalyzer.h:59
FrameAnalyzer::analyzeFrameResult
analyzeFrameResult
Definition: FrameAnalyzer.h:36
FrameAnalyzer.h
FrameAnalyzer::ANALYZE_ERROR
@ ANALYZE_ERROR
Definition: FrameAnalyzer.h:38
gCoreContext
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
Definition: mythcorecontext.cpp:57
MythCoreContext::GetNumSetting
int GetNumSetting(const QString &key, int defaultval=0)
Definition: mythcorecontext.cpp:930
SceneChangeDetector::MythPlayerInited
enum analyzeFrameResult MythPlayerInited(MythPlayer *player, long long nframes) override
Definition: SceneChangeDetector.cpp:124
SceneChangeDetector::m_debugData
QString m_debugData
Definition: SceneChangeDetector.h:59
SceneChangeDetector::m_scData
SceneChangeData * m_scData
Definition: SceneChangeDetector.h:52
HistogramAnalyzer.h
mythcorecontext.h
MythPlayer::GetVideoSize
QSize GetVideoSize(void) const
Definition: mythplayer.h:217
SceneChangeDetector::m_sceneChangeDone
bool m_sceneChangeDone
Definition: SceneChangeDetector.h:61
SceneChangeDetector::SceneChangeDetector
SceneChangeDetector(std::shared_ptr< HistogramAnalyzer > ha, const QString &debugdir)
Definition: SceneChangeDetector.cpp:95
commDetector2
Definition: CommDetector2.cpp:195
SceneChangeDetector::m_fps
float m_fps
Definition: SceneChangeDetector.h:49
SceneChangeDetector::SceneChangeData
scenechange_data[UCHAR_MAX+1] SceneChangeData
Definition: SceneChangeDetector.h:42
frameAnalyzer
Definition: FrameAnalyzer.cpp:7
SceneChangeDetector::m_changeMap
FrameAnalyzer::FrameMap m_changeMap
Definition: SceneChangeDetector.h:55
SceneChangeDetector::m_debugLevel
int m_debugLevel
Definition: SceneChangeDetector.h:58
SceneChangeDetector::m_debugSceneChange
bool m_debugSceneChange
Definition: SceneChangeDetector.h:60
quick_select_ushort
unsigned short quick_select_ushort(unsigned short *arr, int nelems, int select)
Definition: quickselect.cpp:74
frameAnalyzer::frameAnalyzerReportMapms
void frameAnalyzerReportMapms(const FrameAnalyzer::FrameMap *frameMap, float fps, const char *comment)
Definition: FrameAnalyzer.cpp:52