├── .gitignore ├── README.md ├── changeos └── __init__.py └── setup.py /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by .ignore support plugin (hsz.mobi) 2 | 3 | .idea 4 | dev 5 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 |

Building damage assessment for rapid disaster response with a deep object-based semantic change detection framework:
from natural disasters to man-made disasters

3 | 4 | 5 |
by Zhuo Zheng, Yanfei Zhong, Junjue Wang, Ailong Ma and Liangpei Zhang
6 | 7 | [[`Paper`]](https://www.sciencedirect.com/science/article/pii/S0034425721003564) [[`BibTeX`](#Citation)] 8 | 9 |
10 |

11 |
12 | 13 | 14 | This is an official implementation of ChangeOS in our RSE 2021 paper [Building damage assessment for rapid disaster response with a deep object-based semantic change detection framework: from natural disasters to man-made disasters](https://www.sciencedirect.com/science/article/pii/S0034425721003564). 15 | 16 | 17 | --------------------- 18 | 19 | ## Highlights 20 | 21 | - Deep object-based semantic change detection framework (ChangeOS) is proposed. 22 | - ChangeOS seamlessly integrates object-based image analysis and deep learning. 23 | - City-scale building damage assessment can be achieved within one minute. 24 | - A global-scale dataset is used to evaluate the effectiveness of ChangeOS. 25 | - Two local-scale datasets are used to show its great generalization ability. 26 | 27 | 28 | 29 | ## Getting Started 30 | ### Installation 31 | 32 | ```bash 33 | pip install changeos 34 | ``` 35 | 36 | #### Requirements: 37 | - pytorch == 1.10.0 38 | - python >=3.6 39 | - skimage 40 | - Pillow 41 | 42 | ### Usage 43 | 44 | ```python 45 | # changeos has four APIs 46 | # (e.g., 'list_available_models', 'from_name', 'visualize', 'demo_data') 47 | import changeos 48 | 49 | 50 | # constructing ChangeOS model 51 | # support 'changeos_r18', 'changeos_r34', 'changeos_r50', 'changeos_r101' 52 | model = changeos.from_name('changeos_r101') # take 'changeos_r101' as example 53 | 54 | # load your data or our prepared demo data 55 | # numpy array of shape [1024, 1024, 3], [1024, 1024, 3] 56 | pre_disaster_image, post_disaster_image = changeos.demo_data() 57 | 58 | # model inference 59 | loc, dam = model(pre_disaster_image, post_disaster_image) 60 | 61 | # put color map on raw prediction 62 | loc, dam = changeos.visualize(loc, dam) 63 | 64 | # visualize by matplotlib 65 | import matplotlib.pyplot as plt 66 | plt.subplot(121) 67 | plt.imshow(loc) 68 | plt.subplot(122) 69 | plt.imshow(dam) 70 | plt.show() 71 | 72 | ``` 73 | 74 | 75 | 76 | ## Citation 77 | If you use ChangeOS in your research, please cite the following paper: 78 | ```text 79 | @article{zheng2021building, 80 | title={Building damage assessment for rapid disaster response with a deep object-based semantic change detection framework: from natural disasters to man-made disasters}, 81 | author={Zheng, Zhuo and Zhong, Yanfei and Wang, Junjue and Ma, Ailong and Zhang, Liangpei}, 82 | journal={Remote Sensing of Environment}, 83 | volume={265}, 84 | pages={112636}, 85 | year={2021}, 86 | publisher={Elsevier} 87 | } 88 | ``` -------------------------------------------------------------------------------- /changeos/__init__.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | 4 | import numpy as np 5 | from skimage import measure 6 | from skimage.io import imread 7 | from torch.hub import download_url_to_file, get_dir 8 | import torch 9 | import torchvision.transforms.functional as F 10 | from PIL import Image 11 | from urllib.parse import urlparse 12 | 13 | __all__ = ['from_name', 'list_available_models', 'visualize'] 14 | V = 0.2 15 | 16 | AVAILABLE_MODELS = dict( 17 | changeos_r18=f'https://github.com/Z-Zheng/ChangeOS/releases/download/v{V}/changeos_r18.pt', 18 | changeos_r34=f'https://github.com/Z-Zheng/ChangeOS/releases/download/v{V}/changeos_r34.pt', 19 | changeos_r50=f'https://github.com/Z-Zheng/ChangeOS/releases/download/v{V}/changeos_r50.pt', 20 | changeos_r101=f'https://github.com/Z-Zheng/ChangeOS/releases/download/v{V}/changeos_r101.pt', 21 | ) 22 | 23 | 24 | class ChangeOS(object): 25 | def __init__(self, jit_model): 26 | self.model = jit_model 27 | self.device = torch.device('cuda') if torch.cuda.is_available() else torch.device('cpu') 28 | self.model.to(self.device) 29 | 30 | def __call__(self, pre_disaster_image, post_disaster_image): 31 | image = np.concatenate([pre_disaster_image, post_disaster_image], axis=2) 32 | image = torch.from_numpy(image).permute(2, 0, 1).float() 33 | 34 | image = F.normalize(image, 35 | mean=[123.675, 116.28, 103.53, 123.675, 116.28, 103.53], 36 | std=[58.395, 57.12, 57.375, 58.395, 57.12, 57.375], 37 | inplace=True) 38 | 39 | image = image.unsqueeze(0).to(self.device) 40 | loc, dam = self.model(image) 41 | 42 | loc, dam = object_based_infer(loc, dam) 43 | loc = loc.squeeze().astype(np.uint8) 44 | dam = dam.squeeze().astype(np.uint8) 45 | 46 | return loc, dam 47 | 48 | 49 | def object_based_infer(pre_logit, post_logit): 50 | loc = (pre_logit > 0.).cpu().squeeze(1).numpy() 51 | dam = post_logit.argmax(dim=1).cpu().squeeze(1).numpy() 52 | 53 | refined_dam = np.zeros_like(dam) 54 | for i, (single_loc, single_dam) in enumerate(zip(loc, dam)): 55 | refined_dam[i, :, :] = _object_vote(single_loc, single_dam) 56 | 57 | return loc, refined_dam 58 | 59 | 60 | def _object_vote(loc, dam): 61 | damage_cls_list = [1, 2, 3, 4] 62 | local_mask = loc 63 | labeled_local, nums = measure.label(local_mask, connectivity=2, background=0, return_num=True) 64 | region_idlist = np.unique(labeled_local) 65 | if len(region_idlist) > 1: 66 | dam_mask = dam 67 | new_dam = local_mask.copy() 68 | for region_id in region_idlist: 69 | if all(local_mask[local_mask == region_id]) == 0: 70 | continue 71 | region_dam_count = [int(np.sum(dam_mask[labeled_local == region_id] == dam_cls_i)) * cls_weight \ 72 | for dam_cls_i, cls_weight in zip(damage_cls_list, [8., 38., 25., 11.])] 73 | dam_index = np.argmax(region_dam_count) + 1 74 | new_dam = np.where(labeled_local == region_id, dam_index, new_dam) 75 | else: 76 | new_dam = local_mask.copy() 77 | return new_dam 78 | 79 | 80 | def _download_model(name): 81 | assert name in list_available_models(), f'{name} is unsupported.' 82 | url = AVAILABLE_MODELS[name] 83 | hub_dir = get_dir() 84 | model_dir = os.path.join(hub_dir, 'checkpoints') 85 | parts = urlparse(url) 86 | filename = os.path.basename(parts.path) 87 | cached_file = os.path.join(model_dir, filename) 88 | if not os.path.exists(cached_file): 89 | sys.stderr.write('Downloading: "{}" to {}\n'.format(url, cached_file)) 90 | os.makedirs(model_dir, exist_ok=True) 91 | download_url_to_file(url, cached_file) 92 | 93 | model = torch.jit.load(cached_file) 94 | return model 95 | 96 | 97 | def list_available_models(): 98 | return list(AVAILABLE_MODELS.keys()) 99 | 100 | 101 | def from_name(name): 102 | model = _download_model(name) 103 | model.eval() 104 | model = ChangeOS(model) 105 | return model 106 | 107 | 108 | def visualize(loc, dam): 109 | loc = Image.fromarray(loc) 110 | loc.putpalette([0, 0, 0, 111 | 255, 255, 255]) 112 | loc = loc.convert('RGB') 113 | loc = np.asarray(loc) 114 | 115 | dam = Image.fromarray(dam) 116 | dam.putpalette([0, 0, 0, 117 | 255, 255, 255, 118 | 0, 255, 0, 119 | 248, 179, 101, 120 | 255, 0, 0]) 121 | dam = dam.convert('RGB') 122 | dam = np.asarray(dam) 123 | 124 | return loc, dam 125 | 126 | 127 | def demo_data(): 128 | pre = imread(f'https://github.com/Z-Zheng/ChangeOS/releases/download/v{V}/socal-fire_00000667_pre_disaster.png') 129 | post = imread(f'https://github.com/Z-Zheng/ChangeOS/releases/download/v{V}/socal-fire_00000667_post_disaster.png') 130 | return pre, post 131 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import find_packages, setup 2 | 3 | install_requires = [ 4 | 'numpy', 5 | 'Pillow', 6 | 'scikit-image', 7 | 'torch', 8 | 'torchvision' 9 | ] 10 | setup( 11 | name='changeos', 12 | version='0.2', 13 | description='ChangeOS SDK', 14 | keywords='Remote Sensing, ' 15 | 'Earth Vision, ' 16 | 'Deep Learning, ' 17 | 'Building Damage Assessment, ' 18 | 'Change Detection', 19 | packages=find_packages(exclude=[]), 20 | classifiers=[ 21 | 'Development Status :: 4 - Beta', 22 | 'License :: OSI Approved :: Apache Software License', 23 | 'Operating System :: OS Independent', 24 | 'Programming Language :: Python :: 3.6', 25 | 'Programming Language :: Python :: 3.7', 26 | 'Programming Language :: Python :: 3.8', 27 | 'Programming Language :: Python :: 3.9', 28 | 'Topic :: Utilities', 29 | ], 30 | url='https://github.com/Z-Zheng/ChangeOS', 31 | author='Zhuo Zheng', 32 | author_email='zhengzhuo@whu.edu.cn', 33 | license='', 34 | setup_requires=[], 35 | tests_require=[], 36 | install_requires=install_requires, 37 | zip_safe=False) 38 | --------------------------------------------------------------------------------