|
MythTV
0.26-pre
|
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
1.7.6.1