Capacidad de procesar imagenes sin texto
Browse files- __pycache__/app.cpython-311.pyc +0 -0
- __pycache__/funciones.cpython-311.pyc +0 -0
- app.py +3 -12
- funciones.py +39 -16
- herramientas.py +122 -39
- obtenCampo.py +24 -17
__pycache__/app.cpython-311.pyc
CHANGED
Binary files a/__pycache__/app.cpython-311.pyc and b/__pycache__/app.cpython-311.pyc differ
|
|
__pycache__/funciones.cpython-311.pyc
CHANGED
Binary files a/__pycache__/funciones.cpython-311.pyc and b/__pycache__/funciones.cpython-311.pyc differ
|
|
app.py
CHANGED
@@ -30,19 +30,10 @@ async def echo_image(image: UploadFile = File(...)):
|
|
30 |
return StreamingResponse(BytesIO(contents), media_type=image.content_type)
|
31 |
|
32 |
@app.post(
|
33 |
-
"/
|
34 |
tags=["Rapicash"],
|
35 |
summary="Procesamiento de DNI")
|
36 |
-
async def
|
37 |
if not image.content_type.startswith("image/"):
|
38 |
return {"error": "El archivo no es una imagen"}
|
39 |
-
return await funciones.
|
40 |
-
|
41 |
-
@app.post(
|
42 |
-
"/procesa_pasaporte/",
|
43 |
-
tags=["Rapicash"],
|
44 |
-
summary="Procesamiento de DNI")
|
45 |
-
async def procesa_pasaporte(image: UploadFile = File(...)):
|
46 |
-
if not image.content_type.startswith("image/"):
|
47 |
-
return {"error": "El archivo no es una imagen"}
|
48 |
-
return await funciones.procesa_pasaporte(image)
|
|
|
30 |
return StreamingResponse(BytesIO(contents), media_type=image.content_type)
|
31 |
|
32 |
@app.post(
|
33 |
+
"/procesa_documento/",
|
34 |
tags=["Rapicash"],
|
35 |
summary="Procesamiento de DNI")
|
36 |
+
async def procesa_documento(image: UploadFile = File(...)):
|
37 |
if not image.content_type.startswith("image/"):
|
38 |
return {"error": "El archivo no es una imagen"}
|
39 |
+
return await funciones.procesa_documento(image)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
funciones.py
CHANGED
@@ -2,15 +2,22 @@ import herramientas
|
|
2 |
import documentos
|
3 |
import time
|
4 |
|
5 |
-
|
6 |
-
async def procesa_dni(image):
|
7 |
|
8 |
textos_extraidos = await herramientas.procesaImagen(image)
|
9 |
-
|
10 |
-
|
11 |
-
|
12 |
-
|
13 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
14 |
"nombre": nombre,
|
15 |
"apellido": apellido,
|
16 |
"identificacion": identificacion,
|
@@ -18,15 +25,31 @@ async def procesa_dni(image):
|
|
18 |
"sexo": sexo
|
19 |
}
|
20 |
|
21 |
-
|
22 |
-
|
23 |
-
|
24 |
-
|
25 |
-
|
26 |
-
nombre, apellido, identificacion = await documentos.pasaporte(textos_extraidos)
|
27 |
|
28 |
-
|
|
|
29 |
"nombre": nombre,
|
30 |
"apellido": apellido,
|
31 |
-
"identificacion": identificacion
|
32 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2 |
import documentos
|
3 |
import time
|
4 |
|
5 |
+
async def procesa_documento(image):
|
|
|
6 |
|
7 |
textos_extraidos = await herramientas.procesaImagen(image)
|
8 |
+
print("Textos extraídos: ")
|
9 |
+
print(textos_extraidos)
|
10 |
+
|
11 |
+
documento = herramientas.define_documento(textos_extraidos)
|
12 |
+
print("El documento fue: ", documento)
|
13 |
+
|
14 |
+
if documento == 'dni':
|
15 |
+
# Llama a la función 'dni' si el documento es un DNI
|
16 |
+
nombre, apellido, identificacion, fecha_nacimiento, sexo = await documentos.dni(textos_extraidos)
|
17 |
+
print("Procesado como DNI.")
|
18 |
+
|
19 |
+
return {
|
20 |
+
"documento": documento,
|
21 |
"nombre": nombre,
|
22 |
"apellido": apellido,
|
23 |
"identificacion": identificacion,
|
|
|
25 |
"sexo": sexo
|
26 |
}
|
27 |
|
28 |
+
elif documento == 'pasaporte':
|
29 |
+
# Llama a la función 'pasaporte' si el documento es un pasaporte
|
30 |
+
#nombre, apellido, identificacion, fecha_nacimiento, sexo = await documentos.pasaporte(textos_extraidos)
|
31 |
+
nombre, apellido, identificacion, pasaporte, fecha_nacimiento, sexo, fecha_expedicion, fecha_vencimiento = 'pasaporte', 'pasaporte', 'pasaporte', 'pasaporte', 'pasaporte', 'pasaporte', 'pasaporte', 'pasaporte'
|
32 |
+
print("Procesado como Pasaporte.")
|
|
|
33 |
|
34 |
+
return {
|
35 |
+
"documento": documento,
|
36 |
"nombre": nombre,
|
37 |
"apellido": apellido,
|
38 |
+
"identificacion": identificacion,
|
39 |
+
"pasaporte": pasaporte,
|
40 |
+
"fecha_nacimiento": fecha_nacimiento,
|
41 |
+
"sexo": sexo,
|
42 |
+
"fecha_expedicion": fecha_expedicion,
|
43 |
+
"fecha_vencimiento": fecha_vencimiento
|
44 |
+
}
|
45 |
+
|
46 |
+
else:
|
47 |
+
# Maneja cualquier otro caso no esperado
|
48 |
+
print(f"Tipo de documento no reconocido: {documento}")
|
49 |
+
# Podrías asignar None a las variables o levantar un error aquí
|
50 |
+
#nombre, apellido, identificacion, fecha_nacimiento, sexo = None, None, None, None, None
|
51 |
+
return {
|
52 |
+
"error": "El documento no es un dni o pasaporte válido o requiere ser más legible.",
|
53 |
+
}
|
54 |
+
|
55 |
+
|
herramientas.py
CHANGED
@@ -25,7 +25,7 @@ def listaTextosExtraidos(dict_recibido):
|
|
25 |
print("Datos extraídos (acceso directo):")
|
26 |
|
27 |
textos_extraidos = []
|
28 |
-
|
29 |
for item in result:
|
30 |
texto = item[1][0]
|
31 |
print(texto)
|
@@ -41,45 +41,93 @@ def buscaIndexPalabra(arreglo, palabra):
|
|
41 |
return i
|
42 |
return None # Cambiado de 'error' a None
|
43 |
|
44 |
-
def
|
45 |
"""
|
46 |
-
Busca el índice de la primera línea
|
47 |
-
|
48 |
|
49 |
Args:
|
50 |
-
arreglo (list):
|
51 |
-
|
52 |
-
|
53 |
-
La búsqueda es insensible a mayúsculas/minúsculas
|
54 |
-
y limpia espacios en las palabras requeridas.
|
55 |
|
56 |
Returns:
|
57 |
-
|
58 |
-
o None si ninguna línea las contiene.
|
59 |
"""
|
60 |
-
|
61 |
-
|
62 |
-
|
63 |
-
|
64 |
-
|
65 |
-
|
66 |
-
|
67 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
68 |
|
69 |
for i, texto_linea in enumerate(arreglo):
|
70 |
-
# Convertir la línea actual a minúsculas para una comparación insensible a mayúsculas/minúsculas
|
71 |
texto_linea_lower = texto_linea.lower()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
72 |
|
73 |
-
|
|
|
|
|
|
|
74 |
|
75 |
-
|
76 |
-
|
77 |
-
|
78 |
-
|
79 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
80 |
|
81 |
-
|
|
|
|
|
|
|
|
|
|
|
82 |
|
|
|
83 |
def buscarPatronCedula(lista_textos):
|
84 |
for i, texto in enumerate(lista_textos):
|
85 |
if texto and texto[0].isdigit() and '-' in texto:
|
@@ -88,17 +136,21 @@ def buscarPatronCedula(lista_textos):
|
|
88 |
|
89 |
async def procesaImagen(image):
|
90 |
|
91 |
-
|
92 |
-
|
93 |
-
|
94 |
-
|
95 |
-
|
96 |
-
|
97 |
-
|
98 |
-
|
99 |
-
|
100 |
-
|
101 |
-
|
|
|
|
|
|
|
|
|
102 |
|
103 |
def obtener_fecha(texto):
|
104 |
# El patrón busca:
|
@@ -143,4 +195,35 @@ def obtener_sexo(texto):
|
|
143 |
# que es la letra del sexo/seno. match.group(1) sería "Sexo" o "Seno".
|
144 |
return match.group(2)
|
145 |
else:
|
146 |
-
return None
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
25 |
print("Datos extraídos (acceso directo):")
|
26 |
|
27 |
textos_extraidos = []
|
28 |
+
|
29 |
for item in result:
|
30 |
texto = item[1][0]
|
31 |
print(texto)
|
|
|
41 |
return i
|
42 |
return None # Cambiado de 'error' a None
|
43 |
|
44 |
+
def buscaIndexMultiplesPalabras_Corregida(arreglo, conceptos_requeridos):
|
45 |
"""
|
46 |
+
Busca el índice de la primera línea que contiene todas las palabras requeridas (o sus alternativas)
|
47 |
+
y devuelve la línea con las alternativas corregidas a su forma preferida.
|
48 |
|
49 |
Args:
|
50 |
+
arreglo (list): Lista de strings de texto extraído (limpios).
|
51 |
+
conceptos_requeridos (list): Lista de diccionarios, donde cada diccionario define un concepto:
|
52 |
+
Ej: {'preferida': 'nacimiento', 'alternativas': ['nacimento', 'nacimuento']}
|
|
|
|
|
53 |
|
54 |
Returns:
|
55 |
+
tuple: (índice, línea_corregida) si se encuentra, o (None, None) si no se encuentra.
|
|
|
56 |
"""
|
57 |
+
print(f"\n--- Iniciando búsqueda y corrección ---")
|
58 |
+
|
59 |
+
# Paso 1: Normalizar los conceptos y prepararlos para la búsqueda y corrección
|
60 |
+
conceptos_preparados = []
|
61 |
+
for concepto in conceptos_requeridos:
|
62 |
+
preferida = concepto['preferida'].lower().replace(" ", "")
|
63 |
+
alternativas = [alt.lower().replace(" ", "") for alt in concepto.get('alternativas', [])]
|
64 |
+
|
65 |
+
# Combinamos la preferida con las alternativas para la búsqueda
|
66 |
+
todas_las_formas = [preferida] + alternativas
|
67 |
+
|
68 |
+
conceptos_preparados.append({
|
69 |
+
'preferida': preferida,
|
70 |
+
'todas_las_formas': todas_las_formas,
|
71 |
+
'alternativas': alternativas # Solo las alternativas para saber cuáles corregir
|
72 |
+
})
|
73 |
|
74 |
for i, texto_linea in enumerate(arreglo):
|
|
|
75 |
texto_linea_lower = texto_linea.lower()
|
76 |
+
|
77 |
+
# Variables para seguir el rastro de la línea actual
|
78 |
+
es_coincidencia_completa = True
|
79 |
+
palabras_encontradas_en_linea = {} # Almacena {forma_encontrada: forma_preferida}
|
80 |
+
|
81 |
+
# Paso 2: Verificar si TODAS las condiciones se cumplen en esta línea
|
82 |
+
for concepto in conceptos_preparados:
|
83 |
+
encontrado_en_esta_linea = False
|
84 |
+
|
85 |
+
# Buscamos si alguna forma (preferida o alternativa) está en la línea
|
86 |
+
for forma in concepto['todas_las_formas']:
|
87 |
+
if forma in texto_linea_lower:
|
88 |
+
encontrado_en_esta_linea = True
|
89 |
+
|
90 |
+
# Guardamos qué forma se encontró y cuál es la preferida
|
91 |
+
palabras_encontradas_en_linea[forma] = concepto['preferida']
|
92 |
+
|
93 |
+
# Hemos encontrado una forma para este concepto, pasamos al siguiente concepto
|
94 |
+
break
|
95 |
+
|
96 |
+
if not encontrado_en_esta_linea:
|
97 |
+
es_coincidencia_completa = False
|
98 |
+
print(f"Fallo en línea {i}: No se encontró el concepto '{concepto['preferida']}' ni sus alternativas.")
|
99 |
+
break # Si falta un concepto, pasamos a la siguiente línea del arreglo
|
100 |
|
101 |
+
# Paso 3: Si se encontró una coincidencia completa, corregimos la línea y la retornamos
|
102 |
+
if es_coincidencia_completa:
|
103 |
+
print(f"ÉXITO: Coincidencia completa en el índice {i}.")
|
104 |
+
print(f"Texto linea es: {texto_linea}")
|
105 |
|
106 |
+
linea_corregida = texto_linea
|
107 |
+
|
108 |
+
for forma_encontrada, forma_preferida in palabras_encontradas_en_linea.items():
|
109 |
+
# Nota: Esta corrección simple asume que la palabra encontrada está exactamente igual que en la lista de alternativas (minusculas y sin espacios)
|
110 |
+
# Para un OCR más variable, necesitarías una lógica de reemplazo más avanzada (ej. re.sub),
|
111 |
+
# pero para tu caso de "nacimento" a "nacimiento" esto funciona si la palabra se encuentra exactamente.
|
112 |
+
|
113 |
+
# Usamos re.sub para reemplazar la palabra encontrada con la preferida, insensible a mayúsculas/minúsculas
|
114 |
+
# (re.escape para manejar caracteres especiales si los hubiera)
|
115 |
+
patron_reemplazo = re.compile(re.escape(forma_encontrada), re.IGNORECASE)
|
116 |
+
|
117 |
+
# Reemplazamos la palabra encontrada en la línea original con la forma preferida
|
118 |
+
linea_corregida = patron_reemplazo.sub(forma_preferida, linea_corregida, count=1)
|
119 |
+
|
120 |
+
print(f"Línea corregida: '{linea_corregida}'")
|
121 |
+
# Ahora corregimos la línea original usando la información de las palabras encontradas
|
122 |
|
123 |
+
arreglo[i] = linea_corregida
|
124 |
+
return i, arreglo
|
125 |
+
|
126 |
+
print(f"\n--- Búsqueda finalizada ---")
|
127 |
+
print("Ninguna línea contiene todas las palabras requeridas.")
|
128 |
+
return None, None
|
129 |
|
130 |
+
########################################################################
|
131 |
def buscarPatronCedula(lista_textos):
|
132 |
for i, texto in enumerate(lista_textos):
|
133 |
if texto and texto[0].isdigit() and '-' in texto:
|
|
|
136 |
|
137 |
async def procesaImagen(image):
|
138 |
|
139 |
+
try:
|
140 |
+
temp_image = await imageToTemp(image)
|
141 |
+
client = Client("BuzzwordMx/ai_ocr")
|
142 |
+
dict_recibido = client.predict(
|
143 |
+
img=handle_file(temp_image),
|
144 |
+
lang="en",
|
145 |
+
api_name="/predict"
|
146 |
+
)
|
147 |
+
|
148 |
+
#Aquí es donde personalizo el proceso:
|
149 |
+
textos_extraidos = listaTextosExtraidos(dict_recibido)
|
150 |
+
return textos_extraidos
|
151 |
+
except Exception as e:
|
152 |
+
print(f"Error al procesar el archivo: {e}")
|
153 |
+
|
154 |
|
155 |
def obtener_fecha(texto):
|
156 |
# El patrón busca:
|
|
|
195 |
# que es la letra del sexo/seno. match.group(1) sería "Sexo" o "Seno".
|
196 |
return match.group(2)
|
197 |
else:
|
198 |
+
return None
|
199 |
+
|
200 |
+
def define_documento(textos_extraidos):
|
201 |
+
|
202 |
+
#Definiré si el documento subido es un pasaporte o un dni.
|
203 |
+
textos_extraidos_simplificados = [texto.lower().replace(" ", "") for texto in textos_extraidos]
|
204 |
+
|
205 |
+
#Busqueda de DNI
|
206 |
+
conceptos_busqueda_dni = [
|
207 |
+
{'preferida': 'nombre', 'alternativas': []},
|
208 |
+
{'preferida': 'usual', 'alternativas': []}
|
209 |
+
]
|
210 |
+
|
211 |
+
indice, textos_extraidos_corregidos = buscaIndexMultiplesPalabras_Corregida(textos_extraidos_simplificados, conceptos_busqueda_dni)
|
212 |
+
if indice is not None:
|
213 |
+
print("Si es dni...")
|
214 |
+
return 'dni'
|
215 |
+
else:
|
216 |
+
#Revisar si es pasaporte.
|
217 |
+
#Busqueda de DNI
|
218 |
+
conceptos_busqueda_pasaporte = [
|
219 |
+
{'preferida': 'pasaporte', 'alternativas': ['passport']},
|
220 |
+
#{'preferida': 'identidad', 'alternativas': []}
|
221 |
+
]
|
222 |
+
indice, textos_extraidos_corregidos = buscaIndexMultiplesPalabras_Corregida(textos_extraidos_simplificados, conceptos_busqueda_pasaporte)
|
223 |
+
|
224 |
+
if indice is not None:
|
225 |
+
print("Si es pasaporte...")
|
226 |
+
return 'pasaporte'
|
227 |
+
else:
|
228 |
+
print("No es pasaporte ni dni.")
|
229 |
+
return None
|
obtenCampo.py
CHANGED
@@ -2,19 +2,26 @@ import herramientas
|
|
2 |
|
3 |
|
4 |
#Campos para DNI.
|
5 |
-
def Nombre(textos_extraidos,
|
6 |
-
indice = herramientas.buscaIndexPalabra(
|
7 |
nombre = textos_extraidos[indice-2]
|
8 |
apellido = textos_extraidos[indice-1]
|
9 |
return nombre, apellido
|
10 |
|
11 |
-
def Identificacion(textos_extraidos,
|
12 |
-
indice = herramientas.buscarPatronCedula(
|
13 |
identificacion = textos_extraidos[indice]
|
14 |
return identificacion
|
15 |
|
16 |
def Fecha_Nacimiento(textos_extraidos, textos_extraidos_limpios):
|
17 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
18 |
print("El índice de la fecha de nacimiento es: ", indice)
|
19 |
texto_fecha_nacimiento = textos_extraidos_limpios[indice] #En ésta ocasión estoy usando el texto limpio para que el patrón lo identifique más fácilmente.
|
20 |
print("Y su correspondiente texto es: " , texto_fecha_nacimiento)
|
@@ -23,18 +30,18 @@ def Fecha_Nacimiento(textos_extraidos, textos_extraidos_limpios):
|
|
23 |
|
24 |
def Sexo(textos_extraidos, textos_extraidos_limpios):
|
25 |
|
26 |
-
|
|
|
|
|
|
|
|
|
|
|
27 |
|
28 |
-
|
29 |
-
|
30 |
-
if indice is not None:
|
31 |
-
print(f"Se encontró una variación de sexo en el índice: {indice}")
|
32 |
-
else:
|
33 |
-
print("No se encontró ninguna forma de la palabra sexo.")
|
34 |
-
return None
|
35 |
-
else:
|
36 |
-
print(f"Se encontró palabra directamente.")
|
37 |
|
38 |
-
|
39 |
-
|
|
|
|
|
40 |
return sexo
|
|
|
2 |
|
3 |
|
4 |
#Campos para DNI.
|
5 |
+
def Nombre(textos_extraidos, textos_extraidos_simplificados):
|
6 |
+
indice = herramientas.buscaIndexPalabra(textos_extraidos_simplificados, 'usual')
|
7 |
nombre = textos_extraidos[indice-2]
|
8 |
apellido = textos_extraidos[indice-1]
|
9 |
return nombre, apellido
|
10 |
|
11 |
+
def Identificacion(textos_extraidos, textos_extraidos_simplificados):
|
12 |
+
indice = herramientas.buscarPatronCedula(textos_extraidos_simplificados)
|
13 |
identificacion = textos_extraidos[indice]
|
14 |
return identificacion
|
15 |
|
16 |
def Fecha_Nacimiento(textos_extraidos, textos_extraidos_limpios):
|
17 |
+
|
18 |
+
conceptos_busqueda = [
|
19 |
+
{'preferida': 'fecha', 'alternativas': []},
|
20 |
+
{'preferida': 'nacimiento', 'alternativas': ['nacimento']} ]
|
21 |
+
|
22 |
+
indice, textos_extraidos_corregidos = herramientas.buscaIndexMultiplesPalabras_Corregida(textos_extraidos_limpios, conceptos_busqueda)
|
23 |
+
print("182 Ésto es textos extraídos corregidos: ", textos_extraidos_corregidos)
|
24 |
+
textos_extraidos_limpios = textos_extraidos_corregidos
|
25 |
print("El índice de la fecha de nacimiento es: ", indice)
|
26 |
texto_fecha_nacimiento = textos_extraidos_limpios[indice] #En ésta ocasión estoy usando el texto limpio para que el patrón lo identifique más fácilmente.
|
27 |
print("Y su correspondiente texto es: " , texto_fecha_nacimiento)
|
|
|
30 |
|
31 |
def Sexo(textos_extraidos, textos_extraidos_limpios):
|
32 |
|
33 |
+
conceptos_busqueda = [
|
34 |
+
{'preferida': 'sexo', 'alternativas': ['sex', 'seno', 'sen']},
|
35 |
+
#{'preferida': 'sangre', 'alternativas': []}
|
36 |
+
]
|
37 |
+
|
38 |
+
indice, textos_extraidos_corregidos = herramientas.buscaIndexMultiplesPalabras_Corregida(textos_extraidos_limpios, conceptos_busqueda)
|
39 |
|
40 |
+
textos_extraidos_limpios = textos_extraidos_corregidos
|
41 |
+
print("El índice de genero es: ", indice)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
42 |
|
43 |
+
texto_genero = textos_extraidos_limpios[indice]
|
44 |
+
print("Y su correspondiente texto es: " , texto_genero)
|
45 |
+
|
46 |
+
sexo = herramientas.obtener_sexo(texto_genero)
|
47 |
return sexo
|