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.
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)
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] )
...
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)))
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.
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)
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 |
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é: