summaryrefslogtreecommitdiffstats
path: root/src/barcode
diff options
context:
space:
mode:
authortpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2010-03-01 19:17:32 +0000
committertpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2010-03-01 19:17:32 +0000
commite38d2351b83fa65c66ccde443777647ef5cb6cff (patch)
tree1897fc20e9f73a81c520a5b9f76f8ed042124883 /src/barcode
downloadtellico-e38d2351b83fa65c66ccde443777647ef5cb6cff.tar.gz
tellico-e38d2351b83fa65c66ccde443777647ef5cb6cff.zip
Added KDE3 version of Tellico
git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/applications/tellico@1097620 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'src/barcode')
-rw-r--r--src/barcode/Makefile.am14
-rw-r--r--src/barcode/barcode.cpp877
-rw-r--r--src/barcode/barcode.h135
-rw-r--r--src/barcode/barcode_v4l.cpp538
-rw-r--r--src/barcode/barcode_v4l.h184
5 files changed, 1748 insertions, 0 deletions
diff --git a/src/barcode/Makefile.am b/src/barcode/Makefile.am
new file mode 100644
index 0000000..df8f8e2
--- /dev/null
+++ b/src/barcode/Makefile.am
@@ -0,0 +1,14 @@
+AM_CPPFLAGS = $(all_includes)
+
+noinst_LIBRARIES = libbarcode.a
+libbarcode_a_SOURCES = barcode.cpp barcode_v4l.cpp
+
+libbarcode_a_METASOURCES = AUTO
+
+KDE_OPTIONS = noautodist
+
+EXTRA_DIST = \
+barcode.h barcode.cpp \
+barcode_v4l.h barcode_v4l.cpp
+
+CLEANFILES = *~ *.loT
diff --git a/src/barcode/barcode.cpp b/src/barcode/barcode.cpp
new file mode 100644
index 0000000..2427f4e
--- /dev/null
+++ b/src/barcode/barcode.cpp
@@ -0,0 +1,877 @@
+/***************************************************************************
+ copyright : (C) 2007 by Sebastian Held
+ email : sebastian.held@gmx.de
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * ### based on BaToo: http://people.inf.ethz.ch/adelmanr/batoo/ ### *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of version 2 of the GNU General Public License as *
+ * published by the Free Software Foundation; *
+ * *
+ ***************************************************************************/
+
+// includes code from http://v4l2spec.bytesex.org/spec/a16323.htm
+
+#include <qimage.h>
+#include <qmutex.h>
+#include "barcode.h"
+
+#include <stdlib.h>
+
+using barcodeRecognition::barcodeRecognitionThread;
+using barcodeRecognition::Barcode_EAN13;
+using barcodeRecognition::Decoder_EAN13;
+using barcodeRecognition::MatchMakerResult;
+
+barcodeRecognitionThread::barcodeRecognitionThread()
+{
+ m_stop = false;
+ m_barcode_v4l = new barcode_v4l();
+}
+barcodeRecognitionThread::~barcodeRecognitionThread()
+{
+ delete m_barcode_v4l;
+}
+
+bool barcodeRecognitionThread::isWebcamAvailable()
+{
+ return m_barcode_v4l->isOpen();
+}
+
+void barcodeRecognitionThread::run()
+{
+ bool stop;
+ m_stop_mutex.lock();
+ stop = m_stop;
+ m_stop_mutex.unlock();
+
+ if (!isWebcamAvailable())
+ stop = true;
+
+ Barcode_EAN13 old;
+ while (!stop) {
+ QImage img;
+ m_barcode_img_mutex.lock();
+ if (m_barcode_img.isNull())
+ img = m_barcode_v4l->grab_one2();
+ else {
+ img = m_barcode_img;
+ m_barcode_img = QImage();
+ }
+ m_barcode_img_mutex.unlock();
+
+// //DEBUG
+// img.load( "/home/sebastian/black.png" );
+// m_stop = true;
+// //DEBUG
+
+ if (!img.isNull()) {
+ QImage preview = img.scale( 320, 240, QImage::ScaleMin );
+ emit gotImage( preview );
+ Barcode_EAN13 barcode = recognize( img );
+ if (barcode.isValid() && (old != barcode)) {
+ emit recognized( barcode.toString() );
+ old = barcode;
+ }
+ }
+ msleep( 10 ); // reduce load
+
+ m_stop_mutex.lock();
+ stop = m_stop;
+ m_stop_mutex.unlock();
+ }
+}
+
+void barcodeRecognitionThread::stop()
+{
+ // attention! This function is called from GUI context
+ m_stop_mutex.lock();
+ m_stop = true;
+ m_stop_mutex.unlock();
+}
+
+
+void barcodeRecognitionThread::recognizeBarcode( QImage img )
+{
+ // attention! This function is called from GUI context
+ m_barcode_img_mutex.lock();
+ m_barcode_img = img;
+ m_barcode_img_mutex.unlock();
+}
+
+Barcode_EAN13 barcodeRecognitionThread::recognize( QImage img )
+{
+ // PARAMETERS:
+ int amount_scanlines = 30;
+ int w = img.width();
+ int h = img.height();
+
+ // the array which will contain the result:
+ QValueVector< QValueVector<int> > numbers( amount_scanlines, QValueVector<int>(13,-1) ); // no init in java source!!!!!!!!!
+
+ // generate and initialize the array that will contain all detected
+ // digits at a specific code position:
+ int possible_numbers[10][13][2];
+ for (int i = 0; i < 10; i++) {
+ for (int j = 0; j < 13; j++) {
+ possible_numbers[i][j][0] = -1;
+ possible_numbers[i][j][1] = 0;
+ }
+ }
+
+ int successfull_lines = 0;
+
+ // try to detect the barcode along scanlines:
+ for (int i = 0; i < amount_scanlines; i++) {
+ int x1 = 0;
+ int y = (h / amount_scanlines) * i;
+ int x2 = w - 1;
+
+ // try to recognize a barcode along that path:
+ Barcode_EAN13 ean13_code = recognizeCode( img, x1, x2, y );
+ numbers[i] = ean13_code.getNumbers();
+
+ if (ean13_code.isValid()) {
+ successfull_lines++;
+ // add the recognized digits to the array of possible numbers:
+ addNumberToPossibleNumbers( numbers[i], possible_numbers, true );
+ } else {
+ numbers[i] = ean13_code.getNumbers();
+ // add the recognized digits to the array of possible numbers:
+ addNumberToPossibleNumbers(numbers[i], possible_numbers, false);
+ }
+
+#ifdef BarcodeDecoder_DEBUG
+ // show the information that has been recognized along the scanline:
+ qDebug( "Scanline %i result: %s\n", i, ean13_code.toString().latin1() );
+#endif
+ }
+
+ // sort the detected digits at each code position, in accordance to the
+ // amount of their detection:
+ sortDigits(possible_numbers);
+
+#ifdef BarcodeDecoder_DEBUG
+ fprintf( stderr, "detected digits:\n" );
+ printArray( possible_numbers, 0 );
+ fprintf( stderr, "# of their occurence:\n" );
+ printArray( possible_numbers, 1 );
+#endif
+
+ // get the most likely barcode:
+ Barcode_EAN13 code = extractBarcode(possible_numbers);
+
+ return code;
+}
+
+void barcodeRecognitionThread::printArray( int array[10][13][2], int level )
+{
+ for (int i = 0; i < 10; i++) {
+ QCString temp;
+ temp.setNum( i );
+ fprintf( stderr, temp + " : " );
+ for (int j = 0; j < 13; j++) {
+ if (array[i][j][level] == -1)
+ fprintf( stderr, "x " );
+ else {
+ QCString temp;
+ temp.setNum( array[i][j][level] );
+ fprintf( stderr, temp + " " );
+ }
+ }
+ fprintf( stderr, "\n" );
+ }
+}
+
+barcodeRecognition::Barcode_EAN13 barcodeRecognitionThread::recognizeCode( QImage img, int x1, int x2, int y )
+{
+ QValueVector<QRgb> raw_path(x2-x1+1);
+ for (int x=x1; x<=x2; x++)
+ raw_path[x-x1] = img.pixel(x,y);
+ // convert the given path into a string of black and white pixels:
+ QValueVector<int> string = transformPathToBW( raw_path );
+
+ // convert the string of black&white pixels into a list, containing
+ // information about the black and white fields
+ // first indes = field nr.
+ // second index: 0 = color of the field
+ // 1 = field length
+ QValueVector< QValueVector<int> > fields = extractFieldInformation( string );
+
+ // try to recognize a EAN13 code:
+ Barcode_EAN13 barcode = Decoder_EAN13::recognize( fields );
+
+ return barcode;
+}
+
+void barcodeRecognitionThread::addNumberToPossibleNumbers( QValueVector<int> number, int possible_numbers[10][13][2], bool correct_code )
+{
+ int i;
+ bool digit_contained;
+ for (int j = 0; j < 13; j++) {
+ if (number[j] >= 0) {
+ i = 0;
+ digit_contained = false;
+ while ((i < 10) && (possible_numbers[i][j][0] >= 0)) {
+ if (possible_numbers[i][j][0] == number[j]) {
+ digit_contained = true;
+ if (correct_code)
+ possible_numbers[i][j][1] = possible_numbers[i][j][1] + 100;
+ else
+ possible_numbers[i][j][1]++;
+ break;
+ }
+ i++;
+ }
+ if ((i < 10) && (!digit_contained)) {
+ // add new digit:
+ possible_numbers[i][j][0] = number[j];
+ if (correct_code)
+ possible_numbers[i][j][1] = possible_numbers[i][j][1] + 100;
+ else
+ possible_numbers[i][j][1]++;
+ }
+ }
+ }
+}
+
+void barcodeRecognitionThread::sortDigits( int possible_numbers[10][13][2] )
+{
+ int i;
+ int temp_value;
+ int temp_occurence;
+ bool changes;
+
+ for (int j = 0; j < 13; j++) {
+ i = 1;
+ changes = false;
+ while (true) {
+ if ((possible_numbers[i - 1][j][0] >= 0) && (possible_numbers[i][j][0] >= 0)) {
+ if (possible_numbers[i - 1][j][1] < possible_numbers[i][j][1]) {
+ temp_value = possible_numbers[i - 1][j][0];
+ temp_occurence = possible_numbers[i - 1][j][1];
+ possible_numbers[i - 1][j][0] = possible_numbers[i][j][0];
+ possible_numbers[i - 1][j][1] = possible_numbers[i][j][1];
+ possible_numbers[i][j][0] = temp_value;
+ possible_numbers[i][j][1] = temp_occurence;
+
+ changes = true;
+ }
+ }
+
+ if ((possible_numbers[i][j][0] < 0) || (i >= 9)) {
+ if (!changes)
+ break;
+ else {
+ i = 1;
+ changes = false;
+ }
+ } else
+ i++;
+ }
+ }
+}
+
+Barcode_EAN13 barcodeRecognitionThread::extractBarcode( int possible_numbers[10][13][2] )
+{
+ // create and initialize the temporary variables:
+ QValueVector<int> temp_code(13);
+ for (int i = 0; i < 13; i++)
+ temp_code[i] = possible_numbers[0][i][0];
+
+#ifdef Barcode_DEBUG
+ fprintf( stderr, "barcodeRecognitionThread::extractBarcode(): " );
+ for (int i=0; i<13; i++)
+ fprintf( stderr, "%i", temp_code[i] );
+ fprintf( stderr, "\n" );
+#endif
+
+ return Barcode_EAN13(temp_code);
+}
+
+Barcode_EAN13 barcodeRecognitionThread::detectValidBarcode ( int possible_numbers[10][13][2], int max_amount_of_considered_codes )
+{
+ // create and initialize the temporary variables:
+ QValueVector<int> temp_code(13);
+ for ( int i = 0; i < 13; i++ )
+ temp_code[i] = possible_numbers[0][i][0];
+
+ int alternative_amount = 0;
+
+ QValueVector<int> counter( 13 ); // no init in java source!!!
+ int counter_nr = 11;
+
+ // check if there is at least one complete code present:
+ for ( int i = 0; i < 13; i++ ) {
+ // exit and return the "most likely" code parts:
+ if ( temp_code[i] < 0 )
+ return Barcode_EAN13( temp_code );
+ }
+
+ // if there is at least one complete node, try to detect a valid barcode:
+ while ( alternative_amount < max_amount_of_considered_codes ) {
+ // fill the temporary code array with one possible version:
+ for ( int i = 0; i < 13; i++ )
+ temp_code[i] = possible_numbers[counter[i]][i][0];
+
+ alternative_amount++;
+
+ // check if this version represents a valid code:
+ if (isValid( temp_code ))
+ return Barcode_EAN13( temp_code );
+
+ // increment the counters:
+ if ( ( counter[counter_nr] < 9 ) && ( possible_numbers[counter[counter_nr] + 1][counter_nr][0] >= 0 ) ) {
+ // increment the actual counter.
+ counter[counter_nr]++;
+ } else {
+ // check if we have reached the end and no valid barcode has been found:
+ if ( counter_nr == 1 ) {
+ // exit and return the "most likely" code parts:
+ for ( int i = 0; i < 13; i++ )
+ temp_code[i] = possible_numbers[0][i][0];
+ return Barcode_EAN13( temp_code );
+ } else {
+ // reset the actual counter and increment the next one(s):
+ counter[counter_nr] = 0;
+
+ while ( true ) {
+ if ( counter_nr > 2 )
+ counter_nr--;
+ else {
+ for ( int i = 0; i < 13; i++ )
+ temp_code[i] = possible_numbers[0][i][0];
+ return Barcode_EAN13( temp_code );
+ }
+ if ( counter[counter_nr] < 9 ) {
+ counter[counter_nr]++;
+ if ( possible_numbers[counter[counter_nr]][counter_nr][0] < 0 )
+ counter[counter_nr] = 0;
+ else
+ break;
+ } else
+ counter[counter_nr] = 0;
+ }
+ counter_nr = 12;
+ }
+ }
+ }
+
+ for ( int i = 0; i < 13; i++ )
+ temp_code[i] = possible_numbers[0][i][0];
+ return Barcode_EAN13( temp_code );
+}
+
+bool barcodeRecognitionThread::isValid( int numbers[13] )
+{
+ QValueVector<int> temp(13);
+ for (int i=0; i<13; i++)
+ temp[i] = numbers[i];
+ return isValid( temp );
+}
+bool barcodeRecognitionThread::isValid( QValueVector<int> numbers )
+{
+ Q_ASSERT( numbers.count() == 13 );
+ // calculate the checksum of the barcode:
+ int sum1 = numbers[0] + numbers[2] + numbers[4] + numbers[6] + numbers[8] + numbers[10];
+ int sum2 = 3 * (numbers[1] + numbers[3] + numbers[5] + numbers[7] + numbers[9] + numbers[11]);
+ int checksum_value = sum1 + sum2;
+ int checksum_digit = 10 - (checksum_value % 10);
+ if (checksum_digit == 10)
+ checksum_digit = 0;
+
+#ifdef Barcode_DEBUG
+ fprintf( stderr, "barcodeRecognitionThread::isValid(): " );
+ for (int i=0; i<13; i++)
+ fprintf( stderr, "%i", numbers[i] );
+ fprintf( stderr, "\n" );
+#endif
+
+ return (numbers[12] == checksum_digit);
+}
+
+QValueVector<int> barcodeRecognitionThread::transformPathToBW( QValueVector<QRgb> line )
+{
+ int w = line.count();
+ QValueVector<int> bw_line(w,0);
+ bw_line[0] = 255;
+
+ // create greyscale values:
+ QValueVector<int> grey_line(w,0);
+ int average_illumination = 0;
+ for (int x = 0; x < w; x++) {
+ grey_line[x] = (qRed(line.at(x)) + qGreen(line.at(x)) + qBlue(line.at(x))) / 3;
+ average_illumination = average_illumination + grey_line[x];
+ }
+ average_illumination = average_illumination / w;
+
+ // perform the binarization:
+ int range = w / 20;
+
+ // temp values:
+ int moving_sum;
+ int moving_average;
+ int v1_index = -range + 1;
+ int v2_index = range;
+ int v1 = grey_line[0];
+ int v2 = grey_line[range];
+ int current_value;
+ int comparison_value;
+
+ // initialize the moving sum:
+ moving_sum = grey_line[0] * range;
+ for (int i = 0; i < range; i++)
+ moving_sum = moving_sum + grey_line[i];
+
+ // apply the adaptive thresholding algorithm:
+ for (int i = 1; i < w - 1; i++) {
+ if (v1_index > 0) v1 = grey_line[v1_index];
+ if (v2_index < w) v2 = grey_line[v2_index];
+ else v2 = grey_line[w - 1];
+ moving_sum = moving_sum - v1 + v2;
+ moving_average = moving_sum / (range << 1);
+ v1_index++;
+ v2_index++;
+
+ current_value = (grey_line[i - 1] + grey_line[i]) >> 1;
+
+ // decide if the current pixel should be black or white:
+ comparison_value = (3 * moving_average + average_illumination) >> 2;
+ if ((current_value < comparison_value - 3)) bw_line[i] = 0;
+ else bw_line[i] = 255;
+ }
+
+ // filter the values: (remove too small fields)
+
+ if (w >= 640) {
+ for (int x = 1; x < w - 1; x++) {
+ if ((bw_line[x] != bw_line[x - 1]) && (bw_line[x] != bw_line[x + 1])) bw_line[x] = bw_line[x - 1];
+ }
+ }
+
+ QValueVector<int> ret(w,0);
+ for (int i=0; i<w; i++)
+ ret[i] = bw_line[i];
+
+#ifdef Barcode_DEBUG
+ fprintf( stderr, "barcodeRecognitionThread::transformPathToBW(): " );
+ for (int i=0; i<ret.count(); i++)
+ if (bw_line[i] == 0)
+ fprintf( stderr, "0" );
+ else
+ fprintf( stderr, "#" );
+ fprintf( stderr, "\n" );
+#endif
+
+ return ret;
+}
+
+QValueVector< QValueVector<int> > barcodeRecognitionThread::extractFieldInformation( QValueVector<int> string )
+{
+ QValueVector< QValueVector<int> > temp_fields( string.count(), QValueVector<int>(2,0) );
+
+ if (string.count() == 0)
+ return QValueVector< QValueVector<int> >();
+
+ int field_counter = 0;
+ int last_value = string.at(0);
+ int last_fields = 1;
+ for (uint i = 1; i < string.size(); i++) {
+ if ((string.at(i) == last_value) && (i < string.size() - 1)) {
+ last_fields++;
+ } else {
+ // create new field entry:
+ temp_fields[field_counter][0] = last_value;
+ temp_fields[field_counter][1] = last_fields;
+
+ last_value = string.at(i);
+ last_fields = 0;
+ field_counter++;
+ }
+ }
+
+ temp_fields.resize( field_counter );
+
+#ifdef Barcode_DEBUG
+ fprintf( stderr, "barcodeRecognitionThread::extractFieldInformation(): " );
+ for (int i=0; i<temp_fields.count(); i++)
+ fprintf( stderr, "%i,%i ", temp_fields.at(i).at(0), temp_fields.at(i).at(1) );
+ fprintf( stderr, "\n" );
+#endif
+
+ return temp_fields;
+}
+
+//ok
+Barcode_EAN13::Barcode_EAN13()
+{
+ m_numbers.resize(13,-1);
+ m_null = true;
+}
+
+//ok
+Barcode_EAN13::Barcode_EAN13( QValueVector<int> code )
+{
+ setCode( code );
+}
+
+//ok
+void Barcode_EAN13::setCode( QValueVector<int> code )
+{
+ if (code.count() != 13) {
+ m_numbers.clear();
+ m_numbers.resize(13,-1);
+ m_null = true;
+ return;
+ }
+ m_numbers = code;
+ m_null = false;
+}
+
+//ok
+bool Barcode_EAN13::isValid() const
+{
+ if (m_null)
+ return false;
+
+ for (int i = 0; i < 13; i++)
+ if ((m_numbers[i] < 0) || (m_numbers[i] > 9))
+ return false;
+
+ // calculate the checksum of the barcode:
+ int sum1 = m_numbers[0] + m_numbers[2] + m_numbers[4] + m_numbers[6] + m_numbers[8] + m_numbers[10];
+ int sum2 = 3 * (m_numbers[1] + m_numbers[3] + m_numbers[5] + m_numbers[7] + m_numbers[9] + m_numbers[11]);
+ int checksum_value = sum1 + sum2;
+ int checksum_digit = 10 - (checksum_value % 10);
+ if (checksum_digit == 10)
+ checksum_digit = 0;
+
+ return (m_numbers[12] == checksum_digit);
+}
+
+//ok
+QValueVector<int> Barcode_EAN13::getNumbers() const
+{
+ return m_numbers;
+}
+
+//ok
+QString Barcode_EAN13::toString() const
+{
+ QString s;
+ for (int i = 0; i < 13; i++)
+ if ((m_numbers[i] >= 0) && (m_numbers[i] <= 9))
+ s += QString::number(m_numbers[i]);
+ else
+ s += '?';
+ return s;
+}
+
+//ok
+bool Barcode_EAN13::operator!= ( const Barcode_EAN13 &code )
+{
+ if (m_null != code.m_null)
+ return true;
+ if (!m_null)
+ for (int i=0; i<13; i++)
+ if (m_numbers[i] != code.m_numbers[i])
+ return true;
+ return false;
+}
+
+//ok
+Barcode_EAN13 Decoder_EAN13::recognize( QValueVector< QValueVector<int> > fields )
+{
+ // try to extract the encoded information from the field series:
+ QValueVector<int> numbers = decode( fields, 0, fields.count() );
+ Barcode_EAN13 barcode( numbers );
+
+ // return the results:
+ return barcode;
+}
+
+QValueVector<int> Decoder_EAN13::decode( QValueVector< QValueVector<int> > fields, int start_i, int end_i )
+{
+ // determine the length of the path in pixels
+ int length = 0;
+ for (uint i = 0; i < fields.size(); i++)
+ length += fields.at(i).at(1);
+
+ // set the parameters accordingly:
+ int max_start_sentry_bar_differences;
+ int max_unit_length;
+ int min_unit_length;
+
+ if (length <= 800) {
+ max_start_sentry_bar_differences = 6;
+ max_unit_length = 10;
+ min_unit_length = 1;
+ } else {
+ max_start_sentry_bar_differences = 30;
+ max_unit_length = 50;
+ min_unit_length = 1;
+ }
+
+ // consistency checks:
+ if (fields.count() <= 0)
+ return QValueVector<int>();
+ if (start_i > end_i - 3)
+ return QValueVector<int>();
+ if (end_i - start_i < 30)
+ return QValueVector<int>(); // (just a rough value)
+
+ // relevant indexes:
+ int start_sentinel_i;
+ int end_sentinel_i;
+ int left_numbers_i;
+ int middle_guard_i;
+ int right_numbers_i;
+
+ // relevant parameters:
+ float unit_length;
+
+ // results:
+ QValueVector<int> numbers( 13, -1 ); // the java source does no initialization
+
+ // determine the relevant positions:
+
+ // Try to detect the start sentinel (a small black-white-black serie):
+ start_sentinel_i = -1;
+ for (int i = start_i; i < end_i - 56; i++) {
+ if (fields[i][0] == 0) {
+ if ((fields[i][1] >= min_unit_length) && (fields[i][1] <= max_unit_length)) {
+ if ((abs(fields[i][1] - fields[i + 1][1]) <= max_start_sentry_bar_differences)
+ && (abs(fields[i][1] - fields[i + 2][1]) <= max_start_sentry_bar_differences) && (fields[i + 3][1] < fields[i][1] << 3)) {
+ start_sentinel_i = i;
+ break;
+ }
+ }
+ }
+ }
+
+#ifdef Decoder_EAN13_DEBUG
+ fprintf( stderr, "start_sentinal_index: %i\n", start_sentinel_i );
+#endif
+
+ if (start_sentinel_i < 0)
+ return QValueVector<int>();
+
+ // calculate the other positions:
+ left_numbers_i = start_sentinel_i + 3;
+ middle_guard_i = left_numbers_i + 6 * 4;
+ right_numbers_i = middle_guard_i + 5;
+ end_sentinel_i = right_numbers_i + 6 * 4;
+
+ if (end_sentinel_i + 3 > end_i)
+ return QValueVector<int>();
+
+ // calculate the average (pixel) length of a bar that is one unit wide:
+ // (a complete barcode consists out of 95 length units)
+ int temp_length = 0;
+ int field_amount = (end_sentinel_i - start_sentinel_i + 3);
+ for (int i = start_sentinel_i; i < start_sentinel_i + field_amount; i++)
+ temp_length += fields[i][1];
+ unit_length = (float) ((float) temp_length / 95.0f);
+
+#ifdef Decoder_EAN13_DEBUG
+ fprintf( stderr, "unit_width: %f\n", unit_length );
+#endif
+
+ QValueVector< QValueVector<int> > current_number_field( 4, QValueVector<int>(2,0) );
+
+ if (left_numbers_i + 1 > end_i)
+ return QValueVector<int>();
+
+
+ // test the side from which we are reading the barcode:
+ for (int j = 0; j < 4; j++) {
+ current_number_field[j][0] = fields[left_numbers_i + j][0];
+ current_number_field[j][1] = fields[left_numbers_i + j][1];
+ }
+ MatchMakerResult matchMakerResult = recognizeNumber( current_number_field, BOTH_TABLES );
+
+
+ if (matchMakerResult.isEven()) {
+ // we are reading the barcode from the back side:
+
+ // use the already obtained information:
+ numbers[12] = matchMakerResult.getDigit();
+
+ // try to recognize the "right" numbers:
+ int counter = 11;
+ for (int i = left_numbers_i + 4; i < left_numbers_i + 24; i = i + 4) {
+ for (int j = 0; j < 4; j++) {
+ current_number_field[j][0] = fields[i + j][0];
+ current_number_field[j][1] = fields[i + j][1];
+ }
+ matchMakerResult = recognizeNumber(current_number_field, EVEN_TABLE);
+ numbers[counter] = matchMakerResult.getDigit();
+ counter--;
+ }
+
+ bool parity_pattern[6]; // true = even, false = odd
+
+ //(counter has now the value 6)
+
+ // try to recognize the "left" numbers:
+ for (int i = right_numbers_i; i < right_numbers_i + 24; i = i + 4) {
+ for (int j = 0; j < 4; j++) {
+ current_number_field[j][0] = fields[i + j][0];
+ current_number_field[j][1] = fields[i + j][1];
+ }
+ matchMakerResult = recognizeNumber(current_number_field, BOTH_TABLES);
+ numbers[counter] = matchMakerResult.getDigit();
+ parity_pattern[counter-1] = !matchMakerResult.isEven();
+ counter--;
+ }
+
+ // try to determine the system code:
+ matchMakerResult = recognizeSystemCode(parity_pattern);
+ numbers[0] = matchMakerResult.getDigit();
+ } else {
+ // we are reading the barcode from the "correct" side:
+
+ bool parity_pattern[6]; // true = even, false = odd
+
+ // use the already obtained information:
+ numbers[1] = matchMakerResult.getDigit();
+ parity_pattern[0] = matchMakerResult.isEven();
+
+ // try to recognize the left numbers:
+ int counter = 2;
+ for (int i = left_numbers_i + 4; i < left_numbers_i + 24; i = i + 4) {
+ for (int j = 0; j < 4; j++) {
+ current_number_field[j][0] = fields[i + j][0];
+ current_number_field[j][1] = fields[i + j][1];
+ }
+ matchMakerResult = recognizeNumber(current_number_field, BOTH_TABLES);
+ numbers[counter] = matchMakerResult.getDigit();
+ parity_pattern[counter-1] = matchMakerResult.isEven();
+ counter++;
+ }
+
+ // try to determine the system code:
+ matchMakerResult = recognizeSystemCode(parity_pattern);
+ numbers[0] = matchMakerResult.getDigit();
+
+ // try to recognize the right numbers:
+ counter = 0;
+ for (int i = right_numbers_i; i < right_numbers_i + 24; i = i + 4) {
+ for (int j = 0; j < 4; j++) {
+ current_number_field[j][0] = fields[i + j][0];
+ current_number_field[j][1] = fields[i + j][1];
+ }
+ matchMakerResult = recognizeNumber(current_number_field, ODD_TABLE);
+ numbers[counter + 7] = matchMakerResult.getDigit();
+ counter++;
+ }
+ }
+
+ return numbers;
+}
+
+MatchMakerResult Decoder_EAN13::recognizeNumber( QValueVector< QValueVector<int> > fields, int code_table_to_use)
+{
+ // convert the pixel lenghts of the four black&white fields into
+ // normed values that have together a length of 70;
+ int pixel_sum = fields[0][1] + fields[1][1] + fields[2][1] + fields[3][1];
+ int b[4];
+ for (int i = 0; i < 4; i++) {
+ b[i] = round((((float) fields[i][1]) / ((float) pixel_sum)) * 70);
+ }
+
+#ifdef Decoder_EAN13_DEBUG
+ fprintf( stderr, "Recognize Number (code table to use: %i):\n", code_table_to_use );
+ fprintf( stderr, "lengths: %i %i %i %i\n", fields[0][1], fields[1][1], fields[2][1], fields[3][1] );
+ fprintf( stderr, "normed lengths: %i %i %i %i\n", b[0], b[1], b[2], b[3] );
+#endif
+
+ // try to detect the digit that is encoded by the set of four normed bar lenghts:
+ int max_difference_for_acceptance = 60;
+ int temp;
+
+ int even_min_difference = 100000;
+ int even_min_difference_index = 0;
+ int odd_min_difference = 100000;
+ int odd_min_difference_index = 0;
+
+ if ((code_table_to_use == BOTH_TABLES)||(code_table_to_use == EVEN_TABLE)) {
+ QValueVector<int> even_differences(10,0);
+
+ for (int i = 0; i < 10; i++) {
+ for (int j = 0; j < 4; j++) {
+ // calculate the differences in the even group:
+ temp = b[j] - code_even[i][j];
+ if (temp < 0)
+ even_differences[i] = even_differences[i] + ((-temp) << 1);
+ else
+ even_differences[i] = even_differences[i] + (temp << 1);
+ }
+ if (even_differences[i] < even_min_difference) {
+ even_min_difference = even_differences[i];
+ even_min_difference_index = i;
+ }
+ }
+ }
+
+ if ((code_table_to_use == BOTH_TABLES) || (code_table_to_use == ODD_TABLE)) {
+ QValueVector<int> odd_differences(10,0);
+
+ for (int i = 0; i < 10; i++) {
+ for (int j = 0; j < 4; j++) {
+ // calculate the differences in the odd group:
+ temp = b[j] - code_odd[i][j];
+ if (temp < 0)
+ odd_differences[i] = odd_differences[i] + ((-temp) << 1);
+ else
+ odd_differences[i] = odd_differences[i] + (temp << 1);
+ }
+ if (odd_differences[i] < odd_min_difference) {
+ odd_min_difference = odd_differences[i];
+ odd_min_difference_index = i;
+ }
+ }
+ }
+
+ // select the digit and parity with the lowest difference to the found pattern:
+ if (even_min_difference <= odd_min_difference) {
+ if (even_min_difference < max_difference_for_acceptance)
+ return MatchMakerResult( true, even_min_difference_index );
+ } else {
+ if (odd_min_difference < max_difference_for_acceptance)
+ return MatchMakerResult( false, odd_min_difference_index );
+ }
+
+ return MatchMakerResult( false, -1 );
+}
+
+MatchMakerResult Decoder_EAN13::recognizeSystemCode( bool parity_pattern[6] )
+{
+ // search for a fitting parity pattern:
+ bool fits = false;
+ for (int i = 0; i < 10; i++) {
+ fits = true;
+ for (int j = 0; j < 6; j++) {
+ if (parity_pattern_list[i][j] != parity_pattern[j]) {
+ fits = false;
+ break;
+ }
+ }
+ if (fits)
+ return MatchMakerResult( false, i );
+ }
+
+ return MatchMakerResult( false, -1 );
+}
+
+//ok
+MatchMakerResult::MatchMakerResult( bool even, int digit )
+{
+ m_even = even;
+ m_digit = digit;
+}
+
+#include "barcode.moc"
diff --git a/src/barcode/barcode.h b/src/barcode/barcode.h
new file mode 100644
index 0000000..b8d71bc
--- /dev/null
+++ b/src/barcode/barcode.h
@@ -0,0 +1,135 @@
+/***************************************************************************
+ copyright : (C) 2007 by Sebastian Held
+ email : sebastian.held@gmx.de
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * ### based on BaToo: http://people.inf.ethz.ch/adelmanr/batoo/ ### *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of version 2 of the GNU General Public License as *
+ * published by the Free Software Foundation; *
+ * *
+ ***************************************************************************/
+
+#ifndef BARCODE_H
+#define BARCODE_H
+
+#include <qthread.h>
+#include <qimage.h>
+#include <qvaluevector.h>
+#include <qobject.h>
+#include <qmutex.h>
+#include <math.h>
+
+#include "barcode_v4l.h"
+
+//#define BarcodeDecoder_DEBUG
+//#define Decoder_EAN13_DEBUG
+//#define Barcode_DEBUG
+
+namespace barcodeRecognition {
+ static int code_odd[][4] = { { 30, 20, 10, 10 },
+ { 20, 20, 20, 10 },
+ { 20, 10, 20, 20 },
+ { 10, 40, 10, 10 },
+ { 10, 10, 30, 20 },
+ { 10, 20, 30, 10 },
+ { 10, 10, 10, 40 },
+ { 10, 30, 10, 20 },
+ { 10, 20, 10, 30 },
+ { 30, 10, 10, 20 } };
+
+ static int code_even[][4] = { { 10, 10, 20, 30 },
+ { 10, 20, 20, 20 },
+ { 20, 20, 10, 20 },
+ { 10, 10, 40, 10 },
+ { 20, 30, 10, 10 },
+ { 10, 30, 20, 10 },
+ { 40, 10, 10, 10 },
+ { 20, 10, 30, 10 },
+ { 30, 10, 20, 10 },
+ { 20, 10, 10, 30 } };
+
+ static bool parity_pattern_list[][6] = { { false, false, false, false, false, false },
+ { false, false, true, false, true, true },
+ { false, false, true, true, false, true },
+ { false, false, true, true, true, false },
+ { false, true, false, false, true, true },
+ { false, true, true, false, false, true },
+ { false, true, true, true, false, false },
+ { false, true, false, true, false, true },
+ { false, true, false, true, true, false },
+ { false, true, true, false, true, false } };
+
+ class Barcode_EAN13 {
+ public:
+ Barcode_EAN13();
+ Barcode_EAN13( QValueVector<int> code );
+ bool isNull() const { return m_null; }
+ bool isValid() const;
+ QValueVector<int> getNumbers() const;
+ void setCode( QValueVector<int> code );
+ QString toString() const;
+ bool operator!= ( const Barcode_EAN13 &code );
+ protected:
+ QValueVector<int> m_numbers;
+ bool m_null;
+ };
+
+ class MatchMakerResult {
+ public:
+ MatchMakerResult( bool even, int digit );
+ bool isEven() const {return m_even;}
+ int getDigit() const {return m_digit;}
+ protected:
+ int m_digit;
+ bool m_even;
+ };
+
+ class Decoder_EAN13 {
+ public:
+ enum { BOTH_TABLES = 0, EVEN_TABLE = 1, ODD_TABLE = 2 };
+ static Barcode_EAN13 recognize( QValueVector< QValueVector<int> > fields );
+ static QValueVector<int> decode( QValueVector< QValueVector<int> > fields, int start_i, int end_i );
+ static MatchMakerResult recognizeNumber( QValueVector< QValueVector<int> > fields, int code_table_to_use );
+ static MatchMakerResult recognizeSystemCode( bool parity_pattern[6] );
+ };
+
+ /** \brief this thread handles barcode recognition using webcams
+ * @author Sebastian Held <sebastian.held@gmx.de>
+ */
+ class barcodeRecognitionThread : public QObject, public QThread {
+ Q_OBJECT
+ public:
+ barcodeRecognitionThread();
+ ~barcodeRecognitionThread();
+ virtual void run();
+ void stop();
+ void recognizeBarcode( QImage img );
+ bool isWebcamAvailable();
+ signals:
+ void recognized( QString barcode );
+ void gotImage( QImage &img );
+ protected:
+ volatile bool m_stop;
+ QImage m_barcode_img;
+ QMutex m_stop_mutex, m_barcode_img_mutex;
+ barcode_v4l *m_barcode_v4l;
+
+ Barcode_EAN13 recognize( QImage img );
+ Barcode_EAN13 recognizeCode( QImage img, int x1, int x2, int y );
+ void addNumberToPossibleNumbers( QValueVector<int> number, int possible_numbers[10][13][2], bool correct_code );
+ void sortDigits( int possible_numbers[10][13][2] );
+ Barcode_EAN13 extractBarcode( int possible_numbers[10][13][2] );
+ QValueVector<int> transformPathToBW( QValueVector<QRgb> line);
+ QValueVector< QValueVector<int> > extractFieldInformation( QValueVector<int> string );
+ Barcode_EAN13 detectValidBarcode ( int possible_numbers[10][13][2], int max_amount_of_considered_codes );
+ bool isValid( int numbers[13] );
+ bool isValid( QValueVector<int> numbers );
+ void printArray( int array[10][13][2], int level );
+ };
+}
+
+#endif
diff --git a/src/barcode/barcode_v4l.cpp b/src/barcode/barcode_v4l.cpp
new file mode 100644
index 0000000..f644fe1
--- /dev/null
+++ b/src/barcode/barcode_v4l.cpp
@@ -0,0 +1,538 @@
+/***************************************************************************
+ copyright : (C) 2007 by Sebastian Held
+ email : sebastian.held@gmx.de
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of version 2 of the GNU General Public License as *
+ * published by the Free Software Foundation; *
+ * *
+ ***************************************************************************/
+
+// uses code from xawtv: webcam.c (c) 1998-2002 Gerd Knorr
+
+//#include <stdio.h>
+//#include <stdlib.h>
+#include <fcntl.h> /* low-level i/o */
+//#include <unistd.h>
+#include <errno.h>
+//#include <malloc.h>
+//#include <sys/stat.h>
+//#include <sys/types.h>
+//#include <sys/time.h>
+//#include <sys/mman.h>
+#include <sys/ioctl.h>
+//#include <asm/types.h> /* for videodev2.h */
+
+#include "barcode_v4l.h"
+#include "../tellico_debug.h"
+
+using barcodeRecognition::ng_vid_driver;
+using barcodeRecognition::ng_vid_driver_v4l;
+using barcodeRecognition::barcode_v4l;
+using barcodeRecognition::video_converter;
+
+QPtrList<barcodeRecognition::video_converter> barcodeRecognition::ng_vid_driver::m_converter;
+const char *barcodeRecognition::device_cap[] = { "capture", "tuner", "teletext", "overlay", "chromakey", "clipping", "frameram", "scales", "monochrome", 0 };
+const unsigned int barcodeRecognition::ng_vfmt_to_depth[] = {
+ 0, /* unused */
+ 8, /* RGB8 */
+ 8, /* GRAY8 */
+ 16, /* RGB15 LE */
+ 16, /* RGB16 LE */
+ 16, /* RGB15 BE */
+ 16, /* RGB16 BE */
+ 24, /* BGR24 */
+ 32, /* BGR32 */
+ 24, /* RGB24 */
+ 32, /* RGB32 */
+ 16, /* LUT2 */
+ 32, /* LUT4 */
+ 16, /* YUYV */
+ 16, /* YUV422P */
+ 12, /* YUV420P */
+ 0, /* MJPEG */
+ 0, /* JPEG */
+ 16, /* UYVY */
+};
+
+const char* barcodeRecognition::ng_vfmt_to_desc[] = {
+ "none",
+ "8 bit PseudoColor (dithering)",
+ "8 bit StaticGray",
+ "15 bit TrueColor (LE)",
+ "16 bit TrueColor (LE)",
+ "15 bit TrueColor (BE)",
+ "16 bit TrueColor (BE)",
+ "24 bit TrueColor (LE: bgr)",
+ "32 bit TrueColor (LE: bgr-)",
+ "24 bit TrueColor (BE: rgb)",
+ "32 bit TrueColor (BE: -rgb)",
+ "16 bit TrueColor (lut)",
+ "32 bit TrueColor (lut)",
+ "16 bit YUV 4:2:2 (packed, YUYV)",
+ "16 bit YUV 4:2:2 (planar)",
+ "12 bit YUV 4:2:0 (planar)",
+ "MJPEG (AVI)",
+ "JPEG (JFIF)",
+ "16 bit YUV 4:2:2 (packed, UYVY)",
+};
+
+unsigned int barcodeRecognition::ng_yuv_gray[256];
+unsigned int barcodeRecognition::ng_yuv_red[256];
+unsigned int barcodeRecognition::ng_yuv_blue[256];
+unsigned int barcodeRecognition::ng_yuv_g1[256];
+unsigned int barcodeRecognition::ng_yuv_g2[256];
+unsigned int barcodeRecognition::ng_clip[256 + 2 * CLIP];
+
+void barcodeRecognition::ng_color_yuv2rgb_init()
+{
+ int i;
+# define RED_NULL 128
+# define BLUE_NULL 128
+# define LUN_MUL 256
+# define RED_MUL 512
+# define BLUE_MUL 512
+#define GREEN1_MUL (-RED_MUL/2)
+#define GREEN2_MUL (-BLUE_MUL/6)
+#define RED_ADD (-RED_NULL * RED_MUL)
+#define BLUE_ADD (-BLUE_NULL * BLUE_MUL)
+#define GREEN1_ADD (-RED_ADD/2)
+#define GREEN2_ADD (-BLUE_ADD/6)
+
+ /* init Lookup tables */
+ for (i = 0; i < 256; i++) {
+ barcodeRecognition::ng_yuv_gray[i] = i * LUN_MUL >> 8;
+ barcodeRecognition::ng_yuv_red[i] = (RED_ADD + i * RED_MUL) >> 8;
+ barcodeRecognition::ng_yuv_blue[i] = (BLUE_ADD + i * BLUE_MUL) >> 8;
+ barcodeRecognition::ng_yuv_g1[i] = (GREEN1_ADD + i * GREEN1_MUL) >> 8;
+ barcodeRecognition::ng_yuv_g2[i] = (GREEN2_ADD + i * GREEN2_MUL) >> 8;
+ }
+ for (i = 0; i < CLIP; i++)
+ barcodeRecognition::ng_clip[i] = 0;
+ for (; i < CLIP + 256; i++)
+ barcodeRecognition::ng_clip[i] = i - CLIP;
+ for (; i < 2 * CLIP + 256; i++)
+ barcodeRecognition::ng_clip[i] = 255;
+}
+
+
+
+
+
+
+
+
+
+
+
+barcode_v4l::barcode_v4l()
+{
+ m_devname = "/dev/video0";
+ m_grab_width = 640;
+ m_grab_height = 480;
+ m_drv = 0;
+ m_conv = 0;
+
+ barcodeRecognition::ng_color_yuv2rgb_init();
+ //ng_vid_driver::register_video_converter( new barcodeRecognition::yuv422p_to_rgb24() );
+ //ng_vid_driver::register_video_converter( new barcodeRecognition::yuv422_to_rgb24() );
+ ng_vid_driver::register_video_converter( new barcodeRecognition::yuv420p_to_rgb24() );
+
+ grab_init();
+}
+
+barcode_v4l::~barcode_v4l()
+{
+ if (m_drv) {
+ m_drv->close();
+ delete m_drv;
+ }
+}
+
+bool barcode_v4l::isOpen()
+{
+ return m_drv;
+}
+
+QImage barcode_v4l::grab_one2()
+{
+ if (!m_drv) {
+ myDebug() << "no driver/device available" << endl;
+ return QImage();
+ }
+
+ QByteArray *cap;
+ if (!(cap = m_drv->getimage2())) {
+ myDebug() << "capturing image failed" << endl;
+ return QImage();
+ }
+
+ QByteArray *buf;
+ if (m_conv) {
+ buf = new QByteArray( 3*m_fmt.width*m_fmt.height );
+ m_conv->frame( buf, cap, m_fmt_drv );
+ } else {
+ buf = new QByteArray(*cap);
+ }
+ delete cap;
+
+ int depth = 32;
+ QByteArray *buf2 = new QByteArray( depth/8 *m_fmt.width*m_fmt.height );
+ for (uint i=0; i<m_fmt.width*m_fmt.height; i++) {
+ (*buf2)[i*4+0] = buf->at(i*3+2);
+ (*buf2)[i*4+1] = buf->at(i*3+1);
+ (*buf2)[i*4+2] = buf->at(i*3+0);
+ (*buf2)[i*4+3] = 0;
+ }
+ delete buf;
+ QImage *temp = new QImage( (uchar*)buf2->data(), m_fmt.width, m_fmt.height, depth, 0, 0, QImage::LittleEndian );
+ QImage temp2 = temp->copy();
+ delete temp;
+ delete buf2;
+
+ return temp2;
+}
+
+
+
+
+
+
+
+bool barcode_v4l::grab_init()
+{
+ m_drv = ng_vid_driver::createDriver( m_devname );
+ if (!m_drv) {
+ myDebug() << "no grabber device available" << endl;
+ return false;
+ }
+ if (!(m_drv->capabilities() & CAN_CAPTURE)) {
+ myDebug() << "device doesn't support capture" << endl;
+ m_drv->close();
+ delete m_drv;
+ m_drv = 0;
+ return false;
+ }
+
+ /* try native */
+ m_fmt.fmtid = VIDEO_RGB24;
+ m_fmt.width = m_grab_width;
+ m_fmt.height = m_grab_height;
+ if (m_drv->setformat( &m_fmt )) {
+ m_fmt_drv = m_fmt;
+ return true;
+ }
+
+ /* check all available conversion functions */
+ m_fmt.bytesperline = m_fmt.width * ng_vfmt_to_depth[m_fmt.fmtid] / 8;
+ for (int i = 0; i<VIDEO_FMT_COUNT; i++) {
+ video_converter *conv = ng_vid_driver::find_video_converter( VIDEO_RGB24, i );
+ if (!conv)
+ continue;
+
+ m_fmt_drv = m_fmt;
+ m_fmt_drv.fmtid = conv->fmtid_in();
+ m_fmt_drv.bytesperline = 0;
+ if (m_drv->setformat( &m_fmt_drv )) {
+ m_fmt.width = m_fmt_drv.width;
+ m_fmt.height = m_fmt_drv.height;
+ m_conv = conv;
+ //m_conv->init( &m_fmt );
+ return true;
+ }
+ }
+
+ myDebug() << "can't get rgb24 data" << endl;
+ m_drv->close();
+ delete m_drv;
+ m_drv = 0;
+ return false;
+}
+
+
+ng_vid_driver* ng_vid_driver::createDriver( QString device )
+{
+ /* check all grabber drivers */
+ ng_vid_driver *drv = new ng_vid_driver_v4l();
+ if (!drv->open2( device )) {
+ myDebug() << "no v4l device found" << endl;
+ delete drv;
+ drv = 0;
+ }
+
+ return drv;
+}
+
+int ng_vid_driver::xioctl( int fd, int cmd, void *arg )
+{
+ int rc;
+
+ rc = ioctl(fd,cmd,arg);
+ if (rc == 0)
+ return 0;
+ //print_ioctl(stderr,ioctls_v4l1,PREFIX,cmd,arg);
+ qDebug( ": %s\n",(rc == 0) ? "ok" : strerror(errno) );
+ return rc;
+}
+
+
+
+void ng_vid_driver::register_video_converter( video_converter *conv )
+{
+ if (!conv)
+ return;
+
+ m_converter.append( conv );
+}
+
+video_converter *ng_vid_driver::find_video_converter( int out, int in )
+{
+ video_converter *conv;
+ for (conv = m_converter.first(); conv; conv = m_converter.next() )
+ if ((conv->fmtid_in() == in) && (conv->fmtid_out() == out))
+ return conv;
+ return 0;
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ng_vid_driver_v4l::ng_vid_driver_v4l()
+{
+ m_name = "v4l";
+ m_drv = 0;
+ m_fd = -1;
+
+ m_use_read = false;
+
+ for (int i=0; i<VIDEO_FMT_COUNT; i++)
+ format2palette[i] = 0;
+ format2palette[VIDEO_RGB08] = VIDEO_PALETTE_HI240;
+ format2palette[VIDEO_GRAY] = VIDEO_PALETTE_GREY;
+ format2palette[VIDEO_RGB15_LE] = VIDEO_PALETTE_RGB555;
+ format2palette[VIDEO_RGB16_LE] = VIDEO_PALETTE_RGB565;
+ format2palette[VIDEO_BGR24] = VIDEO_PALETTE_RGB24;
+ format2palette[VIDEO_BGR32] = VIDEO_PALETTE_RGB32;
+ format2palette[VIDEO_YUYV] = VIDEO_PALETTE_YUV422;
+ format2palette[VIDEO_UYVY] = VIDEO_PALETTE_UYVY;
+ format2palette[VIDEO_YUV422P] = VIDEO_PALETTE_YUV422P;
+ format2palette[VIDEO_YUV420P] = VIDEO_PALETTE_YUV420P;
+}
+
+bool ng_vid_driver_v4l::open2( QString device )
+{
+ /* open device */
+ if ((m_fd = ::open( device.latin1(), O_RDWR )) == -1) {
+ qDebug( "v4l: open %s: %s\n", device.latin1(), strerror(errno) );
+ return false;
+ }
+ if (ioctl( m_fd, VIDIOCGCAP, &m_capability ) == -1) {
+ ::close( m_fd );
+ return false;
+ }
+
+#ifdef Barcode_DEBUG
+ qDebug( "v4l: open: %s (%s)\n", device.latin1(), m_capability.name );
+#endif
+
+ fcntl( m_fd, F_SETFD, FD_CLOEXEC ); // close on exit
+
+#ifdef Barcode_DEBUG
+ myDebug() << " capabilities: " << endl;
+ for (int i = 0; device_cap[i] != NULL; i++)
+ if (m_capability.type & (1 << i))
+ qDebug( " %s", device_cap[i] );
+ qDebug( " size : %dx%d => %dx%d", m_capability.minwidth, m_capability.minheight, m_capability.maxwidth, m_capability.maxheight );
+#endif
+
+#ifdef Barcode_DEBUG
+ fprintf(stderr," v4l: using read() for capture\n");
+#endif
+ m_use_read = true;
+
+ return true;
+}
+
+void ng_vid_driver_v4l::close()
+{
+#ifdef Barcode_DEBUG
+ fprintf(stderr, "v4l: close\n");
+#endif
+
+ if (m_fd != -1)
+ ::close(m_fd);
+ m_fd = -1;
+ return;
+}
+
+int ng_vid_driver_v4l::capabilities()
+{
+ int ret = 0;
+
+ if (m_capability.type & VID_TYPE_OVERLAY)
+ ret |= CAN_OVERLAY;
+ if (m_capability.type & VID_TYPE_CAPTURE)
+ ret |= CAN_CAPTURE;
+ if (m_capability.type & VID_TYPE_TUNER)
+ ret |= CAN_TUNE;
+ if (m_capability.type & VID_TYPE_CHROMAKEY)
+ ret |= NEEDS_CHROMAKEY;
+ return ret;
+}
+
+bool ng_vid_driver_v4l::setformat( ng_video_fmt *fmt )
+{
+ bool rc = false;
+
+#ifdef Barcode_DEBUG
+ fprintf(stderr,"v4l: setformat\n");
+#endif
+ if (m_use_read) {
+ rc = read_setformat( fmt );
+ } else {
+ Q_ASSERT( false );
+ }
+
+ m_fmt = *fmt;
+
+ return rc;
+}
+
+
+
+
+
+bool ng_vid_driver_v4l::read_setformat( ng_video_fmt *fmt )
+{
+ xioctl( m_fd, VIDIOCGCAP, &m_capability );
+ if (fmt->width > static_cast<uint>(m_capability.maxwidth))
+ fmt->width = m_capability.maxwidth;
+ if (fmt->height > static_cast<uint>(m_capability.maxheight))
+ fmt->height = m_capability.maxheight;
+ fmt->bytesperline = fmt->width * ng_vfmt_to_depth[fmt->fmtid] / 8;
+
+ xioctl( m_fd, VIDIOCGPICT, &m_pict );
+ m_pict.depth = ng_vfmt_to_depth[fmt->fmtid];
+ m_pict.palette = GETELEM(format2palette,fmt->fmtid,0);
+ m_pict.brightness = 20000;
+ if (-1 == xioctl( m_fd, VIDIOCSPICT, &m_pict ))
+ return false;
+
+ xioctl( m_fd, VIDIOCGWIN, &m_win);
+ m_win.width = m_fmt.width;
+ m_win.height = m_fmt.height;
+ m_win.clipcount = 0;
+ if (-1 == xioctl( m_fd, VIDIOCSWIN, &m_win ))
+ return false;
+
+ return true;
+}
+
+QByteArray* ng_vid_driver_v4l::getimage2()
+{
+#ifdef Barcode_DEBUG
+ myDebug() << "v4l: getimage2" << endl;
+#endif
+
+ return read_getframe2();
+}
+
+
+QByteArray* ng_vid_driver_v4l::read_getframe2()
+{
+ QByteArray* buf;
+ int size;
+
+ size = m_fmt.bytesperline * m_fmt.height;
+ buf = new QByteArray( size );
+ if (size != read( m_fd, buf->data(), size )) {
+ delete( buf );
+ return 0;
+ }
+ return buf;
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+#define GRAY(val) barcodeRecognition::ng_yuv_gray[val]
+#define RED(gray,red) ng_clip[ CLIP + gray + barcodeRecognition::ng_yuv_red[red] ]
+#define GREEN(gray,red,blue) ng_clip[ CLIP + gray + barcodeRecognition::ng_yuv_g1[red] + \
+ barcodeRecognition::ng_yuv_g2[blue] ]
+#define BLUE(gray,blue) ng_clip[ CLIP + gray + barcodeRecognition::ng_yuv_blue[blue] ]
+
+void barcodeRecognition::yuv420p_to_rgb24::frame( QByteArray *out, const QByteArray *in, const ng_video_fmt fmt )
+{
+ unsigned char *y, *u, *v, *d;
+ unsigned char *us,*vs;
+ unsigned char *dp;
+ unsigned int i,j;
+ int gray;
+
+ dp = (unsigned char*)out->data();
+ y = (unsigned char*)in->data();
+ u = y + fmt.width * fmt.height;
+ v = u + fmt.width * fmt.height / 4;
+
+ for (i = 0; i < fmt.height; i++) {
+ d = dp;
+ us = u; vs = v;
+ for (j = 0; j < fmt.width; j+= 2) {
+ gray = GRAY(*y);
+ *(d++) = RED(gray,*v);
+ *(d++) = GREEN(gray,*v,*u);
+ *(d++) = BLUE(gray,*u);
+ y++;
+ gray = GRAY(*y);
+ *(d++) = RED(gray,*v);
+ *(d++) = GREEN(gray,*v,*u);
+ *(d++) = BLUE(gray,*u);
+ y++; u++; v++;
+ }
+ if (0 == (i % 2)) {
+ u = us; v = vs;
+ }
+ dp += 3*fmt.width;
+ }
+}
diff --git a/src/barcode/barcode_v4l.h b/src/barcode/barcode_v4l.h
new file mode 100644
index 0000000..f0cde90
--- /dev/null
+++ b/src/barcode/barcode_v4l.h
@@ -0,0 +1,184 @@
+/***************************************************************************
+ copyright : (C) 2007 by Sebastian Held
+ email : sebastian.held@gmx.de
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of version 2 of the GNU General Public License as *
+ * published by the Free Software Foundation; *
+ * *
+ ***************************************************************************/
+
+#ifndef BARCODE_V4L_H
+#define BARCODE_V4L_H
+
+#define GETELEM(array,index,default) \
+ (index < sizeof(array)/sizeof(array[0]) ? array[index] : default)
+
+/*
+ * Taken from xawtv-3.5.9 source 8/15/01 by George Staikos <staikos@kde.org>
+ */
+#ifdef __STRICT_ANSI__
+#undef __STRICT_ANSI__
+#define FOO__STRICT_ANSI__
+#endif
+#include <asm/types.h>
+#ifdef FOO__STRICT_ANSI__
+#define __STRICT_ANSI__ 1
+#undef FOO__STRICT_ANSI__
+#endif
+
+//#include <linux/videodev2.h>
+#include <linux/videodev.h>
+
+#include <qstring.h>
+#include <qimage.h>
+
+namespace barcodeRecognition {
+
+struct ng_video_fmt {
+ unsigned int fmtid; /* VIDEO_* */
+ unsigned int width;
+ unsigned int height;
+ unsigned int bytesperline; /* zero for compressed formats */
+};
+enum { CAN_OVERLAY=1, CAN_CAPTURE=2, CAN_TUNE=4, NEEDS_CHROMAKEY=8 };
+enum { VIDEO_NONE=0, VIDEO_RGB08, VIDEO_GRAY, VIDEO_RGB15_LE, VIDEO_RGB16_LE,
+ VIDEO_RGB15_BE, VIDEO_RGB16_BE, VIDEO_BGR24, VIDEO_BGR32, VIDEO_RGB24,
+ VIDEO_RGB32, VIDEO_LUT2, VIDEO_LUT4, VIDEO_YUYV, VIDEO_YUV422P,
+ VIDEO_YUV420P, VIDEO_MJPEG, VIDEO_JPEG, VIDEO_UYVY, VIDEO_FMT_COUNT };
+extern const char *device_cap[];
+extern const unsigned int ng_vfmt_to_depth[];
+extern const char* ng_vfmt_to_desc[];
+
+/* lookup tables */
+#define CLIP 320
+extern unsigned int ng_yuv_gray[256];
+extern unsigned int ng_yuv_red[256];
+extern unsigned int ng_yuv_blue[256];
+extern unsigned int ng_yuv_g1[256];
+extern unsigned int ng_yuv_g2[256];
+extern unsigned int ng_clip[256 + 2 * CLIP];
+void ng_color_yuv2rgb_init();
+
+
+
+class video_converter
+{
+public:
+ video_converter() {;}
+ virtual ~video_converter() {;}
+ virtual void init( ng_video_fmt* ) {;}
+ virtual void exit() {;}
+ virtual void frame( QByteArray*, const QByteArray*, ng_video_fmt ) {;}
+ int fmtid_in() {return m_fmtid_in;}
+ int fmtid_out() {return m_fmtid_out;}
+protected:
+ int m_fmtid_in, m_fmtid_out;
+};
+
+class yuv422p_to_rgb24 : public video_converter
+{
+public:
+ yuv422p_to_rgb24() {m_fmtid_in=VIDEO_YUV422P; m_fmtid_out=VIDEO_RGB24;}
+ virtual void frame( QByteArray *, const QByteArray *, ng_video_fmt ) {;}
+};
+class yuv422_to_rgb24 : public video_converter
+{
+public:
+ yuv422_to_rgb24() {m_fmtid_in=VIDEO_YUYV; m_fmtid_out=VIDEO_RGB24;}
+ virtual void frame( QByteArray *, const QByteArray *, ng_video_fmt ) {;}
+};
+class yuv420p_to_rgb24 : public video_converter
+{
+public:
+ yuv420p_to_rgb24() {m_fmtid_in=VIDEO_YUV420P; m_fmtid_out=VIDEO_RGB24;}
+ virtual void frame( QByteArray *, const QByteArray *, ng_video_fmt );
+};
+
+class ng_vid_driver
+{
+public:
+ static ng_vid_driver *createDriver( QString device );
+ static int xioctl( int fd, int cmd, void *arg );
+
+ /* open/close */
+ virtual bool open2( QString device ) = 0;
+ virtual void close() = 0;
+
+ /* attributes */
+ virtual QString get_devname() {return m_name;}
+ virtual int capabilities() = 0;
+
+ /* capture */
+ virtual bool setformat( ng_video_fmt *fmt ) = 0;
+ virtual QByteArray* getimage2() = 0; /* single image */
+
+ // video converter
+ static void register_video_converter( video_converter *conv );
+ static video_converter *find_video_converter( int out, int in );
+
+protected:
+ QString m_name;
+ static QPtrList<video_converter> m_converter;
+};
+
+
+class barcode_v4l
+{
+public:
+ barcode_v4l();
+ ~barcode_v4l();
+ QImage grab_one2();
+ bool isOpen();
+
+protected:
+ bool grab_init();
+
+ QString m_devname;
+ int m_grab_width, m_grab_height;
+ ng_vid_driver *m_drv;
+ ng_video_fmt m_fmt, m_fmt_drv;
+ video_converter *m_conv;
+
+};
+
+
+class ng_vid_driver_v4l : public ng_vid_driver
+{
+public:
+ ng_vid_driver_v4l();
+
+ /* open/close */
+ //virtual bool open( QString device );
+ virtual bool open2( QString device );
+ virtual void close();
+
+ /* attributes */
+ virtual int capabilities();
+
+ /* capture */
+ virtual bool setformat( ng_video_fmt *fmt );
+ //virtual ng_video_buf* getimage(); /* single image */
+ virtual QByteArray* getimage2(); /* single image */
+
+protected:
+ bool read_setformat( ng_video_fmt *fmt );
+ QByteArray* read_getframe2();
+ void *m_drv;
+ int m_fd;
+ video_capability m_capability;
+ video_picture m_pict;
+ video_window m_win;
+
+ /* capture */
+ bool m_use_read;
+ ng_video_fmt m_fmt;
+
+ unsigned short format2palette[VIDEO_FMT_COUNT];
+};
+
+} // namespace
+#endif