[mythtv] Help needed testing logo detection code

Leandro Dardini ldardini at tiscali.it
Tue Mar 18 19:42:25 EST 2003


Adding a 0,01 Euro to this... here in Italy and maybe in other parts of
Europe, Tv stations maintan the logo always, during normal programming and
during commercials...

maybe because the programs are so bad that commercials are better? :)

Leandro

----- Original Message -----
From: "Chris Pinkham" <cpinkham at bc2va.org>
To: "MythTV" <mythtv-dev at snowman.net>
Sent: Tuesday, March 18, 2003 2:51 AM
Subject: Re: [mythtv] Help needed testing logo detection code


> I tested this on both the FOX network here on an episode of That 70's Show
> and on an episode of NBC's Law & Order and it didn't detect either logo.
> The NBC one was transparent and the FOX logo was pure white in some parts
> and transparent in others.  Both logos occurred in the lower right hand
> corner of the video.
>
> I think you might want to take a look at the following paper describing
> some ways to do logo detection.  I think they'd be more accurate than
> your current method of just checking for X pixels that are of a certain
> brightness.  The paper even has some code snippets.
>
> http://toonarchive.com/logo-detection/
>
> > Hiya,
> > I've been working on some code to pick up white-transparent and
white-opa=
> > que
> > logos which seems to work with the limited amount of material I've been
a=
> > ble
> > to test it on.
> > When watching a program, press X to start off the logo detection, this
sh=
> > ould
> > take ten seconds then an ascii rendition of the logo will be sent to the
> > console at various levels of luminosity, the code takes a Y-value of
grea=
> > ter
> > than 120 to be part of a logo. From then until the end of the video a
con=
> > sole
> > message will be output saying if a logo is present or not.
> >
> > I've only tested this on pre-recorded material but it ought to work on
li=
> > ve
> >  tv too. This patch isn't supposed to add any extra functionality but is
=
> > at
> >  the proof-of-concept stage.
> >
> > Liam
> >
> > --------------Boundary-00=_P6XVGMBBY66RAED852JC
> > Content-Type: text/x-diff;
> >   charset="us-ascii";
> >   name="logodetect-2003-03-16.patch"
> > Content-Transfer-Encoding: 7bit
> > Content-Disposition: attachment; filename="logodetect-2003-03-16.patch"
> >
> > *** libs/libmythtv/NuppelVideoPlayer.cpp 2003-03-16 19:19:14.000000000
+0000
> > --- libs/libmythtv/NuppelVideoPlayer.cpp 2003-03-16 20:36:15.000000000
+0000
> > ***************
> > *** 3788,3790 ****
> > --- 3788,3942 ----
> >
> >       return true;
> >   }
> > +
> > + void NuppelVideoPlayer::TrainLogoStart(void)
> > + {
> > +   pthread_t id;
> > +   pthread_create(&id, NULL, StartLogoThread, this);
> > + }
> > +
> > + void *NuppelVideoPlayer::StartLogoThread(void *player)
> > + {
> > +   ((NuppelVideoPlayer *)player)->TrainLogo();
> > +   return NULL;
> > + }
> > +
> > + void NuppelVideoPlayer::TrainLogo(void)
> > + {
> > + // Runs as a thread snooping on the videobuffer, first looks for a
logo then if found sends messsage if still present to console
> > + int x, y, i, count;
> > + const int margin = gContext->GetNumSetting("LogoMargin", 2); // Marin
from each edge to avoid noise
> > + int test_width = video_width - (2 * margin) + 1; // area that we will
sample for bright pixels
> > + int test_height = video_height - (2 * margin) + 1;
> > + const int frame_size = test_width * test_height;
> > + int *minframe = new int[frame_size];
> > + long long lastframe = framesPlayed;
> > + int tries = 0;
> > + int minx, maxx, miny, maxy;
> > + const int bright_pixel = 120;
> > + int bright_pixels = 0;
> > + int matches;
> > + const int framerange = gContext->GetNumSetting("LogoFrameRange", 10 *
video_frame_rate);
> > + bool logo_found;
> > +
> > + cout << "Searching for logo..." << endl;
> > + // First look at the pixels which stay constantly bright over the
whole screen (minus the margin)
> > + // if a small enough area is present then it is taken to be a logo
> > + do {
> > + for (i = 0; i < frame_size; i++)
> > +   minframe[i] = 255;
> > +
> > + LogoGetMinValues(minframe, framerange, video_frame_rate / 2, margin,
test_width, margin, test_height);
> > +
> > + minx = test_width + 1; maxx = -1; miny = test_height + 1; maxy = - 1;
> > + // See what the dimensions of the constanly bright part of the screen
are
> > + count = 0;
> > + for (i = 0; i < frame_size ; i++)
> > + {
> > + if (minframe[i] > bright_pixel)
> > + {
> > + count++;
> > + if ((i % test_width) < minx) minx = (i % test_width);
> > + if ((i / test_width) < miny) miny = (i / test_width);
> > + if ((i % test_width) > maxx) maxx = (i % test_width);
> > + if ((i / test_width) > maxy) maxy = (i / test_width);
> > + }
> > + }
> > + logo_found = (maxx - minx < video_width / 5) && (maxy - miny <
video_height / 10);
> > + tries++;
> > +
> > + } while (!eof && !killvideo && !logo_found && tries < 10);
> > +
> > + // Spit out the logo
> > +  if (logo_found)
> > + {
> > +     for (i = bright_pixel - 30; i < bright_pixel + 30; i += 10)
> > + {
> > +     cout << "i:" << i << endl << flush;
> > +
> > +       for (y = miny; y < maxy; y++)
> > +       {
> > +         for (x = minx; x < maxx; x++)
> > +         {
> > +           if (minframe[(y * test_width) + x] > i)    {cout << "@";}
> > +           else                                       {cout << " ";}
> > +         }
> > +         cout << endl;
> > +       }
> > + }
> > +
> > +   }
> > + else {cout << "no logo found" << endl; return;}
> > +
> > + // Take a count of the number of white pixels in the logo
> > + for (y = miny; y < maxy; y++)
> > + {
> > + for (x = minx; x < maxx; x++)
> > + {
> > + if (minframe[(y * test_width) + x] > bright_pixel)
> > + bright_pixels++;
> > + }
> > + }
> > + cout << "Logo has " << bright_pixels << " pixels" << endl;
> > +
> > + // Reduce part of the screen we'll be searching to the area we expect
the logo to be in
> > + test_width = maxx - minx + 1; test_height = maxy - miny + 1;
> > +   delete minframe;
> > +   minframe = new int[test_width * test_height];
> > + // move the minx and miny values to be relative to the display buffer
> > + minx += margin; miny += margin;
> > +
> > + // Keep looking for logo until the file ends or is aborted
> > + do
> > + {
> > +     for (i = 0; i < test_width * test_height; i++)
> > +       minframe[i] = 255;
> > +
> > + LogoGetMinValues(minframe, framerange, video_frame_rate / 2, minx,
test_width, miny, test_height);
> > +
> > + matches = 0;
> > + for (i = 0; i < test_height * test_width; i++)
> > + {
> > + if (minframe[i] > bright_pixel)
> > + matches++;
> > + }
> > +
> > + if (matches > (float)bright_pixels * 0.9)    {cout << "Frame:" <<
framesPlayed << "Logo Present, " << matches << " bright pixels found" <<
endl;}
> > + else      {cout << "Frame:" << framesPlayed << "No Logo Present, " <<
matches << " bright pixels found" << endl;}
> > +
> > + } while (!eof && !killvideo);
> > +
> > + delete minframe;
> > + pthread_exit(NULL);
> > +
> > + }
> > +
> > + void NuppelVideoPlayer::LogoGetMinValues(int minframe[], const int
&framerange, const int &frameinterval, const int &minx, const int
&test_width, const int &miny, const int &test_height)
> > + {
> > + // Takes array and records the min luminesence for each pixel over a
period of time, frameinterval is the number of frames before
> > + // taking a new frame to get the mininum values again and framerange
is the maximum number of frames to to the comparison over
> > + // Minx, and miny specify the top-left hand point of the area being
examined and test_width, test_height is the area to do it over
> > + long long lastframe = framesPlayed;
> > + long long stopframe = framesPlayed + framerange;
> > + int buffer_pos = 0, i;
> > +
> > +     do
> > +     {
> > +       if (framesPlayed - lastframe > frameinterval)
> > +       {
> > +         for (i = 0; i < test_width * test_height; i++)
> > +         {
> > + // this is the point in the display buffer that we map to from our
test area
> > + buffer_pos = (((i/test_width) + miny) * video_width) + (i %
test_width) + minx;
> > +             if (minframe[i] > (int)vbuffer[vpos][buffer_pos])
> > +             minframe[i] = (int)vbuffer[vpos][buffer_pos];
> > +         }
> > +
> > +         lastframe = framesPlayed;
> > +       }
> > +
> > +       usleep(800);
> > +     // check that we should still be looking
> > +     } while (!eof && !killvideo && framesPlayed < stopframe);
> > +
> > + }
> >
> > *** libs/libmythtv/NuppelVideoPlayer.h 2003-03-16 19:19:14.000000000
+0000
> > --- libs/libmythtv/NuppelVideoPlayer.h 2003-03-16 20:03:58.000000000
+0000
> > ***************
> > *** 129,134 ****
> > --- 129,135 ----
> >
> >       void EmbedInWidget(unsigned long wid, int x, int y, int w, int h);
> >       void StopEmbedding(void);
> > +     void TrainLogoStart(void);
> >
> >    protected:
> >       void OutputAudioLoop(void);
> > ***************
> > *** 136,141 ****
> > --- 137,143 ----
> >
> >       static void *kickoffOutputAudioLoop(void *player);
> >       static void *kickoffOutputVideoLoop(void *player);
> > +     static void *StartLogoThread(void *player);
> >
> >    private:
> >       void InitSound(void);
> > ***************
> > *** 178,183 ****
> > --- 180,188 ----
> >       bool DoSkipCommercials(void);
> >       void AutoCommercialSkip(void);
> >
> > +     void TrainLogo(void);
> > + void LogoGetMinValues(int minframe[], const int &framerange, const int
&frameinterval, const int &minx, const int &test_width, const int &miny,
const int &test_height);
> > +
> >       void JumpToFrame(long long frame);
> >       void JumpToNetFrame(long long net) { JumpToFrame(framesPlayed +
net); }
> >
> > *** libs/libmythtv/tv_play.cpp 2003-03-16 19:19:14.000000000 +0000
> > --- libs/libmythtv/tv_play.cpp 2003-03-16 20:07:14.000000000 +0000
> > ***************
> > *** 784,789 ****
> > --- 784,794 ----
> >               DoSkipCommercials();
> >               break;
> >           }
> > + case 'x': case 'X':
> > + {
> > + nvp->TrainLogoStart();
> > + break;
> > + }
> >           case 's': case 'S': case 'p': case 'P':
> >           {
> >               doing_ff = false;
> >
> >
> > --------------Boundary-00=_P6XVGMBBY66RAED852JC
> > Content-Type: text/plain; charset="us-ascii"
> > MIME-Version: 1.0
> > Content-Transfer-Encoding: 7bit
> >
> > _______________________________________________
> > mythtv-dev mailing list
> > mythtv-dev at snowman.net
> > http://www.snowman.net/mailman/listinfo/mythtv-dev
> >
> > --------------Boundary-00=_P6XVGMBBY66RAED852JC--
> >
>
>
> --
>
> Chris
>
>
****************************************************************************
*
> ** Chris Pinkham                  Linux v2.2.18, Sane v1.0.4, Cajun v3.0-8
**
> ** cpinkham at bc2va.org                          http://www.bc2va.org/chris/
**
>
****************************************************************************
*
> _______________________________________________
> mythtv-dev mailing list
> mythtv-dev at snowman.net
> http://www.snowman.net/mailman/listinfo/mythtv-dev



More information about the mythtv-dev mailing list