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.
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
Imprimer et distribuer
Scannez le questionnaire collecté et passez du PDF au JPG Je l'ai converti correctement sur un site de conversion gratuit sur le net
Lisez les résultats de l'enquête à partir du fichier JPG converti
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
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.
Préparez des questions et des notes dans Excel. Les points sont les suivants.
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
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

Je vais imprimer et distribuer ce que j'ai fait comme ça.
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.
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
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