Första sidan | Forum start | Logga in | Bli medlem |


Så scannar jag in OCR-numret på mina räkningar

Har du något? Posta då här.

Så scannar jag in OCR-numret på mina räkningar

Inläggav zwanzig 21 augusti 2008, 17:06

Tröttnade på att skriva in alla siffror så jag kör med detta lilla script... Funderar på att lägga till kontroll av checksummesiffran/siffrorna också.

Kod: Markera allt
#!/bin/bash

#scan only ocr area
scanimage -x 68 -y 13 -l 127 -t 10 --resolution 300 --format=tiff >/tmp/image.tiff

convert /tmp/image.tiff -colorspace Gray /tmp/imagegrey.tiff
mogrify -rotate 180 -white-threshold 32300 -monochrome /tmp/imagegrey.tiff
tesseract /tmp/imagegrey.tiff /tmp/result /etc/tess.config

more /tmp/result.txt
more /tmp/result.txt | xclip -selection clipboard
zwanzig
 
Inlägg: 4
Blev medlem: 21 augusti 2008, 16:50

Re: Så scannar jag in OCR-numret på mina räkningar

Inläggav emj 6 oktober 2008, 17:23

Någon som har koden för checksum uträkning? Detta fungerar utmärkt för mig, tack för tipset.
emj
 
Inlägg: 23
Blev medlem: 2 oktober 2007, 17:32


Re: Så scannar jag in OCR-numret på mina räkningar

Inläggav zwanzig 27 oktober 2008, 21:13

Kod: Markera allt
#!/bin/bash
#Script to scan OCR-number
#set tesseract number probability higher with:
#echo number_depth 5 > /etc/tess.config

#scan only ocr area. l=lateral position
scanimage -x 68 -y 13 -l 128 -t 10 --resolution 300 --format=tiff >/tmp/image.tiff

#image rotation and lineart
convert /tmp/image.tiff -colorspace Gray /tmp/imagegrey.tiff
mogrify -rotate 180 -white-threshold 32300 -monochrome /tmp/imagegrey.tiff

#run ocr.
tesseract /tmp/imagegrey.tiff /tmp/result /etc/tess.config
OCR=$(more /tmp/result.txt)

_ld() #@@ Double a [single-digit] number and, if >10, add both digits
{     #@ USAGE: _ld N
      #@ RESULT: stored in $_LD
      #@ OUTPUT: none
      #@ RETURN: 0 if $1 is a single digit, otherwise 5
    case $1 in
        0) _LD=0 ;;
        1) _LD=2 ;;
        2) _LD=4 ;;
        3) _LD=6 ;;
        4) _LD=8 ;;
        5) _LD=1 ;;
        6) _LD=3 ;;
        7) _LD=5 ;;
        8) _LD=7 ;;
        9) _LD=9 ;;
        *) _LD=; return 5 ;;
    esac
}

n=1
sum=0
number=$OCR

while [ -n "$number" ]
do
  #: Split $number into last digit and everything else
  left=${number%?}        #~ Everything but the last character
  digit=${number#"$left"} #~ The last character
  number=$left            #~ Discard last character

  #: Skip non-digits
  case $digit in
      [0-9]) ;;
      *) continue ;;
  esac

  #: Calculate checksum
  case $n in
      *[24680]) _ld "${digit:-0}" || exit
                sum=$(( $sum + $_LD ))
                ;;
      *) sum=$(( $sum + ${digit:-0} )) ;;
  esac
  n=$(( $n + 1 ))
done

# if cheksum is ok put ocr in clipboard directly, else with unvalid warning in front
  case $sum in
      *0) echo "Checksum OK" && echo $OCR | xclip -selection clipboard ; true ;;
      *) echo "Checksum is unvalid!" && echo "UnvalidOCR:$OCR" | xclip -selection clipboard; false;;
  esac


echo $OCR


Kod för Luhn/modulo-10 kommer från http://cfaj.freeshell.org/src/scripts/luhn-sh
Kollar endast sista checksiffran (skiter i om näst sista siffran är längdkontroll eller inte), alla ocr-nummer har ändå inte längdkontroll så det skulle ändå bli svårt att veta om längden är fel eller om längdkontroll saknas...
zwanzig
 
Inlägg: 4
Blev medlem: 21 augusti 2008, 16:50

Re: Så scannar jag in OCR-numret på mina räkningar

Inläggav larlid 6 december 2008, 23:40

Riktigt intressant.
Hur lång tid tar det från att du kör scriptet tills att du har ocr numret i clipboard?

Jag håller på med ett program som ska göra räkningsbetalningar blixtsnabba med hjälp av scanner, men jag har inte hittat någon bra ocr-scanningsmjukvara till Linux...
Några tips någon?
Egentligen är det bankgiro/postgironumret och beloppet som också behövs...

Tanken är att kunna lägga in en lång sträng i clipboard med alla data och sedan klistra in det i swedbanks internetbank (betala flera räkningar), men tyvärr fungerar inte "tab" i strängarna som byte av textfält i webläsaren, annars hade man kunnat klistra in tio räkningar på en gång.
Finns det någon som har någon lösning på detta? Alltså "paste -> multiple textfields" i Firefox.

Här kommer det inte färdiga javaprogrammet för er som vill se o testa:
Spara det som QuickPay.java och kompilera det med: javac QuickPay.java


// QuickPay

import javax.swing.JLabel;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JComboBox;
import javax.swing.JTextField;
import javax.swing.JFormattedTextField;
import javax.swing.JCheckBox;
import javax.swing.JTable;
import javax.swing.JComponent;
import javax.swing.table.TableColumn;
//import javax.swing.*;
import javax.swing.text.*;
import java.text.*;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
import java.awt.*;
import java.awt.Toolkit;
import java.awt.event.FocusListener;
import java.awt.event.FocusEvent;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
import java.awt.event.ItemListener;
import java.awt.event.ItemEvent;

public class QuickPay extends JPanel
implements ActionListener, FocusListener, ItemListener {
JLabel NumberLabel, AmountLabel, OCRLabel;
JComboBox BgPgComboBox;
boolean bankgiro = true;
JTextField BgPgNrField;
String bgpg = "";
NumberFormat bgpgFormat;
JFormattedTextField AmountField;
double belopp = 0;
NumberFormat beloppFormat;
JCheckBox OcrBox;
boolean ocrbox = true;
JFormattedTextField MessageField;
MaskFormatter formatter = null;
JButton RegButton;
boolean regbutton = false;
// JComponents Middle
JTable table;
int tableCount = 0;
// JComponents Bottom
JButton PrintButton, SendButton;

public QuickPay() {
super(new GridBagLayout());
GridBagConstraints c = new GridBagConstraints();
c.fill = GridBagConstraints.HORIZONTAL;

NumberLabel = new JLabel("Nummer"); c.gridx = 1; c.gridy = 0; add(NumberLabel, c);
AmountLabel = new JLabel("Belopp"); c.gridx = 2; c.gridy = 0; add(AmountLabel, c);
OCRLabel = new JLabel("Meddelande / OCR"); c.gridx = 4; c.gridy = 0; add(OCRLabel, c);

String[] BgPgChoice = { "Bankgiro", "Plusgiro" };
BgPgComboBox = new JComboBox(BgPgChoice); c.gridx = 0; c.gridy = 1;
BgPgComboBox.setSelectedIndex(0);
add(BgPgComboBox, c);
BgPgComboBox.addActionListener(this);

BgPgNrField = new JTextField(9); c.gridx = 1; c.gridy = 1;
BgPgNrField.setColumns(9);
//bgpgFormat.setMaximumIntegerDigits(8);
add(BgPgNrField, c);
BgPgNrField.addFocusListener(this);

beloppFormat = NumberFormat.getNumberInstance(); c.gridx = 2; c.gridy = 1;
AmountField = new JFormattedTextField(beloppFormat);
AmountField.setColumns(7);
beloppFormat.setMaximumFractionDigits(2);
//beloppFormat.setMaximumIntegerDigits(5);
add(AmountField, c);
AmountField.addFocusListener(this);

OcrBox = new JCheckBox("OCR", true); c.gridx = 3; c.gridy = 1;
add(OcrBox, c);
OcrBox.addItemListener(this);

try { formatter = new MaskFormatter("*************************"); }
catch (java.text.ParseException exc) { System.err.println("formatter is bad: " + exc.getMessage()); System.exit(-1); }
MessageField = new JFormattedTextField(formatter); c.gridx = 4; c.gridy = 1;
MessageField.setColumns(20);
add(MessageField, c);

RegButton = new JButton("Registrera"); c.gridx = 5; c.gridy = 1;
add(RegButton, c);
RegButton.addActionListener(this);
RegButton.addFocusListener(this);

table = new JTable(12,6); c.gridwidth = 6; c.gridx = 0; c.gridy = 2;
add(table, c);
TableColumn column = null;
for (int i = 0; i < 5; i++) {
column = table.getColumnModel().getColumn(i);
if (i == 0) { column.setPreferredWidth(50); }
if (i == 1) { column.setPreferredWidth(65); }
if (i == 2) { column.setPreferredWidth(40); }
if (i == 3) { column.setPreferredWidth(0); }
if (i == 4) { column.setPreferredWidth(180); }
}

PrintButton = new JButton("Skriv ut"); c.gridx = 0; c.gridwidth = 2; c.gridy = 3;
add(PrintButton, c);

SendButton = new JButton("Skicka Betalningsuppdrag"); c.gridx = 4; c.gridwidth = 2; c.gridy = 3;
add(SendButton, c);

}

public void actionPerformed(ActionEvent e) {
if(e.getSource().equals(BgPgComboBox)) {
if(bankgiro) bankgiro = false; else bankgiro = true;
}
if(e.getSource().equals(AmountField))
System.out.println("Hej igen");
if(e.getSource().equals(RegButton)) {
System.out.println("Registrerat");
if(belopp != 0 && bgpg != "" && MessageField.getText() != "" && tableCount < 12) {
System.out.println("belopp = " + belopp);
if(bankgiro) table.setValueAt("Bankgiro", tableCount, 0); else table.setValueAt("Plusgiro", tableCount, 0);
table.setValueAt(bgpg, tableCount, 1);
table.setValueAt(belopp, tableCount, 2);
if(ocrbox) table.setValueAt("OCR", tableCount, 3); else table.setValueAt("Message", tableCount, 3);
table.setValueAt(MessageField.getText(), tableCount, 4);
tableCount++;
BgPgComboBox.grabFocus();
}
}

}

public void focusGained(FocusEvent e) {
if(e.getSource().equals(AmountField))
System.out.println("Focus Gained Amount");
if(e.getSource().equals(RegButton))
regbutton = true;
}

public void focusLost(FocusEvent e) {
if(e.getSource().equals(BgPgNrField))
bgpg = BgPgNrField.getText();
if(e.getSource().equals(AmountField))
belopp = Double.parseDouble(AmountField.getText());
if(e.getSource().equals(RegButton))
regbutton = false;

}

public void itemStateChanged(ItemEvent e) {
if(e.getItemSelectable() == OcrBox)
if(e.getStateChange() == ItemEvent.DESELECTED)
ocrbox = false;
if(e.getStateChange() == ItemEvent.SELECTED)
ocrbox = true;
}

/**
* Create the GUI and show it. For thread safety,
* this method should be invoked from the
* event-dispatching thread.
*/
private static void createAndShowGUI() {
//Create and set up the window.
JFrame frame = new JFrame("QuickPay");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

//Create and set up the content pane.
JComponent newContentPane = new QuickPay();
newContentPane.setOpaque(true); //content panes must be opaque
frame.setContentPane(newContentPane);

//Display the window.
frame.pack();
frame.setVisible(true);
}

public static void main(String[] args) {
//Schedule a job for the event-dispatching thread:
//creating and showing this application's GUI.
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGUI();
}
});
}
}
larlid
 
Inlägg: 1
Blev medlem: 6 december 2008, 23:24

Re: Så scannar jag in OCR-numret på mina räkningar

Inläggav zwanzig 20 december 2008, 01:09

@larlid
Det tar kanske en halv sekund eller så.
tesseract är mitt tips för ocr-programvara för GNU/Linux. Det ska inte vara några problem att plocka de uppgifter du vill. Gällande att tabba vid paste i firefox finns ju en del paste extensions att googla efter och undersöka om du kan bygga vidare på.

numera vill tesseract bara ta tiff-filer med ett f (*.tif inte *.tiff) så jag har uppdaterat scriptet lite.
Kod: Markera allt
#!/bin/bash
#Script to scan OCR-number
#set tesseract number probability and association
#echo number_depth 5 > /etc/tess.config
#echo enable_assoc 0 >> /etc/tess.config

#scan only ocr area. l=lateral position
scanimage -x 68 -y 13 -l 128 -t 10 --resolution 300 --format=tiff >/tmp/image.tif

#image rotation and lineart
convert /tmp/image.tif -colorspace Gray /tmp/imagegrey.tif
mogrify -rotate 180 -white-threshold 32300 -monochrome /tmp/imagegrey.tif

#run ocr.
tesseract /tmp/imagegrey.tif /tmp/result /etc/tess.config
OCR=$(more /tmp/result.txt)

_ld() #@@ Double a [single-digit] number and, if >10, add both digits
{     #@ USAGE: _ld N
      #@ RESULT: stored in $_LD
      #@ OUTPUT: none
      #@ RETURN: 0 if $1 is a single digit, otherwise 5
    case $1 in
        0) _LD=0 ;;
        1) _LD=2 ;;
        2) _LD=4 ;;
        3) _LD=6 ;;
        4) _LD=8 ;;
        5) _LD=1 ;;
        6) _LD=3 ;;
        7) _LD=5 ;;
        8) _LD=7 ;;
        9) _LD=9 ;;
        *) _LD=; return 5 ;;
    esac
}

n=1
sum=0
number=$OCR

while [ -n "$number" ]
do
  #: Split $number into last digit and everything else
  left=${number%?}        #~ Everything but the last character
  digit=${number#"$left"} #~ The last character
  number=$left            #~ Discard last character

  #: Skip non-digits
  case $digit in
      [0-9]) ;;
      *) continue ;;
  esac

  #: Calculate checksum
  case $n in
      *[24680]) _ld "${digit:-0}" || exit
                sum=$(( $sum + $_LD ))
                ;;
      *) sum=$(( $sum + ${digit:-0} )) ;;
  esac
  n=$(( $n + 1 ))
done

# if cheksum is ok put ocr in clipboard directly, else with unvalid warning in front
  case $sum in
      *0) echo "Checksum OK" && echo $OCR | xclip -selection clipboard ; true ;;
      *) echo "Checksum is unvalid!" && echo "UnvalidOCR:$OCR" | xclip -selection clipboard; false;;
  esac


echo $OCR


Ville också tvinga tesseract att köra endast siffror. Det gick visst inte direkt i konfigfilen för tesseract så jag patchade tesseracts källkod.
Patch för att få tesseract 2.03 att köra bara siffror:
Kod: Markera allt
--- classify/adaptmatch.cpp~   2008-12-19 23:09:22.000000000 +0100
+++ classify/adaptmatch.cpp   2008-12-19 23:09:22.000000000 +0100
@@ -2884,10 +2884,12 @@
     FLOAT32 *Rating = Results->Ratings;
     CLASS_ID *Match = Results->Classes;
     FLOAT32 BadMatchThreshold;
-    static const char* romans = "i v x I V X";
+    static const char* romans = "";
     BadMatchThreshold = Results->BestRating + BadMatchPad;

-    if (bln_numericmode) {
+    if (bln_numericmode || 1) {   
       UNICHAR_ID unichar_id_one = unicharset.contains_unichar("1") ?
           unicharset.unichar_to_id("1") : -1;
       UNICHAR_ID unichar_id_zero = unicharset.contains_unichar("0") ?
zwanzig
 
Inlägg: 4
Blev medlem: 21 augusti 2008, 16:50


Återgå till Tips och trix

Vilka är online

Användare som besöker denna kategori: Inga registrerade användare och 1 gäst