En utilisant OpenCV et Python, j'ai créé un outil qui détecte les trous d'alignement du papier vidéo d'animation comme indiqué ci-dessous et supprime le désalignement de l'image numérisée. Un article pratique pour les débutants à OpenCV et Python. Voir ici pour OpenCV
Lors de la création d'une animation, il est facile d'utiliser Application d'images clés 2D comme 9VAe Kyubee, mais si vous dessinez une par une sur du papier vidéo, ce sera plusieurs centaines Ce sera bien plus d'un. Si cela est entré sur un ordinateur personnel avec un scanner avec un chargeur automatique, la position de plusieurs points changera inévitablement et l'image sera floue lors de la lecture.
Par conséquent, créons un outil "Peascan.py" qui détecte les trous sur le papier vidéo et corrige le désalignement par traitement d'image.
contribution | Numéro de série avec désalignement 100 images JPEG |
---|---|
production | Numéro de série sans défaut d'alignement 100 images JPEG |
environnement | Windows |
La reconnaissance des trous, la rotation de l'image et la correction du désalignement devraient être faciles avec OpenCV.
procédure | Contenu | Supplément |
---|---|---|
1.Télécharger | http://python-xy.github.io/downloads.htmlDePython(x,y)-2.7.10.0.exe | Cliquezicipourlaméthoded'installationdétaillée |
2.Installation | Cliquez sur le bouton "Suivant" Sur l'écran "Choisir les composants", cliquez sur "↓" à droite de "Personnalisé" et cliquez sur "FullCliquez sur. Après cela, "Suivant" et "Installer" |
Vous pouvez également ouvrir Custom et vérifier OpenCV. |
Il installe Python 2.7.10, OpenCV 2.4.12, qui est un peu plus tôt, mais cela fonctionne bien.
Créons un simple programme Python + OpenCV.
article | point |
---|---|
Code de caractère | 「UTF8」。メモ帳で保存する場合、「ファイル」>「名前を付けて保存」>下の「Code de caractère」を「UTF-Enregistrez sous "8". |
extension | .py |
Courir | À partir de l'invite de commandepython xxx.py |
Enregistrez le texte suivant sous "UTF-8" avec le nom "test.py". Si le japonais est inclus dans le chemin, cela peut ne pas fonctionner, c'est donc une bonne idée de créer un dossier tel que c: \ pytest
et de l'enregistrer. xx Exemple d'image Au lieu de xx, préparez un fichier image approprié et saisissez le nom du chemin (c: /pytest/test.jpg, etc.). Les noms japonais ne peuvent pas être utilisés. Le délimiteur de chemin est "/".
import numpy as np
import cv2
img = cv2.imread('C:/xx exemple d'image xx.jpg')
print img.shape
print img.shape[0], img.shape[1]
cv2.imshow('Title',img)
cv2.waitKey(5000)
Chacun a les significations suivantes.
article | Exemple d'utilisation | La description |
---|---|---|
Calcul numérique | import numpy as np | Calcul numériqueライブラリを使う |
Traitement d'image | import cv2 | OpenCV Traitement d'imageライブラリを使う |
Charger l'image | img = cv2.imread('C:/exemple d'image.jpg') | Le japonais ne peut pas être utilisé pour les fichiers et les noms de chemin, le délimiteur de chemin est "/」 |
Affichage variable | 「,Affichez n'importe quoi en les séparant par "" | |
Taille de l'image | img.shape | Image img(Hauteur, largeur, nombre de canaux) |
Affichage de l'image | cv2.imshow('Title',img) | Afficher l'image img dans la fenêtre |
pause | cv2.waitKey(5000) | Arrêtez-vous pendant 5 secondes, attendez l'entrée de la clé si 0 |
python test.py
et appuyez sur EntréeAprès avoir affiché l'image, réécrivez-la comme suit à partir de la ligne suivant "img =" dans "test.py". En supposant qu'il y ait une marque dans la plage supérieure gauche (0,0) - (200,200) de l'image, trouvez le centre de gravité. Ajustez les valeurs de frmX, toX, frmY, toY en fonction de la taille de l'image.
import numpy as np
import cv2
img = cv2.imread('C:/xx exemple d'image xx.jpg')
frmX,toX = 0,200 #Gamme de marques (trous)
frmY,toY = 0,200 #Gamme de marques (trous)
mark = img[frmY:toY, frmX:toX] #Image partielle
gray = cv2.cvtColor(mark, cv2.COLOR_BGR2GRAY) #Monochrome
ret, bin = cv2.threshold(gray, 127, 255, 0) #Binarisation
cv2.imshow('out',bin) #Marquer la plage
cv2.waitKey(1000) #1 seconde étape
contours, hierarchy = cv2.findContours(bin, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE) #Extraction de contour
cnt = contours[0] #Premier contour
M = cv2.moments(cnt) #Moment
cx = int(M['m10']/M['m00']) #Centre de gravité X
cy = int(M['m01']/M['m00']) #Centre de gravité Y
cv2.circle(img,(cx,cy), 10, (0,0,255), -1)
print cx,cy
cv2.imshow('Title',img)
cv2.waitKey(5000) #Affichage de 5 secondes
article | Exemple d'utilisation | La description |
---|---|---|
Affectations simultanées multiples | frmX,toX = 200,600 | frmX=200 toX=Identique à 600 Plusieurs valeurs peuvent être attribuées avec une fonction |
Image partielle | img[frmY:toY, frmX:toX] | img(frmX,frmY)-(toX,toY)Sortir |
Monochrome | gray = cv2.cvtColor(mark, cv2.COLOR_BGR2GRAY) | Créer un gris monochrome à partir d'une marque d'image couleur |
Binarisation | ret, bin = cv2.threshold(gray, 127, 255, 0) | grayをBinarisationして bin を作成 |
Inversion noir et blanc | bin = ~bin |
Ne fonctionne pas sur l'ensemble du bac de la baie |
Extraction de contour | contours, hierarchy = cv2.findContours(bin, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE) | les contours ont des contours |
Centre de gravité du contour | M = cv2.moments(cnt) cx = int(M['m10']/M['m00']) cy = int(M['m01']/M['m00']) |
M['m00']Est la zone du contour |
Tracez un cercle | cv2.circle(img,(cx,cy), 10, (0,0,255), -1) | 半径10ドットの赤いTracez un cercle |
Réécrivons "test.py" comme suit. Détecte le centre de gravité de la marque (trou) dans la plage de 200x200 dans le coin supérieur gauche et supérieur droit de l'image, et le transforme pour correspondre à la position de l'image de référence.
import numpy as np
import cv2
img = cv2.imread('C:/xx exemple d'image xx.jpg')
frmX,toX = 0,200 #Gamme de marques (trous)
frmY,toY = 0,200 #Gamme de marques (trous)
def searchMark(img, left): #Fonction pour trouver une marque (trou) à gauche==1 est laissé
if left==1: #Trouvez la marque (trou) sur la gauche
mark = img[frmY:toY, frmX:toX]
else: #Trouvez la marque (trou) sur la droite
mark = img[frmY:toY, img.shape[1]-toX:img.shape[1]-frmX]
gray = cv2.cvtColor(mark, cv2.COLOR_BGR2GRAY) #Monochrome
ret, bin = cv2.threshold(gray, 127, 255, 0) #Binarisation
cv2.imshow('out',bin) #Afficher la plage de marques (trous)
cv2.waitKey(1000) #1 seconde étape
contours, hierarchy = cv2.findContours(bin, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE) #Extraction de contour
ax = ay = sum = 0. #Accumulation de tout le centre de gravité
for cnt in contours: #Recherche de tout le centre de gravité
M = cv2.moments(cnt)
ax += M['m10']
ay += M['m01']
sum += cv2.contourArea(cnt)
if left==1:
cx = ax/sum+frmX
cy = ay/sum+frmY
else:
cx = ax/sum + img.shape[1]-toX
cy = ay/sum + frmY
cv2.circle(img,(int(cx),int(cy)), 10, (0,0,255), -1) #Dessinez un cercle rouge au centre de gravité
print cx,cy #Afficher le centre de gravité calculé
return cx,cy #Valeur de retour de la fonction
#Test de conversion Affin
cx0,cy0 = searchMark(img,1) #Centre de gravité (référence) de la marque (trou) en haut à gauche
dx0,dy0 = searchMark(img,0) #Centre de gravité (référence) de la marque (trou) en haut à droite
cx1,cy1 = cx0,cy0
dx1,dy1 = dx0,dy0+10 #Supposons que la marque (trou) en haut à droite soit décalée de 10 points vers le bas.
cv2.circle(img,(int(dx1),int(dy1)), 10, (255,0,0), -1) #Tracez un cercle bleu au point décalé
pts2 = np.float32([[cx0,cy0],[dx0,dy0],[cx0-(dy0-cy0),cy0+(dx0-cx0)]])
pts1 = np.float32([[cx1,cy1],[dx1,dy1],[cx1-(dy1-cy1),cy1+(dx1-cx1)]])
height,width,ch = img.shape
M = cv2.getAffineTransform(pts1,pts2)
dst = cv2.warpAffine(img,M,(width,height))
cv2.imshow('Title',img) #Afficher l'image avant la conversion
cv2.waitKey(5000) #Affichage de 5 secondes
cv2.imshow('Title',dst) #Afficher l'image convertie
cv2.waitKey(5000) #Affichage de 5 secondes
article | Exemple d'utilisation | La description |
---|---|---|
Définition des fonctions | def searchMark(img, left): | L'intérieur de la fonction est abaissé d'un cran. retour cx,Peut renvoyer plusieurs valeurs comme cy |
Si déclaration | if left==1: else: | Si déclarationの中は、一段下げる。 |
Pour boucle | for cnt in contours: | Exécutez tout le contenu des contours. L'intérieur de For est abaissé d'un cran. |
Zone de contour | cv2.contourArea(cnt) | M['m00']Même valeur que |
Coefficient de conversion d'affine | M = cv2.getAffineTransform(pts1,pts2) | M est un coefficient de conversion qui fait correspondre 3 points pts1 à pts2 |
Conversion d'affine | dst = cv2.warpAffine(img,M,(width,height)) | Convertir une image img pour créer une image dst |
Une fois exécutée, l'image sera convertie de sorte que le cercle bleu déplacé intentionnellement chevauche le cercle rouge d'origine. Ceci termine le processus de détection et de conversion des marques (trous).
Maintenant que le traitement de base est terminé, utilisons-le comme suit. Avec cela, même s'il y a des centaines d'images dans le dossier, vous pouvez facilement les convertir. Cet outil s'appelle "peascan.py". (Abréviation de Correction d'erreur de position après SCAN)
Comment utiliser | "Peascan" le dossier contenant les images.Faire glisser l'icône «py» crée un dossier appelé «out» au même emplacement que l'image, et place le résultat de la correction avec le même nom d'image. |
---|
Plage de repère d'alignement (trou) frmX, toX =, frmY, toY = Veuillez ajuster les nombres après cela en fonction de l'image réelle.
peascan.py
import numpy as np
import cv2
import sys #Obtenir argv
import os #Opération de fichier
argv = sys.argv #Obtenir une liste d'arguments de ligne de commande
argc = len(argv) #Nombre d'arguments
if argc == 2: #Vérifiez s'il s'agit d'un dossier
if os.path.isdir(argv[1]) != True: #Si ce n'est pas un dossier
argc = -1 #Faire une erreur
if argc != 2: #Afficher l'utilisation
print 'Usage: Drag Image folder onto this icon.'
key = raw_input('Hit Enter')
quit() #Fin
#Plage pour vérifier la marque d'alignement (trou) ★ Ajuster selon le cas ★
frmX,toX = 10,200 #10 à partir du bord horizontal-200 points (symétriques)
frmY,toY = 10,200 #10 du haut-200 points
def searchMark(img, left): #Fonction pour trouver une marque (trou) à gauche==1 est laissé
if left==1: #Trouvez la marque (trou) sur la gauche
mark = img[frmY:toY, frmX:toX]
else: #Trouvez la marque (trou) sur la droite
mark = img[frmY:toY, img.shape[1]-toX:img.shape[1]-frmX]
gray = cv2.cvtColor(mark, cv2.COLOR_BGR2GRAY) #Monochrome
ret, bin = cv2.threshold(gray, 127, 255, 0) #Binarisation
cv2.imshow('out',bin) #Afficher la plage de marques (trous)
cv2.waitKey(1000) #1 seconde étape
contours, hierarchy = cv2.findContours(bin, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE) #Extraction de contour
ax = ay = sum = 0. #Accumulation de tout le centre de gravité
for cnt in contours: #Trouvez tout le centre de gravité
M = cv2.moments(cnt)
ax += M['m10']
ay += M['m01']
sum += cv2.contourArea(cnt)
if left==1:
cx = ax/sum+frmX
cy = ay/sum+frmY
else:
cx = ax/sum + img.shape[1]-toX
cy = ay/sum + frmY
cv2.circle(img,(int(cx),int(cy)), 10, (0,0,255), -1) #Dessinez un cercle rouge au centre de gravité
print cx,cy #Afficher le centre de gravité calculé
return cx,cy #Valeur de retour de la fonction
#Boucle principale
inpFolder = argv[1] #Dossier d'image d'entrée
parent = os.path.dirname(argv[1])
outFolder = os.path.join(parent,'out') #Dossier d'image de sortie
if os.path.exists(outFolder) != True: #Si ça n'existe pas
os.mkdir(outFolder) #Créer un dossier de sortie
files = [f for f in os.listdir(inpFolder)] #Image d'entrée
files.sort(key=os.path.basename) #Trier par nom de fichier
cx0 = -1
for fn in files:
img = cv2.imread(os.path.join(inpFolder,fn))
if img is None: #Je n'ai pas pu le lire, alors ensuite
continue
if cx0 == -1: #Souvenez-vous de la première image telle qu'elle est
cx0,cy0=searchMark(img,1)
dx0,dy0=searchMark(img,0)
cv2.imwrite(os.path.join(outFolder,fn), img)
else: #La deuxième image et les suivantes sont converties en Affin pour correspondre à la première image.
cx1,cy1=searchMark(img,1)
dx1,dy1=searchMark(img,0)
pts2 = np.float32([[cx0,cy0],[dx0,dy0],[cx0-(dy0-cy0),cy0+(dx0-cx0)]])
pts1 = np.float32([[cx1,cy1],[dx1,dy1],[cx1-(dy1-cy1),cy1+(dx1-cx1)]])
height,width,ch = img.shape
M = cv2.getAffineTransform(pts1,pts2)
dst = cv2.warpAffine(img,M,(width,height))
cv2.imwrite(os.path.join(outFolder,fn), dst) #Image d'écriture
cv2.imshow('Title',dst) #Afficher la dernière image
cv2.waitKey(5000) #Affichage de 5 secondes
article | Exemple d'utilisation | La description |
---|---|---|
Arguments de commande | import sys argv = sys.argv |
Placer les arguments de ligne de commande dans un tableau de caractères |
Opération de fichier | import os | |
Nombre de tableaux | argc = len(argv) | argc est le nombre de contenus dans le tableau argv |
Jugement de dossier | os.path.isdir(argv[1]) | argv[1]Vrai si est le chemin du dossier |
Entrée clé | key = raw_input('Hit Enter') | Entrée pour entrer une chaîne dans la clé |
Fin | quit() | プログラムをFinする |
Dossier parent | os.path.dirname(argv[1]) | Chemin argv[1]Sortez de la fin à la seconde |
Fichier/Nom de dossier | os.path.basename(argv[1]) | Chemin argv[1]Extraire le nom de |
Combinez des dossiers et des fichiers | os.path.join(parent,'out') | Nom de fichier dans le chemin du dossier parent'out'Relier |
Contrôle d'existence | os.path.exists(outFolder) | Vrai si outFolder existe |
Créer le dossier | os.mkdir(outFolder) | Créer un dossier outFolder |
Création de liste de fichiers | files = [f for f in os.listdir(inpFolder)] | Tous les noms de fichiers dans le dossier inpFolder se trouvent dans les fichiers du tableau |
Trier par nom de fichier | files.sort(key=os.path.basename) | 配列 files をTrier par nom de fichier |
En cas d'erreur | if img is None: | Utiliser est ou n'est pas lors de la comparaison avec Aucun |
Pour interruption | continue | Suspendre le traitement dans l'instruction For et passer à la suivante |
Enregistrer l'image | cv2.imwrite(os.path.join(outFolder,fn), img) | Enregistrez l'image img sous fn dans outFolder |
――L'outil créé cette fois ne calcule que le centre de gravité des marques en haut à gauche et en haut à droite de l'image, la forme est donc totalement hors de propos. Même s'il s'agit d'une croix comme une libellule, elle peut être utilisée pour l'alignement si toutes les images ont la même forme.
La plupart des informations OpenCV sur le net proviennent d'OpenCV 3, et dans OpenCV 2.4, une erreur peut se produire.
article | OpenCV 3 | OpenCV 2.4 |
---|---|---|
étiquetage | nLabels, labelImage = cv2.connectedComponents(bin) | Les composants connectés ne peuvent pas être utilisés |
Extraction de contour | image, contours, hierarchy = cv2.findContours( thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE) | contours, hierarchy = cv2.findContours( bin, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE) 2 sorties |
dessin | img = cv2.circle(img, center, radius,(0,255,0),2) | cv2.circle(img, center, radius, (0,255,0),2) 出力はなし、imgに直接dessinされる |
Recommended Posts