Index: programs/mythcommflag/ClassicLogoDetector.h =================================================================== --- programs/mythcommflag/ClassicLogoDetector.h (revision 0) +++ programs/mythcommflag/ClassicLogoDetector.h (revision 0) @@ -0,0 +1,60 @@ +#ifndef _CLASSICLOGOGEDETECTOR_H_ +#define _CLASSICLOGOGEDETECTOR_H_ + +#include "LogoDetectorBase.h" + +typedef struct edgemaskentry EdgeMaskEntry; +typedef struct VideoFrame_ VideoFrame; +class ClassicCommDetector; + +class ClassicLogoDetector : public LogoDetectorBase +{ +public: + ClassicLogoDetector(ClassicCommDetector* commDetector,unsigned int width, unsigned int height, unsigned int commdetectborder, unsigned int xspacing, unsigned int yspacing); + ~ClassicLogoDetector(); + + bool searchForLogo(NuppelVideoPlayer* nvp); + bool doesThisFrameContainTheFoundLogo(unsigned char* frame); + bool pixelInsideLogo(unsigned int x, unsigned int y); + + unsigned int getRequiredAvailableBufferForSearch(); + +private: + void SetLogoMaskArea(); + void SetLogoMask(unsigned char *mask); + void DumpLogo(bool fromCurrentFrame,unsigned char* framePtr); + void DetectEdges(VideoFrame *frame, EdgeMaskEntry *edges, int edgeDiff); + + ClassicCommDetector* commDetector; + unsigned int frameNumber; + bool previousFrameWasSceneChange; + unsigned int xspacing, yspacing; + unsigned int commDetectBorder; + + int commDetectLogoSamplesNeeded; + int commDetectLogoSampleSpacing; + int commDetectLogoSecondsNeeded; + double commDetectLogoGoodEdgeThreshold; + double commDetectLogoBadEdgeThreshold; + + EdgeMaskEntry *edgeMask; + + unsigned char *logoMaxValues; + unsigned char *logoMinValues; + unsigned char *logoFrame; + unsigned char *logoMask; + unsigned char *logoCheckMask; + unsigned char *tmpBuf; + + int logoEdgeDiff; + unsigned int logoFrameCount; + unsigned int logoMinX; + unsigned int logoMaxX; + unsigned int logoMinY; + unsigned int logoMaxY; + + bool logoInfoAvailable; +}; + + +#endif Index: programs/mythcommflag/mythcommflag.pro =================================================================== --- programs/mythcommflag/mythcommflag.pro (revision 9932) +++ programs/mythcommflag/mythcommflag.pro (working copy) @@ -10,5 +10,5 @@ QMAKE_CLEAN += $(TARGET) # Input -SOURCES += main.cpp ClassicCommDetector.cpp CommDetectorFactory.cpp CommDetectorBase.cpp -HEADERS += CommDetectorBase.h ClassicCommDetector.h SlotRelayer.h CustomEventRelayer.h CommDetectorFactory.h +SOURCES += main.cpp ClassicCommDetector.cpp CommDetectorFactory.cpp CommDetectorBase.cpp ClassicLogoDetector.cpp +HEADERS += CommDetectorBase.h ClassicCommDetector.h SlotRelayer.h CustomEventRelayer.h CommDetectorFactory.h ClassicLogoDetector.h LogoDetectorBase.h Index: programs/mythcommflag/ClassicCommDetector.cpp =================================================================== --- programs/mythcommflag/ClassicCommDetector.cpp (revision 9932) +++ programs/mythcommflag/ClassicCommDetector.cpp (working copy) @@ -4,6 +4,7 @@ #include "CommDetector.h" #include "ClassicCommDetector.h" #include "libmythtv/NuppelVideoPlayer.h" +#include "ClassicLogoDetector.h" #include "qstring.h" #include "libmyth/mythcontext.h" @@ -48,17 +49,10 @@ stopsAt(stopsAt_in), recordingStartedAt(recordingStartedAt_in), recordingStopsAt(recordingStopsAt_in), - framesProcessed(0),preRoll(0),postRoll(0) + framesProcessed(0),preRoll(0),postRoll(0), + logoDetector(0) { - edgeMask = NULL; - logoFrame = NULL; - logoMask = NULL; - logoCheckMask = NULL; - logoMaxValues = NULL; - logoMinValues = NULL; - tmpBuf = NULL; - stillRecording = recordingStopsAt > QDateTime::currentDateTime(); commDetectBorder = @@ -81,18 +75,7 @@ gContext->GetNumSetting("CommDetectMinShowLength", 65); commDetectMaxCommLength = gContext->GetNumSetting("CommDetectMaxCommLength", 125); - commDetectLogoSamplesNeeded = - gContext->GetNumSetting("CommDetectLogoSamplesNeeded", 240); - commDetectLogoSampleSpacing = - gContext->GetNumSetting("CommDetectLogoSampleSpacing", 2); - commDetectLogoSecondsNeeded = commDetectLogoSamplesNeeded * - commDetectLogoSampleSpacing; - commDetectLogoGoodEdgeThreshold = - gContext->GetSetting("CommDetectLogoGoodEdgeThreshold", "0.75") - .toDouble(); - commDetectLogoBadEdgeThreshold = - gContext->GetSetting("CommDetectLogoBadEdgeThreshold", "0.85") - .toDouble(); + skipAllBlanks = !!gContext->GetNumSetting("CommSkipAllBlanks", 1); commDetectBlankCanHaveLogo = !!gContext->GetNumSetting("CommDetectBlankCanHaveLogo", 1); @@ -184,35 +167,6 @@ framePtr = NULL; - if (edgeMask) - delete [] edgeMask; - edgeMask = new EdgeMaskEntry[width * height]; - - if (logoFrame) - delete [] logoFrame; - logoFrame = new unsigned char[width * height]; - - if (logoMask) - delete [] logoMask; - logoMask = new unsigned char[width * height]; - - if (logoCheckMask) - delete [] logoCheckMask; - logoCheckMask = new unsigned char[width * height]; - - if (logoMaxValues) - delete [] logoMaxValues; - logoMaxValues = new unsigned char[width * height]; - - if (logoMinValues) - delete [] logoMinValues; - logoMinValues = new unsigned char[width * height]; - - if (tmpBuf) - delete [] tmpBuf; - tmpBuf = new unsigned char[width * height]; - - logoFrameCount = 0; logoInfoAvailable = false; ClearAllMaps(); @@ -226,20 +180,7 @@ ClassicCommDetector::~ClassicCommDetector() { - if (edgeMask) - delete [] edgeMask; - if (logoFrame) - delete [] logoFrame; - if (logoMask) - delete [] logoMask; - if (logoCheckMask) - delete [] logoCheckMask; - if (logoMaxValues) - delete [] logoMaxValues; - if (logoMinValues) - delete [] logoMinValues; - if (tmpBuf) - delete [] tmpBuf; + if (logoDetector) delete logoDetector; } bool ClassicCommDetector::go() @@ -249,10 +190,17 @@ int requiredBuffer = 7; int requiredHeadStart = requiredBuffer; + if (nvp->OpenFile() < 0) + return false; + + Init(); + if (commDetectMethod & COMM_DETECT_LOGO) { + logoDetector = new ClassicLogoDetector(this,width,height,commDetectBorder,horizSpacing,vertSpacing); + requiredHeadStart += max(0,recordingStartedAt.secsTo(startedAt)); - requiredHeadStart += commDetectLogoSecondsNeeded; + requiredHeadStart += logoDetector->getRequiredAvailableBufferForSearch(); } emit statusUpdate("Building Detection Buffer"); @@ -272,11 +220,6 @@ if ((wereRecording) && (!stillRecording) && (secsSince < requiredHeadStart)) return false; - if (nvp->OpenFile() < 0) - return false; - - Init(); - aggressiveDetection = gContext->GetNumSetting("AggressiveCommDetect", 1); if (!nvp->InitVideo()) @@ -287,9 +230,10 @@ } nvp->SetCaptionsEnabled(false); + unsigned int length = static_cast(nvp->GetLength()); if ((commDetectMethod & COMM_DETECT_LOGO) && - ((nvp->GetLength() == 0) || - (nvp->GetLength() > commDetectLogoSecondsNeeded))) + ((length == 0) || + (length > logoDetector->getRequiredAvailableBufferForSearch()))) { emit statusUpdate("Searching for Logo"); @@ -299,7 +243,7 @@ cerr.flush(); } - SearchForLogo(); + logoInfoAvailable = logoDetector->searchForLogo(nvp); if (showProgress) { @@ -714,27 +658,30 @@ { pixel = framePtr[y * width + x]; - if ((commDetectMethod & COMM_DETECT_BLANKS) && - ((!commDetectBlankCanHaveLogo) || - (!logoInfoAvailable) || - ((logoInfoAvailable) && - ((y < logoMinY) || (y > logoMaxY) || - (x < logoMinX) || (x > logoMaxX))))) + if (commDetectMethod & COMM_DETECT_BLANKS) { - blankPixelsChecked++; - totBrightness += pixel; + bool checkPixel = false; + if (!commDetectBlankCanHaveLogo) checkPixel = true; + if (!logoInfoAvailable) checkPixel = true; + else if (!logoDetector->pixelInsideLogo(x,y)) checkPixel=true; - if (pixel < min) - min = pixel; + if (checkPixel) + { + blankPixelsChecked++; + totBrightness += pixel; + + if (pixel < min) + min = pixel; - if (pixel > max) - max = pixel; + if (pixel > max) + max = pixel; - if (pixel > rowMax[y]) - rowMax[y] = pixel; + if (pixel > rowMax[y]) + rowMax[y] = pixel; - if (pixel > colMax[x]) - colMax[x] = pixel; + if (pixel > colMax[x]) + colMax[x] = pixel; + } } if (commDetectMethod & COMM_DETECT_SCENE) @@ -854,7 +801,7 @@ if ((logoInfoAvailable) && (commDetectMethod & COMM_DETECT_LOGO)) { - stationLogoPresent = CheckEdgeLogo(); + stationLogoPresent = logoDetector->doesThisFrameContainTheFoundLogo(framePtr); } #if 0 @@ -2142,172 +2089,6 @@ VERBOSE(VB_COMMFLAG, "---------------------------------------------------"); } -void ClassicCommDetector::DumpLogo(bool fromCurrentFrame) -{ - char scrPixels[] = " .oxX"; - - if (!logoInfoAvailable) - return; - - cerr << "\nLogo Data "; - if (fromCurrentFrame) - cerr << "from current frame\n"; - - cerr << "\n "; - - for(int x = logoMinX - 2; x <= (logoMaxX + 2); x++) - cerr << (x % 10); - cerr << "\n"; - - for(int y = logoMinY - 2; y <= (logoMaxY + 2); y++) - { - cerr << QString::number(y).rightJustify(3, ' ') << ": "; - for(int x = logoMinX - 2; x <= (logoMaxX + 2); x++) - { - if (fromCurrentFrame) - { - cerr << scrPixels[framePtr[y * width + x] / 50]; - } - else - { - switch (logoMask[y * width + x]) - { - case 0: - case 2: cerr << " "; - break; - case 1: cerr << "*"; - break; - case 3: cerr << "."; - break; - } - } - } - cerr << "\n"; - } - cerr.flush(); -} - -void ClassicCommDetector::SetLogoMaskArea() -{ - VERBOSE(VB_COMMFLAG, "SetLogoMaskArea()"); - - logoMinX = width - 1; - logoMaxX = 0; - logoMinY = height - 1; - logoMaxY = 0; - - for (int y = 0; y < height; y++) - { - for (int x = 0; x < width; x++) - { - if (edgeMask[y * width + x].isedge) - { - if (x < logoMinX) - logoMinX = x; - if (y < logoMinY) - logoMinY = y; - if (x > logoMaxX) - logoMaxX = x; - if (y > logoMaxY) - logoMaxY = y; - } - } - } - - logoMinX -= 5; - logoMaxX += 5; - logoMinY -= 5; - logoMaxY += 5; - - if (logoMinX < 4) - logoMinX = 4; - if (logoMaxX > (width-5)) - logoMaxX = (width-5); - if (logoMinY < 4) - logoMinY = 4; - if (logoMaxY > (height-5)) - logoMaxY = (height-5); -} - -void ClassicCommDetector::SetLogoMask(unsigned char *mask) -{ - int pixels = 0; - - memcpy(logoMask, mask, width * height); - - SetLogoMaskArea(); - - for(int y = logoMinY; y <= logoMaxY; y++) - for(int x = logoMinX; x <= logoMaxX; x++) - if (!logoMask[y * width + x] == 1) - pixels++; - - if (pixels < 30) - { - detectStationLogo = false; - return; - } - - // set the pixels around our logo - for(int y = (logoMinY - 1); y <= (logoMaxY + 1); y++) - { - for(int x = (logoMinX - 1); x <= (logoMaxX + 1); x++) - { - if (!logoMask[y * width + x]) - { - for (int y2 = y - 1; y2 <= (y + 1); y2++) - { - for (int x2 = x - 1; x2 <= (x + 1); x2++) - { - if ((logoMask[y2 * width + x2] == 1) && - (!logoMask[y * width + x])) - { - logoMask[y * width + x] = 2; - x2 = x + 2; - y2 = y + 2; - - logoCheckMask[y2 * width + x2] = 1; - logoCheckMask[y * width + x] = 1; - } - } - } - } - } - } - - for(int y = (logoMinY - 2); y <= (logoMaxY + 2); y++) - { - for(int x = (logoMinX - 2); x <= (logoMaxX + 2); x++) - { - if (!logoMask[y * width + x]) - { - for (int y2 = y - 1; y2 <= (y + 1); y2++) - { - for (int x2 = x - 1; x2 <= (x + 1); x2++) - { - if ((logoMask[y2 * width + x2] == 2) && - (!logoMask[y * width + x])) - { - logoMask[y * width + x] = 3; - x2 = x + 2; - y2 = y + 2; - - logoCheckMask[y * width + x] = 1; - } - } - } - } - } - } - -#ifdef SHOW_DEBUG_WIN - DumpLogo(true); -#endif - - logoFrameCount = 0; - logoInfoAvailable = true; -} - void ClassicCommDetector::CondenseMarkMap(QMap&map, int spacing, int length) { @@ -2412,258 +2193,7 @@ /* ideas for this method ported back from comskip.c mods by Jere Jones * which are partially mods based on Myth's original commercial skip * code written by Chris Pinkham. */ -bool ClassicCommDetector::CheckEdgeLogo(void) -{ - int radius = 2; - int x, y; - int pos1, pos2, pos3; - int pixel; - int goodEdges = 0; - int badEdges = 0; - int testEdges = 0; - int testNotEdges = 0; - - for (y = logoMinY; y <= logoMaxY; y++ ) - { - for (x = logoMinX; x <= logoMaxX; x++ ) - { - pos1 = y * width + x; - pos2 = (y - radius) * width + x; - pos3 = (y + radius) * width + x; - - pixel = framePtr[pos1]; - - if (edgeMask[pos1].horiz) - { - if ((abs(framePtr[pos1 - radius] - pixel) >= logoEdgeDiff) || - (abs(framePtr[pos1 + radius] - pixel) >= logoEdgeDiff)) - goodEdges++; - testEdges++; - } - else - { - if ((abs(framePtr[pos1 - radius] - pixel) >= logoEdgeDiff) || - (abs(framePtr[pos1 + radius] - pixel) >= logoEdgeDiff)) - badEdges++; - testNotEdges++; - } - - if (edgeMask[pos1].vert) - { - if ((abs(framePtr[pos2] - pixel) >= logoEdgeDiff) || - (abs(framePtr[pos3] - pixel) >= logoEdgeDiff)) - goodEdges++; - testEdges++; - } - else - { - if ((abs(framePtr[pos2] - pixel) >= logoEdgeDiff) || - (abs(framePtr[pos3] - pixel) >= logoEdgeDiff)) - badEdges++; - testNotEdges++; - } - } - } - - double goodEdgeRatio = (double)goodEdges / (double)testEdges; - double badEdgeRatio = (double)badEdges / (double)testNotEdges; - - if ((goodEdgeRatio > commDetectLogoGoodEdgeThreshold) && - (badEdgeRatio < commDetectLogoBadEdgeThreshold)) - return true; - else - return false; -} - - -void ClassicCommDetector::SearchForLogo() -{ - int seekIncrement = (int)(commDetectLogoSampleSpacing * fps); - long long seekFrame; - int loops; - int maxLoops = commDetectLogoSamplesNeeded; - EdgeMaskEntry *edgeCounts; - int pos, i, x, y, dx, dy; - int edgeDiffs[] = {5, 7, 10, 15, 20, 30, 40, 50, 60, 0 }; - - - VERBOSE(VB_COMMFLAG, "Searching for Station Logo"); - - logoInfoAvailable = false; - - edgeCounts = new EdgeMaskEntry[width * height]; - - for (i = 0; edgeDiffs[i] != 0 && !logoInfoAvailable; i++) - { - int pixelsInMask = 0; - - VERBOSE(VB_COMMFLAG, QString("Trying with edgeDiff == %1") - .arg(edgeDiffs[i])); - - memset(edgeCounts, 0, sizeof(EdgeMaskEntry) * width * height); - memset(edgeMask, 0, sizeof(EdgeMaskEntry) * width * height); - - nvp->DiscardVideoFrame(nvp->GetRawVideoFrame(0)); - - loops = 0; - seekFrame = preRoll + seekIncrement; - - while(loops < maxLoops && !nvp->GetEof()) - { - VideoFrame* vf = nvp->GetRawVideoFrame(seekFrame); - - if ((loops % 50) == 0) - emit breathe(); - - if (m_bStop) - { - nvp->DiscardVideoFrame(vf); - return; - } - - if (!fullSpeed) - usleep(10000); - - DetectEdges(vf, edgeCounts, edgeDiffs[i]); - - seekFrame += seekIncrement; - loops++; - - nvp->DiscardVideoFrame(vf); - } - - VERBOSE(VB_COMMFLAG, "Analyzing edge data"); - -#ifdef SHOW_DEBUG_WIN - unsigned char *fakeFrame; - fakeFrame = new unsigned char[width * height * 3 / 2]; - memset(fakeFrame, 0, width * height * 3 / 2); -#endif - - for (y = 0; y < height; y++) - { - if ((y > (height/4)) && (y < (height * 3 / 4))) - continue; - - for (x = 0; x < width; x++) - { - if ((x > (width/4)) && (x < (width * 3 / 4))) - continue; - - pos = y * width + x; - - if (edgeCounts[pos].isedge > (maxLoops * 0.66)) - { - edgeMask[pos].isedge = 1; - pixelsInMask++; -#ifdef SHOW_DEBUG_WIN - fakeFrame[pos] = 0xff; -#endif - - } - - if (edgeCounts[pos].horiz > (maxLoops * 0.66)) - edgeMask[pos].horiz = 1; - - if (edgeCounts[pos].vert > (maxLoops * 0.66)) - edgeMask[pos].vert = 1; - - if (edgeCounts[pos].ldiag > (maxLoops * 0.66)) - edgeMask[pos].ldiag = 1; - - if (edgeCounts[pos].rdiag > (maxLoops * 0.66)) - edgeMask[pos].rdiag = 1; - } - } - - SetLogoMaskArea(); - - for (y = logoMinY; y < logoMaxY; y++) - { - for (x = logoMinX; x < logoMaxX; x++) - { - int neighbors = 0; - - if (!edgeMask[y * width + x].isedge) - continue; - - for (dy = y - 2; dy <= (y + 2); dy++ ) - { - for (dx = x - 2; dx <= (x + 2); dx++ ) - { - if (edgeMask[dy * width + dx].isedge) - neighbors++; - } - } - - if (neighbors < 5) - edgeMask[y * width + x].isedge = 0; - } - } - - SetLogoMaskArea(); - - VERBOSE(VB_COMMFLAG, QString("Testing Logo area: topleft " - "(%1,%2), bottomright (%3,%4)") - .arg(logoMinX).arg(logoMinY) - .arg(logoMaxX).arg(logoMaxY)); - -#ifdef SHOW_DEBUG_WIN - for (x = logoMinX; x < logoMaxX; x++) - { - pos = logoMinY * width + x; - fakeFrame[pos] = 0x7f; - pos = logoMaxY * width + x; - fakeFrame[pos] = 0x7f; - } - for (y = logoMinY; y < logoMaxY; y++) - { - pos = y * width + logoMinX; - fakeFrame[pos] = 0x7f; - pos = y * width + logoMaxX; - fakeFrame[pos] = 0x7f; - } - - comm_debug_show(fakeFrame); - delete [] fakeFrame; - - cerr << "Hit ENTER to continue" << endl; - getchar(); -#endif - - if (((logoMaxX - logoMinX) < (width / 4)) && - ((logoMaxY - logoMinY) < (height / 4)) && - (pixelsInMask > 50)) - { - logoInfoAvailable = true; - logoEdgeDiff = edgeDiffs[i]; - - VERBOSE(VB_COMMFLAG, QString("Using Logo area: topleft " - "(%1,%2), bottomright (%3,%4)") - .arg(logoMinX).arg(logoMinY) - .arg(logoMaxX).arg(logoMaxY)); - } - else - { - VERBOSE(VB_COMMFLAG, QString("Rejecting Logo area: topleft " - "(%1,%2), bottomright (%3,%4), " - "pixelsInMask (%5). " - "Not within specified limits.") - .arg(logoMinX).arg(logoMinY) - .arg(logoMaxX).arg(logoMaxY) - .arg(pixelsInMask)); - } - } - - delete [] edgeCounts; - - if (!logoInfoAvailable) - VERBOSE(VB_COMMFLAG, "No suitable logo area found."); - - nvp->DiscardVideoFrame(nvp->GetRawVideoFrame(0)); -} - void ClassicCommDetector::CleanupFrameInfo(void) { VERBOSE(VB_COMMFLAG, "CommDetect::CleanupFrameInfo()"); @@ -2753,63 +2283,6 @@ } } -void ClassicCommDetector::DetectEdges(VideoFrame *frame, EdgeMaskEntry *edges, - int edgeDiff) -{ - int r = 2; - unsigned char *buf = frame->buf; - unsigned char p; - int pos, x, y; - - for (y = commDetectBorder + r; y < (height - commDetectBorder - r); y++) - { - if ((y > (height/4)) && (y < (height * 3 / 4))) - continue; - - for (x = commDetectBorder + r; x < (width - commDetectBorder - r); x++) - { - int edgeCount = 0; - - if ((x > (width/4)) && (x < (width * 3 / 4))) - continue; - - pos = y * width + x; - p = buf[pos]; - - if (( abs(buf[y * width + (x - r)] - p) >= edgeDiff) || - ( abs(buf[y * width + (x + r)] - p) >= edgeDiff)) - { - edges[pos].horiz++; - edgeCount++; - } - - if (( abs(buf[(y - r) * width + x] - p) >= edgeDiff) || - ( abs(buf[(y + r) * width + x] - p) >= edgeDiff)) - { - edges[pos].vert++; - edgeCount++; - } - - if (( abs(buf[(y - r) * width + (x - r)] - p) >= edgeDiff) || - ( abs(buf[(y + r) * width + (x + r)] - p) >= edgeDiff)) - { - edges[pos].ldiag++; - edgeCount++; - } - - if (( abs(buf[(y - r) * width + (x + r)] - p) >= edgeDiff) || - ( abs(buf[(y + r) * width + (x - r)] - p) >= edgeDiff)) - { - edges[pos].rdiag++; - edgeCount++; - } - - if (edgeCount >= 3) - edges[pos].isedge++; - } - } -} - void ClassicCommDetector::GetLogoCommBreakMap(QMap &map) { VERBOSE(VB_COMMFLAG, "CommDetect::GetLogoCommBreakMap()"); @@ -2841,5 +2314,9 @@ } +void ClassicCommDetector::logoDetectorBreathe() +{ + emit breathe(); +} /* vim: set expandtab tabstop=4 shiftwidth=4: */ Index: programs/mythcommflag/LogoDetectorBase.h =================================================================== --- programs/mythcommflag/LogoDetectorBase.h (revision 0) +++ programs/mythcommflag/LogoDetectorBase.h (revision 0) @@ -0,0 +1,30 @@ +#ifndef _LOGODETECTORBASE_H_ +#define _LOGODETECTORBASE_H_ + +#include "qobject.h" + +class NuppelVideoPlayer; + +class LogoDetectorBase : public QObject +{ + Q_OBJECT + +public: + LogoDetectorBase(unsigned int w,unsigned int h) : foundLogo(false), width(w),height(h) {}; + ~LogoDetectorBase() {}; + + virtual bool searchForLogo(NuppelVideoPlayer* nvp) = 0; + virtual bool doesThisFrameContainTheFoundLogo(unsigned char* frame) = 0; + virtual bool pixelInsideLogo(unsigned int x, unsigned int y) = 0; + virtual unsigned int getRequiredAvailableBufferForSearch() = 0; + +signals: + void haveNewInformation(unsigned int framenum, bool haslogo, float debugValue = 0.0); + +protected: + bool foundLogo; + unsigned int width, height; +}; + + +#endif Index: programs/mythcommflag/ClassicCommDetector.h =================================================================== --- programs/mythcommflag/ClassicCommDetector.h (revision 9932) +++ programs/mythcommflag/ClassicCommDetector.h (working copy) @@ -6,6 +6,7 @@ #include "qdatetime.h" class NuppelVideoPlayer; +class LogoDetectorBase; class ClassicCommDetector : public CommDetectorBase { @@ -22,18 +23,11 @@ void recordingFinished(long long totalFileSize); void requestCommBreakMapUpdate(void); + void logoDetectorBreathe(); + + friend class ClassicLogoDetector; private: - typedef struct edgemaskentry - { - int isedge; - int horiz; - int vert; - int rdiag; - int ldiag; - } - EdgeMaskEntry; - typedef struct frameinfo { int minBrightness; @@ -81,15 +75,9 @@ void MergeBlankCommList(void); bool FrameIsInBreakMap(long long f, QMap &breakMap); void DumpMap(QMap &map); - void DumpLogo(bool fromCurrentFrame); - void SetLogoMaskArea(); - void SetLogoMask(unsigned char *mask); void CondenseMarkMap(QMap&map, int spacing, int length); void ConvertShowMapToCommMap(QMap&map); - bool CheckEdgeLogo(void); - void SearchForLogo(); void CleanupFrameInfo(void); - void DetectEdges(VideoFrame *frame, EdgeMaskEntry *edges, int edgeDiff); void GetLogoCommBreakMap(QMap &map); int commDetectMethod; @@ -114,12 +102,7 @@ int commDetectMinCommBreakLength; int commDetectMinShowLength; int commDetectMaxCommLength; - int commDetectLogoSamplesNeeded; - int commDetectLogoSampleSpacing; - int commDetectLogoSecondsNeeded; bool commDetectBlankCanHaveLogo; - double commDetectLogoGoodEdgeThreshold; - double commDetectLogoBadEdgeThreshold; bool verboseDebugging; @@ -146,24 +129,11 @@ bool detectSceneChanges; bool detectStationLogo; - bool skipAllBlanks; - - EdgeMaskEntry *edgeMask; - - unsigned char *logoMaxValues; - unsigned char *logoMinValues; - unsigned char *logoFrame; - unsigned char *logoMask; - unsigned char *logoCheckMask; - unsigned char *tmpBuf; bool logoInfoAvailable; - int logoEdgeDiff; - int logoFrameCount; - int logoMinX; - int logoMaxX; - int logoMinY; - int logoMaxY; + LogoDetectorBase* logoDetector; + bool skipAllBlanks; + unsigned char *framePtr; QMap frameInfo; Index: programs/mythcommflag/ClassicLogoDetector.cpp =================================================================== --- programs/mythcommflag/ClassicLogoDetector.cpp (revision 0) +++ programs/mythcommflag/ClassicLogoDetector.cpp (revision 0) @@ -0,0 +1,553 @@ +#include "ClassicLogoDetector.h" +#include "ClassicCommDetector.h" + +#include "libmythtv/NuppelVideoPlayer.h" +#include "libmyth/mythcontext.h" //To be able to use VERBOSE() + +typedef struct edgemaskentry +{ + int isedge; + int horiz; + int vert; + int rdiag; + int ldiag; +} +EdgeMaskEntry; + + +ClassicLogoDetector::ClassicLogoDetector(ClassicCommDetector* commdetector,unsigned int w, unsigned int h, unsigned int commdetectborder_in, unsigned int xspacing_in, unsigned int yspacing_in) : + LogoDetectorBase(w,h), + commDetector(commdetector), + frameNumber(0), + previousFrameWasSceneChange(false), + xspacing(xspacing_in), + yspacing(yspacing_in), + commDetectBorder(commdetectborder_in) +{ + commDetectLogoSamplesNeeded = + gContext->GetNumSetting("CommDetectLogoSamplesNeeded", 240); + commDetectLogoSampleSpacing = + gContext->GetNumSetting("CommDetectLogoSampleSpacing", 2); + commDetectLogoGoodEdgeThreshold = + gContext->GetSetting("CommDetectLogoGoodEdgeThreshold", "0.75") + .toDouble(); + commDetectLogoBadEdgeThreshold = + gContext->GetSetting("CommDetectLogoBadEdgeThreshold", "0.85") + .toDouble(); + commDetectLogoSecondsNeeded = commDetectLogoSamplesNeeded * + commDetectLogoSampleSpacing; + + + + edgeMask = new EdgeMaskEntry[width * height]; + logoFrame = new unsigned char[width * height]; + logoMask = new unsigned char[width * height]; + logoCheckMask = new unsigned char[width * height]; + logoMaxValues = new unsigned char[width * height]; + logoMinValues = new unsigned char[width * height]; + tmpBuf = new unsigned char[width * height]; + + logoFrameCount = 0; +} + +unsigned int ClassicLogoDetector::getRequiredAvailableBufferForSearch() +{ + return commDetectLogoSecondsNeeded; +} + +ClassicLogoDetector::~ClassicLogoDetector() +{ + commDetector = 0; + if (edgeMask) + delete [] edgeMask; + if (logoFrame) + delete [] logoFrame; + if (logoMask) + delete [] logoMask; + if (logoCheckMask) + delete [] logoCheckMask; + if (logoMaxValues) + delete [] logoMaxValues; + if (logoMinValues) + delete [] logoMinValues; + if (tmpBuf) + delete [] tmpBuf; +} + +bool ClassicLogoDetector::searchForLogo(NuppelVideoPlayer* nvp) +{ + int seekIncrement = (int)(commDetectLogoSampleSpacing * nvp->GetFrameRate()); + long long seekFrame; + int loops; + int maxLoops = commDetectLogoSamplesNeeded; + EdgeMaskEntry *edgeCounts; + unsigned int pos, i, x, y, dx, dy; + int edgeDiffs[] = {5, 7, 10, 15, 20, 30, 40, 50, 60, 0 }; + + + VERBOSE(VB_COMMFLAG, "Searching for Station Logo"); + + logoInfoAvailable = false; + + edgeCounts = new EdgeMaskEntry[width * height]; + + for (i = 0; edgeDiffs[i] != 0 && !logoInfoAvailable; i++) + { + int pixelsInMask = 0; + + VERBOSE(VB_COMMFLAG, QString("Trying with edgeDiff == %1") + .arg(edgeDiffs[i])); + + memset(edgeCounts, 0, sizeof(EdgeMaskEntry) * width * height); + memset(edgeMask, 0, sizeof(EdgeMaskEntry) * width * height); + + nvp->DiscardVideoFrame(nvp->GetRawVideoFrame(0)); + + loops = 0; + seekFrame = commDetector->preRoll + seekIncrement; + while(loops < maxLoops && !nvp->GetEof()) + { + VideoFrame* vf = nvp->GetRawVideoFrame(seekFrame); + + if ((loops % 50) == 0) + commDetector->logoDetectorBreathe(); + + if (commDetector->m_bStop) + { + nvp->DiscardVideoFrame(vf); + return false; + } + + if (!commDetector->fullSpeed) + usleep(10000); + + DetectEdges(vf, edgeCounts, edgeDiffs[i]); + + seekFrame += seekIncrement; + loops++; + + nvp->DiscardVideoFrame(vf); + } + + VERBOSE(VB_COMMFLAG, "Analyzing edge data"); + +#ifdef SHOW_DEBUG_WIN + unsigned char *fakeFrame; + fakeFrame = new unsigned char[width * height * 3 / 2]; + memset(fakeFrame, 0, width * height * 3 / 2); +#endif + + for (y = 0; y < height; y++) + { + if ((y > (height/4)) && (y < (height * 3 / 4))) + continue; + + for (x = 0; x < width; x++) + { + if ((x > (width/4)) && (x < (width * 3 / 4))) + continue; + + pos = y * width + x; + + if (edgeCounts[pos].isedge > (maxLoops * 0.66)) + { + edgeMask[pos].isedge = 1; + pixelsInMask++; +#ifdef SHOW_DEBUG_WIN + fakeFrame[pos] = 0xff; +#endif + + } + + if (edgeCounts[pos].horiz > (maxLoops * 0.66)) + edgeMask[pos].horiz = 1; + + if (edgeCounts[pos].vert > (maxLoops * 0.66)) + edgeMask[pos].vert = 1; + + if (edgeCounts[pos].ldiag > (maxLoops * 0.66)) + edgeMask[pos].ldiag = 1; + if (edgeCounts[pos].rdiag > (maxLoops * 0.66)) + edgeMask[pos].rdiag = 1; + } + } + + SetLogoMaskArea(); + + for (y = logoMinY; y < logoMaxY; y++) + { + for (x = logoMinX; x < logoMaxX; x++) + { + int neighbors = 0; + + if (!edgeMask[y * width + x].isedge) + continue; + + for (dy = y - 2; dy <= (y + 2); dy++ ) + { + for (dx = x - 2; dx <= (x + 2); dx++ ) + { + if (edgeMask[dy * width + dx].isedge) + neighbors++; + } + } + + if (neighbors < 5) + edgeMask[y * width + x].isedge = 0; + } + } + + SetLogoMaskArea(); + VERBOSE(VB_COMMFLAG, QString("Testing Logo area: topleft " + "(%1,%2), bottomright (%3,%4)") + .arg(logoMinX).arg(logoMinY) + .arg(logoMaxX).arg(logoMaxY)); + +#ifdef SHOW_DEBUG_WIN + for (x = logoMinX; x < logoMaxX; x++) + { + pos = logoMinY * width + x; + fakeFrame[pos] = 0x7f; + pos = logoMaxY * width + x; + fakeFrame[pos] = 0x7f; + } + for (y = logoMinY; y < logoMaxY; y++) + { + pos = y * width + logoMinX; + fakeFrame[pos] = 0x7f; + pos = y * width + logoMaxX; + fakeFrame[pos] = 0x7f; + } + + comm_debug_show(fakeFrame); + delete [] fakeFrame; + + cerr << "Hit ENTER to continue" << endl; + getchar(); +#endif + if (((logoMaxX - logoMinX) < (width / 4)) && + ((logoMaxY - logoMinY) < (height / 4)) && + (pixelsInMask > 50)) + { + logoInfoAvailable = true; + logoEdgeDiff = edgeDiffs[i]; + + VERBOSE(VB_COMMFLAG, QString("Using Logo area: topleft " + "(%1,%2), bottomright (%3,%4)") + .arg(logoMinX).arg(logoMinY) + .arg(logoMaxX).arg(logoMaxY)); + } + else + { + VERBOSE(VB_COMMFLAG, QString("Rejecting Logo area: topleft " + "(%1,%2), bottomright (%3,%4), " + "pixelsInMask (%5). " + "Not within specified limits.") + .arg(logoMinX).arg(logoMinY) + .arg(logoMaxX).arg(logoMaxY) + .arg(pixelsInMask)); + } + } + + delete [] edgeCounts; + + if (!logoInfoAvailable) + VERBOSE(VB_COMMFLAG, "No suitable logo area found."); + + nvp->DiscardVideoFrame(nvp->GetRawVideoFrame(0)); + return logoInfoAvailable; +} + + +void ClassicLogoDetector::SetLogoMaskArea() +{ + VERBOSE(VB_COMMFLAG, "SetLogoMaskArea()"); + + logoMinX = width - 1; + logoMaxX = 0; + logoMinY = height - 1; + logoMaxY = 0; + + for (unsigned int y = 0; y < height; y++) + { + for (unsigned int x = 0; x < width; x++) + { + if (edgeMask[y * width + x].isedge) + { + if (x < logoMinX) + logoMinX = x; + if (y < logoMinY) + logoMinY = y; + if (x > logoMaxX) + logoMaxX = x; + if (y > logoMaxY) + logoMaxY = y; + } + } + } + + logoMinX -= 5; + logoMaxX += 5; + logoMinY -= 5; + logoMaxY += 5; + + if (logoMinX < 4) + logoMinX = 4; + if (logoMaxX > (width-5)) + logoMaxX = (width-5); + if (logoMinY < 4) + logoMinY = 4; + if (logoMaxY > (height-5)) + logoMaxY = (height-5); +} + +void ClassicLogoDetector::SetLogoMask(unsigned char *mask) +{ + int pixels = 0; + + memcpy(logoMask, mask, width * height); + + SetLogoMaskArea(); + + for(unsigned int y = logoMinY; y <= logoMaxY; y++) + for(unsigned int x = logoMinX; x <= logoMaxX; x++) + if (!logoMask[y * width + x] == 1) + pixels++; + + if (pixels < 30) + return; + + // set the pixels around our logo + for(unsigned int y = (logoMinY - 1); y <= (logoMaxY + 1); y++) + { + for(unsigned int x = (logoMinX - 1); x <= (logoMaxX + 1); x++) + { + if (!logoMask[y * width + x]) + { + for (unsigned int y2 = y - 1; y2 <= (y + 1); y2++) + { + for (unsigned int x2 = x - 1; x2 <= (x + 1); x2++) + { + if ((logoMask[y2 * width + x2] == 1) && + (!logoMask[y * width + x])) + { + logoMask[y * width + x] = 2; + x2 = x + 2; + y2 = y + 2; + + logoCheckMask[y2 * width + x2] = 1; + logoCheckMask[y * width + x] = 1; + } + } + } + } + } + } + for(unsigned int y = (logoMinY - 2); y <= (logoMaxY + 2); y++) + { + for(unsigned int x = (logoMinX - 2); x <= (logoMaxX + 2); x++) + { + if (!logoMask[y * width + x]) + { + for (unsigned int y2 = y - 1; y2 <= (y + 1); y2++) + { + for (unsigned int x2 = x - 1; x2 <= (x + 1); x2++) + { + if ((logoMask[y2 * width + x2] == 2) && + (!logoMask[y * width + x])) + { + logoMask[y * width + x] = 3; + x2 = x + 2; + y2 = y + 2; + + logoCheckMask[y * width + x] = 1; + } + } + } + } + } + } + +#ifdef SHOW_DEBUG_WIN + DumpLogo(true,framePtr); +#endif + + logoFrameCount = 0; + logoInfoAvailable = true; +} + + +void ClassicLogoDetector::DumpLogo(bool fromCurrentFrame, unsigned char* framePtr) +{ + char scrPixels[] = " .oxX"; + + if (!logoInfoAvailable) + return; + + cerr << "\nLogo Data "; + if (fromCurrentFrame) + cerr << "from current frame\n"; + + cerr << "\n "; + + for(unsigned int x = logoMinX - 2; x <= (logoMaxX + 2); x++) + cerr << (x % 10); + cerr << "\n"; + + for(unsigned int y = logoMinY - 2; y <= (logoMaxY + 2); y++) + { + cerr << QString::number(y).rightJustify(3, ' ') << ": "; + for(unsigned int x = logoMinX - 2; x <= (logoMaxX + 2); x++) + { + if (fromCurrentFrame) + { + cerr << scrPixels[framePtr[y * width + x] / 50]; + } + else + { + switch (logoMask[y * width + x]) + { + case 0: + case 2: cerr << " "; + break; + case 1: cerr << "*"; + break; + case 3: cerr << "."; + break; + } + } + } + cerr << "\n"; + } + cerr.flush(); +} + + +/* ideas for this method ported back from comskip.c mods by Jere Jones + * which are partially mods based on Myth's original commercial skip + * code written by Chris Pinkham. */ +bool ClassicLogoDetector::doesThisFrameContainTheFoundLogo(unsigned char* framePtr) +{ + int radius = 2; + unsigned int x, y; + int pos1, pos2, pos3; + int pixel; + int goodEdges = 0; + int badEdges = 0; + int testEdges = 0; + int testNotEdges = 0; + + for (y = logoMinY; y <= logoMaxY; y++ ) + { + for (x = logoMinX; x <= logoMaxX; x++ ) + { + pos1 = y * width + x; + pos2 = (y - radius) * width + x; + pos3 = (y + radius) * width + x; + + pixel = framePtr[pos1]; + + if (edgeMask[pos1].horiz) + { + if ((abs(framePtr[pos1 - radius] - pixel) >= logoEdgeDiff) || + (abs(framePtr[pos1 + radius] - pixel) >= logoEdgeDiff)) + goodEdges++; + testEdges++; + } + else + { + if ((abs(framePtr[pos1 - radius] - pixel) >= logoEdgeDiff) || + (abs(framePtr[pos1 + radius] - pixel) >= logoEdgeDiff)) + badEdges++; + testNotEdges++; + } + + if (edgeMask[pos1].vert) + { + if ((abs(framePtr[pos2] - pixel) >= logoEdgeDiff) || + (abs(framePtr[pos3] - pixel) >= logoEdgeDiff)) + goodEdges++; + testEdges++; + } + else + { + if ((abs(framePtr[pos2] - pixel) >= logoEdgeDiff) || + (abs(framePtr[pos3] - pixel) >= logoEdgeDiff)) + badEdges++; + testNotEdges++; + } + } + } + + frameNumber++; + double goodEdgeRatio = (double)goodEdges / (double)testEdges; + double badEdgeRatio = (double)badEdges / (double)testNotEdges; + if ((goodEdgeRatio > commDetectLogoGoodEdgeThreshold) && + (badEdgeRatio < commDetectLogoBadEdgeThreshold)) + return true; + else + return false; +} + +bool ClassicLogoDetector::pixelInsideLogo(unsigned int x, unsigned int y) +{ + if (!logoInfoAvailable) return false; + return ((x > logoMinX) && (x < logoMaxX) && (y > logoMinY) && (y < logoMaxY)); +} + +void ClassicLogoDetector::DetectEdges(VideoFrame *frame, EdgeMaskEntry *edges, + int edgeDiff) +{ + int r = 2; + unsigned char *buf = frame->buf; + unsigned char p; + unsigned int pos, x, y; + + for (y = commDetectBorder + r; y < (height - commDetectBorder - r); y++) + { + if ((y > (height/4)) && (y < (height * 3 / 4))) + continue; + + for (x = commDetectBorder + r; x < (width - commDetectBorder - r); x++) + { + int edgeCount = 0; + + if ((x > (width/4)) && (x < (width * 3 / 4))) + continue; + + pos = y * width + x; + p = buf[pos]; + + if (( abs(buf[y * width + (x - r)] - p) >= edgeDiff) || + ( abs(buf[y * width + (x + r)] - p) >= edgeDiff)) + { + edges[pos].horiz++; + edgeCount++; + } + if (( abs(buf[(y - r) * width + x] - p) >= edgeDiff) || + ( abs(buf[(y + r) * width + x] - p) >= edgeDiff)) + { + edges[pos].vert++; + edgeCount++; + } + + if (( abs(buf[(y - r) * width + (x - r)] - p) >= edgeDiff) || + ( abs(buf[(y + r) * width + (x + r)] - p) >= edgeDiff)) + { + edges[pos].ldiag++; + edgeCount++; + } + + if (( abs(buf[(y - r) * width + (x + r)] - p) >= edgeDiff) || + ( abs(buf[(y + r) * width + (x - r)] - p) >= edgeDiff)) + { + edges[pos].rdiag++; + edgeCount++; + } + + if (edgeCount >= 3) + edges[pos].isedge++; + } + } +} +