00001 #include <stdio.h>
00002 #include <stdlib.h>
00003 #include <math.h>
00004
00005 #include "mythcontext.h"
00006 #include "NuppelVideoPlayer.h"
00007
00008 #include "CommDetector2.h"
00009 #include "FrameAnalyzer.h"
00010 #include "quickselect.h"
00011 #include "HistogramAnalyzer.h"
00012 #include "SceneChangeDetector.h"
00013
00014 using namespace commDetector2;
00015 using namespace frameAnalyzer;
00016
00017 namespace {
00018
00019 int
00020 scenechange_data_sort_desc_frequency(const void *aa, const void *bb)
00021 {
00022
00023 const struct SceneChangeDetector::scenechange_data *sc1 =
00024 (const struct SceneChangeDetector::scenechange_data*)aa;
00025 const struct SceneChangeDetector::scenechange_data *sc2 =
00026 (const struct SceneChangeDetector::scenechange_data*)bb;
00027 int freqdiff = sc2->frequency - sc1->frequency;
00028 return freqdiff ? freqdiff : sc1->color - sc2->color;
00029 }
00030
00031 void
00032 scenechange_data_init(SceneChangeDetector::SceneChangeData *scdata,
00033 const HistogramAnalyzer::Histogram *hh)
00034 {
00035 unsigned int ncolors = sizeof(*hh)/sizeof((*hh)[0]);
00036
00037 for (unsigned int ii = 0; ii < ncolors; ii++)
00038 {
00039 (*scdata)[ii].color = ii;
00040 (*scdata)[ii].frequency = (*hh)[ii];
00041 }
00042 qsort(*scdata, sizeof(*scdata)/sizeof((*scdata)[0]), sizeof((*scdata)[0]),
00043 scenechange_data_sort_desc_frequency);
00044 }
00045
00046 unsigned short
00047 scenechange_data_diff(const SceneChangeDetector::SceneChangeData *sc1,
00048 const SceneChangeDetector::SceneChangeData *sc2)
00049 {
00050
00051
00052
00053
00054 unsigned short diff = 0;
00055 for (unsigned int ii = 0; ii < sizeof(*sc1)/sizeof((*sc1)[0]); ii++)
00056 diff += abs((*sc1)[ii].frequency - (*sc2)[ii].frequency) +
00057 abs((*sc1)[ii].color - (*sc2)[ii].color);
00058 return diff;
00059 }
00060
00061 bool
00062 writeData(QString filename, const unsigned short *scdiff, long long nframes)
00063 {
00064 FILE *fp;
00065 long long frameno;
00066
00067 if (!(fp = fopen(filename, "w")))
00068 return false;
00069 for (frameno = 0; frameno < nframes; frameno++)
00070 (void)fprintf(fp, "%5u\n", scdiff[frameno]);
00071 if (fclose(fp))
00072 VERBOSE(VB_COMMFLAG, QString("Error closing %1: %2")
00073 .arg(filename).arg(strerror(errno)));
00074 return true;
00075 }
00076
00077 void
00078 computeChangeMap(FrameAnalyzer::FrameMap *changeMap, long long nframes,
00079 const unsigned short *scdiff, unsigned short mindiff)
00080 {
00081
00082
00083
00084 long long frameno;
00085
00086 changeMap->clear();
00087 for (frameno = 0; frameno < nframes; frameno++)
00088 {
00089 if (scdiff[frameno] > mindiff)
00090 changeMap->insert(frameno, 0);
00091 }
00092 }
00093
00094 };
00095
00096 SceneChangeDetector::SceneChangeDetector(HistogramAnalyzer *ha,
00097 QString debugdir)
00098 : FrameAnalyzer()
00099 , histogramAnalyzer(ha)
00100 , scdata(NULL)
00101 , scdiff(NULL)
00102 , debugLevel(0)
00103 , debugdata(debugdir + "/SceneChangeDetector.txt")
00104 , debug_scenechange(false)
00105 , scenechange_done(false)
00106 {
00107 VERBOSE(VB_COMMFLAG, "SceneChangeDetector");
00108
00109
00110
00111
00112
00113
00114 debugLevel = gContext->GetNumSetting("SceneChangeDetectorDebugLevel", 0);
00115
00116 if (debugLevel >= 1)
00117 {
00118 createDebugDirectory(debugdir,
00119 QString("SceneChangeDetector debugLevel %1").arg(debugLevel));
00120 debug_scenechange = true;
00121 }
00122 }
00123
00124 SceneChangeDetector::~SceneChangeDetector(void)
00125 {
00126 if (scdata)
00127 delete []scdata;
00128 if (scdiff)
00129 delete []scdiff;
00130 }
00131
00132 enum FrameAnalyzer::analyzeFrameResult
00133 SceneChangeDetector::nuppelVideoPlayerInited(NuppelVideoPlayer *nvp,
00134 long long nframes)
00135 {
00136 FrameAnalyzer::analyzeFrameResult ares =
00137 histogramAnalyzer->nuppelVideoPlayerInited(nvp, nframes);
00138
00139 fps = nvp->GetFrameRate();
00140
00141 scdata = new SceneChangeData[nframes];
00142 memset(scdata, 0, nframes * sizeof(*scdata));
00143
00144 scdiff = new unsigned short[nframes];
00145 memset(scdiff, 0, nframes * sizeof(*scdiff));
00146
00147 QSize video_disp_dim = nvp->GetVideoSize();
00148
00149 VERBOSE(VB_COMMFLAG, QString(
00150 "SceneChangeDetector::nuppelVideoPlayerInited %1x%2")
00151 .arg(video_disp_dim.width())
00152 .arg(video_disp_dim.height()));
00153
00154 return ares;
00155 }
00156
00157 enum FrameAnalyzer::analyzeFrameResult
00158 SceneChangeDetector::analyzeFrame(const VideoFrame *frame, long long frameno,
00159 long long *pNextFrame)
00160 {
00161 *pNextFrame = NEXTFRAME;
00162
00163 if (histogramAnalyzer->analyzeFrame(frame, frameno) ==
00164 FrameAnalyzer::ANALYZE_OK)
00165 return ANALYZE_OK;
00166
00167 VERBOSE(VB_COMMFLAG,
00168 QString("SceneChangeDetector::analyzeFrame error at frame %1")
00169 .arg(frameno));
00170 return ANALYZE_ERROR;
00171 }
00172
00173 int
00174 SceneChangeDetector::finished(long long nframes, bool final)
00175 {
00176 if (histogramAnalyzer->finished(nframes, final))
00177 return -1;
00178
00179 VERBOSE(VB_COMMFLAG, QString("SceneChangeDetector::finished(%1)")
00180 .arg(nframes));
00181
00182 const HistogramAnalyzer::Histogram *histogram =
00183 histogramAnalyzer->getHistograms();
00184 for (unsigned int frameno = 0; frameno < nframes; frameno++)
00185 (void)scenechange_data_init(&scdata[frameno], &histogram[frameno]);
00186 scdiff[0] = 0;
00187 for (unsigned int frameno = 1; frameno < nframes; frameno++)
00188 scdiff[frameno] = scenechange_data_diff(&scdata[frameno - 1],
00189 &scdata[frameno]);
00190
00191 if (!scenechange_done && debug_scenechange)
00192 {
00193 if (final && writeData(debugdata, scdiff, nframes))
00194 {
00195 VERBOSE(VB_COMMFLAG, QString(
00196 "SceneChangeDetector::finished wrote %1")
00197 .arg(debugdata));
00198 scenechange_done = true;
00199 }
00200 }
00201
00202
00203 unsigned short *scdiffsort = new unsigned short[nframes];
00204 memcpy(scdiffsort, scdiff, nframes * sizeof(*scdiff));
00205 unsigned short mindiff = quick_select_ushort(scdiffsort, nframes,
00206 (int)(0.979472 * nframes));
00207 VERBOSE(VB_COMMFLAG, QString(
00208 "SceneChangeDetector::finished applying threshold value %1")
00209 .arg(mindiff));
00210 computeChangeMap(&changeMap, nframes, scdiff, mindiff);
00211 delete []scdiffsort;
00212 if (debugLevel >= 2)
00213 frameAnalyzerReportMapms(&changeMap, fps, "SC frame");
00214
00215 return 0;
00216 }
00217
00218 int
00219 SceneChangeDetector::reportTime(void) const
00220 {
00221 return histogramAnalyzer->reportTime();
00222 }
00223
00224