Alignement d'images numérisées de papier vidéo animé à l'aide d'OpenCV et de Python

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 peascan.gif

Le problème que vous souhaitez résoudre

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.

1. 1. Installation d'OpenCV et de Python

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.

2. Charger et afficher des images

2-1. Créer un programme Python

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 print 「,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

2-2. Exécution du programme

  1. Ouvrez le dossier contenant "test.py"
  2. Cliquez sur le bouton droit là où il n'y a rien> cliquez sur "Ouvrir la console IPython ici"
    Cela ouvrira le terminal pour Python.
  3. Tapez python test.py et appuyez sur Entrée
  4. Si le fichier image est correctement spécifié, l'image sera affichée pendant 5 secondes. Si "Aucun" ou "Erreur d'attribut" s'affiche, (1) le japonais n'est pas inclus dans l'emplacement ou le nom de fichier de l'image, et (2) Le délimiteur de chemin est "" ("/". (Doit être), (3) Assurez-vous que les noms de fichiers sont corrects.

3. 3. Trouvez le centre de gravité en binarisant la marque (trou) en haut à gauche de l'image

Aprè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

4. Trouvez le centre de gravité des marques (trous) en haut à gauche et en haut à droite et transformez-les pour qu'ils correspondent au centre de gravité de l'image de référence.

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).

5. Lisez les images dans le dossier et exportez les images alignées dans un autre dossier

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.

6. Notez la différence entre OpenCV 3 et 2.4

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される

Article associé

  1. Faisons une animation avec un logiciel gratuit (9VAe Kyubee)
  1. Comment faire une animation d'ordre d'écriture
  2. 9VAe Kyubee: Comment faire une longue animation

Recommended Posts

Alignement d'images numérisées de papier vidéo animé à l'aide d'OpenCV et de Python
Essayez de projeter la conversion d'image en utilisant OpenCV avec Python
Construction d'environnement de python et opencv
Capturer des images avec Pupil, python et OpenCV
Introduction facile de la série python3 et d'OpenCV3
Obtenez et estimez la forme de la tête en utilisant Dlib et OpenCV avec python
Estimation de l'orientation de la tête avec Python et OpenCV + dlib
J'ai essayé la détection d'objets en utilisant Python et OpenCV
Téléchargement anonyme d'images à l'aide de l'API Imgur (à l'aide de Python)
Conversion en ondelettes d'images avec PyWavelets et OpenCV
Collecte et automatisation d'images érotiques à l'aide du deep learning
J'ai essayé la "conversion de morphologie" de l'image avec Python + OpenCV
Application de Python: Nettoyage des données Partie 3: Utilisation d'OpenCV et prétraitement des données d'image
Vérification et mise en œuvre de la méthode de reconstruction vidéo en utilisant GRU et Autoencoder
Convertir une vidéo en noir et blanc avec ffmpeg + python + opencv
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)
Enregistrer des images à l'aide de requêtes python3
[Python] Utilisation d'OpenCV avec Python (basique)
python: principes de base de l'utilisation de scikit-learn ①
Accélérer le chargement des images Python
Installation source et installation de Python
Utiliser OpenCV avec Python @Mac
[Traitement d'image] Poo-san est nu par détection de bord en utilisant Python et OpenCV!
Créez et essayez un environnement OpenCV et Python en quelques minutes à l'aide de Docker
Comment enregistrer une partie d'une longue vidéo en utilisant OpenCV
Obtenez et définissez la valeur du menu déroulant en utilisant Python et Selenium
[Python] Implémentation de la méthode Nelder – Mead et sauvegarde des images GIF par matplotlib
Extraction d'objets dans l'image par correspondance de modèles en utilisant OpenCV avec Python
python> Il n'y a pas de #ifdef> Une autre solution> __debug__ Avantages et inconvénients de l'utilisation
[Python] Essayez de reconnaître les caractères des images avec OpenCV et pyocr
Comment mettre OpenCV dans Raspberry Pi et collecter facilement des images des résultats de détection de visage avec Python
Briller la vie avec Python et OpenCV
Manipulation des pixels d'image en Python
Tour verticale de Pise utilisant OpenCV ~
L'histoire de Python et l'histoire de NaN
Capture d'image de Firefox en utilisant Python
Avantages et exemples d'utilisation de Rabbit Mq
[Python] Utilisation d'OpenCV avec Python (filtrage d'image)
Installer SciPy et matplotlib (Python)
Jugement de l'image rétroéclairée avec OpenCV
Authentification à l'aide de l'authentification des utilisateurs tweepy et de l'authentification d'application (Python)
[Python] Utilisation d'OpenCV avec Python (détection des bords)
Envoyer des messages et des images à l'aide de LineNotify
Suppression de la brume à l'aide de Python detailEnhanceFilter
J'ai essayé d'utiliser GrabCut d'OpenCV
Générer des images de texte multilingues à l'aide de Python
Ceci et cela des propriétés python
Clustering et visualisation à l'aide de Python et CytoScape
Implémentation des notifications de bureau à l'aide de Python
Charger une image gif avec Python + OpenCV
Coexistence de Python2 et 3 avec CircleCI (1.0)
Résumé des index et des tranches Python
Etude de base d'OpenCV avec Python
Réputation des livres Python et des livres de référence
[OpenCV; Python] Résumé de la fonction findcontours
Mettez OpenCV dans OS X avec Homebrew et vidéo d'entrée / sortie avec python
Créer une lecture de feuille de notes avec Python OpenCV (Conseils pour bien lire)
Développement et déploiement de l'API REST en Python à l'aide de Falcon Web Framework