Thread: [Pycodeocr-main] SF.net SVN: pycodeocr:[27]
Status: Beta
Brought to you by:
drtrigon
From: <la...@us...> - 2010-10-25 18:39:38
|
Revision: 27 http://pycodeocr.svn.sourceforge.net/pycodeocr/?rev=27&view=rev Author: laserb Date: 2010-10-25 18:39:31 +0000 (Mon, 25 Oct 2010) Log Message: ----------- Initial support for Doxygen / added blacklist for devices / added checksum test for 42 digit OCR codes , calculates the last digit before the ">" which is a checksum for the amount before, returns an error if this failes / some minor fixes / !! AND support for bigger image view !! Modified Paths: -------------- PyCodeOCR.glade PyCodeOCR.py Added Paths: ----------- __init__.py blacklist checksum.py Modified: PyCodeOCR.glade =================================================================== --- PyCodeOCR.glade 2010-09-12 19:05:23 UTC (rev 26) +++ PyCodeOCR.glade 2010-10-25 18:39:31 UTC (rev 27) @@ -1,4 +1,4 @@ -<?xml version="1.0"?> +<?xml version="1.0" encoding="UTF-8"?> <glade-interface> <!-- interface-requires gtk+ 2.16 --> <!-- interface-naming-policy toplevel-contextual --> @@ -31,7 +31,7 @@ <property name="visible">True</property> <property name="can_focus">True</property> <property name="editable">False</property> - <property name="invisible_char">●</property> + <property name="invisible_char">●</property> </widget> <packing> <property name="x">16</property> @@ -39,17 +39,6 @@ </packing> </child> <child> - <widget class="GtkImage" id="image1"> - <property name="width_request">504</property> - <property name="height_request">84</property> - <property name="visible">True</property> - </widget> - <packing> - <property name="x">16</property> - <property name="y">75</property> - </packing> - </child> - <child> <widget class="GtkLabel" id="label5"> <property name="width_request">90</property> <property name="height_request">21</property> @@ -266,7 +255,7 @@ <property name="use_underline">True</property> <property name="active">True</property> <property name="draw_indicator">True</property> - <signal name="toggled" handler="on_radiobutton2_toggled"/> + <signal name="toggled" handler="on_combobox2_changed"/> </widget> <packing> <property name="x">534</property> @@ -283,7 +272,7 @@ <property name="use_underline">True</property> <property name="draw_indicator">True</property> <property name="group">radiobutton2</property> - <signal name="toggled" handler="on_radiobutton1_toggled"/> + <signal name="toggled" handler="on_combobox2_changed"/> </widget> <packing> <property name="x">318</property> @@ -317,6 +306,25 @@ <property name="y">20</property> </packing> </child> + <child> + <widget class="GtkEventBox" id="eventbox2"> + <property name="width_request">505</property> + <property name="height_request">84</property> + <property name="visible">True</property> + <signal name="button_press_event" handler="on_eventbox2_button_press_event"/> + <child> + <widget class="GtkImage" id="image1"> + <property name="width_request">505</property> + <property name="height_request">84</property> + <property name="visible">True</property> + </widget> + </child> + </widget> + <packing> + <property name="x">16</property> + <property name="y">75</property> + </packing> + </child> </widget> </child> </widget> @@ -371,4 +379,30 @@ </widget> </child> </widget> + <widget class="GtkWindow" id="window3"> + <property name="type">popup</property> + <property name="window_position">mouse</property> + <property name="destroy_with_parent">True</property> + <child> + <widget class="GtkFixed" id="fixed1"> + <property name="visible">True</property> + <child> + <widget class="GtkEventBox" id="eventbox1"> + <property name="width_request">600</property> + <property name="height_request">600</property> + <property name="visible">True</property> + <property name="events">GDK_BUTTON_MOTION_MASK | GDK_BUTTON_PRESS_MASK | GDK_KEY_PRESS_MASK | GDK_STRUCTURE_MASK</property> + <child> + <widget class="GtkImage" id="image2"> + <property name="width_request">600</property> + <property name="height_request">600</property> + <property name="visible">True</property> + <signal name="button_press_event" handler="on_eventbox2_button_press_event"/> + </widget> + </child> + </widget> + </child> + </widget> + </child> + </widget> </glade-interface> Modified: PyCodeOCR.py =================================================================== --- PyCodeOCR.py 2010-09-12 19:05:23 UTC (rev 26) +++ PyCodeOCR.py 2010-10-25 18:39:31 UTC (rev 27) @@ -1,9 +1,13 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- +## @file PyCodeOCR.py +# Turn your scanner into a free document +# reader for invoices +# (e.g. for e-banking) with the help of tesseract-ocr available +# for many unix (and also windows) platforms. - # python standard modules -import os, re, sys, subprocess, time, gobject +import os, re, sys, subprocess, time, gobject, string, struct # GTK, PyGTK, GLADE (GNOME) modules @@ -12,13 +16,15 @@ import gtk, gtk.glade import gobject +from checksum import modulo10 + # PIL image library and it's SANE bindings import Image, ImageDraw #from PIL import Image import sane # f12: 'python-imaging-sane' #(fedora 12 has no libWand, so it's not working at the moment...) -## ImageMagick's MagickWand API +# ImageMagick's MagickWand API #from pythonmagickwand.image import Image # search optional pakages @@ -26,6 +32,7 @@ os.environ["LD_LIBRARY_PATH"] = "/usr/local/lib" # os.system('python %s' % sys.argv[0]) # sys.exit() # +## list of available pakages pakages = [] # gocr if os.path.exists("gocr"): @@ -44,9 +51,13 @@ # global variables and constants +## path from which this program is currently executed local_path = os.path.realpath(os.path.dirname(sys.argv[0])) +## path of program without extension local_path/PyCodeOCR raw_path = os.path.join(local_path, os.path.splitext(sys.argv[0])[0]) +## users home path /home/user home_path = os.path.expanduser('~') +## default scanning coordinates std_scan_koords = { "0 deg": (( 82, 60, 20, 155 ), 0), # or with higher res. ( 820, 600, 200, 1559 ), "90 deg": (( 1, 82, 150, 20 ), 90), "180 deg": (( 3, 1, 20, 150 ), 180), @@ -54,7 +65,7 @@ "A4": (( 0, 0, 296, 215 ), 0), } # scan whole range (A4: http://www.cl.cam.ac.uk/~mgk25/iso-paper.html) #"A4": (( 0, 0, 290, 210 ), 0), } # (more secure; with small border) -# MainWindow +## MainWindow # The GUI was created/designed using GLADE # class MainWindowGTK: @@ -87,34 +98,64 @@ # (constants) MDE = { 'invoices': 0, 'barcode': 1, 'DataMatrix': 2, 'PDF417': 3 } + ## Initialize def __init__(self): - """ ... """ # retrieve widgets # window1 - main window + ## gladefile raw_path.glade self.gladefile = raw_path + ".glade" + ## get xml from gladefile self.xml = gtk.glade.XML(self.gladefile) + ## main window self.window1 = self.xml.get_widget('window1') + ## image window + self.window3 = self.xml.get_widget('window3') + ## scan button self.togglebutton1 = self.xml.get_widget('togglebutton1') + ## restart button self.button1 = self.xml.get_widget('button1') + ## exit button self.button2 = self.xml.get_widget('button2') + ## placement frame self.frame1 = self.xml.get_widget('frame1') + ## image self.image1 = self.xml.get_widget('image1') + ## image + self.image2 = self.xml.get_widget('image2') + ## output line self.entry1 = self.xml.get_widget('entry1') + ## progressbar self.progressbar1 = self.xml.get_widget('progressbar1') + ## Orientation label self.label5 = self.xml.get_widget('label5') + ## Orientation combobox self.combobox1 = self.xml.get_widget('combobox1') + ## mode combobox self.combobox2 = self.xml.get_widget('combobox2') + ## x-position self.spinbutton1 = self.xml.get_widget('spinbutton1') + ## y-position self.spinbutton2 = self.xml.get_widget('spinbutton2') + ## x-size self.spinbutton3 = self.xml.get_widget('spinbutton3') + ## y-size self.spinbutton4 = self.xml.get_widget('spinbutton4') + ## file input self.radiobutton1 = self.xml.get_widget('radiobutton1') + ## sane input self.radiobutton2 = self.xml.get_widget('radiobutton2') + ## choose file self.filechooserbutton1 = self.xml.get_widget('filechooserbutton1') - # window2 - sane + ## device select window self.window2 = self.xml.get_widget('window2') + ## device list self.treeview1 = self.xml.get_widget('treeview1') + ## OK button self.button3 = self.xml.get_widget('button3') + ## event box for image + self.eventbox2 = self.xml.get_widget('eventbox2') + ## event box for image window + self.eventbox1 = self.xml.get_widget('eventbox1') # initiate orientation and position self.combobox1.set_active(0) @@ -138,7 +179,14 @@ # connect signal handlers self.xml.signal_autoconnect( self ) + # connect eventbox to close image window + self.eventbox1.connect('button_press_event', self.on_eventbox2_button_press_event) + # set tooltips + self.button1.set_tooltip_text("Restart PyCodeOCR to search for a SANE device.") + self.image1.set_tooltip_text("Click for bigger image") + self.image2.set_tooltip_text("Click to close") + # take some time to init sane #self.init_sane() @@ -151,6 +199,7 @@ return + ## initialize sane def init_sane(self, source=None): # lock window self.window1.set_sensitive(False) @@ -171,22 +220,49 @@ return False + ## Run gtk mainloop and with it THIS APP. def run(self): - """ Run gtk mainloop and with it THIS APP. """ gtk.main() - # signals / glade callbacks + ## signals / glade callbacks # + def on_eventbox2_button_press_event(self, source=None, event=None): + if self.window3.get_visible(): + # clean-up + os.remove( self.image_file_big ) + # hide window + self.window3.hide() + else: + # load original image + im1 = Image.open(self.imageFile) + # adjust width and height to your needs and keep ratio + (im_width, im_height) = im1.size #get image size + (width, height) = self.image2.get_size_request() #get needed size + ratio = min(float(width)/im_width,float(height)/im_height) #calculate resizing ratio + new_width = int(im_width*ratio) + new_height = int(im_height*ratio) + print self.image2.get_size_request() + # resize to fit popup window + im = im1.resize((new_width, new_height), Image.BILINEAR) + # save + self.image_file_big = self.temp+"06.jpg" + im.save( self.image_file_big ) + # set image + self.image2.set_from_file( self.image_file_big ) + # show window + self.window3.show() + def on_button3_clicked(self, source=None): # get selection from treeview devnr = self.treeview1.get_cursor()[0][0] # hide window - self.window2.destroy() + self.window2.hide() # init scanner self.run_sane.init_scanner(self.progress, self.window2, self.treeview1, devnr) # set sensitivity self.init_sensitivity() + ## set sensitivity of buttons the first time def init_sensitivity(self): # set sensitivity if self.run_sane.found_scanner: @@ -195,7 +271,7 @@ else: self.radiobutton1.set_active(True) self.radiobutton2.set_sensitive(False) - self.on_radiobutton1_toggled() + #self.on_radiobutton1_toggled() self.progress(0.,"No SANE device found. Use file input instead.") self.button1.set_visible(True) @@ -205,14 +281,14 @@ # Try to set orientation and placement sensitive. Will fail if no device is found self.toggle_orientation_sensitive(True) self.toggle_placement_sensitive(True) - + + ## one of the placement coordinates changed def on_spinbutton_value_changed(self, source=None, event=None): - """ Spinbutton value changed signal handler. """ self.scan_koords = (self.spinbutton1.get_value(),self.spinbutton2.get_value(), self.spinbutton3.get_value(),self.spinbutton4.get_value()) + ## Orientation changed def on_combobox1_changed(self, source=None, event=None): - """ Combobox changed signal handler. """ orient = self.combobox1.get_model()[self.combobox1.get_active()][0] self.spinbutton1.set_value(std_scan_koords[orient][0][0]) @@ -222,8 +298,9 @@ self.mode = std_scan_koords[orient][1] + ## mode changed def on_combobox2_changed(self, source=None): - op_mode = self.combobox2.get_active() + op_mode = self.combobox2.get_active() if (op_mode == self.MDE['invoices']): self.toggle_placement_sensitive(True) self.toggle_orientation_sensitive(True) @@ -237,8 +314,8 @@ self.toggle_placement_sensitive(True) self.toggle_orientation_sensitive(True) + ## ToggleButton: 'scan'/'stop'. def on_togglebutton1_toggled(self, source=None, event=None, *a): - """ ToggleButton: 'scan'/'stop'. """ if self.togglebutton1.get_active(): # scan ! self.togglebutton1.set_label("stop") self.__stop = False @@ -266,48 +343,49 @@ self.refresh() return - + ## exit and restart def on_button1_clicked(self, source=None, event=None): - """ Button: 'exit'. """ os.execv(sys.argv[0],sys.argv) self.on_window1_destroy() - + + ## exit def on_button2_clicked(self, source=None, event=None): - """ Button: 'exit'. """ self.on_window1_destroy() - + + ## press x-button on window def on_window1_destroy(self, source=None, event=None): - """ Window closed signal handler. """ + try: + os.remove( self.temp+"04.jpg" ) # clean-up + os.remove( self.image_file_small ) + except: + pass + # exit gtk.main_quit() - + + ## press x-button on device select window def on_window2_destroy(self, source=None): # when windows is closed by pressing the x-button # choose first device - if source != None: - devnr = 0 - # init scanner - self.run_sane.init_scanner(self.progress, self.window2, self.treeview1, devnr) - # set sensitivity - self.init_sensitivity() + devnr = 0 + # init scanner + self.run_sane.init_scanner(self.progress, self.window2, self.treeview1, devnr) + # set sensitivity + self.init_sensitivity() - def on_radiobutton1_toggled(self, source=None, event=None): - #self.filechooserbutton1.set_sensitive( self.radiobutton1.get_active() ) - self.on_combobox2_changed() - - def on_radiobutton2_toggled(self, source=None, event=None): - self.on_combobox2_changed() - + ## file selected def on_filechooserbutton1_file_set(self, source=None): self.radiobutton1.set_active(True) self.inp_file = self.filechooserbutton1.get_filename() if not os.path.exists(self.inp_file): self.progress(0., "File not found!") return None - + + ## toggle orientation sensitivity def toggle_orientation_sensitive(self, value): self.label5.set_sensitive(value) self.combobox1.set_sensitive(value) - + + ## toggle placement sensitivity def toggle_placement_sensitive(self, value): if self.radiobutton2.get_active(): self.frame1.set_sensitive(value) @@ -315,9 +393,9 @@ self.frame1.set_sensitive(False) # helpers - # + # + ## Main scanning and number recognition procedure. def scancode(self): - """ Main scanning and number recognition procedure. """ # Initialization of scanning process # (0/7) self.progress(0., "") @@ -386,7 +464,7 @@ self.run_sane.post_init(self.scan_koords, self.inp_file) # RunSANE().post_init(...) 2nd part of __init__(...) self.run_sane.resolution = opt["resolution"] if self.run_sane(): - self.progress(0., self.run_sane.stderr[:-1]) + self.progress(0., self.run_sane.stderr) return None #del self.run_sane else: # direct file input @@ -487,6 +565,15 @@ if (op_mode == self.MDE['invoices']): # 0: invoices check = (not "?" in data) # any unrecognized char in code? check = check and ( len(data) in opt['valid_code_len'] ) # correct code len? + if len(data) == 42: + # extract details + (tmp, betrag, tmp, referenz, tmp, konto, tmp) = struct.unpack("2s11ss16s2s9ss",data) + print "Betrag: "+str(int(betrag[:-1])/100.) + print "Konto: "+konto[:2]+"-"+konto[3:-2]+"-"+konto[-2:] + print "Referenznr: "+referenz + # modulo10 checksum for betrag + checknr = modulo10().run(int(betrag[:-1])) + check = check and checknr == int(betrag[-1]) elif (op_mode == self.MDE['barcode']): # 1: barcode check = not (data['type'] == "unknown") if check: @@ -555,31 +642,37 @@ # (7/7) return check + ## Refresh window during running processes. def refresh(self): - """ Refresh window during running processes. """ self.window1.queue_draw() time.sleep(0.1) # give also some time to the user... :) while gtk.events_pending(): gtk.main_iteration() - def setimage(self, imageFile): - """ Resize and set PIL image to gtk/gnome window. """ - # http://www.daniweb.com/code/snippet216637.html - # resize an image using the PIL image library - # free from: http://www.pythonware.com/products/pil/index.htm - # tested with Python24 vegaseat 11oct2005 - # open an image file (.bmp,.jpg,.png,.gif) you have in the working folder + ## Resize and set PIL image to gtk/gnome window. + # resize an image using the PIL image library + # tested with Python24 vegaseat 11oct2005 + # open an image file (.bmp,.jpg,.png,.gif) you have in the working folder + # @see http://www.daniweb.com/code/snippet216637.html + # @see http://www.pythonware.com/products/pil/index.htm + def setimage(self, imageFile): print imageFile[-3:] if imageFile[-3:] in ["bmp","jpg","png","gif"]: #already in right format - im1 = Image.open(imageFile) + # original file name + self.imageFile = imageFile + # load file + im1 = Image.open(self.imageFile) else: #convert Image self.run_convert = RunExternal(self.cmd_convert % (imageFile, 0, self.temp+"04.jpg"), error_msg=[ "convert: unable to open image" ]) if self.run_convert(): self.progress(0., self.run_convert.stderr[:-1]) - return None - im1 = Image.open(self.temp+"04.jpg") + return None + # original filename + self.imageFile = self.temp+"04.jpg" + # load file + im1 = Image.open(self.imageFile) # adjust width and height to your needs and keep ratio (im_width, im_height) = im1.size #get image size @@ -596,19 +689,19 @@ #im = im1.resize((new_width, new_height), Image.ANTIALIAS) # best down-sizing filter #ext = ".jpg" - image_file = self.temp+"04.jpg" - im.save( image_file ) - self.image1.set_from_file( image_file ) - os.remove( image_file ) # clean-up + # set image preview + self.image_file_small = self.temp+"05.jpg" + im.save( self.image_file_small ) + self.image1.set_from_file( self.image_file_small ) + ## Set progress and refresh view. def progress(self, fract, text): - """ Set progress and refresh view. """ self.progressbar1.set_fraction(fract) self.progressbar1.set_text(text) self.refresh() + ## Character correction after recogition (on basis that there should be numbers and few special chars). def char_correction(self, data): - """ Character correction after recogition (on basis that there should be numbers and few special chars). """ data = re.sub("\n", "", data) print data corrections = [ @@ -662,22 +755,23 @@ return data - +## Run external shell command/application. class RunExternal: - """ Run external shell command/application. """ error_msg = [ "/bin/sh: " ] + ## initialize def __init__(self, cmd, error_msg): self.cmd = cmd self.error_msg += error_msg + ## call def __call__(self): (self.error, self.stdout, self.stderr) = self._run() return self.error + ## Execute external shell command. def _run(self, piped=True): - """ Execute external shell command. """ if piped: run = subprocess.Popen( self.cmd, stdin =subprocess.PIPE, @@ -773,16 +867,16 @@ # # return (False, "ok", "") +## sSANE/PIL interface python wrapper/bindings. class RunSANE: - """ SANE/PIL interface python wrapper/bindings. """ # "scanimage --format=tif --resolution 300 --mode Gray > %s.tif" # "scanimage --format=tif --resolution 600 --mode Gray -t 82 -y 20 -l 60 -x 155.9 > %s.tif" #cmd_scan = "scanimage --format=tif --resolution 600 --mode Gray -t %d -l %d -y %d -x %d > %s.tif" resolution = 600 + ## Init of sane interface -> device. def __init__(self, progress, window2, treeview): - """ Init of sane and scanner. """ print "init sane python interface ...", sys.stdout.flush() @@ -794,6 +888,9 @@ treeview.set_model(list_store) col = gtk.TreeViewColumn("Devices", gtk.CellRendererText(),text=0) treeview.append_column(col) + + # get blacklist + self.read_blacklist() # init SANE self.n=4. @@ -801,15 +898,28 @@ try: self.version = sane.init() progress(1./self.n,"init sane python interface ... search devices") - self.devices = sane.get_devices() + # get sane devices + self.devices1 = sane.get_devices() + # init filtered list + self.devices = [] + print "\n" # add found devices to treeview - for i in range(len(self.devices)): - print str(i)+": "+self.devices[i][1]+" "+self.devices[i][2] - list_store.append([self.devices[i][1]+" "+self.devices[i][2]]) + for i in range(len(self.devices1)): + # filter devices: remove blacklisted devices + if not self.devices1[i][1]+" "+self.devices1[i][2] in self.blacklist: + self.devices.append(self.devices1[i]) + print str(i)+": "+self.devices1[i][1]+" "+self.devices1[i][2] + list_store.append([self.devices1[i][1]+" "+self.devices1[i][2]]) + else: + print str(i)+": "+self.devices1[i][1]+" "+self.devices1[i][2]+" BLOCKED" + # self.devices is now filtered # check how many devices we found - if len(self.devices) > 1: + if len(self.devices) == 0: + progress(0./self.n,"No device found.") + self.found_scanner = False + elif len(list_store) > 1: # more than one device, choose one. progress(2./self.n,"More than one device found.") if sys.argv[1:]: @@ -832,12 +942,14 @@ # No device found at all self.found_scanner = False print "No sane device found. Restart to try it again." - + + ## iInit of sane interface -> scanner def init_scanner(self, progress, window2, treeview, devnr): - + print "Use device number: "+str(devnr) try: # finish init device - self.dev = sane.get_devices()[devnr][0] # choose first device + self.dev = self.devices[devnr][0] # choose first device + print self.dev progress(3./self.n,"Device initialized. Open scanner...") # open scanner device @@ -847,7 +959,7 @@ self.found_scanner = True - progress(4./self.n,"Ready.") + progress(4./self.n,"Ready: %s" % self.devices[devnr][1]+" "+self.devices[devnr][2]) print "done." except: @@ -856,20 +968,21 @@ print "Loading sane device failed. Restart to try it again." + ## Not init of sane and scanner, but of scan operation. def post_init(self, coords, out_filename): - """ Not init of sane and scanner, but of scan operation. """ self.coords = coords self.out_filename = out_filename self.info() + ## call def __call__(self): (self.error, self.stdout, self.stderr) = self._run() return self.error - # thanks to: http://mail.python.org/pipermail/image-sig/1997-June/000307.html - # (may be look also at: http://sane-pygtk.sourceforge.net/) + ## Scan and save the PIL image object to file. + # @see http://mail.python.org/pipermail/image-sig/1997-June/000307.html + # @see http://sane-pygtk.sourceforge.net/) def _run(self): - """ Scan and save the PIL image object to file. """ try: # Set scan parameters (also with higher resolution!) (t, l, y, x) = self.coords @@ -885,7 +998,7 @@ # Get an Image object containing the scanned image im = self.scanner.snap() - + # Write the image out as a TIFF file (or whatever) im.save(self.out_filename) @@ -895,8 +1008,8 @@ return (False, "ok", "") + ## Show some info about the scanner and SANE. def info(self): - """ Show some info about the scanner and SANE. """ print 'SANE version:', self.version print 'Available devices=', self.devices print 'Selected device=', self.dev #, "\n" @@ -905,10 +1018,24 @@ print 'Device parameters:', self.params #print 'Device options:', "\n",self.opts + ## get blacklisted devices + def read_blacklist(self): + bl = open('%s/blacklist' % local_path, 'r' ) + temp = bl.readlines() + bl.close + self.blacklist = [] + for line in temp: + self.blacklist.append(line.strip()) + ## write blacklisted devices + # @todo add UI way to blacklist device + def write_blacklist(self, blacklist): + bl = open('%s/blacklist' % local_path, 'w' ) + bl.write(blacklist) + bl.close() if __name__ == '__main__': - # run main application + ## run main application main = MainWindowGTK() main.run() Added: __init__.py =================================================================== Added: blacklist =================================================================== --- blacklist (rev 0) +++ blacklist 2010-10-25 18:39:31 UTC (rev 27) @@ -0,0 +1 @@ +Printer Name Added: checksum.py =================================================================== --- checksum.py (rev 0) +++ checksum.py 2010-10-25 18:39:31 UTC (rev 27) @@ -0,0 +1,19 @@ +## @file checksum.py +# Checksum for 13 digit e-banking codes +class modulo10: + # thanks to http://www.hosang.ch/modulo10.aspx + # much more details http://www.sic.ch/de/dl_tkicch_dta.pdf ( page 51 ) + def __init__(self): + self.tabelle = [0,9,4,6,8,2,7,1,3,5] + self.uebertrag = 0 + + def run(self,number): + # make string + nr = str(number) + # iterate over each character + for i in range(len(nr)): + self.uebertrag = self.tabelle[(self.uebertrag + int(nr[i]))%10] + + return self.uebertrag + + This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <la...@us...> - 2010-11-11 19:20:01
|
Revision: 29 http://pycodeocr.svn.sourceforge.net/pycodeocr/?rev=29&view=rev Author: laserb Date: 2010-11-11 19:19:54 +0000 (Thu, 11 Nov 2010) Log Message: ----------- Important bug fix! Corrected checksum calculation. Don't break if checksum is wrong but warn user, so if the code is recognized correct but checksum fails we can still use it. Added ckecksum for all length of code and for all three parts amount, account number and reference number. Modified Paths: -------------- PyCodeOCR.py checksum.py Modified: PyCodeOCR.py =================================================================== --- PyCodeOCR.py 2010-11-09 07:44:23 UTC (rev 28) +++ PyCodeOCR.py 2010-11-11 19:19:54 UTC (rev 29) @@ -576,17 +576,37 @@ # (5/?) self.progress(5./max_steps, "Check on validity...") if (op_mode == self.MDE['invoices']): # 0: invoices - check = (not "?" in data) # any unrecognized char in code? + check = (not "?" in data) or (not "!" in data) # any unrecognized char in code? check = check and ( len(data) in opt['valid_code_len'] ) # correct code len? - if len(data) == 42: - # extract details - (tmp, betrag, tmp, referenz, tmp, konto, tmp) = struct.unpack("2s11ss16s2s9ss",data) - print "Betrag: "+str(int(betrag[:-1])/100.) - print "Konto: "+konto[:2]+"-"+konto[3:-2]+"-"+konto[-2:] - print "Referenznr: "+referenz - # modulo10 checksum for betrag - checknr = modulo10().run(int(betrag[:-1])) - check = check and checknr == int(betrag[-1]) + if check: + tmp = data[:-1].split(">") + amount = tmp[0] + tmp = tmp[1].split("+ ") + reference = tmp[0] + account = tmp[1] + # initialize modulo10 checksum + m10 = modulo10() + # check amount, account number and reference number + checksum_b = (int(amount[-1]) == m10.run(amount[:-1]) ) + checksum_k = (int(account[-1]) == m10.run(account[:-1]) ) + checksum_r = (int(reference[-1]) == m10.run(reference[:-1]) ) + print "Amount: "+str(int(amount[2:-1])/100.),checksum_b + print "Account number: "+account,checksum_k + print "Reference number: "+reference,checksum_r + + checksum = checksum_b and checksum_k and checksum_r + +# if len(data) == 42: +# # extract details +# (tmp, betrag, tmp, referenz, tmp, konto, tmp) = struct.unpack("2s11ss16s2s9ss",data) +# print "Betrag: "+str(int(betrag[:-1])/100.) +# print "Konto: "+konto[:2]+"-"+konto[3:-2]+"-"+konto[-2:] +# print "Referenznr: "+referenz +# if check: +# # modulo10 checksum for betrag +# checknr = modulo10().run(betrag[:-1]) +# print "Checknr: ",checknr +# checknr = ( checknr == int(betrag[-1]) ) elif (op_mode == self.MDE['barcode']): # 1: barcode check = not (data['type'] == "unknown") if check: @@ -632,8 +652,10 @@ # make our data available to other applications clipboard.store() #print "data sent to (gtk/gnome) clipboard" - - self.progress(1., "Code recognized and sent to clipboard. Finished.") + if checksum: + self.progress(1., "Code recognized and sent to clipboard. Finished.") + else: + self.progress(1., "Code recognized and sent to clipboard, BUT checksum failed! CHECK code again! Finished.") else: # create debug info output log = file(self.debug[1], "w") Modified: checksum.py =================================================================== --- checksum.py 2010-11-09 07:44:23 UTC (rev 28) +++ checksum.py 2010-11-11 19:19:54 UTC (rev 29) @@ -1,19 +1,19 @@ ## @file checksum.py -# Checksum for 13 digit e-banking codes +# Modulo 10 checksum for e-banking codes class modulo10: # thanks to http://www.hosang.ch/modulo10.aspx # much more details http://www.sic.ch/de/dl_tkicch_dta.pdf ( page 51 ) + # @see http://www.bundesbank.de/download/zahlungsverkehr/zv_pz201012.pdf + # @see http://www.bundesbank.de/zahlungsverkehr/zahlungsverkehr_pruefziffernberechnung.php def __init__(self): self.tabelle = [0,9,4,6,8,2,7,1,3,5] + + def run(self,nr): self.uebertrag = 0 - - def run(self,number): - # make string - nr = str(number) # iterate over each character for i in range(len(nr)): - self.uebertrag = self.tabelle[(self.uebertrag + int(nr[i]))%10] + self.uebertrag = self.tabelle[(self.uebertrag + int(nr[i]) )%10] - return self.uebertrag + return (10-self.uebertrag)%10 This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <drt...@us...> - 2011-04-27 12:25:27
|
Revision: 44 http://pycodeocr.svn.sourceforge.net/pycodeocr/?rev=44&view=rev Author: drtrigon Date: 2011-04-27 12:25:14 +0000 (Wed, 27 Apr 2011) Log Message: ----------- Thanks to the guys from VisTrails at the University of Utah we have code now for automatic dependency check and installation of needed repos. This works for fedora and ubuntu at the moment. Modified Paths: -------------- PyCodeOCR.py Added Paths: ----------- installbundle.py requirements.py Property Changed: ---------------- PyCodeOCR.py checksum.py Modified: PyCodeOCR.py =================================================================== --- PyCodeOCR.py 2011-04-26 21:19:41 UTC (rev 43) +++ PyCodeOCR.py 2011-04-27 12:25:14 UTC (rev 44) @@ -10,6 +10,14 @@ import os, re, sys, subprocess, time, gobject, string, struct +# check and install requirements +#print "To check and install requirements run: 'python %s --install-deps'" % sys.argv[0] +import requirements +if "--install-deps" in sys.argv: + requirements.check_all_pycodeocr_requirements() + sys.exit() + + # GTK, PyGTK, GLADE (GNOME) modules import pygtk pygtk.require('2.0') @@ -33,19 +41,26 @@ os.system('python %s' % sys.argv[0]) # sys.exit() # ## list of available pakages +print "To check and install requirements run: 'python %s --install-deps'" % sys.argv[0] pakages = [] # gocr -if os.path.exists("gocr"): +paths = { "gocr": requirements.which("gocr"), + "dmtxread": requirements.which("dmtxread"), + "pdf417decode.exe": requirements.which("pdf417decode.exe"), } +if os.path.exists(paths["gocr"]): pakages.append( "gocr" ) print "gocr found." # libdmtx bindings -try: - #from pydmtx import DataMatrix +#try: +# from pydmtx import DataMatrix +# pakages.append( "libdmtx" ) +# print "libdmtx and pydmtx found." +#except: pass +if os.path.exists(paths["dmtxread"]): pakages.append( "libdmtx" ) print "libdmtx and pydmtx found." -except: pass # pdf417decode -if os.path.exists("pdf417decode.exe"): +if os.path.exists(paths["pdf417decode.exe"]): pakages.append( "pdf417decode" ) print "pdf417decode.exe found." @@ -76,7 +91,7 @@ # initialization # (variables) max_retry = 3 # max. scan attempts - valid_code_len = [ 53, 42, 32 ] # valid code lenghts + valid_code_len = [ 53, 42, 32 ] # valid code lenghts scan_koords = std_scan_koords["0 deg"][0] # default scan pos. mode = std_scan_koords["0 deg"][1] # default scan orient. res_color = { True: gtk.gdk.color_parse("#00FF00"), @@ -87,11 +102,11 @@ os.path.join(home_path, "PyCodeOCR_debug.txt"), ) regex_gocr = re.compile('<barcode(.*?)/>') - #cmd_scan = "scanimage --format=tif --resolution 600 --mode Gray -t %d -l %d -y %d -x %d > %s" # SANE scan command - cmd_convert = "convert %s -depth 8 -rotate %d %s" # ImageMagick command - cmd_tesser = "tesseract %s.tif %s" # TesserAct command - cmd_gocr = os.path.join(local_path, "gocr") + " -i %s.pnm" # GOCR command (local) - cmd_pdf417dec = "wine " + os.path.join(local_path, "pdf417decode.exe") + " %s.png" # pdf417decode/wine command (local) + #cmd_scan = "scanimage --format=tif --resolution 600 --mode Gray -t %d -l %d -y %d -x %d > %s" # SANE scan command + cmd_convert = "convert %s -depth 8 -rotate %d %s" # ImageMagick command + cmd_tesser = "tesseract %s.tif %s" # TesserAct command + cmd_gocr = paths["gocr"] + " -i %s.pnm" # GOCR command (local) + cmd_pdf417dec = "wine " + paths["pdf417decode.exe"] + " %s.png" # pdf417decode/wine command (local) __stop = False @@ -440,13 +455,13 @@ self.mode = std_scan_koords["A4"][1] #pydmtx recognition # opt = { 'tmp_file': "02.jpg", # some issues with recogition -# 'recog_class': RunLibdmtx, # . -# 'recog_cmd': self.temp+"02.jpg", # of different file formats +# 'recog_class': RunLibdmtx, # . +# 'recog_cmd': self.temp+"02.jpg", # of different file formats # 'recog_error_msg': [ None ], # . #dmtxread recognition - opt = { 'tmp_file': "02.jpg", # some issues with recogition - 'recog_class': RunExternal, # . - 'recog_cmd': "dmtxread "+self.temp+"02.jpg", # of different file formats + opt = { 'tmp_file': "02.jpg", # some issues with recogition + 'recog_class': RunExternal, # . + 'recog_cmd': "%s %s02.jpg"%(paths["dmtxread"],self.temp), # of different file formats 'recog_error_msg': [ "error" ], # . #opt = { 'tmp_file': "02.bmp", # and drawing/marking the Property changes on: PyCodeOCR.py ___________________________________________________________________ Added: svn:keywords + Id Property changes on: checksum.py ___________________________________________________________________ Added: svn:keywords + Id Added: installbundle.py =================================================================== --- installbundle.py (rev 0) +++ installbundle.py 2011-04-27 12:25:14 UTC (rev 44) @@ -0,0 +1,158 @@ +############################################################################ +## +## Copyright (C) 2006-2010 University of Utah. All rights reserved. +## +## This file is part of VisTrails. +## +## This file may be used under the terms of the GNU General Public +## License version 2.0 as published by the Free Software Foundation +## and appearing in the file LICENSE.GPL included in the packaging of +## this file. Please review the following to ensure GNU General Public +## Licensing requirements will be met: +## http://www.opensource.org/licenses/gpl-license.php +## +## If you are unsure which license is appropriate for your use (for +## instance, you are interested in developing a commercial derivative +## of VisTrails), please contact us at vis...@sc.... +## +## This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +## WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +## +## Changed 2011 by drtrigon. Source from 'core.bundles.installbundle' +## http://downloads.sourceforge.net/project/vistrails/vistrails/nightly/vistrails-src-nightly.tar.gz +## +## This file was modified to be used as part of PyCodeOCR. +## +############################################################################ + +"""Module with utilities to try and install a bundle if possible.""" + +import os + +############################################################################## + +def linux_ubuntu_install(package_name): + qt = has_qt() + hide_splash_if_necessary() + + if qt: + cmd = core.system.vistrails_root_directory() + cmd += '/core/bundles/linux_ubuntu_install.py' + else: + cmd = 'apt-get install -y' + + if type(package_name) == str: + cmd += ' ' + package_name + elif type(package_name) == list: + for package in package_name: + if type(package) != str: + raise TypeError("Expected string or list of strings") + cmd += ' ' + package + + if qt: + sucmd = guess_graphical_sudo() + " '" + cmd + "'" + else: + debug.warning("PyCodeOCR wants to install package(s) '%s'" % + package_name) + sucmd = "sudo " + cmd + + result = os.system(sucmd) + + return (result == 0) # 0 indicates success + +def linux_fedora_install(package_name): + qt = has_qt() + hide_splash_if_necessary() + if qt: + cmd = core.system.vistrails_root_directory() + cmd += '/core/bundles/linux_fedora_install.py' + else: + cmd = 'yum -y install' + + if type(package_name) == str: + cmd += ' ' + package_name + elif type(package_name) == list: + for package in package_name: + if type(package) != str: + raise TypeError("Expected string or list of strings") + cmd += ' ' + package + + if qt: + sucmd = guess_graphical_sudo() + " " + cmd + else: + debug.warning(("PyCodeOCR wants to install package(s) '%s' through " + "_sudo_. Make sure you are a sudoer.") % package_name) + sucmd = "sudo " + cmd + + debug.warning("EXECUTING: sucmd") + result = os.system(sucmd) + debug.warning("RETURN VALUE: %s" % result) + return (result == 0) + +def show_question(which_files): + qt = has_qt() + if qt: + import gui.utils + if type(which_files) == str: + which_files = [which_files] + v = gui.utils.show_question("Required packages missing", + "One or more required packages are missing: " + + " ".join(which_files) + + ". PyCodeOCR can " + + "automaticallly install them. " + + "If you click OK, PyCodeOCR will need "+ + "administrator privileges, and you " + + "might be asked for the administrator password.", + buttons=[gui.utils.OK_BUTTON, + gui.utils.CANCEL_BUTTON], + default=gui.utils.OK_BUTTON) + return v == gui.utils.OK_BUTTON + else: + print "Required package missing" + print ("A required package is missing, but PyCodeOCR can " + + "automatically install it. " + + "If you say Yes, PyCodeOCR will need "+ + "administrator privileges, and you" + + "might be asked for the administrator password.") + print "Give PyCodeOCR permission to try to install package? (y/N)" + v = raw_input().upper() + return v == 'Y' or v == 'YES' + + +def install(dependency_dictionary): + """Tries to import a python module. If unsuccessful, tries to install +the appropriate bundle and then reimport. py_import tries to be smart +about which system it runs on.""" + + # Ugly fix to avoid circular import + distro = guess_system() + if not dependency_dictionary.has_key(distro): + return False + else: + files = dependency_dictionary[distro] + if show_question(files): + callable_ = getattr(installbundle, + distro.replace('-', '_') + '_install') + return callable_(files) + else: + return False + +############################################################################## + +def guess_system(): + import platform + #print os.name, platform.platform() + return ("%s-%s" % (platform.system(), platform.dist()[0])).lower() + +def has_qt(): + return False + +import installbundle + +def hide_splash_if_necessary(): + pass + +class dbg(): + def warning(self, *args): + print "".join(args) +debug = dbg() Property changes on: installbundle.py ___________________________________________________________________ Added: svn:keywords + Id Added: svn:eol-style + native Added: requirements.py =================================================================== --- requirements.py (rev 0) +++ requirements.py 2011-04-27 12:25:14 UTC (rev 44) @@ -0,0 +1,186 @@ +############################################################################ +## +## Copyright (C) 2006-2010 University of Utah. All rights reserved. +## +## This file is part of VisTrails. +## +## This file may be used under the terms of the GNU General Public +## License version 2.0 as published by the Free Software Foundation +## and appearing in the file LICENSE.GPL included in the packaging of +## this file. Please review the following to ensure GNU General Public +## Licensing requirements will be met: +## http://www.opensource.org/licenses/gpl-license.php +## +## If you are unsure which license is appropriate for your use (for +## instance, you are interested in developing a commercial derivative +## of VisTrails), please contact us at vis...@sc.... +## +## This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +## WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +## +## Changed 2011 by drtrigon. Source from 'core.requirements' +## http://downloads.sourceforge.net/project/vistrails/vistrails/nightly/vistrails-src-nightly.tar.gz +## +## This file was modified to be used as part of PyCodeOCR. +## +############################################################################ + +"""module that allows online inspection of environment to test presence of +runtime components such as binaries, libraries, other python modules, etc.""" + +import sys + +############################################################################## + +def python_module_exists(module_name): + """python_module_exists(module_name): Boolean. +Returns if python module of given name can be safely imported.""" + + try: + sys.modules[module_name] + return True + except KeyError: + pass + try: + __import__(module_name) + return True + except ImportError: + return False + + +def executable_file_exists(filename): + """executable_file_exists(filename): Boolean. +Returns if certain file is in current path and is executable.""" + result = executable_is_in_path(filename) + if result == "": + result = executable_is_in_pythonpath(filename) + return result != "" + +# FIXME: Add documentation. + +def require_python_module(module_name): + if not python_module_exists(module_name): + raise MissingRequirement(module_name) + +def require_executable(filename): + if not executable_file_exists(filename): + raise MissingRequirement(filename) + +def check_pyqt4(): + # checks for the presence of pyqt4, which is more important than the rest, + # since using pyqt requires a qapplication. + try: + require_python_module('PyQt4.QtGui') + require_python_module('PyQt4.QtOpenGL') + except MissingRequirement: + r = core.bundles.installbundle.install({'linux-ubuntu': ['python-qt4', + 'python-qt4-gl', + 'python-qt4-sql']}) + if not r: + raise + + +def check_all_vistrails_requirements(): + pass + + # check scipy +# try: +# require_python_module('scipy') +# except MissingRequirement: +# r = core.bundles.installbundle.install({'linux-ubuntu': 'python-scipy'}) +# if not r: +# raise + + +############################################################################## + +class MissingRequirement(Exception): + """Raise this exception in packages when necessary items are missing.""" + def __init__(self, req): + self.requirement = req + def __str__(self): + return "Missing Requirement: %s" % self.requirement + +############################################################################## + +import installbundle +import os + + +# http://stackoverflow.com/questions/377017/test-if-executable-exists-in-python +def which(program, search_path=None): + is_exe = lambda fpath: os.path.exists(fpath) and os.access(fpath, os.X_OK) + #is_exe = lambda fpath: os.path.exists(fpath) + + fpath, fname = os.path.split(program) + if fpath: + if is_exe(program): + return program + else: + if not search_path: + search_path = os.environ["PATH"].split(os.pathsep) + search_path += [ '.' ] # add local path + for path in search_path: + exe_file = os.path.join(path, program) + if is_exe(exe_file): + return exe_file + return "" + +def executable_is_in_path(filename): + #print os.system(filename) + return which(filename) + +def executable_is_in_pythonpath(filename): + return which(filename, search_path=os.environ.get("PYTHONPATH", None)) + + +def check_pygtk(): + # checks for the presence of pygtk, which is more important than the rest. + try: + require_python_module('pygtk') + #pygtk.require('2.0') + require_python_module('gtk') + require_python_module('gtk.glade') + require_python_module('gobject') + except MissingRequirement: + r = core.bundles.installbundle.install({'linux-fedora': ['pygtk2']}) + # other deps? + if not r: + raise + +def check_sane(): + try: + require_python_module('sane') + except MissingRequirement: + r = core.bundles.installbundle.install({'linux-fedora': ['python-imaging-sane']}) + # other deps? + if not r: + raise + +def check_optionals(): + try: + require_executable("gocr") + except MissingRequirement: + r = installbundle.install({'linux-fedora': ['gocr'], + 'linux-ubuntu': ['gocr']}) + + try: + #require_python_module('pydmtx') + require_executable("dmtxread") + except MissingRequirement: + r = installbundle.install({'linux-fedora': ['libdmtx-utils'], + 'linux-ubuntu': ['libdmtx-utils']}) + + +def check_all_pycodeocr_requirements(): + print "Checking all PyCodeOCR requirements:" + + # needed + print " * NEEDED 'pygtk2'" + check_pygtk() + print " * NEEDED 'sane' (scanner)" + check_sane() + + # optional + print " * OPTIONAL 'gocr', 'dmtxread'" + check_optionals() Property changes on: requirements.py ___________________________________________________________________ Added: svn:keywords + Id Added: svn:eol-style + native This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |