diff options
| author | tpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2010-03-01 19:17:32 +0000 |
|---|---|---|
| committer | tpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2010-03-01 19:17:32 +0000 |
| commit | e38d2351b83fa65c66ccde443777647ef5cb6cff (patch) | |
| tree | 1897fc20e9f73a81c520a5b9f76f8ed042124883 /src/barcode | |
| download | tellico-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.am | 14 | ||||
| -rw-r--r-- | src/barcode/barcode.cpp | 877 | ||||
| -rw-r--r-- | src/barcode/barcode.h | 135 | ||||
| -rw-r--r-- | src/barcode/barcode_v4l.cpp | 538 | ||||
| -rw-r--r-- | src/barcode/barcode_v4l.h | 184 |
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 |
