[PYTHON] J'ai essayé d'extraire des caractères des sous-titres (OpenCV: API Google Cloud Vision)

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

J'ai essayé d'extraire des caractères des sous-titres (OpenCV: API Google Cloud Vision)
J'ai essayé d'extraire des caractères des sous-titres (OpenCV: édition tesseract-ocr)
J'ai essayé d'utiliser l'API Google Cloud Vision
J'ai essayé l'API Google Cloud Vision pour la première fois
J'ai essayé de collecter automatiquement des images érotiques de Twitter à l'aide de l'API Cloud Vision de GCP
Utiliser l'API Google Cloud Vision de Python
J'ai essayé "License OCR" avec l'API Google Vision
Lors de l'introduction de l'API Google Cloud Vision sur les rails, j'ai suivi la documentation.
J'ai essayé "Receipt OCR" avec l'API Google Vision
J'ai essayé d'extraire des fonctionnalités avec SIFT d'OpenCV
J'ai essayé de créer une API list.csv avec Python à partir de swagger.yaml
Essayez de juger des photos de plats à l'aide de l'API Google Cloud Vision
J'ai essayé d'obtenir diverses informations de l'API codeforces
J'ai touché l'API de Tesla
J'ai essayé d'extraire des noms de joueurs et de compétences d'articles sportifs
Exemple d'API Google Cloud Vision pour python
J'ai essayé d'utiliser l'API UnityCloudBuild de Python
J'ai essayé de toucher l'API COTOHA
J'ai créé une API Web
Comment utiliser l'API Cloud Vision de GCP
Python appelant l'API Google Cloud Vision depuis LINE BOT via AWS Lambda
Détectez les caractères japonais à partir d'images à l'aide de l'API Cloud Vision de Google avec Python
J'ai essayé d'accéder à l'API Qiita depuis le début
J'ai essayé de détecter rapidement un mouvement avec OpenCV
Comment appeler l'API Cloud à partir de GCP Cloud Functions
Comment utiliser l'API Google Cloud Translation
[IBM Cloud] J'ai essayé d'accéder à la table Db2 on Cloud à partir de Cloud Funtions (python)
J'ai essayé d'extraire le dessin au trait de l'image avec Deep Learning
J'ai essayé d'extraire diverses informations du PC distant de Python par la bibliothèque WMI
J'ai essayé de fonctionner à partir de Postman en utilisant Cisco Guest Shell comme serveur API
J'ai essayé de détecter l'iris à partir de l'image de la caméra
Extraire des caractères d'images à l'aide de l'API de reconnaissance de caractères de docomo
J'ai essayé de créer un pipeline ML avec Cloud Composer
J'ai essayé de devenir un Ann Man en utilisant OpenCV
J'ai essayé de découvrir notre obscurité avec l'API Chatwork
[Deep Learning from scratch] J'ai essayé d'expliquer le décrochage
J'ai essayé d'accéder aux feuilles de calcul Google en utilisant Python
J'ai essayé de déboguer.
[Première API COTOHA] J'ai essayé de résumer l'ancienne histoire
J'ai essayé Google Sign-In avec Spring Boot + Spring Security REST API
J'ai créé un outil pour créer un nuage de mots à partir de wikipedia
J'ai essayé de reconnaître le visage de la vidéo (OpenCV: version python)
Comment masquer la clé API Google Maps du HTML
J'ai essayé de changer le script python de 2.7.11 à 3.6.0 sur Windows10
J'ai essayé de traduire de force des sous-titres anglais en japonais avec Udemy
J'ai essayé d'implémenter Perceptron Part 1 [Deep Learning from scratch]
J'ai essayé d'obtenir rapidement des données d'AS / 400 en utilisant pypyodbc
J'ai essayé d'apprendre PredNet
J'ai essayé d'organiser SVM.
J'ai essayé d'implémenter PCANet
J'ai essayé de réintroduire Linux
J'ai essayé de présenter Pylint
J'ai essayé de résumer SparseMatrix
jupyter je l'ai touché
J'ai essayé d'implémenter StarGAN (1)
Jusqu'à ce que vous essayiez l'API Google Cloud Vision (détection d'images dangereuses)
J'ai essayé de faire la reconnaissance de caractères manuscrits de Kana Partie 1/3 D'abord à partir de MNIST