Outils pour utilisateurs

Outils du site


diy:projets:panorama

Panorama

Introduction

Le but de ce tutoriel sera d'effectuer un panorama utilisant le raspberry pi et sa camera.

On aurrait pu pour cela tourner la camera du raspberry pi à la main, ou utiliser plusieurs raspberry pi avec leurs camera pour pouvoir avoir une vue panoramique, mais c'est asser contraignant soit en temp ou en ressource.

La seule solution possible étant d'utiliser un support avec double servo moteurs photo ci contre
qu'on peut trouver généralement avec des kit du raspberry pi.

support camera pi utilisant 2 servo moteurs

En premier lieu on montrera comment fusionner 2 images sous python, puis je présenterais le code python nomméservo.py permettant de bouger la camera dans toutes les directions.

Ensuite j'expliquerais aussi le montage électrique asser simple à faire mais il faudra quand meme utiliser une source 5v externe et non celle du raspberry pi car l'utilisation des servo moteurs peuvent tirer beaucoup d'ampere et risquerais de terminer le raspberry pi.

Initialisation des rapports cycliques (servo utilisant les gpio PWM) graces a un code python.

Puis apres ça je présenterais un petit code shell nommé fishEye.sh qui utiliserera la commande python faite précedement et qui prendra plusieurs photo avec une vue 360 degré.

Puis pour finir l'utilisation d'un programme opencv pour créer le panorama avec les photos prises, on pourra spécifier le mode de rendu du panorama :

  • Cylindrique
  • Sphérique (par default)
  • Stéréographique
  • et pleins d'autres


Tapez dans un terminal la commande pour voir les autres options

./stitching -h


Vous pouvez télécharger le code source directement avec wget en console, exemple avec le fichier fishEye.sh

wget https://github.com/hiergaut/unix/blob/master/raspbian/bin/fishEye.sh

lire aussi le README.md sur github


Commencement avec python avec 2 images

Le principe même du panorama entre deux photo est de reperer les similitudes entres elles comme des coins ou autres formes facilement reperables.


Prenons ces deux photos pour entrainement (splitter au préalable par mes soins)



On peut visionner ces formes de cette façon en python

img =cv2.imread('panorama_0.jpg')
img_gray =cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
 
sift_obj =cv2.xfeatures2d.SIFT_create()
kp, _ =sift_obj.detectAndCompute(img_gray, None)
 
img =cv2.drawKeypoints(img_gray, kp, img)
# img =cv2.drawKeypoints(img_gray, kp, img, flags=cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS)
 
 
cv2.imshow('w', img)
while cv2.waitKey(10) != 27:
	continue



Avant de faire la fusion il faut trouver les points qui se trouve sur les 2 images, on peut visionner cela avec ce code

img1c =cv2.imread('panorama_0.jpg')
img2c =cv2.imread('panorama_1.jpg')
 
sift_obj =cv2.xfeatures2d.SIFT_create()
kp1, des1 =sift_obj.detectAndCompute(img1c, None)
kp2, des2 =sift_obj.detectAndCompute(img2c, None)
 
# FLANN parameters
FLANN_INDEX_KDTREE = 0
index_params = dict(algorithm = FLANN_INDEX_KDTREE, trees = 5)
search_params = dict(checks=50)   # or pass empty dictionary
 
flann = cv2.FlannBasedMatcher(index_params,search_params)
 
matches = flann.knnMatch(des1,des2,k=2)
 
img3 = cv2.drawMatchesKnn(img1c,kp1,img2c,kp2,matches,None)
 
cv2.imshow("correspondences", img3)
cv2.waitKey()


On visionne les points repere entres les 2 images, on peut voir qu'il y en a beaucoup


Puis on peut utiliser le code decrit dans ce tuto pour fusionner les deux images https://www.pyimagesearch.com/2016/01/11/opencv-panorama-stitching/




Utilisation des servo moteurs avec python (lib GPIO)

On souhaite faire un panorama 360° avec des photos prise par la camera du rpi, on aurrait pu utiliser plusieurs rpi cote à cote, avec des inclinaisons differentes, mais l'option d'utiliser 2 servomoteurs permettra d'avoir une vue spherique de l'ensemble de l'environnement juste avec qu'un camera pi.

Montage électrique


Presentation du montage maison avec le support double servo attaché sur le boitier du raspberry pi avec des tyraps.


Presentation du montage électrique (utilisation de source 5v externe)


Expliquation du PWM (utilisation des servos)

Cliquer pour voir une expliquation détaillé

Le PWM (Pulse Width Modulation) permet d'émettre un signal carré à une certaine fréquence, 2 états 0 ou 1, la proportion de l'état haut par rapport à l'état bas s'appelle le rapport cyclique.

Par exemple si je veux un signal carré parfait (sinus carré) peut importe la fréquence fixé, je fixe le rapport cyclique à 50%, et donc la durée de l'état haut sera égale à celle de l'état bas.


En python on demande a un gpio du raspberry de fonctionner en PWM de cette façon :

pwm =GPIO.PWM(pinDuGpio, fréquence)


Puis on fixe le rapport cyclique :

pwm.start(20)

Le problème et que on ne connait pas à l'avance les rapport cycliques pour permettre aux 2 servo moteurs de s'axer idéalement sur 180 degrés.


Donc je propose un code python permettant de régler cela

python servo.py -i
def findMechanicalStop():
    middle=10
 
    hPwm =GPIO.PWM(hPin, f)
    vPwm =GPIO.PWM(vPin, f)
#    stop=False
#    while (not stop):
#        pos -=1
#        hPwm.start(pos)
#
#        #ret =ord(raw_input("space to continue, enter to confirm abut: "))
#        ret =ord(sys.stdin.read(1))
#        print(ret)
 
    #non-blocking get input
    stdscr = curses.initscr()
    curses.noecho()
    stdscr.nodelay(1) # set getch() non-blocking
 
    stdscr.addstr(0,0,"Press \"ENTER to confirm abut, space to jump")
    line = 1
 
    toes =["vMin", "vMax", "hMin", "hMax"]
    pwms =[vPwm, vPwm, hPwm, hPwm]
    values =[0, 0, 0, 0]
    sens =[-1, 1, -1, 1]
    for i in range(4):
        pos=middle
 
        try:
            while (1):
                c = stdscr.getch()
                if c == 10: 
                    values[i] =round(pos, 2)
                    break
                elif c == 32:
                    print("space")
                    pos =pos +sens[i] *5
                else:
                    pos =pos +sens[i] *0.1
 
                stdscr.addstr(line, 0, "actual pos "+ str(pos))
                pwms[i].start(pos)
 
                time.sleep(0.1)
 
 
        finally:
            curses.endwin()
 
    for i in range(4):
        print(toes[i], " =", str(values[i]))


Avant de lancer le panorama, il faut preparer un programme python pour diriger la camera selon n'importe quel inclinaison.
Je décide de créer une fonction prenant 2 argument [-180..180] pour autour de l'axe vertical, [0..90] autour de l'axe horizontal.


Voici le code python

python servo.py -m <vertical angle> <horizontal angle>
def angle(vAngle, hAngle):
    vMin=4.0
    vMax=25.0
 
    hMin=3.7
    hMax=50.0
 
 
 
 
    hPwm =GPIO.PWM(hPin, f)
    vPwm =GPIO.PWM(vPin, f)
 
 
    assert (-180 <= vAngle and vAngle <= 180)
    assert (0 <= hAngle and hAngle <= 90)
 
 
    if -90 <= vAngle and vAngle <=  90:
        vPwm.start(vMax -(vAngle +90) *(vMax -vMin) /180.0)
        hPwm.start(hAngle *hMax /180.0 +hMin)
 
    elif vAngle < -90:
        vPwm.start(vMin +(-vAngle -90) *(vMax -vMin) /180.0)
        hPwm.start(hMax -hAngle *(hMax -hMin) /180.0)
 
    else:
        vPwm.start(vMax -(vAngle -90) *(vMax -vMin) /180.0)
        hPwm.start(hMax -hAngle *(hMax -hMin) /180.0)
 
    time.sleep(1)
    hPwm.stop()
    vPwm.stop()

On peut prendre des photos avec une inclinaisons autours de l'axe horizontal de 0 et 45 degre tout autour de l'axe vertical d'un pas de 45 degre avec le code suivant, en n'oubliant pas d'importer le code precedent.


Prendre des photos en mode panoramique


Voici le script shell utilisant le programme python pour prendre des photos en mode panoramique.

./fishEye.sh
for j in 0 45 90 135 180 -135 -90 -45; do
	python servo.py -m $j 0
 
	if [ $j -ge -90 -a $j -le 90 ]; then
		raspistill -t 100 -vf -hf -o photo_"$j"_0.jpg
	else
		raspistill -t 100 -o photo_"$j"_0.jpg
	fi
done

J'utilise raspistill au lieu d'utiliser VideoCapture de opencv, en n'oubliant pas de tourner l'image avec l'option vf et hf de raspistill


Video pour résumer

Utilisation de la commande servo.py et fishEye.sh


Vous pouvez télécharger les sources directement sur le raspberry de cette façon

wget https://github.com/hiergaut/unix/blob/master/raspbian/bin/fishEye.sh
wget https://github.com/hiergaut/unix/blob/master/raspbian/bin/servo.py


Video résumant le init pwm, et le fishEye.




Utilisation de stitching.cpp

Le programme python permettant de merger plusieurs photo etant lent je décide dans la suite de ce tuto d'utiliser du code cpp, un programme deja ecrit qui permettra de créer des panorama cylindrique et stéréographique.

./stitching --features orb --warp stereographic picture*.jpg
./stitching --features orb --warp cylindric picture*.jpg


Maintenant reste plus qu'a fusionner les images recement capturées, pour cela telechargeons le code cpp sur github https://github.com/opencv/opencv/blob/master/samples/cpp/stitching.cpp

g++ stitching.cpp `pkg-config --cflags --libs opencv` -o stitching


Divers mode (features) de fusion des images

  • mode cylindrique:
./stitching --features orb --warp cylindric *.jpg


prise de vue salle de cours, team be traitement d'image sous rpi:


  • mode stereographique:
./stitching --features orb --warp stereographic *.jpg




Timelapse en mode stéréographique

diy/projets/panorama.txt · Dernière modification : 2018/05/24 13:07 de gbouyjou