Motive J'ai essayé d'extraire des caractères des sous-titres (OpenCV: édition tesseract-ocr) API Google CloudVision Je voudrais utiliser vision /) pour extraire les caractères des sous-titres.
Method Tout d'abord, pour utiliser l'API Google Cloud Vision, vous devez créer un compte sur la console Google Cloud et obtenir une clé API. Pour la méthode, reportez-vous à Résumé de l'utilisation de l'API Cloud Vision (avec exemple de code).
import requests
import json
import base64
import cv2
import sys
if __name__ == "__main__":
KEY = "--- your api key ---"
url = 'https://vision.googleapis.com/v1/images:annotate?key='
api_url = url + KEY
#Chargement d'image
img_file_path = sys.argv[1]
mat = cv2.imread(img_file_path)
#Partie d'affichage des sous-titres uniquement
roi = mat[435:600, :]
# openCV -> base64
result, dst_data = cv2.imencode('.png', roi)
img_content = base64.b64encode(dst_data)
#Créer le corps de la demande
req_body = json.dumps({
'requests': [{
'image': {
'content': img_content.decode('utf-8')
},
'features': [{
'type': 'DOCUMENT_TEXT_DETECTION'
}]
}]
})
#Demande d'émission
res = requests.post(api_url, data=req_body)
#Obtenir des informations sur l'image à partir de la demande
res_json = res.json()['responses']
if 0 < len(res_json[0]):
textobj = res_json[0]['textAnnotations'][0]
print("".join(textobj["description"].strip().split("\n")))
Lors de la demande d'API, je conçois json en convertissant l'image en base64.
src = cv2.imread("image_path")
result, dst_data = cv2.imencode('.png', src)
img_content = base64.b64encode(dst_data)
Vous pouvez convertir depuis openCV avec.
De plus, il suffit d'extraire les caractères en appelant l'API avec un traitement séquentiel tel quel, mais si le traitement parallèle est effectué en utilisant ʻasyncio, la vitesse de traitement augmentera. La raison d'utiliser ʻasyncio
est qu'il est plus stable que multiprocessing.Pool
car il bloque le traitement des réponses API dans le collout.
async def main_image_process(src):
#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 = await extractTelopText(roi)
await asyncio.sleep(2)
dst = await createFooterTelop(src.content)
dst = await addJapaneseTelop(dst, text, 20, cap_height + telop_height - 30)
dst = await addASCIITelop(dst, str(src.timestamp) + "[sec]", cap_width - 250, cap_height + telop_height - 10, color=(0,255,0))
return MovieFrame(src.id, dst, src.timestamp)
if __name__ == "__main__":
r = []
loop = asyncio.get_event_loop()
try:
r = loop.run_until_complete(
asyncio.gather(*[main_image_process(f) for f in frames])
)
finally:
loop.close()
Il semble qu'il puisse être écrit plus simplement avec Python 3.7 ou version ultérieure, mais il n'est pas encore stable et le package par défaut de centOS était Python 3.6, donc je l'écris basé sur 3.6.
Development
L'ensemble du code.
import sys
import cv2
import io
import os
import numpy as np
import base64
import json
import requests
import asyncio
from PIL import Image, ImageDraw, ImageFont
from collections import namedtuple
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)
o_ret, o_dst = cv2.threshold(gray, 0, 255, cv2.THRESH_OTSU)
dst = cv2.morphologyEx(o_dst, cv2.MORPH_OPEN, kernel)
dst = cv2.bitwise_not(dst)
dst = cv2.cvtColor(dst, cv2.COLOR_GRAY2BGR)
return dst
async def extractTelopText(src):
KEY = "--- your api key ---"
url = 'https://vision.googleapis.com/v1/images:annotate?key='
api_url = url + KEY
message = ""
result, dst_data = cv2.imencode('.png', src)
img_content = base64.b64encode(dst_data)
#Créer le corps de la demande
req_body = json.dumps({
'requests': [{
'image': {
'content': img_content.decode('utf-8')
},
'features': [{
'type': 'DOCUMENT_TEXT_DETECTION'
}]
}]
})
#Demande d'émission
res = requests.post(api_url, data=req_body)
#Obtenir des informations sur l'image à partir de la demande
res_json = res.json()['responses']
if 0 < len(res_json[0]):
textobj = res_json[0]["textAnnotations"][0]
message = "".join(textobj["description"].strip().split("\n"))
return message
async 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
async def main_image_process(src):
#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 = await extractTelopText(roi)
await asyncio.sleep(2)
dst = await createFooterTelop(src.content)
dst = await addJapaneseTelop(dst, text, 20, cap_height + telop_height - 30)
dst = await addASCIITelop(dst, str(src.timestamp) + "[sec]", cap_width - 250, cap_height + telop_height - 10, color=(0,255,0))
return MovieFrame(src.id, dst, src.timestamp)
async 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
async def addJapaneseTelop(src, sentence, px, py, color=(8,8,8), fsize=28):
rgbImg = cv2.cvtColor(src, cv2.COLOR_BGR2RGB)
canvas = Image.fromarray(rgbImg).copy()
draw = ImageDraw.Draw(canvas)
font = ImageFont.truetype("./IPAfont00303/ipag.ttf", fsize)
draw.text((px, py), sentence, fill=color, font=font)
dst = cv2.cvtColor(np.array(canvas, dtype=np.uint8), cv2.COLOR_RGB2BGR)
return dst
if __name__ == '__main__':
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
#read frame
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")
#process
r = []
loop = asyncio.get_event_loop()
try:
r = loop.run_until_complete(
asyncio.gather(*[main_image_process(f) for f in frames])
)
finally:
loop.close()
#sort
sorted_out = sorted(r, key=lambda x: x.id)
#write frame
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)))
Result
tesseract-ocr Temps de traitement: 450sec ≒ 7,5 minutes
GoogleCloudVisionAPI
Temps de traitement: 1315sec ≒ 21 minutes
Le traitement asynchrone a pris un certain temps tout en utilisant une API externe, mais vous pouvez voir que l'API Google CloudVision est meilleure pour la précision de l'OCR.
Future La prochaine fois, je pense éditer la vidéo de J'ai supprimé l'objet en utilisant la réparation d'image (inpaint) (OpenCV: C ++). Je pense en faire du C ++ en fonction du code que j'ai utilisé. Quand cela vient à --Comment utiliser curl --thread (Y a-t-il une bibliothèque équivalente à ʻasyncio` en premier lieu?) Je dois réfléchir à la manière de gérer cela. N'y a-t-il pas d'autre choix que d'utiliser boost? : visage fatigué:
Reference
Recommended Posts