65.9K
CodeProject is changing. Read more.
Home

Fast Barcode 39 Detection

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.71/5 (8 votes)

Sep 11, 2014

CPOL

2 min read

viewsIcon

17666

downloadIcon

1315

Fast detection of big barcode 39

Introduction

You can detect barcode39 from image. Detect it as fast as possible, because it's the first step of the process, barcode is used as document separator in image flow.

Background

The specification are available at Wikipedia Code 39.

Using external library FreeImage 3.16.0.

Using the Code

The main application is written in C#. It's used to reorder the pages of document before its creation in the automatic document recognition process.

So this library will be declared in C#, and called from this environment. The step parameter is the height step which is used to parse image.

You need to update the project with your FreeImage library path in order to compile it. Don't forget to copy the cb93.dll to the Host binary output folder.

class Sample
{
     // Declaration 
   [DllImport("Cb39.dll")]
   [return: MarshalAs(UnmanagedType.SafeArray)]
   private extern static string[] GetCodeBarreList([MarshalAs(UnmanagedType.LPStr)]  string file , [MarshalAs(UnmanagedType.U4)] int step);
   
   static void Main(string[] args)
   {
            DateTime t2 = DateTime.Now;
            string[] p2 = GetCodeBarreList(@"C:\TEMP\TEST.TIF",20);
            DateTime t3 = DateTime.Now;
            var ts2 = new TimeSpan(t3.Ticks - t2.Ticks);
            Console.WriteLine("{1} Delay {0} ms", ts2.TotalMilliseconds, string.Join(" - ", p2));

            Console.ReadKey();
    }
}

How the Application Works

First step, it loads the image with FreeImage lib, and convert to greyscale.

    FreeImage_Initialise(false);
    FREE_IMAGE_FORMAT fif = FreeImage_GetFIFFromFilename(file->c_str());
    FIBITMAP * fimg = FreeImage_Load(fif, file->c_str() ,0);
    FIBITMAP * cfimg = FreeImage_ConvertToGreyscale(fimg);
    FIBITMAP * img , * rcfimg ;

Second step, there is two pass to detect barcode depending on its orientation. For optimization, you can remove the rotation of the image if you know the orientation of the barcode.

    // try to detect vertical and horizontal barcode
    for(int o=0;o<2;o++)
    {
        // Rotation
        if ( o == 1 ) {
            rcfimg = FreeImage_Rotate(cfimg,90);
            img = rcfimg;
        } else {
            img = cfimg;
        }
    ...
    }

Third step, parse image line by line width step, and get barcode string as result. Check and append to the list.

      unsigned int width  = FreeImage_GetWidth(img);
        unsigned int height = FreeImage_GetHeight(img);
        if ( step == 0 ) step = height ;
        for(unsigned int y = 0; y < height ; y += step )
        {
            BYTE * pScanLine = FreeImage_GetScanLine(img, y );
            std::string code = GetCode39(pScanLine, width);

            // check and append to list 
            if ( code.length() > 2 )
            {
                if ((  code.compare(0,1,"*") == 0 ) && (  code.compare( code.length() - 1,1,"*") == 0 ))
                {
                    bool find = false;
                    std::vector<std::string>::iterator it = result->begin();
                    while (( it != result->end()) && (!find ))
                    {
                        std::string val = *it;
                        find = ( val.compare(code) == 0 );
                        ++it;
                    }
                    if (!find) result->push_back(code);
                }
            }
        }

Last step, free and convert to a useable C# pointer.

    if ( rcfimg != NULL )
    {
        FreeImage_Unload(rcfimg);
        rcfimg = NULL;
    }
    if ( cfimg != NULL )
    {
        FreeImage_Unload(cfimg);
        cfimg = NULL;
    }
    if ( fimg != NULL) 
    {
        FreeImage_Unload(fimg);
        fimg = NULL;
    }
    FreeImage_DeInitialise();

    // Convert to SafeArray 
    SAFEARRAY * codesBarre = ConvertStringPtrVectorToSafeArray(result);

    return codesBarre;

The GetCode39 function parse line, point by point. It compares the previous color to the current color, on difference it stores the segment size of the constant color.

    // Detect black/white, white/black transition
    unsigned int size = 0;
    vector<unsigned int> * seg = new vector<unsigned int>();
    BYTE previous_color = (BYTE) (*pScanLine++);
    for(unsigned int x=1;x < width ; x++ )
    {
        BYTE current_color  = (BYTE) (*pScanLine++);
        if (((  previous_color > 196 ) && ( current_color < 64  )) ||
            (( previous_color < 64  ) && ( current_color > 196 )) )
        {
                seg->push_back(++size);
                size = 0;
        } else 
            size++;

        previous_color = current_color;
    }
    
    // Segment count
    unsigned int tcount =  seg->size();
    if ( tcount == 0 ) return std::string();

We need to know which is the most used segment's size. We use the Transition structure to store the segment size and the frequency.

    // count the frequency of segments size
    vector<Transition> * transitions = new vector<Transition>();
    for (unsigned int i = 0 ; i < tcount ; i++)
    {
        bool find = false;
        unsigned int val = (unsigned int) seg->at(i);
        // Rechercher
        for(vector<Transition>::iterator it = transitions->begin(); it != transitions->end(); ++it) 
        {
            if ( it->size == val ) 
            {
                it->freq++;
                find = true;
                break;
            }
        }

        // Append if not exist
        if ( !find ) {
            Transition t;
            t.freq = 1;
            t.size = val ;
            transitions->push_back(t);
        }
    }
    
    // Order by frequency
    sort(transitions->begin(), transitions->end(), SortTransitionsByFrequency );

In order to decode barcode, we have to find the large bar value and the fine one. We also compute an acceptable range for the segment size as the half size of the fine bar. (Limited to a hardcode value of 10 pixels.)

    // try to guess fine and large bar
    unsigned int max = 0;
    unsigned int min = width;
    double r ;
    for(vector<Transition>::iterator it = transitions->begin(); it != transitions->end(); ++it) 
    {
        if ( it->size > max ) max = it->size;
        if ( it->size < min ) min = it->size;

        r = 0.0f;
        if ( min != 0 )  r = max*1.0f / min*1.0f;
        if (( r < 3.2 ) && ( r > 1.8 )) break;
    }

    // Sanity check
    if (( r >= 3.2 ) && ( r <= 1.8 )) return std::string();

  // Acceptable range 
    int delta = min / 2;
    if ( delta > 10 ) delta = 10;

Decoding, have we found a barcode?

    // Decoding
    std::string code = std::string();
    unsigned int val = 0;
    unsigned int pos = 0;
    BOOL skip = false;
    for(unsigned int i = 0 ; i< tcount ; i++)
    {
        unsigned int read = (unsigned int) seg->at(i);
        if ( skip ) 
            skip = false ;
        else {
      // fine bar
            if (( read >= min - delta ) && ( read <= min + delta ))
                pos++;
        // large          
            else if (( read >= max - delta ) && ( read <= max + delta ))
              val += ( 1<< (8-pos++));
            else
            {
              // reset
              val = 0;
              pos = 0;
            }

            if ( pos == 9 ) {
                code.append(Decode(val));
                skip = true;
                val = 0;
                pos = 0;
            }
        }

    }

    return code;

The decode function translates byte to string:

    string Decode(unsigned int code)
    {
    std::string result = std::string();
    switch(code)
    {
        
        case 265 : 
            result.assign("A");
            break;
        ....
        
        default:
            break;
    }

    return result;
}

History

  • 06-2014 First release