• # vid2pano.py

    Posté par (site web personnel) . En réponse au message Transformer une vidéo de travelling en photo panoramique ?. Évalué à 10 (+9/-0).

    Salut,

    J'ai déjà fait ça avec succès, inspiré par ce post StackOverflow qui correspond exactement à ta demande :

    La réponse propose une solution en pur Python avec OpenCV. Je l'utilise moi-même et ça fonctionne plutôt bien, même depuis une vidéo prise au smartphone.

    Voici mon adaptation : un script unique qui prend une vidéo en entrée, extrait les frames significatives (via détection de keypoints ORB) et les assemble en panorama.

    Installation :

    pip install opencv-python numpy

    vid2pano.py :

    #!/usr/bin/env python3
    # https://stackoverflow.com/a/66378715
    import cv2
    import numpy as np
    import os
    import shutil
    import argparse
    # rescale the images (1.00 = original / 0.5 = half size)
    def rescale(img):
     scale = 1.00
     h, w = img.shape[:2]
     h = int(h * scale)
     w = int(w * scale)
     return cv2.resize(img, (w, h))
    # decimate function
    def decimate(input_file):
     # delete and create directory
     folder = "frames/"
     if os.path.isdir(folder):
     shutil.rmtree(folder)
     os.mkdir(folder)
     # open vidcap
     cap = cv2.VideoCapture(input_file)
     counter = 0
     # make an orb feature detector and a brute force matcher
     orb = cv2.ORB_create()
     bf = cv2.BFMatcher(cv2.NORM_HAMMING, crossCheck=False)
     # store the first frame
     _, last = cap.read()
     last = rescale(last)
     cv2.imwrite(folder + str(counter).zfill(5) + ".png", last)
     # get the first frame's stuff
     kp1, des1 = orb.detectAndCompute(last, None)
     # cutoff, the minimum number of keypoints
     cutoff = 50
     # count number of frames
     prev = None
     while True:
     # get frame
     ret, frame = cap.read()
     if not ret:
     break
     # resize
     frame = rescale(frame)
     # count keypoints
     kp2, des2 = orb.detectAndCompute(frame, None)
     # match
     matches = bf.knnMatch(des1, des2, k=2)
     # lowe's ratio
     good = []
     for m, n in matches:
     if m.distance < 0.5 * n.distance:
     good.append(m)
     # check against cutoff
     print(len(good))
     if len(good) < cutoff:
     # swap and save
     counter += 1
     last = frame
     kp1 = kp2
     des1 = des2
     cv2.imwrite(folder + str(counter).zfill(5) + ".png", last)
     print("New Frame: " + str(counter))
     # show
     cv2.imshow("Frame", frame)
     cv2.waitKey(1)
     prev = frame
     # also save last frame
     counter += 1
     cv2.imwrite(folder + str(counter).zfill(5) + ".png", prev)
     # check number of saved frames
     print("Counter: " + str(counter))
    # stitcher function
    def stitcher():
     # target folder
     folder = "frames/"
     # load images
     filenames = os.listdir(folder)
     images = []
     for file in filenames:
     # get image
     img = cv2.imread(folder + file)
     # save
     images.append(img)
     # use built in stitcher
     stitcher = cv2.Stitcher_create()
     (status, stitched) = stitcher.stitch(images)
     cv2.imwrite("result.png", stitched)
    # main function
    def main():
     parser = argparse.ArgumentParser(description="Process a video file.")
     parser.add_argument('-input', type=str, required=True, help='Input video file')
     args = parser.parse_args()
     decimate(args.input)
     stitcher()
    if __name__ == "__main__":
     main()

    Utilisation :

    python vid2pano.py -input ma-video.mp4

    Le résultat est dans result.png. Le paramètre cutoff (par défaut 50) contrôle le nombre de keypoints minimum pour considérer qu'une frame est suffisamment différente de la précédente — à ajuster selon ta vidéo.

    wind0w$ suxX, GNU/Linux roxX!