Créer une lecture de feuille de notes avec Python OpenCV (Conseils pour bien lire)

Depuis que j'ai décidé de faire un questionnaire papier au travail, je me réfère principalement à cet article (Making a simple OMR (mark sheet reader) with Python and OpenCV) Je l'ai fait. J'ai souvent mal reconnu les lignes vierges, alors j'apprécierais que vous puissiez vous y référer.

Flux de l'ensemble du questionnaire

  1. Créez un questionnaire avec un code QR intégré dans Excel J'ai mis des informations dans le code QR et je les ai intégrées pour identifier le numéro de page et l'individu du questionnaire

  2. Imprimer et distribuer

  3. Scannez le questionnaire collecté et passez du PDF au JPG Je l'ai converti correctement sur un site de conversion gratuit sur le net

  4. Lisez les résultats de l'enquête à partir du fichier JPG converti

environnement

1. Créer un questionnaire

2. Scanner

3. Marquer la lecture de la feuille

point

1. Points lors de la création d'un questionnaire

2. Points lors de la numérisation

3. Points lors de la lecture de la feuille de notes

Détails

1. Créer un questionnaire

1. Créez un code QR

Comme il y a plusieurs feuilles de questions cette fois, afin de déterminer quelle question est pour quelle question et qui a écrit la feuille de questions, nous avons décidé d'intégrer le code QR dans la feuille de questions et de capturer la réponse en CSV avec les informations lors de la lecture. fait.

--Référence: Générer et enregistrer l'image de code QR avec Python, Pillow, qrcode

def makeQr(qrMessage, fileName='result.png', filePath='resultQrCode/'):
    """Créez un code QR avec l'argument qrMessage et enregistrez-le dans resultQrCode
    Args:
        qrMessage (str):Code QR à créer
        fileName (str, optional):Nom du fichier de sortie. Defaults to 'result.png'.
        filePath (str, optional):Chemin du fichier de sortie * À la fin de la liste, "/」. Defaults to 'resultQrCode/'.
    """
    import qrcode
    import os

    img = qrcode.make(qrMessage)
    if not os.path.isdir(filePath):
        os.makedirs(filePath)
    if not(filePath[-1] == '\\' or filePath[-1] == '/'):
        filePath = filePath + '\\'
    
    img.save(filePath + fileName)
    print('File out:' + filePath + fileName)

if __name__ == '__main__':
    import re
    import sys
    
    args = sys.argv
    if 1 < len(args):
        if re.findall('[/:*?"<>|]', args[1]):
            print('[Error]Caractère interdit "/:*?"<>|」')
        elif 2 == len(args):
            makeQr(args[1])
        elif re.findall('[:*?"<>|]', args[2]):
            print('[Error]Caractères interdits dans le nom de fichier ":*?"<>|」')
        elif 3 == len(args):
            makeQr(args[1],args[2])
        elif re.findall('[*?"<>|]', args[3]):
            print('[Error]Caractères interdits dans le nom du dossier "*?"<>|」') 
        elif 4 == len(args):
            makeQr(args[1],args[2],args[3])
        else:
            qrMessage = args[1]
            for qrMessageList in args[4:]:
                qrMessage = qrMessage + '\r\n' + qrMessageList
            makeQr(qrMessage,args[2],args[3])
    else:
        print('error: args is one')
        print('usage1: makeQr.exe [QR_Text]')
        print('usage2: makeQr.exe [QR_Text] [Output_FileName]')
        print('usage3: makeQr.exe [QR_Text] [Output_FileName] [Output_FilePath]')
        print('usage4: makeQr.exe [QR_Text] [Output_FileName] [Output_FilePath] [QR_Text_Line2...]')

Utilisez ceci pour que quiconque puisse l'utiliser plus tard. Je l'ai transformé en un exe avec py2exe. (Pyinstaller va bien, mais il était lourd, j'ai donc choisi py2exe ici.) --Référence: Utilisation de py2exe avec python 3.5 --3.7

2. Préparation des marqueurs

Découpez la plage de la feuille de notes afin que la question et la feuille de notes puissent être combinées. Préparez des images en noir et blanc caractéristiques aux quatre coins de la zone de découpe. Étant donné que le code QR est utilisé cette fois, les marqueurs (entourés d'un cadre rouge) utilisés dans le code QR de la figure ci-dessous ne peuvent pas être utilisés. De plus, comme j'utilise Excel, j'ai décidé que si je définissais ★ comme texte dans une figure (forme automatique), ce serait un marron, j'ai donc préparé ★ comme une figure. Puisqu'il est nécessaire de transmettre le marqueur ★ en tant que fichier image au programme d'analyse, j'ai collé la forme Excel avec de la peinture, etc. et l'ai enregistrée. Étant donné que la taille et les marges doivent être identiques à celles de la forme automatique, il est recommandé de minimiser la hauteur et la largeur avant de coller.

3. Préparation de la marque

Préparez des questions et des notes dans Excel. Les points sont les suivants.

4. Redimensionnez le marqueur pour l'adapter à la largeur du papier et collez le marqueur

Comme il s'agit d'Excel, l'impression est agrandie ou réduite, et la taille du marqueur enregistré en tant qu'image et le marqueur au moment de l'impression sont différents, et il peut ne pas être bien reconnu, il est donc nécessaire d'obtenir (voir) le grossissement d'agrandissement pour chaque feuille. -Référence ExecuteExcel4Macro "Page.Setup ()"

Public Function getPrintZoomPer(sheetName As String) As Integer
    Worksheets(sheetName).Activate
    ExecuteExcel4Macro "Page.Setup(,,,,,,,,,,,,{1,#N/A})"
    ExecuteExcel4Macro "Page.Setup(,,,,,,,,,,,,{#N/A,#N/A})"
    getPrintZoomPer = ExecuteExcel4Macro("Get.Document(62)")
End Function

Placez la source du marqueur sur une feuille et collez la source du marqueur aux quatre coins de la feuille que vous souhaitez attacher. Lorsque vous collez le marqueur, multipliez le grossissement acquis par le nombre inverse. Puisque le nombre de questions et d'options est variable, je l'ai fait avec vba sans le réparer.

Public Sub insertMaker(sheetName As String, pasteCellStr As String, _
         printZoomPer As Integer)
    ' sheetName:Nom de feuille de la destination de collage
    ' paseteCellStr:Chaîne de caractères de cellule à coller Exemple: A4, B1, etc.
    Dim srcShape As shape
    Set srcShape = Worksheets("sheet1").Shapes("marker") 
    ' sheet1:Nom de la feuille avec le marqueur Forme de la source de collage
    ' marker:Coller le nom du Shapenamae du marqueur source
    srcShape.Copy
    With Worksheets(sheetName).Pictures.Paste
        .Top = Worksheets(sheetName).Range(pasteCellStr).Top
        .Left = Worksheets(sheetName).Range(pasteCellStr).Left
        .Name = "marker"
        .Width = .Width * 100 / printZoomPer
    End With
End Sub

5. Insérez le code QR

Créez en saisissant "type de question + numéro de page du questionnaire + agence + numéro de personne + nombre de choix + nombre de questions" dans le code QR. Appelez le fichier exe créé en "1" à partir de la macro Excel avec WScript.Shell.

Public Function makeQr(ByVal QrMessage As String, ByVal fileName As String) As String
    Dim WSH, wExec, sCmd As String
    Set WSH = CreateObject("WScript.Shell")
    
    sCmd = ThisWorkbook.Path & "makeQR.exe " & QrMessage & " " & fileName & " " & _
           ThisWorkbook.Path & "resultQrCode"
    Set wExec = WSH.Exec("%ComSpec% /c " & sCmd)
    
    Do While wExec.Status = 0
        DoEvents
    Loop
    makeQr = wExec.StdOut.readall

    Set wExec = Nothing
    Set WSH = Nothing

End Function

Après avoir fait diverses choses, le résultat est comme ci-dessous Enquete.PNG

Je vais imprimer et distribuer ce que j'ai fait comme ça.

2. Scanner

Une fois le questionnaire rempli, une analyse sera effectuée. Puisqu'il y a un réglage de résolution sur le côté lecture de la feuille de repères, je l'ai fixé à 200 dpi cette fois. (De plus, comme la machine multifonction au travail ne pouvait pas être enregistrée directement au format jpg, elle a été convertie par Enregistrer au format PDF et PDF => site de conversion JPG. De plus, j'utilise openCV du côté lecture de la feuille de repère, mais veuillez noter que les caractères pleine largeur ne peuvent pas être utilisés dans le nom de fichier.

3. Marquer la lecture de la feuille

1. Lire le code QR

Placez les fichiers JPG agrégés dans un dossier, lisez le code QR et renvoyez-le en argument.

def qrCodeToStr(filePath):
"""Lire la chaîne de caractères du code QR
Args:
    filePath (String):Chemin du fichier image contenant le code QR
Returns:
    String:Résultat de la lecture du code QR(Échec de nullString)
"""
import cv2

img = cv2.imread(filePath, cv2.IMREAD_GRAYSCALE)
#Décodage du code QR
qr = cv2.QRCodeDetector()
data,_,_ = qr.detectAndDecode(img)

if data == '':
    print('[ERROR]' + filePath + 'Impossible de trouver le code QR de')
else:
    print(data)
return data

2. Marquer la lecture de la feuille

C'est presque le même que cet article (Créer un simple OMR (lecteur de feuille de marque) avec Python et OpenCV). Les changements sont les suivants.

def changeMarkToStr(scanFilePath, n_col, n_row, message):
    """Lisez la feuille de notes et définissez le résultat sur False,Renvoie comme un véritable tableau à deux dimensions
    Args:
        scanFilePath (String):Chemin du fichier JPEG, y compris le format de feuille de repère
        n_col (int):Nombre de choix(Le nombre de colonnes)
        n_row (int):Nombre de questions(Nombre de lignes)
    Returns:
        list:Résultat de la lecture de la feuille de notes Faux,Vrai tableau bidimensionnel
    """
    ### n_col = 6 #Nombre de marques par ligne
    ### n_row = 9 #Nombre de lignes de marque
    import numpy as np
    import cv2

    ###Paramètres des marqueurs
    marker_dpi = 120 #Résolution d'écran(Taille du marqueur)
    scan_dpi = 200 #Résolution d'image numérisée

    #niveaux de gris(mode = 0)Lisez le fichier avec
    marker=cv2.imread('img/setting/marker.jpg',0) 

    #Obtenez la taille du marqueur
    w, h = marker.shape[::-1]

    #Redimensionner le marqueur
    marker = cv2.resize(marker, (int(h*scan_dpi/marker_dpi), int(w*scan_dpi/marker_dpi)))

    ###Charger l'image numérisée
    img = cv2.imread(scanFilePath,0)

    res = cv2.matchTemplate(img, marker, cv2.TM_CCOEFF_NORMED)

    ##Répéter l'extraction à partir de 3 points de fabrication Les conditions d'extraction sont les suivantes
    margin_top = 1 #Nombre de lignes de marge supérieures
    margin_bottom = 0 #Nombre de lignes de marge inférieure
 
    for threshold in [0.8, 0.75, 0.7, 0.65, 0.6]:
    
        loc = np.where( res >= threshold)
        mark_area={}
        try:
            mark_area['top_x']= sorted(loc[1])[0]
            mark_area['top_y']= sorted(loc[0])[0]
            mark_area['bottom_x']= sorted(loc[1])[-1]
            mark_area['bottom_y']= sorted(loc[0])[-1]

            topX_error = sorted(loc[1])[1] - sorted(loc[1])[0]
            bottomX_error = sorted(loc[1])[-1] - sorted(loc[1])[-2]
            topY_error = sorted(loc[0])[1] - sorted(loc[0])[0]
            bottomY_error = sorted(loc[0])[-1] - sorted(loc[0])[-2]
            img = img[mark_area['top_y']:mark_area['bottom_y'],mark_area['top_x']:mark_area['bottom_x']]

            if (topX_error < 5 and bottomX_error < 5 and topY_error < 5 and bottomY_error < 5):    
                break
        except:
            continue

    #Ensuite, afin de faciliter le traitement ultérieur, marquez l'image découpée.
    #Redimensionner à une taille qui est un multiple entier du nombre de colonnes et de lignes.
    #Ici, le nombre de colonnes et de lignes est de 100 fois.
    #Lorsque vous comptez le nombre de lignes, tenez compte de la marge entre la zone de marque et le marqueur.

    n_row = n_row + margin_top + margin_bottom
    img = cv2.resize(img, (n_col*100, n_row*100))

    ###Brouiller
    img = cv2.GaussianBlur(img,(5,5),0)

    ###Binar avec 50 comme seuil
    res, img = cv2.threshold(img, 50, 255, cv2.THRESH_BINARY+cv2.THRESH_OTSU)

    ###Inversion noir et blanc
    img = 255 - img
    cv2.imwrite('img/res.png',img)

    #Marquer la reconnaissance

    ###Préparez un tableau pour mettre le résultat
    result = []
    
    ###Traitement ligne par ligne(Traiter en excluant les lignes de marge)
    for row in range(margin_top, n_row - margin_bottom):
        
        ###Découpez uniquement la ligne à traiter
        tmp_img = img [row*100:(row+1)*100,]
        area_sum = [] #Un tableau pour mettre la valeur totale

        ###Traitement de chaque marque
        for col in range(n_col):

            ###Calculez la valeur totale des images dans chaque zone de marque avec NumPy
            area_sum.append(np.sum(tmp_img[:,col*100:(col+1)*100]))

        ###Jugez si la valeur totale de la zone d'image est 4 fois ou plus de la valeur moyenne
        ###Si vous cousez réellement la marque, 4.9 à 6 fois, il y en a eu 3 car il n'était pas du tout peint
        ###Si elle est 3 fois la valeur médiane, elle ne peut pas être utilisée lorsque 0 continue.
        ressss = (area_sum > np.average(area_sum) * 4)
        #Comme il est facile d'extraire plusieurs conditions dans les conditions ci-dessus, extraire plus de la moitié de la valeur maximale
        if np.sum(ressss == True) > 1:
            ressss = (area_sum > np.max(area_sum) * 0.5)
        result.append(ressss)

    for x in range(len(result)):
        res = np.where(result[x]==True)[0]+1
        if len(res)>1:
            message.append('multi answer:' + str(res))
        elif len(res)==1:
            message.append(res[0])
        else:
            message.append('None')
    message.insert(0,scanFilePath)
    print(message)
    return message

C'est un gribouillage parce que c'est pour mon propre mémo, mais si ça aide

Recommended Posts

Créer une lecture de feuille de notes avec Python OpenCV (Conseils pour bien lire)
Créez un simple OMR (lecteur de feuille de marque) avec Python et OpenCV
Essayez de créer un fichier compressé en utilisant Python et zlib
Tirez en accéléré à partir d'une caméra PC en utilisant Python, OpenCV
[Python] Accès et recadrage des pixels d'image à l'aide d'OpenCV (pour les débutants)
[TouchDesigner] Conseils pour la déclaration par python
Créez une illusion rayée avec correction gamma pour Python3 et openCV3
Conseils d'utilisation de Selenium et Headless Chrome dans un environnement CUI
Conseils pour utiliser python + caffe avec TSUBAME
Procédure de création d'un LineBot réalisé avec Python
Créer une carte Web en utilisant Python et GDAL
J'ai essayé de lire un fichier CSV en utilisant Python
[Hikari-Python] Chapitre 09-02 Classes (Création et instanciation de classes)
Commandes pour créer un environnement python3 avec virtualenv
Procédure de création d'un environnement d'isolation Python (environnement venv)
Conseils pour utiliser Elastic Search de manière efficace
Notes sur la création d'un environnement python par les débutants
Faisons un module pour Python en utilisant SWIG
Paramètres initiaux pour l'utilisation de Python3.8 et pip sur CentOS8
Recherche de balises pixiv et enregistrement d'illustrations à l'aide de Python
Squelettes extensibles pour Vim utilisant Python, Click et Jinja2
Lecture de texte Python pour plusieurs lignes et une ligne
Création d'un environnement de travail Docker R et Python
J'ai créé une VM qui exécute OpenCV pour Python
Créez un graphique à l'aide du bouton et du curseur de l'intrigue
Implémentation d'un générateur en utilisant Python> link> yield et next ()> yield
À propos de la création et de la modification de thèmes personnalisés pour Python IDLE
Chargement / affichage et accélération de gif avec python [OpenCV]
Mémo de construction d'environnement d'apprentissage automatique par Python
Créer un discriminateur Sato Yohei en utilisant OpenCV et TensorFlow
OpenCV pour les débutants en Python
Construire un environnement Python sur un Mac, jusqu'au point d'utiliser Jupyter Lab
[Python] Créer une liste de dates et d'heures pour une période spécifiée
[Python] Chapitre 01-03 À propos de Python (Ecrire et exécuter un programme à l'aide de PyCharm)
Essayez une recherche similaire de recherche d'images à l'aide du SDK Python [Recherche]
Une note lors de la création d'un graphe dirigé à l'aide de Graphviz en Python
Conseils pour coder courts et faciles à lire en Python
Bibliothèque pour spécifier un serveur de noms en python et dig
J'ai créé Chatbot en utilisant l'API LINE Messaging et Python
[Python] Utilisation d'OpenCV avec Python (basique)
Installation d'OpenCV3 pour Python3 @macOS
[Python + Selenium] Conseils pour le grattage
~ Conseils pour les débutants de Python présentés avec amour par Pythonista ③ ~
Utiliser OpenCV avec Python @Mac
Création de l'environnement de travail Docker R et Python 2: prise en charge du japonais
Créer et tester un environnement CI pour plusieurs versions de Python
Python: Introduction à Flask: création d'une application d'identification de numéro à l'aide de MNIST
Créer un environnement de développement local pour Lambda + Python à l'aide de Serverless Framework
Pydroid 3 - J'ai essayé les options OpenCV et TensorFlow de l'IDE pour Python 3 (Android)
Traitez le résultat de l'exécution de Splunk en utilisant Python et enregistrez-le dans un fichier
Essayez d'utiliser virtualenv qui peut créer un environnement virtuel de Python
uproot: bibliothèque basée sur Python / Numpy pour lire et écrire des fichiers ROOT
Comment créer une caméra de surveillance (caméra de sécurité) avec Opencv et Python
Un peu plus sur les références ~ Prenant Python et Java comme exemples ~
Créez un lot planifié simple à l'aide de l'image Python de Docker et de parse-crontab
[Traitement d'image] Poo-san est nu par détection de bord en utilisant Python et OpenCV!
Dessinez une illusion d'aquarelle avec détection des contours en Python3 et openCV3
Créez et essayez un environnement OpenCV et Python en quelques minutes à l'aide de Docker