File indexing completed on 2024-05-12 05:09:21

0001 /***************************************************************************
0002     Copyright (C) 2007-2009 Sebastian Held <sebastian.held@gmx.de>
0003  ***************************************************************************/
0004 
0005 /***************************************************************************
0006  *                                                                         *
0007  *  based on BaToo: http://www.vs.inf.ethz.ch/res/show.html?what=barcode   *
0008  *                                                                         *
0009  *   This program is free software; you can redistribute it and/or         *
0010  *   modify it under the terms of the GNU General Public License as        *
0011  *   published by the Free Software Foundation; either version 2 of        *
0012  *   the License or (at your option) version 3 or any later version        *
0013  *   accepted by the membership of KDE e.V. (or its successor approved     *
0014  *   by the membership of KDE e.V.), which shall act as a proxy            *
0015  *   defined in Section 14 of version 3 of the license.                    *
0016  *                                                                         *
0017  *   This program is distributed in the hope that it will be useful,       *
0018  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
0019  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
0020  *   GNU General Public License for more details.                          *
0021  *                                                                         *
0022  *   You should have received a copy of the GNU General Public License     *
0023  *   along with this program.  If not, see <http://www.gnu.org/licenses/>. *
0024  *                                                                         *
0025  ***************************************************************************/
0026 
0027 // includes code from https://www.linuxtv.org/downloads/v4l-dvb-apis-old/capture-example.html
0028 
0029 #include "barcode.h"
0030 
0031 #include <QImage>
0032 #include <QMutex>
0033 #include <QDebug>
0034 
0035 #include <stdlib.h>
0036 
0037 using barcodeRecognition::barcodeRecognitionThread;
0038 using barcodeRecognition::Barcode_EAN13;
0039 using barcodeRecognition::Decoder_EAN13;
0040 using barcodeRecognition::MatchMakerResult;
0041 
0042 barcodeRecognitionThread::barcodeRecognitionThread()
0043 {
0044   m_stop = false;
0045   m_barcode_v4l = new barcode_v4l();
0046 }
0047 barcodeRecognitionThread::~barcodeRecognitionThread()
0048 {
0049   delete m_barcode_v4l;
0050 }
0051 
0052 bool barcodeRecognitionThread::isWebcamAvailable()
0053 {
0054   return m_barcode_v4l->isOpen();
0055 }
0056 
0057 QSize barcodeRecognitionThread::getPreviewSize() const
0058 {
0059   return QSize( 320,240 );
0060 }
0061 
0062 void barcodeRecognitionThread::run()
0063 {
0064   bool stop;
0065   m_stop_mutex.lock();
0066   stop = m_stop;
0067   m_stop_mutex.unlock();
0068 
0069   if (!isWebcamAvailable())
0070     stop = true;
0071 
0072   Barcode_EAN13 old;
0073   while (!stop) {
0074     QImage img;
0075     m_barcode_img_mutex.lock();
0076     if (m_barcode_img.isNull())
0077       img = m_barcode_v4l->grab_one2();
0078     else {
0079       img = m_barcode_img;
0080       m_barcode_img = QImage();
0081     }
0082     m_barcode_img_mutex.unlock();
0083 
0084 // //DEBUG
0085 //     img.load( "/home/sebastian/black.png" );
0086 //     m_stop = true;
0087 // //DEBUG
0088 
0089     if (!img.isNull()) {
0090       QImage preview = img.scaled( 320, 240, Qt::KeepAspectRatio );
0091       emit gotImage( preview );
0092       Barcode_EAN13 barcode = recognize( img );
0093       if (barcode.isValid() && (old != barcode)) {
0094         emit recognized( barcode.toString() );
0095         old = barcode;
0096       }
0097     }
0098     msleep( 10 ); // reduce load
0099 
0100     m_stop_mutex.lock();
0101     stop = m_stop;
0102     m_stop_mutex.unlock();
0103   }
0104 }
0105 
0106 void barcodeRecognitionThread::stop()
0107 {
0108   // attention! This function is called from GUI context
0109   m_stop_mutex.lock();
0110   m_stop = true;
0111   m_stop_mutex.unlock();
0112 }
0113 
0114 void barcodeRecognitionThread::recognizeBarcode( QImage img )
0115 {
0116   // attention! This function is called from GUI context
0117   m_barcode_img_mutex.lock();
0118   m_barcode_img = img;
0119   m_barcode_img_mutex.unlock();
0120 }
0121 
0122 Barcode_EAN13 barcodeRecognitionThread::recognize( QImage img )
0123 {
0124   // PARAMETERS:
0125   int amount_scanlines = 30;
0126   int w = img.width();
0127   int h = img.height();
0128 
0129   // the array which will contain the result:
0130   QVector< QVector<int> > numbers( amount_scanlines, QVector<int>(13,-1) ); // no init in java source!!!!!!!!!
0131 
0132   // generate and initialize the array that will contain all detected
0133   // digits at a specific code position:
0134   int possible_numbers[10][13][2];
0135   for (int i = 0; i < 10; i++) {
0136     for (int j = 0; j < 13; j++) {
0137       possible_numbers[i][j][0] = -1;
0138       possible_numbers[i][j][1] = 0;
0139     }
0140   }
0141 
0142   // try to detect the barcode along scanlines:
0143   for (int i = 0; i < amount_scanlines; i++) {
0144     int x1 = 0;
0145     int y = (h / amount_scanlines) * i;
0146     int x2 = w - 1;
0147 
0148     // try to recognize a barcode along that path:
0149     Barcode_EAN13 ean13_code = recognizeCode( img, x1, x2, y );
0150     numbers[i] = ean13_code.getNumbers();
0151 
0152     if (ean13_code.isValid()) {
0153       // add the recognized digits to the array of possible numbers:
0154       addNumberToPossibleNumbers( numbers[i], possible_numbers, true );
0155     } else {
0156       numbers[i] = ean13_code.getNumbers();
0157       // add the recognized digits to the array of possible numbers:
0158       addNumberToPossibleNumbers(numbers[i], possible_numbers, false);
0159     }
0160 
0161 #ifdef BarcodeDecoder_DEBUG
0162     // show the information that has been recognized along the scanline:
0163     qDebug( "Scanline %i result: %s\n", i, ean13_code.toString().latin1() );
0164 #endif
0165   }
0166 
0167   // sort the detected digits at each code position, in accordance to the
0168   // amount of their detection:
0169   sortDigits(possible_numbers);
0170 
0171 #ifdef BarcodeDecoder_DEBUG
0172   fprintf( stderr, "detected digits:\n" );
0173   printArray( possible_numbers, 0 );
0174   fprintf( stderr, "# of their occurrence:\n" );
0175   printArray( possible_numbers, 1 );
0176 #endif
0177 
0178   // get the most likely barcode:
0179   Barcode_EAN13 code = extractBarcode(possible_numbers);
0180 
0181   return code;
0182 }
0183 
0184 void barcodeRecognitionThread::printArray( int array[10][13][2], int level )
0185 {
0186   for (int i = 0; i < 10; i++) {
0187     QString temp;
0188     temp = QString::number( i ) + QLatin1String(" :   ");
0189     for (int j = 0; j < 13; j++) {
0190       if (array[i][j][level] == -1)
0191         temp += QLatin1String("x  ");
0192       else
0193       temp += QString::number( array[i][j][level] ) + QLatin1String("  ");
0194     }
0195   qDebug() << temp;
0196   }
0197 }
0198 
0199 barcodeRecognition::Barcode_EAN13 barcodeRecognitionThread::recognizeCode( QImage img, int x1, int x2, int y )
0200 {
0201   QVector<QRgb> raw_path(x2-x1+1);
0202   for (int x=x1; x<=x2; x++)
0203     raw_path[x-x1] = img.pixel(x,y);
0204   // convert the given path into a string of black and white pixels:
0205   QVector<int> string = transformPathToBW( raw_path );
0206 
0207   // convert the string of black&white pixels into a list, containing
0208   // information about the black and white fields
0209   // first indes = field nr.
0210   // second index: 0 = color of the field
0211   //               1 = field length
0212   QVector< QVector<int> > fields = extractFieldInformation( string );
0213 
0214   // try to recognize a EAN13 code:
0215   Barcode_EAN13 barcode = Decoder_EAN13::recognize( fields );
0216 
0217   return barcode;
0218 }
0219 
0220 void barcodeRecognitionThread::addNumberToPossibleNumbers( QVector<int> number, int possible_numbers[10][13][2], bool correct_code )
0221 {
0222   int i;
0223   bool digit_contained;
0224   for (int j = 0; j < 13; j++) {
0225     if (number[j] >= 0) {
0226       i = 0;
0227       digit_contained = false;
0228       while ((i < 10) && (possible_numbers[i][j][0] >= 0)) {
0229         if (possible_numbers[i][j][0] == number[j]) {
0230           digit_contained = true;
0231           if (correct_code)
0232             possible_numbers[i][j][1] = possible_numbers[i][j][1] + 100;
0233           else
0234             possible_numbers[i][j][1]++;
0235           break;
0236         }
0237         i++;
0238       }
0239       if ((i < 10) && (!digit_contained)) {
0240         // add new digit:
0241         possible_numbers[i][j][0] = number[j];
0242         if (correct_code)
0243           possible_numbers[i][j][1] = possible_numbers[i][j][1] + 100;
0244         else
0245           possible_numbers[i][j][1]++;
0246       }
0247     }
0248   }
0249 }
0250 
0251 void barcodeRecognitionThread::sortDigits( int possible_numbers[10][13][2] )
0252 {
0253   int i;
0254   int temp_value;
0255   int temp_occurrence;
0256   bool changes;
0257 
0258   for (int j = 0; j < 13; j++) {
0259     i = 1;
0260     changes = false;
0261     while (true) {
0262       if ((possible_numbers[i - 1][j][0] >= 0) && (possible_numbers[i][j][0] >= 0)) {
0263         if (possible_numbers[i - 1][j][1] < possible_numbers[i][j][1]) {
0264           temp_value = possible_numbers[i - 1][j][0];
0265           temp_occurrence = possible_numbers[i - 1][j][1];
0266           possible_numbers[i - 1][j][0] = possible_numbers[i][j][0];
0267           possible_numbers[i - 1][j][1] = possible_numbers[i][j][1];
0268           possible_numbers[i][j][0] = temp_value;
0269           possible_numbers[i][j][1] = temp_occurrence;
0270 
0271           changes = true;
0272         }
0273       }
0274 
0275       if ((i >= 9) || (possible_numbers[i][j][0] < 0)) {
0276         if (!changes)
0277           break;
0278         else {
0279           i = 1;
0280           changes = false;
0281         }
0282       } else
0283         i++;
0284     }
0285   }
0286 }
0287 
0288 Barcode_EAN13 barcodeRecognitionThread::extractBarcode( int possible_numbers[10][13][2] )
0289 {
0290   // create and initialize the temporary variables:
0291   QVector<int> temp_code(13);
0292   for (int i = 0; i < 13; i++)
0293     temp_code[i] = possible_numbers[0][i][0];
0294 
0295 #ifdef Barcode_DEBUG
0296   fprintf( stderr, "barcodeRecognitionThread::extractBarcode(): " );
0297   for (int i=0; i<13; i++)
0298     fprintf( stderr, "%i", temp_code[i] );
0299   fprintf( stderr, "\n" );
0300 #endif
0301 
0302   return Barcode_EAN13(temp_code);
0303 }
0304 
0305 Barcode_EAN13 barcodeRecognitionThread::detectValidBarcode ( int possible_numbers[10][13][2], int max_amount_of_considered_codes )
0306 {
0307   // create and initialize the temporary variables:
0308   QVector<int> temp_code(13);
0309   for ( int i = 0; i < 13; i++ )
0310     temp_code[i] = possible_numbers[0][i][0];
0311 
0312   int alternative_amount = 0;
0313 
0314   QVector<int> counter( 13 ); // no init in java source!!!
0315   int counter_nr = 11;
0316 
0317   // check if there is at least one complete code present:
0318   for ( int i = 0; i < 13; i++ ) {
0319     // exit and return the "most likely" code parts:
0320     if ( temp_code[i] < 0 )
0321       return Barcode_EAN13( temp_code );
0322   }
0323 
0324   // if there is at least one complete node, try to detect a valid barcode:
0325   while ( alternative_amount < max_amount_of_considered_codes ) {
0326     // fill the temporary code array with one possible version:
0327     for ( int i = 0; i < 13; i++ )
0328       temp_code[i] = possible_numbers[counter[i]][i][0];
0329 
0330     alternative_amount++;
0331 
0332     // check if this version represents a valid code:
0333     if (isValid( temp_code ))
0334       return Barcode_EAN13( temp_code );
0335 
0336     // increment the counters:
0337     if ( ( counter[counter_nr] < 9 ) && ( possible_numbers[counter[counter_nr] + 1][counter_nr][0] >= 0 ) ) {
0338       // increment the actual counter.
0339       counter[counter_nr]++;
0340     } else {
0341       // check if we have reached the end and no valid barcode has been found:
0342       if ( counter_nr == 1 ) {
0343         // exit and return the "most likely" code parts:
0344         for ( int i = 0; i < 13; i++ )
0345           temp_code[i] = possible_numbers[0][i][0];
0346         return Barcode_EAN13( temp_code );
0347       } else {
0348         // reset the actual counter and increment the next one(s):
0349         counter[counter_nr] = 0;
0350 
0351         while ( true ) {
0352           if ( counter_nr > 2 )
0353             counter_nr--;
0354           else {
0355             for ( int i = 0; i < 13; i++ )
0356               temp_code[i] = possible_numbers[0][i][0];
0357             return Barcode_EAN13( temp_code );
0358           }
0359           if ( counter[counter_nr] < 9 ) {
0360             counter[counter_nr]++;
0361             if ( possible_numbers[counter[counter_nr]][counter_nr][0] < 0 )
0362               counter[counter_nr] = 0;
0363             else
0364               break;
0365           } else
0366             counter[counter_nr] = 0;
0367         }
0368         counter_nr = 12;
0369       }
0370     }
0371   }
0372 
0373   for ( int i = 0; i < 13; i++ )
0374     temp_code[i] = possible_numbers[0][i][0];
0375   return Barcode_EAN13( temp_code );
0376 }
0377 
0378 bool barcodeRecognitionThread::isValid( int numbers[13] )
0379 {
0380   QVector<int> temp(13);
0381   for (int i=0; i<13; i++)
0382     temp[i] = numbers[i];
0383   return isValid( temp );
0384 }
0385 bool barcodeRecognitionThread::isValid( QVector<int> numbers )
0386 {
0387   Q_ASSERT( numbers.count() == 13 );
0388   // calculate the checksum of the barcode:
0389   int sum1 = numbers[0] + numbers[2] + numbers[4] + numbers[6] + numbers[8] + numbers[10];
0390   int sum2 = 3 * (numbers[1] + numbers[3] + numbers[5] + numbers[7] + numbers[9] + numbers[11]);
0391   int checksum_value = sum1 + sum2;
0392   int checksum_digit = 10 - (checksum_value % 10);
0393   if (checksum_digit == 10)
0394     checksum_digit = 0;
0395 
0396 #ifdef Barcode_DEBUG
0397   fprintf( stderr, "barcodeRecognitionThread::isValid(): " );
0398   for (int i=0; i<13; i++)
0399     fprintf( stderr, "%i", numbers[i] );
0400   fprintf( stderr, "\n" );
0401 #endif
0402 
0403   return (numbers[12] == checksum_digit);
0404 }
0405 
0406 QVector<int> barcodeRecognitionThread::transformPathToBW( QVector<QRgb> line )
0407 {
0408   int w = line.count();
0409   QVector<int> bw_line(w,0);
0410   bw_line[0] = 255;
0411 
0412   // create greyscale values:
0413   QVector<int> grey_line(w,0);
0414   int average_illumination = 0;
0415   for (int x = 0; x < w; x++) {
0416     grey_line[x] = (qRed(line.at(x)) + qGreen(line.at(x)) + qBlue(line.at(x))) / 3;
0417     average_illumination = average_illumination + grey_line[x];
0418   }
0419   average_illumination = average_illumination / w;
0420 
0421   // perform the binarization:
0422   int range = w / 20;
0423 
0424   // temp values:
0425   int moving_sum;
0426   int moving_average;
0427   int v1_index = -range + 1;
0428   int v2_index = range;
0429   int v1 = grey_line[0];
0430   int v2 = grey_line[range];
0431   int current_value;
0432   int comparison_value;
0433 
0434   // initialize the moving sum:
0435   moving_sum = grey_line[0] * range;
0436   for (int i = 0; i < range; i++)
0437     moving_sum = moving_sum + grey_line[i];
0438 
0439   // apply the adaptive thresholding algorithm:
0440   for (int i = 1; i < w - 1; i++) {
0441     if (v1_index > 0) v1 = grey_line[v1_index];
0442     if (v2_index < w) v2 = grey_line[v2_index];
0443     else v2 = grey_line[w - 1];
0444     moving_sum = moving_sum - v1 + v2;
0445     moving_average = moving_sum / (range << 1);
0446     v1_index++;
0447     v2_index++;
0448 
0449     current_value = (grey_line[i - 1] + grey_line[i]) >> 1;
0450 
0451     // decide if the current pixel should be black or white:
0452     comparison_value = (3 * moving_average + average_illumination) >> 2;
0453     if ((current_value < comparison_value - 3)) bw_line[i] = 0;
0454     else bw_line[i] = 255;
0455   }
0456 
0457   // filter the values: (remove too small fields)
0458 
0459   if (w >= 640) {
0460     for (int x = 1; x < w - 1; x++) {
0461       if ((bw_line[x] != bw_line[x - 1]) && (bw_line[x] != bw_line[x + 1])) bw_line[x] = bw_line[x - 1];
0462     }
0463   }
0464 
0465   QVector<int> ret(w,0);
0466   for (int i=0; i<w; i++)
0467     ret[i] = bw_line[i];
0468 
0469 #ifdef Barcode_DEBUG
0470   fprintf( stderr, "barcodeRecognitionThread::transformPathToBW(): " );
0471   for (int i=0; i<ret.count(); i++)
0472     if (bw_line[i] == 0)
0473       fprintf( stderr, "0" );
0474     else
0475       fprintf( stderr, "#" );
0476   fprintf( stderr, "\n" );
0477 #endif
0478 
0479   return ret;
0480 }
0481 
0482 QVector< QVector<int> > barcodeRecognitionThread::extractFieldInformation( QVector<int> string )
0483 {
0484   QVector< QVector<int> > temp_fields( string.count(), QVector<int>(2,0) );
0485 
0486   if (string.count() == 0)
0487     return QVector< QVector<int> >();
0488 
0489   int field_counter = 0;
0490   int last_value = string.at(0);
0491   int last_fields = 1;
0492   for (int i = 1; i < string.size(); i++) {
0493     if ((string.at(i) == last_value) && (i < string.size() - 1)) {
0494       last_fields++;
0495     } else {
0496       // create new field entry:
0497       temp_fields[field_counter][0] = last_value;
0498       temp_fields[field_counter][1] = last_fields;
0499 
0500       last_value = string.at(i);
0501       last_fields = 0;
0502       field_counter++;
0503     }
0504   }
0505 
0506   temp_fields.resize( field_counter );
0507 
0508 #ifdef Barcode_DEBUG
0509   fprintf( stderr, "barcodeRecognitionThread::extractFieldInformation(): " );
0510   for (int i=0; i<temp_fields.count(); i++)
0511     fprintf( stderr, "%i,%i ", temp_fields.at(i).at(0), temp_fields.at(i).at(1) );
0512   fprintf( stderr, "\n" );
0513 #endif
0514 
0515   return temp_fields;
0516 }
0517 
0518 //ok
0519 Barcode_EAN13::Barcode_EAN13() : m_numbers(13,-1)
0520 {
0521   m_null = true;
0522 }
0523 
0524 //ok
0525 Barcode_EAN13::Barcode_EAN13( QVector<int> code )
0526 {
0527   setCode( code );
0528 }
0529 
0530 //ok
0531 void Barcode_EAN13::setCode( QVector<int> code )
0532 {
0533   if (code.count() != 13) {
0534     m_numbers.clear();
0535     m_numbers.insert(0,13,-1);
0536     m_null = true;
0537     return;
0538   }
0539   m_numbers = code;
0540   m_null = false;
0541 }
0542 
0543 //ok
0544 bool Barcode_EAN13::isValid() const
0545 {
0546   if (m_null)
0547     return false;
0548 
0549   for (int i = 0; i < 13; i++)
0550     if ((m_numbers[i] < 0) || (m_numbers[i] > 9))
0551       return false;
0552 
0553   // calculate the checksum of the barcode:
0554   int sum1 = m_numbers[0] + m_numbers[2] + m_numbers[4] + m_numbers[6] + m_numbers[8] + m_numbers[10];
0555   int sum2 = 3 * (m_numbers[1] + m_numbers[3] + m_numbers[5] + m_numbers[7] + m_numbers[9] + m_numbers[11]);
0556   int checksum_value = sum1 + sum2;
0557   int checksum_digit = 10 - (checksum_value % 10);
0558   if (checksum_digit == 10)
0559     checksum_digit = 0;
0560 
0561   return (m_numbers[12] == checksum_digit);
0562 }
0563 
0564 //ok
0565 QVector<int> Barcode_EAN13::getNumbers() const
0566 {
0567   return m_numbers;
0568 }
0569 
0570 //ok
0571 QString Barcode_EAN13::toString() const
0572 {
0573   QString s;
0574   for (int i = 0; i < 13; i++)
0575     if ((m_numbers[i] >= 0) && (m_numbers[i] <= 9))
0576       s += QString::number(m_numbers[i]);
0577     else
0578       s += QChar::fromLatin1('?');
0579   return s;
0580 }
0581 
0582 //ok
0583 bool Barcode_EAN13::operator!= ( const Barcode_EAN13 &code )
0584 {
0585   if (m_null != code.m_null)
0586     return true;
0587   if (!m_null)
0588     for (int i=0; i<13; i++)
0589       if (m_numbers[i] != code.m_numbers[i])
0590         return true;
0591   return false;
0592 }
0593 
0594 //ok
0595 Barcode_EAN13 Decoder_EAN13::recognize( QVector< QVector<int> > fields )
0596 {
0597   // try to extract the encoded information from the field series:
0598   QVector<int> numbers = decode( fields, 0, fields.count() );
0599   Barcode_EAN13 barcode( numbers );
0600 
0601   // return the results:
0602   return barcode;
0603 }
0604 
0605 QVector<int> Decoder_EAN13::decode( QVector< QVector<int> > fields, int start_i, int end_i )
0606 {
0607   // determine the length of the path in pixels
0608   int length = 0;
0609   for (int i = 0; i < fields.size(); i++)
0610     length += fields.at(i).at(1);
0611 
0612   // set the parameters accordingly:
0613   int max_start_sentry_bar_differences;
0614   int max_unit_length;
0615   int min_unit_length;
0616 
0617   if (length <= 800) {
0618       max_start_sentry_bar_differences = 6;
0619       max_unit_length = 10;
0620       min_unit_length = 1;
0621   } else {
0622       max_start_sentry_bar_differences = 30;
0623       max_unit_length = 50;
0624       min_unit_length = 1;
0625   }
0626 
0627   // consistency checks:
0628   if (fields.count() <= 0)
0629     return QVector<int>();
0630   if (start_i > end_i - 3)
0631     return QVector<int>();
0632   if (end_i - start_i < 30)
0633     return QVector<int>(); // (just a rough value)
0634 
0635   // relevant indexes:
0636   int start_sentinel_i;
0637   int end_sentinel_i;
0638   int left_numbers_i;
0639   int middle_guard_i;
0640   int right_numbers_i;
0641 
0642   // results:
0643   QVector<int> numbers( 13, -1 ); // the java source does no initialization
0644 
0645   // determine the relevant positions:
0646 
0647   // Try to detect the start sentinel (a small black-white-black serie):
0648   start_sentinel_i = -1;
0649   for (int i = start_i; i < end_i - 56; i++) {
0650     if (fields[i][0] == 0) {
0651       if ((fields[i][1] >= min_unit_length) && (fields[i][1] <= max_unit_length)) {
0652         if ((qAbs(fields[i][1] - fields[i + 1][1]) <= max_start_sentry_bar_differences)
0653                   && (qAbs(fields[i][1] - fields[i + 2][1]) <= max_start_sentry_bar_differences) && (fields[i + 3][1] < fields[i][1] << 3)) {
0654           start_sentinel_i = i;
0655           break;
0656         }
0657       }
0658     }
0659   }
0660 
0661 #ifdef Decoder_EAN13_DEBUG
0662   fprintf( stderr, "start_sentinal_index: %i\n", start_sentinel_i );
0663 #endif
0664 
0665   if (start_sentinel_i < 0)
0666     return QVector<int>();
0667 
0668   // calculate the other positions:
0669   left_numbers_i = start_sentinel_i + 3;
0670   middle_guard_i = left_numbers_i + 6 * 4;
0671   right_numbers_i = middle_guard_i + 5;
0672   end_sentinel_i = right_numbers_i + 6 * 4;
0673 
0674   if (end_sentinel_i + 3 > end_i)
0675     return QVector<int>();
0676 
0677   QVector< QVector<int> > current_number_field( 4, QVector<int>(2,0) );
0678 
0679   if (left_numbers_i + 1 > end_i)
0680     return QVector<int>();
0681 
0682   // test the side from which we are reading the barcode:
0683   for (int j = 0; j < 4; j++) {
0684     current_number_field[j][0] = fields[left_numbers_i + j][0];
0685     current_number_field[j][1] = fields[left_numbers_i + j][1];
0686   }
0687   MatchMakerResult matchMakerResult = recognizeNumber( current_number_field, BOTH_TABLES );
0688 
0689   if (matchMakerResult.isEven()) {
0690     // we are reading the barcode from the back side:
0691 
0692     // use the already obtained information:
0693     numbers[12] = matchMakerResult.getDigit();
0694 
0695     // try to recognize the "right" numbers:
0696     int counter = 11;
0697     for (int i = left_numbers_i + 4; i < left_numbers_i + 24; i = i + 4) {
0698             for (int j = 0; j < 4; j++) {
0699                     current_number_field[j][0] = fields[i + j][0];
0700                     current_number_field[j][1] = fields[i + j][1];
0701             }
0702             matchMakerResult = recognizeNumber(current_number_field, EVEN_TABLE);
0703             numbers[counter] = matchMakerResult.getDigit();
0704             counter--;
0705     }
0706 
0707     bool parity_pattern[6];  // true = even, false = odd
0708 
0709     //(counter has now the value 6)
0710 
0711     // try to recognize the "left" numbers:
0712     for (int i = right_numbers_i; i < right_numbers_i + 24; i = i + 4) {
0713       for (int j = 0; j < 4; j++) {
0714         current_number_field[j][0] = fields[i + j][0];
0715         current_number_field[j][1] = fields[i + j][1];
0716       }
0717       matchMakerResult = recognizeNumber(current_number_field, BOTH_TABLES);
0718       numbers[counter] = matchMakerResult.getDigit();
0719       parity_pattern[counter-1] = !matchMakerResult.isEven();
0720       counter--;
0721     }
0722 
0723     // try to determine the system code:
0724     matchMakerResult = recognizeSystemCode(parity_pattern);
0725     numbers[0] = matchMakerResult.getDigit();
0726   } else {
0727     // we are reading the barcode from the "correct" side:
0728 
0729     bool parity_pattern[6];  // true = even, false = odd
0730 
0731     // use the already obtained information:
0732     numbers[1] = matchMakerResult.getDigit();
0733     parity_pattern[0] = matchMakerResult.isEven();
0734 
0735     // try to recognize the left numbers:
0736     int counter = 2;
0737     for (int i = left_numbers_i + 4; i < left_numbers_i + 24; i = i + 4) {
0738       for (int j = 0; j < 4; j++) {
0739         current_number_field[j][0] = fields[i + j][0];
0740         current_number_field[j][1] = fields[i + j][1];
0741       }
0742       matchMakerResult = recognizeNumber(current_number_field, BOTH_TABLES);
0743       numbers[counter] = matchMakerResult.getDigit();
0744       parity_pattern[counter-1] = matchMakerResult.isEven();
0745       counter++;
0746     }
0747 
0748     // try to determine the system code:
0749     matchMakerResult = recognizeSystemCode(parity_pattern);
0750     numbers[0] = matchMakerResult.getDigit();
0751 
0752     // try to recognize the right numbers:
0753     counter = 0;
0754     for (int i = right_numbers_i; i < right_numbers_i + 24; i = i + 4) {
0755       for (int j = 0; j < 4; j++) {
0756         current_number_field[j][0] = fields[i + j][0];
0757         current_number_field[j][1] = fields[i + j][1];
0758       }
0759       matchMakerResult = recognizeNumber(current_number_field, ODD_TABLE);
0760       numbers[counter + 7] = matchMakerResult.getDigit();
0761       counter++;
0762     }
0763   }
0764 
0765   return numbers;
0766 }
0767 
0768 MatchMakerResult Decoder_EAN13::recognizeNumber( QVector< QVector<int> > fields, int code_table_to_use)
0769 {
0770   // convert the pixel lengths of the four black&white fields into
0771   // normed values that have together a length of 70;
0772   int pixel_sum = fields[0][1] + fields[1][1] + fields[2][1] + fields[3][1];
0773   int b[4];
0774   for (int i = 0; i < 4; i++) {
0775     b[i] = ::round((((float) fields[i][1]) / ((float) pixel_sum)) * 70);
0776   }
0777 
0778 #ifdef Decoder_EAN13_DEBUG
0779   fprintf( stderr, "Recognize Number (code table to use: %i):\n", code_table_to_use );
0780   fprintf( stderr, "lengths: %i %i %i %i\n", fields[0][1], fields[1][1], fields[2][1], fields[3][1] );
0781   fprintf( stderr, "normed lengths: %i %i %i %i\n", b[0], b[1], b[2], b[3] );
0782 #endif
0783 
0784   // try to detect the digit that is encoded by the set of four normed bar lengths:
0785   int max_difference_for_acceptance = 60;
0786   int temp;
0787 
0788   int even_min_difference = 100000;
0789   int even_min_difference_index = 0;
0790   int odd_min_difference = 100000;
0791   int odd_min_difference_index = 0;
0792 
0793   if ((code_table_to_use == BOTH_TABLES)||(code_table_to_use == EVEN_TABLE)) {
0794     QVector<int> even_differences(10,0);
0795 
0796     for (int i = 0; i < 10; i++) {
0797       for (int j = 0; j < 4; j++) {
0798         // calculate the differences in the even group:
0799         temp = b[j] - code_even[i][j];
0800         if (temp < 0)
0801           even_differences[i] = even_differences[i] + ((-temp) << 1);
0802         else
0803           even_differences[i] = even_differences[i] + (temp << 1);
0804       }
0805       if (even_differences[i] < even_min_difference) {
0806         even_min_difference = even_differences[i];
0807         even_min_difference_index = i;
0808       }
0809     }
0810   }
0811 
0812   if ((code_table_to_use == BOTH_TABLES) || (code_table_to_use == ODD_TABLE)) {
0813     QVector<int> odd_differences(10,0);
0814 
0815     for (int i = 0; i < 10; i++) {
0816       for (int j = 0; j < 4; j++) {
0817         // calculate the differences in the odd group:
0818         temp = b[j] - code_odd[i][j];
0819         if (temp < 0)
0820           odd_differences[i] = odd_differences[i] + ((-temp) << 1);
0821         else
0822           odd_differences[i] = odd_differences[i] + (temp << 1);
0823       }
0824       if (odd_differences[i] < odd_min_difference) {
0825         odd_min_difference = odd_differences[i];
0826         odd_min_difference_index = i;
0827       }
0828     }
0829   }
0830 
0831   // select the digit and parity with the lowest difference to the found pattern:
0832   if (even_min_difference <= odd_min_difference) {
0833     if (even_min_difference < max_difference_for_acceptance)
0834       return MatchMakerResult( true, even_min_difference_index );
0835   } else {
0836     if (odd_min_difference < max_difference_for_acceptance)
0837       return MatchMakerResult( false, odd_min_difference_index );
0838   }
0839 
0840   return MatchMakerResult( false, -1 );
0841 }
0842 
0843 MatchMakerResult Decoder_EAN13::recognizeSystemCode( bool parity_pattern[6] )
0844 {
0845   // search for a fitting parity pattern:
0846   bool fits = false;
0847   for (int i = 0; i < 10; i++) {
0848     fits = true;
0849     for (int j = 0; j < 6; j++) {
0850       if (parity_pattern_list[i][j] != parity_pattern[j]) {
0851         fits = false;
0852         break;
0853       }
0854     }
0855     if (fits)
0856       return MatchMakerResult( false, i );
0857   }
0858 
0859   return MatchMakerResult( false, -1 );
0860 }
0861 
0862 //ok
0863 MatchMakerResult::MatchMakerResult( bool even, int digit )
0864 {
0865   m_even = even;
0866   m_digit = digit;
0867 }