Si vous souhaitez exécuter OpenCV en Python et afficher les résultats de l'analyse et du traitement, il est plus simple d'utiliser la méthode cv2.imshow ()
.
Mais si vous voulez faire plus que simplement afficher, la fenêtre OpenCV ne suffit pas.
Une façon de résoudre ce problème est d'utiliser Pygame, le framework Python GUI [^ Pygame Reasons to Choose].
Dans cet article, j'écrirai sur la façon de convertir des images OpenCV en images pour Pygame.
[Raison du choix de ^ Pygame]: La raison de Pygame est qu'il peut également être dessiné avec Raspbian Lite (très important). La plupart des frameworks d'interface graphique (y compris la fonctionnalité de fenêtre d'OpenCV) reposent sur le système X Window et ne peuvent pas être visualisés sur Raspbian Lite sans interface graphique intégrée. Cependant, Pygame a également la possibilité de dessiner en utilisant SDL, donc il fonctionne également avec Raspbian Lite.
#Après avoir écrit le système d'exploitation, connectez-vous avec ssh
#Mettre à jour la liste des packages
sudo apt update
#Installez OpenCV pour Python3
sudo apt install python3-opencv
#Installez Pygame pour Python 3
sudo apt install python3-pygame
#Vérifier la version de Python
python3 --version
#Vérifiez la version d'OpenCV
python3 -c 'import cv2; print(cv2.__version__)'
#Vérifiez la version de Pygame
python3 -c 'import pygame; print(pygame.version.ver)'
Selon "Dessine une image créée avec opencv avec pygame. --BlankTar", c'est comme suit. Il semble qu'il puisse être converti par la méthode.
opencv_image = opencv_image[:,:,::-1] #Puisque OpenCV est BGR et pygame est RVB, il est nécessaire de le convertir.
shape = opencv_image.shape[1::-1] #OpenCV(la taille,largeur,Nombre de couleurs), Pygame(largeur, la taille)Donc, cela est également converti.
pygame_image = pygame.image.frombuffer(opencv_image.tostring(), shape, 'RGB')
Écrivons le code pour convertir de cette façon.
show-image.py
import time
import cv2
import pygame
def get_opencv_img_res(opencv_image):
height, width = opencv_image.shape[:2]
return width, height
def convert_opencv_img_to_pygame(opencv_image):
"""
Convertir des images OpenCV pour Pygame.
see https://blanktar.jp/blog/2016/01/pygame-draw-opencv-image.html
"""
opencv_image = opencv_image[:,:,::-1] #Puisque OpenCV est BGR et pygame est RVB, il est nécessaire de le convertir.
shape = opencv_image.shape[1::-1] #OpenCV(la taille,largeur,Nombre de couleurs), Pygame(largeur, la taille)Donc, cela est également converti.
pygame_image = pygame.image.frombuffer(opencv_image.tostring(), shape, 'RGB')
return pygame_image
def main():
#Charger des images avec OpenCV
image_path = '/usr/share/info/gnupg-module-overview.png' #Le chemin du fichier image initialement inclus dans Raspbian Buster Lite
opencv_image = cv2.imread(image_path)
#Initialiser Pygame
pygame.init()
width, height = get_opencv_img_res(opencv_image)
screen = pygame.display.set_mode((width, height))
#Convertir des images OpenCV pour Pygame
pygame_image = convert_opencv_img_to_pygame(opencv_image)
#Dessiner une image
screen.blit(pygame_image, (0, 0))
pygame.display.update() #Écran de mise à jour
#Attendez 5 secondes pour terminer
time.sleep(5)
pygame.quit()
if __name__ == '__main__':
main()
Commande de démarrage
sudo python3 show-image.py
# Note:Vous devez ajouter "sudo" et l'exécuter avec les privilèges root afin de l'afficher à l'aide de la bibliothèque SDL.
#Version "avec bureau" de Raspbian qui n'est pas Lite et "commande ssh"-Par exemple, si vous vous connectez avec l'option "X" ajoutée,
#Aucune commande "sudo" n'est requise car elle peut être visualisée à l'aide du système X Window.
# see https://www.subthread.co.jp/blog/20181206/
Lorsque vous le démarrez, l'image sera affichée pendant 5 secondes et elle se terminera automatiquement. La conversion fonctionne correctement.
Considérons maintenant le cas de l'affichage de la vidéo analysée en temps réel par la méthode ci-dessus. De telles applications sont souvent utilisées, comme lors de l'analyse et de l'affichage d'images lues à partir d'une caméra. Dans ce cas, l'important est la vitesse de conversion. Mesurons.
show-image.py
import time
import cv2
import pygame
def get_opencv_img_res(opencv_image):
height, width = opencv_image.shape[:2]
return width, height
def convert_opencv_img_to_pygame(opencv_image):
"""
Convertir des images OpenCV pour Pygame.
see https://blanktar.jp/blog/2016/01/pygame-draw-opencv-image.html
"""
opencv_image = opencv_image[:,:,::-1] #Puisque OpenCV est BGR et pygame est RVB, il est nécessaire de le convertir.
shape = opencv_image.shape[1::-1] #OpenCV(la taille,largeur,Nombre de couleurs), Pygame(largeur, la taille)Donc, cela est également converti.
pygame_image = pygame.image.frombuffer(opencv_image.tostring(), shape, 'RGB')
return pygame_image
def main():
#Charger des images avec OpenCV
image_path = '/usr/share/info/gnupg-module-overview.png' #Le chemin du fichier image initialement inclus dans Raspbian Buster Lite
opencv_image = cv2.imread(image_path)
#Initialiser Pygame
pygame.init()
width, height = get_opencv_img_res(opencv_image)
screen = pygame.display.set_mode((width, height))
#Convertir des images OpenCV pour Pygame
time_start = time.perf_counter() #Commencer la mesure
pygame_image = convert_opencv_img_to_pygame(opencv_image)
time_end = time.perf_counter() #Fin de la mesure
print(f'Temps de conversion: {time_end - time_start}Secondes/ {1/(time_end - time_start)}fps')
#Dessiner une image
screen.blit(pygame_image, (0, 0))
pygame.display.update() #Écran de mise à jour
#Attendez 5 secondes pour terminer
time.sleep(5)
pygame.quit()
if __name__ == '__main__':
main()
Temps de conversion: 0.14485926300017127 secondes/ 6.903252020540914fps
**lent! !! !! ** **
0,14 seconde avec un code qui s'affiche simplement. Si vous écrivez 7fps en termes de fréquence d'images, vous pouvez voir cette lenteur.
Est-ce la limite d'OpenCV + Pygame? Non ce n'est pas. Je fais juste mal. La raison pour laquelle il est si lent est que les données d'image sont volontairement converties en chaîne de caractères par la méthode tostring ()
. Comme vous pouvez le voir en mesurant, 90% de ce poids est dû à la méthode tostring ()
.
Les données d'image réelles d'OpenCV pour Python sont le ndarray de NumPy. Pygame a une fonction qui lit les données d'image à partir d'un tableau de NumPy. La façon d'en tirer pleinement parti est décrite dans «OpenCV VideoCapture s'exécutant sur PyGame».
frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
frame = np.rot90(frame)
frame = pygame.surfarray.make_surface(frame)
Aussi, dans la section Commentaire de "OpenCV VideoCapture s'exécutant sur PyGame", la solution au problème d'inversion d'image causé par cette méthode + accélération supplémentaire Est également décrit.
frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
-frame = np.rot90(frame)
+frame = frame.swapaxes(0,1)
frame = pygame.surfarray.make_surface(frame)
Changez pour cette méthode et mesurez à nouveau.
show-image.py
import time
import cv2
import pygame
def get_opencv_img_res(opencv_image):
height, width = opencv_image.shape[:2]
return width, height
def convert_opencv_img_to_pygame(opencv_image):
"""
Convertir des images OpenCV pour Pygame.
see https://gist.github.com/radames/1e7c794842755683162b
"""
rgb_image = cv2.cvtColor(opencv_image, cv2.COLOR_BGR2RGB).swapaxes(0, 1)
#Générer une surface pour dessiner des images avec Pygame à partir d'images OpenCV
pygame_image = pygame.surfarray.make_surface(rgb_image)
return pygame_image
def main():
#Charger des images avec OpenCV
image_path = '/usr/share/info/gnupg-module-overview.png' #Le chemin du fichier image initialement inclus dans Raspbian Buster Lite
opencv_image = cv2.imread(image_path)
#Initialiser Pygame
pygame.init()
width, height = get_opencv_img_res(opencv_image)
screen = pygame.display.set_mode((width, height))
#Convertir des images OpenCV pour Pygame
time_start = time.perf_counter() #Commencer la mesure
pygame_image = convert_opencv_img_to_pygame(opencv_image)
time_end = time.perf_counter() #Fin de la mesure
print(f'Temps de conversion: {time_end - time_start}Secondes/ {1/(time_end - time_start)}fps')
#Dessiner une image
screen.blit(pygame_image, (0, 0))
pygame.display.update() #Écran de mise à jour
#Attendez 5 secondes pour terminer
time.sleep(5)
pygame.quit()
if __name__ == '__main__':
main()
Temps de conversion: 0.030075492999912967 secondes/ 33.24966277370395fps
Cela s'est beaucoup amélioré.
Maintenant que le problème de vitesse a été amélioré, voyons le résultat de faire quelque chose avec OpenCV. Par exemple, supposons que vous souhaitiez afficher le résultat binarisé.
show-image.py
import time
import cv2
import pygame
def get_opencv_img_res(opencv_image):
height, width = opencv_image.shape[:2]
return width, height
def convert_opencv_img_to_pygame(opencv_image):
"""
Convertir des images OpenCV pour Pygame.
see https://gist.github.com/radames/1e7c794842755683162b
"""
rgb_image = cv2.cvtColor(opencv_image, cv2.COLOR_BGR2RGB).swapaxes(0, 1)
#Générer une surface pour dessiner des images avec Pygame à partir d'images OpenCV
pygame_image = pygame.surfarray.make_surface(rgb_image)
return pygame_image
def main():
#Charger des images avec OpenCV
image_path = '/usr/share/info/gnupg-module-overview.png' #Le chemin du fichier image initialement inclus dans Raspbian Buster Lite
opencv_image = cv2.imread(image_path)
#Image de processus
opencv_gray_image = cv2.cvtColor(opencv_image, cv2.COLOR_BGR2GRAY)
ret, opencv_threshold_image = cv2.threshold(opencv_gray_image, 128, 255, cv2.THRESH_BINARY)
opencv_image = opencv_threshold_image
#Initialiser Pygame
pygame.init()
width, height = get_opencv_img_res(opencv_image)
screen = pygame.display.set_mode((width, height))
#Convertir des images OpenCV pour Pygame
pygame_image = convert_opencv_img_to_pygame(opencv_image)
#Dessiner une image
screen.blit(pygame_image, (0, 0))
pygame.display.update() #Écran de mise à jour
#Attendez 5 secondes pour terminer
time.sleep(5)
pygame.quit()
if __name__ == '__main__':
main()
Cependant, l'exécution échoue avec l'erreur suivante:
OpenCV Error: Assertion failed (scn == 3 || scn == 4) in cvtColor, file /build/opencv-L65chJ/opencv-3.2.0+dfsg/modules/imgproc/src/color.cpp, line 9716
Traceback (most recent call last):
File "show-image.py", line 51, in <module>
main()
File "show-image.py", line 39, in main
pygame_image = convert_opencv_img_to_pygame(opencv_image)
File "show-image.py", line 17, in convert_opencv_img_to_pygame
rgb_image = cv2.cvtColor(opencv_image, cv2.COLOR_BGR2RGB).swapaxes(0, 1)
cv2.error: /build/opencv-L65chJ/opencv-3.2.0+dfsg/modules/imgproc/src/color.cpp:9716: error: (-215) scn == 3 || scn == 4 in function cvtColor
L'image binarisée est une image en échelle de gris sans composants de couleur. Cependant, lors du processus de conversion en image pour Pygame, il existe un code qui convertit la couleur de l'image d'entrée de BGR en RVB. Par conséquent, une erreur se produira dans cette partie.
Pour résoudre ce problème, pour les images en niveaux de gris, un processus de branche qui change le deuxième argument de la méthode cv2.cvtColor ()
en cv2.COLOR_GRAY2RGB
est requis.
show-image.py
import time
import cv2
import pygame
def get_opencv_img_res(opencv_image):
height, width = opencv_image.shape[:2]
return width, height
def convert_opencv_img_to_pygame(opencv_image):
"""
Convertir des images OpenCV pour Pygame.
see https://gist.github.com/radames/1e7c794842755683162b
see https://github.com/atinfinity/lab/wiki/%5BOpenCV-Python%5D%E7%94%BB%E5%83%8F%E3%81%AE%E5%B9%85%E3%80%81%E9%AB%98%E3%81%95%E3%80%81%E3%83%81%E3%83%A3%E3%83%B3%E3%83%8D%E3%83%AB%E6%95%B0%E3%80%81depth%E5%8F%96%E5%BE%97
"""
if len(opencv_image.shape) == 2:
#Pour les images en niveaux de gris
cvt_code = cv2.COLOR_GRAY2RGB
else:
#Dans d'autres cas:
cvt_code = cv2.COLOR_BGR2RGB
rgb_image = cv2.cvtColor(opencv_image, cvt_code).swapaxes(0, 1)
#Générer une surface pour dessiner des images avec Pygame à partir d'images OpenCV
pygame_image = pygame.surfarray.make_surface(rgb_image)
return pygame_image
def main():
#Charger des images avec OpenCV
image_path = '/usr/share/info/gnupg-module-overview.png' #Le chemin du fichier image initialement inclus dans Raspbian Buster Lite
opencv_image = cv2.imread(image_path)
#Image de processus
opencv_gray_image = cv2.cvtColor(opencv_image, cv2.COLOR_BGR2GRAY)
ret, opencv_threshold_image = cv2.threshold(opencv_gray_image, 128, 255, cv2.THRESH_BINARY)
opencv_image = opencv_threshold_image
#Initialiser Pygame
pygame.init()
width, height = get_opencv_img_res(opencv_image)
screen = pygame.display.set_mode((width, height))
#Convertir des images OpenCV pour Pygame
pygame_image = convert_opencv_img_to_pygame(opencv_image)
#Dessiner une image
screen.blit(pygame_image, (0, 0))
pygame.display.update() #Écran de mise à jour
#Attendez 5 secondes pour terminer
time.sleep(5)
pygame.quit()
if __name__ == '__main__':
main()
Vous devriez maintenant pouvoir voir également une image binarisée en niveaux de gris.
Premièrement, mesurons la manière conventionnelle d'utiliser la méthode pygame.surfarray.make_surface ()
pour chaque conversion.
show-image.py
import statistics
import time
import cv2
import pygame
def get_opencv_img_res(opencv_image):
height, width = opencv_image.shape[:2]
return width, height
def convert_opencv_img_to_pygame(opencv_image):
"""
Convertir des images OpenCV pour Pygame.
see https://gist.github.com/radames/1e7c794842755683162b
see https://github.com/atinfinity/lab/wiki/%5BOpenCV-Python%5D%E7%94%BB%E5%83%8F%E3%81%AE%E5%B9%85%E3%80%81%E9%AB%98%E3%81%95%E3%80%81%E3%83%81%E3%83%A3%E3%83%B3%E3%83%8D%E3%83%AB%E6%95%B0%E3%80%81depth%E5%8F%96%E5%BE%97
"""
if len(opencv_image.shape) == 2:
#Pour les images en niveaux de gris
cvt_code = cv2.COLOR_GRAY2RGB
else:
#Dans d'autres cas:
cvt_code = cv2.COLOR_BGR2RGB
rgb_image = cv2.cvtColor(opencv_image, cvt_code).swapaxes(0, 1)
#Générer une surface pour dessiner des images avec Pygame à partir d'images OpenCV
pygame_image = pygame.surfarray.make_surface(rgb_image)
return pygame_image
def main():
#Charger des images avec OpenCV
image_path = '/usr/share/info/gnupg-module-overview.png' #Le chemin du fichier image initialement inclus dans Raspbian Buster Lite
opencv_image = cv2.imread(image_path)
#Initialiser Pygame
pygame.init()
width, height = get_opencv_img_res(opencv_image)
screen = pygame.display.set_mode((width, height))
#Convertir des images OpenCV pour Pygame
time_diff_list = []
for _ in range(500):
time_start = time.perf_counter() #Commencer la mesure
pygame_image = convert_opencv_img_to_pygame(opencv_image)
time_end = time.perf_counter() #Fin de la mesure
time_diff_list.append(time_end - time_start)
time_diff = statistics.mean(time_diff_list)
print(f'Temps de conversion moyen: {time_diff}Secondes/ {1/time_diff}fps')
#Dessiner une image
screen.blit(pygame_image, (0, 0))
pygame.display.update() #Écran de mise à jour
#Attendez 1 seconde et terminez
time.sleep(1)
pygame.quit()
if __name__ == '__main__':
main()
Temps de conversion moyen: 0.01808052065800075 secondes/ 55.30814177950641fps
C'est aussi rapide que d'approcher 60fps. C'est encore assez rapide, mais c'est un peu ennuyeux quand vous pensez que vous venez de convertir.
Ensuite, mesurons la méthode d'accélération en utilisant la méthode pygame.surfarray.blit_array ()
.
show-image.py
import statistics
import time
import cv2
import pygame
def get_opencv_img_res(opencv_image):
height, width = opencv_image.shape[:2]
return width, height
pygame_surface_cache = {}
def convert_opencv_img_to_pygame(opencv_image):
"""
Convertir des images OpenCV pour Pygame.
see https://gist.github.com/radames/1e7c794842755683162b
see https://github.com/atinfinity/lab/wiki/%5BOpenCV-Python%5D%E7%94%BB%E5%83%8F%E3%81%AE%E5%B9%85%E3%80%81%E9%AB%98%E3%81%95%E3%80%81%E3%83%81%E3%83%A3%E3%83%B3%E3%83%8D%E3%83%AB%E6%95%B0%E3%80%81depth%E5%8F%96%E5%BE%97
see https://stackoverflow.com/a/42589544/4907315
"""
if len(opencv_image.shape) == 2:
#Pour les images en niveaux de gris
cvt_code = cv2.COLOR_GRAY2RGB
else:
#Dans d'autres cas:
cvt_code = cv2.COLOR_BGR2RGB
rgb_image = cv2.cvtColor(opencv_image, cvt_code).swapaxes(0, 1)
#Obtenez une surface générée à partir du cache avec la même taille d'image
cache_key = rgb_image.shape
cached_surface = pygame_surface_cache.get(cache_key)
if cached_surface is None:
#Générer une surface pour dessiner des images avec Pygame à partir d'images OpenCV
cached_surface = pygame.surfarray.make_surface(rgb_image)
#Ajouter une surface au cache
pygame_surface_cache[cache_key] = cached_surface
else:
#Si vous trouvez une surface avec la même taille d'image, réutilisez la surface déjà générée.
pygame.surfarray.blit_array(cached_surface, rgb_image)
return cached_surface
def main():
#Charger des images avec OpenCV
image_path = '/usr/share/info/gnupg-module-overview.png' #Le chemin du fichier image initialement inclus dans Raspbian Buster Lite
opencv_image = cv2.imread(image_path)
#Initialiser Pygame
pygame.init()
width, height = get_opencv_img_res(opencv_image)
screen = pygame.display.set_mode((width, height))
#Convertir des images OpenCV pour Pygame
time_diff_list = []
for _ in range(500):
time_start = time.perf_counter() #Commencer la mesure
pygame_image = convert_opencv_img_to_pygame(opencv_image)
time_end = time.perf_counter() #Fin de la mesure
time_diff_list.append(time_end - time_start)
time_diff = statistics.mean(time_diff_list)
print(f'Temps de conversion moyen: {time_diff}Secondes/ {1/time_diff}fps')
#Dessiner une image
screen.blit(pygame_image, (0, 0))
pygame.display.update() #Écran de mise à jour
#Attendez 1 seconde et terminez
time.sleep(1)
pygame.quit()
if __name__ == '__main__':
main()
Temps de conversion moyen: 0.013679669161995207 secondes/ 73.1011830884182fps
Il a dépassé 60fps! Si c'est si rapide, ce sera assez pratique.
def convert_opencv_img_to_pygame(opencv_image):
"""
Convertir des images OpenCV pour Pygame.
see https://gist.github.com/radames/1e7c794842755683162b
see https://github.com/atinfinity/lab/wiki/%5BOpenCV-Python%5D%E7%94%BB%E5%83%8F%E3%81%AE%E5%B9%85%E3%80%81%E9%AB%98%E3%81%95%E3%80%81%E3%83%81%E3%83%A3%E3%83%B3%E3%83%8D%E3%83%AB%E6%95%B0%E3%80%81depth%E5%8F%96%E5%BE%97
"""
if len(opencv_image.shape) == 2:
#Pour les images en niveaux de gris
cvt_code = cv2.COLOR_GRAY2RGB
else:
#Dans d'autres cas:
cvt_code = cv2.COLOR_BGR2RGB
rgb_image = cv2.cvtColor(opencv_image, cvt_code).swapaxes(0, 1)
#Générer une surface pour dessiner des images avec Pygame à partir d'images OpenCV
pygame_image = pygame.surfarray.make_surface(rgb_image)
return pygame_image
Si vous souhaitez convertir des images de même taille encore et encore:
pygame_surface_cache = {}
def convert_opencv_img_to_pygame(opencv_image):
"""
Convertir des images OpenCV pour Pygame.
see https://gist.github.com/radames/1e7c794842755683162b
see https://github.com/atinfinity/lab/wiki/%5BOpenCV-Python%5D%E7%94%BB%E5%83%8F%E3%81%AE%E5%B9%85%E3%80%81%E9%AB%98%E3%81%95%E3%80%81%E3%83%81%E3%83%A3%E3%83%B3%E3%83%8D%E3%83%AB%E6%95%B0%E3%80%81depth%E5%8F%96%E5%BE%97
see https://stackoverflow.com/a/42589544/4907315
"""
if len(opencv_image.shape) == 2:
#Pour les images en niveaux de gris
cvt_code = cv2.COLOR_GRAY2RGB
else:
#Dans d'autres cas:
cvt_code = cv2.COLOR_BGR2RGB
rgb_image = cv2.cvtColor(opencv_image, cvt_code).swapaxes(0, 1)
#Obtenez une surface générée à partir du cache avec la même taille d'image
cache_key = rgb_image.shape
cached_surface = pygame_surface_cache.get(cache_key)
if cached_surface is None:
#Générer une surface pour dessiner des images avec Pygame à partir d'images OpenCV
cached_surface = pygame.surfarray.make_surface(rgb_image)
#Ajouter une surface au cache
pygame_surface_cache[cache_key] = cached_surface
else:
#Si vous trouvez une surface avec la même taille d'image, réutilisez la surface déjà générée.
pygame.surfarray.blit_array(cached_surface, rgb_image)
return cached_surface
Recommended Posts