00001
00002 #include <unistd.h>
00003
00004
00005 #include <cstdlib>
00006
00007
00008 #include "mythcorecontext.h"
00009 #include "mythplayer.h"
00010
00011
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
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
00441
00442
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
00573