├── .gitignore ├── README.md ├── __init__.py ├── checkpoint └── __init__.py ├── config.py ├── log └── __init__.py ├── main.py ├── main2.py ├── model ├── __init__.py ├── dataloader.py ├── resnet.py ├── sym_util.py └── vgg16.py ├── pretrain_model └── __init__.py ├── start.sh ├── train.py └── utils ├── __init__.py ├── augmentation.py ├── filter.py ├── io.py ├── label_data.py ├── misc.py ├── plot_curve.py └── preprocess.py /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | *.egg-info/ 24 | .installed.cfg 25 | *.egg 26 | MANIFEST 27 | 28 | # PyInstaller 29 | # Usually these files are written by a python script from a template 30 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 31 | *.manifest 32 | *.spec 33 | 34 | # Installer logs 35 | pip-log.txt 36 | pip-delete-this-directory.txt 37 | 38 | # Unit test / coverage reports 39 | htmlcov/ 40 | .tox/ 41 | .coverage 42 | .coverage.* 43 | .cache 44 | nosetests.xml 45 | coverage.xml 46 | *.cover 47 | .hypothesis/ 48 | 49 | # Translations 50 | *.mo 51 | *.pot 52 | 53 | # Django stuff: 54 | *.log 55 | .static_storage/ 56 | .media/ 57 | local_settings.py 58 | 59 | # Flask stuff: 60 | instance/ 61 | .webassets-cache 62 | 63 | # Scrapy stuff: 64 | .scrapy 65 | 66 | # Sphinx documentation 67 | docs/_build/ 68 | 69 | # PyBuilder 70 | target/ 71 | 72 | # Jupyter Notebook 73 | .ipynb_checkpoints 74 | 75 | # pyenv 76 | .python-version 77 | 78 | # celery beat schedule file 79 | celerybeat-schedule 80 | 81 | # SageMath parsed files 82 | *.sage.py 83 | 84 | # Environments 85 | .env 86 | .venv 87 | env/ 88 | venv/ 89 | ENV/ 90 | env.bak/ 91 | venv.bak/ 92 | 93 | # Spyder project settings 94 | .spyderproject 95 | .spyproject 96 | 97 | # Rope project settings 98 | .ropeproject 99 | 100 | # mkdocs documentation 101 | /site 102 | 103 | # mypy 104 | .mypy_cache/ 105 | 106 | # data 107 | data/* 108 | 109 | # notebook 110 | *.ipynb 111 | 112 | # Idea 113 | .idea 114 | 115 | # paras 116 | *.params 117 | *.json 118 | 119 | src.tar.gz -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Change Detection in Remote Sensing 2 | 3 | 4 | #### Project Organization 5 | 6 | - ```main```: Pipeline for training and evaluation 7 | - ```config```: Model config and file paths 8 | - ```checkpoint```: MXNet checkpoint files 9 | - ```data```: Rawdata and temporal data 10 | - ```Pretrain_model```: pretrained model file 11 | - ```model```: MXNet Symbols of VGG16, Resnet 12 | - ```utils.preprocessing```: Utility function for preprocessing, such as PCA, Rescaling. 13 | - ```utils.io```: Utility function for I/O 14 | - ```utils.filter```: Weighted median filter 15 | - ```utils.label_data```: A handy tool for labeling data manually. 16 | 17 | #### Requirements 18 | - Apache MXNet 0.12.0 19 | - python 2.7 20 | 21 | 22 | #### Usage 23 | ``` 24 | python main.py --model vgg16 --opt sgd --lr 1e-5 --t1 0.2 --t2 0.99 --epoch 0 --num_epoch 100 25 | ``` 26 | -------------------------------------------------------------------------------- /__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dsqx71/Change-Detection-in-Remote-Sensing-Images/8541371bb13284371a1de77ef99630f5eb163198/__init__.py -------------------------------------------------------------------------------- /checkpoint/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dsqx71/Change-Detection-in-Remote-Sensing-Images/8541371bb13284371a1de77ef99630f5eb163198/checkpoint/__init__.py -------------------------------------------------------------------------------- /config.py: -------------------------------------------------------------------------------- 1 | import easydict 2 | import numpy as np 3 | cfg = easydict.EasyDict() 4 | 5 | ### dirs 6 | cfg.dirs = easydict.EasyDict() 7 | 8 | # original data 9 | cfg.dirs.FILE_2015 = './data/quickbird2015.tif' 10 | cfg.dirs.FILE_2017 = './data/quickbird2017.tif' 11 | cfg.dirs.FILE_cadastral2015 = './data/cadastral2015.tif' 12 | cfg.dirs.FILE_tinysample = './data/tinysample.tif' 13 | 14 | # labelled patch data 15 | cfg.dirs.FILE_2015patch_img = './data/2015/' 16 | cfg.dirs.FILE_2017patch_img = './data/2017/' 17 | cfg.dirs.FILE_2015patch_label = './data/mylabel_2015/' 18 | cfg.dirs.FILE_2017patch_label = './data/mylabel_2017/' 19 | 20 | # Full labelled data 21 | cfg.dirs.FILE_label2015 = './data/label/label2015.npy' 22 | cfg.dirs.FILE_label2017 = './data/label/label2017.npy' 23 | cfg.dirs.PCA_img2015 = './data/pca_img2015.npy' 24 | cfg.dirs.PCA_img2017 = './data/pca_img2017.npy' 25 | cfg.dirs.tiny_label = './data/tiny_label.npy' 26 | 27 | # checkpoint 28 | cfg.dirs.pretrain_model ='./pretrain_model/' 29 | cfg.dirs.checkpoint = './checkpoint/' 30 | 31 | # logging 32 | cfg.dirs.log_prefix = './log/' 33 | 34 | ### data setting 35 | cfg.data = easydict.EasyDict() 36 | cfg.data.data_shape = 5106, 15106 37 | cfg.data.batch_shape = (1, 3, 256, 256) 38 | cfg.data.label_shape = (1, 1, 256, 256) 39 | cfg.data.r = 128 40 | cfg.data.mean = np.array([103.939, 116.779, 123.68]) 41 | 42 | 43 | -------------------------------------------------------------------------------- /log/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dsqx71/Change-Detection-in-Remote-Sensing-Images/8541371bb13284371a1de77ef99630f5eb163198/log/__init__.py -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | import mxnet as mx 2 | import numpy as np 3 | import argparse 4 | import logging 5 | import os 6 | 7 | from config import cfg 8 | from utils import io, misc 9 | from collections import namedtuple 10 | from model import vgg16 11 | DataBatch = namedtuple('DataBatch', ['data', 'label']) 12 | 13 | if __name__ == '__main__': 14 | # args 15 | parser = argparse.ArgumentParser() 16 | parser.add_argument('--model', type=str, help='pretrain model', choices=['vgg16']) 17 | parser.add_argument('--epoch', type=int, help='continue training', default=0) 18 | parser.add_argument('--lr', type=float, help='Learning rate', default=1e-5) 19 | parser.add_argument('--num_epoch', type=int, default=100) 20 | parser.add_argument('--t1', type=float, default=0.05) 21 | parser.add_argument('--t2', type=float, default=0.999) 22 | parser.add_argument('--opt', type=str, default='sgd', choices=['sgd', 'Adam']) 23 | parser.add_argument('--num_train', type=int, default=100) 24 | args = parser.parse_args() 25 | 26 | # logging 27 | exp_name = '_'.join([args.model, str(args.opt), str(args.t1), str(args.t2)]) 28 | log_file = os.path.join(cfg.dirs.log_prefix, exp_name) 29 | logging.basicConfig(level=logging.INFO, format='%(asctime)s %(message)s', filename=log_file, filemode='a') 30 | console = logging.StreamHandler() 31 | console.setLevel(logging.INFO) 32 | formatter = logging.Formatter('%(asctime)s %(message)s') 33 | console.setFormatter(formatter) 34 | logging.getLogger('').addHandler(console) 35 | logging.info(args) 36 | 37 | # Load data 38 | pca_img2015, pca_img2017, label = io.read_data() 39 | pca_img2015 = pca_img2015 - cfg.data.mean 40 | pca_img2017 = pca_img2017 - cfg.data.mean 41 | gt = io.tiff.imread('./data/answer_complete 2.tif') 42 | gt[gt > 0] = 1 43 | 44 | # create dir 45 | checkpoint_prefix = os.path.join(cfg.dirs.checkpoint, exp_name) 46 | checkpoint_path = os.path.join(checkpoint_prefix, exp_name) 47 | if os.path.isdir(checkpoint_prefix) == False: 48 | os.makedirs(checkpoint_prefix) 49 | 50 | # Init executor 51 | net = eval('{}.{}'.format(args.model, args.model))(ratio_neg=10) 52 | fix_params = [item for item in net.list_arguments() if 'upsampling' in item] 53 | if args.epoch == 0: 54 | # Load pretrain model 55 | init = mx.initializer.Xavier(rnd_type='gaussian', factor_type='in', magnitude=8) 56 | mod = mx.module.Module(symbol=net, 57 | data_names=['img1', 'img2'], 58 | label_names=['label', ], 59 | context=mx.gpu(0), 60 | fixed_param_names=fix_params) 61 | mod.bind(data_shapes=[('img1', cfg.data.batch_shape), ('img2', cfg.data.batch_shape)], 62 | label_shapes=[('label', cfg.data.label_shape)], 63 | for_training=True, force_rebind=False) 64 | pretrain_args, pretrain_auxs = misc.load_pretrainModel(args.model, net) 65 | mod.init_params(initializer=init, arg_params=pretrain_args, aux_params=pretrain_auxs, 66 | allow_missing=True, force_init=False) 67 | else: 68 | mod = mx.module.Module.load(prefix=checkpoint_path, 69 | epoch=args.epoch, 70 | load_optimizer_states=True, 71 | data_names=['img1', 'img2'], 72 | label_names=['label', ], 73 | context=mx.gpu(0), 74 | fixed_param_names=fix_params) 75 | mod.bind(data_shapes=[('img1', cfg.data.batch_shape), ('img2', cfg.data.batch_shape)], 76 | label_shapes=[('label', cfg.data.label_shape)], 77 | for_training=True, force_rebind=False) 78 | label = np.load(checkpoint_path + '_predict_{}.npy'.format(args.epoch)) 79 | a,b = mod.get_params() 80 | for key in a: 81 | assert (a[key].asnumpy() !=0).any() 82 | 83 | 84 | if args.opt =='sgd': 85 | optimizer_params = dict(learning_rate=args.lr, 86 | wd=0.0004, 87 | momentum=0.90) 88 | else: 89 | optimizer_params = dict(learning_rate=args.lr, 90 | beta1=0.90, 91 | beta2=0.999, 92 | epsilon=1e-4, 93 | rescale_grad=1.0 / cfg.data.batch_shape[0], 94 | wd=0.0004, 95 | lr_scheduler=mx.lr_scheduler.FactorScheduler(step=250000, 96 | factor=0.5, 97 | stop_factor_lr=3.125E-6)) 98 | mod.init_optimizer(kvstore='device', 99 | optimizer=args.opt, 100 | optimizer_params=optimizer_params) 101 | 102 | # EM 103 | print ("EM alogtihm") 104 | logging.info("Start EM algorithm...") 105 | for k in range(args.epoch+1, args.num_epoch): 106 | 107 | logging.info("-------------Epoch:{}----------------".format(k)) 108 | logging.info("E-step..............") 109 | minm_p = np.inf 110 | maxm_p = -np.inf 111 | count_p = 0 112 | 113 | minm_n = np.inf 114 | maxm_n = -np.inf 115 | count_n = 0 116 | 117 | flag_p = False 118 | flag_n = False 119 | 120 | # E-step 121 | samples = io.sample_label(pca_img2015, pca_img2017, label, 100, 100) 122 | 123 | for i in range(len(samples)): 124 | dbatch = DataBatch(data=[mx.nd.array(np.expand_dims(samples[i][0], 0).transpose(0, 3, 1, 2)), 125 | mx.nd.array(np.expand_dims(samples[i][1], 0).transpose(0, 3, 1, 2))], 126 | label=[mx.nd.array([np.expand_dims(samples[i][2], 0)])]) 127 | 128 | mod.forward(dbatch) 129 | out = mod.get_outputs()[0].asnumpy()[0][0] 130 | 131 | if (samples[i][2] == samples[i][2]).any(): 132 | 133 | if (out[samples[i][2] == 1].size > 0): 134 | minm_p = min(np.percentile(out[samples[i][2] == 1], 10), minm_p) 135 | maxm_p = max(np.percentile(out[samples[i][2] == 1], 95), maxm_p) 136 | count_p += 1 137 | 138 | if (out[samples[i][2] == 0].size > 0): 139 | # print(np.argmin(out)) 140 | minm_n = min(np.percentile(out[samples[i][2] == 0], 10), minm_n) 141 | maxm_n = max(np.percentile(out[samples[i][2] == 0], 95), maxm_n) 142 | count_n += 1 143 | 144 | if count_p >= 50: 145 | # Label those data with negative class 146 | mask1 = (out < args.t1 * minm_p) & (samples[i][2] != samples[i][2]) 147 | samples[i][2][mask1] = 0 148 | 149 | # Label those data with positive class 150 | mask2 = (out > args.t2 * maxm_p) & (samples[i][2] != samples[i][2]) 151 | samples[i][2][mask2] = 1 152 | if mask1.size > 0: 153 | flag_n = True 154 | 155 | if mask2.size > 0: 156 | flag_p = True 157 | 158 | if i % 100 == 0 and i!=0: 159 | logging.info('minm_p:{}, maxm_p:{}, minm_n:{}, maxm_n:{}, positive class: {}, negative class: {}'.format(minm_p, maxm_p, minm_n, maxm_n, (label == 1).sum(), (label == 0).sum())) 160 | 161 | # M-step 162 | logging.info("M-step..........") 163 | for i in range(len(samples)): 164 | if ((samples[i][2] == samples[i][2]).any() and (count_p > 0 or count_n > 0)): 165 | dbatch = DataBatch(data=[mx.nd.array(np.expand_dims(samples[i][0], 0).transpose(0, 3, 1, 2)), 166 | mx.nd.array(np.expand_dims(samples[i][1], 0).transpose(0, 3, 1, 2))], 167 | label=[mx.nd.array([np.expand_dims(samples[i][2], 0)])]) 168 | mod.forward_backward(dbatch) 169 | mod.update() 170 | 171 | # Save checkpoint and result 172 | mod.save_checkpoint(prefix=checkpoint_path, epoch=k, save_optimizer_states=True) 173 | np.save(checkpoint_path + '_predict_{}.npy'.format(k), label) 174 | 175 | # Evaluation 176 | score, density = misc.F1_score(label, gt) 177 | acc = misc.accuracy(label, gt) 178 | logging.info("Epoch : %d, F1-score : %.4f, accuracy: %.4f, Density : %.4f" %(k, score, acc, density)) 179 | 180 | 181 | if flag_n == False: 182 | args.t1 += 0.05 183 | logging.info("update t1:{}".format(args.t1)) 184 | 185 | if flag_p == False: 186 | args.t2 -= 0.01 187 | logging.info("update t2:{}".format(args.t2)) 188 | 189 | 190 | # Training 191 | for k in range(args.num_train): 192 | samples = io.sample_label(pca_img2015, pca_img2017, label, 200, 0) 193 | for i in range(len(samples)): 194 | if ((samples[i][2] == samples[i][2]).any() and (count_p > 0 or count_n > 0)): 195 | dbatch = DataBatch(data=[mx.nd.array(np.expand_dims(samples[i][0], 0).transpose(0, 3, 1, 2)), 196 | mx.nd.array(np.expand_dims(samples[i][1], 0).transpose(0, 3, 1, 2))], 197 | label=[mx.nd.array([np.expand_dims(samples[i][2], 0)])]) 198 | mod.forward_backward(dbatch) 199 | mod.update() -------------------------------------------------------------------------------- /main2.py: -------------------------------------------------------------------------------- 1 | import mxnet as mx 2 | import numpy as np 3 | import argparse 4 | import logging 5 | import os 6 | 7 | from config import cfg 8 | from utils import io, misc 9 | from collections import namedtuple 10 | from model import vgg16 11 | DataBatch = namedtuple('DataBatch', ['data', 'label']) 12 | 13 | if __name__ == '__main__': 14 | # args 15 | parser = argparse.ArgumentParser() 16 | parser.add_argument('--model', type=str, help='pretrain model', choices=['vgg16']) 17 | parser.add_argument('--epoch', type=int, help='continue training', default=0) 18 | parser.add_argument('--lr', type=float, help='Learning rate', default=1e-5) 19 | parser.add_argument('--num_epoch', type=int, default=100) 20 | parser.add_argument('--t1', type=float, default=0.05) 21 | parser.add_argument('--t2', type=float, default=0.999) 22 | parser.add_argument('--opt', type=str, default='sgd', choices=['sgd', 'Adam']) 23 | args = parser.parse_args() 24 | 25 | # logging 26 | exp_name = '_'.join([args.model, str(args.opt), str(args.t1), str(args.t2)]) 27 | log_file = os.path.join(cfg.dirs.log_prefix, exp_name) 28 | logging.basicConfig(level=logging.INFO, format='%(asctime)s %(message)s', filename=log_file, filemode='a') 29 | console = logging.StreamHandler() 30 | console.setLevel(logging.INFO) 31 | formatter = logging.Formatter('%(asctime)s %(message)s') 32 | console.setFormatter(formatter) 33 | logging.getLogger('').addHandler(console) 34 | logging.info(args) 35 | 36 | # Load data 37 | pca_img2015, pca_img2017, label = io.read_data() 38 | pca_img2015 = pca_img2015 - cfg.data.mean 39 | pca_img2017 = pca_img2017 - cfg.data.mean 40 | gt = io.tiff.imread('./data/answer_complete 2.tif') 41 | gt[gt > 0] = 1 42 | 43 | # create dir 44 | checkpoint_prefix = os.path.join(cfg.dirs.checkpoint, exp_name) 45 | checkpoint_path = os.path.join(checkpoint_prefix, exp_name) 46 | if os.path.isdir(checkpoint_prefix) == False: 47 | os.makedirs(checkpoint_prefix) 48 | 49 | # Init executor 50 | net = eval('{}.{}'.format(args.model, args.model))(ratio_neg=10) 51 | fix_params = [item for item in net.list_arguments() if 'upsampling' in item] 52 | if args.epoch == 0: 53 | # Load pretrain model 54 | init = mx.initializer.Xavier(rnd_type='gaussian', factor_type='in', magnitude=8) 55 | mod = mx.module.Module(symbol=net, 56 | data_names=['img1', 'img2'], 57 | label_names=['label', ], 58 | context=mx.gpu(0), 59 | fixed_param_names=fix_params) 60 | mod.bind(data_shapes=[('img1', cfg.data.batch_shape), ('img2', cfg.data.batch_shape)], 61 | label_shapes=[('label', cfg.data.label_shape)], 62 | for_training=True, force_rebind=False) 63 | pretrain_args, pretrain_auxs = misc.load_pretrainModel(args.model, net) 64 | mod.init_params(initializer=init, arg_params=pretrain_args, aux_params=pretrain_auxs, 65 | allow_missing=True, force_init=False) 66 | else: 67 | mod = mx.module.Module.load(prefix=checkpoint_path, 68 | epoch=args.epoch, 69 | load_optimizer_states=True, 70 | data_names=['img1', 'img2'], 71 | label_names=['label', ], 72 | context=mx.gpu(0), 73 | fixed_param_names=fix_params) 74 | mod.bind(data_shapes=[('img1', cfg.data.batch_shape), ('img2', cfg.data.batch_shape)], 75 | label_shapes=[('label', cfg.data.label_shape)], 76 | for_training=True, force_rebind=False) 77 | label = np.load(checkpoint_path + '_predict_{}.npy'.format(args.epoch)) 78 | a,b = mod.get_params() 79 | for key in a: 80 | assert (a[key].asnumpy() !=0).any() 81 | 82 | 83 | if args.opt =='sgd': 84 | optimizer_params = dict(learning_rate=args.lr, 85 | wd=0.0004, 86 | momentum=0.90) 87 | else: 88 | optimizer_params = dict(learning_rate=args.lr, 89 | beta1=0.90, 90 | beta2=0.999, 91 | epsilon=1e-4, 92 | rescale_grad=1.0 / cfg.data.batch_shape[0], 93 | wd=0.0004, 94 | lr_scheduler=mx.lr_scheduler.FactorScheduler(step=250000, 95 | factor=0.5, 96 | stop_factor_lr=3.125E-6)) 97 | mod.init_optimizer(kvstore='device', 98 | optimizer=args.opt, 99 | optimizer_params=optimizer_params) 100 | 101 | # EM 102 | print ("EM alogtihm") 103 | logging.info("Start EM algorithm...") 104 | for k in range(args.epoch+1, args.num_epoch): 105 | 106 | logging.info("-------------Epoch:{}----------------".format(k)) 107 | logging.info("E-step..............") 108 | minm_p = np.inf 109 | maxm_p = -np.inf 110 | count_p = 0 111 | 112 | minm_n = np.inf 113 | maxm_n = -np.inf 114 | count_n = 0 115 | 116 | flag_p = False 117 | flag_n = False 118 | 119 | # E-step 120 | samples = io.sample_label(pca_img2015, pca_img2017, label, 100, 400) 121 | 122 | for i in range(len(samples)): 123 | dbatch = DataBatch(data=[mx.nd.array(np.expand_dims(samples[i][0], 0).transpose(0, 3, 1, 2)), 124 | mx.nd.array(np.expand_dims(samples[i][1], 0).transpose(0, 3, 1, 2))], 125 | label=[mx.nd.array([np.expand_dims(samples[i][2], 0)])]) 126 | 127 | mod.forward(dbatch) 128 | out = mod.get_outputs()[0].asnumpy()[0][0] 129 | 130 | if (samples[i][2] == samples[i][2]).any(): 131 | 132 | if (out[samples[i][2] == 1].size > 0): 133 | minm_p = min(out[samples[i][2] == 1].min(), minm_p) 134 | maxm_p = max(out[samples[i][2] == 1].max(), maxm_p) 135 | count_p += 1 136 | 137 | if (out[samples[i][2] == 0].size > 0): 138 | # print(np.argmin(out)) 139 | minm_n = min(out[samples[i][2] == 0].min(), minm_n) 140 | maxm_n = max(out[samples[i][2] == 0].max(), maxm_n) 141 | count_n += 1 142 | 143 | if count_p >= 30: 144 | # Label those data with negative class 145 | mask1 = (out < args.t1 * minm_p) & (samples[i][2] != samples[i][2]) 146 | samples[i][2][mask1] = 0 147 | 148 | # Label those data with positive class 149 | mask2 = (out > args.t2 * maxm_p) & (samples[i][2] != samples[i][2]) 150 | samples[i][2][mask2] = 1 151 | if mask1.size > 0: 152 | flag_n = True 153 | 154 | if mask2.size > 0: 155 | flag_p = True 156 | 157 | if i % 200 == 0: 158 | logging.info('minm_p:{}, maxm_p:{}, minm_n:{}, maxm_n:{}, positive class: {}, negative class: {}'.format(minm_p, maxm_p, minm_n, maxm_n, (label == 1).sum(), (label == 0).sum())) 159 | 160 | # M-step 161 | logging.info("M-step..........") 162 | for i in range(len(samples)): 163 | if ((samples[i][2] == samples[i][2]).any() and (count_p > 0 or count_n > 0)): 164 | dbatch = DataBatch(data=[mx.nd.array(np.expand_dims(samples[i][0], 0).transpose(0, 3, 1, 2)), 165 | mx.nd.array(np.expand_dims(samples[i][1], 0).transpose(0, 3, 1, 2))], 166 | label=[mx.nd.array([np.expand_dims(samples[i][2], 0)])]) 167 | mod.forward_backward(dbatch) 168 | mod.update() 169 | 170 | # Save checkpoint and result 171 | mod.save_checkpoint(prefix=checkpoint_path, epoch=k, save_optimizer_states=True) 172 | np.save(checkpoint_path + '_predict_{}.npy'.format(k), label) 173 | 174 | # Evaluation 175 | score, density = misc.F1_score(label, gt) 176 | acc = misc.accuracy(label, gt) 177 | logging.info("Epoch : %d, F1-score : %.4f, accuracy: %.4f, Density : %.4f" %(k, score, acc, density)) 178 | 179 | 180 | if flag_n == False: 181 | args.t1 += 0.05 182 | logging.info("update t1:{}".format(args.t1)) 183 | 184 | if flag_p == False: 185 | args.t2 -= 0.01 186 | logging.info("update t2:{}".format(args.t2)) 187 | 188 | -------------------------------------------------------------------------------- /model/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dsqx71/Change-Detection-in-Remote-Sensing-Images/8541371bb13284371a1de77ef99630f5eb163198/model/__init__.py -------------------------------------------------------------------------------- /model/dataloader.py: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /model/resnet.py: -------------------------------------------------------------------------------- 1 | import mxnet as mx 2 | 3 | def residual_unit(data, num_filter, stride, dim_match, name, bottle_neck=True, bn_mom=0.9, workspace=512, memonger=False): 4 | """Return ResNet Unit symbol for building ResNet 5 | Parameters 6 | ---------- 7 | data : str 8 | Input data 9 | num_filter : int 10 | Number of output channels 11 | bnf : int 12 | Bottle neck channels factor with regard to num_filter 13 | stride : tupe 14 | Stride used in convolution 15 | dim_match : Boolen 16 | True means channel number between input and output is the same, otherwise means differ 17 | name : str 18 | Base name of the operators 19 | workspace : int 20 | Workspace used in convolution operator 21 | """ 22 | if bottle_neck: 23 | # the same as https://github.com/facebook/fb.resnet.torch#notes, a bit difference with origin paper 24 | bn1 = mx.sym.BatchNorm(data=data, fix_gamma=False, eps=2e-5, momentum=bn_mom, name=name + '_bn1') 25 | act1 = mx.sym.Activation(data=bn1, act_type='relu', name=name + '_relu1') 26 | conv1 = mx.sym.Convolution(data=act1, num_filter=int(num_filter*0.25), kernel=(1,1), stride=(1,1), pad=(0,0), 27 | no_bias=True, workspace=workspace, name=name + '_conv1') 28 | bn2 = mx.sym.BatchNorm(data=conv1, fix_gamma=False, eps=2e-5, momentum=bn_mom, name=name + '_bn2') 29 | act2 = mx.sym.Activation(data=bn2, act_type='relu', name=name + '_relu2') 30 | conv2 = mx.sym.Convolution(data=act2, num_filter=int(num_filter*0.25), kernel=(3,3), stride=stride, pad=(1,1), 31 | no_bias=True, workspace=workspace, name=name + '_conv2') 32 | bn3 = mx.sym.BatchNorm(data=conv2, fix_gamma=False, eps=2e-5, momentum=bn_mom, name=name + '_bn3') 33 | act3 = mx.sym.Activation(data=bn3, act_type='relu', name=name + '_relu3') 34 | conv3 = mx.sym.Convolution(data=act3, num_filter=num_filter, kernel=(1,1), stride=(1,1), pad=(0,0), no_bias=True, 35 | workspace=workspace, name=name + '_conv3') 36 | if dim_match: 37 | shortcut = data 38 | else: 39 | shortcut = mx.sym.Convolution(data=act1, num_filter=num_filter, kernel=(1,1), stride=stride, no_bias=True, 40 | workspace=workspace, name=name+'_sc') 41 | if memonger: 42 | shortcut._set_attr(mirror_stage='True') 43 | return conv3 + shortcut 44 | else: 45 | bn1 = mx.sym.BatchNorm(data=data, fix_gamma=False, momentum=bn_mom, eps=2e-5, name=name + '_bn1') 46 | act1 = mx.sym.Activation(data=bn1, act_type='relu', name=name + '_relu1') 47 | conv1 = mx.sym.Convolution(data=act1, num_filter=num_filter, kernel=(3,3), stride=stride, pad=(1,1), 48 | no_bias=True, workspace=workspace, name=name + '_conv1') 49 | bn2 = mx.sym.BatchNorm(data=conv1, fix_gamma=False, momentum=bn_mom, eps=2e-5, name=name + '_bn2') 50 | act2 = mx.sym.Activation(data=bn2, act_type='relu', name=name + '_relu2') 51 | conv2 = mx.sym.Convolution(data=act2, num_filter=num_filter, kernel=(3,3), stride=(1,1), pad=(1,1), 52 | no_bias=True, workspace=workspace, name=name + '_conv2') 53 | if dim_match: 54 | shortcut = data 55 | else: 56 | shortcut = mx.sym.Convolution(data=act1, num_filter=num_filter, kernel=(1,1), stride=stride, no_bias=True, 57 | workspace=workspace, name=name+'_sc') 58 | if memonger: 59 | shortcut._set_attr(mirror_stage='True') 60 | return conv2 + shortcut 61 | 62 | def resnet(units, num_stage, filter_list, num_class, data_type, bottle_neck=True, bn_mom=0.9, workspace=512, memonger=False): 63 | """Return ResNet symbol of cifar10 and imagenet 64 | Parameters 65 | ---------- 66 | units : list 67 | Number of units in each stage 68 | num_stage : int 69 | Number of stage 70 | filter_list : list 71 | Channel size of each stage 72 | num_class : int 73 | Ouput size of symbol 74 | dataset : str 75 | Dataset type, only cifar10 and imagenet supports 76 | workspace : int 77 | Workspace used in convolution operator 78 | """ 79 | num_unit = len(units) 80 | assert(num_unit == num_stage) 81 | data = mx.sym.Variable(name='data') 82 | data = mx.sym.BatchNorm(data=data, fix_gamma=True, eps=2e-5, momentum=bn_mom, name='bn_data') 83 | if data_type == 'cifar10': 84 | body = mx.sym.Convolution(data=data, num_filter=filter_list[0], kernel=(3, 3), stride=(1,1), pad=(1, 1), 85 | no_bias=True, name="conv0", workspace=workspace) 86 | elif data_type == 'imagenet': 87 | body = mx.sym.Convolution(data=data, num_filter=filter_list[0], kernel=(7, 7), stride=(2,2), pad=(3, 3), 88 | no_bias=True, name="conv0", workspace=workspace) 89 | body = mx.sym.BatchNorm(data=body, fix_gamma=False, eps=2e-5, momentum=bn_mom, name='bn0') 90 | body = mx.sym.Activation(data=body, act_type='relu', name='relu0') 91 | body = mx.symbol.Pooling(data=body, kernel=(3, 3), stride=(2,2), pad=(1,1), pool_type='max') 92 | else: 93 | raise ValueError("do not support {} yet".format(data_type)) 94 | for i in range(num_stage): 95 | body = residual_unit(body, filter_list[i+1], (1 if i==0 else 2, 1 if i==0 else 2), False, 96 | name='stage%d_unit%d' % (i + 1, 1), bottle_neck=bottle_neck, workspace=workspace, 97 | memonger=memonger) 98 | for j in range(units[i]-1): 99 | body = residual_unit(body, filter_list[i+1], (1,1), True, name='stage%d_unit%d' % (i + 1, j + 2), 100 | bottle_neck=bottle_neck, workspace=workspace, memonger=memonger) 101 | bn1 = mx.sym.BatchNorm(data=body, fix_gamma=False, eps=2e-5, momentum=bn_mom, name='bn1') 102 | relu1 = mx.sym.Activation(data=bn1, act_type='relu', name='relu1') 103 | # Although kernel is not used here when global_pool=True, we should put one 104 | pool1 = mx.symbol.Pooling(data=relu1, global_pool=True, kernel=(7, 7), pool_type='avg', name='pool1') 105 | flat = mx.symbol.Flatten(data=pool1) 106 | fc1 = mx.symbol.FullyConnected(data=flat, num_hidden=num_class, name='fc1') 107 | return mx.symbol.SoftmaxOutput(data=fc1, name='softmax') -------------------------------------------------------------------------------- /model/sym_util.py: -------------------------------------------------------------------------------- 1 | import mxnet as mx 2 | import numpy as np 3 | 4 | var_registrar = {} 5 | 6 | def get_variable(name, shape=None, init=None): 7 | global var_registrar 8 | if name not in var_registrar: 9 | var_registrar[name] = mx.sym.Variable(name, shape=shape, init=init, dtype=np.float32) 10 | return var_registrar[name] 11 | 12 | 13 | class SparseRegressionLoss(mx.operator.CustomOp): 14 | """ 15 | SparseRegressionLoss will ignore labels with values of NaN 16 | """ 17 | def __init__(self,loss_scale, is_l1, adaptive, ratio_neg): 18 | # due to mxnet serialization problem 19 | super(SparseRegressionLoss, self).__init__() 20 | 21 | self.loss_scale = float(loss_scale) 22 | self.is_l1 = bool(is_l1) 23 | self.ratio_neg = float(ratio_neg) 24 | self.adaptive = bool(adaptive) 25 | 26 | def forward(self, is_train, req, in_data, out_data, aux): 27 | 28 | x = in_data[0] 29 | y = out_data[0] 30 | self.assign(y, req[0], x) 31 | 32 | def backward(self, req, out_grad, in_data, out_data, in_grad, aux): 33 | 34 | label = in_data[1].asnumpy() 35 | y = out_data[0].asnumpy() 36 | 37 | # find negative samples 38 | mask_neg = (label == 0) 39 | 40 | # find invalid labels 41 | mask_nan = (label != label) 42 | 43 | # total number of valid points 44 | normalize_coeff = (~mask_nan[:, 0, :, :]).sum() 45 | if self.is_l1: 46 | tmp = np.sign(y - label) * self.loss_scale / float(normalize_coeff) 47 | else: 48 | tmp = (y - label) * self.loss_scale / float(normalize_coeff) 49 | 50 | # ignore NaN 51 | tmp[mask_nan] = 0 52 | if normalize_coeff == 0: 53 | tmp[:] = 0 54 | 55 | if self.adaptive: 56 | mask_pos = (label==1) 57 | if mask_pos.size == 0 or mask_neg.size == 0: 58 | tmp[:] = 0 59 | else: 60 | tmp[mask_neg] = mask_pos.size / mask_neg.size * tmp[mask_neg] 61 | 62 | tmp[mask_neg] = self.ratio_neg * tmp[mask_neg] 63 | self.assign(in_grad[0], req[0], mx.nd.array(tmp)) 64 | 65 | 66 | @mx.operator.register("SparseRegressionLoss") 67 | class SparseRegressionLossProp(mx.operator.CustomOpProp): 68 | 69 | def __init__(self, loss_scale, is_l1, adaptive=False, ratio_neg=1): 70 | super(SparseRegressionLossProp, self).__init__(False) 71 | self.loss_scale = loss_scale 72 | self.is_l1 = is_l1 73 | self.ratio_neg = ratio_neg 74 | self.adaptive = adaptive 75 | 76 | def list_arguments(self): 77 | return ['data', 'label'] 78 | 79 | def list_outputs(self): 80 | return ['output'] 81 | 82 | def infer_shape(self, in_shape): 83 | data_shape = in_shape[0] 84 | label_shape = in_shape[0] 85 | output_shape = in_shape[0] 86 | 87 | return [data_shape, label_shape], [output_shape] 88 | 89 | def create_operator(self, ctx, shapes, dtypes): 90 | 91 | return SparseRegressionLoss(self.loss_scale, self.is_l1, self.adaptive, self.ratio_neg) 92 | -------------------------------------------------------------------------------- /model/vgg16.py: -------------------------------------------------------------------------------- 1 | from .sym_util import * 2 | 3 | def get_conv(name, data, num_filter, kernel, stride=(1,1), pad=(1,1), dilate=(1, 1), with_relu=True): 4 | 5 | weight = get_variable(name=name+'_weight') 6 | bias = get_variable(name=name+'_bias') 7 | 8 | conv = mx.symbol.Convolution(name=name, data=data, num_filter=num_filter, kernel=kernel, 9 | weight=weight, bias=bias, 10 | stride=stride, pad=pad, dilate=dilate, no_bias=False) 11 | 12 | 13 | return mx.sym.Activation(conv, act_type="relu") if with_relu else conv 14 | 15 | def siamese(data): 16 | 17 | # conv1 18 | conv1_1 = get_conv(data=data, kernel=(3, 3), pad=(1, 1), num_filter=64, name="conv1_1") 19 | conv1 = conv1_2 = get_conv(data=conv1_1, kernel=(3, 3), pad=(1, 1), num_filter=64, name="conv1_2") 20 | conv1_2 = mx.sym.Pooling(data=conv1_2, kernel=(2, 2), stride=(2, 2), pool_type="max", name="pool1") 21 | 22 | # conv2s 23 | conv2_1 = get_conv(data=conv1_2, kernel=(3, 3), pad=(1, 1), num_filter=128, name="conv2_1", dilate=(1, 1)) 24 | conv2 = conv2_2 = get_conv(data=conv2_1, kernel=(3, 3), pad=(1, 1), num_filter=128, name="conv2_2") 25 | conv2_2 = mx.sym.Pooling(data=conv2_2, kernel=(2, 2), stride=(2, 2), pool_type="max", name="pool2") 26 | 27 | # # conv3 28 | conv3_1 = get_conv(data=conv2_2, kernel=(3, 3), pad=(1, 1), num_filter=256, name="conv3_1", dilate=(1, 1)) 29 | conv3_2 = get_conv(data=conv3_1, kernel=(3, 3), pad=(1, 1), num_filter=256, name="conv3_2", dilate=(1 ,1)) 30 | conv3 = conv3_3 = get_conv(data=conv3_2, kernel=(3, 3), pad=(1, 1), num_filter=256, name="conv3_3") 31 | # conv3_3 = mx.sym.Pooling(data=conv3_3, kernel=(2, 2), stride=(2, 2), pool_type="max", name="pool3") 32 | # 33 | # # conv4 34 | # conv4_1 = get_conv(data=conv3_3, kernel=(3, 3), pad=(1, 1), num_filter=512, name="conv4_1", dilate=(1, 1)) 35 | # conv4_2 = get_conv(data=conv4_1, kernel=(3, 3), pad=(1, 1), num_filter=512, name="conv4_2", dilate=(1, 1)) 36 | # conv4 = conv4_3 = get_conv(data=conv4_2, kernel=(3, 3), pad=(1, 1), num_filter=512, name="conv4_3") 37 | # conv4_3 = mx.sym.Pooling(data=conv4_3, kernel=(2, 2), stride=(2, 2), pool_type="max", name="pool4") 38 | # 39 | # # conv5 40 | # conv5_1 = get_conv(data=conv4_3, kernel=(3, 3), pad=(16, 16), num_filter=512, name="conv5_1", dilate=(16, 16)) 41 | # conv5_2 = get_conv(data=conv5_1, kernel=(3, 3), pad=(16, 16), num_filter=512, name="conv5_2", dilate=(16, 16)) 42 | # conv5_3 = get_conv(data=conv5_2, kernel=(3, 3), pad=(1, 1), num_filter=512, name="conv5_3") 43 | # 44 | # b5 = mx.sym.UpSampling(data=conv5_3, scale=16, num_filter=512, num_args=1, sample_type='bilinear', workspace=2048) 45 | # b4 = mx.sym.UpSampling(data=conv4, scale=8, num_filter=512, num_args=1, sample_type='bilinear', workspace=2048) 46 | b3 = mx.sym.UpSampling(data=conv3, scale=4, num_filter=256, num_args=1, sample_type='bilinear', workspace=2048) 47 | b2 = mx.sym.UpSampling(data=conv2, scale=2, num_filter=128, num_args=1, sample_type='bilinear', workspace=2048) 48 | # 49 | # ret = mx.sym.concat(conv1, b2, b3, b4, b5) 50 | ret = mx.sym.concat(conv1, b2, b3) 51 | return ret 52 | 53 | 54 | 55 | def vgg16(ratio_neg=50, metric='cosine'): 56 | 57 | img1 = mx.sym.Variable('img1') 58 | img2 = mx.sym.Variable('img2') 59 | label = mx.sym.Variable('label') 60 | 61 | out1 = siamese(img1) 62 | out2 = siamese(img2) 63 | 64 | 65 | if metric == 'l2_distance': 66 | data = mx.sym.sqrt(mx.sym.sum(mx.sym.square(out1 - out2), axis=1, keepdims=True)) 67 | 68 | elif metric == 'cosine': 69 | data = mx.sym.sum(out1 * out2, axis=1, keepdims=True) 70 | norm1 = mx.sym.sqrt(mx.sym.sum(mx.sym.square(out1), axis=1, keepdims=True)) 71 | norm2 = mx.sym.sqrt(mx.sym.sum(mx.sym.square(out2), axis=1, keepdims=True)) 72 | data = data / norm1 73 | data = data / norm2 74 | data = 1 - data 75 | 76 | loss = mx.symbol.Custom(data=data, label=label, name='L1_sparse', loss_scale=1.0, is_l1=False, adaptive=True, ratio_neg=ratio_neg, op_type='SparseRegressionLoss') 77 | 78 | return loss 79 | -------------------------------------------------------------------------------- /pretrain_model/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dsqx71/Change-Detection-in-Remote-Sensing-Images/8541371bb13284371a1de77ef99630f5eb163198/pretrain_model/__init__.py -------------------------------------------------------------------------------- /start.sh: -------------------------------------------------------------------------------- 1 | python main.py --model vgg16 --ratio 5 --lr 1e-5 --t1 1000 --t2 15000 --epoch 0 -------------------------------------------------------------------------------- /train.py: -------------------------------------------------------------------------------- 1 | import mxnet as mx 2 | import numpy as np 3 | import argparse 4 | import logging 5 | import os 6 | 7 | from config import cfg 8 | from utils import io, misc 9 | from collections import namedtuple 10 | from model import vgg16 11 | DataBatch = namedtuple('DataBatch', ['data', 'label']) 12 | 13 | if __name__ == '__main__': 14 | # args 15 | parser = argparse.ArgumentParser() 16 | parser.add_argument('--model', type=str, help='pretrain model', choices=['vgg16']) 17 | parser.add_argument('--epoch', type=int, help='continue training', default=0) 18 | parser.add_argument('--lr', type=float, help='Learning rate', default=1e-5) 19 | parser.add_argument('--num_epoch', type=int, default=100) 20 | parser.add_argument('--t1', type=float, default=0.05) 21 | parser.add_argument('--t2', type=float, default=0.999) 22 | parser.add_argument('--opt', type=str, default='sgd', choices=['sgd', 'Adam']) 23 | args = parser.parse_args() 24 | 25 | # logging 26 | exp_name = '_'.join([args.model, str(args.opt), str(args.t1), str(args.t2)]) 27 | log_file = os.path.join(cfg.dirs.log_prefix, exp_name) 28 | logging.basicConfig(level=logging.INFO, format='%(asctime)s %(message)s', filename=log_file, filemode='a') 29 | console = logging.StreamHandler() 30 | console.setLevel(logging.INFO) 31 | formatter = logging.Formatter('%(asctime)s %(message)s') 32 | console.setFormatter(formatter) 33 | logging.getLogger('').addHandler(console) 34 | logging.info(args) 35 | 36 | # Load data 37 | pca_img2015, pca_img2017, label = io.read_data() 38 | pca_img2015 = pca_img2015 - cfg.data.mean 39 | pca_img2017 = pca_img2017 - cfg.data.mean 40 | gt = io.tiff.imread('./data/answer_complete 2.tif') 41 | gt[gt > 0] = 1 42 | 43 | # create dir 44 | checkpoint_prefix = os.path.join(cfg.dirs.checkpoint, exp_name) 45 | checkpoint_path = os.path.join(checkpoint_prefix, exp_name) 46 | if os.path.isdir(checkpoint_prefix) == False: 47 | os.makedirs(checkpoint_prefix) 48 | 49 | # Init executor 50 | net = eval('{}.{}'.format(args.model, args.model))(ratio_neg=10) 51 | fix_params = [item for item in net.list_arguments() if 'upsampling' in item] 52 | if args.epoch == 0: 53 | # Load pretrain model 54 | init = mx.initializer.Xavier(rnd_type='gaussian', factor_type='in', magnitude=8) 55 | mod = mx.module.Module(symbol=net, 56 | data_names=['img1', 'img2'], 57 | label_names=['label', ], 58 | context=mx.gpu(0), 59 | fixed_param_names=fix_params) 60 | mod.bind(data_shapes=[('img1', cfg.data.batch_shape), ('img2', cfg.data.batch_shape)], 61 | label_shapes=[('label', cfg.data.label_shape)], 62 | for_training=True, force_rebind=False) 63 | pretrain_args, pretrain_auxs = misc.load_pretrainModel(args.model, net) 64 | mod.init_params(initializer=init, arg_params=pretrain_args, aux_params=pretrain_auxs, 65 | allow_missing=True, force_init=False) 66 | else: 67 | mod = mx.module.Module.load(prefix=checkpoint_path, 68 | epoch=args.epoch, 69 | load_optimizer_states=True, 70 | data_names=['img1', 'img2'], 71 | label_names=['label', ], 72 | context=mx.gpu(0), 73 | fixed_param_names=fix_params) 74 | mod.bind(data_shapes=[('img1', cfg.data.batch_shape), ('img2', cfg.data.batch_shape)], 75 | label_shapes=[('label', cfg.data.label_shape)], 76 | for_training=True, force_rebind=False) 77 | label = np.load(checkpoint_path + '_predict_{}.npy'.format(args.epoch)) 78 | a,b = mod.get_params() 79 | for key in a: 80 | assert (a[key].asnumpy() !=0).any() 81 | 82 | 83 | if args.opt =='sgd': 84 | optimizer_params = dict(learning_rate=args.lr, 85 | wd=0.0004, 86 | momentum=0.90) 87 | else: 88 | optimizer_params = dict(learning_rate=args.lr, 89 | beta1=0.90, 90 | beta2=0.999, 91 | epsilon=1e-4, 92 | rescale_grad=1.0 / cfg.data.batch_shape[0], 93 | wd=0.0004, 94 | lr_scheduler=mx.lr_scheduler.FactorScheduler(step=250000, 95 | factor=0.5, 96 | stop_factor_lr=3.125E-6)) 97 | mod.init_optimizer(kvstore='device', 98 | optimizer=args.opt, 99 | optimizer_params=optimizer_params) 100 | 101 | # EM 102 | print ("EM alogtihm") 103 | logging.info("Start EM algorithm...") 104 | for k in range(args.epoch+1, args.num_epoch): 105 | 106 | logging.info("-------------Epoch:{}----------------".format(k)) 107 | logging.info("E-step..............") 108 | minm_p = np.inf 109 | maxm_p = -np.inf 110 | count_p = 0 111 | 112 | minm_n = np.inf 113 | maxm_n = -np.inf 114 | count_n = 0 115 | 116 | flag_p = False 117 | flag_n = False 118 | 119 | # E-step 120 | samples = io.sample_label(pca_img2015, pca_img2017, label, 100, 100) 121 | 122 | for i in range(len(samples)): 123 | dbatch = DataBatch(data=[mx.nd.array(np.expand_dims(samples[i][0], 0).transpose(0, 3, 1, 2)), 124 | mx.nd.array(np.expand_dims(samples[i][1], 0).transpose(0, 3, 1, 2))], 125 | label=[mx.nd.array([np.expand_dims(samples[i][2], 0)])]) 126 | 127 | mod.forward(dbatch) 128 | out = mod.get_outputs()[0].asnumpy()[0][0] 129 | 130 | if (samples[i][2] == samples[i][2]).any(): 131 | 132 | if (out[samples[i][2] == 1].size > 0): 133 | minm_p = min(np.percentile(out[samples[i][2] == 1], 10), minm_p) 134 | maxm_p = max(np.percentile(out[samples[i][2] == 1], 90), maxm_p) 135 | count_p += 1 136 | 137 | if (out[samples[i][2] == 0].size > 0): 138 | # print(np.argmin(out)) 139 | minm_n = min(np.percentile(out[samples[i][2] == 0], 10), minm_n) 140 | maxm_n = max(np.percentile(out[samples[i][2] == 0], 90), maxm_n) 141 | count_n += 1 142 | 143 | if count_p >= 50: 144 | # Label those data with negative class 145 | mask1 = (out < args.t1 * minm_p) & (samples[i][2] != samples[i][2]) 146 | samples[i][2][mask1] = 0 147 | 148 | # Label those data with positive class 149 | mask2 = (out > args.t2 * maxm_p) & (samples[i][2] != samples[i][2]) 150 | samples[i][2][mask2] = 1 151 | if mask1.size > 0: 152 | flag_n = True 153 | 154 | if mask2.size > 0: 155 | flag_p = True 156 | 157 | if i % 100 == 0 and i!=0: 158 | logging.info('minm_p:{}, maxm_p:{}, minm_n:{}, maxm_n:{}, positive class: {}, negative class: {}'.format(minm_p, maxm_p, minm_n, maxm_n, (label == 1).sum(), (label == 0).sum())) 159 | 160 | # M-step 161 | logging.info("M-step..........") 162 | for i in range(len(samples)): 163 | if ((samples[i][2] == samples[i][2]).any() and (count_p > 0 or count_n > 0)): 164 | dbatch = DataBatch(data=[mx.nd.array(np.expand_dims(samples[i][0], 0).transpose(0, 3, 1, 2)), 165 | mx.nd.array(np.expand_dims(samples[i][1], 0).transpose(0, 3, 1, 2))], 166 | label=[mx.nd.array([np.expand_dims(samples[i][2], 0)])]) 167 | mod.forward_backward(dbatch) 168 | mod.update() 169 | 170 | # Save checkpoint and result 171 | mod.save_checkpoint(prefix=checkpoint_path, epoch=k, save_optimizer_states=True) 172 | np.save(checkpoint_path + '_predict_{}.npy'.format(k), label) 173 | 174 | # Evaluation 175 | score, density = misc.F1_score(label, gt) 176 | acc = misc.accuracy(label, gt) 177 | logging.info("Epoch : %d, F1-score : %.4f, accuracy: %.4f, Density : %.4f" %(k, score, acc, density)) 178 | 179 | 180 | if flag_n == False: 181 | args.t1 += 0.05 182 | logging.info("update t1:{}".format(args.t1)) 183 | 184 | if flag_p == False: 185 | args.t2 -= 0.01 186 | logging.info("update t2:{}".format(args.t2)) 187 | 188 | -------------------------------------------------------------------------------- /utils/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dsqx71/Change-Detection-in-Remote-Sensing-Images/8541371bb13284371a1de77ef99630f5eb163198/utils/__init__.py -------------------------------------------------------------------------------- /utils/augmentation.py: -------------------------------------------------------------------------------- 1 | import cv2 2 | import random 3 | import numpy as np 4 | 5 | from . import data_util 6 | from easydict import EasyDict as edict 7 | from math import * 8 | 9 | def between(data, low, high): 10 | """ 11 | within the boundary or not 12 | """ 13 | if low <= data and data <= high: 14 | return True 15 | else: 16 | return False 17 | 18 | def clip(data, low, high): 19 | """ 20 | Clip RGB data 21 | """ 22 | data[datahigh] = high 24 | return data 25 | 26 | class augmentation(object): 27 | """ 28 | Data augmentation. The design purpose of this class is to decouple augmentation from dataloaders 29 | 30 | Parameters 31 | --------- 32 | max_num_tries: int 33 | Not all spatial transformation coefficients are valid, 34 | max_num_tries denotes the maximum times it will try to find valid spatial coefficients. 35 | cropped_width: int 36 | target width 37 | cropped_height, 38 | target height 39 | data_type : str, 40 | the allowable values are 'stereo' and 'flow' 41 | augment_ratio : 42 | the probability of performing augmentation. 43 | noise_std: float, 44 | standard deviation of noise 45 | interpolation_method : str, 46 | how to interpolate data, 47 | the allowable values are 'bilinear' and 'nearest', 48 | mirror_rate: float 49 | the probability of performing mirror transformation 50 | rotate_range: 51 | translate_range: 52 | zoom_range: 53 | squeeze_range: 54 | gamma_range: 55 | brightness_range: 56 | contrast_range: 57 | rgb_multiply_range: 58 | dict, there are two forms of the dict: 59 | {'method':'uniform','low': float, 'high': float}, samples are uniformly distributed over the interval [low, high) 60 | or 61 | {'method':'normal', 'mean': float, 'scale': float}, samples are drawed from a normal distribution 62 | 63 | 64 | Notes: 65 | - For stereo matching, introducing any rotation, or vertical shift would break the epipolar constraint 66 | - When data type is 'stereo', it will ignore rotate_range, and not perform rotation transformation. 67 | 68 | Examples 69 | ---------- 70 | augment_pipeline = augmentation.augmentation(max_num_tries=50, 71 | cropped_height=320, 72 | cropped_width=768, 73 | data_type='stereo', 74 | augment_ratio=1.0, 75 | noise_std=0.0004, 76 | mirror_rate=0.5, 77 | rotate_range={'method': 'uniform', 'low': -17, 'high': 17}, 78 | translate_range={'method': 'uniform', 'low': -0.2, 'high': 0.2}, 79 | zoom_range={'method': 'uniform', 'low': 0.8, 'high': 1.5}, 80 | squeeze_range={'method': 'uniform', 'low': 0.75, 'high': 1.25}, 81 | gamma_range={'method': 'uniform', 'low': 0.9, 'high': 1.1}, 82 | brightness_range={'method': 'normal', 'mean': 0.0, 'scale': 0.002}, 83 | contrast_range={'method': 'uniform', 'low': 0.8, 'high': 1.2}, 84 | rgb_multiply_range={'method': 'uniform', 'low': 0.75, 'high': 1.25}, 85 | interpolation_method='bilinear') 86 | 87 | img1, img2, label = augment_pipeline(img1, img2, label, discount_coeff=0.5) 88 | """ 89 | def __init__(self, 90 | max_num_tries, 91 | cropped_width, 92 | cropped_height, 93 | data_type, 94 | augment_ratio, 95 | interpolation_method, 96 | # spatial transformation 97 | mirror_rate, 98 | flip_rate, 99 | rotate_range, 100 | translate_range, 101 | zoom_range, 102 | squeeze_range, 103 | # chromatic transformation 104 | gamma_range, 105 | brightness_range, 106 | contrast_range, 107 | rgb_multiply_range, 108 | # eigenvector transformation 109 | lmult_pow, 110 | lmult_mult, 111 | lmult_add, 112 | sat_pow, 113 | sat_mult, 114 | sat_add, 115 | col_pow, 116 | col_mult, 117 | col_add, 118 | ladd_pow, 119 | ladd_mult, 120 | ladd_add, 121 | col_rotate, 122 | noise_range): 123 | 124 | self.augment_ratio = augment_ratio 125 | self.data_type = data_type 126 | self.max_num_tries = max_num_tries 127 | self.cropped_width = cropped_width 128 | self.cropped_height = cropped_height 129 | 130 | if 'bilinear' in interpolation_method: 131 | self.interpolation_method = cv2.INTER_LINEAR 132 | elif 'nearest' in interpolation_method: 133 | self.interpolation_method = cv2.INTER_NEAREST 134 | else: 135 | raise ValueError("Wrong interpolation method") 136 | 137 | # spatial transform 138 | self.flip_rate = flip_rate 139 | self.mirror_rate = mirror_rate 140 | self.rotate_range = rotate_range 141 | self.translate_range = translate_range 142 | self.zoom_range = zoom_range 143 | self.squeeze_range = squeeze_range 144 | 145 | # chromatic transform 146 | self.gamma_range = gamma_range 147 | self.brightness_range = brightness_range 148 | self.contrast_range = contrast_range 149 | self.rgb_multiply_range = rgb_multiply_range 150 | 151 | # eigvector tranform 152 | self.lmult_pow = lmult_pow 153 | self.lmult_mult = lmult_mult 154 | self.lmult_add = lmult_add 155 | self.sat_pow = sat_pow 156 | self.sat_mult = sat_mult 157 | self.sat_add = sat_add 158 | self.col_pow = col_pow 159 | self.col_mult = col_mult 160 | self.col_add = col_add 161 | self.ladd_pow = ladd_pow 162 | self.ladd_mult = ladd_mult 163 | self.ladd_add = ladd_add 164 | self.col_rotate = col_rotate 165 | 166 | # noise 167 | self.noise_range = noise_range 168 | 169 | # eigen vector 170 | self.eigvec = np.array([[0.51, 0.56, 0.65], 171 | [0.79, 0.01,-0.62], 172 | [0.35,-0.83, 0.44]]) 173 | 174 | def generate_random(self, random_range, size=(1,)): 175 | """ 176 | random number generator 177 | """ 178 | mean = random_range['mean'] 179 | spread = random_range['spread'] * self.discount_coeff 180 | result = np.zeros(size) 181 | if random_range['method'] == 'uniform': 182 | cv2.randu(result, mean-spread, mean+spread) 183 | elif random_range['method'] == 'normal': 184 | cv2.randn(result, mean=mean, stddev=spread) 185 | else: 186 | raise ValueError("Wrong sampling method") 187 | result = np.exp(result) if random_range['exp'] else result 188 | 189 | if size == (1, ): 190 | result = result[0] 191 | return result 192 | 193 | def generate_spatial_coeffs(self): 194 | # order: mirror, rotate, translate, zoom, squeeze 195 | for i in range(self.max_num_tries): 196 | # identity matrix 197 | coeff = np.array([[1, 0, 0], 198 | [0, 1, 0], 199 | [0, 0, 1]]) 200 | 201 | # mirror 202 | if np.random.uniform(0, 1) < self.mirror_rate * self.discount_coeff: 203 | mirror = np.array([[-1, 0, 0.5*self.cropped_width], 204 | [ 0, 1, -0.5*self.cropped_height], 205 | [ 0, 0, 1]]) 206 | else: 207 | # move the center to (0, 0) 208 | mirror = np.array([[1, 0, -0.5*self.cropped_width], 209 | [0, 1, -0.5*self.cropped_height], 210 | [0, 0, 1]]) 211 | coeff = np.dot(mirror, coeff) 212 | 213 | # vertical flip 214 | if np.random.uniform(0, 1) < self.flip_rate * self.discount_coeff: 215 | flip = np.array([[1, 0, 0], 216 | [0,-1, 0], 217 | [0, 0, 1]]) 218 | coeff = np.dot(flip, coeff) 219 | 220 | # rotate 221 | if self.data_type == 'flow': 222 | angle = self.generate_random(self.rotate_range) / 180.0 * pi 223 | rotate = np.array([[cos(angle), - sin(angle), 0], 224 | [sin(angle), cos(angle), 0], 225 | [0, 0, 1]]) 226 | coeff = np.dot(rotate, coeff) 227 | 228 | # translate 229 | dx = self.generate_random(self.translate_range) 230 | dy = self.generate_random(self.translate_range) 231 | translate = np.array([[1, 0, dx*self.cropped_width], 232 | [0, 1, dy*self.cropped_height], 233 | [0, 0, 1]]) 234 | coeff = np.dot(translate, coeff) 235 | 236 | # zoom 237 | zoom_x = self.generate_random(self.zoom_range) 238 | zoom_y = zoom_x 239 | 240 | # squeeze 241 | squeeze_coeff = self.generate_random(self.squeeze_range) 242 | zoom_x *= squeeze_coeff 243 | zoom_y /= squeeze_coeff 244 | zoom = np.array([[1.0/zoom_x, 0, 0], 245 | [0, 1.0/zoom_y, 0], 246 | [0, 0, 1]]) 247 | coeff = np.dot(zoom, coeff) 248 | 249 | # move_back 250 | move_back = np.array([[1, 0, self.width*0.5], 251 | [0, 1, self.height*0.5], 252 | [0, 0, 1]]) 253 | coeff = np.dot(move_back, coeff) 254 | 255 | # Four corners should not exceed the boundaries of the origin 256 | flag = True 257 | for x in [0, self.cropped_width - 1]: 258 | for y in [0, self.cropped_height - 1]: 259 | dest = np.array([x, y, 1]) 260 | src = np.dot(coeff, dest) 261 | if between(src[0], 1, self.width - 2) == False or between(src[1], 1, self.height - 2) == False: 262 | flag = False 263 | break 264 | if flag: 265 | return coeff 266 | return None 267 | 268 | def spatial_transform(self, img1, img2, label): 269 | """ 270 | coeff = a1, a2, t1, 271 | a3, a4, t2, 272 | 0, 0, 1 273 | a1, a2, a3, a4 : rotate, zoom, squeeze ; t1, t2 : crop and translate 274 | 275 | src_grid = np.dot(coeff, dst_grid) 276 | """ 277 | coeff = self.generate_spatial_coeffs() 278 | if coeff is not None: 279 | grid = np.zeros((3, self.cropped_height, self.cropped_width)) 280 | xv, yv = np.meshgrid(np.arange(self.cropped_height), np.arange(self.cropped_width)) 281 | grid[0, :, :] = yv.T 282 | grid[1, :, :] = xv.T 283 | grid[2, :, :] = 1.0 284 | grid = grid.reshape(3, -1) 285 | grid = np.dot(coeff, grid).astype(np.float32) 286 | grid = grid.reshape((3, self.cropped_height, self.cropped_width)) 287 | img1_result = cv2.remap(img1, map1 = grid[0], map2=grid[1], interpolation=self.interpolation_method, 288 | borderValue=0) 289 | img2_result = cv2.remap(img2, map1=grid[0], map2=grid[1], interpolation=self.interpolation_method, 290 | borderValue=0) 291 | if label is not None : 292 | label_result = cv2.remap(label, map1=grid[0], map2=grid[1], interpolation=self.interpolation_method, 293 | borderValue=np.nan) 294 | if self.data_type == 'stereo': 295 | label_result /= coeff[0,0] 296 | elif self.data_type == 'flow': 297 | label_result = np.dot(label_result.reshape(-1,2), np.linalg.inv(coeff[:2, :2]).T) 298 | label_result = label_result.reshape((self.cropped_height, self.cropped_width, 2)) 299 | else: 300 | label_result = None 301 | 302 | return img1_result, img2_result, label_result 303 | else: 304 | # print("Augmentation: Exceeded maximum tries in finding spatial coeffs.") 305 | img1_result, img2_result, label_result = data_util.crop(img1, img2, label, target_height=self.cropped_height, 306 | target_width=self.cropped_width) 307 | return img1_result, img2_result, label_result 308 | 309 | def generate_chromatic_coeffs(self): 310 | 311 | coeff = edict() 312 | coeff.gamma = self.generate_random(self.gamma_range) 313 | coeff.brightness = self.generate_random(self.brightness_range) 314 | coeff.contrast = self.generate_random(self.contrast_range) 315 | coeff.rgb = np.array([self.generate_random(self.rgb_multiply_range) for i in range(3)]) 316 | return coeff 317 | 318 | def apply_chromatic_transform(self, img, coeff): 319 | # normalize into [0, 1] 320 | img = img / 255.0 321 | # color change 322 | brightness_in = img.sum(axis=2) 323 | img = img * coeff.rgb 324 | brightness_out = img.sum(axis=2) 325 | brightness_coeff = brightness_in / (brightness_out + 1E-5) 326 | brightness_coeff = np.expand_dims(brightness_coeff, 2) 327 | brightness_coeff = np.concatenate([brightness_coeff for i in range(3)], axis=2) 328 | # compensate brightness 329 | img = img * brightness_coeff 330 | img = clip(img, 0, 1.0) 331 | # gamma change 332 | img = cv2.pow(img, coeff.gamma) 333 | # brightness change 334 | img = cv2.add(img, coeff.brightness) 335 | # contrast change 336 | img = 0.5 + (img-0.5) * coeff.contrast 337 | img = clip(img, 0.0, 1.0) 338 | img = img * 255 339 | return img 340 | 341 | def chromatic_transform(self, img1, img2): 342 | coeff = self.generate_chromatic_coeffs() 343 | if random.uniform(0,1) < 0.5: 344 | img2 = self.apply_chromatic_transform(img2, coeff) 345 | else: 346 | img1 = self.apply_chromatic_transform(img1, coeff) 347 | return img1, img2 348 | 349 | def generate_eigenvec_coeffs(self): 350 | 351 | coeff = edict() 352 | coeff.pow_nomean = np.array([self.generate_random(self.ladd_pow), 353 | self.generate_random(self.col_pow), 354 | self.generate_random(self.col_pow)]) 355 | coeff.add_nomean = np.array([self.generate_random(self.ladd_add), 356 | self.generate_random(self.col_add), 357 | self.generate_random(self.col_add)]) 358 | coeff.multi_nomean = np.array([self.generate_random(self.ladd_mult), 359 | self.generate_random(self.col_mult), 360 | self.generate_random(self.col_mult)]) 361 | tmp = self.generate_random(self.sat_pow) 362 | coeff.pow_withmean = np.array([tmp, tmp]) 363 | 364 | tmp = self.generate_random(self.sat_add) 365 | coeff.add_withmean = np.array([tmp, tmp]) 366 | 367 | tmp = self.generate_random(self.sat_mult) 368 | coeff.mult_withmean = np.array([tmp, tmp]) 369 | 370 | coeff.lmult_pow = self.generate_random(self.lmult_pow) 371 | coeff.lmult_mult = self.generate_random(self.lmult_mult) 372 | coeff.lmult_add = self.generate_random(self.lmult_add) 373 | coeff.col_angle = self.generate_random(self.col_rotate) 374 | 375 | return coeff 376 | 377 | def apply_eigenvec_transform(self, coeff, img): 378 | 379 | shape = img.shape 380 | img = img.reshape(-1, 3) 381 | mean_rgb = img.mean(axis=0) 382 | eig = np.dot(self.eigvec, img.T) 383 | max_abs_eig = np.abs(eig).max(axis=1) 384 | 385 | mean_eig = np.dot(self.eigvec, mean_rgb) / (max_abs_eig + 1E-7) 386 | max_length = np.linalg.norm(max_abs_eig, ord=2) 387 | 388 | # doing the nomean stuff 389 | img = img - mean_rgb 390 | eig = np.dot(self.eigvec, img.T) 391 | eig = eig.T 392 | eig = eig / (max_abs_eig + 1E-7) 393 | eig = eig.reshape(shape) 394 | for i in range(3): 395 | eig[:, :, i] = cv2.pow(np.abs(eig[:, :, i]), coeff.pow_nomean[i]) * np.sign(eig[:, :, i]) 396 | eig = eig + coeff.add_nomean 397 | eig = eig * coeff.multi_nomean 398 | 399 | # re-adding the mean 400 | eig = eig + mean_eig 401 | eig[:, :, 0] = cv2.pow(np.abs(eig[:, :, 0]), coeff.pow_withmean[0]) * np.sign(eig[:, :, 0]) 402 | eig[:, :, 0] = eig[:, :, 0] + coeff.add_withmean[0] 403 | eig[:, :, 0] = eig[:, :, 0] * coeff.mult_withmean[0] 404 | 405 | # doing the withmean stuff 406 | s = np.sqrt(eig[:, :, 1] * eig[:, :, 1] + eig[:, :, 2] * eig[:, :, 2]) 407 | s1 = s.copy() 408 | s1 = cv2.pow(s1, coeff.pow_withmean[1]) 409 | s1 = s1 + coeff.add_withmean[1] 410 | s1[s1 < 0] = 0 411 | s1 = s1 * coeff.mult_withmean[1] 412 | 413 | if coeff.col_angle !=0 : 414 | tmp1 = cos(coeff.col_angle) * eig[:, :, 1] - sin(coeff.col_angle) * eig[:, :, 2] 415 | tmp2 = sin(coeff.col_angle) * eig[:, :, 1] + cos(coeff.col_angle) * eig[:, :, 2] 416 | eig[:, :, 1] = tmp1 417 | eig[:, :, 2] = tmp2 418 | 419 | eig = eig * max_abs_eig 420 | l1 = np.linalg.norm(eig, axis=2, ord=2) 421 | l1 = l1 / (max_length + 1E-7) 422 | 423 | for i in range(1, 3): 424 | eig[:, :, i] = eig[:, :, i] / (s + 1E-7) * s1 425 | 426 | l = np.linalg.norm(eig, ord=2, axis=2) 427 | l1 = cv2.pow(l1, coeff.lmult_pow) 428 | l1 = l1 + coeff.lmult_add 429 | l1[ l1 < 0] = 0 430 | l1 = l1 * coeff.lmult_mult 431 | l1 = l1 * max_length 432 | 433 | for i in range(3): 434 | eig[:, :, i] = eig[:, :, i] / (l + 1E-7) * l1 435 | eig[:, :, i] = np.where(eig[:, :, i] < max_abs_eig[i], eig[:, :, i], max_abs_eig[i]) 436 | # convert to RGB 437 | rgb = np.dot(eig, self.eigvec) 438 | rgb = clip(rgb, 0, 255) 439 | rgb = rgb.reshape(shape) 440 | return rgb 441 | 442 | def eigenvec_transform(self, img1, img2): 443 | 444 | coeff = self.generate_eigenvec_coeffs() 445 | img1 = self.apply_eigenvec_transform(coeff, img1) 446 | img2 = self.apply_eigenvec_transform(coeff, img2) 447 | 448 | return img1, img2 449 | 450 | def __call__(self, img1, img2, label, discount_coeff): 451 | """ 452 | Perform data augmentation 453 | 454 | Parameters 455 | ---------- 456 | img1: numpy.ndarray 457 | img2: numpy.ndarray 458 | label: numpy.ndarray 459 | discount_coeff : float 460 | the discount of augmentation coefficients 461 | """ 462 | self.height = img1.shape[0] 463 | self.width = img1.shape[1] 464 | self.discount_coeff = discount_coeff 465 | if np.random.uniform(0, 1) < self.augment_ratio * self.discount_coeff: 466 | img1, img2, label = self.spatial_transform(img1, img2, label) 467 | img1, img2 = self.eigenvec_transform(img1, img2) 468 | img1, img2 = self.chromatic_transform(img1, img2) 469 | noise = self.generate_random(self.noise_range) 470 | img1 += noise 471 | img2 += noise 472 | else: 473 | img1, img2, label = data_util.crop(img1, img2, label, self.cropped_height, self.cropped_width) 474 | return img1, img2, label 475 | -------------------------------------------------------------------------------- /utils/filter.py: -------------------------------------------------------------------------------- 1 | from guided_filter.core import filters 2 | import numpy as np 3 | 4 | def weight_median_filter(i, left, radius, epsilon, mask): 5 | """ 6 | Constant Time Weighted Median Filtering for Stereo Matching and Beyond 7 | Parameters 8 | ---------- 9 | i : ndarray 10 | disparity 11 | left : ndarray 12 | original image 13 | radius : int 14 | epsilon : float 15 | mask: ndarray of boolean 16 | indicate which need to be changed 17 | Returns 18 | ------- 19 | dispout : ndarray 20 | filted disparity 21 | """ 22 | dispin = i.copy() 23 | dispout = dispin.copy() 24 | dispout[mask] = 0 25 | vecdisp = np.unique(dispin) 26 | 27 | tot = np.zeros(i.shape) 28 | imgaccum = np.zeros(i.shape) 29 | 30 | gf = filters.GuidedFilterColor(left.copy(), radius, epsilon) 31 | 32 | for d in vecdisp: 33 | if d<=0: 34 | continue 35 | ab = gf._computeCoefficients((dispin==d).astype(float)) 36 | weight = gf._computeOutput(ab, gf._I) 37 | tot = tot + weight 38 | 39 | for d in vecdisp: 40 | if d<=0: 41 | continue 42 | ab = gf._computeCoefficients((dispin==d).astype(float)) 43 | weight = gf._computeOutput(ab, gf._I) 44 | imgaccum = imgaccum + weight 45 | musk = (imgaccum > 0.5*tot) & (dispout==0) & (mask) & (tot> 0.0001) 46 | dispout[musk] = d 47 | 48 | return dispout -------------------------------------------------------------------------------- /utils/io.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import random 3 | from config import cfg 4 | import tifffile as tiff 5 | 6 | def scale_percentile(matrix): 7 | w, h, d = matrix.shape 8 | matrix = np.reshape(matrix, [w * h, d]).astype(np.float64) 9 | mins = np.percentile(matrix, 1, axis=0) 10 | maxs = np.percentile(matrix, 99, axis=0) - mins 11 | matrix = (matrix - mins[None, :]) / maxs[None, :] 12 | matrix = np.reshape(matrix, [w, h, d]) 13 | matrix = matrix.clip(0, 1) 14 | return matrix 15 | 16 | def read_rawdata(FILE_2015=cfg.dirs.FILE_2015, 17 | FILE_2017=cfg.dirs.FILE_2017, 18 | FILE_tinysample=cfg.dirs.FILE_tinysample, 19 | FILE_cadastral2015=cfg.dirs.FILE_cadastral2015): 20 | 21 | # Read raw data 22 | im_2015 = tiff.imread(FILE_2015).transpose([1, 2, 0]) 23 | im_2017 = tiff.imread(FILE_2017).transpose([1, 2, 0]) 24 | im_tiny = tiff.imread(FILE_tinysample) 25 | im_cada = tiff.imread(FILE_cadastral2015) 26 | 27 | assert im_2015.shape[:2] == tuple(cfg.data.data_shape) 28 | assert im_2015.shape[:2] == tuple(cfg.data.data_shape) 29 | assert im_2015.shape[:2] == tuple(cfg.data.data_shape) 30 | 31 | return im_2015, im_2017, im_tiny, im_cada 32 | 33 | def read_data(): 34 | 35 | pca_img2015 = np.load(cfg.dirs.PCA_img2015) 36 | pca_img2017 = np.load(cfg.dirs.PCA_img2017) 37 | tiny_label = np.load(cfg.dirs.tiny_label) 38 | 39 | return pca_img2015, pca_img2017, tiny_label 40 | 41 | def sample_label(pca_img2015, pca_img2017, label, num_pos, num_neg): 42 | 43 | data = [] 44 | labeled_points = np.argwhere(label == label) 45 | explore = [(random.randint(cfg.data.r, label.shape[0] - cfg.data.r), 46 | random.randint(cfg.data.r, label.shape[1] - cfg.data.r)) for i in range(num_neg+100)] 47 | rnd_range = [i for i in range(labeled_points.shape[0])] 48 | random.shuffle(rnd_range) 49 | rnd_range = rnd_range[:num_pos] + explore 50 | count = 0 51 | 52 | for i in rnd_range: 53 | 54 | if type(i) == int: 55 | rnd_y = random.randint(-cfg.data.r + 20, cfg.data.r - 20) 56 | rnd_x = random.randint(-cfg.data.r + 20, cfg.data.r - 20) 57 | y, x = labeled_points[i] 58 | y += rnd_y 59 | x += rnd_x 60 | else: 61 | y, x = i 62 | 63 | img1_patch = pca_img2015[y - cfg.data.r:y + cfg.data.r, x - cfg.data.r:x + cfg.data.r] 64 | img2_patch = pca_img2017[y - cfg.data.r:y + cfg.data.r, x - cfg.data.r:x + cfg.data.r] 65 | label_patch = label[y - cfg.data.r:y + cfg.data.r, x - cfg.data.r:x + cfg.data.r] 66 | 67 | if img1_patch.size == 3 * cfg.data.batch_shape[2] ** 2: 68 | data.append([img1_patch, img2_patch, label_patch, y, x]) 69 | count += 1 70 | 71 | if count == num_neg + num_pos: 72 | break 73 | 74 | return data -------------------------------------------------------------------------------- /utils/label_data.py: -------------------------------------------------------------------------------- 1 | import cv2 2 | import os 3 | import queue as Queue 4 | import numpy as np 5 | DATA_DIR = "/Volumes/DATA/train" 6 | 7 | IM_ROWS = 5106 8 | IM_COLS = 15106 9 | ROI_SIZE = 960 10 | alpha = 0.3 # 1是不透明,0是全透明 11 | 12 | def on_mouse(event, x, y, flags, params): 13 | img, points = params['img'], params['points'] 14 | if event == cv2.EVENT_LBUTTONDOWN: 15 | points.append((x, y)) 16 | 17 | if event == cv2.EVENT_RBUTTONDOWN: 18 | points.pop() 19 | 20 | temp = img.copy() 21 | if len(points) > 2: 22 | cv2.fillPoly(temp, [np.array(points)], (0, 0, 255)) 23 | 24 | for i in range(len(points)): 25 | cv2.circle(temp, points[i], 1, (0, 0, 255)) 26 | 27 | cv2.circle(temp, (x, y), 3, (0, 255, 0)) 28 | cv2.imshow(params['name'], temp) 29 | 30 | def label_img(img, label_name): 31 | c = 'x' 32 | if os.path.exists(label_name): 33 | tiny = cv2.imread(label_name, 1) 34 | else: 35 | tiny = np.zeros(img.shape) 36 | 37 | while c not in ['n', 'p', 'q']: 38 | cv2.namedWindow(label_name, cv2.WINDOW_AUTOSIZE) 39 | temp = img.copy() 40 | points = [] 41 | cv2.setMouseCallback(label_name, on_mouse, {'img': temp, 'points': points, 'name': label_name}) 42 | cv2.imshow(label_name, img) 43 | c = chr(cv2.waitKey(0)) 44 | 45 | if c == 's': 46 | if len(points) > 0: 47 | cv2.fillPoly(img, [np.array(points)], (0, 0, 255)) 48 | cv2.fillPoly(tiny, [np.array(points)], (255, 255, 255)) 49 | elif c == 'd': 50 | if len(points) > 0: 51 | mask = np.zeros((img.shape[0], img.shape[1])) 52 | 53 | cv2.fillPoly(tiny, [np.array(points)], (0, 0, 0)) 54 | cv2.fillPoly(mask, [np.array(points)], True, 1) 55 | mask = (mask == 1) 56 | img[mask] = origin_img[mask] 57 | temp[mask] = origin_img[mask] 58 | 59 | print(label_name) 60 | cv2.imwrite(label_name, tiny) 61 | cv2.destroyWindow(label_name) 62 | if c == 'n': 63 | return 0 64 | elif c == 'p': 65 | return 1 66 | elif c == 'q': 67 | return -1 68 | 69 | if __name__ == '__main__': 70 | # i 是你所要标记的行,year是要标记的年份 71 | i = 1 72 | year = 2015 73 | # 按键说明 74 | # s 保存当前多边形 75 | # d 清除当前多边形内的所有标记 76 | # n 保存当前标记图片,并切换到下一张 77 | # p 保存当前标记图片,并返回上一张 78 | # q 退出程序 79 | 80 | pre = Queue.LifoQueue() 81 | 82 | j = 0 83 | origin_img = None 84 | while j < int(IM_COLS // ROI_SIZE): 85 | 86 | ss1 = '{}/{}/{}_{}_{}_.jpg'.format(DATA_DIR, year, i, j, ROI_SIZE) 87 | 88 | ss2 = '{}/mylabel_{}/{}_{}_{}_.jpg'.format(DATA_DIR, year, i, j, ROI_SIZE) 89 | 90 | src = cv2.imread(ss1, 1) 91 | origin_img = src.copy() 92 | if os.path.exists(ss2): 93 | tiny = cv2.imread(ss2, 0) 94 | src[tiny > 128] = src[tiny > 128] * (1 - alpha) + np.array([(0, 0, 255)]) * alpha 95 | 96 | status = label_img(src, ss2) 97 | if status == 0: 98 | pre.put(ss2) 99 | j += 1 100 | elif status == 1: 101 | try: 102 | pre.get(False) 103 | j -= 1 104 | except Queue.Empty: 105 | print('当前是第一张,不能回退了') 106 | elif status == -1: 107 | exit() -------------------------------------------------------------------------------- /utils/misc.py: -------------------------------------------------------------------------------- 1 | import mxnet as mx 2 | import subprocess 3 | import os 4 | from config import cfg 5 | from sklearn.metrics import f1_score, accuracy_score 6 | 7 | def load_checkpoint(prefix, epoch): 8 | save_dict = mx.nd.load('%s-%04d.params' % (prefix, epoch)) 9 | arg_params = {} 10 | aux_params = {} 11 | for k, v in save_dict.items(): 12 | tp, name = k.split(':', 1) 13 | if tp == 'arg': 14 | arg_params[name] = v 15 | if tp == 'aux': 16 | aux_params[name] = v 17 | return arg_params,aux_params 18 | 19 | def load_pretrainModel(name, net): 20 | # Load pretrained model 21 | arg_name = net.list_arguments() 22 | aux_name = net.list_auxiliary_states() 23 | pretrain_args, pretrain_auxs = load_checkpoint(os.path.join(cfg.dirs.pretrain_model,name), 0) 24 | 25 | args = {} 26 | auxs = {} 27 | for key in pretrain_args: 28 | if key in arg_name: 29 | args[key] = pretrain_args[key] 30 | 31 | for key in pretrain_auxs: 32 | if key in aux_name: 33 | auxs[key][:] = pretrain_auxs[key] 34 | 35 | return args, auxs 36 | 37 | def get_gpus(): 38 | """ 39 | return a list of GPUs 40 | """ 41 | try: 42 | re = subprocess.check_output(["nvidia-smi", "-L"], universal_newlines=True) 43 | except OSError: 44 | return [] 45 | return range(len([i for i in re.split('\n') if 'GPU' in i])) 46 | 47 | 48 | def F1_score(pred, gt): 49 | 50 | mask = pred == pred 51 | val_pred = pred[mask] 52 | val_gt = gt[mask] 53 | score = f1_score(val_gt, val_pred, average='binary') 54 | density = mask.sum() / float(gt.size) 55 | 56 | return score, density 57 | 58 | 59 | def accuracy(pred, gt): 60 | 61 | mask = pred == pred 62 | val_pred = pred[mask] 63 | val_gt = gt[mask] 64 | 65 | score = accuracy_score(val_gt, val_pred) 66 | 67 | return score -------------------------------------------------------------------------------- /utils/plot_curve.py: -------------------------------------------------------------------------------- 1 | import matplotlib.pyplot as plt 2 | import numpy as np 3 | import re 4 | import argparse 5 | 6 | res = [re.compile('.*Epoch\[(\d+)\] .*Train-accuracy.*=([.\d]+)'), 7 | re.compile('.*Epoch\[(\d+)\] Validation-accuracy.*=([.\d]+)')] 8 | 9 | 10 | def plot_acc(log_name, color="r"): 11 | 12 | train_name = log_name.replace(".log", " train") 13 | val_name = log_name.replace(".log", " val") 14 | 15 | data = {} 16 | with open(log_name) as f: 17 | lines = f.readlines() 18 | for l in lines: 19 | i = 0 20 | for r in res: 21 | m = r.match(l) 22 | if m is not None: # i=0, match train acc 23 | break 24 | i += 1 # i=1, match validation acc 25 | if m is None: 26 | continue 27 | assert len(m.groups()) == 2 28 | epoch = int(m.groups()[0]) 29 | val = float(m.groups()[1]) 30 | if epoch not in data: 31 | data[epoch] = [0] * len(res) * 2 32 | data[epoch][i*2] += val # data[epoch], val:number 33 | data[epoch][i*2+1] += 1 34 | 35 | train_acc = [] 36 | val_acc = [] 37 | for k, v in data.items(): 38 | if v[1]: 39 | train_acc.append(1.0 - v[0]/(v[1])) 40 | if v[2]: 41 | val_acc.append(1.0 - v[2]/(v[3])) 42 | 43 | x_train = np.arange(len(train_acc)) 44 | x_val = np.arange(len(val_acc)) 45 | plt.plot(x_train, train_acc, '-', linestyle='--', color=color, linewidth=2, label=train_name) 46 | plt.plot(x_val, val_acc, '-', linestyle='-', color=color, linewidth=2, label=val_name) 47 | plt.legend(loc="best") 48 | plt.xticks(np.arange(0, 131, 10)) 49 | plt.yticks(np.arange(0.1, 0.71, 0.05)) 50 | plt.xlim([0, 130]) 51 | plt.ylim([0.1, 0.7]) 52 | 53 | def main(): 54 | plt.figure(figsize=(14, 8)) 55 | plt.xlabel("epoch") 56 | plt.ylabel("Top-1 error") 57 | color = ['r', 'g', 'b', 'c', 'm', 'y', 'k', 'w'] 58 | log_files = [i for i in args.logs.split(',')] 59 | color = color[:len(log_files)] 60 | for c in range(len(log_files)): 61 | plot_acc(log_files[c], color[c]) 62 | plt.grid(True) 63 | plt.savefig(args.out) 64 | 65 | if __name__ == "__main__": 66 | parser = argparse.ArgumentParser(description='Parses log file and generates train/val curves, using like: \n' 67 | 'python -u plot_curve.py --log=resnet-18.log,resnet-50.log') 68 | parser.add_argument('--logs', type=str, default="resnet-50.log", 69 | help='the path of log file, --logs=resnet-50.log,resnet-101.log') 70 | parser.add_argument('--out', type=str, default="training-curve.png", help='the name of output curve ') 71 | args = parser.parse_args() 72 | main() -------------------------------------------------------------------------------- /utils/preprocess.py: -------------------------------------------------------------------------------- 1 | import cv2 2 | import numpy as np 3 | from config import cfg 4 | from sklearn.decomposition import PCA 5 | 6 | from . import io 7 | 8 | 9 | def concat_patche(): 10 | 11 | s = 960 12 | data2015 = np.zeros(cfg.data.data_shape + [3]) 13 | data2017 = np.zeros(cfg.data.data_shape + [3]) 14 | 15 | label2015 = np.zeros(cfg.data.data_shape) 16 | label2017 = np.zeros(cfg.data.data_shape) 17 | 18 | for i in range(6): 19 | for j in range(16): 20 | 21 | Patch2015 = cv2.imread(cfg.dirs.FILE_2015patch_img + '{}_{}_960_.jpg'.format(i, j), 1) 22 | Label2015 = cv2.imread(cfg.dirs.FILE_2015patch_label + '{}_{}_960_.jpg'.format(i, j), 0) 23 | 24 | h, w = Patch2015.shape[:2] 25 | if (i != 5 and j != 15): 26 | assert (h == s and w == s) 27 | 28 | _, binary = cv2.threshold(Label2015, 128, 1, cv2.THRESH_BINARY) 29 | 30 | data2015[s * i: s * i + h, s * j: s * j + w, :3] = Patch2015 31 | label2015[s * i: s * i + h, s * j: s * j + w] = binary 32 | 33 | Patch2017 = cv2.imread(cfg.dirs.FILE_2017patch_img + '{}_{}_960_.jpg'.format(i, j), 1) 34 | Label2017 = cv2.imread(cfg.dirs.FILE_2017patch_label + '{}_{}_960_.jpg'.format(i, j), 0) 35 | 36 | h, w = Patch2017.shape[:2] 37 | if (i != 5 and j != 15): 38 | assert (h == s and w == s) 39 | 40 | _, binary = cv2.threshold(Label2017, 128, 1, cv2.THRESH_BINARY) 41 | 42 | data2017[s * i: s * i + h, s * j: s * j + w, :3] = Patch2017 43 | label2017[s * i: s * i + h, s * j: s * j + w] = binary 44 | 45 | # The two images cannot be completely the same 46 | assert (np.abs(Patch2015 - Patch2017) > 0).any() 47 | if (Label2015 is not None): 48 | assert (np.abs(Label2015 - Label2017) > 0).any() 49 | 50 | assert data2015.shape[:2] == tuple(cfg.data.data_shape) 51 | assert data2017.shape[:2] == tuple(cfg.data.data_shape) 52 | assert label2015.shape == tuple(cfg.data.data_shape) 53 | assert label2017.shape == tuple(cfg.data.data_shape) 54 | assert np.abs(label2015 - label2017).any() 55 | 56 | return label2015, label2017 57 | 58 | def pca_transform(): 59 | 60 | im_2015, im_2017, im_tiny, im_cada = io.read_rawdata() 61 | randn = np.random.uniform(0, 1, size=(cfg.data.data_shape[0], cfg.data.data_shape[1])) 62 | mask = randn < 0.1 63 | tmp = np.r_[im_2015[mask], im_2017[mask]] 64 | pca = PCA(n_components=3) 65 | pca.fit(tmp) 66 | 67 | tmp2015 = pca.transform(im_2015.reshape((cfg.data.data_shape[0] * cfg.data.data_shape[1], 4))).reshape( 68 | (cfg.data.data_shape[0], cfg.data.data_shape[1], 3)) 69 | tmp2017 = pca.transform(im_2017.reshape((cfg.data.data_shape[0] * cfg.data.data_shape[1], 4))).reshape( 70 | (cfg.data.data_shape[0], cfg.data.data_shape[1], 3)) 71 | 72 | tmp2015 = io.scale_percentile(tmp2015) * 255 73 | tmp2017 = io.scale_percentile(tmp2017) * 255 74 | 75 | label = (im_tiny > 0).astype(float) 76 | label = label[:,:,0] 77 | label[label == 0] = np.nan 78 | 79 | return tmp2015, tmp2017, label 80 | 81 | 82 | if __name__ == '__main__': 83 | 84 | label2015, label2017 = concat_patche() 85 | np.save(cfg.dirs.FILE_label2015, label2015) 86 | np.save(cfg.dirs.FILE_label2017, label2017) 87 | 88 | pca_img2015, pca_img2017, tiny_label = pca_transform() 89 | 90 | np.save(cfg.dirs.PCA_img2015, pca_img2015) 91 | np.save(cfg.dirs.PCA_img2017, pca_img2017) 92 | np.save(cfg.dirs.tiny_label, tiny_label) 93 | 94 | print('Positive sample in 2015 : %f' % ((label2015 == 1).sum() / float(label2015.size))) 95 | print('Positive sample in 2017 : %f' % ((label2017 == 1).sum() / float(label2017.size))) 96 | --------------------------------------------------------------------------------