<jupyter_start><jupyter_text>Instalar dependencias Se usarán las siguientes librerías:* pdf2image: Librería para convertir automáticamente archivos .pdf a .jpg.* pillow: Librería de imágenes de Python, se usa para manipular imágenes.* requests: Librería de llamadas http, se usa para llamar a los endpoints en Azure.* condacolab: Implementación de anaconda en Google Colab, requerido para instalar un requerimiento de pdf2image, "poppler".Finalmente, importamos drive de google.colab.Nota: cuando Anaconda se instala por primera vez, este reinicia el kernel automáticamente.<jupyter_code>!pip install pdf2image
!pip install pillow
!pip install requests
!pip install -q condacolab
import condacolab
condacolab.install()
!conda install -c conda-forge poppler
from google.colab import drive
drive.mount('/content/drive')<jupyter_output>Drive already mounted at /content/drive; to attempt to forcibly remount, call drive.mount("/content/drive", force_remount=True).<jupyter_text>DirectoriosAquí se definen los directorios para el proceso, el primero es el directorio donde se encuentran las imágenes, y el segundo directorio es donde se almacenan las imágenes procesadas.Todas las imagenes dentro del directorio especificado se procesarán.Cada imagen se tarda entre 3 y 4 segundos para ser procesada.Recuerda poner el directorio con "/" al final.Cambiar los paths dependiendo del directorio donde tengas las fotos y donde quieras que se guarden las copias.<jupyter_code>#path = 'drive/MyDrive/Datos - Hackathon JusticIA/Expedientes/'
path = 'drive/MyDrive/Colab Notebooks/RII AA Hackathon/imgtest2/'
pathResult = 'drive/MyDrive/Colab Notebooks/RII AA Hackathon/imgtestresult2/'<jupyter_output><empty_output><jupyter_text>Copiar archivosSe copian los archivos del directorio 1 al directorio 2.Esto se hace para no alterar los archivos originales.<jupyter_code>from distutils.dir_util import copy_tree
copy_tree(path, pathResult)<jupyter_output><empty_output><jupyter_text>Verificar copiadoPodemos verificar que se copiaron los archivos haciendo una muestra del segundo directorio.<jupyter_code>import PIL
from glob import glob
from random import sample

files = glob(pathResult+'*.*')
files = sample(files, 1)

for file_name in files:
    print(file_name)<jupyter_output>drive/MyDrive/Colab Notebooks/RII AA Hackathon/imgtestresult2/Ficheros_UAG_76-11-16_a_77-03-19_UAG,_fichas-113-.JPG<jupyter_text>Métodos necesariosSe utilizarán los siguientes métodos en nuestro pipeline* checkValueField: Lee campos anidados de un json, con manejo de errores.* convertPdf: Usa un archivo .pdf y crea una copia en formato .jpg, y regresa el nombre de este nuevo archivo .jpg, usando la librería pdf2image.* orientationModel: Usando la librería requests, llama al modelo de orientación de documentos entrenado con Azure Custom Vision, este regresa la orientación de la imagen con una probabilidad. La orientación sigue ángulos según las agujas del reloj y regresa un número entero con el ángulo.  * 0: 0 grados, imagen recta.  * 90: 90 grados, imagen horizontal a la derecha.  * 180: 180 grados, imagen vertical boca abajo.  * 270: 270 grados, imagen horizontal a la izquierda.* rotateFile: Rota la imagen tomando en cuenta la orientación que recibió del método orientationModel, usa la librería PIL. Nota importante: PIL rota las imágenes en sentido contra-reloj.* removeExif: Método usado para eliminar los metadatos de los archivos .jpg conocidos como EXIF(Exchangeable Image File Format), se incluye específicamente porque algunas imágenes incluyen metadatos que indican el ángulo en el que se tomó la foto, lo cual interfiere con los servicios de orientación y rotación. Se corre para cada imagen antes de hacer todo el proceso.* getImageSize: Obtiene el ancho y la altura de una imagen, usando la librería PIL.* roundNum: Redondea un número, si su decimal es >= .5 lo redondea para arriba, de lo contrario lo redondea para abajo.* calculateBoundingBox: Convierte coordenadas de Bounding Box (Left, Top, Width, Height) junto con el ancho y el alto de la imagen base al formato necesario para los resultados (xmin, ymin, xmax, ymax)* objectDetectionModel: Usando la librería requests, llama al modelo de detección de objetos entrenado con Azure Custom Vision, iteración 3. Regresa todos los objetos en rectángulos con coordenadas, e une los resultados en un diccionario final, filtrando los que no cumplan con una probabilidad mínima.* checkDetectionModel: Usando la librería requests, llama al modelo de detección de objetos entrenado con Azure Custom Vision, iteración 4. Regresa todos los objetos en rectángulos con coordenadas, e une los resultados en un diccionario final, filtrando los que no cumplan con una probabilidad mínima y filtrando los que sus tags no sean "paloma" esto porque esta iteración 4 del modelo fue la mejor para detectar estos objetos.* createCsv: Crea el csv de resultados con sus headers* appendCsv: Agrega los resultados de cada archivo al csv de resultados<jupyter_code>import os
from PIL import Image
from pdf2image import convert_from_path
from math import ceil, floor
import requests
import json
import csv
import time

#Function to check if returned JSON has all the necessary fields in each nested field of the JSON
def checkValueField(rs,field,i,var):
    val = ""
    try:
        val = rs[field][i][var]
    except ValueError:
        val = ""
    except KeyError:
        val = ""
    finally:
        return val

def convertPdf(path, file):
    s = time.perf_counter()
    fullpath = path + file
    images = convert_from_path(fullpath)
    name = file.split('.pdf')[0]
    nameJpg = name +'.jpg'
    for i in range(len(images)):
        # Save pages as images in the pdf
        images[i].save(path + nameJpg, 'JPEG')
    print(f'convertPdf in {time.perf_counter() - s:0.2f} seconds.')
    return nameJpg

def orientationModel(path, file):
    s = time.perf_counter()
    #Request to orientation model
    with open(path+file, 'rb') as f:
        data = f.read()
    req = requests.post(
        url="https://cscvriia-prediction.cognitiveservices.azure.com/customvision/v3.0/Prediction/2bb8fee1-6413-418c-9c6e-3c41629397ce/classify/iterations/Iteration2/image",
        headers={
            "Content-Type": "application/octet-stream",
            "Prediction-Key": "bac8aef998f94f04b527450124a7864a"
        },
        data = data
    )
    result = json.loads(req.content)
    tag = result['predictions'][0]['tagName']
    print(f'orientationModel in {time.perf_counter() - s:0.2f} seconds.')
    return int(tag)

def rotateFile(path, file, orientation):
    fullpath = path + file
    im = Image.open(fullpath)

    if orientation == 90:
        im = im.transpose(Image.ROTATE_90)
        im.save(fullpath,'JPEG')

    elif orientation == 180:
        im = im.transpose(Image.ROTATE_180)
        im.save(fullpath,'JPEG')

    elif orientation == 270:
        im = im.transpose(Image.ROTATE_270)
        im.save(fullpath,'JPEG')

def removeExif(path, file):
    s = time.perf_counter()

    fullpath = path + file
    try:
      im = Image.open(fullpath)
      # next 4 lines strip exif metadata
      data = list(im.getdata())
      im = Image.new(im.mode, im.size)
      im.putdata(data)
      im.save(fullpath)
    except IOError:
      return
    
    print(f'removeExif in {time.perf_counter() - s:0.2f} seconds.')

def getImageSize(path, file):
    im = Image.open(path+file)
    return int(im.width),int(im.height)

def roundNum(num):
    decimal = num % 1
    if decimal >= .5:
        return ceil(num)
    else:
        return floor(num)

def calculateBoundingBox(left, top, width, height, widthImg, heightImg):
    #https://stackoverflow.com/questions/53737055/bounding-box-left-top-height-width-to-php-x1-x2-y1-y2-coordinates
    w = widthImg *  width
    h = heightImg * height

    xmin = left * widthImg
    ymin = top * heightImg
    xmax = xmin + w
    ymax = ymin + h

    return roundNum(xmin), roundNum(ymin), roundNum(xmax), roundNum(ymax)

def objectDetectionModel(path, file, width, height):
    s = time.perf_counter()
    #request to Object Detection Model
    with open(path+file, 'rb') as f:
        data = f.read()
    req = requests.post(
        url="https://cscvriia-prediction.cognitiveservices.azure.com/customvision/v3.0/Prediction/c2380253-45d3-4804-9c76-093802973de6/detect/iterations/Iteration3/image",
        headers={
            "Content-Type": "application/octet-stream",
            "Prediction-Key": "bac8aef998f94f04b527450124a7864a"
        },
        data = data
    )
    result = json.loads(req.content)
    finalResult = {}
    #Ignore probability below 0.5
    i = 0
    acum = 0
    while checkValueField(result,'predictions',i,'probability'):
        probability = checkValueField(result,'predictions',i,'probability')
        if probability >= 0.5:
            tagName = checkValueField(result,'predictions',i,'tagName')
            boundingBoxLeft = result['predictions'][i]['boundingBox']['left']
            boundingBoxTop = result['predictions'][i]['boundingBox']['top']
            boundingBoxWidth = result['predictions'][i]['boundingBox']['width']
            boundingBoxHeight = result['predictions'][i]['boundingBox']['height']
            xmin, ymin, xmax, ymax = calculateBoundingBox(boundingBoxLeft, boundingBoxTop, boundingBoxWidth, boundingBoxHeight, width, height)
            finalResult[acum] = [tagName, probability, xmin, ymin, xmax, ymax]
            acum += 1
        
        i += 1

    print(f'objectDetectionModel in {time.perf_counter() - s:0.2f} seconds.')
    return finalResult

def checkDetectionModel(path, file, width, height, finalResult):
    s = time.perf_counter()
    #request to check Detection Model
    with open(path+file, 'rb') as f:
        data = f.read()
    req = requests.post(
        url="https://cscvriia-prediction.cognitiveservices.azure.com/customvision/v3.0/Prediction/c2380253-45d3-4804-9c76-093802973de6/detect/iterations/Iteration4/image",
        headers={
            "Content-Type": "application/octet-stream",
            "Prediction-Key": "bac8aef998f94f04b527450124a7864a"
        },
        data = data
    )
    result = json.loads(req.content)
    
    acum = 0
    for i in finalResult:
      acum += 1

    #Ignore probability below 0.5
    i = 0
    while checkValueField(result,'predictions',i,'probability'):
        probability = checkValueField(result,'predictions',i,'probability')
        tagName = checkValueField(result,'predictions',i,'tagName')
        if probability >= 0.5 and tagName == "check":
            boundingBoxLeft = result['predictions'][i]['boundingBox']['left']
            boundingBoxTop = result['predictions'][i]['boundingBox']['top']
            boundingBoxWidth = result['predictions'][i]['boundingBox']['width']
            boundingBoxHeight = result['predictions'][i]['boundingBox']['height']
            xmin, ymin, xmax, ymax = calculateBoundingBox(boundingBoxLeft, boundingBoxTop, boundingBoxWidth, boundingBoxHeight, width, height)
            finalResult[acum] = [tagName, probability, xmin, ymin, xmax, ymax]
            acum += 1
        
        i += 1

    print(f'checkDetectionModel in {time.perf_counter() - s:0.2f} seconds.')
    return finalResult

def createCsv(csvname, prefix):
    with open(prefix+csvname, 'w', newline='') as file:
        writer = csv.writer(file)
        writer.writerow(['filename','width','height','class','xmin','ymin','xmax','ymax'])

def appendCsv(csvname, filename, width, height, result,prefix):
    with open(prefix+csvname, 'a+', newline='') as file:
        writer = csv.writer(file)
        #For loop each result
        for i in result:
            writer.writerow([filename, width, height, result[i][0], result[i][2], result[i][3], result[i][4], result[i][5]])<jupyter_output><empty_output><jupyter_text>Pipeline principal<jupyter_code>s = time.perf_counter()

list_of_files = []
prefix = pathResult

#Initializing suffix list
suff_list = ['.jpg', '.pdf', '.JPG']

#Leer todos los archivos del directorio que terminen con los sufijos aprobados
for root, dirs, files in os.walk(prefix):
    for file in files:
        if not file.endswith(tuple(suff_list)):
            continue
        list_of_files.append(os.path.join(file))

#Eliminar metadata de las imágenes
for file in list_of_files:
    removeExif(prefix, file)

#El csv de resultados se guarda en el mismo directorio
createCsv('resultados.csv',prefix)

for file in list_of_files:
    #Si el archivo es pdf, convertirlo a jpg
    if file.endswith('.pdf'):
        file = convertPdf(prefix, file)
    #Obtener la orientacion del archivo y rotar la imagen
    #orientation = orientationModel(prefix, file)
    #rotateFile(prefix, file, orientation)

    #Obtener ancho y largo de la foto
    width, height = getImageSize(prefix, file)
    #Llamar al modelo de detección de objetos
    result = objectDetectionModel(prefix, file, width, height)
    #Llamar al modelo de detección de check
    result = checkDetectionModel(prefix, file, width, height, result)
    #Adjuntar a un csv
    appendCsv('resultados.csv', file, width, height, result,prefix)
    print(file)
    print("----------")


print(f'finished in {time.perf_counter() - s:0.2f} seconds.')<jupyter_output>removeExif in 0.52 seconds.
removeExif in 2.18 seconds.
removeExif in 0.52 seconds.
removeExif in 2.19 seconds.
removeExif in 0.52 seconds.
removeExif in 2.14 seconds.
removeExif in 2.18 seconds.
removeExif in 2.18 seconds.
removeExif in 2.22 seconds.
removeExif in 2.18 seconds.
removeExif in 2.18 seconds.
removeExif in 2.20 seconds.
removeExif in 2.19 seconds.
removeExif in 2.21 seconds.
removeExif in 4.05 seconds.
removeExif in 3.93 seconds.
removeExif in 2.17 seconds.
removeExif in 1.96 seconds.
removeExif in 3.89 seconds.
removeExif in 1.01 seconds.
removeExif in 3.92 seconds.
removeExif in 3.91 seconds.
removeExif in 3.93 seconds.
removeExif in 1.02 seconds.
removeExif in 3.97 seconds.
objectDetectionModel in 1.17 seconds.
checkDetectionModel in 1.17 seconds.
AGN_DFS_C.18_1_de_2_Cabrera_Arenas_Emma_pg-002.jpg
----------
objectDetectionModel in 1.16 seconds.
checkDetectionModel in 2.53 seconds.
AGN_DFS_C.18_2_de_2_Cabrera_Arenas_Emma_pg-326.jpg
----------
objectDetectionModel in 1.[...]
{'fraction_non_alphanumeric': 0.09858364885710279, 'fraction_numerical': 0.02566259991586033, 'mean_word_length': 3.813702328720891, 'pattern_counts': {'":': 7, '<': 22, '<?xml version=': 0, '>': 26, 'https://': 4, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '6', 'dir': 'jupyter-structured-clean-dedup', 'id': '374001', 'license': 'MIT', 'n_tokens_mistral': 4832, 'n_tokens_neox': 4574, 'n_words': 1370, 'path': 'JusticIA_AccesoDatos.ipynb', 'repo_name': 'georgetegral/ModeloRepositorioJusticIA', 'size': '14262'}