[PYTHON] Drehen Sie das Sprite mit OpenCV # 3 ~ Berechnen Sie es selbst, ohne es anderen zu überlassen ~

Einführung

Dieses Mal ändere ich das vorherige Programm, aber zu diesem Zeitpunkt ändere ich auch die Variablennamen usw. im Detail.

  1. Funktionalisieren des Zeichnens japanischer Schriftarten mit OpenCV
  2. Machen Sie die Funktion zum Zeichnen japanischer Schriftarten mit OpenCV für allgemeine Zwecke
  3. Umgang mit transparenten Bildern mit OpenCV-Making Sprites Dance-
  4. Sprites mit OpenCV drehen
  5. Sprites mit OpenCV # 2 drehen ~ cv2.warpAffine () ~ beherrschen
  6. Drehen Sie das Sprite mit OpenCV # 3 ~ Berechnen Sie selbst, ohne es anderen zu überlassen ~ ← Jetzt hier

Zielfigur

Über die Sprite-ähnliche Überlagerungsfunktion des alten Hobby-PCs   putSprite(back, front4, pos, angle=0, home=(0,0)) Und. Der Ausdruck ist fast der gleiche wie in den vorherigen Artikeln, aber die Bedeutung des Arguments wird geändert.

Ich denke, dieser ist einfacher zu bedienen.

Grundprogramm

Es ist jedes Mal mühsam zu animieren, also habe ich das gemacht.

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)):
    #Implementieren Sie auf verschiedene Arten und wählen Sie die beste aus.
    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

    #Dies ist die Hauptsache. Ändern Sie den Funktionsnamen nach Bedarf
    img = putSprite(img_back.copy(), img_front, pos, angle, home)

    cv2.circle(img, pos, 5, (0,0,255), -1)  #Gleiche Koordinaten(pos)Zeichne einen Kreis
    cv2.imshow("rotation", img)
    cv2.waitKey(0)
    cv2.destroyAllWindows()

if __name__ == "__main__":
    main()

Das Ergebnis ist dies. Sollte sein.

Ergebnis
rotatin_test.png

Finden Sie das minimale umschreibende Viereck

In "Sprites mit OpenCV drehen" konnte ich die oberen linken Koordinaten des gedrehten Bildes nicht finden, aber ich konnte sie danach berechnen. Wie ich wusste, war es Mathematik auf Highschool-Niveau.

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             #Wechseln Sie von der Referenz oben links zur Referenz für die Bildmitte nach Hause
    a = np.radians(angle)
    cos , sin = np.cos(a), np.sin(a)                    #Diese Dreiecksfunktion wird häufig angezeigt. Machen Sie sie daher zu einer Variablen
    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)  #In der Bildmitte drehen
    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))   #Externes Viereck mit gedrehtem Bild

    #Tun Sie nichts, wenn sich das gesamte äußere Quadrat außerhalb des Hintergrundbilds befindet
    xc_rot = xc * cos + yc * sin                        #Bewegungsumfang beim Drehen in der Bildmitte
    yc_rot = -xc * sin + yc * cos
    x0 = int(x - xc_rot - w_rot / 2)                    #Obere linke Koordinate des umschreibenden Quadrats
    y0 = int(y - yc_rot - h_rot / 2)
    if not ((-w_rot < x0 < bw) and (-h_rot < y0 < bh)) :
        return back
    
    #Holen Sie sich nur das Hintergrundbild des äußeren Quadrats
    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]

    #Kombinieren Sie das umschreibende Viereck und den Hintergrund mit der Maskenmethode
    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

Änderung des quadratischen ROI

In "Sprites mit OpenCV drehen" habe ich ein nutzlos großes Quadrat mit bekannten Dimensionsbeziehungen verwendet. Die Funktion zu diesem Zeitpunkt wird an diese Spezifikation geändert.

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

    #Finden Sie den Maximalwert des Abstands zwischen dem Drehpunkt und den vier Ecken
    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))))

    #Quadrat mit gedrehtem Bild
    M = cv2.getRotationMatrix2D((xc,yc), angle, 1)      #Zu Hause drehen
    M[0][2] += r - xc
    M[1][2] += r - yc
    imgRot = cv2.warpAffine(front4, M, (2*r,2*r))       #Quadrat mit gedrehtem Bild

    #Tun Sie nichts, wenn sich das gesamte Quadrat außerhalb des Hintergrundbilds befindet
    x0, y0 = x-r, y-r
    if not ((-2*r < x0 < bw) and (-2*r < y0 < bh)) :
        return back    

    #Holen Sie sich nur das Hintergrundbild des Rechtecks
    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]

    #Kombinieren Sie das umschreibende Viereck und den Hintergrund mit der Maskenmethode
    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

Minimieren Sie den quadratischen ROI

Als ich mich fragte, ob ich das kleinste umschreibende Quadrat aus diesem nutzlos großen Quadrat programmatisch und nicht mathematisch erhalten könnte, fand ich den Artikel, den ich suchte.

Verstehen, wie Informationen über das Rechteck abgerufen werden, das den Nicht-Null-Bereich eines zweidimensionalen Arrays von Numpys umgibt (Kei Minagawas Blog)

Ich würde mich darum kümmern, wenn ich das Figurenproblem nicht bis zum Ende lösen könnte.

imgRot Minimieren
rot_putSprite_mask2.png rot_putSprite_calc.png

Änderung der Sprite-Funktion durch cv2.warpAffine ()

"Sprites mit OpenCV # 2 drehen ~ cv2.warpAffine () ~ beherrschen" Lassen Sie uns auch das Programm ändern. Da die Bildgröße während der Drehung der des Hintergrundbilds entspricht, sinkt die Ausführungsgeschwindigkeit sofort, wenn Sie versuchen, viele kleine Sprites auf einem großen Hintergrundbild zu platzieren.

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  #Der einzige Änderungspunkt ist hier, wo die Definition von pos geändert wurde.
    M[1][2] += y - yc  #Cv2 erfordert keine zusätzliche Berechnung.warpAffine()Stärken.
    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

Vergleich der Ausführungsgeschwindigkeit

Fügen Sie dem in Umgang mit transparenten Bildern mit OpenCV-Making Sprites Dance- erstellten Vergleichsprogramm ein Rotationselement hinzu und führen Sie es aus.

rot_test.py


import cv2
import numpy as np
import time

# def makeSampleImg(img4)Ist unnötig

def putSprite_calc(back, front4, pos, angle=0, home=(0,0)):
    #Die oben aufgeführten

def putSprite_mask2(back, front4, pos, angle=0, home=(0,0)):
    #Die oben aufgeführten

def putSprite_Affine2(back, front4, pos, angle=0, home=(0,0)):
    #Die oben aufgeführten

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))

    ###Beginnen Sie von hier aus, um die Zeit zu messen
    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)

        #Dies kann nach Bedarf aktiviert oder deaktiviert werden
        #cv2.imshow(func, img)
        #cv2.waitKey(1)

    elasped_time = time.time() - start_time
    ###Bisher

    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)

Die erstellte Animation entspricht in etwa der folgenden Abbildung, obwohl der Übersichtlichkeit halber verschiedene Elemente hinzugefügt wurden.

Ergebnis
rotatin_test.png

Die Zeit, die benötigt wird, um dies zu verarbeiten, ist in meiner Umgebung.

python


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

Wie ich wusste, ist die Ausführungsgeschwindigkeit umso schneller, je kleiner der ROI-Bereich ist. Die Größe des ROI hängt direkt mit dem Rechenaufwand für vertikale x horizontale x RGB 3-Kanäle zusammen. Nur weil OpenCV gute Arbeit leistet, verlangsamt sich der ROI schnell, wenn Sie einen verschwenderisch großen ROI verwenden.

Am Ende

Jetzt, wo ich es bisher geschafft habe, möchte ich ein Spiel machen. Ich muss auch tiefes Lernen lernen. Wenn es zwangsweise mit Deep Learning zusammenhängt, ist es möglich, den Rechenaufwand zu reduzieren, indem die Größe auch beim Deep Learning deutlich reduziert wird.

Recommended Posts

Drehen Sie das Sprite mit OpenCV # 3 ~ Berechnen Sie es selbst, ohne es anderen zu überlassen ~
Sprites mit OpenCV # 2 drehen ~ Mastering cv2.warpAffine () ~