Classifying plants & creating an excursion diary

February 3, 2025

About 3 years ago, I became interested in learning more about plant taxonomy.

In Switzerland, there is a certification, starting with the most common 200 plants, which you have to know by their latin name (family and species). I haven't attempted it yet, but I've been learning at least parts of the list by random plant encounters on the way.

For classifying plants out in the field, the Pl@ntNet project turned out to be immensely useful, as it allows to give a classification, including certainty.

I like going on a hike, but trying to classify more than a handful of plants on the way turned out to be pretty tedious work, and I don't like to stumble too much of the way with my smartphone out.

When coming back from a hike I got the idea of automatically creating a plant diary afterwards - with my photos, all the information was there!

It looks like this:

Using the Pl@ntNet API

Besides an App, Pl@ntNet also provides an API, with some generous limitation (last time I checked 500 calls per day were okay). If you register on identify.plantnet.org, you can get a free API key to make classification requests.

I decided to use Python to make multiple requests for a list of images provided:

def get_species(filename: str) -> List[str]:
  files = {'images': open(filename, 'rb')}
  response = requests.post(base_url, files=files, data={'organs': ['flower']}) # or 'leaf'
  json_result = json.loads(response.text)

  names = []
  for result in json_result['results']:
    score = result['score']
    # Ignore results with certainty below 5%
    if score < 0.05:
      break
    lat_name = result['species']['scientificNameWithoutAuthor']
    common_name = '-'
    if len(result['species']['commonNames']) > 0:
      common_name = result['species']['commonNames'][0]
    names.append([lat_name + ' (' + str(score * 100)[:4] + '%)', common_name])
  return names

Using this script, the latin name and common names will be collected and returned, and added to the image as information.

Reading GPS info

I decided to include a small map with Leaflet, and set a pin on the first GPS coordinates found in any image.

With PIL it is pretty easy to read out the EXIF metadata from images:

from PIL import Image

def get_exif(filename):
    exif = Image.open(filename)._getexif()

    if exif is not None:
        # 34853 corresponds to 'GPSInfo'
        entry = list(filter(lambda x: x[0] == 34853, exif.items()))
        gps_info = entry[0][1]
        print('gps_info:', gps_info)

exif = get_exif('img.jpg')

This would return something like this:

gps_info: {1: 'N', 2: (46.0, 26.0, 49.63992), 3: 'E', 4: (7.0, 53.0, 20.6736), 5: 0, 6: 2076.0}

(This can be converted to the more common decimal number format, see script below.)

Python script

Combining the above with a simple HTML template, a single Python script is enough to generate a plant diary for an excursion, as seen above. I added some image transformations to downscale the images and put them into an img_scaled folder, suitable for serving them for web. You can visit the diary page from above here: http://kleemans.ch/static/plant-diary/.

You can find the Python script here: Github Gist

Happy classifying!