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