Get real-time photos from the Anafi in sphinx

I am simulating an anafi drone in a sphinx environment and I am trying to get real time data from the drones camera through the olympe interface.

I attempt to do this using the following code:

import olympe

# Connection
drone = olympe.Drone("10.202.0.1")
drone.connection()

camera = olympe.messages.camera 
camera.set_camera_mode(0, 'photo')
camera.set_photo_mode(0, 'time_lapse', 'full_frame', 'jpeg', 'burst_14_over_4s', 'preset_1ev', 5) 
print("Photo state :", drone.get_state(camera.photo_state))  
camera.take_photo(0) 

The output of the print command is:
Photo state : OrderedDict([(0, OrderedDict([(ā€˜cam_idā€™, 0), (ā€˜availableā€™, <availability.not_available: 0>), (ā€˜stateā€™, <state.inactive: 0>)]))])

This shows that the camera is neither available nor active, and thus the take_photo command has no effect.

How do I make this work?
Do I need to link the camera object to the drone object somehow?

Thank you!

Hello,

You have to pass the command message to the drone object! Here you are just creating some command messages without sending them to the drone.

For the camera_mode it should be:

 drone(set_camera_mode(cam_id=0, value="photo")).wait()

and not

camera.set_camera_mode(0, 'photo')

Please find below an Olympe script example to take some photo and download them using python/requests. I hope this will help you getting started.

from olympe.messages.camera import (
    set_camera_mode,
    set_photo_mode,
    take_photo,
    photo_progress,
)
import olympe
import os
import re
import requests
import shutil
import tempfile
import xml.etree.ElementTree as ET


# Drone IP
ANAFI_IP = "192.168.42.1"

# Drone web server URL
ANAFI_URL = "http://{}/".format(ANAFI_IP)

# Drone media web API URL
ANAFI_MEDIA_API_URL = ANAFI_URL + "api/v1/media/medias/"

XMP_TAGS_OF_INTEREST = (
    "CameraRollDegree",
    "CameraPitchDegree",
    "CameraYawDegree",
    "CaptureTsUs",
    # NOTE: GPS metadata is only present if the drone has a GPS fix
    # (i.e. they won't be present indoor)
    "GPSLatitude",
    "GPSLongitude",
    "GPSAltitude",
)


def take_photo_burst(drone):
    # take a photo burst and get the associated media_id
    photo_saved = drone(photo_progress(result="photo_saved", _policy="wait"))
    drone(take_photo(cam_id=0)).wait()
    photo_saved.wait()
    media_id = photo_saved.received_events().last().args["media_id"]

    # download the photos associated with this media id
    media_info_response = requests.get(ANAFI_MEDIA_API_URL + media_id)
    media_info_response.raise_for_status()
    download_dir = tempfile.mkdtemp()
    for resource in media_info_response.json()["resources"]:
        image_response = requests.get(ANAFI_URL + resource["url"], stream=True)
        download_path = os.path.join(download_dir, resource["resource_id"])
        image_response.raise_for_status()
        with open(download_path, "wb") as image_file:
            shutil.copyfileobj(image_response.raw, image_file)

        # parse the xmp metadata
        with open(download_path, "rb") as image_file:
            image_data = image_file.read()
            image_xmp_start = image_data.find(b"<x:xmpmeta")
            image_xmp_end = image_data.find(b"</x:xmpmeta")
            image_xmp = ET.fromstring(image_data[image_xmp_start : image_xmp_end + 12])
            for image_meta in image_xmp[0][0]:
                xmp_tag = re.sub(r"{[^}]*}", "", image_meta.tag)
                xmp_value = image_meta.text
                # only print the XMP tags we are interested in
                if xmp_tag in XMP_TAGS_OF_INTEREST:
                    print(resource["resource_id"], xmp_tag, xmp_value)


def setup_photo_burst_mode(drone):
    drone(set_camera_mode(cam_id=0, value="photo")).wait()
    # For the file_format: jpeg is the only available option
    # dng is not supported in burst mode
    drone(
        set_photo_mode(
            cam_id=0,
            mode="burst",
            format="rectilinear",
            file_format="jpeg",
            burst="burst_14_over_1s",
            bracketing="preset_1ev",
            capture_interval=0.0,
        )
    ).wait()


def main(drone):
    drone.connection()
    setup_photo_burst_mode(drone)
    take_photo_burst(drone)


if __name__ == "__main__":
    with olympe.Drone(ANAFI_IP, loglevel=0) as drone:
        main(drone)

As you can see in this example to list available medias one the drone you have to use the REST/JSON media API (that is not part of Olympe): http://192.168.42.1/api/v1/media/medias for a physical drone and http://10.202.0.1/api/v1/media/medias for a simulated drone.

This API endpoint returns a JSON data structure which is a list of media that might look like this:

[
  {
    "media_id": "10000001",
    "type": "PHOTO",
    "datetime": "19700101T000949+0000",
    "size": 4338126,
    "run_id": "D42C35807315EDB8893F0C577975D02E",
    "thumbnail": "/data/thumbnails/100000010001.JPG",
    "resources": [
      {
        "media_id": "10000001",
        "resource_id": "100000010001.JPG",
        "type": "PHOTO",
        "format": "JPG",
        "datetime": "19700101T000949+0000",
        "size": 4338126,
        "url": "/data/media/100000010001.JPG",
        "width": 4608,
        "height": 3456,
        "thumbnail": "/data/thumbnails/100000010001.JPG",
        "md5": ""
      }
    ],
    "photo_mode": "SINGLE" } ]

each media can have multiple resource (when photo_mode=ā€œburstā€ for example).
To download a media resource, you just have to HTTP GET the resource URL for example:
http://192.168.42.1/data/media/100000010001.JPG
To delete a media resource, just send an HTTP DELETE request to the same resource URL.

The (media) web API documentation should be published by Parrot soon. Thanks

1 Like

Can you share your script here?
It seems that you are not connected to the drone. (Did you forget a drone.connection() somewhere?)

The ANAFI_IP should be ā€œ10.202.0.1ā€ for the simulation and not ā€œ192.168.42.1ā€

First of all, thank you for your answer! That does indeed put me on the right track. However, when I tried it, somehow the command ā€œphoto_saved.wait()ā€ never completes. Could you please shed some light as to why this is the case?

Thanks.

OK, I think thatā€™s because the simulated ANAFI firmware (ANAFI_IP = "10.202.0.1") does not support every photo and video mode supported by a physical ANAFI (ANAFI_IP = "192.168.42.1"). The ā€œphoto_savedā€ event is never received from the drone so the photo_saved.wait() waits indefinitely for it. Instead of waiting for nothing you should handle the error with something like:

if not photo_saved.wait(_timeout=5.).success():
    # raise or return an error here

If you want to test the photo capture with a simulated drone you should stick the the ā€œsingleā€ photo mode instead of the ā€œburstā€ photo mode (i.e. replace ā€œburstā€ mode by ā€œsingleā€ in the example above) :

    drone(
        set_photo_mode(
            cam_id=0,
            mode="single",  # any other mode might not be supported on a simulated drone
            format="rectilinear",
            file_format="jpeg",
            burst="burst_14_over_1s",  # this gets ignored in "single" photo mode
            bracketing="preset_1ev",
            capture_interval=0.0,  # this gets ignored in "single" photo mode
        )
    ).wait()

As for the photo metadata with a simulated drone, you should not expect them to be representative of what you would get with a physical ANAFI.

Unfortunately, also with the single photo mode, the photo_saved.wait() command still times outā€¦ and then of course nothing could be found on the media url.
I really hope there is some available way to do it in the simulation.

Maybe an alternative path if the above doesnā€™t work out: Is it possible to process the images directly with a callback function (without needing to save them)?

Thanks

Hi Melsa,

I had a similar issue. It is weird because I can download a specific image file in the URL(ex: http://10.202.0.1/data/media/100000010001.JPG). However, it shows nothing when I access to (http://10.202.0.1/data/media/). I tried to download all the files in the link, but the link shows nothing.
Anyone any ideas?

Hi,

As stated by @ndessart in his post, the way to get the media list is via a GET call to the /api/v1/media/medias API.

Getting /data/media/ directly returns nothing by design.

Regards,
Nicolas.

Hi Nicolas,

According to his post, he said that HTTP GET for /data/media/ as below

< To download a media resource, you just have to HTTP GET the resource URL for example:
http://192.168.42.1/data/media/100000010001.JPG >

Anyway, I tried both and failed.
If I specify the file name like ā€œ/data/media/1000001.JPGā€, it has no issue.
However, I cannot download the entire folder ā€œ/data/media/ā€ and it says there is nothing in the folder even though I accessed the file from there.

Yeah well, the problem is now still the the photo does not even get saved (as stated in my above reply). Any news on that?

Thank you!

Melsa,

That didnā€™t happen to me, but I think it also has issues in saving photos.
It initially saved images like 10 to 30, but it stopped saving images and error messages appeared which said that the url canā€™t be reached.
It is wierd because it saved like 10 to 30 times with no problem and said like that all of a sudden.
Did you check the temporary folder in your computer?
I also have the same images in the temp folder.

@ndessart @Nicolas Could you please look at this issue?