[mythtv] Help needed testing logo detection code

Chris Pinkham cpinkham at bc2va.org
Tue Mar 18 14:46:20 EST 2003


This is why when I added the blank frame detection method that I also
added a setup option to choose which method of detection to use.  The
commercial stuff needs a lot of thought as to how the various detection
methods will either compliment or contradict each other, but I'm sure
we'll get lots accomplished before 0.9 comes around.  The logos aren't
consistent here either, they are on sometimes during the show and off
at others, so I couldn't rely on them for my uses, but they can compliment
blank-frame or other detection methods since logos don't usually occur
(if ever) during commercials.

> 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
> 
> _______________________________________________
> mythtv-dev mailing list
> mythtv-dev at snowman.net
> http://www.snowman.net/mailman/listinfo/mythtv-dev
> 


-- 

Chris

*****************************************************************************
** Chris Pinkham                  Linux v2.2.18, Sane v1.0.4, Cajun v3.0-8 **
** cpinkham at bc2va.org                          http://www.bc2va.org/chris/ **
*****************************************************************************


More information about the mythtv-dev mailing list