MythTV  0.26-pre
SceneChangeDetector.cpp
Go to the documentation of this file.
00001 // ANSI C headers
00002 #include <cstdlib>
00003 #include <cmath>
00004 
00005 // MythTV headers
00006 #include "mythcorecontext.h"    /* gContext */
00007 #include "mythplayer.h"
00008 #include "mythlogging.h"
00009 
00010 // Commercial Flagging headers
00011 #include "CommDetector2.h"
00012 #include "FrameAnalyzer.h"
00013 #include "quickselect.h"
00014 #include "HistogramAnalyzer.h"
00015 #include "SceneChangeDetector.h"
00016 
00017 using namespace commDetector2;
00018 using namespace frameAnalyzer;
00019 
00020 namespace {
00021 
00022 int
00023 scenechange_data_sort_desc_frequency(const void *aa, const void *bb)
00024 {
00025     /* Descending by frequency, then ascending by color. */
00026     const struct SceneChangeDetector::scenechange_data *sc1 =
00027         (const struct SceneChangeDetector::scenechange_data*)aa;
00028     const struct SceneChangeDetector::scenechange_data *sc2 =
00029         (const struct SceneChangeDetector::scenechange_data*)bb;
00030     int freqdiff = sc2->frequency - sc1->frequency;
00031     return freqdiff ? freqdiff : sc1->color - sc2->color;
00032 }
00033 
00034 void
00035 scenechange_data_init(SceneChangeDetector::SceneChangeData *scdata,
00036         const HistogramAnalyzer::Histogram *hh)
00037 {
00038     unsigned int    ncolors = sizeof(*hh)/sizeof((*hh)[0]);
00039 
00040     for (unsigned int ii = 0; ii < ncolors; ii++)
00041     {
00042         (*scdata)[ii].color = ii;
00043         (*scdata)[ii].frequency = (*hh)[ii];
00044     }
00045     qsort(*scdata, sizeof(*scdata)/sizeof((*scdata)[0]), sizeof((*scdata)[0]),
00046             scenechange_data_sort_desc_frequency);
00047 }
00048 
00049 unsigned short
00050 scenechange_data_diff(const SceneChangeDetector::SceneChangeData *sc1,
00051         const SceneChangeDetector::SceneChangeData *sc2)
00052 {
00053     /*
00054      * Compute a notion of "difference" that takes into account the difference
00055      * in relative frequencies of the dominant colors.
00056      */
00057     unsigned short diff = 0;
00058     for (unsigned int ii = 0; ii < sizeof(*sc1)/sizeof((*sc1)[0]); ii++)
00059         diff += abs((*sc1)[ii].frequency - (*sc2)[ii].frequency) +
00060             abs((*sc1)[ii].color - (*sc2)[ii].color);
00061     return diff;
00062 }
00063 
00064 bool
00065 writeData(QString filename, const unsigned short *scdiff, long long nframes)
00066 {
00067     FILE            *fp;
00068     long long       frameno;
00069 
00070     QByteArray fname = filename.toLocal8Bit();
00071     if (!(fp = fopen(fname.constData(), "w")))
00072         return false;
00073     for (frameno = 0; frameno < nframes; frameno++)
00074         (void)fprintf(fp, "%5u\n", scdiff[frameno]);
00075     if (fclose(fp))
00076         LOG(VB_COMMFLAG, LOG_ERR, QString("Error closing %1: %2")
00077                 .arg(filename).arg(strerror(errno)));
00078     return true;
00079 }
00080 
00081 void
00082 computeChangeMap(FrameAnalyzer::FrameMap *changeMap, long long nframes,
00083         const unsigned short *scdiff, unsigned short mindiff)
00084 {
00085     /*
00086      * Look for sudden changes in histogram.
00087      */
00088     long long           frameno;
00089 
00090     changeMap->clear();
00091     for (frameno = 0; frameno < nframes; frameno++)
00092     {
00093         if (scdiff[frameno] > mindiff)
00094             changeMap->insert(frameno, 0);
00095     }
00096 }
00097 
00098 };  /* namespace */
00099 
00100 SceneChangeDetector::SceneChangeDetector(HistogramAnalyzer *ha,
00101         QString debugdir)
00102     : FrameAnalyzer()
00103     , histogramAnalyzer(ha)
00104     , fps(0.0f)
00105     , scdata(NULL)
00106     , scdiff(NULL)
00107     , debugLevel(0)
00108     , debugdata(debugdir + "/SceneChangeDetector.txt")
00109     , debug_scenechange(false)
00110     , scenechange_done(false)
00111 {
00112     LOG(VB_COMMFLAG, LOG_INFO, "SceneChangeDetector");
00113 
00114     /*
00115      * debugLevel:
00116      *      0: no debugging
00117      *      2: extra verbosity [O(nframes)]
00118      */
00119     debugLevel = gCoreContext->GetNumSetting("SceneChangeDetectorDebugLevel", 0);
00120 
00121     if (debugLevel >= 1)
00122     {
00123         createDebugDirectory(debugdir,
00124             QString("SceneChangeDetector debugLevel %1").arg(debugLevel));
00125         debug_scenechange = true;
00126     }
00127 }
00128 
00129 void SceneChangeDetector::deleteLater(void)
00130 {
00131     if (scdata)
00132         delete []scdata;
00133     if (scdiff)
00134         delete []scdiff;
00135 }
00136 
00137 enum FrameAnalyzer::analyzeFrameResult
00138 SceneChangeDetector::MythPlayerInited(MythPlayer *player,
00139         long long nframes)
00140 {
00141     FrameAnalyzer::analyzeFrameResult ares =
00142         histogramAnalyzer->MythPlayerInited(player, nframes);
00143 
00144     fps = player->GetFrameRate();
00145 
00146     scdata = new SceneChangeData[nframes];
00147     memset(scdata, 0, nframes * sizeof(*scdata));
00148 
00149     scdiff = new unsigned short[nframes];
00150     memset(scdiff, 0, nframes * sizeof(*scdiff));
00151 
00152     QSize video_disp_dim = player->GetVideoSize();
00153 
00154     LOG(VB_COMMFLAG, LOG_INFO, 
00155         QString("SceneChangeDetector::MythPlayerInited %1x%2")
00156             .arg(video_disp_dim.width())
00157             .arg(video_disp_dim.height()));
00158 
00159     return ares;
00160 }
00161 
00162 enum FrameAnalyzer::analyzeFrameResult
00163 SceneChangeDetector::analyzeFrame(const VideoFrame *frame, long long frameno,
00164         long long *pNextFrame)
00165 {
00166     *pNextFrame = NEXTFRAME;
00167 
00168     if (histogramAnalyzer->analyzeFrame(frame, frameno) ==
00169             FrameAnalyzer::ANALYZE_OK)
00170         return ANALYZE_OK;
00171 
00172     LOG(VB_COMMFLAG, LOG_ERR,
00173         QString("SceneChangeDetector::analyzeFrame error at frame %1")
00174             .arg(frameno));
00175     return ANALYZE_ERROR;
00176 }
00177 
00178 int
00179 SceneChangeDetector::finished(long long nframes, bool final)
00180 {
00181     if (histogramAnalyzer->finished(nframes, final))
00182         return -1;
00183 
00184     LOG(VB_COMMFLAG, LOG_INFO, QString("SceneChangeDetector::finished(%1)")
00185             .arg(nframes));
00186 
00187     const HistogramAnalyzer::Histogram *histogram =
00188         histogramAnalyzer->getHistograms();
00189     for (unsigned int frameno = 0; frameno < nframes; frameno++)
00190         (void)scenechange_data_init(&scdata[frameno], &histogram[frameno]);
00191     scdiff[0] = 0;
00192     for (unsigned int frameno = 1; frameno < nframes; frameno++)
00193         scdiff[frameno] = scenechange_data_diff(&scdata[frameno - 1],
00194                 &scdata[frameno]);
00195 
00196     if (!scenechange_done && debug_scenechange)
00197     {
00198         if (final && writeData(debugdata, scdiff, nframes))
00199         {
00200             LOG(VB_COMMFLAG, LOG_INFO, 
00201                 QString("SceneChangeDetector::finished wrote %1")
00202                     .arg(debugdata));
00203             scenechange_done = true;
00204         }
00205     }
00206 
00207     /* Identify all scene-change frames (changeMap). */
00208     unsigned short *scdiffsort = new unsigned short[nframes];
00209     memcpy(scdiffsort, scdiff, nframes * sizeof(*scdiff));
00210     unsigned short mindiff = quick_select_ushort(scdiffsort, nframes,
00211                                                  (int)(0.979472 * nframes));
00212     LOG(VB_COMMFLAG, LOG_INFO,
00213         QString("SceneChangeDetector::finished applying threshold value %1")
00214             .arg(mindiff));
00215     computeChangeMap(&changeMap, nframes, scdiff, mindiff);
00216     delete []scdiffsort;
00217     if (debugLevel >= 2)
00218         frameAnalyzerReportMapms(&changeMap, fps, "SC frame");
00219 
00220     return 0;
00221 }
00222 
00223 int
00224 SceneChangeDetector::reportTime(void) const
00225 {
00226     return histogramAnalyzer->reportTime();
00227 }
00228 
00229 /* vim: set expandtab tabstop=4 shiftwidth=4: */
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends