MythTV  0.26-pre
ClassicLogoDetector.cpp
Go to the documentation of this file.
00001 // POSIX headers
00002 #include <unistd.h>
00003 
00004 // ANSI C headers
00005 #include <cstdlib>
00006 
00007 // MythTV headers
00008 #include "mythcorecontext.h"
00009 #include "mythplayer.h"
00010 
00011 // Commercial Flagging headers
00012 #include "ClassicLogoDetector.h"
00013 #include "ClassicCommDetector.h"
00014 
00015 typedef struct edgemaskentry
00016 {
00017     int isedge;
00018     int horiz;
00019     int vert;
00020     int rdiag;
00021     int ldiag;
00022 }
00023 EdgeMaskEntry;
00024 
00025 
00026 ClassicLogoDetector::ClassicLogoDetector(ClassicCommDetector* commdetector,
00027                                          unsigned int w, unsigned int h,
00028                                          unsigned int commdetectborder_in,
00029                                          unsigned int xspacing_in,
00030                                          unsigned int yspacing_in)
00031     : LogoDetectorBase(w,h),
00032       commDetector(commdetector),                       frameNumber(0),
00033       previousFrameWasSceneChange(false),
00034       xspacing(xspacing_in),                            yspacing(yspacing_in),
00035       commDetectBorder(commdetectborder_in),            edgeMask(new EdgeMaskEntry[width * height]),
00036       logoMaxValues(new unsigned char[width * height]), logoMinValues(new unsigned char[width * height]),
00037       logoFrame(new unsigned char[width * height]),     logoMask(new unsigned char[width * height]),
00038       logoCheckMask(new unsigned char[width * height]), tmpBuf(new unsigned char[width * height]),
00039       logoEdgeDiff(0),                                  logoFrameCount(0),
00040       logoMinX(0),                                      logoMaxX(0),
00041       logoMinY(0),                                      logoMaxY(0),
00042       logoInfoAvailable(false)
00043 {
00044     commDetectLogoSamplesNeeded =
00045         gCoreContext->GetNumSetting("CommDetectLogoSamplesNeeded", 240);
00046     commDetectLogoSampleSpacing =
00047         gCoreContext->GetNumSetting("CommDetectLogoSampleSpacing", 2);
00048     commDetectLogoSecondsNeeded = (int)(1.3 * commDetectLogoSamplesNeeded *
00049                                               commDetectLogoSampleSpacing);
00050     commDetectLogoGoodEdgeThreshold =
00051         gCoreContext->GetSetting("CommDetectLogoGoodEdgeThreshold", "0.75")
00052         .toDouble();
00053     commDetectLogoBadEdgeThreshold =
00054         gCoreContext->GetSetting("CommDetectLogoBadEdgeThreshold", "0.85")
00055         .toDouble();
00056 }
00057 
00058 unsigned int ClassicLogoDetector::getRequiredAvailableBufferForSearch()
00059 {
00060     return commDetectLogoSecondsNeeded;
00061 }
00062 
00063 void ClassicLogoDetector::deleteLater(void)
00064 {
00065     commDetector = 0;
00066     if (edgeMask)
00067         delete [] edgeMask;
00068     if (logoFrame)
00069         delete [] logoFrame;
00070     if (logoMask)
00071         delete [] logoMask;
00072     if (logoCheckMask)
00073         delete [] logoCheckMask;
00074     if (logoMaxValues)
00075         delete [] logoMaxValues;
00076     if (logoMinValues)
00077         delete [] logoMinValues;
00078     if (tmpBuf)
00079         delete [] tmpBuf;
00080 
00081     LogoDetectorBase::deleteLater();
00082 }
00083 
00084 bool ClassicLogoDetector::searchForLogo(MythPlayer* player)
00085 {
00086     int seekIncrement = 
00087         (int)(commDetectLogoSampleSpacing * player->GetFrameRate());
00088     long long seekFrame;
00089     int loops;
00090     int maxLoops = commDetectLogoSamplesNeeded;
00091     EdgeMaskEntry *edgeCounts;
00092     unsigned int pos, i, x, y, dx, dy;
00093     int edgeDiffs[] = {5, 7, 10, 15, 20, 30, 40, 50, 60, 0 };
00094 
00095 
00096     LOG(VB_COMMFLAG, LOG_INFO, "Searching for Station Logo");
00097 
00098     logoInfoAvailable = false;
00099 
00100     edgeCounts = new EdgeMaskEntry[width * height];
00101 
00102     for (i = 0; edgeDiffs[i] != 0 && !logoInfoAvailable; i++)
00103     {
00104         int pixelsInMask = 0;
00105 
00106         LOG(VB_COMMFLAG, LOG_INFO, QString("Trying with edgeDiff == %1")
00107                 .arg(edgeDiffs[i]));
00108 
00109         memset(edgeCounts, 0, sizeof(EdgeMaskEntry) * width * height);
00110         memset(edgeMask, 0, sizeof(EdgeMaskEntry) * width * height);
00111 
00112         player->DiscardVideoFrame(player->GetRawVideoFrame(0));
00113 
00114         loops = 0;
00115         seekFrame = commDetector->preRoll + seekIncrement;
00116         while(loops < maxLoops && !player->GetEof())
00117         {
00118             VideoFrame* vf = player->GetRawVideoFrame(seekFrame);
00119 
00120             if ((loops % 50) == 0)
00121                 commDetector->logoDetectorBreathe();
00122 
00123             if (commDetector->m_bStop)
00124             {
00125                 player->DiscardVideoFrame(vf);
00126                 delete[] edgeCounts;
00127                 return false;
00128             }
00129 
00130             if (!commDetector->fullSpeed)
00131                 usleep(10000);
00132 
00133             DetectEdges(vf, edgeCounts, edgeDiffs[i]);
00134 
00135             seekFrame += seekIncrement;
00136             loops++;
00137 
00138             player->DiscardVideoFrame(vf);
00139         }
00140 
00141         LOG(VB_COMMFLAG, LOG_INFO, "Analyzing edge data");
00142 
00143 #ifdef SHOW_DEBUG_WIN
00144         unsigned char *fakeFrame;
00145         fakeFrame = new unsigned char[width * height * 3 / 2];
00146         memset(fakeFrame, 0, width * height * 3 / 2);
00147 #endif
00148 
00149         for (y = 0; y < height; y++)
00150         {
00151             if ((y > (height/4)) && (y < (height * 3 / 4)))
00152                 continue;
00153 
00154             for (x = 0; x < width; x++)
00155             {
00156                 if ((x > (width/4)) && (x < (width * 3 / 4)))
00157                     continue;
00158 
00159                 pos = y * width + x;
00160 
00161                 if (edgeCounts[pos].isedge > (maxLoops * 0.66))
00162                 {
00163                     edgeMask[pos].isedge = 1;
00164                     pixelsInMask++;
00165 #ifdef SHOW_DEBUG_WIN
00166                     fakeFrame[pos] = 0xff;
00167 #endif
00168 
00169                 }
00170 
00171                 if (edgeCounts[pos].horiz > (maxLoops * 0.66))
00172                     edgeMask[pos].horiz = 1;
00173 
00174                 if (edgeCounts[pos].vert > (maxLoops * 0.66))
00175                     edgeMask[pos].vert = 1;
00176 
00177                 if (edgeCounts[pos].ldiag > (maxLoops * 0.66))
00178                     edgeMask[pos].ldiag = 1;
00179                 if (edgeCounts[pos].rdiag > (maxLoops * 0.66))
00180                     edgeMask[pos].rdiag = 1;
00181             }
00182         }
00183 
00184         SetLogoMaskArea();
00185 
00186         for (y = logoMinY; y < logoMaxY; y++)
00187         {
00188             for (x = logoMinX; x < logoMaxX; x++)
00189             {
00190                 int neighbors = 0;
00191 
00192                 if (!edgeMask[y * width + x].isedge)
00193                     continue;
00194 
00195                 for (dy = y - 2; dy <= (y + 2); dy++ )
00196                 {
00197                     for (dx = x - 2; dx <= (x + 2); dx++ )
00198                     {
00199                         if (edgeMask[dy * width + dx].isedge)
00200                             neighbors++;
00201                     }
00202                 }
00203 
00204                 if (neighbors < 5)
00205                     edgeMask[y * width + x].isedge = 0;
00206             }
00207         }
00208 
00209         SetLogoMaskArea();
00210         LOG(VB_COMMFLAG, LOG_INFO,
00211             QString("Testing Logo area: topleft (%1,%2), bottomright (%3,%4)")
00212                 .arg(logoMinX).arg(logoMinY)
00213                 .arg(logoMaxX).arg(logoMaxY));
00214 
00215 #ifdef SHOW_DEBUG_WIN
00216         for (x = logoMinX; x < logoMaxX; x++)
00217         {
00218             pos = logoMinY * width + x;
00219             fakeFrame[pos] = 0x7f;
00220             pos = logoMaxY * width + x;
00221             fakeFrame[pos] = 0x7f;
00222         }
00223         for (y = logoMinY; y < logoMaxY; y++)
00224         {
00225             pos = y * width + logoMinX;
00226             fakeFrame[pos] = 0x7f;
00227             pos = y * width + logoMaxX;
00228             fakeFrame[pos] = 0x7f;
00229         }
00230 
00231         comm_debug_show(fakeFrame);
00232         delete [] fakeFrame;
00233 
00234         cerr << "Hit ENTER to continue" << endl;
00235         getchar();
00236 #endif
00237         if (((logoMaxX - logoMinX) < (width / 4)) &&
00238             ((logoMaxY - logoMinY) < (height / 4)) &&
00239             (pixelsInMask > 50))
00240         {
00241             logoInfoAvailable = true;
00242             logoEdgeDiff = edgeDiffs[i];
00243 
00244             LOG(VB_COMMFLAG, LOG_INFO, 
00245                 QString("Using Logo area: topleft (%1,%2), "
00246                         "bottomright (%3,%4)")
00247                     .arg(logoMinX).arg(logoMinY)
00248                     .arg(logoMaxX).arg(logoMaxY));
00249         }
00250         else
00251         {
00252             LOG(VB_COMMFLAG, LOG_INFO, 
00253                 QString("Rejecting Logo area: topleft (%1,%2), "
00254                         "bottomright (%3,%4), pixelsInMask (%5). "
00255                         "Not within specified limits.")
00256                     .arg(logoMinX).arg(logoMinY)
00257                     .arg(logoMaxX).arg(logoMaxY)
00258                     .arg(pixelsInMask));
00259         }
00260     }
00261 
00262     delete [] edgeCounts;
00263 
00264     if (!logoInfoAvailable)
00265         LOG(VB_COMMFLAG, LOG_NOTICE, "No suitable logo area found.");
00266 
00267     player->DiscardVideoFrame(player->GetRawVideoFrame(0));
00268     return logoInfoAvailable;
00269 }
00270 
00271 
00272 void ClassicLogoDetector::SetLogoMaskArea()
00273 {
00274     LOG(VB_COMMFLAG, LOG_INFO, "SetLogoMaskArea()");
00275 
00276     logoMinX = width - 1;
00277     logoMaxX = 0;
00278     logoMinY = height - 1;
00279     logoMaxY = 0;
00280 
00281     for (unsigned int y = 0; y < height; y++)
00282     {
00283         for (unsigned int x = 0; x < width; x++)
00284         {
00285             if (edgeMask[y * width + x].isedge)
00286             {
00287                 if (x < logoMinX)
00288                     logoMinX = x;
00289                 if (y < logoMinY)
00290                     logoMinY = y;
00291                 if (x > logoMaxX)
00292                     logoMaxX = x;
00293                 if (y > logoMaxY)
00294                     logoMaxY = y;
00295             }
00296         }
00297     }
00298 
00299     logoMinX -= 5;
00300     logoMaxX += 5;
00301     logoMinY -= 5;
00302     logoMaxY += 5;
00303 
00304     if (logoMinX < 4)
00305         logoMinX = 4;
00306     if (logoMaxX > (width-5))
00307         logoMaxX = (width-5);
00308     if (logoMinY < 4)
00309         logoMinY = 4;
00310     if (logoMaxY > (height-5))
00311         logoMaxY = (height-5);
00312 }
00313 
00314 void ClassicLogoDetector::SetLogoMask(unsigned char *mask)
00315 {
00316     int pixels = 0;
00317 
00318     memcpy(logoMask, mask, width * height);
00319 
00320     SetLogoMaskArea();
00321 
00322     for(unsigned int y = logoMinY; y <= logoMaxY; y++)
00323         for(unsigned int x = logoMinX; x <= logoMaxX; x++)
00324             if (!logoMask[y * width + x] == 1)
00325                 pixels++;
00326 
00327     if (pixels < 30)
00328         return;
00329 
00330     // set the pixels around our logo
00331     for(unsigned int y = (logoMinY - 1); y <= (logoMaxY + 1); y++)
00332     {
00333         for(unsigned int x = (logoMinX - 1); x <= (logoMaxX + 1); x++)
00334         {
00335             if (!logoMask[y * width + x])
00336             {
00337                 for (unsigned int y2 = y - 1; y2 <= (y + 1); y2++)
00338                 {
00339                     for (unsigned int x2 = x - 1; x2 <= (x + 1); x2++)
00340                     {
00341                         if ((logoMask[y2 * width + x2] == 1) &&
00342                             (!logoMask[y * width + x]))
00343                         {
00344                             logoMask[y * width + x] = 2;
00345                             x2 = x + 2;
00346                             y2 = y + 2;
00347 
00348                             logoCheckMask[y2 * width + x2] = 1;
00349                             logoCheckMask[y * width + x] = 1;
00350                         }
00351                     }
00352                 }
00353             }
00354         }
00355     }
00356 
00357     for(unsigned int y = (logoMinY - 2); y <= (logoMaxY + 2); y++)
00358     {
00359         for(unsigned int x = (logoMinX - 2); x <= (logoMaxX + 2); x++)
00360         {
00361             if (!logoMask[y * width + x])
00362             {
00363                 for (unsigned int y2 = y - 1; y2 <= (y + 1); y2++)
00364                 {
00365                     for (unsigned int x2 = x - 1; x2 <= (x + 1); x2++)
00366                     {
00367                         if ((logoMask[y2 * width + x2] == 2) &&
00368                             (!logoMask[y * width + x]))
00369                         {
00370                             logoMask[y * width + x] = 3;
00371                             x2 = x + 2;
00372                             y2 = y + 2;
00373 
00374                             logoCheckMask[y * width + x] = 1;
00375                         }
00376                     }
00377                 }
00378             }
00379         }
00380     }
00381 
00382 #ifdef SHOW_DEBUG_WIN
00383     DumpLogo(true,framePtr);
00384 #endif
00385 
00386     logoFrameCount = 0;
00387     logoInfoAvailable = true;
00388 }
00389 
00390 
00391 void ClassicLogoDetector::DumpLogo(bool fromCurrentFrame,
00392     unsigned char* framePtr)
00393 {
00394     char scrPixels[] = " .oxX";
00395 
00396     if (!logoInfoAvailable)
00397         return;
00398 
00399     cerr << "\nLogo Data ";
00400     if (fromCurrentFrame)
00401         cerr << "from current frame\n";
00402 
00403     cerr << "\n     ";
00404 
00405     for(unsigned int x = logoMinX - 2; x <= (logoMaxX + 2); x++)
00406         cerr << (x % 10);
00407     cerr << "\n";
00408 
00409     for(unsigned int y = logoMinY - 2; y <= (logoMaxY + 2); y++)
00410     {
00411         QString tmp = QString("%1: ").arg(y, 3);
00412         QString ba = tmp.toAscii();
00413         cerr << ba.constData();
00414         for(unsigned int x = logoMinX - 2; x <= (logoMaxX + 2); x++)
00415         {
00416             if (fromCurrentFrame)
00417             {
00418                 cerr << scrPixels[framePtr[y * width + x] / 50];
00419             }
00420             else
00421             {
00422                 switch (logoMask[y * width + x])
00423                 {
00424                         case 0:
00425                         case 2: cerr << " ";
00426                         break;
00427                         case 1: cerr << "*";
00428                         break;
00429                         case 3: cerr << ".";
00430                         break;
00431                 }
00432             }
00433         }
00434         cerr << "\n";
00435     }
00436     cerr.flush();
00437 }
00438 
00439 
00440 /* ideas for this method ported back from comskip.c mods by Jere Jones
00441  * which are partially mods based on Myth's original commercial skip
00442  * code written by Chris Pinkham. */
00443 bool ClassicLogoDetector::doesThisFrameContainTheFoundLogo(
00444     unsigned char* framePtr)
00445 {
00446     int radius = 2;
00447     unsigned int x, y;
00448     int pos1, pos2, pos3;
00449     int pixel;
00450     int goodEdges = 0;
00451     int badEdges = 0;
00452     int testEdges = 0;
00453     int testNotEdges = 0;
00454 
00455     for (y = logoMinY; y <= logoMaxY; y++ )
00456     {
00457         for (x = logoMinX; x <= logoMaxX; x++ )
00458         {
00459             pos1 = y * width + x;
00460             pos2 = (y - radius) * width + x;
00461             pos3 = (y + radius) * width + x;
00462 
00463             pixel = framePtr[pos1];
00464 
00465             if (edgeMask[pos1].horiz)
00466             {
00467                 if ((abs(framePtr[pos1 - radius] - pixel) >= logoEdgeDiff) ||
00468                     (abs(framePtr[pos1 + radius] - pixel) >= logoEdgeDiff))
00469                     goodEdges++;
00470                 testEdges++;
00471             }
00472             else
00473             {
00474                 if ((abs(framePtr[pos1 - radius] - pixel) >= logoEdgeDiff) ||
00475                     (abs(framePtr[pos1 + radius] - pixel) >= logoEdgeDiff))
00476                     badEdges++;
00477                 testNotEdges++;
00478             }
00479 
00480             if (edgeMask[pos1].vert)
00481             {
00482                 if ((abs(framePtr[pos2] - pixel) >= logoEdgeDiff) ||
00483                     (abs(framePtr[pos3] - pixel) >= logoEdgeDiff))
00484                     goodEdges++;
00485                 testEdges++;
00486             }
00487             else
00488             {
00489                 if ((abs(framePtr[pos2] - pixel) >= logoEdgeDiff) ||
00490                     (abs(framePtr[pos3] - pixel) >= logoEdgeDiff))
00491                     badEdges++;
00492                 testNotEdges++;
00493             }
00494         }
00495     }
00496 
00497     frameNumber++;
00498     double goodEdgeRatio = (double)goodEdges / (double)testEdges;
00499     double badEdgeRatio = (double)badEdges / (double)testNotEdges;
00500     if ((goodEdgeRatio > commDetectLogoGoodEdgeThreshold) &&
00501         (badEdgeRatio < commDetectLogoBadEdgeThreshold))
00502         return true;
00503     else
00504         return false;
00505 }
00506 
00507 bool ClassicLogoDetector::pixelInsideLogo(unsigned int x, unsigned int y)
00508 {
00509     if (!logoInfoAvailable)
00510         return false;
00511 
00512     return ((x > logoMinX) && (x < logoMaxX) &&
00513             (y > logoMinY) && (y < logoMaxY));
00514 }
00515 
00516 void ClassicLogoDetector::DetectEdges(VideoFrame *frame, EdgeMaskEntry *edges,
00517                                       int edgeDiff)
00518 {
00519     int r = 2;
00520     unsigned char *buf = frame->buf;
00521     unsigned char p;
00522     unsigned int pos, x, y;
00523 
00524     for (y = commDetectBorder + r; y < (height - commDetectBorder - r); y++)
00525     {
00526         if ((y > (height/4)) && (y < (height * 3 / 4)))
00527             continue;
00528 
00529         for (x = commDetectBorder + r; x < (width - commDetectBorder - r); x++)
00530         {
00531             int edgeCount = 0;
00532 
00533             if ((x > (width/4)) && (x < (width * 3 / 4)))
00534                 continue;
00535 
00536             pos = y * width + x;
00537             p = buf[pos];
00538 
00539             if (( abs(buf[y * width + (x - r)] - p) >= edgeDiff) ||
00540                 ( abs(buf[y * width + (x + r)] - p) >= edgeDiff))
00541             {
00542                 edges[pos].horiz++;
00543                 edgeCount++;
00544             }
00545             if (( abs(buf[(y - r) * width + x] - p) >= edgeDiff) ||
00546                 ( abs(buf[(y + r) * width + x] - p) >= edgeDiff))
00547             {
00548                 edges[pos].vert++;
00549                 edgeCount++;
00550             }
00551 
00552             if (( abs(buf[(y - r) * width + (x - r)] - p) >= edgeDiff) ||
00553                 ( abs(buf[(y + r) * width + (x + r)] - p) >= edgeDiff))
00554             {
00555                 edges[pos].ldiag++;
00556                 edgeCount++;
00557             }
00558 
00559             if (( abs(buf[(y - r) * width + (x + r)] - p) >= edgeDiff) ||
00560                 ( abs(buf[(y + r) * width + (x - r)] - p) >= edgeDiff))
00561             {
00562                 edges[pos].rdiag++;
00563                 edgeCount++;
00564             }
00565 
00566             if (edgeCount >= 3)
00567                 edges[pos].isedge++;
00568         }
00569     }
00570 }
00571 
00572 /* vim: set expandtab tabstop=4 shiftwidth=4: */
00573 
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends