1 """!@brief Interface between qgisForm and function_historical_map.py
2 ./***************************************************************************
5 Mapping old landcover (specially forest) from historical maps
9 copyright : (C) 2016 by Karasiak & Lomellini
10 email : karasiak.nicolas@gmail.com
11 ***************************************************************************/
13 /***************************************************************************
15 * This program is free software; you can redistribute it and/or modify *
16 * it under the terms of the GNU General Public License as published by *
17 * the Free Software Foundation; either version 2 of the License, or *
18 * (at your option) any later version. *
20 ***************************************************************************/
23 import function_dataraster
as dataraster
26 import accuracy_index
as ai
28 import gmm_ridge
as gmmr
30 from scipy
import ndimage
31 from osgeo
import gdal, ogr, osr
32 from PyQt4.QtGui
import QProgressBar, QApplication
33 from PyQt4
import QtCore
34 from qgis.utils
import iface
35 from qgis.core
import QgsMessageLog
38 """!@brief Filter a raster with median and closing filter.
40 Filter class to isolate the forest, delete dark lines and fonts from Historical Map
43 inImage : image name to filter ('text.tif',str)
44 outName : outname name of the filtered file (str)
45 inShapeGrey : Size for the grey closing convolution matrix (odd number, int)
46 inShapeMedian : Size for the median convolution matrix (odd number, int)
49 Nothing except a raster file (outName)
53 def __init__(self,inImage,outName,inShapeGrey,inShapeMedian,iterMedian):
56 data,im=dataraster.open_data_band(inImage)
58 print 'Cannot open image'
61 proj = data.GetProjection()
62 geo = data.GetGeoTransform()
66 maxStep=d+d*iterMedian
70 print 'Failed loading progress Bar'
74 outFile=dataraster.create_empty_tiff(outName,im,d,geo,proj)
76 print 'Cannot write empty image '+outName
82 filterProgress.addStep()
83 temp = data.GetRasterBand(i+1).ReadAsArray()
86 print 'Cannot get rasterband'+i
87 QgsMessageLog.logMessage(
"Problem reading band "+str(i)+
" from image "+inImage)
90 temp = ndimage.morphology.grey_closing(temp,size=(inShapeGrey,inShapeGrey))
92 print 'Cannot filter with Grey_Closing'
93 QgsMessageLog.logMessage(
"Problem with Grey Closing")
95 for j
in range(iterMedian):
97 filterProgress.addStep()
98 temp = ndimage.filters.median_filter(temp,size=(inShapeMedian,inShapeMedian))
100 print 'Cannot filter with Median'
101 QgsMessageLog.logMessage(
"Problem with median filter")
105 out=outFile.GetRasterBand(i+1)
110 QgsMessageLog.logMessage(
"Cannot save band"+str(i)+
" on image" + outName)
112 filterProgress.reset()
115 """!@brief Learn model with a shp file and a raster image.
118 inRaster : Filtered image name ('sample_filtered.tif',str).
119 inVector : Name of the training shpfile ('training.shp',str).
120 inField : Column name where are stored class number (str).
123 outModel : Name of the model to save, will be compulsory for the 3rd step (classifying).
124 outMatrix : Default the name of the file inRaster(minus the extension)_inClassifier_inSeed_confu.csv (str).
125 inClassifier : GMM,KNN,SVM, or RF. (str).
132 def __init__(self,inRaster,inVector,inField='Class',inSplit=0.5,inSeed=0,outModel=None,outMatrix=None,inClassifier='GMM'):
135 learningProgress=
progressBar(
'Learning model...',6)
140 temp_folder = tempfile.mkdtemp()
141 filename = os.path.join(temp_folder,
'temp.tif')
143 data = gdal.Open(inRaster,gdal.GA_ReadOnly)
144 shp = ogr.Open(inVector)
148 QgsMessageLog.logMessage(
"Problem with making tempfile or opening raster or vector")
152 driver = gdal.GetDriverByName(
'GTiff')
153 dst_ds = driver.Create(filename,data.RasterXSize,data.RasterYSize, 1,gdal.GDT_Byte)
154 dst_ds.SetGeoTransform(data.GetGeoTransform())
155 dst_ds.SetProjection(data.GetProjection())
156 OPTIONS =
'ATTRIBUTE='+inField
157 gdal.RasterizeLayer(dst_ds, [1], lyr,
None,options=[OPTIONS])
158 data,dst_ds,shp,lyr=
None,
None,
None,
None
160 QgsMessageLog.logMessage(
"Cannot create temporary data set")
164 X,Y = dataraster.get_samples_from_roi(inRaster,filename)
166 QgsMessageLog.logMessage(
"Problem while getting samples from ROI with"+inRaster)
172 os.rmdir(temp_folder)
175 X,M,m = self.
scale(X)
178 learningProgress.addStep()
186 x = sp.array([]).reshape(0,d)
187 y = sp.array([]).reshape(0,1)
188 xt = sp.array([]).reshape(0,d)
189 yt = sp.array([]).reshape(0,1)
191 sp.random.seed(inSeed)
193 t = sp.where((i+1)==Y)[0]
196 rp = sp.random.permutation(nc)
197 x = sp.concatenate((X[t[rp[0:ns]],:],x))
198 xt = sp.concatenate((X[t[rp[ns:]],:],xt))
199 y = sp.concatenate((Y[t[rp[0:ns]]],y))
200 yt = sp.concatenate((Y[t[rp[ns:]]],yt))
203 QgsMessageLog.logMessage(
"Problem while learning if SPLIT <1")
208 learningProgress.addStep()
211 if inClassifier ==
'GMM':
218 QgsMessageLog.logMessage(
"Cannot train with GMMM")
221 from sklearn
import neighbors
222 from sklearn.svm
import SVC
223 from sklearn.ensemble
import RandomForestClassifier
224 from sklearn.cross_validation
import StratifiedKFold
225 from sklearn.grid_search
import GridSearchCV
227 QgsMessageLog.logMessage(
"You must have sklearn dependencies on your computer. Please consult the documentation")
229 if inClassifier ==
'RF':
230 param_grid_rf = dict(n_estimators=5**sp.arange(1,5),max_features=sp.arange(1,4))
232 cv = StratifiedKFold(y, n_folds=5)
233 grid = GridSearchCV(RandomForestClassifier(), param_grid=param_grid_rf, cv=cv,n_jobs=-1)
235 model = grid.best_estimator_
237 elif inClassifier ==
'SVM':
238 param_grid_svm = dict(gamma=2.0**sp.arange(-4,4), C=10.0**sp.arange(-2,5))
240 cv = StratifiedKFold(y, n_folds=5)
241 grid = GridSearchCV(SVC(), param_grid=param_grid_svm, cv=cv,n_jobs=-1)
243 model = grid.best_estimator_
245 elif inClassifier ==
'KNN':
246 param_grid_knn = dict(n_neighbors = sp.arange(1,50,5))
248 cv = StratifiedKFold(y, n_folds=5)
249 grid = GridSearchCV(neighbors.KNeighborsClassifier(), param_grid=param_grid_knn, cv=cv,n_jobs=-1)
251 model = grid.best_estimator_
254 print 'Cannot train with Classifier '+inClassifier
255 QgsMessageLog.logMessage(
"Cannot train with Classifier"+inClassifier)
259 learningProgress.prgBar.setValue(5)
265 yp = model.predict(xt)
266 CONF = ai.CONFUSION_MATRIX()
267 CONF.compute_confusion_matrix(yp,yt)
268 sp.savetxt(outMatrix,CONF.confusion_matrix,delimiter=
',',fmt=
'%1.4d')
272 if outModel
is not None:
273 output = open(outModel,
'wb')
274 pickle.dump([model,M,m], output)
277 learningProgress.addStep()
280 learningProgress.reset()
281 learningProgress=
None
283 """!@brief Function that standardize the data.
290 x: the standardize data
295 if not sp.issubdtype(x.dtype,float):
299 xs = sp.empty_like(x)
302 M,m = sp.amax(x,axis=0),sp.amin(x,axis=0)
306 xs[:,i] = 2*(x[:,i]-m[i])/den[i]-1
313 """!@brief Classify image with learn clasifier and learned model
315 Create a raster file, fill hole from your give class (inClassForest), convert to a vector,
316 remove parcel size which are under a certain size (defined in inMinSize) and save it to shp.
319 inRaster : Filtered image name ('sample_filtered.tif',str)
320 inModel : Output name of the filtered file ('training.shp',str)
321 outShpFile : Output name of vector files ('sample.shp',str)
322 inMinSize : min size in acre for the forest, ex 6 means all polygons below 6000 m2 (int)
323 TODO inMask : Mask size where no classification is done |||| NOT YET IMPLEMENTED
324 inField : Column name where are stored class number (str)
325 inNODATA : if NODATA (int)
326 inClassForest : Classification number of the forest class (int)
329 SHP file with deleted polygon below inMinSize
339 model = open(inModel,
'rb')
341 print "Model not load"
342 QgsMessageLog.logMessage(
"Model : "+inModel+
" is none")
344 tree,M,m = pickle.load(model)
347 QgsMessageLog.logMessage(
"Error while loading the model : "+inModel)
351 temp_folder = tempfile.mkdtemp()
352 rasterTemp = os.path.join(temp_folder,
'temp.tif')
354 QgsMessageLog.logMessage(
"Cannot create temp file "+rasterTemp)
357 predictedImage=self.
predict_image(inRaster,rasterTemp,tree,
None,-10000,SCALE=[M,m])
359 QgsMessageLog.logMessage(
"Problem while predicting "+inRaster+
" in temp"+rasterTemp)
361 return predictedImage
366 data,im=dataraster.open_data_band(rasterTemp)
368 proj = data.GetProjection()
369 geo = data.GetGeoTransform()
372 rasterTemp=rasterTemp+
'f'
374 QgsMessageLog.logMessage(
"Cant opening band")
376 outFile=dataraster.create_empty_tiff(rasterTemp,im,1,geo,proj)
378 QgsMessageLog.logMessage(
"Cannot create empty tif in "+rasterTemp)
381 temp = data.GetRasterBand(1).ReadAsArray()
384 temp[temp!=inClassForest]=0
385 temp[temp==inClassForest]=1
387 temp = ndimage.morphology.binary_fill_holes(temp)
390 QgsMessageLog.logMessage(
"Cannot fill binary holes")
396 out=outFile.GetRasterBand(1)
403 print 'Cannot save '+rasterTemp
409 sourceRaster = gdal.Open(rasterTemp)
410 band = sourceRaster.GetRasterBand(1)
411 driver = ogr.GetDriverByName(
"ESRI Shapefile")
413 if os.path.exists(outShpFile):
414 driver.DeleteDataSource(outShpFile)
416 outDatasource = driver.CreateDataSource(outShpFile)
418 srs = osr.SpatialReference()
419 srs.ImportFromWkt( sourceRaster.GetProjectionRef() )
421 outLayer = outDatasource.CreateLayer(outShpFile,srs)
424 newField = ogr.FieldDefn(
'Class', ogr.OFTInteger)
425 outLayer.CreateField(newField)
426 gdal.Polygonize(band,
None,outLayer, 0,[],callback=
None)
427 outDatasource.Destroy()
431 QgsMessageLog.logMessage(
"Cannot vectorize "+rasterTemp)
435 ioShpFile = ogr.Open(outShpFile, update = 1)
437 lyr = ioShpFile.GetLayerByIndex(0)
440 field_defn = ogr.FieldDefn(
"Area", ogr.OFTReal )
441 lyr.CreateField(field_defn)
446 geom = i.GetGeometryRef()
447 area = round(geom.GetArea())
450 i.SetField(
"Area", area )
453 if area<inMinSize
or i.GetField(
'Class')!=1:
454 lyr.DeleteFeature(i.GetFID())
457 QgsMessageLog.logMessage(
"Cannot add area and remove it if size under"+inMinSize)
461 """!@brief Function that standardize the data
468 x: the standardize data
473 if not sp.issubdtype(x.dtype,float):
477 xs = sp.empty_like(x)
481 M,m = sp.amax(x,axis=0),sp.amin(x,axis=0)
486 xs[:,i] = 2*(x[:,i]-m[i])/den[i]-1
492 def predict_image(self,inRaster,outRaster,model,inMask=None,NODATA=-10000,SCALE=None):
493 """!@brief The function classify the whole raster image, using per block image analysis.
495 The classifier is given in classifier and options in kwargs
498 inRaster : Filtered image name ('sample_filtered.tif',str)
499 outRaster :Raster image name ('outputraster.tif',str)
500 model : model file got from precedent step ('model', str)
502 NODATA : Default set to -10000 (int)
503 SCALE : Default set to None
506 nothing but save a raster image
509 raster = gdal.Open(inRaster,gdal.GA_ReadOnly)
511 print 'Impossible to open '+inRaster
518 mask = gdal.Open(inMask,gdal.GA_ReadOnly)
520 print 'Impossible to open '+inMask
523 if (raster.RasterXSize != mask.RasterXSize)
or (raster.RasterYSize != mask.RasterYSize):
524 print 'Image and mask should be of the same size'
526 if SCALE
is not None:
527 M,m=sp.asarray(SCALE[0]),sp.asarray(SCALE[1])
530 d = raster.RasterCount
531 nc = raster.RasterXSize
532 nl = raster.RasterYSize
535 GeoTransform = raster.GetGeoTransform()
536 Projection = raster.GetProjection()
539 band = raster.GetRasterBand(1)
540 block_sizes = band.GetBlockSize()
541 x_block_size = block_sizes[0]
542 y_block_size = block_sizes[1]
546 driver = gdal.GetDriverByName(
'GTiff')
547 dst_ds = driver.Create(outRaster, nc,nl, 1, gdal.GDT_Byte)
548 dst_ds.SetGeoTransform(GeoTransform)
549 dst_ds.SetProjection(Projection)
550 out = dst_ds.GetRasterBand(1)
553 for i
in range(0,nl,y_block_size):
554 if i + y_block_size < nl:
558 for j
in range(0,nc,x_block_size):
559 if j + x_block_size < nc:
565 X = sp.empty((cols*lines,d))
566 for ind
in xrange(d):
567 X[:,ind] = raster.GetRasterBand(int(ind+1)).ReadAsArray(j, i, cols, lines).reshape(cols*lines)
571 mask_temp=raster.GetRasterBand(1).ReadAsArray(j, i, cols, lines).reshape(cols*lines)
572 t = sp.where((mask_temp!=0) & (X[:,0]!=NODATA))[0]
573 yp=sp.zeros((cols*lines,))
575 mask_temp=mask.GetRasterBand(1).ReadAsArray(j, i, cols, lines).reshape(cols*lines)
576 t = sp.where((mask_temp!=0) & (X[:,0]!=NODATA))[0]
577 yp=sp.zeros((cols*lines,))
581 yp[t]= model.predict(self.
scale(X[t,:],M=M,m=m)).astype(
'uint8')
584 out.WriteArray(yp.reshape(lines,cols),j,i)
595 """!@brief Manage progressBar and loading cursor.
596 Allow to add a progressBar in Qgis and to change cursor to loading
598 -inMsg : Message to show to the user (str)
599 -inMax : The steps of the script (int)
602 nothing but changing cursor and print progressBar inside Qgis
604 def __init__(self,inMsg=' Loading...',inMaxStep=1):
608 QApplication.processEvents()
610 widget = iface.messageBar().createMessage(
'Please wait ',inMsg)
611 prgBar = QProgressBar()
614 widget.layout().addWidget(self.
prgBar)
615 iface.messageBar().pushWidget(widget, iface.messageBar().WARNING)
616 QApplication.setOverrideCursor(QtCore.Qt.WaitCursor)
622 prgBar.setMaximum(inMaxStep)
625 """!@brief Add a step to the progressBar
626 addStep() simply add +1 to current value of the progressBar
628 plusOne=self.prgBar.value()+1
629 self.prgBar.setValue(plusOne)
631 """!@brief Simply remove progressBar and reset cursor
635 self.iface.messageBar().clearWidgets()
636 self.iface.mapCanvas().refresh()
637 QApplication.restoreOverrideCursor()
Filter a raster with median and closing filter.
def scale
Function that standardize the data.
def predict_image
The function classify the whole raster image, using per block image analysis.
Classify image with learn clasifier and learned model.
def addStep
Add a step to the progressBar addStep() simply add +1 to current value of the progressBar.
def scale
Function that standardize the data.
Manage progressBar and loading cursor.
Learn model with a shp file and a raster image.
def reset
Simply remove progressBar and reset cursor.