--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 **.
Contenu prêt à lire avec OCR (la lecture du contenu sera présentée dans le prochain article)
Puisque je ne ferai pas d'OCR, je remplacerai cette fois une carte nanaco de la même taille
** De la diagonale ci-dessus ** La carte que j'ai prise ... → Vous pouvez maintenant ** corriger l'angle et afficher la carte ** comme ceci →
J'utilise Pipenv.
Pipenv
brew install pipenv
pipenv install numpy matplotlib opencv-contrib-python pyocr
pipenv install jupyterlab --dev
est ʻopencv-python
. [^ opencv]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
pipenv run jupyter lab
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))
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.
#Échelle de gris
gray_img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
plt.imshow(gray_img)
plt.gray()
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 | |||
_, binarized = cv2.threshold(gray_img, threshold, 255, cv2.THRESH_BINARY)
plt.imshow(cv2.cvtColor(binarized, cv2.COLOR_BGR2RGB))
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))
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))
**l'a fait! !! ** Les lettres inclinées sont maintenant droites!
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 ...)
J'ai essayé de convertir cette image en texte en utilisant pyocr + tesseract
selon le tutoriel. [^ ocr]
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.