An attribute table is basically a table that has a value - (pixel) count relation for all your classes. I'll assume you have the raster data as a numpy array before writing it as a GeoTIFF. Look at the following example and imagine that arr is the numpy array with the raster data.
import numpy as np
np.random.seed(99)
arr = np.random.randint(1, 11, size=(400, 300))
values, counts = np.unique(arr, return_counts=True)
If you take a look at values and counts you will see they both are 1D numpy arrays containing all unique values of your array and their frequency, respectively.
>>> values
array([ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
>>> counts
array([12078, 11871, 11998, 12004, 12077, 11918, 11951, 12102, 11829,
12172], dtype=int64
Now you can use pandas in order to create a DataFrame and then export it as a CSV file.
import pandas as pd
df = pd.DataFrame({'VALUE': values, 'COUNT': counts})
The DataFrame looks like this:
>>>df
VALUE COUNT
0 1 12078
1 2 11871
2 3 11998
3 4 12004
4 5 12077
5 6 11918
6 7 11951
7 8 12102
8 9 11829
9 10 12172
Note that you can also add additional fields to your attribute table. For exmaple, suppose you want to store also the name of each class and not just their codes (integers). You could create a dict with the name of each class and then map it to a new column of your DataFrame named class.
class_map = {
1: 'Evergreen Needleleaf Forests',
2: 'Evergreen Broadleaf Forests',
3: 'Deciduous Needleleaf Forests',
4: 'Deciduous Broadleaf Forests',
5: 'Mixed Forests',
6: 'Closed Shrublands',
7: 'Open Shrublands',
8: 'Woody Savannas',
9: 'Savannas',
10: 'Grasslands'
}
df['CLASS'] = df['VALUE'].map(class_map)
Now the DataFrame looks like this:
>>>df
VALUE COUNT CLASS
0 1 12078 Evergreen Needleleaf Forests
1 2 11871 Evergreen Broadleaf Forests
2 3 11998 Deciduous Needleleaf Forests
3 4 12004 Deciduous Broadleaf Forests
4 5 12077 Mixed Forests
5 6 11918 Closed Shrublands
6 7 11951 Open Shrublands
7 8 12102 Woody Savannas
8 9 11829 Savannas
9 10 12172 Grasslands
Finally, you just have to export the DataFrame to a CSV (or other format you consider more appropiate) file. Note that you can just ignore the index by passing index=False as an argument. Here I just give the index a new label, just like ArcGIS does.
df.to_csv('raster_attribute_table.csv', index_label='OID')
If you are using
gdal to write the GeoTIFF, the process could be as simple as:
# create an empty Raster Attribute Table and populate it using the values, their frequency and their class
rat = gdal.RasterAttributeTable()
rat.CreateColumn('VALUE', gdal.GFT_Integer, gdal.GFU_Generic)
rat.CreateColumn('COUNT', gdal.GFT_Integer, gdal.GFU_Generic)
rat.CreateColumn('CLASS', gdal.GFT_String, gdal.GFU_Generic)
for value, count in zip(values, counts):
rat.SetValueAsInt(i, 0, value)
rat.SetValueAsInt(i, 1, count)
rat.SetValueAsInt(i, 2, class_map[value])
# save the raster atttribute table to the band (assume ds is the gdal DataSet)
band = ds.GetRasterBand(1)
band.SetDefaultRAT(rat)
band.FlushCache()
del ds, band