Essayez de créer un logiciel de capture aussi précis que possible avec python (1) https://qiita.com/akaiteto/items/b2119260d732bb189c87
En gros, le but Créer des logiciels comme Amarekoko avec python, Enregistrons avec une personnalisation élevée, Le but est.
La dernière fois, comment capturer des images avec python, Nous avons examiné l'utilisation de base de chaque partie de la méthode de capture sonore du système.
Cette fois, nous envisagerons d'améliorer la précision de la capture d'écran.
La dernière fois, j'ai entendu dire que le processus de capture d'écran était lent. La vitesse de traitement globale est aussi lente que 18 ips. S'il s'agit d'une vidéo, elle semble un peu raide. Personnellement, j'en veux plus de 28.
Dans l'ensemble du processus
1.Capture d'image
2.Convertir le format de couleur de l'image en RVB
Quand je pense à ces deux étapes Dans l'étude précédente, nous avons amélioré une étape.
Plus précisément, lorsque le traitement est mesuré à une résolution de 1920 x 1080, Auparavant (ImageGrab.grab) était de 26 ips, Avec l'introduction de win32, il est devenu fps42.
Ensuite, nous envisagerons d'améliorer le processus de conversion dans 2. Avant cela, je me demande s'il est nécessaire de poursuivre la vitesse en premier lieu.
Si vous n'avez pas besoin du temps réel Conservez les données sous forme de tableau ou de fichier jpg, Je pense qu'il n'y a pas de problème même si le processus de conversion est effectué plus tard.
Cependant, j'ose ici viser des performances en temps réel. Envisager d'enregistrer pendant une longue période, si vous continuez à conserver les données sans réfléchir La mémoire est susceptible d'être comprimée et il peut y avoir des avantages.
Alors, examinons la vitesse de traitement du processus de conversion.
ImageGrab.grab de Pillow utilisé la dernière fois produit des images au format BGR. Lors de l'enregistrement d'une vidéo, il doit s'agir d'une image RVB, donc La conversion OpenCV était absolument nécessaire, ce qui ralentissait la vitesse de traitement.
Et cette fois. La sortie d'image par l'api win32 est RGBA. La conversion en RVB est nécessaire pour enregistrer en tant que film.
La dernière fois que j'ai essayé d'accélérer la capture, Comparons maintenant la vitesse de capture et de conversion.
#ImageGrab traditionnelle.grab
parentTime = time.time()
for i in range(40):
img_cv = np.asarray(ImageGrab.grab())
img = cv2.cvtColor(img_cv, cv2.COLOR_BGR2RGB)
current = time.time()
diff = (current - parentTime)
print("fps:" + str(float(40)/diff))
#Amélioration win32+opencv
parentTime = time.time()
for i in range(40):
memdc.BitBlt((0, 0), (width, height), srcdc, (0, 0), win32con.SRCCOPY)
img = np.fromstring(bmp.GetBitmapBits(True), np.uint8).reshape(height, width, 4)
img = cv2.cvtColor(img, cv2.COLOR_RGBA2RGB)
current = time.time()
diff = (current - parentTime)
print("fps:" + str(float(40)/diff))
fps:17.665207097327016
fps:29.761997556556736
Oh, win32 est assez rapide même dans le processus de conversion d'opencv. Jetons un coup d'œil à cela une fois.
Nous n'avons pas fait beaucoup de progrès, mais écrivons le code jusqu'à présent. (C'est du code totalement inorganisé et problématique, alors ne l'utilisez pas)
import cv2
import numpy as np
from PIL import ImageGrab
import ctypes
import time
import pyaudio
import wave
import win32gui, win32ui, win32con, win32api
import warnings
warnings.simplefilter("ignore", DeprecationWarning)
hnd = win32gui.GetDesktopWindow()
width = 1920
height = 1080
windc = win32gui.GetWindowDC(hnd)
srcdc = win32ui.CreateDCFromHandle(windc)
memdc = srcdc.CreateCompatibleDC()
bmp = win32ui.CreateBitmap()
bmp.CreateCompatibleBitmap(srcdc, width, height)
memdc.SelectObject(bmp)
user32 = ctypes.windll.user32
capSize = (user32.GetSystemMetrics(0), user32.GetSystemMetrics(1))
print(capSize)
fourcc = cv2.VideoWriter_fourcc(*"DIVX")
writer = cv2.VideoWriter("test.mov", fourcc, 20, capSize)
count = 0
FirstFlag = True
WAVE_OUTPUT_FILENAME = "test.wav"
RECORD_SECONDS = 5
FORMAT = pyaudio.paInt16
CHANNELS = 1
RATE = 44100
CHUNK = 2 ** 11
audio = pyaudio.PyAudio()
stream = audio.open(format=FORMAT,
channels=CHANNELS,
rate=RATE,
input=True,
input_device_index=0,
frames_per_buffer=CHUNK)
frames = []
sTime = time.time()
count = 0
arrScreenShot = []
print ("start")
for i in range(0, int(RATE / CHUNK * RECORD_SECONDS)):
count+=1
if count == 30 :
current = time.time()
diff = (current - sTime)
print("fps:" + str(float(count)/diff))
sTime = time.time()
count = 0
#Capture d'image
# fps18
# img_cv = np.asarray(ImageGrab.grab())
# img = cv2.cvtColor(img_cv, cv2.COLOR_BGR2RGB)
# writer.write(img)
# fps29
memdc.BitBlt((0, 0), (width, height), srcdc, (0, 0), win32con.SRCCOPY)
img = np.fromstring(bmp.GetBitmapBits(True), np.uint8).reshape(height, width, 4)
img = cv2.cvtColor(img, cv2.COLOR_RGBA2RGB)
#Exportation vidéo
writer.write(img)
# #Capture de voix
# data = stream.read(CHUNK)
# frames.append(data)
writer.release()
stream.stop_stream()
stream.close()
audio.terminate()
waveFile = wave.open(WAVE_OUTPUT_FILENAME, 'wb')
waveFile.setnchannels(CHANNELS)
waveFile.setsampwidth(audio.get_sample_size(FORMAT))
waveFile.setframerate(RATE)
waveFile.writeframes(b''.join(frames))
waveFile.close()
print ("finish")
Le processus de capture audio est commenté. Si vous effectuez une capture d'écran et une capture audio en même temps, cela sera plus lent, donc J'ai l'intention de le réécrire en parallèle. (Celui du bas est réécrit)
Pour l'instant, terminons la capture d'écran.
fps:20.089757121871102
Quand je lance la source ci-dessus et mesure la vitesse ... c'est lent. La cause est en train d'exporter la vidéo.
writer.write(img)
L'existence de ce traitement effectué dans la boucle abaisse le fps d'environ 10. Cette fonction est une fonction d'opencv, une fonction d'une classe appelée cv2.VideoWriter. S'il existe une autre bibliothèque d'exportation vidéo rapide, je voudrais l'utiliser.
Les problèmes liés à l'utilisation de cette fonction sont les suivants, dans le cadre des spécifications que je comprends.
1. 1. Lent lors de l'écriture.
2. Vous ne pouvez enregistrer des vidéos qu'à une fréquence d'images fixe.
S'il existe une bibliothèque d'exportation vidéo autre qu'opencv, c'est également un problème que vous souhaitez vérifier.
Pour compléter le second, Quand j'ai capturé l'écran la dernière fois, le fps était de 14-19fps Le résultat était assez variable. Pour cette raison, le temps de sortie vidéo était désactivé.
Vous pouvez écrire dans un cadre fixe, en ignorant certains écarts de fps, Compte tenu de la combinaison finale de la vidéo et de l'audio, Je veux l'enregistrer exactement si je le peux.
Avec l'API iOS standard, lors de la sortie d'une vidéo Passer l'horodatage avec l'image du cadre J'ai l'impression d'avoir vu la vidéo enregistrée. Ce serait plus simple si vous pouviez enregistrer la vidéo à une fréquence d'images variable (VFR) comme celle-ci. De plus, c'est encore mieux si la vitesse d'écriture est rapide.
Découvrons s'il existe une autre bibliothèque de ce type.
・ ・ ・ ・ ・ ・ ・ ・ ・ ・
... Je l'ai recherché, mais je ne l'ai pas trouvé. Il ne semble pas y avoir d'autre choix que de faire un compromis.
1. 1. Lent lors de l'écriture.
Si possible, je voulais l'écrire immédiatement après l'avoir capturé, mais abandonnons. Essayez de conserver les images capturées dans un tableau et de les traiter après la prise de vue.
Si vous ne sortez pas les données conservées sous une forme ou une autre, Si vous enregistrez pendant une longue période, la mémoire semble perforer en un clin d'œil. Mais pour l'instant, concentrons-nous sur l'accomplissement de la fonction.
2. Vous ne pouvez enregistrer des vidéos qu'à une fréquence d'images fixe.
Avant la deuxième mesure, nous allons régler les problèmes actuels. À titre d'essai, représentons le temps de traitement par image lorsque chaque image est capturée.
... (omis) ...
arrScreenShot = []
imgList = []
graph = []
print ("start")
for i in range(0, int(RATE / CHUNK * RECORD_SECONDS)):
count+=1
current = time.time()
diff = (current - sTime)
graph.append(diff)
# print( str(i) + "Cadre:" + str(diff))
sTime = time.time()
count = 0
#Capture d'image
memdc.BitBlt((0, 0), (width, height), srcdc, (0, 0), win32con.SRCCOPY)
img = np.fromstring(bmp.GetBitmapBits(True), np.uint8).reshape(height, width, 4)
img = cv2.cvtColor(img, cv2.COLOR_RGBA2RGB)
imgList.append(img)
import statistics
import matplotlib.pyplot as plt
median = statistics.median(graph)
print("Médian:" + str(median))
x = list(range(len(graph)))
x3 = [median] * len(graph)
plt.plot(x, graph, color = "red")
plt.plot(x, x3, color = "blue")
plt.show()
... (omis) ...
L'axe horizontal correspond au nombre d'images d'images capturées. (107 feuilles au total) L'axe vertical représente le temps de traitement par image (s). Par exemple, s'il est de 30 ips, le temps de traitement par feuille sera de 0,03 s.
La ligne rouge est la ligne de tracé du temps de traitement mesuré, La ligne bleue est la valeur médiane de la ligne rouge. La valeur est 0,03341841697692871. En termes de fps, il est de 29,9.
En regardant le graphique, le temps de traitement de la partie montante Vous pouvez voir que c'est extrêmement lent. Cela semble être la principale cause d'avance rapide et d'avance lente.
J'ai pensé à ce qui suit comme contre-mesure.
1. 1. Écrire une vidéo à une fréquence d'images variable
2. Corrigé pour que les captures d'écran soient prises à intervalles réguliers
Écrire une vidéo dans un cadre fixe
3. 3. Ignorez les images d'image qui dépassent ou n'ont pas l'intervalle de temps spécifié et utilisent l'image précédente.
Écrivez une vidéo dans un cadre fixe.
4. N'enregistrez pas la première partie trop lente. Cible 20 images et plus.
J'ai cherché une bibliothèque qui pouvait être écrite en VFR, mais j'ai abandonné parce qu'elle était introuvable. 4 semble être le plus simple, mais avec cela, il y aura un écart dans le temps de lecture entre la vidéo et l'audio. Vous pouvez voir combien il est éteint, mais C'est un peu désagréable quand on pense à fusionner le son et la vidéo à la fin.
D'une manière ou d'une autre, 2 semble avoir le plus de sens. Allons-y avec la politique de 2.
Actuellement, le processus de capture d'écran est exécuté chaque fois que vous bouclez avec for. Il n'est pas censé être capturé à des intervalles de temps réguliers.
https://qiita.com/montblanc18/items/05715730d99d450fd0d3 Donc, en se référant à ce site, Essayez de sortir à intervalles de temps réguliers. Pour le moment, je vais essayer de l'exécuter tel quel sans penser à rien.
~ (Omis) ~
import time
import threading
def worker():
print(time.time())
memdc.BitBlt((0, 0), (width, height), srcdc, (0, 0), win32con.SRCCOPY)
img = np.fromstring(bmp.GetBitmapBits(True), np.uint8).reshape(height, width, 4)
img = cv2.cvtColor(img, cv2.COLOR_RGBA2RGB)
imgList.append(img)
def scheduler(interval, f, wait = True):
base_time = time.time()
next_time = 0
while True:
t = threading.Thread(target = f)
t.start()
if wait:
t.join()
next_time = ((base_time - time.time()) % interval) or interval
time.sleep(next_time)
scheduler(0.035, worker, False)
exit()
~ (Omis) ~
En conséquence, de nombreuses images étaient sorties sous forme de vidéos en toute sécurité, mais elles échouaient souvent.
La cause semble être qu'un objet a été référencé par plusieurs threads. Jusqu'à présent, une instance appelée memdc qui contrôle la capture d'écran Je l'utilise tout le temps.
En utilisant le traitement des threads, une instance est référencée. C'est foiré. Réécrivons.
~ (Omis) ~
frames = []
sTime = time.time()
count = 0
arrScreenShot = []
imgList = []
graph = []
print ("start")
import time
import threading
def worker(imgList):
print(time.time())
imgList.append(win32con.SRCCOPY)
def scheduler(interval,MAX_SECOND, f, wait = False):
base_time = time.time()
next_time = 0
while (time.time()-base_time) < MAX_SECOND:
t = threading.Thread(target = f,args=(imgList,))
t.start()
if wait:
t.join()
next_time = ((base_time - time.time()) % interval) or interval
time.sleep(next_time)
scheduler(1/fps, 40, worker, False)
for tmpSRCCOPY in imgList:
memdc.BitBlt((0, 0), (width, height), srcdc, (0, 0), tmpSRCCOPY)
img = np.fromstring(bmp.GetBitmapBits(True), np.uint8).reshape(height, width, 4)
img = cv2.cvtColor(img, cv2.COLOR_RGBA2RGB)
writer.write(img)
~ (Omis) ~
Wow putain de vu s ... mettez-le de côté La dernière fois, j'ai essayé de sortir une vidéo de 40 secondes, mais elle est revenue en 28 secondes. Que va-t-il se passer cette fois?
39 secondes. Oh, ça marche. Il n'y a pas de problème car c'est un problème de point décimal qu'il est inférieur à 1 seconde. C'est un succès. La capture d'écran est bonne une fois comme ça Vérifions la précision plus finement dans la seconde moitié.
À propos, avec la mise en œuvre jusqu'à présent Les points à craindre et les problèmes laissés en suspens sont les suivants.
● Parce que je viens de mettre les données d'image dans le tableau
Si vous enregistrez pendant longtemps, il tombera.
● J'ai introduit un win32 haute vitesse pour enregistrer en temps réel.
Après tout, notre hypothèse d'enregistrement en temps réel a disparu.
En fait ImageGrab.grab()C'est devenu un mécanisme qui n'est pas très différent de l'utilisation.
Organisez la source. Exécutez après avoir ajouté la bibliothèque suivante.
pip install moviepy
Cette bibliothèque est utilisée pour fusionner la vidéo et l'audio.
Et ce qui suit est la source qui enregistre et enregistre en même temps. Pour éviter les retards, j'ai décidé d'exécuter l'audio et la capture d'écran dans des threads séparés ... (Légèrement suspect)
capture.py
import cv2
import numpy as np
import pyaudio
import wave
import win32gui, win32ui, win32con, win32api
import time
import threading
#Contre-mesures d'erreur lors de la tentative de conversion en numpy avec l'api win32
import warnings
warnings.simplefilter("ignore", DeprecationWarning)
class VideoCap:
FrameList=[]
def __init__(self,width,height,fps,FileName):
capSize = (width, height)
fourcc = cv2.VideoWriter_fourcc('m', 'p', '4', 'v')
self.writer = cv2.VideoWriter(FileName, fourcc, fps, capSize)
hnd = win32gui.GetDesktopWindow()
windc = win32gui.GetWindowDC(hnd)
self.srcdc = win32ui.CreateDCFromHandle(windc)
def RecordStart(self,fps,rec_time):
def StoreFrameCap(FrameList):
# print(time.time())
FrameList.append(win32con.SRCCOPY)
def scheduler(interval, MAX_SECOND, f, wait=False):
base_time = time.time()
next_time = 0
while (time.time() - base_time) < MAX_SECOND:
t = threading.Thread(target=f, args=(self.FrameList,))
t.start()
if wait:
t.join()
next_time = ((base_time - time.time()) % interval) or interval
time.sleep(next_time)
scheduler(1 / fps,rec_time, StoreFrameCap, False)
def RecordFinish(self):
for tmpSRCCOPY in self.FrameList:
memdc = self.srcdc.CreateCompatibleDC()
bmp = win32ui.CreateBitmap()
bmp.CreateCompatibleBitmap(self.srcdc, width, height)
memdc.SelectObject(bmp)
memdc.BitBlt((0, 0), (width, height), self.srcdc, (0, 0), tmpSRCCOPY)
img = np.fromstring(bmp.GetBitmapBits(True), np.uint8).reshape(height, width, 4)
img = cv2.cvtColor(img, cv2.COLOR_RGBA2RGB)
self.writer.write(img)
memdc.DeleteDC()
win32gui.DeleteObject(bmp.GetHandle())
self.writer.release()
class AudioCap:
class default:
FORMAT = pyaudio.paInt16
CHANNELS = 1
RATE = 44100
CHUNK = 2 ** 11
frames=[]
audio = pyaudio.PyAudio()
def __init__(self,FORMAT=default.FORMAT,CHANNELS=default.CHANNELS,RATE=default.RATE,CHUNK=default.CHUNK):
self.FORMAT = FORMAT
self.CHANNELS = CHANNELS
self.RATE = RATE
self.CHUNK = CHUNK
def RecordStart(self,rec_time):
self.stream = self.audio.open(format=self.FORMAT,
channels=self.CHANNELS,
rate=self.RATE,
input=True,
input_device_index=0,
frames_per_buffer=self.CHUNK)
for i in range(0, int(self.RATE / self.CHUNK * rec_time)):
data = self.stream.read(self.CHUNK)
self.frames.append(data)
def RecordFinish(self):
self.stream.stop_stream()
self.stream.close()
self.audio.terminate()
def writeWAV(self,FileName):
waveFile = wave.open(FileName, 'wb')
waveFile.setnchannels(self.CHANNELS)
waveFile.setsampwidth(self.audio.get_sample_size(self.FORMAT))
waveFile.setframerate(self.RATE)
waveFile.writeframes(b''.join(self.frames))
waveFile.close()
#configuration de base
width = 1920 #Résolution horizontale
height = 1080 #Résolution verticale
fps = 30 # FPS
RECORD_SECONDS = 60 #Temps de lecture
VIDEO_OUTPUT_FILENAME = "test.mp4" #Fichier audio
AUDIO_OUTPUT_FILENAME = "test.wav" #Fichier vidéo
FINAL_VIDEO = "final_video.mp4" #Vidéo + fichier audio
#exemple
CapAuidio = AudioCap()
CapVideo = VideoCap(width,height,fps,VIDEO_OUTPUT_FILENAME)
#Pour les threads de traitement de la voix
def threadFuncAudio(obj):
obj.RecordStart(RECORD_SECONDS)
obj.RecordFinish()
obj.writeWAV(AUDIO_OUTPUT_FILENAME)
thrAudio = threading.Thread(target=threadFuncAudio(CapAuidio,))
#Début de la capture simultanée
thrAudio.start()
CapVideo.RecordStart(fps,RECORD_SECONDS)
CapVideo.RecordFinish()
#Vérification: quelle est la différence de temps de lecture?
from pydub import AudioSegment
sound = AudioSegment.from_file(AUDIO_OUTPUT_FILENAME, "wav")
time = sound.duration_seconds #Temps de lecture(Secondes)
print('Audio: temps de lecture:', time)
cap = cv2.VideoCapture(VIDEO_OUTPUT_FILENAME)
print('Vidéo: temps de lecture:',cap.get(cv2.CAP_PROP_FRAME_COUNT) / cap.get(cv2.CAP_PROP_FPS))
#Vérification: fusion vidéo / audio
from moviepy.editor import VideoFileClip
from moviepy.editor import AudioFileClip
my_clip = VideoFileClip(VIDEO_OUTPUT_FILENAME)
audio_background = AudioFileClip(AUDIO_OUTPUT_FILENAME)
final_clip = my_clip.set_audio(audio_background)
final_clip.write_videofile(FINAL_VIDEO, fps=fps)
Les résultats sont les suivants.
Audio: temps de lecture: 4.96907029478458
Vidéo: temps de lecture: 4.933333333333334
Moviepy - Building video final_video.mp4.
MoviePy - Writing audio in final_videoTEMP_MPY_wvf_snd.mp3
MoviePy - Done.
Moviepy - Writing video final_video.mp4
Moviepy - Done !
Moviepy - video ready final_video.mp4
Process finished with exit code 0
Il y a un intervalle de 0,03 seconde dans une vidéo de 5 secondes. Si la durée d'enregistrement est longue, l'écart deviendra-t-il important?
Audio: Durée de lecture: 59.953922902494334
Vidéo: Durée de lecture: 59.06666666666667
Un délai d'environ 1 seconde en 60 secondes ... Lorsque vous exportez une vidéo avec une image fixe Des retards sont susceptibles de se produire quoi qu'il arrive.
À partir de la prochaine fois ● Quelle est la différence entre la vidéo et l'audio ● Que se passera-t-il si vous enregistrez pendant une longue période? Considérer.
Continuer à la prochaine fois
Recommended Posts