Advertisement
Guest User

codebook

a guest
Apr 15th, 2019
257
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 16.63 KB | None | 0 0
  1.  
  2. //Example 15-4. Codebook algorithm implementation
  3. #include <opencv2/opencv.hpp>
  4. #include <vector>
  5. #include <iostream>
  6. #include <cstdlib>
  7. #include <fstream>
  8. #include <opencv2\opencv.hpp>
  9. #include <opencv2/core/core.hpp>
  10. #include <opencv2/highgui/highgui.hpp>
  11. #include <opencv2/video/background_segm.hpp>
  12. #include <opencv2/bgsegm.hpp>
  13. #include <opencv2/videoio.hpp>
  14.  
  15. using namespace std;
  16.  
  17. #define CHANNELS 3          //Always 3 because yuv
  18. int cbBounds[CHANNELS];     // IF pixel is within this bound outside of codebook, learn it, else form new code
  19. int minMod[CHANNELS];       // If pixel is lower than a codebook by this amount, it's matched
  20. int maxMod[CHANNELS];       // If pixel is high than a codebook by this amount, it's matched
  21.  
  22.  
  23. //The variable t counts the number of points we’ve accumulated since the start or the last
  24. //clear operation. Here’s how the actual codebook elements are described:
  25. //
  26. class CodeElement {
  27.     public:
  28.         uchar learnHigh[CHANNELS];  //High side threshold for learning
  29.         uchar learnLow[CHANNELS];   //Low side threshold for learning
  30.         uchar max[CHANNELS];        //High side of box boundary
  31.         uchar min[CHANNELS];        //Low side of box boundary
  32.         int t_last_update;          //Allow us to kill stale entries
  33.         int stale;                  //max negative run (longest period of inactivity)
  34.  
  35.         CodeElement() {
  36.             for(int i = 0; i < CHANNELS; i++)
  37.                 learnHigh[i] = learnLow[i] = max[i] = min[i] = 0;
  38.             t_last_update = stale = 0;
  39.         }
  40.  
  41.         CodeElement& operator=( const CodeElement& ce ) {
  42.             for(int i=0; i<CHANNELS; i++ ) {
  43.                 learnHigh[i] = ce.learnHigh[i];
  44.                 learnLow[i] = ce.learnLow[i];
  45.                 min[i] = ce.min[i];
  46.                 max[i] = ce.max[i];
  47.             }
  48.             t_last_update = ce.t_last_update;
  49.             stale = ce.stale;
  50.             return *this;
  51.         }
  52.  
  53.         CodeElement( const CodeElement& ce ) { *this = ce; }
  54. };
  55.  
  56. // You need one of these for each pixel in the video image (rowXcol)
  57. //
  58. class CodeBook : public vector<CodeElement> {
  59.     public:
  60.     int t;     //Count of every image learned on
  61.  
  62.     // count every access
  63.     CodeBook() { t=0; }
  64.  
  65.     // Default is an empty book
  66.     CodeBook( int n ) : vector<CodeElement>(n) { t=0; } // Construct book of size n
  67. };
  68.  
  69.  
  70. // Updates the codebook entry with a new data point
  71. // Note: cbBounds must be of length equal to numChannels
  72. //
  73. //
  74. int updateCodebook(     // return CodeBook index
  75.     const cv::Vec3b& p, // incoming YUV pixel
  76.     CodeBook& c,        // CodeBook for the pixel
  77.     int* cbBounds,  // Bounds for codebook (usually: {10,10,10})
  78.     int numChannels     // Number of color channels we're learning
  79.     ) {
  80.     if(c.size() == 0)
  81.         c.t = 0;
  82.     c.t += 1;       //Record learning event
  83.     //SET HIGH AND LOW BOUNDS
  84.     unsigned int high[3], low[3], n;
  85.     for( n=0; n<numChannels; n++ ) {
  86.         high[n] = p[n] + *(cbBounds+n);
  87.         if( high[n] > 255 ) high[n] = 255;
  88.         low[n] = p[n] - *(cbBounds+n);
  89.         if( low[n] < 0) low[n] = 0;
  90.     }
  91.  
  92.     // SEE IF THIS FITS AN EXISTING CODEWORD
  93.     //
  94.     int i;
  95.     int matchChannel;
  96.     for( i=0; i<c.size(); i++ ) {
  97.         matchChannel = 0;
  98.         for( n=0; n<numChannels; n++ ) {
  99.             if( // Found an entry for this channel
  100.                 ( c[i].learnLow[n] <= p[n] ) && ( p[n] <= c[i].learnHigh[n]))
  101.                 matchChannel++;
  102.             }
  103.  
  104.         if( matchChannel == numChannels ) {// If an entry was found
  105.             c[i].t_last_update = c.t;
  106.  
  107.             // adjust this codeword for the first channel
  108.             //
  109.             for( n=0; n<numChannels; n++ ) {
  110.                 if( c[i].max[n] < p[n] )
  111.                     c[i].max[n] = p[n];
  112.                 else if( c[i].min[n] > p[n] )
  113.                     c[i].min[n] = p[n];
  114.             }
  115.             break;
  116.         }
  117.     }
  118.  
  119.     // OVERHEAD TO TRACK POTENTIAL STALE ENTRIES
  120.     //
  121.     for( int s=0; s<c.size(); s++ ) {
  122.  
  123.         // Track which codebook entries are going stale:
  124.         //
  125.         int negRun = c.t - c[s].t_last_update;
  126.         if( c[s].stale < negRun ) c[s].stale = negRun;
  127.     }
  128.  
  129.     // ENTER A NEW CODEWORD IF NEEDED
  130.     //
  131.     if( i == c.size() ) {
  132.         // if no existing codeword found, make one
  133.         CodeElement ce;
  134.         for( n=0; n<numChannels; n++ ) {
  135.             ce.learnHigh[n] = high[n];
  136.             ce.learnLow[n] = low[n];
  137.             ce.max[n] = p[n];
  138.             ce.min[n] = p[n];
  139.         }
  140.             ce.t_last_update = c.t;
  141.             ce.stale = 0;
  142.             c.push_back( ce );
  143.     }
  144.  
  145.     // SLOWLY ADJUST LEARNING BOUNDS
  146.     //
  147.     for( n=0; n<numChannels; n++ ) {
  148.         if( c[i].learnHigh[n] < high[n]) c[i].learnHigh[n] += 1;
  149.         if( c[i].learnLow[n] > low[n] ) c[i].learnLow[n] -= 1;
  150.     }
  151.     return c.size();
  152. }
  153.  
  154. // During learning, after you've learned for some period of time,
  155. // periodically call this to clear out stale codebook entries
  156. //
  157. int foo = 0;
  158. int clearStaleEntries(
  159.     // return number of entries cleared
  160.     CodeBook &c
  161.     // Codebook to clean up
  162. ){
  163.     int staleThresh = c.t>>1;
  164.     int *keep = new int[c.size()];
  165.     int keepCnt = 0;
  166.  
  167.     // SEE WHICH CODEBOOK ENTRIES ARE TOO STALE
  168.     //
  169.     int foogo2 = 0;
  170.     for( int i=0; i<c.size(); i++ ){
  171.         if(c[i].stale > staleThresh)
  172.             keep[i] = 0; // Mark for destruction
  173.         else
  174.         {
  175.             keep[i] = 1; // Mark to keep
  176.             keepCnt += 1;
  177.         }
  178.     }
  179.  
  180.     // move the entries we want to keep to the front of the vector and then
  181.     // truncate to the correct length once all of the good stuff is saved.
  182.     //
  183.     int k = 0;
  184.     int numCleared = 0;
  185.     for( int ii=0; ii<c.size(); ii++ ) {
  186.         if( keep[ii] ) {
  187.             c[k] = c[ii];
  188.             // We have to refresh these entries for next clearStale
  189.             c[k].t_last_update = 0;
  190.             k++;
  191.         } else {
  192.             numCleared++;
  193.         }
  194.     }
  195.     c.resize( keepCnt );
  196.     delete[] keep;
  197.     return numCleared;
  198. }
  199.  
  200. // Given a pixel and a codebook, determine whether the pixel is
  201. // covered by the codebook
  202. //
  203. // NOTES:
  204. // minMod and maxMod must have length numChannels,
  205. // e.g. 3 channels => minMod[3], maxMod[3]. There is one min and
  206. //      one max threshold per channel.
  207. //
  208. uchar backgroundDiff( // return 0 => background, 255 => foreground
  209. const cv::Vec3b& p,   // Pixel (YUV)
  210. CodeBook& c,          // Codebook
  211. int numChannels,      // Number of channels we are testing
  212. int* minMod_,          // Add this (possibly negative) number onto max level
  213.                       //    when determining whether new pixel is foreground
  214. int* maxMod_           // Subtract this (possibly negative) number from min
  215.                       //    level when determining whether new pixel is
  216.                       //    foreground
  217. ) {
  218.     int matchChannel;
  219.  
  220.     // SEE IF THIS FITS AN EXISTING CODEWORD
  221.     //
  222.     int i;
  223.     for( i=0; i<c.size(); i++ ) {
  224.         matchChannel = 0;
  225.         for( int n=0; n<numChannels; n++ ) {
  226.             if((c[i].min[n] - minMod_[n] <= p[n] ) && (p[n] <= c[i].max[n] + maxMod_[n]))
  227.             {
  228.                 matchChannel++; // Found an entry for this channel
  229.             } else {
  230.                 break;
  231.             }
  232.         }
  233.         if(matchChannel == numChannels) {
  234.             break; // Found an entry that matched all channels
  235.         }
  236.     }
  237.     if( i >= c.size() ) //No match with codebook => foreground
  238.         return 255;
  239.     return 0;           //Else background
  240. }
  241.  
  242. ///////////////////////////////////////////////////////////////////////////////////////////////////
  243. /////////////////// This part adds a "main" to run the above code. ////////////////////////////////
  244. ///////////////////////////////////////////////////////////////////////////////////////////////////
  245.  
  246. // Just make a convienience class (assumes image will not change size in video);
  247. class CbBackgroudDiff {
  248.     public:
  249.     cv::Mat Iyuv;                   //Will hold the yuv converted image
  250.     cv::Mat mask;                   //Will hold the background difference mask
  251.     vector<CodeBook> codebooks;     //Will hold a CodeBook for each pixel
  252.     int row, col, image_length;     //How many pixels are in the image
  253.  
  254.     //Constructor
  255.     void init(cv::Mat &I_from_video) {
  256.         vector<int> v(3,10);
  257.         set_global_vecs(cbBounds, v);
  258.         v[0] = 6; v[1] = 20; v[2] = 8; //Just some decent defaults for low side
  259.         set_global_vecs(minMod, v);
  260.         v[0] = 70; v[1] = 20; v[2] = 6; //Decent defaults for high side
  261.         set_global_vecs(maxMod, v);
  262.         Iyuv.create(I_from_video.size(), I_from_video.type());
  263.         mask.create(I_from_video.size(), CV_8UC1);
  264.         row = I_from_video.rows;
  265.         col = I_from_video.cols;
  266.         image_length = row*col;
  267.         cout << "(row,col,len) = (" << row << ", " << col << ", " << image_length << ")" << endl;
  268.         codebooks.resize(image_length);
  269.     }
  270.  
  271.     CbBackgroudDiff(cv::Mat &I_from_video) {
  272.         init(I_from_video);
  273.     }
  274.  
  275.     CbBackgroudDiff(){};
  276.  
  277.     //Convert to YUV
  278.     void convert_to_yuv(cv::Mat &Irgb)
  279.     {
  280.         cvtColor(Irgb, Iyuv, cv::COLOR_BGR2YUV);
  281.     }
  282.  
  283.     int size_check(cv::Mat &I) { //Check that image doesn't change size, return -1 if size doesn't match, else 0
  284.         int ret = 0;
  285.         if((row != I.rows) || (col != I.cols)) {
  286.             cerr << "ERROR: Size changed! old[" << row << ", " << col << "], now [" << I.rows << ", " << I.cols << "]!" << endl;
  287.             ret = -1;
  288.         }
  289.         return ret;
  290.     }
  291.  
  292.     //Utilities for setting gloabals
  293.     void set_global_vecs(int *globalvec, vector<int> &vec) {
  294.         if(vec.size() != CHANNELS) {
  295.             cerr << "Input vec[" << vec.size() << "] should equal CHANNELS [" << CHANNELS << "]" << endl;
  296.             vec.resize(CHANNELS, 10);
  297.         }
  298.         int i = 0;
  299.         for (vector<int>::iterator it = vec.begin(); it != vec.end(); ++it, ++i) {
  300.              globalvec[i] = *it;
  301.          }
  302.      }
  303.  
  304.     //Background operations
  305.     int updateCodebookBackground(cv::Mat &Irgb) { //Learn codebook, -1 if error, else total # of codes
  306.         convert_to_yuv(Irgb);
  307.         if(size_check(Irgb))
  308.             return -1;
  309.         int total_codebooks = 0;
  310.         cv::Mat_<cv::Vec3b>::iterator Iit = Iyuv.begin<cv::Vec3b>(), IitEnd = Iyuv.end<cv::Vec3b>();
  311.         vector<CodeBook>::iterator Cit = codebooks.begin(), CitEnd = codebooks.end();
  312.         for(; Iit != IitEnd; ++Iit,++Cit) {
  313.             total_codebooks += updateCodebook(*Iit,*Cit,cbBounds,CHANNELS);
  314.         }
  315.         if(Cit != CitEnd)
  316.             cerr << "ERROR: Cit != CitEnd in updateCodeBackground(...) " << endl;
  317.         return(total_codebooks);
  318.     }
  319.  
  320.     int clearStaleEntriesBackground() { //Clean out stuff that hasn't been updated for a long time
  321.         int total_cleared = 0;
  322.         vector<CodeBook>::iterator Cit = codebooks.begin(), CitEnd = codebooks.end();
  323.         for(; Cit != CitEnd; ++Cit) {
  324.             total_cleared += clearStaleEntries(*Cit);
  325.         }
  326.         return(total_cleared);
  327.     }
  328.  
  329.     int backgroundDiffBackground(cv::Mat &Irgb) {  //Take the background difference of the image
  330.         convert_to_yuv(Irgb);
  331.         if(size_check(Irgb))
  332.             return -1;
  333.         cv::Mat_<cv::Vec3b>::iterator Iit = Iyuv.begin<cv::Vec3b>(), IitEnd = Iyuv.end<cv::Vec3b>();
  334.         vector<CodeBook>::iterator Cit = codebooks.begin(), CitEnd = codebooks.end();
  335.         cv::Mat_<uchar>::iterator Mit = mask.begin<uchar>(), MitEnd = mask.end<uchar>();
  336.         for(; Iit != IitEnd; ++Iit,++Cit,++Mit) {
  337.             *Mit = backgroundDiff(*Iit,*Cit,CHANNELS,minMod,maxMod);
  338.         }
  339.         if((Cit != CitEnd)||(Mit != MitEnd)){
  340.             cerr << "ERROR: Cit != CitEnd and, or Mit != MitEnd in updateCodeBackground(...) " << endl;
  341.             return -1;
  342.         }
  343.         return 0;
  344.     }
  345. }; // end CbBackgroudDiff
  346.  
  347.  
  348. void help(char** argv ) {
  349.     cout << "\n"
  350.     << "Train a codebook background model on the first <#frames to train on> frames of an incoming video, then run the model\n"
  351.     << argv[0] <<" <#frames to train on> <avi_path/filename>\n"
  352.     << "For example:\n"
  353.     << argv[0] << " 50 ../tree.avi\n"
  354.     << "'A' or 'a' to adjust thresholds, esc, 'q' or 'Q' to quit"
  355.     << endl;
  356. }
  357.  
  358. //Adjusting the distance you have to be on the low side (minMod) or high side (maxMod) of a codebook
  359. //to be considered as recognized/background
  360. //
  361. void adjustThresholds(char* charstr, cv::Mat &Irgb, CbBackgroudDiff &bgd) {
  362.     int key = 1;
  363.     int y = 1,u = 0,v = 0, index = 0, thresh = 0;
  364.     if(thresh)
  365.         cout << "yuv[" << y << "][" << u << "][" << v << "] maxMod active" << endl;
  366.     else
  367.         cout << "yuv[" << y << "][" << u << "][" << v << "] minMod active" << endl;
  368.     cout << "minMod[" << minMod[0] << "][" << minMod[1] << "][" << minMod[2] << "]" << endl;
  369.     cout << "maxMod[" << maxMod[0] << "][" << maxMod[1] << "][" << maxMod[2] << "]" << endl;
  370.     while((key = cv::waitKey()) != 27 && key != 'Q' && key != 'q')  // Esc or Q or q to exit
  371.     {
  372.         if(thresh)
  373.             cout << "yuv[" << y << "][" << u << "][" << v << "] maxMod active" << endl;
  374.         else
  375.             cout << "yuv[" << y << "][" << u << "][" << v << "] minMod active" << endl;
  376.         cout << "minMod[" << minMod[0] << "][" << minMod[1] << "][" << minMod[2] << "]" << endl;
  377.         cout << "maxMod[" << maxMod[0] << "][" << maxMod[1] << "][" << maxMod[2] << "]" << endl;
  378.  
  379.  
  380.  
  381.         if(key == 'y') { y = 1; u = 0; v = 0; index = 0;}
  382.         if(key == 'u') { y = 0; u = 1; v = 0; index = 1;}
  383.         if(key == 'v') { y = 0; u = 0; v = 1; index = 2;}
  384.         if(key == 'l') { thresh = 0;} //minMod
  385.         if(key == 'h') { thresh = 1;} //maxMod
  386.         if(key == '.') { //Up
  387.             if(thresh == 0) { minMod[index] += 4;}
  388.             if(thresh == 1) { maxMod[index] += 4;}
  389.         }
  390.         if(key == ',') { //Down
  391.             if(thresh == 0) { minMod[index] -= 4;}
  392.             if(thresh == 1) { maxMod[index] -= 4;}
  393.         }
  394.         cout << "y,u,v for channel; l for minMod, h for maxMod threshold; , for down, . for up; esq or q to quit;" << endl;
  395.         bgd.backgroundDiffBackground(Irgb);
  396.         cv::imshow(charstr, bgd.mask);
  397.     }
  398. }
  399.  
  400.  
  401. ////////////////////////////////////////////////////////////////
  402. int main( int argc, char** argv) {
  403.     cv::namedWindow( argv[0], cv::WINDOW_AUTOSIZE );
  404.     cv::VideoCapture cap;
  405.     if((argc < 3)|| !cap.open(argv[2])) {
  406.         cerr << "Couldn't run the program" << endl;
  407.         help(argv);
  408.         cap.open(0);
  409.         return -1;
  410.     }
  411.  
  412.  
  413.  
  414.     int number_to_train_on = atoi( argv[1] );
  415.     cv::Mat image;
  416.     CbBackgroudDiff bgd;
  417.  
  418.     double fps = cap.get(CV_CAP_PROP_POS_FRAMES);
  419.     cout << "Aktualna klatka :  " << fps << std::endl ;
  420.  
  421.     // FIRST PROCESSING LOOP (TRAINING):
  422.     //
  423.     int frame_count = 0;
  424.     int key;
  425.     bool first_frame = true;
  426.     cout << "Total frames to train on = " << number_to_train_on << endl; //db
  427.     char seg[] = "Segmentation";
  428.     while(1) {
  429.         cout << "frame#: " << frame_count;
  430.         cap >> image;
  431.         if( !image.data ) exit(1); // Something went wrong, abort
  432.         if(frame_count == 0) { bgd.init(image);}
  433.  
  434.         cout << ", Codebooks: " << bgd.updateCodebookBackground(image) << endl;
  435.  
  436.         cv::imshow( argv[0], image );
  437.         frame_count++;
  438.         if( (key = cv::waitKey(7)) == 27 || key == 'q' || key == 'Q' || frame_count >= number_to_train_on) break; //Allow early exit on space, esc, q
  439.     }
  440.  
  441.     // We have accumulated our training, now create the models
  442.     //
  443.     cout << "Created the background model" << endl;
  444.     cout << "Total entries cleared = " << bgd.clearStaleEntriesBackground() << endl;
  445.     cout << "Press a key to start background differencing, 'a' to set thresholds, esc or q or Q to quit" << endl;
  446.  
  447.     // SECOND PROCESSING LOOP (TESTING):
  448.     //
  449.     cv::namedWindow( seg, cv::WINDOW_AUTOSIZE );
  450.     while((key = cv::waitKey()) != 27 || key == 'q' || key == 'Q'  ) { // esc, 'q' or 'Q' to exit
  451.         cap >> image;
  452.         if( !image.data ) exit(0);
  453.         cout <<  frame_count++ << " 'a' to adjust threholds" << endl;
  454.         if(key == 'a') {
  455.             cout << "Adjusting thresholds" << endl;
  456.         cout << "y,u,v for channel; l for minMod, h for maxMod threshold; , for down, . for up; esq or q to quit;" << endl;
  457.             adjustThresholds(seg,image,bgd);
  458.  
  459.         }
  460.         else {
  461.             if(bgd.backgroundDiffBackground(image)) {
  462.                 cerr << "ERROR, bdg.backgroundDiffBackground(...) failed" << endl;
  463.                 exit(-1);
  464.             }
  465.         }
  466.  
  467.  
  468.         cv::imshow("Segmentation",bgd.mask);
  469.         cv::imshow("oryg",image);
  470.     }
  471.     exit(0);
  472. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement