[PYTHON] J'ai essayé d'accélérer la création vidéo en traitant en parallèle

lent! !! !! J'ai vraiment pensé

J'ai essayé d'extraire des caractères des sous-titres (OpenCV: édition tesseract-ocr) À la suite du traitement d'image avec vidéo, 810 secondes (environ 13,5 minutes) Je l'ai perdu. Ici, j'ai essayé d'améliorer la vitesse de création de vidéos en refactorisant.

Méthode 1 PIL <-> OpenCV

Lorsque j'ai essayé de transférer les mêmes données d'image de PIL vers OpenCV et de les traiter, je les ai temporairement enregistrées dans un fichier, puis je les lisais, mais après un examen plus approfondi, il semble que cela puisse être fait simplement en convertissant le type de numpy. avec ça 810 secondes (environ 13,5 minutes) -> 450 secondes (environ 7,5 minutes) J'ai pu le réduire.

Avant correction

def createTextImage(src, sentence, px, py, color=(8,8,8), fsize=28):

    #Enregistrer l'image
    tmp_path = "src_temp.png "
    cv2.imwrite(tmp_path, src)

    #Stocker dans l'objet PIL
    img = Image.open(tmp_path)
    draw = ImageDraw.Draw(img)

    #Écrire du texte sur l'image avec PIL
    font = ImageFont.truetype("./IPAfont00303/ipag.ttf", fsize)
    draw.text((px, py), sentence, fill=color, font=font)
    img.save(tmp_path)

    #Stocker dans openCV
    return cv2.imread(tmp_path)

modifié

opencv(BGR) -> PIL(RGB)

cvimg = cv2.imread("sample.png ")
rgbImg = cv2.cvtColor(cvimg, cv2.COLOR_BGR2RGB)
pilImg = Image.fromarray(rgbImg)

------------------

PIL(RGB) -> opencv(BGR)
cvImg = np.array(pilImg, dtype=np.uint8)
dst = cv2.cvtColor(cvImg, cv2.COLOR_RGB2BGR)

Méthode 2 Traitement parallèle

J'avais l'habitude de traiter la lecture, le traitement et l'écriture de vidéo de manière séquentielle pour chaque image, mais j'ai écrit un script dans le flux suivant pour ne paralléliser que le traitement.

Le traitement parallèle utilise joblib. À la source de rappel, vous pouvez écrire sur presque une ligne en utilisant pleinement le traitement d'inclusion. n_jobs = 16 est le nombre de processus.


from joblib import Parallel, delayed

def main_image_process(src, tool):
    
    #Faisons un peu de traitement d'image ici
        #Prétraitement
        gray_frame = pre_process(src.content)
        #Extraction de caractères
        #Création de sous-titres

...
Parallel(n_jobs=16)( [delayed(main_image_process)(f, tool) for f in frames] )
...

développement de

import sys

import cv2
import io
import os
import numpy as np

import pyocr
import pyocr.builders

from PIL import Image, ImageDraw, ImageFont

from collections import namedtuple
from joblib import Parallel, delayed

import time

MovieFrame = namedtuple("MovieFrame", ["id", "content", "timestamp"])

telop_height = 50
cap_width = 1
cap_height = 1


def pre_process(src):
	kernel = np.ones((3,3),np.uint8)
	gray = cv2.cvtColor(src, cv2.COLOR_BGR2GRAY)
        
    #Binarisation
	o_ret, o_dst = cv2.threshold(gray, 0, 255, cv2.THRESH_OTSU)
    #Réduction d'ouverture->Expansion
	dst = cv2.morphologyEx(o_dst, cv2.MORPH_OPEN, kernel)
    #Inverser
	dst = cv2.bitwise_not(dst)
    # channel 1 ->Convertir en 3
	dst = cv2.cvtColor(dst, cv2.COLOR_GRAY2BGR)
	return dst

#Extraction de caractères
def extractTelopText(src, tool):

	rgbImg = cv2.cvtColor(src, cv2.COLOR_BGR2RGB)
	dst = tool.image_to_string(
		Image.fromarray(rgbImg),
		lang='jpn',
		builder=pyocr.builders.WordBoxBuilder(tesseract_layout=6)
	)

	sentence = []
	for item in dst:
		sentence.append(item.content)

	return "".join(sentence)

#Créer des sous-titres vides
def createFooterTelop(src):

	telop = np.zeros((telop_height, cap_width, 3), np.uint8)
	telop[:] = tuple((128,128,128))

	images = [src, telop]
	dst = np.concatenate(images, axis=0)
	return dst

#Traitement parallèle
def main_image_process(src, tool):

	#Traité pour faciliter la reconnaissance des caractères
	gray_frame = pre_process(src.content)

	#Coupez uniquement là où telop est susceptible d'apparaître
	roi = gray_frame[435:600, :]

	#Extraire le texte
	text = extractTelopText(roi, tool)
        
	#Création de sous-titres
	dst = createFooterTelop(src.content)

	#Ajouter du texte à l'image
	dst = addJapaneseTelop(dst, text, 20, cap_height + telop_height - 30)
	dst = addASCIITelop(dst, str(src.timestamp) + "[sec]", cap_width - 250, cap_height + telop_height - 10, color=(0,255,0))

        #Stocker dans nametuble
	return MovieFrame(src.id, dst, src.timestamp)

#Ajouter des personnages(Alphabet uniquement)
def addASCIITelop(src, sentence, px, py, color=(8,8,8), fsize=28):

	cv2.putText(src, sentence, 
						(px, py), 
						cv2.FONT_HERSHEY_SIMPLEX, 
						1, 
						color, 
						2, 
						cv2.LINE_AA)	
	return src

#Ajouter des personnages(Japonais)
def addJapaneseTelop(src, sentence, px, py, color=(8,8,8), fsize=28):

	rgbImg = cv2.cvtColor(src, cv2.COLOR_BGR2RGB)

	#openCV -> PIL
	canvas = Image.fromarray(rgbImg).copy()

	draw = ImageDraw.Draw(canvas)
	font = ImageFont.truetype("./IPAfont00303/ipag.ttf", fsize)

	#Ajouter des personnages
	draw.text((px, py), sentence, fill=color, font=font)

	#PIL -> openCV	
	dst = cv2.cvtColor(np.array(canvas, dtype=np.uint8), cv2.COLOR_RGB2BGR)

	return dst

if __name__ == '__main__':

	tools = pyocr.get_available_tools()
	if len(tools) == 0:
		print("No OCR tool found")
		sys.exit(1)

	tool = tools[0]

	cap = cv2.VideoCapture('one_minutes.mp4')

	cap_width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
	cap_height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
	fps = cap.get(cv2.CAP_PROP_FPS)

	telop_height = 50

	fourcc = cv2.VideoWriter_fourcc('m','p','4','v')
	writer = cv2.VideoWriter('extract_telop_async.mp4',fourcc, fps, (cap_width, cap_height + telop_height))

	frames = []

	start = time.time()
	idx = 0

	#Chargement de la vidéo
	try :
		while True:
			if not cap.isOpened():
				break

			if cv2.waitKey(1) & 0xFF == ord('q'):
				break

			ret, frame = cap.read()

			if frame is None:
				break

			frames.append(MovieFrame(idx,frame, round(idx/fps, 4)) )
			idx += 1

	except cv2.error as e:
		print(e)

	cap.release()
	print("read movie file")
	
	#Traitement parallèle(Votre interlocuteur)
	r = Parallel(n_jobs=16)( [delayed(main_image_process)(f, tool) for f in frames] )

	#sort
	sorted_out = sorted(r, key=lambda x: x.id)

	#Écriture vidéo
	try :
		for item in sorted_out:
			writer.write(item.content)

	except cv2.error as e:
		print(e)


	writer.release()
	
	print("write movie file")
	print("Done!!! {}[sec]".format(round(time.time() - start,4)))

Autres points

nametuples

Je voulais les traiter tous en même temps, j'ai donc décidé de créer un objet simple et j'ai essayé d'utiliser des «tuples nommés».

MovieFrame = namedtuple("MovieFrame", ["id", "content", "timestamp"])

Avec cela, vous pouvez définir src.id et src.content ainsi que la propriété (getter), afin de raccourcir le code.

Trier

Les trames sont agrégées après un traitement parallèle, mais l'ordre peut être légèrement en désordre. Par conséquent, l'ordre d'affichage (id) est trié.

sorted_out = sorted(r, key=lambda x: x.id)

résultat

Quel est le temps de traitement? .. .. 267,7924 secondes (environ 4,5 minutes) Il a été abrégé en. : tada:

C'est une grosse différence par rapport aux 810 premières secondes.

temps de traitement[sec] temps de traitement[min]
Avant correction 810 13.5
Méthode 1(PIL <-> OpenCV) 450 7.5
Méthode 1(PIL <-> OpenCV) +Méthode 2(Traitement parallèle) 268 4.46

en conclusion

Comme il est difficile d'afficher un message d'erreur, il est difficile de démarrer le traitement parallèle depuis le début à moins de réduire le nombre de processus à un et d'éliminer le bogue. Dans le processus d'ajout de caractères en japonais, si vous ne dupliquez pas les données d'image comme canvas = Image.fromarray (rgbImg) .copy (), vous pouvez effectuer un traitement séquentiel, mais une erreur se produit dans le traitement parallèle. Étant donné que le traitement parallèle ne peut être effectué sans préparer un peu d'environnement, le traitement est rapide, mais il est inévitable qu'il soit gênant. : visage fatigué:

Lien référencé

Recommended Posts

J'ai essayé d'accélérer la création vidéo en traitant en parallèle
J'ai essayé de résumer moi-même le flux général jusqu'à la création de services.
Mongodb Shortest Introduction (3) J'ai essayé d'accélérer même des millions
J'ai essayé d'obtenir une image en grattant
J'ai essayé de classer les boules de dragon par adaline
J'ai essayé de déboguer.
J'ai essayé de comparer la vitesse de traitement avec dplyr de R et pandas de Python
Ce que j'ai fait pour accélérer la tâche de recherche de chaînes
J'ai essayé d'apprendre PredNet
J'ai essayé d'implémenter la détection d'anomalies par apprentissage de structure clairsemée
J'ai essayé d'organiser SVM.
[Introduction à la simulation] J'ai essayé de jouer en simulant une infection corona ♬
J'ai essayé d'implémenter PCANet
[Django] J'ai essayé d'implémenter des restrictions d'accès par héritage de classe.
Ubuntu a explosé lorsque j'ai essayé de changer mon nom d'utilisateur
[Introduction à Pandas] J'ai essayé d'augmenter les données d'échange par interpolation de données ♬
J'ai essayé de classer MNIST par GNN (avec PyTorch géométrique)
J'ai essayé de réintroduire Linux
J'ai essayé de présenter Pylint
J'ai essayé 100 traitements linguistiques Knock 2020
J'ai essayé de résumer SparseMatrix
jupyter je l'ai touché
J'ai essayé d'implémenter StarGAN (1)
J'ai essayé de créer un rapport Power BI en traitant les journaux CSV / JSON à l'aide d'Azure Databricks
J'ai essayé de créer un pointage de crédit simple avec régression logistique.
[Introduction à la simulation] J'ai essayé de jouer en simulant une infection corona ♬ Partie 2
J'ai essayé de résoudre 100 traitements linguistiques Knock 2020 version [Chapitre 2: Commandes UNIX 10 à 14]
J'ai essayé de visualiser l'ensemble de données de préférence de boisson par décomposition tenseur.
Accélérez le traitement en utilisant le vecteur C ++, unordered_map en interne en Cython
J'ai essayé d'afficher le temps de lecture de la vidéo (OpenCV: version Python)
J'ai essayé d'implémenter la classification des phrases par Self Attention avec PyTorch
J'ai essayé de résumer les commandes utilisées par les ingénieurs débutants aujourd'hui
J'ai fait apprendre à RNN la vague de péché et j'ai essayé de prédire
J'ai essayé de visualiser Boeing de la performance du violon par estimation de pose
J'ai essayé de couper une image fixe de la vidéo
J'ai essayé de résoudre 100 traitements linguistiques Knock 2020 version [Chapitre 2: Commandes UNIX 15-19]
J'ai essayé de résoudre le problème de planification des équipes par diverses méthodes
Comparaison de la vitesse de traitement de la pile par langue
J'ai essayé d'implémenter Deep VQE
J'ai essayé de créer l'API Quip
Numba pour accélérer en Python
J'ai essayé de toucher Python (installation)
J'ai essayé 100 traitements linguistiques Knock 2020: Chapitre 3
J'ai essayé de mettre en place une validation contradictoire
J'ai essayé d'expliquer l'ensemble de données de Pytorch
J'ai essayé l'authentification vocale Watson (Speech to Text)
J'ai touché l'API de Tesla
Projet Euler 4 Tentative d'accélération
Comment accélérer les calculs Python
J'ai essayé de m'organiser à propos de MCMC.
J'ai essayé 100 traitements linguistiques Knock 2020: Chapitre 1
J'ai essayé d'implémenter Realness GAN
[DRF] Extrait pour accélérer PrimaryKeyRelatedField
J'ai essayé de déplacer le ballon
J'ai essayé 100 traitements linguistiques Knock 2020: Chapitre 2
J'ai essayé 100 traitements linguistiques Knock 2020: Chapitre 4
J'ai essayé le traitement asynchrone en utilisant asyncio
J'ai essayé d'estimer la section.
J'ai essayé d'obtenir les résultats de Hachinai en utilisant le traitement d'image