-
Notifications
You must be signed in to change notification settings - Fork 7
Automated feature classification
Using a new vector layer and polygon and line tools, it's possible to manually trace and recognize features of a particular landscape. For some areas though you may want some more automated process to figure out what's in an image; usually this comes up when there's a need to classify the nature of land cover. So for example, if you have an image with wooded areas, grass, rocks and roads, it's possible to automatically classify between them, although you may get false matches if the appearance is very close (but then you can usually use other tools to reclassify these features).
This process is a bit convoluted and not for the faint of heart, because it's difficult to follow and may generate strange errors or problems during the execution. I've had issues trying to run this on Linux for example, but on Windows it worked ok.
- Start with an image set in QGIS (GeoTiff orthomosaic and a DEM)
- Create a mapset for "GRASS". This is just a local database to work with grass data, so that all vectors and rasters are in known locations.
- Set the "current GRASS region" in QGIS. (you can simply draw a bounding box here over the entire ortho).
- Import the ortho raster from QGIS into "GRASS" as separate R,G,B channels (r.in.gdal.qgis). Let's call this rgb_blue, rgb_red, rgb_green.
- Create a new vector layer and create a new attribute in that layer to describe the kind of data you're mapping. You can also simply use the "ID" field and set the same number there.
- Create polygons over foresty areas and give them the same ID. Create polygons over grassland and give these another ID, but the same for each grass polygon. Etc. for other types of data.
- Import the vector layer into GRASS (v.in.ogr.qgis). Let's call this tm_grass.
- Add the GRASS vector layer you just created in GRASS back into QGIS. You now have 2 vector layers with the same data, one from qgis that you created and the other from GRASS. You can remove the one created in QGIS.
- Convert the GRASS vector layer with polygons to a raster in GRASS (v.to.rast.attr). Let's call this tm_raster.
- Open a "Grass shell" from the GRASS tools. Unfortunately this function is not available from the list.
- Type "i.groups" to create a new imagery group for processing. Let's call the group and subgroup the same: "imagery". Add the "rgb_blue/green/red" raster layers to this imagery group.
- In the grass console, run the "i.gensig" command. Select the imagery groups that were specified, the tm_raster layer used to specify "training data" and specify "sigs" for a layer that receives signature information about the layers.
- Now run "i.maxlik" from the console. This will open a dialog box where you can finally execute the classification run. Again, select the imagery group and subgroup, type the "sigs" layer in (can't select it, which is weird) and specify a name for the output of the classification run ("classes").
- After you hit "run", the layer is complete and you now have a classes layer ready for visualization.
- In QGIS, open the "classes" GRASS layer, which shows the result of the classification step.
Possible result:


Green is the wooded area, greyish the gravel and roads, black things that look like coal and purple the roofs. As you can see, the classification is not perfect, as roof and coal areas appear in places with a lot of shadow, but it does a very good job at detecting wood and grass.
Notice how inside the gravel area it also detects vegetation. There's actually grass there, so that was done correctly.
The purple bits in the gravel area all the way at the bottom turn up because the gravel is very grey there, very similar to the roof definition I specified.
You can now run another operation called "r.neighbors" from GRASS on the result to remove the noisy speckles from the data. If you were to vectorize now, it would show a polygon for each point that differs from its neighboring raster points, which can easily lead to thousands of polygons. A raster operation like neighbors or a "majority filter" in Saga generalize the raster. In "r.neighbors", you can use the "mode" operator with a kernel of 5-9 for example.
Result:

and after vectorization now (~4400 features):
