Motive Ich habe versucht, Zeichen aus Untertiteln zu extrahieren (OpenCV: tesseract-ocr edition) Google CloudVision API Ich möchte vision /) verwenden, um die Zeichen in den Untertiteln zu extrahieren.
Method Um die Google Cloud Vision-API verwenden zu können, müssen Sie zunächst ein Konto in der Google Cloud-Konsole registrieren und einen API-Schlüssel erhalten. Informationen zur Methode finden Sie unter Zusammenfassung der Verwendung der Cloud Vision-API (mit Beispielcode).
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
	#Bild wird geladen
	img_file_path = sys.argv[1]
	mat = cv2.imread(img_file_path)
	#Nur Untertitel-Anzeigeteil
	roi = mat[435:600, :]
	# openCV -> base64
	result, dst_data = cv2.imencode('.png', roi)
	img_content = base64.b64encode(dst_data)
	#Anforderungshauptteil erstellen
	req_body = json.dumps({
	    'requests': [{
	        'image': {
	            'content': img_content.decode('utf-8')
	        },
	        'features': [{
				'type': 'DOCUMENT_TEXT_DETECTION'
	        }]
	    }]
	})
	#Ausstellung anfordern
	res = requests.post(api_url, data=req_body)
	#Holen Sie sich Bildinformationen von der Anfrage
	res_json = res.json()['responses']
	if 0 < len(res_json[0]):
		textobj = res_json[0]['textAnnotations'][0]
		print("".join(textobj["description"].strip().split("\n")))
Wenn ich eine API anfordere, entwerfe ich json, indem ich das Bild in base64 konvertiere.
src = cv2.imread("image_path")
result, dst_data = cv2.imencode('.png', src)
img_content = base64.b64encode(dst_data)
Sie können von openCV mit konvertieren.
Darüber hinaus ist es ausreichend, Zeichen zu extrahieren, während die API so wie sie ist sequentiell verarbeitet wird. Die Verarbeitungsgeschwindigkeit erhöht sich jedoch, wenn die parallele Verarbeitung mit "asyncio" durchgeführt wird. Der Grund für die Verwendung von "asyncio" ist, dass es stabiler als "multiprocessing.Pool" ist, da es die API-Antwortverarbeitung innerhalb des Collouts blockiert.
async def main_image_process(src):
	#Wird verarbeitet, um die Zeichenerkennung zu vereinfachen
	gray_frame = pre_process(src.content)
	#Schneiden Sie nur dort, wo Telop wahrscheinlich erscheint
	roi = gray_frame[435:600, :]
	#Text extrahieren
	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()
Es scheint, dass es einfacher mit Python 3.7 oder höher geschrieben werden kann, aber es ist noch nicht stabil und das Standardpaket von centOS war Python 3.6, also schreibe ich es basierend auf 3.6.
Development
Der ganze 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)
	#Anforderungshauptteil erstellen
	req_body = json.dumps({
	    'requests': [{
	        'image': {
	            'content': img_content.decode('utf-8')
	        },
	        'features': [{
				'type': 'DOCUMENT_TEXT_DETECTION'
	        }]
	    }]
	})
	#Ausstellung anfordern
	res = requests.post(api_url, data=req_body)
	#Holen Sie sich Bildinformationen von der Anfrage
	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):
	#Wird verarbeitet, um die Zeichenerkennung zu vereinfachen
	gray_frame = pre_process(src.content)
	#Schneiden Sie nur dort, wo Telop wahrscheinlich erscheint
	roi = gray_frame[435:600, :]
	#Text extrahieren
	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
 Verarbeitungszeit: 450 Sekunden ≒ 7,5 Minuten
Verarbeitungszeit: 450 Sekunden ≒ 7,5 Minuten
GoogleCloudVisionAPI

Verarbeitungszeit: 1315 Sekunden ≒ 21 Minuten
Die asynchrone Verarbeitung bei Verwendung einer externen API hat eine Weile gedauert. Sie können jedoch feststellen, dass die Google CloudVision-API für die OCR-Genauigkeit besser geeignet ist.
Future Das nächste Mal denke ich darüber nach, das Video von Ich habe das Objekt mithilfe der Bildreparatur (inpaint) (OpenCV: C ++) zu bearbeiten. Ich denke darüber nach, es C ++ basierend auf dem von mir verwendeten Code zu machen. Wenn es darum geht
Reference
Recommended Posts