[go: nahoru, domu]

Skip to content

Commit

Permalink
Add modules for Joint Semantic Segmentation/ DHM estimation
Browse files Browse the repository at this point in the history
  • Loading branch information
venkai committed Apr 27, 2020
1 parent 3395a54 commit 4b1bb6e
Show file tree
Hide file tree
Showing 39 changed files with 29,391 additions and 2 deletions.
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
# Modules for Shadow-Removal, Inpainting and MSI2RGB
# Modules for Shadow-Removal, Inpainting, MSI2RGB and Joint Semantic Segmentation/ Single-View DHM estimation
![placeholder](https://obj.umiacs.umd.edu/deploy-core3d/placeholder.png)
The list of modules are as follows:
* Joint Shadow Removal and Shadow Probability Estimation for Satellite Imagery.
* Inpainting
1. RGB to RGB inpainting
2. RGBD to RGBD tiled iterative inpainting.
* 8-band MSI (unknown normalization/sensor data) to photo-realistic RGB, shadow-free RGB and shadow probabilities.
* 8-band MSI (unknown normalization/sensor data) to photo-realistic RGB, shadow-free RGB and shadow probabilities.
* Joint Semantic Segmentation and single-view DHM estimation from satellite imagery.

# Usage (using pre-built docker images on [dockerhub](https://hub.docker.com/u/venkai))

Expand All @@ -14,6 +15,7 @@ Each module has its own docker image that is built using [NVcaffe-v17](https://g
- **[RGB inpainting](https://hub.docker.com/r/venkai/inpainting-rgb)** : `docker pull venkai/inpainting-rgb:latest`
- **[RGBD iterative inpainting](https://hub.docker.com/r/venkai/inpainting-iterative-rgbd)** : `docker pull venkai/inpainting-iterative-rgbd:latest`
- **[MSI2RGB](https://hub.docker.com/r/venkai/msi-to-rgb)**: `docker pull venkai/msi-to-rgb:latest`
- **[Joint segmentation/DHM-estimation](https://hub.docker.com/r/venkai/joint-seg-dhm)**: `docker pull venkai/joint-seg-dhm:latest`

To run any module, do
`docker run --gpus 1 -it venkai/[module-name]`
Expand Down
1 change: 1 addition & 0 deletions software/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ Each module has its own docker image that is built using [NVcaffe-v17](https://g
- **[RGB inpainting](https://hub.docker.com/r/venkai/inpainting-rgb)** : `docker pull venkai/inpainting-rgb:latest`
- **[RGBD iterative inpainting](https://hub.docker.com/r/venkai/inpainting-iterative-rgbd)** : `docker pull venkai/inpainting-iterative-rgbd:latest`
- **[MSI2RGB](https://hub.docker.com/r/venkai/msi-to-rgb)**: `docker pull venkai/msi-to-rgb:latest`
- **[Joint segmentation/DHM-estimation](https://hub.docker.com/r/venkai/joint-seg-dhm)**: `docker pull venkai/joint-seg-dhm:latest`

To run any module, do
`docker run --gpus 1 -it venkai/[module-name]`
Expand Down
45 changes: 45 additions & 0 deletions software/joint_seg_dhm/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# Modules for Joint Satellite Segmentation/DHM estimation

![placeholder](https://obj.umiacs.umd.edu/deploy-core3d/software/joint_seg_dhm/placeholders/placeholder.png)

Takes an 8-band MSI or 3-channel RGB or both as input and produce either DHM estimate or
segmentation estimate (ground, building, tree, water, road) or both jointly. In case DHM is
already known, there is another model provided that takes all 3 modalities (RGB, MSI and DHM)
as input and outputs a segmentation estimate. Refer to the [GRSS data page](https://github.com/pubgeo/dfc2019/tree/master/data#classification-labels) for information on how semantic classes are labelled.

In total, the following **8** modules are provided:
1. `msi_to_agl`: Takes MSI as input and produces DHM estimate.
2. `msi_to_cls`: Takes MSI as input and produces Segmentation estimate.
3. `rgb_to_agl`: Takes RGB as input and produces DHM estimate.
4. `rgb_to_cls`: Takes RGB as input and produces Segmentation estimate.
5. `rgb_msi_to_agl`: Takes RGB and MSI as input and produces DHM estimate.
6. `rgb_msi_to_cls`: Takes RGB and MSI as input and produces Segmentation estimate.
7. `rgb_msi_agl_to_cls`: Takes RGB, MSI and DHM as input and produces Segmentation estimate.
8. `rgb_msi_to_agl_cls`: Takes RGB and MSI as input and jointly estimates DHM and Segmentation labels.

This is all achieved using an end-to-end DCNN based on [RBDN](https://github.com/venkai/RBDN), that is trained on the [GRSS dataset](https://github.com/pubgeo/dfc2019/tree/master/data).

# Results

### Example 1
![r1](https://obj.umiacs.umd.edu/deploy-core3d/software/joint_seg_dhm/placeholders/r1.png)

### Example 2
![r2](https://obj.umiacs.umd.edu/deploy-core3d/software/joint_seg_dhm/placeholders/r2.png)

### Example 3
![r3](https://obj.umiacs.umd.edu/deploy-core3d/software/joint_seg_dhm/placeholders/r3.png)


# Usage
Modify `params.py` and do `python run_model.py`.

Modify `TEST_DIR` in `params.py` to point to a folder containing the desired inputs.
Modify `OUTPUT_DIR` to point to where you want to save the relevant outputs.
The default module is the joint-estimation model `rgb_msi_to_agl_cls`. Look at `run_model.py`
for examples on how to change this to any other module.

Run `python run_model.py` without any arguments to process the example images in the `inputs` folder,
using each of the **8** modules provided. Check the `results` folder to see if the outputs match the
ones in the `results/expected` folder.

9 changes: 9 additions & 0 deletions software/joint_seg_dhm/download_model.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#!/bin/bash

ROOT="https://obj.umiacs.umd.edu/deploy-core3d/software/joint_seg_dhm"
for DEST in models/*/ ; do
SOURCE="${ROOT}/${DEST}trn_iter_50000.caffemodel"
echo -e "Downloading pre-trained model from \n${SOURCE} \nto ${DEST}\n"
wget -N ${SOURCE} -P ${DEST}
done

210 changes: 210 additions & 0 deletions software/joint_seg_dhm/inference.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,210 @@
import caffe

import numpy as np
import cv2
from glob import glob
import os
import tifffile
import glog as log
import time

def get_image_paths(params):
return dict(
rgbPaths = sorted(glob(os.path.join(params.TEST_DIR, 'RGB/*%s*.%s' % (params.IMG_FILE_STR,params.IMG_FILE_EXT)))),
msiPaths = sorted(glob(os.path.join(params.TEST_DIR, 'MSI/*%s*.%s' % (params.MSI_FILE_STR,params.IMG_FILE_EXT)))),
dhmPaths = sorted(glob(os.path.join(params.TEST_DIR, 'AGL/*%s*.%s' % (params.DEPTH_FILE_STR,params.IMG_FILE_EXT)))),
)


def load_img(imgPath):
"""
Load image
:param imgPath: path of the image to load
:return: numpy array of the image
"""
log.info('Reading %s' % imgPath)
img = tifffile.imread(imgPath).astype(np.float32)
if img.ndim == 2:
img = img[:,:,np.newaxis]
if img.shape[2] == 3:
img=img[:,:,::-1] # RGB to BGR
return img

def split_img_based_on_exp(img_in_curr, exp_type='rgb_msi'):
img_in = img_in_curr.copy()
if exp_type == 'rgb_msi_agl':
return img_in
elif exp_type == 'rgb_msi':
return img_in[:,:,:-1]
elif exp_type == 'rgb':
return img_in[:,:,0:3]
else:
return img_in[:,:,3:-1]


def convert_labels(Lorig, params, toLasStandard=True):
"""
Convert the labels from the original CLS file to consecutive integer values starting at 0
:param Lorig: numpy array containing original labels
:param params: input parameters from params.py
:param toLasStandard: determines if labels are converted from the las standard labels to training labels
or from training labels to the las standard
:return: Numpy array containing the converted labels
"""
L = Lorig.copy()
if toLasStandard:
labelMapping = params.LABEL_MAPPING_TRAIN2LAS
else:
labelMapping = params.LABEL_MAPPING_LAS2TRAIN

for key,val in labelMapping.items():
L[Lorig==key] = val

return L

def prepare_mult_channel_img(fnames,basename='',axis=2):
res = load_img(basename+fnames[0])
for i in range(1,len(fnames)):
I = load_img(basename+fnames[i])
res = np.concatenate((res,I),axis=axis)
return res

def get_prototxt_caffemodel(params,mode):
if mode == params.CLS_PRED_MODE:
prototxt = params.CLS_PROTOTXT
caffemodel = params.CLS_CAFFEMODEL
elif mode == params.DEPTH_PRED_MODE:
prototxt = params.DEPTH_PROTOTXT
caffemodel = params.DEPTH_CAFFEMODEL
else:
prototxt = params.PROTOTXT
caffemodel = params.CAFFEMODEL
return [prototxt, caffemodel]

def deploy_model(img_in, params, mode=2):
st = time.time()
H, W, _ = img_in.shape
[prototxt, caffemodel] = get_prototxt_caffemodel(params,mode)
net = caffe.Net(prototxt, caffemodel, caffe.TEST)
log.info('DCNN Weights: %s' % caffemodel)
log.info('DCNN Model Definition: %s' % prototxt)
t=time.time()
log.info('Loading the DCNN network took %0.4f secs\n' % (t - st))
# H*W*C to C*H*W
img_in_curr = img_in.copy()
img_in_curr=np.transpose(img_in_curr,[2,0,1])
# Add Batch Dimension (C*H*W to 1*C*H*W)
img_in_curr = img_in_curr[np.newaxis,:,:,:]
log.info('Input Shape: {}'.format(img_in_curr.shape))
if not H==320 or not W==320:
net.blobs['data'].reshape(*img_in.shape)
net.reshape()
net.blobs['data'].data[...] = img_in_curr
net.forward()
img_out = net.blobs['pred'].data[0,...].transpose([1,2,0]).copy()
log.info('Output Shape: {}'.format(img_out.shape))
log.info('DCNN forward pass took %0.4f secs' % (time.time() - t))
del net
return img_out


def test_joint_model(params):
"""
Joint model for simultaneously predicting DHM and CLS
:return: None
"""
caffe.set_mode_gpu()
caffe.set_device(params.GPU_NUM)
OUTPUT_DIR = os.path.join(params.OUTPUT_DIR,'joint')
if not os.path.isdir(OUTPUT_DIR):
os.makedirs(OUTPUT_DIR)
allPaths = get_image_paths(params)
rgbPaths = allPaths['rgbPaths']
msiPaths = allPaths['msiPaths']

Nimg = len(rgbPaths)
log.info('Number of files = %d' % Nimg)
st=time.time()
for ind in range(Nimg):
imageName = os.path.split(rgbPaths[ind])[-1]
dhmOutName = imageName.replace(params.IMG_FILE_STR, params.DEPTH_FILE_STR)
clsOutName = imageName.replace(params.IMG_FILE_STR, params.CLASS_FILE_STR)
out_cls_file = os.path.join(OUTPUT_DIR, clsOutName)
out_dhm_file = os.path.join(OUTPUT_DIR, dhmOutName)
if os.path.isfile(out_dhm_file) and os.path.isfile(out_cls_file):
log.info('Files Already Exist: %s\n Files already exist: %s', out_cls_file, out_dhm_file)
continue
img_in = prepare_mult_channel_img([rgbPaths[ind],msiPaths[ind]])
img_out = deploy_model(img_in, params)
pred_dhm = img_out[:,:,0]
pred_cls = img_out[:,:,1:]
if params.NUM_CATEGORIES > 1:
pred_cls = np.argmax(pred_cls, axis=2).astype('uint8')
else:
pred_cls = (pred_cls > params.BINARY_CONF_TH).astype('uint8')
if params.CONVERT_LABELS:
pred_cls = convert_labels(pred_cls, params, toLasStandard=True)

log.info('[Image %d/%d] Saving predicted DHM to %s' % (ind + 1, Nimg, out_dhm_file))
tifffile.imsave(out_dhm_file, pred_dhm, compress=6)
log.info('[Image %d/%d] Saving predicted CLS to %s\n' % (ind + 1, Nimg, out_cls_file))
tifffile.imsave(out_cls_file, pred_cls, compress=6)
log.info('[Image %d/%d] Total Time Elapsed = %0.4f secs\n\n' % (ind + 1, Nimg, time.time() - st))
time.sleep(0.2)

def test_model(params):
"""
Launches separate models for DHM prediction and CLS prediction
:return: None
"""
if params.CURR_MODE == params.JOINT_PRED_MODE:
test_joint_model(params)
return
caffe.set_mode_gpu()
caffe.set_device(params.GPU_NUM)
exp_cls_type = params.CLS_CAFFEMODEL.split('/')[-2].split('_to')[0]
OUTPUT_CLS_DIR = os.path.join(params.OUTPUT_DIR,exp_cls_type)
if not os.path.isdir(OUTPUT_CLS_DIR):
os.makedirs(OUTPUT_CLS_DIR)
exp_dhm_type = params.DEPTH_CAFFEMODEL.split('/')[-2].split('_to')[0]
OUTPUT_DHM_DIR = os.path.join(params.OUTPUT_DIR,exp_dhm_type)
if not os.path.isdir(OUTPUT_DHM_DIR):
os.makedirs(OUTPUT_DHM_DIR)
allPaths = get_image_paths(params)
rgbPaths = allPaths['rgbPaths']
msiPaths = allPaths['msiPaths']
dhmPaths = allPaths['dhmPaths']
Nimg = len(rgbPaths)
log.info('Number of files = %d' % Nimg)
st=time.time()
for ind in range(Nimg):
imageName = os.path.split(rgbPaths[ind])[-1]
dhmOutName = imageName.replace(params.IMG_FILE_STR, params.DEPTH_FILE_STR)
clsOutName = imageName.replace(params.IMG_FILE_STR, params.CLASS_FILE_STR)
out_cls_file=os.path.join(OUTPUT_CLS_DIR, clsOutName)
out_dhm_file=os.path.join(OUTPUT_DHM_DIR, dhmOutName)
if os.path.isfile(out_cls_file) and os.path.isfile(out_dhm_file):
log.info('File Already Exists: %s', out_cls_file)
log.info('File Already Exists: %s', out_dhm_file)
continue
img_in = prepare_mult_channel_img([rgbPaths[ind],msiPaths[ind],dhmPaths[ind]])
if params.CURR_MODE == params.CLS_PRED_MODE or params.CURR_MODE == params.SEP_PRED_MODE and not os.path.isfile(out_cls_file):
img_in_curr = split_img_based_on_exp(img_in, exp_type=exp_cls_type)
pred_cls = deploy_model(img_in_curr, params, mode=params.CLS_PRED_MODE)
if params.NUM_CATEGORIES > 1:
pred_cls = np.argmax(pred_cls, axis=2).astype('uint8')
else:
pred_cls = (pred_cls > params.BINARY_CONF_TH).astype('uint8')
if params.CONVERT_LABELS:
pred_cls = convert_labels(pred_cls, params, toLasStandard=True)
log.info('[Image %d/%d] Saving predicted CLS to %s' % (ind + 1, Nimg, out_cls_file))
tifffile.imsave(out_cls_file, pred_cls, compress=6)
if params.CURR_MODE == params.DEPTH_PRED_MODE or params.CURR_MODE == params.SEP_PRED_MODE and not os.path.isfile(out_dhm_file):
img_in_curr = split_img_based_on_exp(img_in, exp_type=exp_dhm_type)
pred_dhm = deploy_model(img_in_curr, params, mode=params.DEPTH_PRED_MODE)
log.info('[Image %d/%d] Saving predicted DHM to %s' % (ind + 1, Nimg, out_dhm_file))
tifffile.imsave(out_dhm_file, pred_dhm, compress=6)
log.info('[Image %d/%d] Total Time Elapsed = %0.4f secs' % (ind + 1, Nimg, time.time() - st))



Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Loading

0 comments on commit 4b1bb6e

Please sign in to comment.