{"id":19233611,"url":"https://github.com/out-of-cheese-error/mars2020api","last_synced_at":"2025-04-21T04:33:24.144Z","repository":{"id":42769241,"uuid":"341872140","full_name":"out-of-cheese-error/mars2020api","owner":"out-of-cheese-error","description":"Unofficial Python API for NASA's Mars rover images and metadata (with a touch of image processing)","archived":false,"fork":false,"pushed_at":"2021-03-10T14:25:22.000Z","size":140894,"stargazers_count":7,"open_issues_count":0,"forks_count":1,"subscribers_count":3,"default_branch":"main","last_synced_at":"2025-04-01T09:47:11.485Z","etag":null,"topics":["mars-rover","nasa","nasa-api","perseverance","rover"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-3-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/out-of-cheese-error.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2021-02-24T11:04:59.000Z","updated_at":"2024-12-21T17:55:45.000Z","dependencies_parsed_at":"2022-09-15T02:20:39.161Z","dependency_job_id":null,"html_url":"https://github.com/out-of-cheese-error/mars2020api","commit_stats":null,"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/out-of-cheese-error%2Fmars2020api","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/out-of-cheese-error%2Fmars2020api/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/out-of-cheese-error%2Fmars2020api/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/out-of-cheese-error%2Fmars2020api/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/out-of-cheese-error","download_url":"https://codeload.github.com/out-of-cheese-error/mars2020api/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":249996234,"owners_count":21358094,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["mars-rover","nasa","nasa-api","perseverance","rover"],"created_at":"2024-11-09T16:11:17.806Z","updated_at":"2025-04-21T04:33:19.156Z","avatar_url":"https://github.com/out-of-cheese-error.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Perseverance Rover Image API (with a touch of image processing)\n\nPython API for downloading [NASA's Perseverance Rover](https://mars.nasa.gov) images and metadata.\n\nCombines JSON metadata about the camera and image with \n[image filename metadata](https://mastcamz.asu.edu/decoding-the-raw-publicly-released-mastcam-z-image-filenames/) about the instrument.\n\n## Installation\n```sh\npip install git+https://github.com/out-of-cheese-error/mars2020api\n```\n\n## Usage\n\nSome imports and a helper function to plot a grid of images:\n\n```python\nimport PIL\nimport numpy as np\n\nfrom mars2020 import image_api\n```\n\nFetch all of NASA's Mars data (this just gets all the image metadata, the actual images are downloaded lazily when requested)\n\n```python\nall_data = image_api.ImageDataCollection.fetch_all_mars2020_imagedata()\n```\n\n## Collage\n\nDuring the descent, the EDL_RDCAM camera continously took a ton of pictures that were perfect for collaging together. \n\n```python\nimages = [\n    x for x in all_data.images \n    if x.camera_type.instrument == \"EDL_RDCAM\" \n    and not x.instrument_metadata.thumbnail # Not a thumbnail pic\n    and x.instrument_metadata.filter_number == \"E\"\n]\n```\n\nWe used Photoshop's Photomerge algorithm (had to subsample to a 100 images to keep Photoshop from crashing) to get this absolute beauty:\n\n[comment]: \u003c\u003e (![collage EDL_RDCAM Filter E]\u0026#40;./images/collage_EDL_RDCAM_E.png\u0026#41;)\n\n\n[comment]: \u003c\u003e (And similarly for `filter_number = F`:)\n\n![collage EDL_RDCAM Filter F](./images/collage_EDL_RDCAM_F.png)\n\u003c!-- #endregion --\u003e\n\n## Panorama\n\nNASA released a beautiful [360-degree panorama](https://mars.nasa.gov/resources/25640/mastcam-zs-first-360-degree-panorama/) shot by the Mastcam-Z cameras on board. We tried to replicate this by getting the same images and running it through Photomerge again.\n\n\n[NASA's claims to have used](https://www.nasa.gov/offices/oct/home/tech_life_gigapan.html) the [GigaPan software](http://gigapan.com/) for this but we couldn't really get this to work probably because of the ordering of the images.\n\n```python\nimages = [\n        x\n        for x in all_data.images\n        if x.camera_type.instrument == \"MCZ_LEFT\" # MastCam Z - Left\n        and not x.instrument_metadata.thumbnail # Not a thumbnail picture\n        and x.instrument_metadata.filter_number == \"F\"\n        and x.date_received_on_earth_utc.day == 24 # Received on 24th Feb 2021\n    ]\n```\n\nHere are some results from Photomerge!\n\n![panorama_spherical](./images/panorama_MCZ_LEFT_spherical.jpg)\n\n![panorama](./images/panorama_MCZ_LEFT.jpg)\n\n\n## RGB\n\n\nA bunch of the cameras took separate R, G, and B channels for each image. We matched these together with the `camera_vector` information and composited them.\n\n```python\ndef match_rgb(r_image: image_api.ImageData, g_images: [image_api.ImageData], b_images: [image_api.ImageData]):\n    vector = r_image.camera_type.camera_vector\n    g_image = [x for x in g_images if x.camera_type.camera_vector == vector]\n    if len(g_image) == 0:\n        return None\n    b_image = [x for x in b_images if x.camera_type.camera_vector == vector]\n    if len(b_image) == 0:\n        return None\n    return (r_image, g_image[0], b_image[0])\n```\n\n```python\nrgb_matches = []\nfor cam_type in all_data.instrument_names:\n    cam_images = [x for x in all_data.images if x.camera_type.instrument == cam_type and not x.instrument_metadata.thumbnail]\n    filters = set(x.instrument_metadata.filter_number for x in cam_images)\n    if not set(\"RGB\").difference(filters):\n        print(cam_type)\n        r_images = [x for x in cam_images if x.instrument_metadata.filter_number == \"R\"]\n        g_images = [x for x in cam_images if x.instrument_metadata.filter_number == \"G\"]\n        b_images = [x for x in cam_images if x.instrument_metadata.filter_number == \"B\"]\n        rgb_matches += [match_rgb(r_image, g_images, b_images) for r_image in r_images]\n\nrgb_images = []\nfor m in rgb_matches:\n    rgb_image = np.zeros((m[0].dimension[1], m[0].dimension[0], 3), dtype=np.int8)\n    for i in range(3):\n        rgb_image[:, :, i] = np.asarray(m[i].image_data.split()[i])\n    rgb_images.append(PIL.Image.fromarray(rgb_image, \"RGB\"))\n```\n\nAn example of what that looks like:\n\n![RGB image example](./images/m6.png)\n\n# Demosaicing/DeBayering\n\n* Implemented! example soon\n\n# Grid images\n\n* Implemented! example soon\n* GUI app in releases to form grid images with debayering\n\nSee the rest, as well as high-res versions of all of the above in [this Flickr album](https://flic.kr/s/aHsmUybm5N)\n\nVisit our [blog post](https://out-of-cheese-error.netlify.app/perseverance) for more details .\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fout-of-cheese-error%2Fmars2020api","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fout-of-cheese-error%2Fmars2020api","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fout-of-cheese-error%2Fmars2020api/lists"}