[PYTHON] Faites pivoter le sprite avec OpenCV # 3 ~ Calculez-le vous-même sans le laisser aux autres ~

introduction

Cette fois, je modifie le programme précédent, mais à ce moment-là, je change également les noms de variables, etc. en détail.

  1. Fonctionnalisation des polices japonaises de dessin avec OpenCV
  2. Rendez la fonction de dessin de polices japonaises avec OpenCV à usage général
  3. Gestion des images transparentes avec OpenCV-Making sprites dance-
  4. Faire pivoter les sprites avec OpenCV
  5. Faire pivoter les sprites avec OpenCV # 2 ~ Maîtriser cv2.warpAffine () ~
  6. Faites pivoter le sprite avec OpenCV # 3 ~ Calculez vous-même sans le laisser aux autres ~ ← Maintenant ici

Viser la figure

À propos de la fonction de superposition de type sprite de l'ancien ordinateur personnel de loisir   putSprite(back, front4, pos, angle=0, home=(0,0)) Et. L'expression est presque la même que les articles précédents, mais la signification de l'argument est modifiée.

Je pense que celui-ci est plus facile à utiliser.

Programme de base

C'est difficile à animer à chaque fois, alors j'ai fait ça.

sample.py


import cv2
import numpy as np

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(img_back, img_front, pos, angle=0, home=(0,0)):
    #Mettez en œuvre de différentes manières et choisissez la meilleure.
    pass

def main():
    img_front = cv2.imread("uchuhikoushi.png ", -1)
    img_front = makeSampleImg(img_front)
    img_back = cv2.imread("space.jpg ", -1)
    pos = (100,80)
    home = (140,60)
    angle = 30

    #C'est le principal. Modifiez le nom de la fonction selon vos besoins
    img = putSprite(img_back.copy(), img_front, pos, angle, home)

    cv2.circle(img, pos, 5, (0,0,255), -1)  #Mêmes coordonnées(pos)Tracez un cercle
    cv2.imshow("rotation", img)
    cv2.waitKey(0)
    cv2.destroyAllWindows()

if __name__ == "__main__":
    main()

Le résultat est le suivant. Devrait être.

résultat
rotatin_test.png

Trouvez le quadrilatère circonscrit minimum

Dans "Faire pivoter les sprites avec OpenCV", je n'ai pas pu trouver les coordonnées en haut à gauche de l'image pivotée, mais j'ai réussi à le calculer après cela. Comme je le savais, c'était des mathématiques de niveau secondaire.

putSprite_calc


def putSprite_calc(back, front4, pos, angle=0, home=(0,0)):
    fh, fw = front4.shape[:2]
    bh, bw = back.shape[:2]
    x, y = pos
    xc, yc = home[0] - fw/2, home[1] - fh/2             #Changer le point d'origine de la référence supérieure gauche à la référence du centre de l'image
    a = np.radians(angle)
    cos , sin = np.cos(a), np.sin(a)                    #Cette fonction triangulaire apparaît plusieurs fois, alors faites-en une variable
    w_rot = int(fw * abs(cos) + fh * abs(sin))
    h_rot = int(fw * abs(sin) + fh * abs(cos))
    M = cv2.getRotationMatrix2D((fw/2,fh/2), angle, 1)  #Rotation au centre de l'image
    M[0][2] += w_rot/2 - fw/2
    M[1][2] += h_rot/2 - fh/2
    imgRot = cv2.warpAffine(front4, M, (w_rot,h_rot))   #Quadrilatère externe contenant une image pivotée

    #Ne rien faire si le carré extrinsèque entier est en dehors de l'image d'arrière-plan
    xc_rot = xc * cos + yc * sin                        #Quantité de mouvement lors de la rotation au centre de l'image
    yc_rot = -xc * sin + yc * cos
    x0 = int(x - xc_rot - w_rot / 2)                    #Coordonnée supérieure gauche du carré circonscrit
    y0 = int(y - yc_rot - h_rot / 2)
    if not ((-w_rot < x0 < bw) and (-h_rot < y0 < bh)) :
        return back
    
    #Obtenez uniquement l'image d'arrière-plan du carré extrinsèque
    x1, y1 = max(x0,  0), max(y0,  0)
    x2, y2 = min(x0 + w_rot, bw), min(y0 + h_rot, bh)
    imgRot = imgRot[y1-y0:y2-y0, x1-x0:x2-x0]

    #Combinez le quadrilatère circonscrit et l'arrière-plan avec la méthode du masque
    result = back.copy()
    front = imgRot[:, :, :3]
    mask1 = imgRot[:, :, 3]
    mask = 255 - cv2.merge((mask1, mask1, mask1))
    roi = result[y1:y2, x1:x2]
    tmp = cv2.bitwise_and(roi, mask)
    tmp = cv2.bitwise_or(tmp, front)
    result[y1:y2, x1:x2] = tmp
    return result
imgRot
rot_putSprite_calc.png

Modification du ROI carré

"Faire pivoter les sprites avec OpenCV" a fini par utiliser un carré inutilement grand avec des relations dimensionnelles connues. La fonction à ce moment est modifiée selon cette spécification.

putSprite_mask2 break


def putSprite_mask2(back, front4, pos, angle=0, home=(0,0)):
    fh, fw = front4.shape[:2]
    bh, bw = back.shape[:2]
    x, y = pos
    xc, yc = home

    #Trouvez la valeur maximale de la distance entre le centre de rotation et les quatre coins
    pts = np.array([(0,0), (fw,0), (fw,fh), (0,fh)])
    ctr = np.array([(xc,yc)])
    r = int(np.sqrt(max(np.sum((pts-ctr)**2, axis=1))))

    #Carré contenant une image pivotée
    M = cv2.getRotationMatrix2D((xc,yc), angle, 1)      #Rotation à la maison
    M[0][2] += r - xc
    M[1][2] += r - yc
    imgRot = cv2.warpAffine(front4, M, (2*r,2*r))       #Carré contenant une image pivotée

    #Ne rien faire si le carré entier est en dehors de l'image d'arrière-plan
    x0, y0 = x-r, y-r
    if not ((-2*r < x0 < bw) and (-2*r < y0 < bh)) :
        return back    

    #Obtenez uniquement l'image d'arrière-plan du rectangle
    x1, y1 = max(x0,  0), max(y0,  0)
    x2, y2 = min(x0+2*r, bw), min(y0+2*r, bh)
    imgRot = imgRot[y1-y0:y2-y0, x1-x0:x2-x0]

    #Combinez le quadrilatère circonscrit et l'arrière-plan avec la méthode du masque
    result = back.copy()
    front = imgRot[:, :, :3]
    mask1 = imgRot[:, :, 3]
    mask = 255 - cv2.merge((mask1, mask1, mask1))
    roi = result[y1:y2, x1:x2]
    tmp = cv2.bitwise_and(roi, mask)
    tmp = cv2.bitwise_or(tmp, front)
    result[y1:y2, x1:x2] = tmp
    return result
imgRot
rot_putSprite_mask2.png

Minimiser le ROI carré

Quand je me demandais si je pouvais obtenir le plus petit carré circonscrit de ce carré inutilement grand par programmation plutôt que mathématiquement, j'ai trouvé l'article que je cherchais.

Comprendre comment obtenir des informations sur le rectangle qui entoure la zone non nulle d'un tableau bidimensionnel de numpy (Blog de Kei Minagawa)

J'allais m'en occuper si je ne pouvais pas résoudre le problème de la figure jusqu'au bout.

imgRot Minimiser
rot_putSprite_mask2.png rot_putSprite_calc.png

Modification de la fonction sprite par cv2.warpAffine ()

"Faire pivoter les sprites avec OpenCV # 2 ~ Maîtriser cv2.warpAffine () ~" Modifions également le programme. Puisque la taille de l'image pendant la rotation est égale à celle de l'image d'arrière-plan, la vitesse d'exécution ralentira dès que vous essayez de placer de nombreux petits sprites sur la grande image d'arrière-plan.

imgRot
imgRot_affine.png

putSprite_Affine2


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

    M = cv2.getRotationMatrix2D(home, angle, 1)
    M[0][2] += x - xc  #Le seul point de changement est ici où la définition de pos a été modifiée.
    M[1][2] += y - yc  #Cv2 ne nécessite pas de calcul supplémentaire.warpAffine()Forces.
    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

Comparaison de la vitesse d'exécution

Ajoutez un élément de rotation au programme de comparaison créé dans Gestion des images transparentes avec OpenCV-Making sprites dance- et exécutez-le.

rot_test.py


import cv2
import numpy as np
import time

# def makeSampleImg(img4)Est inutile

def putSprite_calc(back, front4, pos, angle=0, home=(0,0)):
    #Ceux énumérés ci-dessus

def putSprite_mask2(back, front4, pos, angle=0, home=(0,0)):
    #Ceux énumérés ci-dessus

def putSprite_Affine2(back, front4, pos, angle=0, home=(0,0)):
    #Ceux énumérés ci-dessus

def main(func):
    filename_back = "space.jpg "
    filename_front = "uchuhikoushi.png "
    img_back = cv2.imread(filename_back)
    img_front = cv2.imread(filename_front, -1)
    bh, bw = img_back.shape[:2]
    xc, yc = bw//2, bh//2
    rx, ry = bw*0.3, bh*0.4
    home = (140,60)
    cv2.putText(img_back, func, (20,bh-20), cv2.FONT_HERSHEY_SIMPLEX, 1, (255,255,255))

    ###Commencez à partir d'ici pour mesurer le temps
    start_time = time.time()

    for angle in range(-180, 180, 10):
        back = img_back.copy()
        x = int(xc + rx * np.cos(np.radians(angle)))
        y = int(yc + ry * np.sin(np.radians(angle)))
        img = eval(func)(img_back, img_front, (x,y), angle=angle, home=home)

        #Cela peut être activé ou désactivé selon les besoins
        #cv2.imshow(func, img)
        #cv2.waitKey(1)

    elasped_time = time.time() - start_time
    ###Jusque là

    print (f"{func} : {elasped_time} sec")    
    cv2.destroyAllWindows()


if __name__ == "__main__":
    funcs = ["putSprite_calc",
             "putSprite_mask2",
             "putSprite_Affine2" ]
    for func in funcs:
        for i in range(10):
            main(func)

L'animation créée est à peu près celle illustrée ci-dessous, bien que divers éléments aient été ajoutés par souci de clarté.

résultat
rotatin_test.png

Le temps qu'il faut pour traiter cela est dans mon environnement.

python


putSprite_calc    : 0.12500691413879395 sec
putSprite_mask2   : 0.27501583099365234 sec
putSprite_Affine2 : 0.5620322227478027 sec

Comme je le savais, plus la zone de retour sur investissement est petite, plus la vitesse d'exécution est rapide. La taille du ROI est directement liée à la quantité de calcul des canaux verticaux x horizontaux x RVB 3, donc juste parce qu'OpenCV fait du bon travail, si vous utilisez un ROI excessivement important, il ralentira rapidement.

À la fin

Maintenant que je suis arrivé jusqu'ici, je veux créer un jeu. Je dois également étudier l'apprentissage profond. S'il est lié de force à l'apprentissage en profondeur, il sera possible de réduire la quantité de calcul en réduisant bien la taille même lors du passage à l'apprentissage profond.

Recommended Posts

Faites pivoter le sprite avec OpenCV # 3 ~ Calculez-le vous-même sans le laisser aux autres ~
Faire pivoter les sprites avec OpenCV # 2 ~ Maîtriser cv2.warpAffine () ~