├── .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 |
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 |
--------------------------------------------------------------------------------