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
10
11// Commercial Flagging headers
12#include "CommDetector2.h"
13#include "FrameAnalyzer.h"
14#include "HistogramAnalyzer.h"
15#include "SceneChangeDetector.h"
16#include "quickselect.h"
17
18using namespace commDetector2;
19using namespace frameAnalyzer;
20
21namespace {
22
23int
24scenechange_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
33void
36{
37 unsigned int ncolors = hh->size();
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->data(), scdata->size(), sizeof(SceneChangeDetector::scenechange_data),
46}
47
51{
52 /*
53 * Compute a notion of "difference" that takes into account the difference
54 * in relative frequencies of the dominant colors.
55 */
56 uint16_t 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
63bool
64writeData(const QString& filename, const std::vector<uint16_t>& scdiff)
65{
66 QByteArray fname = filename.toLocal8Bit();
67 FILE *fp = fopen(fname.constData(), "w");
68 if (fp == nullptr)
69 return false;
70 for (auto value : scdiff)
71 (void)fprintf(fp, "%5u\n", value);
72 if (fclose(fp))
73 LOG(VB_COMMFLAG, LOG_ERR, QString("Error closing %1: %2")
74 .arg(filename, strerror(errno)));
75 return true;
76}
77
78void
80 const std::vector<uint16_t>& scdiff, uint16_t mindiff)
81{
82 /*
83 * Look for sudden changes in histogram.
84 */
85 changeMap->clear();
86 for (size_t frameno = 0; frameno < scdiff.size(); frameno++)
87 {
88 if (scdiff[frameno] > mindiff)
89 changeMap->insert(frameno, 0);
90 }
91}
92
93}; /* namespace */
94
95SceneChangeDetector::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
117
120 long long nframes)
121{
123 m_histogramAnalyzer->MythPlayerInited(player, nframes);
124
125 m_fps = player->GetFrameRate();
126
127 m_scData.resize(nframes);
128 m_scDiff.resize(nframes);
129
130 QSize video_disp_dim = player->GetVideoSize();
131
132 LOG(VB_COMMFLAG, LOG_INFO,
133 QString("SceneChangeDetector::MythPlayerInited %1x%2")
134 .arg(video_disp_dim.width())
135 .arg(video_disp_dim.height()));
136
137 return ares;
138}
139
141SceneChangeDetector::analyzeFrame(const MythVideoFrame *frame, long long frameno,
142 long long *pNextFrame)
143{
144 *pNextFrame = kNextFrame;
145
146 if (m_histogramAnalyzer->analyzeFrame(frame, frameno) ==
148 return ANALYZE_OK;
149
150 LOG(VB_COMMFLAG, LOG_ERR,
151 QString("SceneChangeDetector::analyzeFrame error at frame %1")
152 .arg(frameno));
153 return ANALYZE_ERROR;
154}
155
156int
157SceneChangeDetector::finished(long long nframes, bool final)
158{
159 if (m_histogramAnalyzer->finished(nframes, final))
160 return -1;
161
162 LOG(VB_COMMFLAG, LOG_INFO, QString("SceneChangeDetector::finished(%1)")
163 .arg(nframes));
164
165 const HistogramAnalyzer::Histogram *histogram =
166 m_histogramAnalyzer->getHistograms();
167 for (long long frameno = 0; frameno < nframes; frameno++)
168 scenechange_data_init(&m_scData[frameno], &histogram[frameno]);
169 m_scDiff[0] = 0;
170 for (long long frameno = 1; frameno < nframes; frameno++)
171 m_scDiff[frameno] = scenechange_data_diff(&m_scData[frameno - 1],
172 &m_scData[frameno]);
173
175 {
176 if (final && writeData(m_debugData, m_scDiff))
177 {
178 LOG(VB_COMMFLAG, LOG_INFO,
179 QString("SceneChangeDetector::finished wrote %1")
180 .arg(m_debugData));
181 m_sceneChangeDone = true;
182 }
183 }
184
185 /* Identify all scene-change frames (changeMap). */
186 std::vector<uint16_t> scdiffsort = m_scDiff;
187 auto mindiff = quick_select<uint16_t>(scdiffsort.data(), nframes,
188 (int)(0.979472 * nframes));
189 LOG(VB_COMMFLAG, LOG_INFO,
190 QString("SceneChangeDetector::finished applying threshold value %1")
191 .arg(mindiff));
193 if (m_debugLevel >= 2)
195
196 return 0;
197}
198
199int
201{
202 return m_histogramAnalyzer->reportTime();
203}
204
205/* vim: set expandtab tabstop=4 shiftwidth=4: */
static const long long kNextFrame
Definition: FrameAnalyzer.h:59
QMap< long long, long long > FrameMap
Definition: FrameAnalyzer.h:45
std::array< uint8_t, UCHAR_MAX+1 > Histogram
int GetNumSetting(const QString &key, int defaultval=0)
QSize GetVideoSize(void) const
Definition: mythplayer.h:131
float GetFrameRate(void) const
Definition: mythplayer.h:133
std::vector< SceneChangeData > m_scData
std::array< scenechange_data, UCHAR_MAX+1 > SceneChangeData
std::vector< uint16_t > m_scDiff
std::shared_ptr< HistogramAnalyzer > m_histogramAnalyzer
int finished(long long nframes, bool final) override
enum analyzeFrameResult MythPlayerInited(MythPlayer *player, long long nframes) override
SceneChangeDetector(std::shared_ptr< HistogramAnalyzer > ha, const QString &debugdir)
FrameAnalyzer::FrameMap m_changeMap
int reportTime(void) const override
enum analyzeFrameResult analyzeFrame(const MythVideoFrame *frame, long long frameno, long long *pNextFrame) override
unsigned short uint16_t
Definition: iso6937tables.h:3
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
void scenechange_data_init(SceneChangeDetector::SceneChangeData *scdata, const HistogramAnalyzer::Histogram *hh)
bool writeData(const QString &filename, const std::vector< uint16_t > &scdiff)
uint16_t scenechange_data_diff(const SceneChangeDetector::SceneChangeData *sc1, const SceneChangeDetector::SceneChangeData *sc2)
int scenechange_data_sort_desc_frequency(const void *aa, const void *bb)
void computeChangeMap(FrameAnalyzer::FrameMap *changeMap, const std::vector< uint16_t > &scdiff, uint16_t mindiff)
void createDebugDirectory(const QString &dirname, const QString &comment)
void frameAnalyzerReportMapms(const FrameAnalyzer::FrameMap *frameMap, float fps, const char *comment)
int FILE
Definition: mythburn.py:138
STL namespace.