Main files of the project

This commit is contained in:
NeoXgroupEU 2018-08-29 17:04:00 +02:00 committed by GitHub
parent ccf4adc566
commit 6a5a01c117
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 2713 additions and 0 deletions

50
CNI_GLOBALVAR.py Normal file
View File

@ -0,0 +1,50 @@
"""
********************************************************************************
*** Projet CNI_Revelator ***
GNU GPL * 07/2018
Adrien Bourmault
VARIABLES
********************************************************************************
"""
CST_REV = "8"
CST_VERTITLE = "2.2"
CST_TAB_VER = ["2","2","1"]
CST_VER = "{0}.{1}.{2}".format(CST_TAB_VER[0], CST_TAB_VER[1], CST_TAB_VER[2])
CST_TYPE = "Final Release"
CST_NAME = "CNIRevelator"
CST_TITLE = CST_NAME + " " + CST_VER + " - GNU/GPL Licensing 2018"
CST_MARK = CST_NAME + " " + CST_TYPE + " " + CST_VER + " - by NeoX, GNU/GPL Licensing 2018"
CST_SUM_VER = int(CST_TAB_VER[0])*100 + int(CST_TAB_VER[1])*10 + int(CST_TAB_VER[2])
CST_LINK = <lien vers serveur>
CST_COLOR = "#003380"
import base64
import hashlib
from Crypto import Random
from Crypto.Cipher import AES
from tkinter import *
from tkinter.messagebox import *
from tkinter import filedialog
from tkinter import ttk as ttk
import os
import time
import threading
import sys
import urllib.request as urllib2
import urllib.error as URLExcept
import random
from pypac import PACSession
from requests.auth import HTTPProxyAuth
import subprocess
from datetime import datetime
from PIL import Image, ImageFont, ImageDraw, ImageTk, ImageEnhance, ImageFilter
import math
import warnings
import string
CST_FOLDER = os.getenv('APPDATA') + "/CNIRevelator/"

131
CNI_Revelator.py Normal file
View File

@ -0,0 +1,131 @@
"""
********************************************************************************
*** Projet CNI_Revelator ***
GNU GPL * 07/2018
Adrien Bourmault
main
********************************************************************************
"""
###IMPORTS GLOBAUX
from CNI_GLOBALVAR import *
##LOGGING
try:
os.remove("error.log")
os.remove("conf.ig")
except:
pass
import logging
CST_NIVEAU_LOG = logging.ERROR
from logging.handlers import RotatingFileHandler
# création de l'objet logger qui va nous servir à écrire dans les logs
logger = logging.getLogger()
# on met le niveau du logger à DEBUG, comme ça il écrit tout
logger.setLevel(CST_NIVEAU_LOG)
# création d'un formateur qui va ajouter le temps, le niveau
# de chaque message quand on écrira un message dans le log
formatter = logging.Formatter('%(asctime)s :: %(levelname)s :: %(message)s')
# création d'un handler qui va rediriger une écriture du log vers
# un fichier en mode 'append', avec 1 backup et une taille max de 1Mo
file_handler = RotatingFileHandler('error.log', 'a', 1000000, 1)
# on lui met le niveau sur DEBUG, on lui dit qu'il doit utiliser le formateur
# créé précédement et on ajoute ce handler au logger
file_handler.setLevel(CST_NIVEAU_LOG)
file_handler.setFormatter(formatter)
logger.addHandler(file_handler)
##History
### IMPORTS LOCAUX
from CNI_classes import *
from CNI_Update import *
### FONCTION PRINCIPALE
def main(logger):
logger.info("main() : " +"**** Creating App_main() ****")
main_w = App_main(logger)
main_w.montext("* " + CST_NAME + " " + CST_VER + " " + CST_TYPE + " Revision " + CST_REV + " *\n")
import CNI_pytesseract as pytesseract
try:
os.environ["PATH"] = CST_FOLDER + "Tesseract-OCR4\\"
os.environ["TESSDATA_PREFIX"] = CST_FOLDER + "Tesseract-OCR4\\tessdata"
tesser_version = pytesseract.get_tesseract_version()
except Exception as e:
logger.error("main() : " +"**** ERROR WITH TESSERACT MODULE " + str(e) + " ****")
else:
text = "Tesseract version " + str(tesser_version) +" Licensed Apache 2004 successfully initiated\n"
main_w.montext(text)
main_w.montext("\n\nEntrez la première ligne de MRZ svp \n")
logger.info("main() : " +"**** Launching App_main() ****")
main_w.mainloop()
logger.info("main() : " +"**** Ending App_main() ****")
##Launcher
logger.info("launcher : " + CST_NAME +" "+ CST_VER)
logger.info("launcher : " +"*****Hello World*****")
logger.info("launcher : " +"*****Launching SoftUpdate()*****")
try:
Answer = SoftUpdate(logger)
except Exception as e:
logger.info("launcher : " +"*****FATAL ERROR*****" + str(e))
os.abort()
logger.info("launcher : " +"*****Ending SoftUpdate()*****")
try:
if Answer == True:
logger.info("launcher : " +"*****Launching main()*****")
State = main(logger)
except Exception as e:
logger.info("launcher : " +"*****FATAL ERROR*****" + str(e))
os.abort()
logger.info("launcher : " +"*****Ending main()*****")
logger.info("launcher : " +"*****Goodbye!*****")
handlers = logger.handlers[:]
for handler in handlers:
handler.close()
logger.removeHandler(handler)
if os.path.getsize("error.log") == 0:
try:
os.remove("error.log")
except:
raise(OSError)
os.abort()
sys.exit(0)

568
CNI_Update.py Normal file
View File

@ -0,0 +1,568 @@
"""
********************************************************************************
*** Projet CNI_Revelator ***
GNU GPL * 07/2018
Adrien Bourmault
UPDATE
********************************************************************************
"""
###IMPORTS GLOBAUX
from CNI_GLOBALVAR import *
###IMPORTS LOCAUX
from CNI_classes import *
###BEGIN
def SoftUpdate(logger): #Fonction de mise à jour de l'application
import zipfile
# zip_ref = zipfile.ZipFile(path_to_zip_file, 'r')
# zip_ref.extractall(directory_to_extract_to)
# zip_ref.close()
logger.info("SoftUpdate() : " +"Verifying local data dir...")
if not os.path.exists(CST_FOLDER):
os.makedirs(CST_FOLDER)
logger.info("SoftUpdate() : " +"Data dir created !")
logger.info("SoftUpdate() : " +"Looking for older version in dir...")
list = os.listdir('.')
for file in list:
if file.startswith("CNIRevelator_"):
temp = ["0","0","0"]
ver = file[13:].split(".")
for i in range(len(ver)):
if ver[i] != "exe" :
try:
temp[i] = ver[i]
except:
None
ver = temp.copy()
try :
sum_ver = int(ver[0])*100 + int(ver[1])*10 + int(ver[2])
if sum_ver < CST_SUM_VER:
os.remove(file)
logger.info("SoftUpdate() : " + "Removed old version : " + str(file) )
except:
logger.error("SoftUpdate() : " +"Failing to remove old version : " + str(file) )
None
def updating(): #Fonction de lancement du thread de maj
def updator(): #Fonction d'exécution de la maj
logger.info("[updator() thread] : " + "Welcome !")
global ret
ret = 11
canvas.itemconfigure(message, text="Recherche de mises-à-jour...")
p.configure(mode = "indeterminate", value = 0, maximum = 20)
p.start()
upwin.update()
logger.info("[updator() thread] : " + "Looking for updates...")
try:#Essai par proxy
def download(url, filename):
try: # if identifiants disponibles dans un fichier
logger.info("[download() thread] : " + "Trying getting credentials in the config file")
with open(CST_FOLDER +'conf.ig', "rb") as config:
AESObj = AESCipher("<clé>")
IPN, IPN_PASS = AESObj.decrypt(config.read()).split("||")[0:2]
logger.info("[download() thread] : " + "Got credentials !")
session = PACSession(proxy_auth=HTTPProxyAuth(IPN, IPN_PASS))
logger.info("[download() thread] : " + "Authenticated to proxy successfully")
except IOError as e: #Else : on vérifie les identifiants de proxy
logger.error("[download() thread] : " + "False or absent credentials in the config file : " + str(e))
NoConnect = True
while NoConnect:
class LoginDialog(Toplevel):
global login
global key
def __init__(self, parent):
super().__init__(parent)
self.title("Connexion")
# ---------------------------------------
Label(self, text="IPN : ").pack()
self.entry_login = Entry(self)
self.entry_login.insert(0, "")
self.entry_login.pack()
# ---------------------------------------
Label(self, text="Mot de passe : ").pack()
self.entry_pass = Entry(self, show='*')
self.entry_pass.insert(0, "")
self.entry_pass.pack()
# ---------------------------------------
Button(self, text="Connexion", command=self.connecti).pack()
self.resizable(width=False, height=False)
#taille souhaite de la fenetre
w = 150
h = 110
#pour centrer la fenetre
#taille de l'ecran
self.update()
ws = self.winfo_screenwidth()
hs = self.winfo_screenheight()
if getattr( sys, 'frozen', False ) :
self.iconbitmap(sys._MEIPASS + "\id-card.ico\id-card.ico")
else:
self.iconbitmap("id-card.ico")
upwin.update()
#calcul la position de la fenetre
x = (ws/2) - (w/2)
y = (hs/2) - (h/2)
#applique la taille et la position
self.geometry('%dx%d+%d+%d' % (w, h, x, y))
def connecti(self):
global login
global key
login = self.entry_login.get().strip()
key = self.entry_pass.get().strip()
self.destroy()
session = PACSession()
if session.get_pac() == None:
IPN = ""
IPN_PASS = ""
NoConnect = False
break
canvas.itemconfigure(message, text="En attente de connexion au serveur proxy...")
global login
global key
login = ""
key = ""
result = LoginDialog(upwin)
result.transient(upwin)
result.grab_set()
upwin.wait_window(result)
IPN = login
IPN_PASS = key
session = PACSession(proxy_auth=HTTPProxyAuth(IPN, IPN_PASS))
Ans = session.get("http://www.google.com")
if str(Ans) == "<Response [407]>":
canvas.itemconfigure(message, text="Identifiants erronés, accès refusé")
logger.info("[download() thread] : " + "407 Error")
time.sleep(1)
elif str(Ans) == "<Response [200]>":
logger.info("[download() thread] : " + "Connection ok !")
NoConnect = False
else:
raise(IOError())
AESObj = AESCipher("<clé>")
with open(CST_FOLDER +"conf.ig","wb+") as f:
logger.info("[download() thread] : " + "Saving credentials in encrypted config file")
f.write(AESObj.encrypt(IPN + "||" + IPN_PASS))
if IPN == "i005316":
canvas.itemconfigure(message, text="Bienvenue Thierry !")
elif IPN == "i020251":
canvas.itemconfigure(message, text="Bienvenue Samia !")
elif IPN == "i018410":
canvas.itemconfigure(message, text="Bienvenue Adrien !")
elif IPN == "i003067":
canvas.itemconfigure(message, text="Bienvenue Remy !")
elif IPN == "i018422":
canvas.itemconfigure(message, text="Bienvenue Eloise !")
time.sleep(1)
try:
Prox_us = session.get_pac().find_proxy_for_url(CST_LINK,"neoxgroup.eu")
PROXY_USABLE = Prox_us[6:-1].split(";")[0]
proxy_server_url = IPN +":"+IPN_PASS+"@" + PROXY_USABLE
ph = urllib2.ProxyHandler( { 'http' : proxy_server_url } )
auth = urllib2.ProxyBasicAuthHandler()
server= urllib2.build_opener( ph, auth, urllib2.HTTPHandler )
urllib2.install_opener(server)
logger.info("[download() thread] : " + "Proxy connection initiated successfully")
except:
logger.info("[download() thread] : " + "Proxy connection not initiated")
try:
urllib2.urlretrieve(url, filename)
return True
except Exception as e:
logger.error("[download() thread] : " + "HTTP ERROR " )
return e
##updator()
logger.info("[updator() thread] : " + "Prepare downloading the version recap file...")
tempfile = CST_FOLDER + 'temp' + str(random.randint(11111,99999)) + ".cniu"
isOk = download(CST_LINK+"Version.txt", tempfile)
if not isOk:
raise(isOk)
urllib2.urlcleanup()
logger.info("[updator() thread] : " + "Opening version recap file...")
file_ver = open(tempfile, "r")
logger.info("[updator() thread] : " + "Reading version recap file...")
version = file_ver.read()
logger.info("[updator() thread] : " + "Closing version recap file...")
repert = version.split("|")
file_ver.close()
logger.info("[updator() thread] : " + "Deleting version recap file...")
os.remove(tempfile)
logger.info("[updator() thread] : " + "Parsing informations about version...")
final_f = "CNI_file"
final_ver = ['0','0','0']
for file in repert:
if str.startswith(file,CST_NAME): #On prend les fichiers d'interêt du répertoire
ver = file.replace(CST_NAME+"_","").split(".") #On ne garde que le numéro de version
temp = ["0","0","0"]
for i in range(len(ver)):
temp[i] = ver[i]
ver = temp.copy()
sum_fver = int(final_ver[0])*100 + int(final_ver[1])*10 + int(final_ver[2])
sum_ver = int(ver[0])*100 + int(ver[1])*10 + int(ver[2])
if sum_ver > sum_fver:
final_ver = ver.copy()
final_f = file
sum_ver = int(final_ver[0])*100 + int(final_ver[1])*10 + int(final_ver[2])
if final_f != "CNI_file" and (sum_ver > CST_SUM_VER):
#On a une maj
logger.info("[updator() thread] : " + "New version of CNIRevelator found !")
canvas.itemconfigure(message, text="Mise à jour disponible ! Préparation du téléchargement...")
logger.info("[updator() thread] : " + "Preparing download")
with open(CST_FOLDER +'conf.ig', "rb") as config:
logger.info("[updator() thread] : " + "Reading credentials for proxy in config file...")
AESObj = AESCipher("<clé>")
IPN, IPN_PASS = AESObj.decrypt(config.read()).split("||")[0:2]
session = PACSession(proxy_auth=HTTPProxyAuth(IPN, IPN_PASS))
try:
Prox_us = session.get_pac().find_proxy_for_url(CST_LINK,"neoxgroup.eu")
PROXY_USABLE = Prox_us[6:-1].split(";")[0]
proxy_server_url = IPN +":"+IPN_PASS+"@" + PROXY_USABLE
ph = urllib2.ProxyHandler( { 'http' : proxy_server_url } )
auth = urllib2.ProxyBasicAuthHandler()
server= urllib2.build_opener( ph, auth, urllib2.HTTPHandler )
logger.info("[updator() thread] : " + "Connection to the proxy initiated successfully !")
except:
canvas.itemconfigure(message, text="Téléchargement en connexion directe...")
server= urllib2.build_opener()
logger.info("[updator() thread] : " + "Direct connection initiated successfully")
logger.info("[updator() thread] : " + "Launching download of " + final_f)
Statut = Download( CST_LINK + final_f, final_f, final_f, server, p, canvas, message, logger )
if Statut.success:
try:
os.rename(final_f, final_f + ".exe")
except IOError:
logger.error("[updator() thread] : " + "Unable to rename the file ! Wait 3 sec and retry")
time.sleep(3)
try:
os.rename(final_f, final_f + ".exe")
except IOError:
logger.critical("[updator() thread] : " + "Unable to rename the file !")
else:
canvas.itemconfigure(message, text="Téléchargement terminé ! Préparation du lancement...")
logger.info("[updator() thread] : " + "Download of " + final_f + "finished successfully")
p.configure(mode = "indeterminate", value = 0, maximum = 20)
p.start()
time.sleep(1)
logger.info("[updator() thread] : " + "Launching " + final_f)
try:
proc = subprocess.Popen(final_f + ".exe", shell=False,stdin=None, stdout=None, stderr=None, close_fds=True)
except:
logger.error("[updator() thread] : " + "Unable to start the new version ! Wait 3 sec and retry")
time.sleep(3)
try:
proc = subprocess.Popen(final_f + ".exe", shell=False,stdin=None, stdout=None, stderr=None, close_fds=True)
except Exception as e:
logger.critical("[updator() thread] : " + "Unable to start the new version ! Stopping : " + str(e))
showerror("Erreur d'appel de procédure distante", "Le lancement du nouveau programme a échoué, vous devez le lancer manuellement une fois cette fenêtre fermée")
ret = 12
else:
canvas.itemconfigure(message, text="Echec de la mise à jour : Erreur HTTP. Préparation du lancement...")
logger.error("[updator() thread] : " + "Update has failed with HTTP error")
time.sleep(1)
else:
canvas.itemconfigure(message, text="Logiciel déjà à jour. Préparation du lancement...")
logger.info("[updator() thread] : " + "CNIRevelator is up to date !")
time.sleep(1)
ret = 11
#Tesseract
if os.path.exists(CST_FOLDER + "Tesseract-OCR4\\tesseract.exe"):
os.environ["PATH"] = CST_FOLDER + "Tesseract-OCR4\\"
os.environ["TESSDATA_PREFIX"] = CST_FOLDER + "Tesseract-OCR4\\tessdata"
else:
final_f = "tesseract_4"
#On a une maj
logger.info("[updator() thread] : " + "Downloading tesseract 4 !")
canvas.itemconfigure(message, text="Mise à jour du module OCR ! Préparation du téléchargement...")
logger.info("[updator() thread] : " + "Preparing download")
with open(CST_FOLDER +'conf.ig', "rb") as config:
logger.info("[updator() thread] : " + "Reading credentials for proxy in config file...")
AESObj = AESCipher("<clé>")
IPN, IPN_PASS = AESObj.decrypt(config.read()).split("||")[0:2]
session = PACSession(proxy_auth=HTTPProxyAuth(IPN, IPN_PASS))
try:
Prox_us = session.get_pac().find_proxy_for_url(CST_LINK,"neoxgroup.eu")
PROXY_USABLE = Prox_us[6:-1].split(";")[0]
proxy_server_url = IPN +":"+IPN_PASS+"@" + PROXY_USABLE
ph = urllib2.ProxyHandler( { 'http' : proxy_server_url } )
auth = urllib2.ProxyBasicAuthHandler()
server= urllib2.build_opener( ph, auth, urllib2.HTTPHandler )
logger.info("[updator() thread] : " + "Connection to the proxy initiated successfully !")
except:
canvas.itemconfigure(message, text="Téléchargement en connexion directe...")
server= urllib2.build_opener()
logger.info("[updator() thread] : " + "Direct connection initiated successfully")
logger.info("[updator() thread] : " + "Launching download of " + final_f)
Statut = Download( CST_LINK + final_f, CST_FOLDER + final_f, final_f, server, p, canvas, message, logger )
if Statut.success:
canvas.itemconfigure(message, text="Téléchargement terminé ! Installation...")
logger.info("[updator() thread] : " + "Download of " + final_f + "finished successfully")
p.configure(mode = "indeterminate", value = 0, maximum = 20)
p.start()
try:
zip_ref = zipfile.ZipFile(CST_FOLDER + final_f, 'r')
zip_ref.extractall(CST_FOLDER)
zip_ref.close()
os.environ["PATH"] = CST_FOLDER + "Tesseract-OCR4\\"
os.environ["TESSDATA_PREFIX"] = CST_FOLDER + "Tesseract-OCR4\\tessdata"
canvas.itemconfigure(message, text="Installation terminée !")
except:
logger.error("[updator() thread] : " + "Unable to install the module. Wait and retry")
time.sleep(3)
try:
zip_ref = zipfile.ZipFile(CST_FOLDER + final_f, 'r')
zip_ref.extractall(CST_FOLDER)
zip_ref.close()
os.environ["PATH"] = CST_FOLDER + "Tesseract-OCR4\\"
os.environ["TESSDATA_PREFIX"] = CST_FOLDER + "Tesseract-OCR4\\tessdata"
canvas.itemconfigure(message, text="Installation terminée !")
except Exception as e:
logger.critical("[updator() thread] : " + "Unable to install the module ! Stopping : " + str(e))
showerror("Erreur d'appel de procédure distante", "L'installation du module OCR a échoué, contactez le développeur.")
ret = 11
else:
logger.critical("[updator() thread] : " + "Unable to download the module ! ")
showerror("Erreur de téléchargement", "L'installation du module OCR a échoué, merci d'indiquer le chemin d'accès au fichier tesseract_4 dans la fenêtre suivante")
path = filedialog.askopenfilename(title = "Indiquez le chemin d'accès à tesseract_4...",filetypes = (("Tesseract_4","*.cni4"), ("Tesseract_4","*.cni4")))
if path != "":
try:
canvas.itemconfigure(message, text="Installation...")
zip_ref = zipfile.ZipFile(path, 'r')
zip_ref.extractall(CST_FOLDER)
zip_ref.close()
logger.error("[updator() thread] : " + "Manual installation successed")
canvas.itemconfigure(message, text="Installation terminée !")
os.environ["PATH"] = CST_FOLDER + "Tesseract-OCR4\\"
os.environ["TESSDATA_PREFIX"] = CST_FOLDER + "Tesseract-OCR4\\tessdata"
except Exception as e:
logger.error("[updator() thread] : " + "Manual installation has failed" + str(e) )
showerror("Erreur de lecture", "Le module OCR n'a pas pu être installé, la saisie automatique de scans ne pourra donc fonctionner")
else:
showerror("Opération annulée", "Le module OCR n'a été installé, la saisie automatique de scans ne pourra donc fonctionner")
except URLExcept.HTTPError as e:
canvas.itemconfigure(message, text="Echec de la mise à jour : Erreur HTTP " + str(e.code) + " . Préparation du lancement...")
logger.error("[updator() thread] : " + "Update has failed with HTTP error" + str(e.code) )
if int(e.code) == 407:
showerror("Erreur 407", "Attention : le système de mise à jour automatique a fait face à une erreur 407, signifiant que la connexion au serveur proxy a été refusée. Vos identifiants vous seront redemandés au prochain démarrage. La mise à jour a échoué.")
logger.info("[updator() thread] : " + "Credential error. Deleting the config file...")
os.remove(CST_FOLDER +"conf.ig")
p.configure(mode = "indeterminate", value = 0, maximum = 20)
p.start()
time.sleep(3)
except Exception as e:
canvas.itemconfigure(message, text="Echec de la mise à jour. Préparation du lancement...")
logger.error("[updator() thread] : " + "Error from the updating system : " + str(e))
p.configure(mode = "indeterminate", value = 0, maximum = 20)
p.start()
time.sleep(2)
p.stop()
upwin.destroy()
root.destroy()
return ret
##updating()
global ret
logger.info("updating() : " + "Launching updator() thread...")
threading.Thread(target=updator, daemon=True).start()
logger.info("updating() [Thread] : " + "Ending updator() thread")
return None
##SoftUpdate()
global ret
ret = 11
global upwin
root = Tk()
root.attributes('-alpha', 0.0) #For icon
#root.lower()
root.iconify()
upwin = Toplevel(root)
upwin.overrideredirect(1)
upwin.configure(bg = CST_COLOR)
upwin.resizable(width=False, height=False)
#taille souhaite de la fenetre
w = 600
h = 300
#pour centrer la fenetre
#taille de l'ecran
upwin.update()
canvas = Canvas(upwin, width=600, height=270, bg=CST_COLOR, highlightthickness=0)
pbar = Canvas(upwin, width=600, height=30, bg=CST_COLOR)
p = ttk.Progressbar(pbar, orient=HORIZONTAL, length=590, mode='determinate')
upwin.update()
ws = upwin.winfo_screenwidth()
hs = upwin.winfo_screenheight()
canvas.create_text(w/2, h/3, text=CST_NAME + " " + CST_VERTITLE, font="Calibri 30 bold", fill="white")
message = canvas.create_text(w/2, h/1.150, text=" ", font="Calibri 9", fill="white")
upwin.update()
#calcul la position de la fenetre
x = (ws/2) - (w/2)
y = (hs/2) - (h/2)
#applique la taille et la position
upwin.geometry('%dx%d+%d+%d' % (w, h, x, y))
canvas.grid()
pbar.grid()
p.grid()
upwin.after(2000, updating)
if getattr( sys, 'frozen', False ) :
root.iconbitmap(sys._MEIPASS + "\id-card.ico\id-card.ico")
else:
root.iconbitmap("id-card.ico")
# On démarre la boucle Tkinter qui s'interrompt quand on ferme la fenêtre
logger.info("SoftUpdate() : " + "Entering upwin mainloop()")
upwin.protocol('WM_DELETE_WINDOW', lambda: root.destroy())
upwin.mainloop()
logger.info("SoftUpdate() : " + "Exiting upwin mainloop()")
if ret == 11:
logger.info("SoftUpdate() : " + "OK to start to main() normally !")
return True
else:
logger.info("SoftUpdate() : " + "Program will stop !")
return False

1551
CNI_classes.py Normal file

File diff suppressed because one or more lines are too long

413
CNI_pytesseract.py Normal file
View File

@ -0,0 +1,413 @@
"""
********************************************************************************
*** Projet CNI_Revelator ***
GNU GPL * 07/2018
Adrien Bourmault
Pytesseract modification to comply with Pyinstaller
*****
"""
#!/usr/bin/env python
'''
Python-tesseract. For more information: https://github.com/madmaze/pytesseract
'''
try:
import Image
except ImportError:
from PIL import Image
import os
import sys
import subprocess
import tempfile
import shlex
import string
from glob import iglob
from pkgutil import find_loader
from distutils.version import LooseVersion
from os.path import realpath, normpath, normcase
numpy_installed = find_loader('numpy') is not None
if numpy_installed:
from numpy import ndarray
# CHANGE THIS IF TESSERACT IS NOT IN YOUR PATH, OR IS NAMED DIFFERENTLY
tesseract_cmd = 'tesseract'
RGB_MODE = 'RGB'
OSD_KEYS = {
'Page number': ('page_num', int),
'Orientation in degrees': ('orientation', int),
'Rotate': ('rotate', int),
'Orientation confidence': ('orientation_conf', float),
'Script': ('script', str),
'Script confidence': ('script_conf', float)
}
class Output:
STRING = "string"
BYTES = "bytes"
DICT = "dict"
class TesseractError(RuntimeError):
def __init__(self, status, message):
self.status = status
self.message = message
self.args = (status, message)
class TesseractNotFoundError(EnvironmentError):
def __init__(self):
super(TesseractNotFoundError, self).__init__(
tesseract_cmd + " is not installed or it's not in your path"
)
class TSVNotSupported(EnvironmentError):
def __init__(self):
super(TSVNotSupported, self).__init__(
'TSV output not supported. Tesseract >= 3.05 required'
)
def run_once(func):
def wrapper(*args, **kwargs):
if wrapper._result is wrapper:
wrapper._result = func(*args, **kwargs)
return wrapper._result
wrapper._result = wrapper
return wrapper
def get_errors(error_string):
return u' '.join(
line for line in error_string.decode('utf-8').splitlines()
).strip()
def cleanup(temp_name):
''' Tries to remove files by filename wildcard path. '''
for filename in iglob(temp_name + '*' if temp_name else temp_name):
try:
os.remove(filename)
except OSError:
pass
def prepare(image):
if isinstance(image, Image.Image):
return image
if numpy_installed and isinstance(image, ndarray):
return Image.fromarray(image)
raise TypeError('Unsupported image object')
def save_image(image):
temp_name = tempfile.mktemp(prefix='tess_')
if isinstance(image, str):
return temp_name, realpath(normpath(normcase(image)))
image = prepare(image)
img_extension = image.format
if image.format not in {'JPEG', 'PNG', 'TIFF', 'BMP', 'GIF'}:
img_extension = 'PNG'
if not image.mode.startswith(RGB_MODE):
image = image.convert(RGB_MODE)
if 'A' in image.getbands():
# discard and replace the alpha channel with white background
background = Image.new(RGB_MODE, image.size, (255, 255, 255))
background.paste(image, (0, 0), image)
image = background
input_file_name = temp_name + os.extsep + img_extension
image.save(input_file_name, format=img_extension, **image.info)
return temp_name, input_file_name
def subprocess_args(include_stdout=True):
# The following is true only on Windows.
if hasattr(subprocess, 'STARTUPINFO'):
# On Windows, subprocess calls will pop up a command window by default
# when run from Pyinstaller with the ``--noconsole`` option. Avoid this
# distraction.
si = subprocess.STARTUPINFO()
si.dwFlags |= subprocess.STARTF_USESHOWWINDOW
# Windows doesn't search the path by default. Pass it an environment so
# it will.
env = os.environ
else:
si = None
env = None
# ``subprocess.check_output`` doesn't allow specifying ``stdout``::
#
# Traceback (most recent call last):
# File "test_subprocess.py", line 58, in <module>
# **subprocess_args(stdout=None))
# File "C:\Python27\lib\subprocess.py", line 567, in check_output
# raise ValueError('stdout argument not allowed, it will be overridden.')
# ValueError: stdout argument not allowed, it will be overridden.
#
# So, add it only if it's needed.
if include_stdout:
ret = {'stdout': subprocess.PIPE}
else:
ret = {}
# On Windows, running this from the binary produced by Pyinstaller
# with the ``--noconsole`` option requires redirecting everything
# (stdin, stdout, stderr) to avoid an OSError exception
# "[Error 6] the handle is invalid."
ret.update({'stdin': subprocess.PIPE,
'stderr': subprocess.PIPE,
'startupinfo': si,
'env': env
})
return ret
return kwargs
def run_tesseract(input_filename,
output_filename_base,
extension,
lang,
config='',
nice=0):
cmd_args = []
if not sys.platform.startswith('win32') and nice != 0:
cmd_args += ('nice', '-n', str(nice))
cmd_args += (tesseract_cmd, input_filename, output_filename_base)
if lang is not None:
cmd_args += ('-l', lang)
cmd_args += shlex.split(config)
if extension not in ('box', 'osd', 'tsv'):
cmd_args.append(extension)
try:
proc = subprocess.Popen(cmd_args, **subprocess_args())
except OSError:
raise TesseractNotFoundError()
status_code, error_string = proc.wait(), proc.stderr.read()
proc.stderr.close()
if status_code:
raise TesseractError(status_code, get_errors(error_string))
return True
def run_and_get_output(image,
extension,
lang=None,
config='',
nice=0,
return_bytes=False):
temp_name, input_filename = '', ''
try:
temp_name, input_filename = save_image(image)
kwargs = {
'input_filename': input_filename,
'output_filename_base': temp_name + '_out',
'extension': extension,
'lang': lang,
'config': config,
'nice': nice
}
run_tesseract(**kwargs)
filename = kwargs['output_filename_base'] + os.extsep + extension
with open(filename, 'rb') as output_file:
if return_bytes:
return output_file.read()
return output_file.read().decode('utf-8').strip()
finally:
cleanup(temp_name)
def file_to_dict(tsv, cell_delimiter, str_col_idx):
result = {}
rows = [row.split(cell_delimiter) for row in tsv.split('\n')]
if not rows:
return result
header = rows.pop(0)
if len(rows[-1]) < len(header):
# Fixes bug that occurs when last text string in TSV is null, and
# last row is missing a final cell in TSV file
rows[-1].append('')
if str_col_idx < 0:
str_col_idx += len(header)
for i, head in enumerate(header):
result[head] = [
int(row[i]) if i != str_col_idx else row[i] for row in rows
]
return result
def is_valid(val, _type):
if _type is int:
return val.isdigit()
if _type is float:
try:
float(val)
return True
except ValueError:
return False
return True
def osd_to_dict(osd):
return {
OSD_KEYS[kv[0]][0]: OSD_KEYS[kv[0]][1](kv[1]) for kv in (
line.split(': ') for line in osd.split('\n')
) if len(kv) == 2 and is_valid(kv[1], OSD_KEYS[kv[0]][1])
}
@run_once
def get_tesseract_version():
'''
Returns LooseVersion object of the Tesseract version
'''
try:
return LooseVersion(
subprocess.check_output([tesseract_cmd, '--version'],
**subprocess_args(False)).decode('utf-8').split()[1].lstrip(string.printable[10:])
)
except OSError:
raise TesseractNotFoundError()
def image_to_string(image,
lang=None,
config='',
nice=0,
boxes=False,
output_type=Output.STRING):
'''
Returns the result of a Tesseract OCR run on the provided image to string
'''
if boxes:
# Added for backwards compatibility
print('\nWarning: Argument \'boxes\' is deprecated and will be removed'
' in future versions. Use function image_to_boxes instead.\n')
return image_to_boxes(image, lang, config, nice, output_type)
args = [image, 'txt', lang, config, nice]
if output_type == Output.DICT:
return {'text': run_and_get_output(*args)}
elif output_type == Output.BYTES:
args.append(True)
return run_and_get_output(*args)
def image_to_boxes(image,
lang=None,
config='',
nice=0,
output_type=Output.STRING):
'''
Returns string containing recognized characters and their box boundaries
'''
config += ' batch.nochop makebox'
args = [image, 'box', lang, config, nice]
if output_type == Output.DICT:
box_header = 'char left bottom right top page\n'
return file_to_dict(box_header + run_and_get_output(*args), ' ', 0)
elif output_type == Output.BYTES:
args.append(True)
return run_and_get_output(*args)
def image_to_data(image,
lang=None,
config='',
nice=0,
output_type=Output.STRING):
'''
Returns string containing box boundaries, confidences,
and other information. Requires Tesseract 3.05+
'''
if get_tesseract_version() < '3.05':
raise TSVNotSupported()
config = '{} {}'.format('-c tessedit_create_tsv=1', config.strip()).strip()
args = [image, 'tsv', lang, config, nice]
if output_type == Output.DICT:
return file_to_dict(run_and_get_output(*args), '\t', -1)
elif output_type == Output.BYTES:
args.append(True)
return run_and_get_output(*args)
def image_to_osd(image,
lang='osd',
config='',
nice=0,
output_type=Output.STRING):
'''
Returns string containing the orientation and script detection (OSD)
'''
config = '{}-psm 0 {}'.format(
'' if get_tesseract_version() < '3.05' else '-',
config.strip()
).strip()
args = [image, 'osd', lang, config, nice]
if output_type == Output.DICT:
return osd_to_dict(run_and_get_output(*args))
elif output_type == Output.BYTES:
args.append(True)
return run_and_get_output(*args)
def main():
if len(sys.argv) == 2:
filename, lang = sys.argv[1], None
elif len(sys.argv) == 4 and sys.argv[1] == '-l':
filename, lang = sys.argv[3], sys.argv[2]
else:
sys.stderr.write('Usage: python pytesseract.py [-l lang] input_file\n')
exit(2)
try:
print(image_to_string(Image.open(filename), lang=lang))
except IOError:
sys.stderr.write('ERROR: Could not open file "%s"\n' % filename)
exit(1)
if __name__ == '__main__':
main()