[PYTHON] Correction d'angle (conversion de projection) de la licence à l'aide d'OpenCV - Déterminer automatiquement le seuil de binarisation -

Motivation

Aperçu

--Le contour d'une carte (carte nanaco) de la même taille que la licence est détecté par ** OpenCV **, et la ** conversion par projection ** est effectuée pour rendre le contenu de la carte plus facile à lire **.

Points de différenciation d'autres articles similaires

Lecteur supposé

Procédure de travail

  1. ** Construction de l'environnement **
  2. ** Binarisation ** [^ binarisation]
  3. ** Extraction de contour **
  4. ** Conversion de projection **

Environnement

J'utilise Pipenv.

Pipenv

brew install pipenv

Package relationnel

pipenv install numpy matplotlib opencv-contrib-python pyocr
pipenv install jupyterlab --dev

Structure du répertoire

Trois images avec la configuration suivante

--nanaco.jpeg (pris de la manière la plus simple) --nanaco_skew.jpeg (prise de telle sorte que la forme de la carte soit déformée d'un angle) --nanaco_in_hand.jpeg (pris en le tenant dans la main sur un fond blanc)

Je vais essayer. Le code source utilise jupyter notebook card.ipynb.

.
├── Pipfile
├── Pipfile.lock
├── images
│   ├── nanaco.jpeg
│   ├── nanaco_in_hand.jpeg
│   └── nanaco_skew.jpeg
└── notebooks
    └── card.ipynb

Essayez d'afficher une image de la carte dans OpenCV

  1. Démarrez Jupyter Lab
pipenv run jupyter lab
  1. Créez notebooks / card.ipynb et exécutez ce qui suit dans la cellule (exécutez tous les autres scripts de la cellule)
%matplotlib inline
import cv2
import matplotlib.pyplot as plt
img = cv2.imread('../images/nanaco_skew.jpeg')
plt.imshow(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))

image.png

Je suis curieux de connaître l'échelle de matplotlib, mais je m'en fiche, mais les coordonnées sont plutôt faciles à comprendre, je vais donc procéder comme c'est cette fois.

Binarisation

Échelle de gris

#Échelle de gris
gray_img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
plt.imshow(gray_img)
plt.gray()

Déterminer le seuil de binarisation

Dans de nombreux didacticiels et articles, le seuil de binarisation était codé en dur à une valeur d'environ «200» et était traité comme s'il était déterminé manuellement. Dans cet article, j'ai inclus ** une logique qui détermine dynamiquement (automatiquement) le seuil **

import numpy as np
#nanaco vaut 0.Environ 2 semble bon. Si vous avez une licence, vous devrez peut-être effectuer à nouveau le réglage
card_luminance_percentage = 0.2

# TODO:Perspectives de performance
def luminance_threshold(gray_img):
    """
Valeur en échelle de gris(Luminosité appelée)Mais`x`Le nombre de points ci-dessus est de 20%Calculez le x maximum qui dépasse
cependant,`100 <= x <= 200`À
    """
    number_threshold = gray_img.size * card_luminance_percentage
    flat = gray_img.flatten()
    # 200 -> 100 
    for diff_luminance in range(100):
        if np.count_nonzero(flat > 200 - diff_luminance) >= number_threshold:
            return 200 - diff_luminance
    return 100

threshold = luminance_threshold(gray_img)
print(f'threshold: {threshold}')

Les seuils pour les trois types d'images cette fois ont été calculés comme suit.

Par exemple, dans nanaco_skew.jpeg, cela ne fonctionnait pas si le seuil était (couramment utilisé) 200, probablement à cause de la quantité de lumière réfléchie. En utilisant «138» calculé à partir du code source ci-dessus, vous pouvez obtenir le contour de la carte plus tard.

nanaco.jpeg nanaco_skew.jpeg nanaco_in_hand.jpeg
Seuil de binarisation 200 138 199
image nanaco.jpeg nanaco_skew.jpeg nanaco_in_hand.jpeg

Binarisation


_, binarized = cv2.threshold(gray_img, threshold, 255, cv2.THRESH_BINARY)
plt.imshow(cv2.cvtColor(binarized, cv2.COLOR_BGR2RGB))

image.png

Extraction de contour

contours, _ = cv2.findContours(binarized, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)

#Sélectionnez celui avec la plus grande surface
card_cnt = max(contours, key=cv2.contourArea)

#Dessinez des contours sur l'image
line_color = (0, 255, 0)
thickness = 30
cv2.drawContours(img, [card_cnt], -1, line_color, thickness)
plt.imshow(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))

image.png

Transformation de projection

La conversion de projection (correction d'angle) est effectuée sur la base des informations de contour capturées ci-dessus.

#Contour approximatif avec convexe
#Valeur fixe de 0 pour la longueur totale du contour.Il suffit de multiplier un coefficient de 1
#Il semble que le réglage du coefficient est presque inutile dans la mesure où la carte est copiée correctement dans une certaine mesure.(Peut être nécessaire pour le réglage OCR)
epsilon = 0.1 * cv2.arcLength(card_cnt, True)
approx = cv2.approxPolyDP(card_cnt, epsilon, True)

#Largeur de la carte(Comme la carte est verticale dans l'image, la largeur et la hauteur sont inversées pendant la conversion de projection.)
card_img_width = 2400 #Valeur appropriée
card_img_height = round(card_img_width * (5.4 / 8.56)) #Ration de licence(=rapport nanaco)Calculé en divisant par

src = np.float32(list(map(lambda x: x[0], approx)))
dst = np.float32([[0,0],[0,card_img_width],[card_img_height,card_img_width],[card_img_height,0]])

projectMatrix = cv2.getPerspectiveTransform(src, dst)

#Étant donné que la ligne a été écrasée plus tôt, récupérez l'image
img = cv2.imread('../images/nanaco_skew.jpeg')
transformed = cv2.warpPerspective(img, projectMatrix, (card_img_height, card_img_width))
plt.imshow(cv2.cvtColor(transformed, cv2.COLOR_BGR2RGB))

image.png

**l'a fait! !! ** Les lettres inclinées sont maintenant droites!

prime

Après cela, je pense essayer de lire le contenu en utilisant l'OCR en utilisant une licence réelle, mais j'ai aussi essayé un peu avec le nanaco actuel. Cependant, s'il est nécessaire de mettre des restrictions sur la partie à lire, c'est grossièrement fait.

En utilisant l'image de nanaco_in_hand.jpeg, j'ai essayé d'appliquer l'OCR à la dernière image obtenue en utilisant pyocr pour l'image entière. Vous pouvez l'obtenir en exécutant le même script que ci-dessus pour nanaco_in_hand.jpeg (légèrement diagonale ...) image.png

J'ai essayé de convertir cette image en texte en utilisant pyocr + tesseract selon le tutoriel. [^ ocr]

Le résultat est ...

Dans ce plan d'utilisation de la carte nanaco
Pour plus d'informations sur l'utilisation de la carte mâchoire, veuillez consulter le contrat d'adhésion. Cinq
La carte d'Ako est un magasin membre avec la marque nanaco sur la droite, et vous pouvez utiliser le gestionnaire électronique et le gestionnaire électronique dans la carte.
Vous pourrez confirmer votre solde.
Ne pliez pas la carte, ne lui donnez pas un impact important, ne la laissez pas à haute température ou lorsqu'elle est magnétisée.
La carte d'Ako et le gestionnaire électronique de la carte ne sont pas encaissables.
Le prix maximum de la carte d'Ako est de 50 000 yens.
La carte Ako ne peut être utilisée que par le membre qui a approuvé le contrat d'adhésion et signé le champ du nom du bureau membre.
La propriété de la carte d'Ako appartient à Seven Card Service Co., Ltd. et ne peut être prêtée ou remise à une autre personne.

Est-ce un endroit qui est bâclé malgré le fait de le faire grossièrement? J'améliorerai la précision de cette zone avec une licence et continuerai. (Il est assez intéressant que "●" soit reconnu comme "A")

[^ binarisation]: image RVB avec informations de chaque point (256x256x256) → Convertit l'image binaire des informations binaires de chaque point (2 = 1/0). Il s'agit d'un processus pour faciliter l'extraction des contours. [^ opencv]: ʻopencv-contrib-python importe le module contrib en plus de ʻopen-python. ʻOpencv-python semble être d'accord avec cv2`. Cependant, en ce qui concerne le document officiel, il semble qu'il soit préférable d'utiliser cette notation pour les nouveaux éléments. (Référence: https://pypi.org/project/opencv-python/) [^ ocr]: Cet article a également été utile! Une installation telle que tesseract est requise.

Recommended Posts

Correction d'angle (conversion de projection) de la licence à l'aide d'OpenCV - Déterminer automatiquement le seuil de binarisation -
Déterminez le nombre de classes à l'aide de la formule Starges
Déterminer le seuil à l'aide de la méthode P-tile en python