I wanted to know how many roofs in Brno have plants on them, and if that number is going up. So I counted them from aerial photos over 4 years. The photos are free to use, you only have to say where they came from. Here is how I did it, and where it goes wrong.

The photos

All the photos are Ortofoto ČR from ČÚZK, the Czech state mapping office. ČÚZK flies over half the country each year. Brno is in the east half, so it gets photographed in even years. Each year is one seamless photo mosaic. So there is exactly 1 photo of each place per year, not many.

I kept 4 years: 2016, 2020, 2022 and 2024. I dropped 2018, since it was flown on 12 August, much later than the rest. I dropped years before 2016 too, since their pixels are bigger and blurrier. Each sheet has its own flight date, and the ones below are for the city centre. Other sheets differ by a few days.

year city centre flown pixel size
2016 23 May 0.2 m
2020 1 Jul 0.2 m
2022 5 Jun 0.125 m
2024 17 Jun 0.125 m

The whole city is 76 map sheets, each 2.5 × 2 km. That is about 68 billion pixels in total.

The method, in short

To know which pixels are a roof, I need building outlines. I use RÚIAN, the Czech building register, for the current outlines (garages included). I fuse it with OpenStreetMap history, so for each year I know which buildings existed back then. Only buildings inside Brno city limits count. That is 83 062 buildings.

The photos differ in colour from year to year. So I first colour-match every year to 2024. I fit a simple per-channel line on pixels that should not change, like pavement and grey roofs.

Then 2 detectors look at each roof pixel and say “plant or not”:

  • Pixel colour rule ‒> looks at hue and greenness of each roof pixel. I hand-labelled 460 roofs to tune it. Best score: F1 = 0.82 (precision 0.94, recall 0.73).
  • SAM (Segment Anything Model) ‒> gets each building outline as a box prompt, cuts the roof into parts, then I keep only the green parts. I also tried a text prompt “green roof”. It flagged 86-94% of the whole image as green, so I threw it out.

Both detectors work on the same 0.5 m grid. A roof counts as green when it has at least 1 m² of plants.

Three green roofs in 2024: raw photo with outline, pixel rule mask in green, SAM mask in blue
Source: own work, Ortofoto ČR © ČÚZK. Left: 2024 photo with the building outline in red. Middle: pixel colour rule (green). Right: SAM (blue). Row A: a small roof garden in the centre. Row B: the largest roof both detectors agree on. Row C: a lawn on top of an underground building. Note how SAM spills onto tree tops next to the roof in row C.
Centre of Brno in 2024 with building outlines in red and green roof pixels found by either detector in green
Source: own work, Ortofoto ČR © ČÚZK. The centre 2.5 km of Brno in 2024. Red: building outlines. Green: roof plants found by the pixel rule or SAM.

The pixel rule hugs the true edge of the plants. SAM fills whole roof parts, and now and then bleeds onto a tree next door.

Building up the count

First, a simple count. How many buildings have at least 1 m² of plants on the roof each year? There are 3 lines: the pixel rule, SAM, and both. Both means each detector finds 1 m² on its own.

Buildings with a green roof per year, pixel rule vs SAM vs both
Source: own work.
year pixel rule SAM both
2016 1 124 1 740 596
2020 3 424 4 011 1 911
2022 5 997 4 967 2 926
2024 4 673 4 037 2 292

The jump from 2016 is partly real and partly the photo. 2016 was flown in late May. Plants are still pale then. And 1 photo is noisy. A shadow, a wet patch or a tarp can look green.

So I add a stricter rule. A roof only counts if it was green in 2 photos in a row. That is this year and the one before.

Buildings green in two consecutive photos
Source: own work. 2 photos in a row.

And a stricter one still: green in 3 photos in a row.

Buildings green in three consecutive photos
Source: own work. 3 photos in a row.
year both, 2 in a row both, 3 in a row
2020 300 –
2022 1 082 195
2024 1 478 670

Now the chart I care about. It counts square metres, not roofs. It only uses roofs that both detectors flag in 2 photos in a row. And it comes as a band. The lower bound is the area where SAM and the pixel rule agree. The upper bound is the area where either one says green.

Green roof square metres over consecutive both-detector buildings, lower and upper bound
Source: own work. Band = from where the detectors agree (lower) to where either one says green (upper). Dashed lines = each detector alone.
year lower bound upper bound
2020 37 650 m² 69 337 m²
2022 42 750 m² 84 226 m²
2024 59 093 m² 109 024 m²

So in 2024 Brno had 5.9 to 10.9 hectares of stable green roof. That is about 8 to 15 football pitches. The band grew by about half since 2020, at both ends. The trend is up, even if the exact number is soft.

The 3-in-a-row version is smaller, 3.5 to 6.5 hectares in 2024. But it points the same way.

Green roof square metres over three consecutive photos
Source: own work. 3 photos in a row.

Where this goes wrong

The biggest problem: I have no true history of building outlines. OSM history helps, but it is patchy. Where a tree stood before a building was built, the older photo shows tree tops inside today’s outline. My detectors do not know if a plant is on a roof or next to it. So the tree counts as a green roof.

Same outline in 2016 with trees on an empty lot, and in 2024 with a new building and a real green roof
Source: own work, Ortofoto ČR © ČÚZK. Left: 2016, the 2024 outline sits on a building lot full of trees. Right: 2024, a new building with a real green roof. Without the OSM history, 2016 would count the trees as a green roof.

Other things that go wrong:

  • Trees hanging over real roofs count as roof plants.
  • Different months. 2016 was late May, 2020 was July. Plants look different across the season, and so does the count.
  • Sedum roofs are often yellow, red or brown, not green. A colour rule misses them. So the real number is likely higher.
  • JPEG artefacts and pixel size (0.2 m vs 0.125 m) change what the detectors see from year to year.
  • The pixel rule was tuned on the city centre only.
  • No height data. With LiDAR I could check that the green stuff is really up on the roof.
  • Green tarps, pools and painted roofs fool a colour rule now and then.

Future work

The detectors should be fine-tuned on roofs picked by hand, by a professional with care, not just by me in an evening. The 1 m² threshold is very low. Moving it up to 10 or 20 m² would cut most of the noise from tarps and tree tips. And a height model from LiDAR would settle if the green is on the roof at all.

Data and attribution

  • Aerial photos: Ortofoto ČR © ČÚZK ‒> geoportal.cuzk.cz
  • Building outlines: RÚIAN © ČÚZK
  • Building history: © OpenStreetMap contributors, ODbL