[PYTHON] Faire pivoter les sprites avec OpenCV # 2 ~ Maîtriser cv2.warpAffine () ~

introduction

L'article précédent "Faire pivoter les sprites avec OpenCV" ne pouvait pas être terminé pendant les vacances d'été, et j'ai réussi à le diviser en utilisant le week-end. Cependant, le lendemain de son téléchargement, une nouvelle idée m'est venue. Après tout, l'environnement avec de nombreuses tentations n'est pas bon. Vous devez vous mettre dans un environnement plus difficile pour étudier. Lieu de travail. </ grève>

Détails de cv2.warpAffine ()

Il y avait un argument intéressant non essentiel de cv2.warpAffine () que j'ai omis la dernière fois. Article de référence: Dessinez une autre image sur une image avec OpenCV

--src Image d'origine. Obligatoire. -M matrice de conversion 2 * 3. Obligatoire. --dsize Spécifiez la taille de l'image de sortie avec une touche de (largeur, hauteur). Obligatoire. --dst image d'arrière-plan (en dehors de l'image d'origine). La taille doit être la même que dsize. --flags Méthode de complément d'image. La valeur par défaut est «cv2.INTER_LINEAR». De plus, cv2.INTER_NEAREST etc. --BorderMode Méthode de traitement de l'arrière-plan (en dehors de l'image d'origine). La valeur par défaut est «cv2.BORDER_CONSTANT». --BorderValue La couleur lorsque borderMode = cv2.BORDER_CONSTANT. La valeur par défaut est «0». Voir ci-dessous.

Petit traitement de l'image originale à l'avance

Créez une petite fonction pour rendre le résultat du traitement d'image facile à comprendre. Veuillez noter que seule la partie minimale est décrite.

python


def makeSampleImg(img4):   #Apportez une image RGBA 4 canaux
    h, w = img.shape[:2]
    #A ainsi que la coloration des environs=C'est une erreur d'en faire 255 (opaque)
    cv2.rectangle(img, (0,0), (w-1,h-1), (0,0,255,255), 1)
    return img

img_origin = cv2.imread(filename, -1) #Image RGBA
img4 = makeSampleImg(img_origin)
img3 = img4[:, :, :3]  #Éliminez l'élément A et faites-en une image RVB
L'image originale Petit traitement 3 canaux
uchuhikoushi.png uchuhikoushi4w.png uchuhikoushi3w.png

Remplissez l'arrière-plan d'une seule couleur

Faites de l'arrière-plan une couleur unique avec borderMode = cv2.BORDER_CONSTANT. Comme il s'agit de la valeur par défaut de borderMode, il n'est pas nécessaire de se soucier de la décrire. Je vais en essayer.

Spécifiez la couleur d'arrière-plan


M = cv2.getRotationMatrix2D((w/2,h/2), 30, 1)
img_rot31 = cv2.warpAffine(img3, M, (w, h), borderValue=(255,255,0))     #RVB de premier plan, RVB d'arrière-plan
img_rot32 = cv2.warpAffine(img3, M, (w, h), borderValue=(255,255,0,0))   #RVB de premier plan, arrière-plan RVBA (composant A = 0)
img_rot33 = cv2.warpAffine(img3, M, (w, h))                              #RVB de premier plan, aucun arrière-plan spécifié
img_rot41 = cv2.warpAffine(img4, M, (w, h), borderValue=(255,255,0))     #RGBA de premier plan, arrière-plan RGB
img_rot42 = cv2.warpAffine(img4, M, (w, h), borderValue=(255,255,0,0))   #RGBA de premier plan, RGBA d'arrière-plan (composant A = 0)
img_rot43 = cv2.warpAffine(img4, M, (w, h))                              #RGBA de premier plan, aucun arrière-plan spécifié
img_rot44 = cv2.warpAffine(img4, M, (w, h), borderValue=(255,255,0,255)) #RGBA de premier plan, RGBA d'arrière-plan (avec composant A)

** Si le premier plan est une image RVB **, le composant A est ignoré même si 4 canaux RVBA sont spécifiés comme couleur d'arrière-plan (img_rot32). Il faut comprendre que si borderValue n'est pas spécifié, l'arrière-plan sera noir (img_rot33, ou par défaut), mais la couleur d'arrière-plan sera (0,0,0).

img_rot31 img_rot32 img_rot33
uchuhikoushi31.png uchuhikoushi32.png uchuhikoushi33.png

** Si le premier plan est une image RGBA **, le résultat de sortie sera de 4 canaux RGBA même si 3 canaux RVB sont spécifiés comme couleur d'arrière-plan. La composante A donnée à ce moment est «0». Puisque A est l'opacité, pas la transparence, c'est «0», ce qui signifie que même si une valeur BGR qui n'était pas RVB… est définie, elle sera transparente en conséquence (img_rot41). Même si borderValue n'est pas spécifié, l'arrière-plan devient transparent (img_rot43), mais il est facile à comprendre si vous pensez que la couleur d'arrière-plan sera (0,0,0,0). Bien sûr, si vous définissez une valeur autre que «0» pour le composant A, il sera correctement coloré (img_rot44).

img_rot41 img_rot42 img_rot43 img_rot44
uchuhikoushi41.png uchuhikoushi42.png uchuhikoushi43.png uchuhikoushi44.png

Définir une image comme arrière-plan

Si vous définissez borderMode = cv2.BORDER_TRANSPARENT, vous pouvez spécifier l'image d'arrière-plan avec dst. Comme c'est souvent le cas avec OpenCV, lorsque cv2.warpAffine () est utilisé, l'image d'arrière-plan spécifiée par dst est traitée. Si vous avez besoin de conserver l'image d'origine, vous devez définir dst = back.copy ().

Je m'attendais à ce que ce soit transparent car il était TRANSPARENT, donc si je ne spécifiais pas dst, ce serait un arrière-plan transparent, mais ce n'était pas si facile. Oh, d'une manière ou d'une autre, je veux manger Ikura.

Spécifiez l'image d'arrière-plan


back = cv2.imread(back_name)  #Image RVB de la même taille que l'image de premier plan
back4 = cv2.cvtColor(back, cv2.COLOR_BGR2BGRA) #Créer une image RGBA Un composant est 255 au lieu de 0

M = cv2.getRotationMatrix2D((w/2,h/2), 30, 1)
img_rot35 = cv2.warpAffine(img3, M, (w, h), borderMode=cv2.BORDER_TRANSPARENT, dst=back.copy())   #RVB de premier plan, RVB d'arrière-plan
img_rot36 = cv2.warpAffine(img3, M, (w, h), borderMode=cv2.BORDER_TRANSPARENT, dst=back4.copy())  #RVB de premier plan, arrière-plan RVBA
img_rot37 = cv2.warpAffine(img3, M, (w, h), borderMode=cv2.BORDER_TRANSPARENT)                    #RVB de premier plan, aucun arrière-plan spécifié
img_rot45 = cv2.warpAffine(img4, M, (w, h), borderMode=cv2.BORDER_TRANSPARENT, dst=back.copy())   #RGBA de premier plan, arrière-plan RGB
img_rot46 = cv2.warpAffine(img4, M, (w, h), borderMode=cv2.BORDER_TRANSPARENT, dst=back4.copy())  #RGBA de premier plan, arrière-plan RGBA
img_rot47 = cv2.warpAffine(img4, M, (w, h), borderMode=cv2.BORDER_TRANSPARENT)                    #RGBA de premier plan, aucun arrière-plan spécifié

Le premier plan et l'arrière-plan se sont comportés comme prévu avec l'image RVB (img_rot35).

Si je ne spécifiais pas d'image d'arrière-plan au premier plan RVB, le résultat changeait à chaque fois que je l'exécutais (img_rot37). numpy a une fonction appelée numpy.empty () qui crée un tableau non initialisé. L'image d'arrière-plan (ou plutôt le tableau numpy) peut avoir été créée avec les mêmes spécifications ici aussi. Je ne sais pas pourquoi img_rot36, qui spécifiait l'arrière-plan RGBA au premier plan RVB, s'est transformé en fond noir avec de la poussière.

img_rot35 img_rot36 img_rot37 partie 1 img_rot37 partie 2
uchuhikoushi35.png uchuhikoushi36.png uchuhikoushi37.png uchuhikoushi37_2.png

L'image RGBA (img_rot46) pour le premier plan et l'arrière-plan est comme prévu, mais c'est aussi un résultat décevant. J'étais content si l'arrière-plan était affiché dans la partie transparente du premier plan, mais cela ne peut pas être aidé car ** la conversion Affin n'est pas ce genre de chose en premier lieu **. L'arrière-plan de img_rot45, qui spécifiait un arrière-plan RVB pour le premier plan RGBA, et img_rot47, qui ne spécifiait pas d'image d'arrière-plan pour le premier plan RGBA, est devenu transparent. Dans les deux cas, il semble que «0» ait été ajouté à l'élément A.

En bref, la conclusion banale est qu'il est sage de ne pas l'utiliser de manière inattendue.

img_rot45 img_rot46 img_rot47
uchuhikoushi45.png uchuhikoushi46.png uchuhikoushi47.png

Utiliser pour les sprites

Comme mentionné ci-dessus, en référence à l'article de mon prédécesseur, j'ai pensé à coller une image RGBA avec transparence sur l'image, mais il était impossible de conclure. Cependant, les spécifications qui ne provoquent pas d'erreur même si elle s'étend au-delà de l'image de fond sont attrayantes. Par conséquent, j'ai décidé de combiner la méthode du masque que j'ai fait jusqu'à présent avec la conversion Affin. Ensuite ... c'est fait, super facile. Il n'y a pas de problème avec le carré extrinsèque ou le retour sur investissement.

La source


import cv2

def makeSampleImg(img4):
    h, w = img4.shape[:2]
    cv2.rectangle(img4, (0,0), (w-1,h-1), (0,0,255,255), 1)
    return img4

def putSprite_Affine(back, front4, pos, angle=0, center=(0,0)):
    x, y = pos
    front3 = front4[:, :, :3]
    mask1 =  front4[:, :, 3]
    mask3 = 255- cv2.merge((mask1, mask1, mask1))
    bh, bw = back.shape[:2]

    M = cv2.getRotationMatrix2D(center, angle, 1)
    M[0][2] += x
    M[1][2] += y
    front_rot = cv2.warpAffine(front3, M, (bw,bh))
    mask_rot = cv2.warpAffine(mask3, M, (bw,bh), borderValue=(255,255,255))
    tmp = cv2.bitwise_and(back, mask_rot)
    result = cv2.bitwise_or(tmp, front_rot)
    return result

if __name__ == "__main__":
    filename_front = "uchuhikoushi.png "
    filename_back = "space.jpg "
    img_front = cv2.imread(filename_front, -1)
    img_front = makeSampleImg(img_front)  #Ajouter un cadre à l'image RGBA (non requis)
    img_back = cv2.imread(filename_back)

    pos = [(0, 50), (300,200), (400,400), (500,-50), (-100,1000)] #Coordonnée en haut à gauche pour mettre l'image
    xc, yc = 140, 60  #Centre de rotation de l'image de premier plan
    angle = 0

    while True:
        img = img_back.copy()
        for x,y in pos:
            img = putSprite_Affine(img, img_front, (x,y), angle, (xc,yc))

            #Assurez-vous qu'il est correctement représenté (non requis)
            cv2.circle(img, (x,y), 5, (0,255,0), -1)       #Marque en haut à gauche de l'image de premier plan
            cv2.circle(img, (x+xc,y+yc), 5, (0,0,255), -1) #Marque au centre de rotation
            cv2.putText(img, f"angle={angle}", (10,440), cv2.FONT_HERSHEY_SIMPLEX, 1, (0,0,255), 2)

        cv2.imshow("putSprite_Affine", img)

        key = cv2.waitKey(1) & 0xFF
        if key == ord("q"):
            break
        angle = (angle + 30) % 360

    cv2.destroyAllWindows()

En parlant d'inconvénients, la quantité de calcul est inutilement importante car l'image du masque et l'image RVB sont converties en l'image d'arrière-plan entière ("[Overhead](https://ja.wikipedia.org/wiki/%E3%82%]" AA% E3% 83% BC% E3% 83% 90% E3% 83% BC% E3% 83% 98% E3% 83% 83% E3% 83% 89) semble être grand. "

Devant au milieu du dessin_rot
front_rot.png
Masque au milieu du dessin_rot
mask_rot.png
résultat
Ici, le GIF animé précédent est posté, mais en réalité, le personnage avec le cadre rouge tourne.uchuhikoushi_anim_3.gif

Voyons maintenant comment cela affecte la vitesse d'exécution.

À la fin

Ce n'est pas encore fini.

Recommended Posts

Faire pivoter les sprites avec OpenCV # 2 ~ Maîtriser cv2.warpAffine () ~
Faire pivoter les sprites avec OpenCV
Faites pivoter le sprite avec OpenCV # 3 ~ Calculez-le vous-même sans le laisser aux autres ~
Détecter le retour du chat avec OpenCV
Binarisation avec OpenCV / Python
Augmentation des données avec openCV
TopView facile avec OpenCV
Trébucher avec opencv3 de homebrew
Reconnaissance faciale avec OpenCV de Python
"Traitement Apple" avec OpenCV3 + Python3
Essayez la détection des bords avec OpenCV
Édition d'image avec python OpenCV
Capture de caméra avec Python + OpenCV
[Python] Utilisation d'OpenCV avec Python (basique)
Binariser les données photo avec OpenCV
Chargement de la vidéo en boucle avec opencv
Détection des bords en temps réel avec OpenCV
Détection de visage avec Python + OpenCV
Obtenez des fonctionnalités d'image avec OpenCV
Reconnaissance faciale / coupe avec OpenCV
Essayez OpenCV avec Google Colaboratory
Création d'un classificateur en cascade avec opencv
Utiliser OpenCV avec Python @Mac
Reconnaissance d'image avec Keras + OpenCV
Détection de visage d'anime avec OpenCV