Comment convertir une image OpenCV (cv2) (BGR et BGRA) en objet pygame.Surface
J'ai créé des images à partir de https://opencv.org//https://pypi.org/project/opencv-python/ (https://numpy.org/doc/stable/reference/generated/numpy.array.html) et je souhaite les convertir en https://www.pygame.org/docs/ref/surface.html objet:
def cv2ImageToSurface(cv2Image):
pygameSurface = # ? create from "cv2Image"
return pygameSurface
surface = cv2ImageToSurface(cv2Image)
Certaines des images ont 3 canaux ( BGR ) et certaines des images ont également un canal alpha ( BGRA ). Que dois-je faire cv2ImageToSurfacepour convertir des images avec l'un des formats en unhttps://www.pygame.org/docs/ref/surface.html objet?
Réponses
L' shapeattribut d'unhttps://numpy.org/doc/stable/reference/generated/numpy.array.htmlest le nombre d'éléments dans chaque dimension. Le premier élément est la hauteur, le second la largeur et le troisième le nombre de canaux.
UNEhttps://www.pygame.org/docs/ref/surface.html peut être généré par https://www.pygame.org/docs/ref/image.html#pygame.image.frombuffer. Le 1er argument peut être a numpy.arrayet le 2ème argument est le format ( RGBou RGBA).
Obtenez la taille ( largeur , hauteur ) de l' pygame.Surfaceobjet en découpant:
size = cv2Image.shape[1::-1]
Déterminez le format cible de l' pygame.Surfaceobjet, en fonction du troisième canal:
format = 'RGBA' if cv2Image.shape[2] == 4 else 'RGB'
Puisque le format source est BGR ou BGRA , mais que le format cible est RGB ou RGBA , les canaux rouge et bleu doivent être permutés:
cv2Image[:, :, [0, 2]] = cv2Image[:, :, [2, 0]]
Dans le cas d'une image en niveaux de gris, la forme du tableau doit être modifiée à l'aide de https://numpy.org/doc/stable/reference/generated/numpy.reshape.html et le canal gris doit être étendu à un canal de couleur rouge-vert et bleu en utilisant https://numpy.org/doc/stable/reference/generated/numpy.repeat.html:
cv2Image = np.repeat(cv2Image.reshape(size[1], size[0], 1), 3, axis = 2)
Avec ses données l' pygame.Surfaceobjet peut être généré par pygame.image.frombuffer:
surface = pygame.image.frombuffer(cv2Image.flatten(), size, format)
Pour garantir que l'image a le même format de pixel que la surface d' affichage et pour des performances optimales, la surface doit être convertie avec l'un ou l'autrehttps://www.pygame.org/docs/ref/surface.html#pygame.Surface.convert ou https://www.pygame.org/docs/ref/surface.html#pygame.Surface.convert_alpha:
surface = surface.convert_alpha() if format == 'RGBA' else surface.convert()
Fonction complète cv2ImageToSurface:
def cv2ImageToSurface(cv2Image):
if cv2Image.dtype.name == 'uint16':
cv2Image = (cv2Image / 256).astype('uint8')
size = cv2Image.shape[1::-1]
if len(cv2Image.shape) == 2:
cv2Image = np.repeat(cv2Image.reshape(size[1], size[0], 1), 3, axis = 2)
format = 'RGB'
else:
format = 'RGBA' if cv2Image.shape[2] == 4 else 'RGB'
cv2Image[:, :, [0, 2]] = cv2Image[:, :, [2, 0]]
surface = pygame.image.frombuffer(cv2Image.flatten(), size, format)
return surface.convert_alpha() if format == 'RGBA' else surface.convert()
Exemple minimal:
import os
import pygame
import cv2
import numpy as np
def cv2ImageToSurface(cv2Image):
if cv2Image.dtype.name == 'uint16':
cv2Image = (cv2Image / 256).astype('uint8')
size = cv2Image.shape[1::-1]
if len(cv2Image.shape) == 2:
cv2Image = np.repeat(cv2Image.reshape(size[1], size[0], 1), 3, axis = 2)
format = 'RGB'
else:
format = 'RGBA' if cv2Image.shape[2] == 4 else 'RGB'
cv2Image[:, :, [0, 2]] = cv2Image[:, :, [2, 0]]
surface = pygame.image.frombuffer(cv2Image.flatten(), size, format)
return surface.convert_alpha() if format == 'RGBA' else surface.convert()
pygame.init()
window = pygame.display.set_mode((400, 400))
clock = pygame.time.Clock()
cv2Image1 = cv2.imread('woodtiles.jpg', cv2.IMREAD_GRAYSCALE)
cv2Image2 = cv2.imread('woodtiles.jpg', cv2.IMREAD_UNCHANGED)
cv2Image3 = cv2.imread('Apple1-256.png', cv2.IMREAD_UNCHANGED)
pygameSurface1 = cv2ImageToSurface(cv2Image1)
pygameSurface2 = cv2ImageToSurface(cv2Image2)
pygameSurface3 = cv2ImageToSurface(cv2Image3)
run = True
while run:
clock.tick(60)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
window.fill(0)
window.blit(pygameSurface1, pygameSurface1.get_rect(topleft = window.get_rect().inflate(-10, -10).topleft))
window.blit(pygameSurface2, pygameSurface2.get_rect(center = window.get_rect().center))
window.blit(pygameSurface3, pygameSurface3.get_rect(bottomright = window.get_rect().inflate(-10, -10).bottomright))
pygame.display.flip()
pygame.quit()