├── 1-XD_XD ├── .DS_Store ├── 20170613_docs_dockerized_solution.html ├── Dockerfile ├── README.md ├── code │ ├── merge.py │ ├── test.sh │ ├── train.sh │ ├── v12_im.py │ ├── v13.py │ ├── v16.py │ ├── v17.py │ ├── v5_im.py │ ├── v9s.py │ └── visualizer-2.0.zip ├── images │ ├── .DS_Store │ ├── Figure1ModelExample.jpg │ ├── Figure2VegasExample.jpg │ ├── Figure2p1VegasExample.jpg │ ├── Figure3ModelPerformance.jpg │ ├── Figure3p1_OSM.jpg │ ├── Figure3p2_OSM.jpg │ ├── Figure3p3_OSM.jpg │ ├── Figure3p4_OSM.jpg │ ├── Figure4p1Khartoum.jpg │ ├── Figure4p2.jpg │ ├── Figure4p3.jpg │ ├── Figure4p4.jpg │ └── Figure4p5.jpg ├── keras.json ├── osmdata │ └── readme.txt ├── py35.yml └── working │ └── readme.txt ├── 2-wleite ├── Dockerfile ├── README.md ├── bin │ ├── Building.class │ ├── BuildingDetectorTester$1.class │ ├── BuildingDetectorTester$2.class │ ├── BuildingDetectorTester.class │ ├── BuildingDetectorTrainer$1.class │ ├── BuildingDetectorTrainer.class │ ├── BuildingFeatureExtractor.class │ ├── ClassificationNode.class │ ├── ClassificationTree.class │ ├── ImageViewPanel$1.class │ ├── ImageViewPanel$2.class │ ├── ImageViewPanel.class │ ├── ImgViewer$1.class │ ├── ImgViewer.class │ ├── MatchPolygon.class │ ├── MultiChannelImage.class │ ├── PolygonFeatureExtractor.class │ ├── PolygonMatcherTrainer$1.class │ ├── PolygonMatcherTrainer.class │ ├── RandomForestBuilder$1.class │ ├── RandomForestBuilder.class │ ├── RandomForestPredictor.class │ ├── SpacenetMain.class │ ├── Util$1.class │ └── Util.class ├── lib │ ├── imageio-ext-geocore-1.1.16.jar │ ├── imageio-ext-streams-1.1.16.jar │ ├── imageio-ext-tiff-1.1.16.jar │ ├── imageio-ext-utilities-1.1.16.jar │ ├── jai_codec-1.1.3.jar │ ├── jai_core-1.1.3.jar │ ├── jai_imageio-1.1.jar │ ├── slf4j-api-1.7.25.jar │ └── slf4j-nop-1.7.25.jar ├── model │ └── readme.txt ├── src │ ├── Building.java │ ├── BuildingDetectorTester.java │ ├── BuildingDetectorTrainer.java │ ├── BuildingFeatureExtractor.java │ ├── ClassificationNode.java │ ├── ClassificationTree.java │ ├── ImgViewer.java │ ├── MatchPolygon.java │ ├── MultiChannelImage.java │ ├── PolygonFeatureExtractor.java │ ├── PolygonMatcherTrainer.java │ ├── RandomForestBuilder.java │ ├── RandomForestPredictor.java │ ├── SpacenetMain.java │ ├── Util.java │ └── visualizer │ │ ├── BuildingVisualizer.java │ │ └── Utils.java ├── test.sh └── train.sh ├── 3-nofto ├── Dockerfile ├── README.md ├── lib │ ├── imageio-ext-geocore-1.1.16.jar │ ├── imageio-ext-streams-1.1.16.jar │ ├── imageio-ext-tiff-1.1.16.jar │ ├── imageio-ext-utilities-1.1.16.jar │ ├── jai_codec-1.1.3.jar │ ├── jai_core-1.1.3.jar │ └── jai_imageio-1.1.jar ├── models │ └── readme.txt ├── src │ ├── Beam.java │ ├── Building.java │ ├── BuildingDetectorTester.java │ ├── BuildingDetectorTrainer.java │ ├── BuildingFeatureExtractor.java │ ├── ClassificationNode.java │ ├── ClassificationTree.java │ ├── ImgViewer.java │ ├── MatchPolygon.java │ ├── MultiChannelImage.java │ ├── PolygonFeatureExtractor.java │ ├── PolygonMatcherTrainer.java │ ├── RandomForestBuilder.java │ ├── RandomForestPredictor.java │ └── Util.java ├── test.sh ├── test_one_city.sh ├── train.sh ├── train_one_city_a.sh └── train_one_city_b.sh ├── LICENSE ├── README.md └── solution_descriptions ├── .DS_Store ├── 1-XD_XD.docx ├── 2-wleite.docx └── 3-nofto.docx /1-XD_XD/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SpaceNetChallenge/BuildingDetectors_Round2/04778fdcdd95cc395ebc7e73942eda58143afb42/1-XD_XD/.DS_Store -------------------------------------------------------------------------------- /1-XD_XD/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM nvidia/cuda:8.0-cudnn5-devel-ubuntu16.04 2 | MAINTAINER kohei 3 | 4 | # Install dependent packages via apt-get 5 | RUN apt-get -y update &&\ 6 | echo ">>>>> packages for building python" &&\ 7 | apt-get --no-install-recommends -y install \ 8 | g++ \ 9 | libsqlite3-dev \ 10 | libssl-dev \ 11 | libreadline-dev \ 12 | libncurses5-dev \ 13 | lzma-dev \ 14 | liblzma-dev \ 15 | libbz2-dev \ 16 | libz-dev \ 17 | libgdbm-dev \ 18 | build-essential \ 19 | cmake \ 20 | make \ 21 | wget \ 22 | unzip \ 23 | &&\ 24 | echo ">>>>> packages for building python packages" &&\ 25 | apt-get --no-install-recommends -y install \ 26 | libblas-dev \ 27 | liblapack-dev \ 28 | libpng-dev \ 29 | libfreetype6-dev \ 30 | pkg-config \ 31 | ca-certificates \ 32 | libhdf5-serial-dev \ 33 | postgresql \ 34 | libpq-dev \ 35 | curl \ 36 | &&\ 37 | apt-get clean 38 | 39 | # -=-=-=- Java -=-=-=- 40 | RUN apt-get --no-install-recommends -y install software-properties-common &&\ 41 | add-apt-repository ppa:webupd8team/java -y &&\ 42 | apt-get update &&\ 43 | echo oracle-java8-installer shared/accepted-oracle-license-v1-1 select true | /usr/bin/debconf-set-selections &&\ 44 | apt-get install -y oracle-java8-installer &&\ 45 | apt-get clean 46 | 47 | # -=-=-=- Anaconda -=-=-=- 48 | RUN ANACONDA_URL="https://repo.continuum.io/archive/Anaconda3-4.3.1-Linux-x86_64.sh" &&\ 49 | ANACONDA_FILE="anaconda.sh" &&\ 50 | mkdir -p /opt &&\ 51 | cd /opt &&\ 52 | wget -q --no-check-certificate $ANACONDA_URL -O $ANACONDA_FILE &&\ 53 | echo "4447b93d2c779201e5fb50cfc45de0ec96c3804e7ad0fe201ab6b99f73e90302 ${ANACONDA_FILE}" | sha256sum -c - &&\ 54 | bash $ANACONDA_FILE -b -p /opt/conda &&\ 55 | rm $ANACONDA_FILE 56 | ENV PATH "$PATH:/opt/conda/bin" 57 | 58 | # -=-=-=- Python packages (py35 env) -=-=-=- 59 | COPY py35.yml /opt/ 60 | RUN cd /opt &&\ 61 | conda env create -f py35.yml 62 | 63 | # Keras 64 | RUN mkdir /root/.keras 65 | COPY keras.json /root/.keras/ 66 | 67 | # Deploy OSMdata 68 | RUN mkdir /root/osmdata 69 | COPY osmdata /root/osmdata/ 70 | RUN unzip /root/osmdata/las-vegas_nevada.imposm-shapefiles.zip \ 71 | -d /root/osmdata/las-vegas_nevada_osm/ &&\ 72 | unzip /root/osmdata/shanghai_china.imposm-shapefiles.zip \ 73 | -d /root/osmdata/shanghai_china_osm/ &&\ 74 | unzip /root/osmdata/paris_france.imposm-shapefiles.zip \ 75 | -d /root/osmdata/paris_france_osm/ &&\ 76 | unzip /root/osmdata/ex_s2cCo6gpCXAvihWVygCAfSjNVksnQ.imposm-shapefiles.zip \ 77 | -d /root/osmdata/ex_s2cCo6gpCXAvihWVygCAfSjNVksnQ_osm/ 78 | 79 | # Copy and unzip visualizer 80 | COPY code/visualizer-2.0.zip /root/ 81 | RUN unzip -d /root/visualizer-2.0 /root/visualizer-2.0.zip 82 | 83 | # Deploy codes 84 | COPY code /root/ 85 | RUN chmod a+x /root/train.sh &&\ 86 | chmod a+x /root/test.sh 87 | 88 | ENV PATH $PATH:/root/ 89 | 90 | # Env 91 | ENV LC_ALL C.UTF-8 92 | ENV LANG C.UTF-8 93 | WORKDIR /root/ 94 | -------------------------------------------------------------------------------- /1-XD_XD/README.md: -------------------------------------------------------------------------------- 1 | **Marathon Match - Solution Description** 2 | 3 | **Overview** 4 | 5 | ![Figure1](images/Figure1ModelExample.jpg) 6 | **Figure1** : Best individual model with using OpenStreetMap and Pan-sharpened Multispectral data. 7 | 8 | I applied a modified U-Net model, one of deep neural network model for image segmentation. My final submission is the averaging ensemble from individually trained three U-Net models. In addition, I found the use of OpenStreetMap data is effective for predicting the building footprint. My best individual model simply uses OpenStreetMap layers and multispectral layers as the input of the deep neural network simultaneously (as described in Figure1). 9 | 10 | [Nicolas et al 2017] published on May 17, also investigates the use of OpenStreetMap for semantic labeling of satellite image. I individually investigated and evaluated the same approach on Spacenet Challenge dataset (my first submission with the approach is on May 13, a little earlier than their publication). 11 | 12 | 1. **1.**** Introduction** 13 | 14 | - **●●**** Handle**: XD\_XD 15 | 16 | 1. **2.**** Solution Development** 17 | 18 | How did you solve the problem? What approaches did you try and what choices did you make, and why? Also, what alternative approaches did you consider? 19 | 20 | - **●●**** (Modeling)**. I solved the problem as a semantic segmentation task in computer vision. My model is based on a variant of fully convolutional neural network, U-Net [[Olaf et al, 2015]](https://arxiv.org/abs/1505.04597). U-Net is one of the most successful and popular convolutional neural network architecture for medical image segmentation. It can be trained end-to-end from few images and outperform the prior best method on the ISBI cell tracking challenge 2015. 21 | Figure 2 shows an example output by my solution with U-Net models. Most building footprints are successfully detected with high intersection area over union (> 0.8). Another example output of L-shaped and concave buildings in Vegas is shown in Figure 2.1. Lee Cohn, a data scientist at CosmiQ Works, [described his result of applying the Multi-task Network Cascades (MNC)](https://medium.com/the-downlinq/object-segmentation-on-spacenet-via-multi-task-network-cascades-mnc-f1c89d790b42) and MNC struggles with L-shaped and concave buildings. By contrast, my U-Net based model can detect L-shaped and concave buildings successfully. 22 | - **●●**** (RGB or MUL)**. In early stage of the contest, I only used RGB 3 channels. Later I found that using 8-bands multispectral data improves the performance. 23 | 24 | 25 | ![Figure2](images/Figure2VegasExample.jpg) 26 | **Figure 2** : An example output in Vegas from validation set. Most building footprints are precisely detected. The number displayed on the building footprint represents the intersection area over union (IoU) between the prediction and the ground truth. 27 | 28 | ![Figure2.1](images/Figure2p1VegasExample.jpg) 29 | **Figure 2.1** : Another example output in Vegas. L-shaped and concave buildings are detected successfully. 30 | 31 | 1. **3.**** Final Approach** 32 | 33 | Please provide a bulleted description of your final approach. What ideas/decisions/features have been found to be the most important for your solution performance: 34 | 35 | - **●●** ( **Parameter optimization** ). To develop the individual U-net model, I split the training data into two parts: 70 percent for training and the remaining 30 percent for validation. To avoid overfitting, early stopping with Jaccard coefficient is applied for training. After training the model with 70 percent of the data, the trained model is evaluated on the remaining 30 percent and chose the best one. 36 | - **●●** ( **Trade-off between precision and recall** ). I noticed precision on small objects is not good compared with other objects. Consequently, it is possible to improve F-score by eliminating small objects. I searched the threshold size of minimum polygon area by using validation set and eliminated small objects under the area size. 37 | - **●●** ( **Averaging Ensemble** ). I created various U-Net models and built an averaging ensemble of three U-Net models. A diverse set of models was selected for ensemble. First model is learnt with global context by rescaling multispectral images from 650x650 pixsels into 256x256 pixsels. Second model uses the original scale multispectral data. Third model uses the original scale and the combination of OpenStreetMap and multispectral data. 38 | - **●●** ( **OpenStreetMap** ). Joint learning from OpenStreetMap and multispectral data works. By observation, footprints of residential and industrial buildings has different shape. Obviously, there are no buildings on water area or road in general. I used the layer of residential land use, agricultural land use, industrial land use, water area, buildings and roads on OpenStreetMap. Figure 3.1 shows the example of layers on OpenStreetMap. 39 | 40 | 41 | ![Figure3](images/Figure3ModelPerformance.jpg) 42 | **Figure 3** : Performance comparison on the provisional scores. v9s uses 256/650 scale multispectral images. v13 uses the original scale multispectral images. v16 uses the original scale multispectral images and OpenStreetMap data. v17 is an ensemble model with averaging the prediction probability of v9s, v13 and v16 model. 43 | 44 | 45 | 46 | 47 | ![Figure3.1](images/Figure3p1_OSM.jpg) 48 | ![Figure3.2](images/Figure3p2_OSM.jpg) 49 | ![Figure3.3](images/Figure3p3_OSM.jpg) 50 | ![Figure3.4](images/Figure3p4_OSM.jpg) 51 | **Figure 3.1** : Examples of layers on OpenStreetMap: From left side, ground truth, earth observation, buildings, residential land use, industrial land use and roads. 52 | 53 | 1. **4.**** Open Source Resources, Frameworks and Libraries** 54 | 55 | Please specify the name of the open source resource along with a URL to where it's housed and it's license type: 56 | 57 | - **●●** Docker, [https://www.docker.com](https://www.docker.com), (Apache License 2.0) 58 | - **●●** Nvidia-docker, [https://github.com/NVIDIA/nvidia-docker](https://github.com/NVIDIA/nvidia-docker), ( [BSD 3-clause](https://github.com/NVIDIA/nvidia-docker/blob/master/LICENSE)) 59 | - **●●** OpenStreetMap, [http://www.openstreetmap.org/export](http://www.openstreetmap.org/export), ( [ODbL](http://www.openstreetmap.org/copyright)) 60 | - **○○** Shapefiles are dumped via Metro-extracts [https://mapzen.com/data/metro-extracts/](https://mapzen.com/data/metro-extracts/). This tool is also open-sourced: 61 | - **○○** Metro-extracts, [https://github.com/mapzen/metro-extracts](https://github.com/mapzen/metro-extracts), ISC 62 | - **●●** Python 3, https://www.python.org/, ( [PSFL (Python Software Foundation License)](https://docs.python.org/3/license.html)) 63 | - **●●** Shapely, [https://github.com/Toblerity/Shapely](https://github.com/Toblerity/Shapely), ( [BSD 3-clause](https://github.com/Toblerity/Shapely/blob/master/LICENSE.txt)) 64 | - **●●** Fiona, [https://github.com/Toblerity/Fiona](https://github.com/Toblerity/Fiona), ( [BSD 3-clause](https://github.com/Toblerity/Fiona/blob/master/LICENSE.txt)) 65 | - **●●** Scikti-image, [http://scikit-image.org/](http://scikit-image.org/), ( [BSD 3-clause](https://github.com/scikit-image/scikit-image/blob/master/LICENSE.txt)) 66 | - **●●** Scikit-learn, [http://scikit-learn.org/stable/](http://scikit-learn.org/stable/), (BSD 3-clause) 67 | - **●●** Numpy, [http://www.numpy.org/](http://www.numpy.org/), (BSD) 68 | - **●●** Scipy, [https://www.scipy.org/](https://www.scipy.org/), (BSD) 69 | - **●●** Tqdm, [https://github.com/noamraph/tqdm](https://github.com/noamraph/tqdm), ( [The MIT License](https://github.com/noamraph/tqdm/blob/master/LICENSE)) 70 | - **●●** Gdal, [http://www.gdal.org/](http://www.gdal.org/), (X/MIT) 71 | - **●●** Click, [http://click.pocoo.org/5/](http://click.pocoo.org/5/), ( [BSD 3-clause](http://click.pocoo.org/5/license/)) 72 | - **●●** Pandas, [http://pandas.pydata.org/](http://pandas.pydata.org/), ( [BSD 3-clause](https://github.com/pandas-dev/pandas/blob/master/LICENSE)) 73 | - **●●** Pytables, [http://www.pytables.org/](http://www.pytables.org/), ( [BSD 3-clause](https://github.com/PyTables/PyTables/blob/develop/LICENSE.txt)) 74 | - **●●** Rasterio, https://github.com/mapbox/rasterio,( [BSD 3-clause](https://github.com/mapbox/rasterio/blob/master/LICENSE.txt)) 75 | - **●●** Keras, [https://keras.io/](https://keras.io/), ( [The MIT License](https://github.com/fchollet/keras/blob/master/LICENSE)) 76 | - **●●** Anaconda, https://www.continuum.io/Anaconda-Overview,( [New BSD License](https://docs.continuum.io/anaconda/eula)) 77 | 78 | 1. **5.**** Potential Algorithm Improvements** 79 | 80 | Please specify any potential improvements that can be made to the algorithm: 81 | 82 | - **●●**** (More models into Ensemble)**. Before the final submission, I verified an ensemble model with 6 models and it outperforms my final submission, scored over 700,000 on the provisional stage. 83 | - **●●**** (More different scales)**. Adding different scales could improve the score. 84 | - **●●**** (More layers of OpenStreetMap)**. Developing and changing a network architecture could reduce the size of the required graphical RAM and computational time. See also: [[Dimitrios et al, 2016]](https://arxiv.org/abs/1612.01337) and [[Nicolas et al, 2017]](https://arxiv.org/abs/1705.06057) 85 | - **●●**** (Modeling)**. Pixel Deconvolutional Networks [[Hongyang et al 2017]](https://arxiv.org/abs/1705.06820) is also interesting to try out for semantic segmentation. 86 | - **●●**** (Signed distance transform)**. Signed distance transformation of roads on OpenStreetMap could be good as preprocessing, though I used binary representation of the OpenStreetMap data for roads. [[Nicolas et al, 2017]](https://arxiv.org/abs/1705.06057) uses "Signed distance transform" for roads. 87 | - **●●**** (Batch normalization)**. Segmentation nets are numerically unstable. Due to the fact, Julian de Wit applied Batch normalization for his U-net model, described on [his blogpost](http://juliandewit.github.io/kaggle-ndsb/). 88 | 89 | 1. **6.**** Algorithm Limitations** 90 | 91 | Please specify any potential limitations with the algorithm: 92 | 93 | - **●●** Low precision for small object, especially in the case of Shanghai and Khartoum. 94 | - **●●** As in the case of Khartoum, my solution does not have good results in scenes where annotation rules are not clear, see Figure 4. 95 | - **●●** My model is unable to recognize multiple buildings that are close in distance as one building footprint. Examples of false negatives are shown in Figure 4.1. 96 | 97 | 98 | ![Figure4](images/Figure4p1Khartoum.jpg) 99 | **Figure 4** : The annotation rules in Khartoum are relatively ambiguous. As the result, the shape of predictions are unclear. On the right image, the ground truth (blue or white part) is grouped and large. On the left, the ground truth is separated and small. 100 | 101 | 102 | ![Figure4.1](images/Figure4p2.jpg) 103 | ![Figure4.1](images/Figure4p3.jpg) 104 | ![Figure4.1](images/Figure4p4.jpg) 105 | ![Figure4.1](images/Figure4p5.jpg) 106 | **Figure 4.1** : Examples of false negatives. My model recognizes multiple buildings that are close in distance as one building footprint. 107 | 108 | 1. **7.**** Deployment Guide** 109 | 110 | Please provide the exact steps required to build and deploy the code: 111 | 112 | [The instruction is described in a separate document](http://htmlpreview.github.io/?https://github.com/SpaceNetChallenge/BuildingDetectors_Round2/blob/master/1-XD_XD/20170613_docs_dockerized_solution.html) 113 | 114 | 115 | 1. **8.**** Final Verification** 116 | 117 | Please provide instructions that explain how to train the algorithm and have it execute against sample data: 118 | 119 | 120 | [The instruction is described in a separate document](http://htmlpreview.github.io/?https://github.com/SpaceNetChallenge/BuildingDetectors_Round2/blob/master/1-XD_XD/20170613_docs_dockerized_solution.html) 121 | -------------------------------------------------------------------------------- /1-XD_XD/code/merge.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from logging import getLogger, Formatter, StreamHandler, INFO 3 | from pathlib import Path 4 | import sys 5 | import warnings 6 | 7 | 8 | MODEL_NAME = 'v17' 9 | MODEL_DIR = "/data/working/models/{}".format(MODEL_NAME) 10 | FMT_TESTPOLY_PATH = MODEL_DIR + "/{}_poly.csv" 11 | 12 | LOGFORMAT = '%(asctime)s %(levelname)s %(message)s' 13 | 14 | 15 | # Logger 16 | warnings.simplefilter("ignore", UserWarning) 17 | warnings.simplefilter("ignore", FutureWarning) 18 | handler = StreamHandler() 19 | handler.setLevel(INFO) 20 | handler.setFormatter(Formatter(LOGFORMAT)) 21 | logger = getLogger('spacenet2') 22 | logger.setLevel(INFO) 23 | 24 | 25 | if __name__ == '__main__': 26 | logger.addHandler(handler) 27 | 28 | 29 | def area_id_to_prefix(area_id): 30 | """ 31 | area_id から prefix を返す 32 | """ 33 | area_dict = { 34 | 2: 'AOI_2_Vegas', 35 | 3: 'AOI_3_Paris', 36 | 4: 'AOI_4_Shanghai', 37 | 5: 'AOI_5_Khartoum', 38 | } 39 | return area_dict[area_id] 40 | 41 | 42 | def directory_name_to_area_id(datapath): 43 | """ 44 | Directory name to AOI number 45 | 46 | Usage: 47 | 48 | >>> directory_name_to_area_id("/data/test/AOI_2_Vegas") 49 | 2 50 | """ 51 | dir_name = Path(datapath).name 52 | if dir_name.startswith('AOI_2_Vegas'): 53 | return 2 54 | elif dir_name.startswith('AOI_3_Paris'): 55 | return 3 56 | elif dir_name.startswith('AOI_4_Shanghai'): 57 | return 4 58 | elif dir_name.startswith('AOI_5_Khartoum'): 59 | return 5 60 | else: 61 | raise RuntimeError("Unsupported city id is given.") 62 | 63 | 64 | def _remove_interiors(line): 65 | if "), (" in line: 66 | line_prefix = line.split('), (')[0] 67 | line_terminate = line.split('))",')[-1] 68 | line = ( 69 | line_prefix + 70 | '))",' + 71 | line_terminate 72 | ) 73 | return line 74 | 75 | 76 | def _merge(area_id_list, output_fn): 77 | # file check 78 | for area_id in area_id_list: 79 | prefix = area_id_to_prefix(area_id) 80 | fn_out = FMT_TESTPOLY_PATH.format(prefix) 81 | if not Path(fn_out).exists(): 82 | logger.info("Required file not found: {}".format(fn_out)) 83 | sys.exit(1) 84 | 85 | # merge files 86 | rows = [] 87 | for area_id in area_id_list: 88 | prefix = area_id_to_prefix(area_id) 89 | fn_out = FMT_TESTPOLY_PATH.format(prefix) 90 | with open(fn_out, 'r') as f: 91 | line = f.readline() 92 | if area_id == area_id_list[0]: 93 | rows.append(line) # header line 94 | for line in f: 95 | line = _remove_interiors(line) 96 | rows.append(line) 97 | with open(output_fn, 'w') as f: 98 | for line in rows: 99 | f.write(line) 100 | 101 | 102 | def merge(): 103 | if len(sys.argv) < 3: 104 | print("Usage: merge.py [/data/test/AOI_2_Vegas_Test ...] out.csv") 105 | 106 | test_path_list = sys.argv[1:-1] 107 | output_fn = sys.argv[-1] 108 | 109 | if not output_fn.endswith('.csv'): 110 | print("Error: output must be end with '.csv'.") 111 | sys.exit(1) 112 | 113 | area_id_list = [ 114 | directory_name_to_area_id(datapath) 115 | for datapath in test_path_list] 116 | _merge(area_id_list, output_fn) 117 | 118 | 119 | if __name__ == '__main__': 120 | merge() 121 | -------------------------------------------------------------------------------- /1-XD_XD/code/test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | export THEANO_FLAGS=mode=FAST_RUN,device=gpu,floatX=float32 3 | 4 | ARGS=$@ 5 | shift $(($# - 1)) 6 | OUT=$1 7 | 8 | # clean up 9 | mkdir -p /data/output /data/working 10 | rm -f /data/working/images/v5/test_AOI_*_im.h5 11 | rm -f /data/working/images/v5/test_AOI_*_mul.h5 12 | rm -f /data/working/images/v12/test_AOI_*_mul.h5 13 | rm -f /data/working/images/v16/test_AOI_*_osm.h5 14 | 15 | source activate py35 && for test_path in $ARGS; do 16 | # Skip last arg 17 | [[ $test_path = $OUT ]] && break 18 | 19 | echo ">>> PREPROCESSING STEP" 20 | echo ">>>" python v5_im.py preproc_test $test_path 21 | python v5_im.py preproc_test $test_path 22 | echo ">>>" python v12_im.py preproc_test $test_path 23 | python v12_im.py preproc_test $test_path 24 | echo ">>>" python v16.py preproc_test $test_path 25 | python v16.py preproc_test $test_path 26 | 27 | echo ">>> INFERENCE STEP" 28 | echo ">>>" python v17.py testproc $test_path 29 | python v17.py testproc $test_path 30 | done 31 | 32 | # Merge infenrece results 33 | echo ">>> MERGE INFERENCE RESULTS" 34 | echo ">>>" python merge.py $ARGS 35 | python merge.py $ARGS 36 | -------------------------------------------------------------------------------- /1-XD_XD/code/train.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | export THEANO_FLAGS=mode=FAST_RUN,device=gpu,floatX=float32 3 | 4 | echo ">>> CLEAN UP" 5 | echo rm -rf /data/working 6 | rm -rf /data/working && mkdir -p /data/working 7 | 8 | source activate py35 && for train_path in $@; do 9 | echo ">>> PREPROCESSING STEP" 10 | echo python v5_im.py preproc_train $train_path 11 | python v5_im.py preproc_train $train_path 12 | echo python v12_im.py preproc_train $train_path 13 | python v12_im.py preproc_train $train_path 14 | echo python v16.py preproc_train $train_path 15 | python v16.py preproc_train $train_path 16 | 17 | echo ">>> TRAINING v9s model" 18 | echo python v9s.py validate $train_path 19 | python v9s.py validate $train_path 20 | echo python v9s.py evalfscore $train_path 21 | python v9s.py evalfscore $train_path 22 | 23 | ### v13 -------------- 24 | # Training for v13 model 25 | echo ">>>>>>>>>> v13.py" 26 | python v13.py validate $train_path 27 | # Parametr optimization for v13 model 28 | echo ">>>>>>>>>> v13.py" 29 | python v13.py evalfscore $train_path 30 | 31 | ### v16 -------------- 32 | # Training for v16 model 33 | echo ">>>>>>>>>> v16.py" 34 | python v16.py validate $train_path 35 | # Parametr optimization for v16 model 36 | echo ">>>>>>>>>> v16.py" 37 | python v16.py evalfscore $train_path 38 | 39 | ### v17 -------------- 40 | echo ">>>>>>>>>> v17.py" 41 | python v17.py evalfscore $train_path 42 | done 43 | -------------------------------------------------------------------------------- /1-XD_XD/code/visualizer-2.0.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SpaceNetChallenge/BuildingDetectors_Round2/04778fdcdd95cc395ebc7e73942eda58143afb42/1-XD_XD/code/visualizer-2.0.zip -------------------------------------------------------------------------------- /1-XD_XD/images/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SpaceNetChallenge/BuildingDetectors_Round2/04778fdcdd95cc395ebc7e73942eda58143afb42/1-XD_XD/images/.DS_Store -------------------------------------------------------------------------------- /1-XD_XD/images/Figure1ModelExample.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SpaceNetChallenge/BuildingDetectors_Round2/04778fdcdd95cc395ebc7e73942eda58143afb42/1-XD_XD/images/Figure1ModelExample.jpg -------------------------------------------------------------------------------- /1-XD_XD/images/Figure2VegasExample.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SpaceNetChallenge/BuildingDetectors_Round2/04778fdcdd95cc395ebc7e73942eda58143afb42/1-XD_XD/images/Figure2VegasExample.jpg -------------------------------------------------------------------------------- /1-XD_XD/images/Figure2p1VegasExample.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SpaceNetChallenge/BuildingDetectors_Round2/04778fdcdd95cc395ebc7e73942eda58143afb42/1-XD_XD/images/Figure2p1VegasExample.jpg -------------------------------------------------------------------------------- /1-XD_XD/images/Figure3ModelPerformance.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SpaceNetChallenge/BuildingDetectors_Round2/04778fdcdd95cc395ebc7e73942eda58143afb42/1-XD_XD/images/Figure3ModelPerformance.jpg -------------------------------------------------------------------------------- /1-XD_XD/images/Figure3p1_OSM.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SpaceNetChallenge/BuildingDetectors_Round2/04778fdcdd95cc395ebc7e73942eda58143afb42/1-XD_XD/images/Figure3p1_OSM.jpg -------------------------------------------------------------------------------- /1-XD_XD/images/Figure3p2_OSM.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SpaceNetChallenge/BuildingDetectors_Round2/04778fdcdd95cc395ebc7e73942eda58143afb42/1-XD_XD/images/Figure3p2_OSM.jpg -------------------------------------------------------------------------------- /1-XD_XD/images/Figure3p3_OSM.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SpaceNetChallenge/BuildingDetectors_Round2/04778fdcdd95cc395ebc7e73942eda58143afb42/1-XD_XD/images/Figure3p3_OSM.jpg -------------------------------------------------------------------------------- /1-XD_XD/images/Figure3p4_OSM.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SpaceNetChallenge/BuildingDetectors_Round2/04778fdcdd95cc395ebc7e73942eda58143afb42/1-XD_XD/images/Figure3p4_OSM.jpg -------------------------------------------------------------------------------- /1-XD_XD/images/Figure4p1Khartoum.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SpaceNetChallenge/BuildingDetectors_Round2/04778fdcdd95cc395ebc7e73942eda58143afb42/1-XD_XD/images/Figure4p1Khartoum.jpg -------------------------------------------------------------------------------- /1-XD_XD/images/Figure4p2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SpaceNetChallenge/BuildingDetectors_Round2/04778fdcdd95cc395ebc7e73942eda58143afb42/1-XD_XD/images/Figure4p2.jpg -------------------------------------------------------------------------------- /1-XD_XD/images/Figure4p3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SpaceNetChallenge/BuildingDetectors_Round2/04778fdcdd95cc395ebc7e73942eda58143afb42/1-XD_XD/images/Figure4p3.jpg -------------------------------------------------------------------------------- /1-XD_XD/images/Figure4p4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SpaceNetChallenge/BuildingDetectors_Round2/04778fdcdd95cc395ebc7e73942eda58143afb42/1-XD_XD/images/Figure4p4.jpg -------------------------------------------------------------------------------- /1-XD_XD/images/Figure4p5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SpaceNetChallenge/BuildingDetectors_Round2/04778fdcdd95cc395ebc7e73942eda58143afb42/1-XD_XD/images/Figure4p5.jpg -------------------------------------------------------------------------------- /1-XD_XD/keras.json: -------------------------------------------------------------------------------- 1 | { 2 | "image_dim_ordering": "th", 3 | "epsilon": 1e-07, 4 | "floatx": "float32", 5 | "backend": "theano" 6 | } 7 | -------------------------------------------------------------------------------- /1-XD_XD/osmdata/readme.txt: -------------------------------------------------------------------------------- 1 | .imposm-shapefiles.zip files go here. They can be downloaded from "Metro Extracts", the exact link for downloading them are following: 2 | 3 | https://mapzen.com/data/metro-extracts/metro/las-vegas_nevada/ 4 | https://mapzen.com/data/metro-extracts/metro/paris_france/ 5 | https://mapzen.com/data/metro-extracts/metro/shanghai_china/ 6 | 7 | There are two types of shapefile. "IMPOSM file format" is used in the solution. 8 | 9 | As for the shapefiles of Khartoum, we need to search the name of city on "Metro Extracts" and issue a request for generating the shapefiles. Then the shapefiles for Khartoum will be generated. -------------------------------------------------------------------------------- /1-XD_XD/py35.yml: -------------------------------------------------------------------------------- 1 | name: py35 2 | channels: 3 | - menpo 4 | - defaults 5 | dependencies: 6 | - affine=2.0.0=py35_0 7 | - boost=1.61.0=py35_0 8 | - click=6.7=py35_0 9 | - click-plugins=1.0.3=py35_0 10 | - cligj=0.4.0=py35_0 11 | - curl=7.49.0=1 12 | - cycler=0.10.0=py35_0 13 | - dbus=1.10.10=0 14 | - decorator=4.0.11=py35_0 15 | - expat=2.1.0=0 16 | - fiona=1.7.0=py35_0 17 | - fontconfig=2.12.1=3 18 | - freetype=2.5.5=2 19 | - gdal=2.1.0=py35_0 20 | - geos=3.5.0=0 21 | - geotiff=1.4.1=0 22 | - gflags=2.1.2=0 23 | - glib=2.50.2=1 24 | - glog=0.3.4=0 25 | - gst-plugins-base=1.8.0=0 26 | - gstreamer=1.8.0=0 27 | - h5py=2.7.0=np111py35_0 28 | - hdf4=4.2.12=0 29 | - hdf5=1.8.17=1 30 | - icu=54.1=0 31 | - ipython=5.3.0=py35_0 32 | - ipython_genutils=0.2.0=py35_0 33 | - jbig=2.1=0 34 | - jpeg=8d=2 35 | - kealib=1.4.6=0 36 | - leveldb=1.19=0 37 | - libffi=3.2.1=1 38 | - libgcc=5.2.0=0 39 | - libgdal=2.1.0=0 40 | - libgfortran=3.0.0=1 41 | - libiconv=1.14=0 42 | - libnetcdf=4.4.1=0 43 | - libpng=1.6.27=0 44 | - libprotobuf=3.2.0=0 45 | - libtiff=4.0.6=2 46 | - libxcb=1.12=1 47 | - libxml2=2.9.4=0 48 | - lmdb=0.9.18=0 49 | - matplotlib=2.0.0=np111py35_0 50 | - mkl=11.3.3=0 51 | - mkl-service=1.1.2=py35_3 52 | - munch=2.1.0=py35_0 53 | - networkx=1.11=py35_0 54 | - numexpr=2.6.1=np111py35_1 55 | - numpy=1.11.2=py35_0 56 | - opencv=3.1.0=np111py35_1 57 | - openssl=1.0.2k=1 58 | - pandas=0.19.2=np111py35_1 59 | - path.py=10.1=py35_0 60 | - pcre=8.39=1 61 | - pexpect=4.2.1=py35_0 62 | - pickleshare=0.7.4=py35_0 63 | - pillow=3.4.2=py35_0 64 | - pip=9.0.1=py35_1 65 | - proj4=4.9.2=0 66 | - prompt_toolkit=1.0.13=py35_0 67 | - protobuf=3.2.0=py35_0 68 | - ptyprocess=0.5.1=py35_0 69 | - py=1.4.32=py35_0 70 | - pygments=2.2.0=py35_0 71 | - pyparsing=2.1.4=py35_0 72 | - pyqt=5.6.0=py35_2 73 | - pytables=3.3.0=np111py35_0 74 | - pytest=3.0.7=py35_0 75 | - python=3.5.3=1 76 | - python-dateutil=2.6.0=py35_0 77 | - python-gflags=2.0=py35_0 78 | - python-leveldb=0.194=py35_0 79 | - pytz=2016.10=py35_0 80 | - pyyaml=3.12=py35_0 81 | - qt=5.6.2=2 82 | - rasterio=0.36.0=np111py35_0 83 | - readline=6.2=2 84 | - scikit-image=0.12.3=np111py35_1 85 | - scikit-learn=0.18.1=np111py35_0 86 | - scipy=0.18.1=np111py35_0 87 | - seaborn=0.7.1=py35_0 88 | - setuptools=27.2.0=py35_0 89 | - shapely=1.5.16=py35_0 90 | - simplegeneric=0.8.1=py35_1 91 | - sip=4.18=py35_0 92 | - six=1.10.0=py35_0 93 | - snappy=1.1.3=0 94 | - snuggs=1.4.0=py35_0 95 | - sqlite=3.13.0=0 96 | - tk=8.5.18=0 97 | - tqdm=4.11.2=py35_0 98 | - traitlets=4.3.2=py35_0 99 | - wcwidth=0.1.7=py35_0 100 | - wheel=0.29.0=py35_0 101 | - xerces-c=3.1.4=0 102 | - xz=5.2.2=1 103 | - yaml=0.1.6=0 104 | - zlib=1.2.8=3 105 | - pip: 106 | - bleach==2.0.0 107 | - entrypoints==0.2.2 108 | - keras==1.1.0 109 | - markupsafe==1.0 110 | - mistune==0.7.4 111 | - tables==3.3.0 112 | - theano==0.9.0 113 | prefix: /home/ubuntu/anaconda3/envs/py35 114 | 115 | -------------------------------------------------------------------------------- /1-XD_XD/working/readme.txt: -------------------------------------------------------------------------------- 1 | Model files are not stored in git, they should be regenerated using train.sh -------------------------------------------------------------------------------- /2-wleite/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:16.04 2 | 3 | # Install General Requirements 4 | RUN apt-get update && \ 5 | apt-get install -y --no-install-recommends \ 6 | apt-utils \ 7 | build-essential \ 8 | software-properties-common 9 | 10 | # Install Java 11 | RUN add-apt-repository ppa:webupd8team/java -y && \ 12 | apt-get update && \ 13 | echo oracle-java8-installer shared/accepted-oracle-license-v1-1 select true | /usr/bin/debconf-set-selections && \ 14 | apt-get install -y oracle-java8-installer && \ 15 | apt-get clean 16 | 17 | RUN mkdir wleite 18 | 19 | COPY . /opt/wleite/ 20 | 21 | WORKDIR /opt/wleite 22 | 23 | 24 | -------------------------------------------------------------------------------- /2-wleite/README.md: -------------------------------------------------------------------------------- 1 | **SpaceNet2 - Marathon Match - Solution Description** 2 | 3 | **Overview** 4 | 5 | Congrats on winning this marathon match. As part of your final submission and in order to receive payment for this marathon match, please complete the following document. 6 | 7 | 1. **1.**** Introduction** 8 | 9 | Tell us a bit about yourself, and why you have decided to participate in the contest. 10 | 11 | - **●●** Handle: wleite 12 | - **●●** Placement you achieved in the MM: 2nd 13 | 14 | 15 | 16 | 1. **2.**** Solution Development** 17 | 18 | How did you solve the problem? What approaches did you try and what choices did you make, and why? Also, what alternative approaches did you consider? 19 | 20 | - **●●** My solution is roughly the same from the first challenge. [First challenge write up](https://github.com/SpaceNetChallenge/BuildingDetectors/tree/master/wleite) 21 | - **●●** What I did in a different way was to test one change at time, as we had much more time, instead of relying on intuition or on a very small testing subset, as I did in the first contest. 22 | 23 | 1. **3.**** Final Approach** 24 | 25 | Please provide a bulleted description of your final approach. What ideas/decisions/features have been found to be the most important for your solution performance: 26 | 27 | - **●●** After adapting the solution from the first round to work with the different organization and formats of input files, I revisited the main points of my solution. Few things were removed, as they only add complexity without any visible gain. Others where adjusted, based on the results of tests with the new data sets. The following paragraphs describe the final solution. 28 | - **●●**** Cities:** Each city is treated in a completely independent way, i.e. training and testing cycles were executed for each city, as buildings characteristics look somewhat different, and there are enough data for each city. 29 | - **●●**** Image Formats:** My final solution uses the high-resolution pan image, the 3-band RGB pan sharpen and the low-resolution multiband. I tried to replace the multiband images by their pan sharpen version, but it produced worst results. Although visually the pan sharpen version looks much better, I am not sure it adds more information, compared to the original low-resolution version, as the high-resolution pan image is already used. 30 | - **●●**** Image Preprocessing:** A simple blur is applied to the gray and each RGB channels, using a standard 3x3 neighborhood of the pixel with the weight. After blurring RGB values, each pixel is converted to HSL channels, which will be used later. Finally, a simple edge detection is executed for each channel of the multiband images and for the gray channel. The edge detection combines horizontal and vertical values of edges computed using a Sobel filter. 31 | - **●●**** Pixel Classification Training:**This is the first step of my solution, implemented by the class BuildingDetectorTrainer. It uses as input 60% of the training images (for a given city). It builds two random forests of binary classification trees: one that classifies each pixel as**belonging or not to the border **, and the other as** inside of a building or not**. Pixels are treated individually, i.e. border and inside classification is based on individual pixels (in the first contest a 2x2 area was used here). 32 | 33 | The resulting random forests were saved into two files: rfBorder.dat and rfBuilding.dat. My final model has 60 trees for each of these forests. 34 | 35 | The features used were the same for the two classification forests. It uses the average, variance and skewness for small neighborhood squares around the evaluated pixel (more focused than used in the first contest). A total of 68 of values were used in the final submission, less than I used in the first contest. H (hue), S (saturation), G (gray) and E (edge) channels were used for the high-resolution versions of the images. For the lower-resolution multiband versions, the raw pixel value and edge value were used, for all 16 channels 36 | 37 | To keep the number of sampled pixels in a reasonable limit, not every pixel is used for training. Pixels inside buildings are subsampled randomly in a rate of 1/8. For pixels outside the buildings, the subsample ratio is 1/16. All border pixels are used (1/1). For images with no buildings at all, the subsample ratio is 1/64. 38 | 39 | - **Polygon Evaluation Training:** This is the second main step of the solution, implemented by the class PolygonMatcherTrainer. It used as input 40% of the training images (a different set of images from the previous step). For local tests it used a little less, reserving about 10% of the images for evaluating the solution. 40 | 41 | Initially, it used the classifiers previously built to assign for each pixel a probability value of being a "border" and of being "inside a building". 42 | 43 | The following step is finding polygon candidates from the pixels, based on their classifications (border / inside). It then combines border and inside information in a single value, subtracting (possibly weighted) "border" values from "inside" values. The goal is to keep each building region separated from its neighbor buildings, so a simple flood fill can detect a single building. Borders act as separators to avoid merging different buildings close to each other. 44 | 45 | A simple flood fill (expanding groups of 4-connected pixels) is executed, using as an input parameter the threshold level to decide where to stop flooding (higher values break the result into many smaller groups, while lower values join pixels into a smaller number of larger groups). Finally, a "border" is added to found groups because the building borders were not filled by the previous step. This process is repeated for many different threshold values. 46 | 47 | A convex hull procedure is used to generate a polygon from a group of pixels. At this point, no verification (intersection, size, shape, position) is made, as these are only "candidates". 48 | 49 | Now the actually training part starts. Each candidate polygon is compared to ground truth buildings, calculating the best IOU value. A random forest of binary classification trees is built to predict, for a given polygon candidate, if its IOU is above 0.5. Final submission used 120 trees. In the first contest I used regression trees that tried to predict the IOU value of a given polygon candidate. This time, tests showed that using a binary classification produced similar final scores, but much more stable in regard of the chosen threshold used later to accept or reject polygons. 50 | 51 | Features used are very similar to what I had in the first match using general polygon properties (including area, lengths of the smallest rectangle that contains the polygon, proportion between sides of this rectangle, proportion between the area of the bounding rectangle and the actual area, predicted "border" and "inside" values). 52 | 53 | The resulting random forest is saved as a file named rfPolyMatch.dat. 54 | 55 | - **Testing (Finding polygons):** For local tests, I used 10% of training data to evaluate my solution. This step is very straightforward, as it reuses methods described before. It is implemented by the BuildingDetectorTester class. 56 | 57 | Using the same process for finding polygon candidates, many polygon candidates are found and then evaluated using the trained random forest. 58 | 59 | At this point, each polygon candidate has a predicted value of having a IOU above 0.5. All candidates are then sorted, from higher to low probability values. Then each polygon is checked to verify if the intersection area with any previously accepted polygon is higher than 10% of either polygon area and, in such case, it is discarded. 60 | 61 | Only polygons with a predicted probability of having a "high IOU" (above 0.5) higher than certain threshold are accepted. This threshold number came from local tests, trying to balance precision and recall to achieve the maximal F-Score. This is the only parameter that was tuned differently for each city, although the effect on final score was minor. 62 | 63 | The actual values used in my final submission were: 64 | 65 | - 66 | - 67 | - 68 | - Vegas: 0.40; 69 | - Paris: 0.38; 70 | - Shanghai: 0.31; 71 | - Khartoum: 0.29. 72 | 73 | 1. **4.**** Open Source Resources, Frameworks and Libraries** 74 | 75 | Please specify the name of the open source resource along with a URL to where it's housed and it's license type: 76 | 77 | - **●●** I did not use any external resource or library. 78 | 79 | 1. **5.**** Potential Algorithm Improvements** 80 | 81 | Please specify any potential improvements that can be made to the algorithm: 82 | 83 | - **●●** I believe that the search for polygon candidates is the part that could provide the most visible gain. I tried many different things during this second contest, but didn't manage to find a better approach than the one used in the first round. It clearly misses a lot of buildings (specially smaller ones), and that prevent them to even be considered during the polygon evaluation phase. 84 | - **●●** Another point that could be improved is the usage of multichannel pan sharpen images. Visually they as much better than the original multichannel images, but when I tried to use them instead, I got worse results. I had a feeling that the real information contained in the pan sharpen images is about the same contained in the original images, mixed with the high-resolution pan images, which were already used. 85 | 86 | 1. **6.**** Algorithm Limitations** 87 | 88 | Please specify any potential limitations with the algorithm: 89 | 90 | - **●●** It assumes that polygons are convex, so for regions with a lot of irregular building, this can be a serious limitation. I successfully implemented "concave-hull" algorithm this time. It produced much better matches for many cases, increasing the IOU value for "easy" cases, but decreased the final score, so I decided to go back to the original convex hull approach. 91 | 92 | 1. **7.**** Deployment Guide** 93 | 94 | Please provide the exact steps required to build and deploy the code: 95 | 96 | 1. **1.** In the case of this contest, a Dockerized version of the solution was required, which should run out of box. 97 | 2. **2.** My solution only depends of JRE 8. All the rest (model files, compiled classes etc.) can be just copied. 98 | 99 | 1. **8.**** Final Verification** 100 | 101 | Please provide instructions that explain how to train the algorithm and have it execute against sample data: 102 | 103 | Training and testing scripts were provided with the solution. They just call "java" with the expected parameters: 104 | 105 | **java** –Xmx120G -cp bin:lib/imageio-ext-geocore-1.1.16.jar:lib/imageio-ext-streams-1.1.16.jar:lib/imageio-ext-tiff-1.1.16.jar:lib/imageio-ext-utilities-1.1.16.jar:lib/jai\_codec-1.1.3.jar:lib/jai\_core-1.1.3.jar:lib/jai\_imageio-1.1.jar **SpacenetMain**** train <dataFolder1> [<dataFolder2>...]** 106 | 107 | **java** –Xmx40G -cp bin:lib/imageio-ext-geocore-1.1.16.jar:lib/imageio-ext-streams-1.1.16.jar:lib/imageio-ext-tiff-1.1.16.jar:lib/imageio-ext-utilities-1.1.16.jar:lib/jai\_codec-1.1.3.jar:lib/jai\_core-1.1.3.jar:lib/jai\_imageio-1.1.jar **SpacenetMain**** test <dataFolder1> [<dataFolder2>...] <output csv file>** 108 | -------------------------------------------------------------------------------- /2-wleite/bin/Building.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SpaceNetChallenge/BuildingDetectors_Round2/04778fdcdd95cc395ebc7e73942eda58143afb42/2-wleite/bin/Building.class -------------------------------------------------------------------------------- /2-wleite/bin/BuildingDetectorTester$1.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SpaceNetChallenge/BuildingDetectors_Round2/04778fdcdd95cc395ebc7e73942eda58143afb42/2-wleite/bin/BuildingDetectorTester$1.class -------------------------------------------------------------------------------- /2-wleite/bin/BuildingDetectorTester$2.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SpaceNetChallenge/BuildingDetectors_Round2/04778fdcdd95cc395ebc7e73942eda58143afb42/2-wleite/bin/BuildingDetectorTester$2.class -------------------------------------------------------------------------------- /2-wleite/bin/BuildingDetectorTester.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SpaceNetChallenge/BuildingDetectors_Round2/04778fdcdd95cc395ebc7e73942eda58143afb42/2-wleite/bin/BuildingDetectorTester.class -------------------------------------------------------------------------------- /2-wleite/bin/BuildingDetectorTrainer$1.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SpaceNetChallenge/BuildingDetectors_Round2/04778fdcdd95cc395ebc7e73942eda58143afb42/2-wleite/bin/BuildingDetectorTrainer$1.class -------------------------------------------------------------------------------- /2-wleite/bin/BuildingDetectorTrainer.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SpaceNetChallenge/BuildingDetectors_Round2/04778fdcdd95cc395ebc7e73942eda58143afb42/2-wleite/bin/BuildingDetectorTrainer.class -------------------------------------------------------------------------------- /2-wleite/bin/BuildingFeatureExtractor.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SpaceNetChallenge/BuildingDetectors_Round2/04778fdcdd95cc395ebc7e73942eda58143afb42/2-wleite/bin/BuildingFeatureExtractor.class -------------------------------------------------------------------------------- /2-wleite/bin/ClassificationNode.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SpaceNetChallenge/BuildingDetectors_Round2/04778fdcdd95cc395ebc7e73942eda58143afb42/2-wleite/bin/ClassificationNode.class -------------------------------------------------------------------------------- /2-wleite/bin/ClassificationTree.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SpaceNetChallenge/BuildingDetectors_Round2/04778fdcdd95cc395ebc7e73942eda58143afb42/2-wleite/bin/ClassificationTree.class -------------------------------------------------------------------------------- /2-wleite/bin/ImageViewPanel$1.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SpaceNetChallenge/BuildingDetectors_Round2/04778fdcdd95cc395ebc7e73942eda58143afb42/2-wleite/bin/ImageViewPanel$1.class -------------------------------------------------------------------------------- /2-wleite/bin/ImageViewPanel$2.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SpaceNetChallenge/BuildingDetectors_Round2/04778fdcdd95cc395ebc7e73942eda58143afb42/2-wleite/bin/ImageViewPanel$2.class -------------------------------------------------------------------------------- /2-wleite/bin/ImageViewPanel.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SpaceNetChallenge/BuildingDetectors_Round2/04778fdcdd95cc395ebc7e73942eda58143afb42/2-wleite/bin/ImageViewPanel.class -------------------------------------------------------------------------------- /2-wleite/bin/ImgViewer$1.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SpaceNetChallenge/BuildingDetectors_Round2/04778fdcdd95cc395ebc7e73942eda58143afb42/2-wleite/bin/ImgViewer$1.class -------------------------------------------------------------------------------- /2-wleite/bin/ImgViewer.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SpaceNetChallenge/BuildingDetectors_Round2/04778fdcdd95cc395ebc7e73942eda58143afb42/2-wleite/bin/ImgViewer.class -------------------------------------------------------------------------------- /2-wleite/bin/MatchPolygon.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SpaceNetChallenge/BuildingDetectors_Round2/04778fdcdd95cc395ebc7e73942eda58143afb42/2-wleite/bin/MatchPolygon.class -------------------------------------------------------------------------------- /2-wleite/bin/MultiChannelImage.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SpaceNetChallenge/BuildingDetectors_Round2/04778fdcdd95cc395ebc7e73942eda58143afb42/2-wleite/bin/MultiChannelImage.class -------------------------------------------------------------------------------- /2-wleite/bin/PolygonFeatureExtractor.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SpaceNetChallenge/BuildingDetectors_Round2/04778fdcdd95cc395ebc7e73942eda58143afb42/2-wleite/bin/PolygonFeatureExtractor.class -------------------------------------------------------------------------------- /2-wleite/bin/PolygonMatcherTrainer$1.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SpaceNetChallenge/BuildingDetectors_Round2/04778fdcdd95cc395ebc7e73942eda58143afb42/2-wleite/bin/PolygonMatcherTrainer$1.class -------------------------------------------------------------------------------- /2-wleite/bin/PolygonMatcherTrainer.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SpaceNetChallenge/BuildingDetectors_Round2/04778fdcdd95cc395ebc7e73942eda58143afb42/2-wleite/bin/PolygonMatcherTrainer.class -------------------------------------------------------------------------------- /2-wleite/bin/RandomForestBuilder$1.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SpaceNetChallenge/BuildingDetectors_Round2/04778fdcdd95cc395ebc7e73942eda58143afb42/2-wleite/bin/RandomForestBuilder$1.class -------------------------------------------------------------------------------- /2-wleite/bin/RandomForestBuilder.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SpaceNetChallenge/BuildingDetectors_Round2/04778fdcdd95cc395ebc7e73942eda58143afb42/2-wleite/bin/RandomForestBuilder.class -------------------------------------------------------------------------------- /2-wleite/bin/RandomForestPredictor.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SpaceNetChallenge/BuildingDetectors_Round2/04778fdcdd95cc395ebc7e73942eda58143afb42/2-wleite/bin/RandomForestPredictor.class -------------------------------------------------------------------------------- /2-wleite/bin/SpacenetMain.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SpaceNetChallenge/BuildingDetectors_Round2/04778fdcdd95cc395ebc7e73942eda58143afb42/2-wleite/bin/SpacenetMain.class -------------------------------------------------------------------------------- /2-wleite/bin/Util$1.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SpaceNetChallenge/BuildingDetectors_Round2/04778fdcdd95cc395ebc7e73942eda58143afb42/2-wleite/bin/Util$1.class -------------------------------------------------------------------------------- /2-wleite/bin/Util.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SpaceNetChallenge/BuildingDetectors_Round2/04778fdcdd95cc395ebc7e73942eda58143afb42/2-wleite/bin/Util.class -------------------------------------------------------------------------------- /2-wleite/lib/imageio-ext-geocore-1.1.16.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SpaceNetChallenge/BuildingDetectors_Round2/04778fdcdd95cc395ebc7e73942eda58143afb42/2-wleite/lib/imageio-ext-geocore-1.1.16.jar -------------------------------------------------------------------------------- /2-wleite/lib/imageio-ext-streams-1.1.16.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SpaceNetChallenge/BuildingDetectors_Round2/04778fdcdd95cc395ebc7e73942eda58143afb42/2-wleite/lib/imageio-ext-streams-1.1.16.jar -------------------------------------------------------------------------------- /2-wleite/lib/imageio-ext-tiff-1.1.16.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SpaceNetChallenge/BuildingDetectors_Round2/04778fdcdd95cc395ebc7e73942eda58143afb42/2-wleite/lib/imageio-ext-tiff-1.1.16.jar -------------------------------------------------------------------------------- /2-wleite/lib/imageio-ext-utilities-1.1.16.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SpaceNetChallenge/BuildingDetectors_Round2/04778fdcdd95cc395ebc7e73942eda58143afb42/2-wleite/lib/imageio-ext-utilities-1.1.16.jar -------------------------------------------------------------------------------- /2-wleite/lib/jai_codec-1.1.3.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SpaceNetChallenge/BuildingDetectors_Round2/04778fdcdd95cc395ebc7e73942eda58143afb42/2-wleite/lib/jai_codec-1.1.3.jar -------------------------------------------------------------------------------- /2-wleite/lib/jai_core-1.1.3.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SpaceNetChallenge/BuildingDetectors_Round2/04778fdcdd95cc395ebc7e73942eda58143afb42/2-wleite/lib/jai_core-1.1.3.jar -------------------------------------------------------------------------------- /2-wleite/lib/jai_imageio-1.1.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SpaceNetChallenge/BuildingDetectors_Round2/04778fdcdd95cc395ebc7e73942eda58143afb42/2-wleite/lib/jai_imageio-1.1.jar -------------------------------------------------------------------------------- /2-wleite/lib/slf4j-api-1.7.25.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SpaceNetChallenge/BuildingDetectors_Round2/04778fdcdd95cc395ebc7e73942eda58143afb42/2-wleite/lib/slf4j-api-1.7.25.jar -------------------------------------------------------------------------------- /2-wleite/lib/slf4j-nop-1.7.25.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SpaceNetChallenge/BuildingDetectors_Round2/04778fdcdd95cc395ebc7e73942eda58143afb42/2-wleite/lib/slf4j-nop-1.7.25.jar -------------------------------------------------------------------------------- /2-wleite/model/readme.txt: -------------------------------------------------------------------------------- 1 | Model files are not stored in git, they should be regenerated using train.sh -------------------------------------------------------------------------------- /2-wleite/src/Building.java: -------------------------------------------------------------------------------- 1 | import java.awt.Polygon; 2 | import java.awt.geom.Area; 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | public class Building { 7 | final int id; 8 | final List in = new ArrayList(); 9 | final List out = new ArrayList(); 10 | private Area area; 11 | private double areaVal = -1; 12 | 13 | public Building(int id) { 14 | this.id = id; 15 | } 16 | 17 | public Area getArea() { 18 | if (area == null) { 19 | area = new Area(); 20 | for (Polygon p : in) { 21 | area.add(new Area(p)); 22 | } 23 | for (Polygon p : out) { 24 | area.subtract(new Area(p)); 25 | } 26 | } 27 | return area; 28 | } 29 | 30 | public double getAreaVal() { 31 | if (areaVal == -1) 32 | areaVal = Util.areaVal(getArea()); 33 | return areaVal; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /2-wleite/src/BuildingDetectorTester.java: -------------------------------------------------------------------------------- 1 | import java.awt.Polygon; 2 | import java.awt.geom.Area; 3 | import java.awt.image.BufferedImage; 4 | import java.io.BufferedWriter; 5 | import java.io.File; 6 | import java.io.FileWriter; 7 | import java.util.ArrayList; 8 | import java.util.Collections; 9 | import java.util.Comparator; 10 | import java.util.HashSet; 11 | import java.util.List; 12 | import java.util.Map; 13 | import java.util.Set; 14 | 15 | import javax.imageio.ImageIO; 16 | 17 | public class BuildingDetectorTester { 18 | private RandomForestPredictor buildingPredictor, borderPredictor, polyMatchPredictor; 19 | private final int numThreads = SpacenetMain.numThreads; 20 | private final List answers = new ArrayList(); 21 | private final List testingImages = new ArrayList(); 22 | private static boolean isLocal = false; 23 | private int minConf; 24 | 25 | public void run(String trainingFolder, String dataSet, int minConf, File answerFile) { 26 | this.minConf = minConf; 27 | File trainingCsv = isLocal ? new File(trainingFolder, "summaryData/AOI_" + dataSet + "_Train_Building_Solutions.csv") : null; 28 | File folderPan = new File(trainingFolder, "PAN"); 29 | File folder3band = new File(trainingFolder, "RGB-PanSharpen"); 30 | File folder8band = new File(trainingFolder, "MUL"); 31 | File rfModel = new File("model", dataSet); 32 | File rfBuilding = new File(rfModel, "rfBuilding.dat"); 33 | File rfBorder = new File(rfModel, "rfBorder.dat"); 34 | File rfPolyMatch = new File(rfModel, "rfPolyMatch.dat"); 35 | runTest(rfBuilding, rfBorder, rfPolyMatch, folderPan, folder3band, folder8band, answerFile, trainingCsv); 36 | } 37 | 38 | public void runTest(File rfBuilding, File rfBorder, File rfPolyMatch, File folderPan, File folder3band, File folder8band, File answerFile, File trainingCsv) { 39 | loadPredictors(rfBuilding, rfBorder, rfPolyMatch); 40 | if (isLocal) { 41 | Map> buildingsPerImage = Util.readBuildingsCsv(trainingCsv); 42 | Util.splitImages(buildingsPerImage, new int[] { 60, 30, 10 }, 2); 43 | testingImages.addAll(buildingsPerImage.keySet()); 44 | Collections.sort(testingImages, new Comparator() { 45 | public int compare(String a, String b) { 46 | return Integer.compare(buildingsPerImage.get(b).size(), buildingsPerImage.get(a).size()); 47 | } 48 | }); 49 | } else { 50 | File[] files = folder3band.listFiles(); 51 | List ids = new ArrayList(); 52 | String prefix = folder3band.getName() + "_"; 53 | String suffix = ".tif"; 54 | for (File file : files) { 55 | if (file.getName().startsWith(prefix) && file.getName().endsWith(suffix)) { 56 | String id = file.getName().substring(prefix.length(), file.getName().length() - suffix.length()); 57 | ids.add(id); 58 | } 59 | } 60 | testingImages.addAll(ids); 61 | } 62 | processImages(folderPan, folder3band, folder8band); 63 | writeAnswer(answerFile, true); 64 | } 65 | 66 | private void processImages(File folderPan, File folder3band, File folder8band) { 67 | try { 68 | System.err.println("Processing Images"); 69 | long t = System.currentTimeMillis(); 70 | final List images = new ArrayList(testingImages); 71 | final int tot = images.size(); 72 | Thread[] threads = new Thread[numThreads]; 73 | for (int i = 0; i < numThreads; i++) { 74 | threads[i] = new Thread() { 75 | public void run() { 76 | while (true) { 77 | String imageId = null; 78 | int size = 0; 79 | synchronized (images) { 80 | if (images.isEmpty()) break; 81 | imageId = images.remove(0); 82 | size = images.size(); 83 | } 84 | System.err.println("\t" + imageId + " : " + size + "/" + tot); 85 | processImage(imageId, folderPan, folder3band, folder8band); 86 | } 87 | } 88 | }; 89 | threads[i].start(); 90 | } 91 | for (int i = 0; i < numThreads; i++) { 92 | threads[i].join(); 93 | threads[i] = null; 94 | } 95 | System.err.println("\t Elapsed Time: " + (System.currentTimeMillis() - t) + " ms"); 96 | System.err.println(); 97 | } catch (Exception e) { 98 | e.printStackTrace(); 99 | } 100 | } 101 | 102 | private void writeAnswer(File answerFile, boolean checkEmpty) { 103 | try { 104 | System.err.println("Writing Answer"); 105 | long t = System.currentTimeMillis(); 106 | BufferedWriter out = new BufferedWriter(new FileWriter(answerFile, true)); 107 | Set seen = new HashSet(); 108 | for (String line : answers) { 109 | if (line == null) continue; 110 | out.write(line); 111 | out.newLine(); 112 | int p = line.indexOf(','); 113 | if (p > 0) seen.add(line.substring(0, p)); 114 | } 115 | if (checkEmpty) { 116 | for (String id : testingImages) { 117 | if (seen.contains(id)) continue; 118 | out.write(id); 119 | out.write(",-1,POLYGON EMPTY,1"); 120 | out.newLine(); 121 | } 122 | } 123 | out.close(); 124 | System.err.println("\t File: " + answerFile.getPath()); 125 | System.err.println("\t Lines Written: " + answers.size()); 126 | 127 | System.err.println("\t Elapsed Time: " + (System.currentTimeMillis() - t) + " ms"); 128 | System.err.println(); 129 | } catch (Exception e) { 130 | e.printStackTrace(); 131 | } 132 | } 133 | 134 | private void processImage(String imageId, File folderPan, File folder3band, File folder8band) { 135 | try { 136 | File imageFilePan = new File(folderPan, folderPan.getName() + "_" + imageId + ".tif"); 137 | File imageFile3band = new File(folder3band, folder3band.getName() + "_" + imageId + ".tif"); 138 | File imageFile8band = new File(folder8band, folder8band.getName() + "_" + imageId + ".tif"); 139 | BufferedImage imgPan = ImageIO.read(imageFilePan); 140 | BufferedImage img3band = ImageIO.read(imageFile3band); 141 | BufferedImage img8band = ImageIO.read(imageFile8band); 142 | MultiChannelImage mci = new MultiChannelImage(imgPan, img3band, img8band); 143 | double[][][] vals = Util.evalImage(mci, buildingPredictor, borderPredictor); 144 | double[][] buildingValues = vals[0]; 145 | double[][] borderValues = vals[1]; 146 | PolygonFeatureExtractor pfe = new PolygonFeatureExtractor(buildingValues[0].length, buildingValues.length); 147 | List candidates = new ArrayList(); 148 | for (int i = 0; i < PolygonFeatureExtractor.borderShifts.length; i++) { 149 | int borderShift = PolygonFeatureExtractor.borderShifts[i]; 150 | double borderWeight = PolygonFeatureExtractor.borderWeights[i]; 151 | for (double cut : PolygonFeatureExtractor.buildingsCuts) { 152 | List polygons = Util.findBuildings(buildingValues, borderValues, cut, borderWeight, PolygonFeatureExtractor.buildingsPolyBorder + borderShift, 153 | PolygonFeatureExtractor.buildingsPolyBorder2 + borderShift); 154 | for (Polygon polygon : polygons) { 155 | double prob = polyMatchPredictor.predict(pfe.getFeatures(mci, polygon, buildingValues, borderValues)); 156 | if (prob > minConf / 100.0) { 157 | candidates.add(new MatchPolygon(prob, polygon)); 158 | } 159 | } 160 | } 161 | } 162 | double maxInter = 0.1; 163 | Collections.sort(candidates); 164 | List areas = new ArrayList(); 165 | List aa = new ArrayList(); 166 | List added = new ArrayList(); 167 | NEXT: for (int i = 0; i < candidates.size(); i++) { 168 | Polygon pi = candidates.get(i).polygon; 169 | double pia = Util.getArea(pi); 170 | Area ai = new Area(pi); 171 | for (int j = 0; j < added.size(); j++) { 172 | Polygon pj = added.get(j).polygon; 173 | if (pi.getBounds().getMaxX() <= pj.getBounds().getMinX()) continue; 174 | if (pi.getBounds().getMaxY() <= pj.getBounds().getMinY()) continue; 175 | if (pj.getBounds().getMaxX() <= pi.getBounds().getMinX()) continue; 176 | if (pj.getBounds().getMaxY() <= pi.getBounds().getMinY()) continue; 177 | Area aj = new Area(areas.get(j)); 178 | aj.intersect(ai); 179 | double aaj = Util.areaVal(aj); 180 | double pja = aa.get(j); 181 | if (aaj > maxInter * pia || aaj > maxInter * pja) { 182 | continue NEXT; 183 | } 184 | } 185 | added.add(candidates.get(i)); 186 | areas.add(ai); 187 | aa.add(pia); 188 | } 189 | 190 | for (int i = 0; i < added.size(); i++) { 191 | Polygon polygon = added.get(i).polygon; 192 | StringBuilder sb = new StringBuilder(); 193 | sb.append(imageId); 194 | sb.append(","); 195 | sb.append(i); 196 | sb.append(","); 197 | sb.append("\"POLYGON (("); 198 | for (int j = 0; j <= polygon.npoints; j++) { 199 | if (j > 0) sb.append(", "); 200 | int k1 = j == polygon.npoints ? 0 : j; 201 | sb.append(polygon.xpoints[k1]); 202 | sb.append(" "); 203 | sb.append(polygon.ypoints[k1]); 204 | sb.append(" "); 205 | sb.append(0); 206 | } 207 | sb.append("))\""); 208 | sb.append(","); 209 | sb.append(added.size() - i); 210 | synchronized (answers) { 211 | answers.add(sb.toString()); 212 | } 213 | } 214 | } catch (Exception e) { 215 | e.printStackTrace(); 216 | } 217 | } 218 | 219 | private void loadPredictors(File rfBuilding, File rfBorder, File rfPolyMatch) { 220 | try { 221 | System.err.println("Loading Predictors"); 222 | long t = System.currentTimeMillis(); 223 | buildingPredictor = loadPredictor(rfBuilding); 224 | borderPredictor = loadPredictor(rfBorder); 225 | polyMatchPredictor = loadPredictor(rfPolyMatch); 226 | System.err.println("\t Elapsed Time: " + (System.currentTimeMillis() - t) + " ms"); 227 | System.err.println(); 228 | } catch (Exception e) { 229 | e.printStackTrace(); 230 | } 231 | } 232 | 233 | private RandomForestPredictor loadPredictor(File rfFile) { 234 | try { 235 | return RandomForestPredictor.load(rfFile); 236 | } catch (Exception e) { 237 | e.printStackTrace(); 238 | } 239 | return null; 240 | } 241 | } -------------------------------------------------------------------------------- /2-wleite/src/BuildingDetectorTrainer.java: -------------------------------------------------------------------------------- 1 | import java.awt.Polygon; 2 | import java.awt.Rectangle; 3 | import java.awt.image.BufferedImage; 4 | import java.io.File; 5 | import java.util.ArrayList; 6 | import java.util.List; 7 | import java.util.Map; 8 | import java.util.SplittableRandom; 9 | 10 | import javax.imageio.ImageIO; 11 | 12 | public class BuildingDetectorTrainer { 13 | private Map> buildingsPerImage; 14 | private final int numThreads = SpacenetMain.numThreads; 15 | private static final int minRowsPerNode = 16; 16 | private final int maxTrees = 60; 17 | private static final int maxSamples = 100_000_000; 18 | private int totSamples = 0; 19 | private float[][] features = new float[BuildingFeatureExtractor.numFeatures][maxSamples]; 20 | private boolean[] borderClassif = new boolean[maxSamples]; 21 | private boolean[] buildingClassif = new boolean[maxSamples]; 22 | 23 | public void run(String trainingFolder, String dataSet) { 24 | File trainingCsv = new File(trainingFolder, "summaryData/AOI_" + dataSet + "_Train_Building_Solutions.csv"); 25 | File folderPan = new File(trainingFolder, "PAN"); 26 | File folder3band = new File(trainingFolder, "RGB-PanSharpen"); 27 | File folder8band = new File(trainingFolder, "MUL"); 28 | File rfModel = new File("model", dataSet); 29 | if (!rfModel.exists()) rfModel.mkdirs(); 30 | File rfBuilding = new File(rfModel, "rfBuilding.dat"); 31 | File rfBorder = new File(rfModel, "rfBorder.dat"); 32 | train(trainingCsv, folderPan, folder3band, folder8band, rfBuilding, rfBorder); 33 | } 34 | 35 | public void train(File trainingCsv, File folderPan, File folder3band, File folder8band, File rfBuilding, File rfBorder) { 36 | buildingsPerImage = Util.readBuildingsCsv(trainingCsv); 37 | Util.splitImages(buildingsPerImage, new int[] { 60, 40, 0 }, 0); 38 | processImages(folderPan, folder3band, folder8band); 39 | buildRandomForests(rfBuilding, rfBorder); 40 | } 41 | 42 | private void buildRandomForests(File rfBuilding, File rfBorder) { 43 | try { 44 | System.err.println("Building Random Forests"); 45 | for (int k = 0; k <= 1; k++) { 46 | long t = System.currentTimeMillis(); 47 | if (k == 0) { 48 | RandomForestBuilder.train(features, buildingClassif, totSamples, maxTrees, rfBuilding, numThreads, minRowsPerNode); 49 | System.err.println("\t RF Building: " + rfBuilding.length() + " bytes"); 50 | System.err.println("\t Elapsed Time: " + (System.currentTimeMillis() - t) + " ms"); 51 | } else if (k == 1) { 52 | RandomForestBuilder.train(features, borderClassif, totSamples, maxTrees, rfBorder, numThreads, minRowsPerNode); 53 | System.err.println("\t RF Border: " + rfBorder.length() + " bytes"); 54 | System.err.println("\t Elapsed Time: " + (System.currentTimeMillis() - t) + " ms"); 55 | } 56 | } 57 | System.err.println(); 58 | } catch (Exception e) { 59 | e.printStackTrace(); 60 | } 61 | } 62 | 63 | private void processImages(final File folderPan, final File folder3band, final File folder8band) { 64 | try { 65 | System.err.println("Processing Images"); 66 | long t = System.currentTimeMillis(); 67 | final List images = new ArrayList(buildingsPerImage.keySet()); 68 | Thread[] threads = new Thread[numThreads]; 69 | for (int i = 0; i < numThreads; i++) { 70 | final int start = i; 71 | threads[i] = new Thread() { 72 | public void run() { 73 | for (int j = start; j < images.size(); j += numThreads) { 74 | String imageId = images.get(j); 75 | File imageFilePan = new File(folderPan, folderPan.getName() + "_" + imageId + ".tif"); 76 | File imageFile3band = new File(folder3band, folder3band.getName() + "_" + imageId + ".tif"); 77 | File imageFile8band = new File(folder8band, folder8band.getName() + "_" + imageId + ".tif"); 78 | List buildings = buildingsPerImage.get(imageId); 79 | System.err.println("\t\t" + (j + 1) + "/" + images.size() + "\t" + imageId); 80 | processImage(imageFilePan, imageFile3band, imageFile8band, buildings); 81 | } 82 | } 83 | }; 84 | threads[i].start(); 85 | } 86 | for (int i = 0; i < numThreads; i++) { 87 | threads[i].join(); 88 | threads[i] = null; 89 | } 90 | int nInside = 0; 91 | int nBorder = 0; 92 | for (int i = 0; i < totSamples; i++) { 93 | if (borderClassif[i]) nBorder++; 94 | if (buildingClassif[i]) nInside++; 95 | } 96 | System.err.println("\t Inside: " + nInside); 97 | System.err.println("\t Border: " + nBorder); 98 | System.err.println("\t Samples: " + totSamples); 99 | System.err.println("\t Elapsed Time: " + (System.currentTimeMillis() - t) + " ms"); 100 | System.err.println(); 101 | } catch (Exception e) { 102 | e.printStackTrace(); 103 | } 104 | } 105 | 106 | private void processImage(File imageFilePan, File imageFile3band, File imageFile8band, List buildings) { 107 | try { 108 | BufferedImage img3band = ImageIO.read(imageFile3band); 109 | int w = img3band.getWidth(); 110 | int h = img3band.getHeight(); 111 | int[][] inside = new int[h][w]; 112 | boolean[][] border = new boolean[h][w]; 113 | int id = 0; 114 | for (Building building : buildings) { 115 | id++; 116 | for (int step = 0; step <= 1; step++) { 117 | int value = step == 0 ? id : 0; 118 | List polygons = step == 0 ? building.in : building.out; 119 | for (Polygon polygon : polygons) { 120 | Rectangle rc = polygon.getBounds(); 121 | int yMin = Math.max(rc.y, 0); 122 | int yMax = Math.min(rc.y + rc.height, h); 123 | int xMin = Math.max(rc.x, 0); 124 | int xMax = Math.min(rc.x + rc.width, w); 125 | for (int y = yMin; y < yMax; y++) { 126 | int[] iy = inside[y]; 127 | for (int x = xMin; x < xMax; x++) { 128 | if (polygon.contains(x, y)) iy[x] = value; 129 | } 130 | } 131 | } 132 | } 133 | } 134 | 135 | int[] queue = new int[w * h * 4]; 136 | int tot = 0; 137 | for (int y = 0; y < h; y++) { 138 | for (int x = 0; x < w; x++) { 139 | int v = inside[y][x]; 140 | boolean b = false; 141 | if (x > 0 && v != inside[y][x - 1]) b = true; 142 | else if (x < w - 1 && v != inside[y][x + 1]) b = true; 143 | else if (y > 0 && v != inside[y - 1][x]) b = true; 144 | else if (y < h - 1 && v != inside[y + 1][x]) b = true; 145 | if (b) { 146 | border[y][x] = true; 147 | queue[tot++] = y * w + x; 148 | } 149 | } 150 | } 151 | 152 | SplittableRandom rnd = new SplittableRandom(22062012 + imageFile8band.getName().hashCode()); 153 | BufferedImage img8band = ImageIO.read(imageFile8band); 154 | BufferedImage imgPan = ImageIO.read(imageFilePan); 155 | MultiChannelImage mci = new MultiChannelImage(imgPan, img3band, img8band); 156 | int outSubsample = 16; 157 | int inSubsample = 8; 158 | int borderSubsample = 1; 159 | if (id == 0) outSubsample *= 4; 160 | for (int y = 0; y < h; y++) { 161 | int[] iy = inside[y]; 162 | boolean[] by = border[y]; 163 | for (int x = 0; x < w; x++) { 164 | boolean isBorder = by[x]; 165 | boolean isInside = iy[x] > 0; 166 | int subsample = isBorder ? borderSubsample : isInside ? inSubsample : outSubsample; 167 | if (rnd.nextInt(subsample) > 0) continue; 168 | float[] arrFeatures = BuildingFeatureExtractor.getFeatures(mci, x, y, w, h); 169 | synchronized (borderClassif) { 170 | if (totSamples < maxSamples) { 171 | buildingClassif[totSamples] = isInside; 172 | borderClassif[totSamples] = isBorder; 173 | for (int i = 0; i < arrFeatures.length; i++) { 174 | features[i][totSamples] = arrFeatures[i]; 175 | } 176 | totSamples++; 177 | } 178 | } 179 | } 180 | } 181 | } catch (Exception e) { 182 | e.printStackTrace(); 183 | } 184 | } 185 | } -------------------------------------------------------------------------------- /2-wleite/src/BuildingFeatureExtractor.java: -------------------------------------------------------------------------------- 1 | public class BuildingFeatureExtractor { 2 | public static final int numFeatures = 68; 3 | 4 | public static float[] getFeatures(MultiChannelImage image, int sx, int sy, int w, int h) { 5 | float[] ret = new float[numFeatures]; 6 | int k = 0; 7 | for (int i : new int[] {0,1,3,5}) { 8 | int rx = sx - i; 9 | int ry = sy - i; 10 | int rs = i * 2 + 1; 11 | int p = i == 0 ? 1 : 3; 12 | System.arraycopy(rectStatFeatures(image.h, rx, ry, rs, rs, w, h), 0, ret, k, p); 13 | k += p; 14 | System.arraycopy(rectStatFeatures(image.s, rx, ry, rs, rs, w, h), 0, ret, k, p); 15 | k += p; 16 | System.arraycopy(rectStatFeatures(image.gray, rx, ry, rs, rs, w, h), 0, ret, k, p); 17 | k += p; 18 | System.arraycopy(rectStatFeatures(image.edge, rx, ry, rs, rs, w, h), 0, ret, k, p); 19 | k += p; 20 | if (i > 0) { 21 | p = 2; 22 | System.arraycopy(text(image.gray, rx, ry, rs, rs, w, h), 0, ret, k, p); 23 | k += p; 24 | System.arraycopy(text(image.edge, rx, ry, rs, rs, w, h), 0, ret, k, p); 25 | k += p; 26 | } 27 | } 28 | int w8 = image.w8; 29 | int h8 = image.h8; 30 | int rx = sx / 4; 31 | int ry = sy / 4; 32 | int p = rx + ry * w8; 33 | if (ry < 0 || rx < 0 || rx >= w8 || ry >= h8) { 34 | for (int i = 0; i < 16; i++) { 35 | ret[k++] = -1; 36 | } 37 | } else { 38 | for (int i = 0; i < 8; i++) { 39 | ret[k++] = image.extraBands[i][p]; 40 | ret[k++] = image.extraEdges[i][p]; 41 | } 42 | } 43 | return ret; 44 | } 45 | 46 | private static float[] text(int[] arr, int rx, int ry, int rw, int rh, int width, int height) { 47 | final int[] cnt1 = new int[256]; 48 | final int[] cnt2 = new int[256]; 49 | final int[] dxy = new int[] {-width - 1,-1,width - 1,width,width + 1,1,-width + 1,-width}; 50 | int max1 = 0; 51 | int max2 = 0; 52 | int ret1 = -1; 53 | int ret2 = -1; 54 | for (int y = ry; y < ry + rh; y++) { 55 | if (y < 1 || y >= height - 1) continue; 56 | int yw = y * width; 57 | for (int x = rx; x < rx + rw; x++) { 58 | if (x < 1 || x >= width - 1) continue; 59 | int m = 0; 60 | int s = 0; 61 | int c = yw + x; 62 | int ac = arr[c]; 63 | int aq = arr[c + dxy[7]]; 64 | for (int i = 0; i < 8; i++) { 65 | int ap = arr[c + dxy[i]]; 66 | if (ap > aq) m |= 1 << i; 67 | if (ap > ac) s |= 1 << i; 68 | aq = ap; 69 | } 70 | if (++cnt1[m] > max1) max1 = cnt1[ret1 = m]; 71 | if (++cnt2[s] > max2) max2 = cnt2[ret2 = s]; 72 | } 73 | } 74 | return new float[] {ret1,ret2}; 75 | } 76 | 77 | private static float[] rectStatFeatures(int[] a, int rx, int ry, int rw, int rh, int width, int height) { 78 | double sum = 0; 79 | double sumSquares = 0; 80 | double sumCubes = 0; 81 | int cnt = 0; 82 | for (int y = ry; y < ry + rh; y++) { 83 | if (y < 0 || y >= height) continue; 84 | for (int x = rx; x < rx + rw; x++) { 85 | if (x < 0 || x >= width) continue; 86 | int off = y * width + x; 87 | double p = a[off]; 88 | sum += p; 89 | double pp = p * p; 90 | sumSquares += pp; 91 | sumCubes += pp * p; 92 | cnt++; 93 | } 94 | } 95 | float[] ret = new float[3]; 96 | if (cnt > 0) { 97 | double k3 = (sumCubes - 3 * sumSquares * sum / cnt + 2 * sum * sum * sum / cnt / cnt) / cnt; 98 | double k2 = (sumSquares - sum * sum / cnt) / cnt; 99 | ret[0] = (float) (sum / cnt); 100 | ret[1] = (float) k2; 101 | ret[2] = (float) (k2 == 0 ? 0 : k3 / Math.pow(k2, 1.5)); 102 | } 103 | return ret; 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /2-wleite/src/ClassificationNode.java: -------------------------------------------------------------------------------- 1 | 2 | public class ClassificationNode { 3 | ClassificationNode left, right; 4 | float splitVal; 5 | int classif, splitFeature, startRow, endRow, numRows; 6 | double impurity, average = Double.NaN; 7 | 8 | public ClassificationNode(int classif, int numRows, double impurtity, int startRow, int endRow) { 9 | this.classif = classif; 10 | this.numRows = numRows; 11 | this.startRow = startRow; 12 | this.endRow = endRow; 13 | this.impurity = impurtity; 14 | } 15 | 16 | public ClassificationNode(int numRows, int startRow, int endRow, double average, double error) { 17 | this.numRows = numRows; 18 | this.startRow = startRow; 19 | this.endRow = endRow; 20 | this.average = average; 21 | this.impurity = error; 22 | } 23 | 24 | public float getValue() { 25 | return Double.isNaN(average) ? classif / (float) numRows : (float) average; 26 | } 27 | 28 | public boolean isLeaf() { 29 | return left == null && right == null; 30 | } 31 | 32 | public boolean isPure() { 33 | return classif == 0 || classif == numRows; 34 | } 35 | } -------------------------------------------------------------------------------- /2-wleite/src/ClassificationTree.java: -------------------------------------------------------------------------------- 1 | import java.util.Arrays; 2 | import java.util.SplittableRandom; 3 | 4 | public class ClassificationTree { 5 | private static double[] log = new double[1 << 27]; 6 | 7 | static { 8 | for (int tot = 1; tot < log.length; tot++) { 9 | log[tot] = Math.log(tot); 10 | } 11 | } 12 | 13 | private ClassificationNode root; 14 | private final SplittableRandom rnd; 15 | 16 | ClassificationTree(float[][] features, boolean[] classif, int totRows, int idx, int minRowsPerNode, int maxNodes) { 17 | long t = System.currentTimeMillis(); 18 | ClassificationNode[] nodes = new ClassificationNode[maxNodes + 2]; 19 | 20 | rnd = new SplittableRandom(197209091220L + idx); 21 | int featuresSteps = features.length; 22 | int[] weight = new int[totRows]; 23 | for (int i = 0; i < totRows; i++) { 24 | weight[rnd.nextInt(totRows)]++; 25 | } 26 | int numSel = 0; 27 | for (int i = 0; i < totRows; i++) { 28 | if (weight[i] > 0) numSel++; 29 | } 30 | int[] selRows = new int[numSel]; 31 | numSel = 0; 32 | int classifCount = 0; 33 | for (int i = 0; i < totRows; i++) { 34 | if (weight[i] > 0) { 35 | selRows[numSel++] = i; 36 | if (classif[i]) classifCount += weight[i]; 37 | } 38 | } 39 | root = new ClassificationNode(classifCount, totRows, impurity(classifCount, totRows), 0, numSel - 1); 40 | int nodeCnt = 0; 41 | nodes[nodeCnt++] = root; 42 | int msg = 2; 43 | float[] splitVals = new float[10]; 44 | for (int i = 0; i < nodeCnt && nodeCnt < maxNodes; i++) { 45 | ClassificationNode node = nodes[i]; 46 | if (i == msg) { 47 | System.err.println("\t\t\t" + idx + "\t" + nodeCnt + " nodes\t" + node.numRows + " rows\t" + (System.currentTimeMillis() - t) / 1000 + "s"); 48 | msg *= 2; 49 | } 50 | if (node.isPure() || node.numRows < minRowsPerNode * 2) continue; 51 | 52 | double maxSplitGain = 0; 53 | float bestSplitVal = 0; 54 | int bestSplitFeature = -1; 55 | 56 | for (int j = 0; j < featuresSteps; j++) { 57 | int splitFeature = rnd.nextInt(features.length); 58 | float[] featuresSplitFeature = features[splitFeature]; 59 | for (int k = 0; k < 10; k++) { 60 | splitVals[k] = featuresSplitFeature[randomNodeRow(selRows, node, rnd)]; 61 | } 62 | Arrays.sort(splitVals); 63 | float splitVal1 = splitVals[0]; 64 | float splitVal2 = splitVals[1]; 65 | float splitVal3 = splitVals[2]; 66 | float splitVal4 = splitVals[3]; 67 | float splitVal5 = splitVals[4]; 68 | float splitVal6 = splitVals[5]; 69 | float splitVal7 = splitVals[6]; 70 | float splitVal8 = splitVals[7]; 71 | float splitVal9 = splitVals[8]; 72 | float splitVal10 = splitVals[9]; 73 | int leftTot1 = 0; 74 | int leftClassif1 = 0; 75 | int leftTot2 = 0; 76 | int leftClassif2 = 0; 77 | int leftTot3 = 0; 78 | int leftClassif3 = 0; 79 | int leftTot4 = 0; 80 | int leftClassif4 = 0; 81 | int leftTot5 = 0; 82 | int leftClassif5 = 0; 83 | int leftTot6 = 0; 84 | int leftClassif6 = 0; 85 | int leftTot7 = 0; 86 | int leftClassif7 = 0; 87 | int leftTot8 = 0; 88 | int leftClassif8 = 0; 89 | int leftTot9 = 0; 90 | int leftClassif9 = 0; 91 | int leftTot10 = 0; 92 | int leftClassif10 = 0; 93 | for (int r = node.startRow; r <= node.endRow; r++) { 94 | int row = selRows[r]; 95 | int w = weight[row]; 96 | float rowVal = featuresSplitFeature[row]; 97 | boolean rowClassif = classif[row]; 98 | if (rowVal < splitVal1) { 99 | if (rowClassif) leftClassif1 += w; 100 | leftTot1 += w; 101 | } else if (rowVal < splitVal2) { 102 | if (rowClassif) leftClassif2 += w; 103 | leftTot2 += w; 104 | } else if (rowVal < splitVal3) { 105 | if (rowClassif) leftClassif3 += w; 106 | leftTot3 += w; 107 | } else if (rowVal < splitVal4) { 108 | if (rowClassif) leftClassif4 += w; 109 | leftTot4 += w; 110 | } else if (rowVal < splitVal5) { 111 | if (rowClassif) leftClassif5 += w; 112 | leftTot5 += w; 113 | } else if (rowVal < splitVal6) { 114 | if (rowClassif) leftClassif6 += w; 115 | leftTot6 += w; 116 | } else if (rowVal < splitVal7) { 117 | if (rowClassif) leftClassif7 += w; 118 | leftTot7 += w; 119 | } else if (rowVal < splitVal8) { 120 | if (rowClassif) leftClassif8 += w; 121 | leftTot8 += w; 122 | } else if (rowVal < splitVal9) { 123 | if (rowClassif) leftClassif9 += w; 124 | leftTot9 += w; 125 | } else if (rowVal < splitVal10) { 126 | if (rowClassif) leftClassif10 += w; 127 | leftTot10 += w; 128 | } 129 | } 130 | if (leftTot1 >= minRowsPerNode && node.numRows - leftTot1 >= minRowsPerNode) { 131 | double splitGain = node.impurity - impurity(leftClassif1, leftTot1, node.classif - leftClassif1, node.numRows - leftTot1); 132 | if (splitGain > maxSplitGain) { 133 | maxSplitGain = splitGain; 134 | bestSplitFeature = splitFeature; 135 | bestSplitVal = splitVal1; 136 | } 137 | } 138 | leftTot2 += leftTot1; 139 | leftClassif2 += leftClassif1; 140 | if (leftTot2 >= minRowsPerNode && node.numRows - leftTot2 >= minRowsPerNode) { 141 | double splitGain = node.impurity - impurity(leftClassif2, leftTot2, node.classif - leftClassif2, node.numRows - leftTot2); 142 | if (splitGain > maxSplitGain) { 143 | maxSplitGain = splitGain; 144 | bestSplitFeature = splitFeature; 145 | bestSplitVal = splitVal2; 146 | } 147 | } 148 | leftTot3 += leftTot2; 149 | leftClassif3 += leftClassif2; 150 | if (leftTot3 >= minRowsPerNode && node.numRows - leftTot3 >= minRowsPerNode) { 151 | double splitGain = node.impurity - impurity(leftClassif3, leftTot3, node.classif - leftClassif3, node.numRows - leftTot3); 152 | if (splitGain > maxSplitGain) { 153 | maxSplitGain = splitGain; 154 | bestSplitFeature = splitFeature; 155 | bestSplitVal = splitVal3; 156 | } 157 | } 158 | leftTot4 += leftTot3; 159 | leftClassif4 += leftClassif3; 160 | if (leftTot4 >= minRowsPerNode && node.numRows - leftTot4 >= minRowsPerNode) { 161 | double splitGain = node.impurity - impurity(leftClassif4, leftTot4, node.classif - leftClassif4, node.numRows - leftTot4); 162 | if (splitGain > maxSplitGain) { 163 | maxSplitGain = splitGain; 164 | bestSplitFeature = splitFeature; 165 | bestSplitVal = splitVal4; 166 | } 167 | } 168 | leftTot5 += leftTot4; 169 | leftClassif5 += leftClassif4; 170 | if (leftTot5 >= minRowsPerNode && node.numRows - leftTot5 >= minRowsPerNode) { 171 | double splitGain = node.impurity - impurity(leftClassif5, leftTot5, node.classif - leftClassif5, node.numRows - leftTot5); 172 | if (splitGain > maxSplitGain) { 173 | maxSplitGain = splitGain; 174 | bestSplitFeature = splitFeature; 175 | bestSplitVal = splitVal5; 176 | } 177 | } 178 | leftTot6 += leftTot5; 179 | leftClassif6 += leftClassif5; 180 | if (leftTot6 >= minRowsPerNode && node.numRows - leftTot6 >= minRowsPerNode) { 181 | double splitGain = node.impurity - impurity(leftClassif6, leftTot6, node.classif - leftClassif6, node.numRows - leftTot6); 182 | if (splitGain > maxSplitGain) { 183 | maxSplitGain = splitGain; 184 | bestSplitFeature = splitFeature; 185 | bestSplitVal = splitVal6; 186 | } 187 | } 188 | leftTot7 += leftTot6; 189 | leftClassif7 += leftClassif6; 190 | if (leftTot7 >= minRowsPerNode && node.numRows - leftTot7 >= minRowsPerNode) { 191 | double splitGain = node.impurity - impurity(leftClassif7, leftTot7, node.classif - leftClassif7, node.numRows - leftTot7); 192 | if (splitGain > maxSplitGain) { 193 | maxSplitGain = splitGain; 194 | bestSplitFeature = splitFeature; 195 | bestSplitVal = splitVal7; 196 | } 197 | } 198 | leftTot8 += leftTot7; 199 | leftClassif8 += leftClassif7; 200 | if (leftTot8 >= minRowsPerNode && node.numRows - leftTot8 >= minRowsPerNode) { 201 | double splitGain = node.impurity - impurity(leftClassif8, leftTot8, node.classif - leftClassif8, node.numRows - leftTot8); 202 | if (splitGain > maxSplitGain) { 203 | maxSplitGain = splitGain; 204 | bestSplitFeature = splitFeature; 205 | bestSplitVal = splitVal8; 206 | } 207 | } 208 | leftTot9 += leftTot8; 209 | leftClassif9 += leftClassif8; 210 | if (leftTot9 >= minRowsPerNode && node.numRows - leftTot9 >= minRowsPerNode) { 211 | double splitGain = node.impurity - impurity(leftClassif9, leftTot9, node.classif - leftClassif9, node.numRows - leftTot9); 212 | if (splitGain > maxSplitGain) { 213 | maxSplitGain = splitGain; 214 | bestSplitFeature = splitFeature; 215 | bestSplitVal = splitVal9; 216 | } 217 | } 218 | leftTot10 += leftTot9; 219 | leftClassif10 += leftClassif9; 220 | if (leftTot10 >= minRowsPerNode && node.numRows - leftTot10 >= minRowsPerNode) { 221 | double splitGain = node.impurity - impurity(leftClassif10, leftTot10, node.classif - leftClassif10, node.numRows - leftTot10); 222 | if (splitGain > maxSplitGain) { 223 | maxSplitGain = splitGain; 224 | bestSplitFeature = splitFeature; 225 | bestSplitVal = splitVal10; 226 | } 227 | } 228 | } 229 | if (bestSplitFeature >= 0) { 230 | int leftTot = 0; 231 | int rightTot = 0; 232 | int leftClassif = 0; 233 | int rightClassif = 0; 234 | int endLeft = node.endRow; 235 | float[] featuresSplitFeature = features[bestSplitFeature]; 236 | for (int r = node.startRow; r <= endLeft; r++) { 237 | int row = selRows[r]; 238 | int w = weight[row]; 239 | if (featuresSplitFeature[row] < bestSplitVal) { 240 | if (classif[row]) leftClassif += w; 241 | leftTot += w; 242 | } else { 243 | if (classif[row]) rightClassif += w; 244 | rightTot += w; 245 | selRows[r--] = selRows[endLeft]; 246 | selRows[endLeft--] = row; 247 | } 248 | } 249 | node.left = new ClassificationNode(leftClassif, leftTot, impurity(leftClassif, leftTot), node.startRow, endLeft); 250 | node.right = new ClassificationNode(rightClassif, rightTot, impurity(rightClassif, rightTot), endLeft + 1, node.endRow); 251 | nodes[nodeCnt++] = node.left; 252 | nodes[nodeCnt++] = node.right; 253 | node.splitVal = bestSplitVal; 254 | node.splitFeature = bestSplitFeature; 255 | } 256 | } 257 | System.err.println("\t\t" + idx + "\t" + nodeCnt + " nodes\t" + (System.currentTimeMillis() - t) / 1000 + "s"); 258 | 259 | } 260 | 261 | public ClassificationNode getRoot() { 262 | return root; 263 | } 264 | 265 | private final double impurity(int cnt, int tot) { 266 | if (tot <= 1) return 0; 267 | double lt = log[tot]; 268 | double val = 0; 269 | if (cnt > 0) val -= cnt * (log[cnt] - lt) / tot; 270 | cnt = tot - cnt; 271 | if (cnt > 0) val -= cnt * (log[cnt] - lt) / tot; 272 | return val; 273 | } 274 | 275 | private final double impurity(int cnt1, int tot1, int cnt2, int tot2) { 276 | return (impurity(cnt1, tot1) * tot1 + impurity(cnt2, tot2) * tot2) / (tot1 + tot2); 277 | } 278 | 279 | private final int randomNodeRow(int[] rows, ClassificationNode node, SplittableRandom rnd) { 280 | return rows[rnd.nextInt(node.endRow - node.startRow + 1) + node.startRow]; 281 | } 282 | } 283 | -------------------------------------------------------------------------------- /2-wleite/src/ImgViewer.java: -------------------------------------------------------------------------------- 1 | import java.awt.BorderLayout; 2 | import java.awt.Container; 3 | import java.awt.Cursor; 4 | import java.awt.Dimension; 5 | import java.awt.Graphics; 6 | import java.awt.Graphics2D; 7 | import java.awt.Insets; 8 | import java.awt.Point; 9 | import java.awt.Rectangle; 10 | import java.awt.RenderingHints; 11 | import java.awt.event.KeyAdapter; 12 | import java.awt.event.KeyEvent; 13 | import java.awt.event.MouseAdapter; 14 | import java.awt.event.MouseEvent; 15 | import java.awt.image.BufferedImage; 16 | import java.util.ArrayList; 17 | import java.util.List; 18 | 19 | import javax.swing.JFrame; 20 | import javax.swing.JPanel; 21 | import javax.swing.JScrollPane; 22 | 23 | public class ImgViewer extends JFrame { 24 | private static final long serialVersionUID = -5820105568092949073L; 25 | private static final Object lock = new Object(); 26 | private static int off = 0; 27 | private List images = new ArrayList(); 28 | private List ids = new ArrayList(); 29 | int idx = 0; 30 | 31 | public ImgViewer(final BufferedImage img) { 32 | this(img, ""); 33 | } 34 | 35 | public ImgViewer(final BufferedImage img, String title) { 36 | setTitle(title); 37 | final ImageViewPanel panel = new ImageViewPanel(); 38 | getContentPane().add(panel); 39 | synchronized (lock) { 40 | setBounds(0 * 1920 + off, off, 1620, 860); 41 | off += 24; 42 | if (off > 300) off = 0; 43 | } 44 | setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); 45 | setVisible(true); 46 | setExtendedState(JFrame.MAXIMIZED_BOTH); 47 | try { 48 | Thread.sleep(100); 49 | } catch (InterruptedException e) { 50 | } 51 | panel.setImage(img); 52 | images.add(img); 53 | ids.add(title); 54 | 55 | panel.setFocusable(true); 56 | panel.requestFocusInWindow(); 57 | panel.addKeyListener(new KeyAdapter() { 58 | public void keyPressed(KeyEvent e) { 59 | if (e.getKeyCode() == KeyEvent.VK_RIGHT) { 60 | if (++idx >= images.size()) idx = 0; 61 | setTitle(ids.get(idx)); 62 | panel.setImage(images.get(idx)); 63 | } else if (e.getKeyCode() == KeyEvent.VK_LEFT) { 64 | if (--idx < 0) idx = images.size() - 1; 65 | setTitle(ids.get(idx)); 66 | panel.setImage(images.get(idx)); 67 | } 68 | } 69 | }); 70 | } 71 | 72 | public void add(final BufferedImage img, String title) { 73 | images.add(img); 74 | ids.add(title); 75 | } 76 | } 77 | 78 | class ImageViewPanel extends JPanel { 79 | private BufferedImage image = null; 80 | private static final long serialVersionUID = 5489808946126559253L; 81 | private JScrollPane scrollPane; 82 | private double zoomFactor; 83 | private JPanel imgPanel; 84 | 85 | public ImageViewPanel() { 86 | super(new BorderLayout()); 87 | imgPanel = new JPanel() { 88 | private static final long serialVersionUID = -7607117377739877804L; 89 | 90 | public void paintComponent(Graphics g) { 91 | super.paintComponent(g); 92 | Graphics2D g2 = (Graphics2D) g; 93 | g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR); 94 | g2.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); 95 | g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); 96 | if (image != null) { 97 | int w = (int) (image.getWidth() * zoomFactor); 98 | int h = (int) (image.getHeight() * zoomFactor); 99 | g2.drawImage(image, (getWidth() - w) / 2, (getHeight() - h) / 2, w, h, null); 100 | } 101 | } 102 | }; 103 | scrollPane = new JScrollPane(imgPanel, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED); 104 | add(BorderLayout.CENTER, scrollPane); 105 | 106 | imgPanel.addMouseListener(new MouseAdapter() { 107 | public void mousePressed(MouseEvent evt) { 108 | if (image == null) return; 109 | if (evt.getButton() == MouseEvent.BUTTON1) changeZoom(1.25, evt.getPoint()); 110 | else if (evt.getButton() == MouseEvent.BUTTON3) changeZoom(1 / 1.25, evt.getPoint()); 111 | } 112 | }); 113 | } 114 | 115 | public boolean setImage(BufferedImage img) { 116 | image = img; 117 | zoomFactor = 1; 118 | if (image != null) { 119 | updateZoomFactor(); 120 | scrollPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED); 121 | scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED); 122 | } else { 123 | Insets insets = scrollPane.getInsets(); 124 | int sh = ImageViewPanel.this.getHeight() - insets.top - insets.bottom; 125 | int sw = ImageViewPanel.this.getWidth() - insets.left - insets.right; 126 | imgPanel.setPreferredSize(new Dimension(sw, sh)); 127 | scrollPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER); 128 | scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_NEVER); 129 | imgPanel.setCursor(Cursor.getDefaultCursor()); 130 | } 131 | imgPanel.revalidate(); 132 | return image != null; 133 | } 134 | 135 | public void changeZoom(double factor, Point reference) { 136 | Point pt = (reference == null) ? new Point((int) imgPanel.getVisibleRect().getCenterX(), (int) imgPanel.getVisibleRect().getCenterY()) : reference; 137 | 138 | int sbx = scrollPane.getHorizontalScrollBar().getValue(); 139 | int sby = scrollPane.getVerticalScrollBar().getValue(); 140 | 141 | int iw = (int) (image.getWidth() * zoomFactor); 142 | int ih = (int) (image.getHeight() * zoomFactor); 143 | pt.x -= (imgPanel.getWidth() - iw) / 2; 144 | pt.y -= (imgPanel.getHeight() - ih) / 2; 145 | 146 | if (pt.x < 0) pt.x = 0; 147 | if (pt.y < 0) pt.y = 0; 148 | if (pt.x > image.getWidth() * zoomFactor) pt.x = (int) (image.getWidth() * zoomFactor); 149 | if (pt.y > image.getHeight() * zoomFactor) pt.y = (int) (image.getHeight() * zoomFactor); 150 | 151 | double currFactor = zoomFactor; 152 | zoomFactor *= factor; 153 | if (zoomFactor > 10) zoomFactor = 10; 154 | if (zoomFactor < 0.1) zoomFactor = 0.1; 155 | double f = zoomFactor / currFactor; 156 | int w = image == null ? 0 : (int) (image.getWidth() * zoomFactor); 157 | int h = image == null ? 0 : (int) (image.getHeight() * zoomFactor); 158 | Dimension d = new Dimension(w, h); 159 | imgPanel.setPreferredSize(d); 160 | imgPanel.revalidate(); 161 | 162 | for (int i = 0; i < 100; i++) { 163 | if (scrollPane.isValid()) break; 164 | scrollPane.validate(); 165 | try { 166 | Thread.sleep(50); 167 | } catch (InterruptedException e) { 168 | } 169 | } 170 | sbx += (int) (pt.x * (f - 1)); 171 | sby += (int) (pt.y * (f - 1)); 172 | imgPanel.scrollRectToVisible(new Rectangle(sbx, sby, imgPanel.getVisibleRect().width, imgPanel.getVisibleRect().height)); 173 | imgPanel.repaint(); 174 | 175 | scrollPane.getVerticalScrollBar().setUnitIncrement(d.height / 50 + 1); 176 | scrollPane.getHorizontalScrollBar().setUnitIncrement(d.width / 50 + 1); 177 | } 178 | 179 | private void updateZoomFactor() { 180 | Container parent = ImageViewPanel.this; 181 | Insets insets = parent.getInsets(); 182 | int sw = parent.getWidth() - insets.left - insets.right - 4; 183 | int sh = parent.getHeight() - insets.top - insets.bottom - 4; 184 | 185 | zoomFactor = Math.min(((double) sw) / image.getWidth(), ((double) sh) / image.getHeight()); 186 | //if (zoomFactor > 1) zoomFactor = 1; 187 | 188 | Dimension d = new Dimension((int) (image.getWidth() * zoomFactor), (int) (image.getHeight() * zoomFactor)); 189 | imgPanel.setPreferredSize(d); 190 | scrollPane.getVerticalScrollBar().setUnitIncrement(d.height / 50); 191 | scrollPane.getHorizontalScrollBar().setUnitIncrement(d.width / 50); 192 | } 193 | } 194 | -------------------------------------------------------------------------------- /2-wleite/src/MatchPolygon.java: -------------------------------------------------------------------------------- 1 | import java.awt.Polygon; 2 | 3 | public class MatchPolygon implements Comparable { 4 | double val; 5 | Polygon polygon; 6 | 7 | public MatchPolygon(double val, Polygon poly) { 8 | this.val = val; 9 | this.polygon = poly; 10 | } 11 | 12 | public int compareTo(MatchPolygon o) { 13 | return Double.compare(o.val, val); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /2-wleite/src/MultiChannelImage.java: -------------------------------------------------------------------------------- 1 | import java.awt.Color; 2 | import java.awt.image.BufferedImage; 3 | import java.util.Arrays; 4 | 5 | public class MultiChannelImage { 6 | int[] h, s, l, r, g, b, gray, edge; 7 | int[][] extraBands, extraEdges; 8 | int width, height, w8, h8; 9 | 10 | public MultiChannelImage(BufferedImage imgPan, BufferedImage img3band, BufferedImage img8band) { 11 | width = img3band.getWidth(); 12 | height = img3band.getHeight(); 13 | int length = width * height; 14 | r = new int[length]; 15 | g = new int[length]; 16 | b = new int[length]; 17 | h = new int[length]; 18 | s = new int[length]; 19 | l = new int[length]; 20 | float[] hsl = new float[3]; 21 | int[] rgbPixels = new int[width * height * 3]; 22 | img3band.getRaster().getPixels(0, 0, width, height, rgbPixels); 23 | 24 | int rmax = 1780; 25 | int gmax = 1630; 26 | int bmax = 1080; 27 | int rmin = 0; 28 | int gmin = 100; 29 | int bmin = 150; 30 | int ptr = 0; 31 | for (int i = 0; i < length; i++) { 32 | r[i] = rgbPixels[ptr++]; 33 | g[i] = rgbPixels[ptr++]; 34 | b[i] = rgbPixels[ptr++]; 35 | } 36 | for (int i = 0; i < length; i++) { 37 | r[i] = range((r[i] - rmin) * 255 / (rmax - rmin)); 38 | g[i] = range((g[i] - gmin) * 255 / (gmax - gmin)); 39 | b[i] = range((b[i] - bmin) * 255 / (bmax - bmin)); 40 | } 41 | gray = new int[length]; 42 | imgPan.getRaster().getPixels(0, 0, width, height, gray); 43 | 44 | /////////////////////////BLUR///////////// 45 | for (int channel = 0; channel <= 3; channel++) { 46 | int[] a = channel == 0 ? r : channel == 1 ? g : channel == 2 ? b : gray; 47 | int[] c = a.clone(); 48 | for (int y = 1; y < height - 1; y++) { 49 | int yw = y * width; 50 | int p1 = c[yw - width]; 51 | int p2 = c[yw]; 52 | int p3 = c[yw + width]; 53 | int p4 = c[yw - width + 1]; 54 | int p5 = c[yw + 1]; 55 | int p6 = c[yw + width + 1]; 56 | for (int x = 1; x < width - 1; x++) { 57 | int p7 = c[yw - width + x + 1]; 58 | int p8 = c[yw + x + 1]; 59 | int p9 = c[yw + width + x + 1]; 60 | a[yw + x] = (p1 + p3 + p7 + p9 + ((p2 + p4 + p6 + p8) << 1) + (p5 << 2)) >>> 4; 61 | p1 = p4; 62 | p2 = p5; 63 | p3 = p6; 64 | p4 = p7; 65 | p5 = p8; 66 | p6 = p9; 67 | } 68 | } 69 | } 70 | 71 | for (int i = 0; i < length; i++) { 72 | int rr = r[i]; 73 | int gg = g[i]; 74 | int bb = b[i]; 75 | Color.RGBtoHSB(rr, gg, bb, hsl); 76 | h[i] = (((int) Math.round(hsl[0] * 999)) + 500) % 1000; 77 | s[i] = (int) Math.round(hsl[1] * 999); 78 | l[i] = (int) Math.round(hsl[2] * 999); 79 | } 80 | edge = new int[length]; 81 | h8 = img8band.getHeight(); 82 | w8 = img8band.getWidth(); 83 | extraBands = new int[8][w8 * h8]; 84 | extraEdges = new int[8][w8 * h8]; 85 | int[] extraPixels = new int[w8 * h8 * 8]; 86 | img8band.getRaster().getPixels(0, 0, w8, h8, extraPixels); 87 | ptr = 0; 88 | int pos = 0; 89 | for (int i = 0; i < h8; i++) { 90 | for (int j = 0; j < w8; j++) { 91 | for (int k = 0; k < 8; k++) { 92 | extraBands[k][pos] = extraPixels[ptr++]; 93 | } 94 | pos++; 95 | } 96 | } 97 | for (int i = 0; i < 8; i++) { 98 | Arrays.fill(extraEdges[i], -1); 99 | int[] p = extraBands[i]; 100 | int[] e = extraEdges[i]; 101 | for (int y = 1; y < h8 - 1; y++) { 102 | int off = y * w8 + 1; 103 | for (int x = 1; x < w8 - 1; x++, off++) { 104 | int p1 = p[off - 1 - w8]; 105 | int p2 = p[off - 1]; 106 | int p3 = p[off - 1 + w8]; 107 | int p4 = p[off - w8]; 108 | int p5 = p[off + w8]; 109 | int p6 = p[off + 1 - w8]; 110 | int p7 = p[off + 1]; 111 | int p8 = p[off + 1 + w8]; 112 | if (p1 < 0 || p3 < 0 || p6 < 0 || p8 < 0) continue; 113 | int vert = Math.abs(p1 + 2 * p4 + p6 - p3 - 2 * p5 - p8); 114 | int horiz = Math.abs(p1 + 2 * p2 + p3 - p6 - 2 * p7 - p8); 115 | e[off] = (int) Math.sqrt((vert * vert + horiz * horiz) / 2); 116 | } 117 | } 118 | } 119 | Arrays.fill(edge, -1); 120 | for (int y = 1; y < height - 1; y++) { 121 | int off = y * width + 1; 122 | for (int x = 1; x < width - 1; x++, off++) { 123 | /* 124 | * p1 p4 p6 125 | * p2 p7 126 | * p3 p5 p8 127 | */ 128 | int p1 = gray[off - 1 - width]; 129 | int p2 = gray[off - 1]; 130 | int p3 = gray[off - 1 + width]; 131 | int p4 = gray[off - width]; 132 | int p5 = gray[off + width]; 133 | int p6 = gray[off + 1 - width]; 134 | int p7 = gray[off + 1]; 135 | int p8 = gray[off + 1 + width]; 136 | if (p1 < 0 || p3 < 0 || p6 < 0 || p8 < 0) continue; 137 | int vert = Math.abs(p1 + 2 * p4 + p6 - p3 - 2 * p5 - p8); 138 | int horiz = Math.abs(p1 + 2 * p2 + p3 - p6 - 2 * p7 - p8); 139 | edge[off] = (int) Math.sqrt((vert * vert + horiz * horiz) / 2); 140 | } 141 | } 142 | } 143 | 144 | public int getRGB(int x, int y) { 145 | int p = y * width + x; 146 | return (r[p] << 16) | (g[p] << 8) | b[p]; 147 | } 148 | 149 | private static final int range(int v) { 150 | return v < 0 ? 0 : v > 255 ? 255 : v; 151 | } 152 | } -------------------------------------------------------------------------------- /2-wleite/src/PolygonFeatureExtractor.java: -------------------------------------------------------------------------------- 1 | import java.awt.Polygon; 2 | import java.awt.Rectangle; 3 | import java.awt.geom.AffineTransform; 4 | import java.awt.geom.Point2D; 5 | import java.util.ArrayList; 6 | import java.util.Arrays; 7 | import java.util.List; 8 | 9 | public class PolygonFeatureExtractor { 10 | public static final double[] buildingsCuts = new double[] {0.06,0.09,0.12,0.15,0.18,0.21,0.24,0.27,0.30,0.33,0.36,0.39,0.42,0.45,0.48,0.51,0.54,0.57,0.60,0.63,0.66,0.69,0.72,0.75,0.78}; 11 | public static final double[] borderWeights = new double[] {1, 0.25, -0.5}; 12 | public static final int[] borderShifts = new int[] {0, -2, -4}; 13 | public static final int buildingsPolyBorder = 5; 14 | public static final int buildingsPolyBorder2 = 4; 15 | public static final int numFeatures = 209; 16 | private final int[][] dist; 17 | private final List> values8band = new ArrayList>(), values3band = new ArrayList>(); 18 | 19 | public PolygonFeatureExtractor(int w, int h){ 20 | dist = new int[h][w]; 21 | for (int i = 0; i < 7 * 6; i++) { 22 | values3band.add(new ArrayList()); 23 | } 24 | for (int i = 0; i < 16 * 6; i++) { 25 | values8band.add(new ArrayList()); 26 | } 27 | } 28 | 29 | public float[] getFeatures(MultiChannelImage mci, Polygon polygon, double[][] buildingValues, double[][] borderValues) { 30 | float[] ret = new float[numFeatures]; 31 | int k = 0; 32 | double area = Util.getArea(polygon); 33 | double[] r = rectLen(polygon); 34 | ret[k++] = (float) area; 35 | ret[k++] = (float) r[0]; 36 | ret[k++] = (float) r[1]; 37 | ret[k++] = (float) (r[1] == 0 ? 0 : r[0] / r[1]); 38 | ret[k++] = (float) (area == 0 ? 0 : r[0] * r[1] / area); 39 | 40 | Rectangle rc = polygon.getBounds(); 41 | int cnt = 0; 42 | double buildingSum = 0; 43 | double borderSum = 0; 44 | final int w = borderValues[0].length; 45 | final int h = borderValues.length; 46 | boolean[][] inside = new boolean[h][w]; 47 | for (int y = rc.y; y <= rc.y + rc.height; y++) { 48 | int x0 = rc.x; 49 | for (int x = rc.x; x <= rc.x + rc.width; x++) { 50 | if (polygon.contains(x, y)) { 51 | x0 = x; 52 | break; 53 | } 54 | } 55 | int x1 = rc.x + rc.width; 56 | for (int x = x1; x >= x0; x--) { 57 | if (polygon.contains(x, y)) { 58 | x1 = x; 59 | break; 60 | } 61 | } 62 | for (int x = x0; x <= x1; x++) { 63 | cnt++; 64 | if (x >= 0 && y >= 0 && x < w && y < h) { 65 | inside[y][x] = true; 66 | buildingSum += buildingValues[y][x]; 67 | borderSum += borderValues[y][x]; 68 | } 69 | } 70 | } 71 | ret[k++] = (float) borderSum; 72 | ret[k++] = (float) buildingSum; 73 | ret[k++] = cnt == 0 ? -1 : (float) (borderSum / cnt); 74 | ret[k++] = cnt == 0 ? -1 : (float) (buildingSum / cnt); 75 | ret[k++] = buildingSum == 0 ? -1 : (float) (borderSum / buildingSum); 76 | ret[k++] = (float) (buildingSum - borderSum); 77 | 78 | final int maxDist = 8; 79 | final int halfDist = maxDist / 2; 80 | for (int y = 0; y < h; y++) { 81 | Arrays.fill(dist[y], 1000); 82 | } 83 | int y0 = Math.max(0, rc.y - maxDist); 84 | int y1 = Math.min(h - 1, rc.y + rc.height + maxDist); 85 | int x0 = Math.max(0, rc.x - maxDist); 86 | int x1 = Math.min(w - 1, rc.x + rc.width + maxDist); 87 | int[] queue = new int[(y1 - y0 + 1) * (x1 - x0 + 1) * 4]; 88 | int tot = 0; 89 | for (int y = y0; y <= y1; y++) { 90 | boolean[] iy = inside[y]; 91 | for (int x = x0; x <= x1; x++) { 92 | boolean v = iy[x]; 93 | boolean b = false; 94 | if (x > 0 && v != iy[x - 1]) b = true; 95 | else if (x < w - 1 && v != iy[x + 1]) b = true; 96 | else if (y > 0 && v != inside[y - 1][x]) b = true; 97 | else if (y < h - 1 && v != inside[y + 1][x]) b = true; 98 | if (b) { 99 | queue[tot++] = y * w + x; 100 | dist[y][x] = 0; 101 | } 102 | } 103 | } 104 | int curr = 0; 105 | while (curr < tot) { 106 | int p = queue[curr++]; 107 | int x = p % w; 108 | int y = p / w; 109 | int nd = dist[y][x] + 1; 110 | if (nd > maxDist) continue; 111 | for (int i = 0; i < 4; i++) { 112 | int nx = i == 0 ? x + 1 : i == 1 ? x - 1 : x; 113 | if (nx < 0 || nx >= w) continue; 114 | int ny = i == 2 ? y + 1 : i == 3 ? y - 1 : y; 115 | if (ny < 0 || ny >= h) continue; 116 | if (dist[ny][nx] > nd) { 117 | dist[ny][nx] = nd; 118 | queue[tot++] = ny * w + nx; 119 | } 120 | } 121 | } 122 | for (List l : values3band) { 123 | l.clear(); 124 | } 125 | for (List l : values8band) { 126 | l.clear(); 127 | } 128 | for (int y = y0; y <= y1; y++) { 129 | boolean[] iy = inside[y]; 130 | int[] dy = dist[y]; 131 | double[] buy = buildingValues[y]; 132 | double[] boy = borderValues[y]; 133 | for (int x = x0; x <= x1; x++) { 134 | boolean v = iy[x]; 135 | int d = dy[x]; 136 | int idx = -1; 137 | if (d == 0) idx = 0; 138 | else if (d <= halfDist && v) idx = 1; 139 | else if (d <= halfDist && !v) idx = 2; 140 | else if (d <= maxDist && v) idx = 3; 141 | else if (d <= maxDist && !v) idx = 4; 142 | else if (d > maxDist && v) idx = 5; 143 | if (idx >= 0) { 144 | int p = y * w + x; 145 | double a = buy[x]; 146 | double b = boy[x]; 147 | int pos = idx * 7; 148 | values3band.get(pos++).add(a); 149 | values3band.get(pos++).add(b); 150 | values3band.get(pos++).add(a - b); 151 | values3band.get(pos++).add((double) mci.edge[p]); 152 | values3band.get(pos++).add((double) mci.h[p]); 153 | values3band.get(pos++).add((double) mci.s[p]); 154 | values3band.get(pos++).add((double) mci.gray[p]); 155 | int x8 = x / 4; 156 | int y8 = y / 4; 157 | pos = idx * 16; 158 | if (x8 < mci.w8 && y8 < mci.h8) { 159 | p = y8 * mci.w8 + x8; 160 | for (int i = 0; i < 8; i++) { 161 | values8band.get(pos++).add((double) mci.extraBands[i][p]); 162 | values8band.get(pos++).add((double) mci.extraEdges[i][p]); 163 | } 164 | } 165 | } 166 | } 167 | } 168 | int idx = 0; 169 | for (List a : values3band) { 170 | int n = idx % 7 < 3 ? 3 : 2; 171 | System.arraycopy(stats(a), 0, ret, k, n); 172 | k += n; 173 | idx++; 174 | } 175 | for (List a : values8band) { 176 | ret[k++] = avg(a); 177 | } 178 | return ret; 179 | } 180 | 181 | private static float[] stats(List l) { 182 | double sum = 0; 183 | double sumSquares = 0; 184 | int cnt = 0; 185 | for (double p : l) { 186 | sum += p; 187 | double pp = p * p; 188 | sumSquares += pp; 189 | cnt++; 190 | } 191 | float[] ret = new float[3]; 192 | if (cnt > 0) { 193 | ret[0] = (float) (sum / cnt); 194 | ret[1] = (float) ((sumSquares - sum * sum / cnt) / cnt); 195 | ret[2] = (float) sum; 196 | } 197 | return ret; 198 | } 199 | 200 | private static float avg(List l) { 201 | double sum = 0; 202 | for (double p : l) { 203 | sum += p; 204 | } 205 | return l.size() == 0 ? -1 : (float) (sum / l.size()); 206 | } 207 | 208 | private static double[] rectLen(Polygon polygon) { 209 | Rectangle rc = polygon.getBounds(); 210 | double xc = rc.getCenterX(); 211 | double yc = rc.getCenterY(); 212 | Point2D[] org = new Point2D[polygon.npoints]; 213 | Point2D[] dst = new Point2D[polygon.npoints]; 214 | for (int i = 0; i < polygon.npoints; i++) { 215 | org[i] = new Point2D.Double(polygon.xpoints[i], polygon.ypoints[i]); 216 | } 217 | double minArea = 1e99; 218 | double[] ret = new double[2]; 219 | for (int a = 0; a < 90; a += 3) { 220 | AffineTransform.getRotateInstance(Math.toRadians(a), xc, yc).transform(org, 0, dst, 0, org.length); 221 | Point2D r = dst[0]; 222 | double xmin = r.getX(); 223 | double ymin = r.getY(); 224 | double xmax = xmin; 225 | double ymax = ymin; 226 | for (int i = 1; i < polygon.npoints; i++) { 227 | r = dst[i]; 228 | xmin = Math.min(xmin, r.getX()); 229 | xmax = Math.max(xmax, r.getX()); 230 | ymin = Math.min(ymin, r.getY()); 231 | ymax = Math.max(ymax, r.getY()); 232 | } 233 | double dx = xmax - xmin; 234 | double dy = ymax - ymin; 235 | double currArea = dx * dy; 236 | if (currArea < minArea) { 237 | minArea = currArea; 238 | ret[0] = Math.min(dx, dy); 239 | ret[1] = Math.max(dx, dy); 240 | } 241 | } 242 | return ret; 243 | } 244 | } -------------------------------------------------------------------------------- /2-wleite/src/PolygonMatcherTrainer.java: -------------------------------------------------------------------------------- 1 | import java.awt.Polygon; 2 | import java.awt.Rectangle; 3 | import java.awt.geom.Area; 4 | import java.awt.image.BufferedImage; 5 | import java.io.File; 6 | import java.util.ArrayList; 7 | import java.util.List; 8 | import java.util.Map; 9 | 10 | import javax.imageio.ImageIO; 11 | 12 | public class PolygonMatcherTrainer { 13 | private Map> buildingsPerImage; 14 | private static final int numThreads = SpacenetMain.numThreads; 15 | private static final int maxTrees = 120; 16 | private static final int minRowsPerNode = 8; 17 | private static final int maxSamples = 16_000_000; 18 | private int totSamples = 0; 19 | private float[][] polyMatchFeatures = new float[PolygonFeatureExtractor.numFeatures][maxSamples]; 20 | private boolean[] polyMatchClassif = new boolean[maxSamples]; 21 | private RandomForestPredictor buildingPredictor, borderPredictor; 22 | 23 | public void run(String trainingFolder, String dataSet) { 24 | File trainingCsv = new File(trainingFolder, "summaryData/AOI_" + dataSet + "_Train_Building_Solutions.csv"); 25 | File folderPan = new File(trainingFolder, "PAN"); 26 | File folder3band = new File(trainingFolder, "RGB-PanSharpen"); 27 | File folder8band = new File(trainingFolder, "MUL"); 28 | File rfModel = new File("model", dataSet); 29 | File rfBuilding = new File(rfModel, "rfBuilding.dat"); 30 | File rfBorder = new File(rfModel, "rfBorder.dat"); 31 | File rfPolyMatch = new File(rfModel, "rfPolyMatch.dat"); 32 | train(trainingCsv, folderPan, folder3band, folder8band, rfBuilding, rfBorder, rfPolyMatch); 33 | } 34 | 35 | public void train(File trainingCsv, File folderPan, File folder3band, File folder8band, File rfBuilding, File rfBorder, File rfPolyMatch) { 36 | buildingsPerImage = Util.readBuildingsCsv(trainingCsv); 37 | Util.splitImages(buildingsPerImage, new int[] { 60, 40, 0 }, 1); 38 | loadPredictors(rfBuilding, rfBorder); 39 | processImages(folderPan, folder3band, folder8band); 40 | buildRandomForest(rfPolyMatch); 41 | } 42 | 43 | private void buildRandomForest(File rfPolyMatch) { 44 | try { 45 | System.err.println("Building Random Forest"); 46 | long t = System.currentTimeMillis(); 47 | RandomForestBuilder.train(polyMatchFeatures, polyMatchClassif, totSamples, maxTrees, rfPolyMatch, numThreads, minRowsPerNode); 48 | System.err.println("\t RF Poly Match: " + rfPolyMatch.length() + " bytes"); 49 | System.err.println("\t Elapsed Time: " + (System.currentTimeMillis() - t) + " ms"); 50 | System.err.println(); 51 | } catch (Exception e) { 52 | e.printStackTrace(); 53 | } 54 | } 55 | 56 | private void processImages(final File folderPan, final File folder3band, final File folder8band) { 57 | try { 58 | System.err.println("Processing Images"); 59 | long t = System.currentTimeMillis(); 60 | final List images = new ArrayList(buildingsPerImage.keySet()); 61 | final int tot = images.size(); 62 | Thread[] threads = new Thread[numThreads]; 63 | for (int i = 0; i < numThreads; i++) { 64 | threads[i] = new Thread() { 65 | public void run() { 66 | while (true) { 67 | String imageId = null; 68 | int size = 0; 69 | synchronized (images) { 70 | if (images.isEmpty()) break; 71 | imageId = images.remove(0); 72 | size = images.size(); 73 | } 74 | File imageFilePan = new File(folderPan, folderPan.getName() + "_" + imageId + ".tif"); 75 | File imageFile3band = new File(folder3band, folder3band.getName() + "_" + imageId + ".tif"); 76 | File imageFile8band = new File(folder8band, folder8band.getName() + "_" + imageId + ".tif"); 77 | List buildings = buildingsPerImage.get(imageId); 78 | System.err.println("\t" + imageId + " : " + size + "/" + tot); 79 | processImage(imageFilePan, imageFile3band, imageFile8band, buildings); 80 | } 81 | } 82 | }; 83 | threads[i].start(); 84 | } 85 | for (int i = 0; i < numThreads; i++) { 86 | threads[i].join(); 87 | threads[i] = null; 88 | } 89 | System.err.println("\tPoly Match Samples: " + totSamples); 90 | System.err.println("\t Elapsed Time: " + (System.currentTimeMillis() - t) + " ms"); 91 | System.err.println(); 92 | } catch (Exception e) { 93 | e.printStackTrace(); 94 | } 95 | } 96 | 97 | private void processImage(File imageFilePan, File imageFile3band, File imageFile8band, List groundTruthBuildings) { 98 | try { 99 | BufferedImage imgPan = ImageIO.read(imageFilePan); 100 | BufferedImage img3band = ImageIO.read(imageFile3band); 101 | BufferedImage img8band = ImageIO.read(imageFile8band); 102 | MultiChannelImage mci = new MultiChannelImage(imgPan, img3band, img8band); 103 | double[][][] vals = Util.evalImage(mci, buildingPredictor, borderPredictor); 104 | double[][] buildingValues = vals[0]; 105 | double[][] borderValues = vals[1]; 106 | 107 | List candidates = new ArrayList(); 108 | for (int i = 0; i < PolygonFeatureExtractor.borderShifts.length; i++) { 109 | int borderShift = PolygonFeatureExtractor.borderShifts[i]; 110 | double borderWeight = PolygonFeatureExtractor.borderWeights[i]; 111 | for (double cut : PolygonFeatureExtractor.buildingsCuts) { 112 | candidates.addAll(Util.findBuildings(buildingValues, borderValues, cut, borderWeight, PolygonFeatureExtractor.buildingsPolyBorder + borderShift, 113 | PolygonFeatureExtractor.buildingsPolyBorder2 + borderShift)); 114 | } 115 | } 116 | 117 | PolygonFeatureExtractor pfe = new PolygonFeatureExtractor(buildingValues[0].length, buildingValues.length); 118 | for (Polygon polygon : candidates) { 119 | float value = (float) iou(polygon, groundTruthBuildings); 120 | float[] features = pfe.getFeatures(mci, polygon, buildingValues, borderValues); 121 | synchronized (polyMatchClassif) { 122 | if (totSamples < maxSamples) { 123 | polyMatchClassif[totSamples] = value > 0.5; 124 | for (int i = 0; i < features.length; i++) { 125 | polyMatchFeatures[i][totSamples] = features[i]; 126 | } 127 | totSamples++; 128 | } 129 | } 130 | } 131 | } catch (Exception e) { 132 | e.printStackTrace(); 133 | } 134 | } 135 | 136 | private double iou(Polygon poly, List buildings) { 137 | double best = 0; 138 | Area polyArea = new Area(poly); 139 | double polyAreaVal = Util.areaVal(polyArea); 140 | Rectangle polyRect = polyArea.getBounds(); 141 | for (Building building : buildings) { 142 | Area buildingArea = building.getArea(); 143 | Rectangle buildingRect = buildingArea.getBounds(); 144 | if (!buildingRect.intersects(polyRect)) continue; 145 | Area interArea = new Area(polyArea); 146 | interArea.intersect(buildingArea); 147 | double a = Util.areaVal(interArea); 148 | double curr = a / (polyAreaVal + building.getAreaVal() - a); 149 | if (curr > best) best = curr; 150 | } 151 | return best; 152 | } 153 | 154 | private void loadPredictors(File rfBuilding, File rfBorder) { 155 | try { 156 | System.err.println("Loading Predictors"); 157 | long t = System.currentTimeMillis(); 158 | buildingPredictor = RandomForestPredictor.load(rfBuilding); 159 | borderPredictor = RandomForestPredictor.load(rfBorder); 160 | System.err.println("\t Elapsed Time: " + (System.currentTimeMillis() - t) + " ms"); 161 | System.err.println(); 162 | } catch (Exception e) { 163 | e.printStackTrace(); 164 | } 165 | } 166 | } -------------------------------------------------------------------------------- /2-wleite/src/RandomForestBuilder.java: -------------------------------------------------------------------------------- 1 | 2 | import java.io.File; 3 | 4 | public class RandomForestBuilder { 5 | public static RandomForestPredictor train(final float[][] features, final boolean[] classif, final int totRows, final int maxTrees, final File out, final int numThreads, final int minRowsPerNode) { 6 | final int maxNodes = totRows / minRowsPerNode * 2; 7 | final RandomForestPredictor rf = new RandomForestPredictor(maxTrees, maxNodes, true); 8 | Thread[] threads = new Thread[numThreads]; 9 | for (int i = 0; i < numThreads; i++) { 10 | final int idx = i; 11 | threads[i] = new Thread() { 12 | public void run() { 13 | for (int i = idx; i < maxTrees; i += numThreads) { 14 | ClassificationNode root = new ClassificationTree(features, classif, totRows, i, minRowsPerNode, maxNodes).getRoot(); 15 | synchronized (rf) { 16 | rf.add(root); 17 | } 18 | } 19 | } 20 | }; 21 | threads[i].start(); 22 | } 23 | try { 24 | for (int i = 0; i < numThreads; i++) { 25 | threads[i].join(); 26 | threads[i] = null; 27 | } 28 | } catch (InterruptedException e) { 29 | } 30 | try { 31 | rf.save(out); 32 | } catch (Exception e) { 33 | e.printStackTrace(); 34 | } 35 | return rf; 36 | } 37 | } -------------------------------------------------------------------------------- /2-wleite/src/RandomForestPredictor.java: -------------------------------------------------------------------------------- 1 | 2 | import java.io.File; 3 | import java.io.FileInputStream; 4 | import java.io.FileOutputStream; 5 | import java.io.InputStream; 6 | import java.io.OutputStream; 7 | import java.util.zip.GZIPInputStream; 8 | import java.util.zip.GZIPOutputStream; 9 | 10 | public class RandomForestPredictor { 11 | private final int[] roots; 12 | private final int[] nodeLeft; 13 | private final short[] splitFeature; 14 | private final float[] value; 15 | private int trees, free; 16 | 17 | public RandomForestPredictor(int trees, int nodes, boolean max) { 18 | if (max) { 19 | roots = new int[trees]; 20 | int totalNodes = trees * nodes; 21 | nodeLeft = new int[totalNodes]; 22 | splitFeature = new short[totalNodes]; 23 | value = new float[totalNodes]; 24 | } else { 25 | this.trees = trees; 26 | this.free = nodes; 27 | roots = new int[trees]; 28 | nodeLeft = new int[nodes]; 29 | splitFeature = new short[nodes]; 30 | value = new float[nodes]; 31 | } 32 | } 33 | 34 | public synchronized void add(ClassificationNode root) { 35 | int rt = roots[trees++] = free; 36 | free++; 37 | expand(root, rt); 38 | } 39 | 40 | public int size() { 41 | return trees; 42 | } 43 | 44 | private void expand(ClassificationNode node, int pos) { 45 | if (node.left == null) { 46 | nodeLeft[pos] = -1; 47 | splitFeature[pos] = -1; 48 | value[pos] = node.getValue(); 49 | } else { 50 | int l = nodeLeft[pos] = free; 51 | free += 2; 52 | splitFeature[pos] = (short) node.splitFeature; 53 | value[pos] = node.splitVal; 54 | expand(node.left, l); 55 | expand(node.right, l + 1); 56 | } 57 | } 58 | 59 | public double predict(float[] features) { 60 | double ret = 0; 61 | for (int root : roots) { 62 | ret += classify(root, features); 63 | } 64 | return ret / roots.length; 65 | } 66 | 67 | private double classify(int pos, float[] features) { 68 | while (true) { 69 | int sf = splitFeature[pos]; 70 | if (sf < 0) return value[pos]; 71 | if (features[sf] < value[pos]) pos = nodeLeft[pos]; 72 | else pos = nodeLeft[pos] + 1; 73 | } 74 | } 75 | 76 | public void save(File file) throws Exception { 77 | OutputStream out = new GZIPOutputStream(new FileOutputStream(file), 1 << 20); 78 | byte[] bytes = new byte[trees * 4 + 8]; 79 | int cnt = 0; 80 | bytes[cnt++] = (byte) ((trees >>> 24) & 0xFF); 81 | bytes[cnt++] = (byte) ((trees >>> 16) & 0xFF); 82 | bytes[cnt++] = (byte) ((trees >>> 8) & 0xFF); 83 | bytes[cnt++] = (byte) ((trees >>> 0) & 0xFF); 84 | for (int i = 0; i < trees; i++) { 85 | int ri = roots[i]; 86 | bytes[cnt++] = (byte) ((ri >>> 24) & 0xFF); 87 | bytes[cnt++] = (byte) ((ri >>> 16) & 0xFF); 88 | bytes[cnt++] = (byte) ((ri >>> 8) & 0xFF); 89 | bytes[cnt++] = (byte) ((ri >>> 0) & 0xFF); 90 | } 91 | bytes[cnt++] = (byte) ((free >>> 24) & 0xFF); 92 | bytes[cnt++] = (byte) ((free >>> 16) & 0xFF); 93 | bytes[cnt++] = (byte) ((free >>> 8) & 0xFF); 94 | bytes[cnt++] = (byte) ((free >>> 0) & 0xFF); 95 | out.write(bytes, 0, cnt); 96 | for (int i = 0; i < free; i++) { 97 | cnt = 0; 98 | int si = splitFeature[i]; 99 | bytes[cnt++] = (byte) ((si >>> 8) & 0xFF); 100 | bytes[cnt++] = (byte) ((si >>> 0) & 0xFF); 101 | int ni = nodeLeft[i]; 102 | bytes[cnt++] = (byte) ((ni >>> 24) & 0xFF); 103 | bytes[cnt++] = (byte) ((ni >>> 16) & 0xFF); 104 | bytes[cnt++] = (byte) ((ni >>> 8) & 0xFF); 105 | bytes[cnt++] = (byte) ((ni >>> 0) & 0xFF); 106 | int fi = Float.floatToIntBits(value[i]); 107 | bytes[cnt++] = (byte) ((fi >>> 24) & 0xFF); 108 | bytes[cnt++] = (byte) ((fi >>> 16) & 0xFF); 109 | bytes[cnt++] = (byte) ((fi >>> 8) & 0xFF); 110 | bytes[cnt++] = (byte) ((fi >>> 0) & 0xFF); 111 | out.write(bytes, 0, cnt); 112 | } 113 | out.close(); 114 | } 115 | 116 | public static RandomForestPredictor load(File file) throws Exception { 117 | InputStream in = new GZIPInputStream(new FileInputStream(file), 1 << 20); 118 | byte[] bytes = new byte[4]; 119 | in.read(bytes); 120 | int trees = (((bytes[0] & 0xFF) << 24) + ((bytes[1] & 0xFF) << 16) + ((bytes[2] & 0xFF) << 8) + ((bytes[3] & 0xFF) << 0)); 121 | int[] t = new int[trees]; 122 | for (int i = 0; i < trees; i++) { 123 | in.read(bytes); 124 | t[i] = (((bytes[0] & 0xFF) << 24) + ((bytes[1] & 0xFF) << 16) + ((bytes[2] & 0xFF) << 8) + ((bytes[3] & 0xFF) << 0)); 125 | } 126 | in.read(bytes); 127 | int nodes = (((bytes[0] & 0xFF) << 24) + ((bytes[1] & 0xFF) << 16) + ((bytes[2] & 0xFF) << 8) + ((bytes[3] & 0xFF) << 0)); 128 | RandomForestPredictor predictor = new RandomForestPredictor(trees, nodes, false); 129 | System.arraycopy(t, 0, predictor.roots, 0, trees); 130 | bytes = new byte[10]; 131 | for (int i = 0; i < nodes; i++) { 132 | int cnt = in.read(bytes); 133 | while (cnt != 10) { 134 | int add = in.read(bytes, cnt, 10 - cnt); 135 | cnt += add; 136 | } 137 | predictor.splitFeature[i] = (short) (((bytes[0] & 0xFF) << 8) + ((bytes[1] & 0xFF) << 0)); 138 | predictor.nodeLeft[i] = (((bytes[2] & 0xFF) << 24) + ((bytes[3] & 0xFF) << 16) + ((bytes[4] & 0xFF) << 8) + ((bytes[5] & 0xFF) << 0)); 139 | predictor.value[i] = Float.intBitsToFloat((((bytes[6] & 0xFF) << 24) + ((bytes[7] & 0xFF) << 16) + ((bytes[8] & 0xFF) << 8) + ((bytes[9] & 0xFF) << 0))); 140 | } 141 | in.close(); 142 | return predictor; 143 | } 144 | } -------------------------------------------------------------------------------- /2-wleite/src/SpacenetMain.java: -------------------------------------------------------------------------------- 1 | import java.io.File; 2 | 3 | public class SpacenetMain { 4 | private static final String[] dataSets = new String[] { "2_Vegas", "3_Paris", "4_Shanghai", "5_Khartoum" }; 5 | private static final int[] minConf = new int[] { 40, 38, 31, 29 }; 6 | public static int numThreads = Runtime.getRuntime().availableProcessors(); 7 | 8 | public static void main(String[] args) { 9 | if (args.length >= 2 && args[0].equalsIgnoreCase("train")) { 10 | for (int i = 1; i < args.length; i++) { 11 | String trainingFolder = args[i]; 12 | if (!new File(trainingFolder).exists()) { 13 | System.err.println("ERROR: Training folder not found: " + trainingFolder); 14 | return; 15 | } 16 | String dataSet = null; 17 | for (String s : dataSets) { 18 | if (trainingFolder.toLowerCase().contains(s.toLowerCase())) { 19 | dataSet = s; 20 | break; 21 | } 22 | } 23 | if (dataSet == null) { 24 | System.err.println("ERROR: Training folder does not match any known data set: " + trainingFolder); 25 | return; 26 | } 27 | System.err.println("TRAINING"); 28 | System.err.println("\t Folder: " + trainingFolder); 29 | System.err.println("\tData Set: " + dataSet); 30 | System.err.println(); 31 | new BuildingDetectorTrainer().run(trainingFolder, dataSet); 32 | new PolygonMatcherTrainer().run(trainingFolder, dataSet); 33 | } 34 | return; 35 | } else if (args.length >= 3 && args[0].equalsIgnoreCase("test")) { 36 | File outputFile = new File(args[args.length - 1]); 37 | if (outputFile.exists()) outputFile.delete(); 38 | for (int i = 1; i < args.length - 1; i++) { 39 | String testingFolder = args[i]; 40 | if (!new File(testingFolder).exists()) { 41 | System.err.println("ERROR: Testing folder not found: " + testingFolder); 42 | return; 43 | } 44 | String dataSet = null; 45 | int conf = 35; 46 | for (int j = 0; j < dataSets.length; j++) { 47 | if (testingFolder.toLowerCase().contains(dataSets[j].toLowerCase())) { 48 | dataSet = dataSets[j]; 49 | conf = minConf[j]; 50 | break; 51 | } 52 | } 53 | if (dataSet == null) { 54 | System.err.println("ERROR: Testing folder does not match any known data set: " + testingFolder); 55 | return; 56 | } 57 | System.err.println("TESTING"); 58 | System.err.println("\t Folder: " + testingFolder); 59 | System.err.println("\tData Set: " + dataSet); 60 | System.err.println(); 61 | new BuildingDetectorTester().run(testingFolder, dataSet, conf, outputFile); 62 | } 63 | return; 64 | } 65 | System.err.println("Usage:"); 66 | System.err.println(" java SpacenetMain train [...]"); 67 | System.err.println(" java SpacenetMain test [...] "); 68 | } 69 | } -------------------------------------------------------------------------------- /2-wleite/src/Util.java: -------------------------------------------------------------------------------- 1 | import java.awt.Point; 2 | import java.awt.Polygon; 3 | import java.awt.geom.Area; 4 | import java.awt.geom.Line2D; 5 | import java.awt.geom.PathIterator; 6 | import java.io.BufferedReader; 7 | import java.io.File; 8 | import java.io.FileReader; 9 | import java.util.ArrayList; 10 | import java.util.Arrays; 11 | import java.util.Collections; 12 | import java.util.Comparator; 13 | import java.util.HashMap; 14 | import java.util.List; 15 | import java.util.Map; 16 | import java.util.SplittableRandom; 17 | 18 | public class Util { 19 | private static List findCandidates(int w, int h, double cut, double[][] m1, int buildingsPolyBorder, int buildingsPolyBorder2) { 20 | int minPts = 4; 21 | List candidates = new ArrayList(); 22 | 23 | double[][] m2 = new double[h][w]; 24 | Integer[] ord = new Integer[w * h]; 25 | int oc = 0; 26 | for (int y = 1; y < h - 1; y++) { 27 | double a = Math.min(Math.min(m1[y - 1][0], m1[y][0]), m1[y + 1][0]); 28 | double b = Math.min(Math.min(m1[y - 1][1], m1[y][1]), m1[y + 1][1]); 29 | for (int x = 1; x < w - 1; x++) { 30 | double c = Math.min(Math.min(m1[y - 1][x + 1], m1[y][x + 1]), m1[y + 1][x + 1]); 31 | m2[y][x] = Math.min(a, Math.min(b, c)); 32 | if (m2[y][x] < cut) m2[y][x] = 0; 33 | else ord[oc++] = y * w + x; 34 | a = b; 35 | b = c; 36 | } 37 | } 38 | Arrays.sort(ord, 0, oc, new Comparator() { 39 | public int compare(Integer a, Integer b) { 40 | double va = m2[a / w][a % w]; 41 | double vb = m2[b / w][b % w]; 42 | return Double.compare(vb, va); 43 | } 44 | }); 45 | int[][] m3 = new int[h][w]; 46 | int id = 0; 47 | int[] q = new int[w * h]; 48 | for (int k = 0; k < oc; k++) { 49 | int pk = ord[k]; 50 | int x = pk % w; 51 | int y = pk / w; 52 | if (m3[y][x] == 0) { 53 | int tot = 0; 54 | id++; 55 | m3[y][x] = id; 56 | q[tot++] = y * w + x; 57 | int curr = 0; 58 | while (curr < tot) { 59 | int cq = q[curr++]; 60 | int cx = cq % w; 61 | int cy = cq / w; 62 | for (int i = 0; i < 4; i++) { 63 | int nx = i == 0 ? cx + 1 : i == 1 ? cx - 1 : cx; 64 | int ny = i == 2 ? cy + 1 : i == 3 ? cy - 1 : cy; 65 | if (m2[ny][nx] > 0 && m3[ny][nx] == 0) { 66 | m3[ny][nx] = id; 67 | q[tot++] = ny * w + nx; 68 | } 69 | } 70 | } 71 | if (tot > minPts) { 72 | List pts = new ArrayList(); 73 | for (int j = 0; j < tot; j++) { 74 | int cq = q[j]; 75 | int cx = cq % w; 76 | int cy = cq / w; 77 | int eq = 0; 78 | for (int i = 0; i < 4; i++) { 79 | int nx = i == 0 ? cx + 1 : i == 1 ? cx - 1 : cx; 80 | int ny = i == 2 ? cy + 1 : i == 3 ? cy - 1 : cy; 81 | if (m3[ny][nx] == id) eq++; 82 | } 83 | if (eq == 4) continue; 84 | pts.add(new Point(cx, Math.min(h, cy + buildingsPolyBorder + 1))); 85 | pts.add(new Point(cx, Math.max(0, cy - buildingsPolyBorder))); 86 | pts.add(new Point(Math.min(w, cx + buildingsPolyBorder + 1), cy)); 87 | pts.add(new Point(Math.max(0, cx - buildingsPolyBorder), cy)); 88 | pts.add(new Point(Math.min(w, cx + buildingsPolyBorder2 + 1), Math.min(h, cy + buildingsPolyBorder2 + 1))); 89 | pts.add(new Point(Math.max(0, cx - buildingsPolyBorder2), Math.max(0, cy - buildingsPolyBorder2))); 90 | pts.add(new Point(Math.min(w, cx + buildingsPolyBorder + 1), Math.max(0, cy - buildingsPolyBorder2))); 91 | pts.add(new Point(Math.max(0, cx - buildingsPolyBorder2), Math.min(h, cy + buildingsPolyBorder2 + 1))); 92 | } 93 | List hull = Util.convexHull(pts); 94 | if (hull != null) { 95 | int[] xp = new int[hull.size()]; 96 | int[] yp = new int[hull.size()]; 97 | for (int i = 0; i < xp.length; i++) { 98 | Point p = hull.get(i); 99 | xp[i] = p.x; 100 | yp[i] = p.y; 101 | } 102 | Polygon polygon = new Polygon(xp, yp, xp.length); 103 | candidates.add(polygon); 104 | } 105 | } 106 | } 107 | } 108 | return candidates; 109 | } 110 | 111 | public static List findBuildings(double[][] buildingValue, double[][] borderValue, double buildingsCut, double borderWeight, int buildingsPolyBorder, int buildingsPolyBorder2) { 112 | int w = borderValue[0].length; 113 | int h = borderValue.length; 114 | double[][] m1 = new double[h][w]; 115 | for (int y = 1; y < h - 1; y++) { 116 | double a = 0; 117 | double b = 0; 118 | for (int ay = y - 1; ay <= y + 1; ay++) { 119 | a += buildingValue[ay][0] - borderWeight * borderValue[ay][0]; 120 | b += buildingValue[ay][1] - borderWeight * borderValue[ay][1]; 121 | } 122 | for (int x = 1; x < w - 1; x++) { 123 | double c = 0; 124 | for (int ay = y - 1; ay <= y + 1; ay++) { 125 | c += buildingValue[ay][x + 1] - borderWeight * borderValue[ay][x + 1]; //FIXED 126 | } 127 | m1[y][x] = (a + b + c + (buildingValue[y][x] - borderWeight * borderValue[y][x])) / 10; 128 | a = b; 129 | b = c; 130 | } 131 | } 132 | return findCandidates(w, h, buildingsCut, m1, buildingsPolyBorder, buildingsPolyBorder2); 133 | } 134 | 135 | public static void splitImages(Map> buildingsPerImage, int[] a, int idx) { 136 | try { 137 | System.err.println("Spliting Images"); 138 | long t = System.currentTimeMillis(); 139 | List l = new ArrayList(buildingsPerImage.keySet()); 140 | SplittableRandom rnd = new SplittableRandom(14121974); 141 | for (int i = 0; i < l.size() * l.size(); i++) { 142 | int p1 = rnd.nextInt(l.size()); 143 | int p2 = rnd.nextInt(l.size()); 144 | Collections.swap(l, p1, p2); 145 | } 146 | int p1 = 0; 147 | for (int i = 0; i < idx; i++) { 148 | p1 += a[i]; 149 | } 150 | int p2 = p1 + a[idx]; 151 | p1 = p1 * l.size() / 100; 152 | p2 = p2 * l.size() / 100; 153 | if (p2 > l.size()) p2 = l.size(); 154 | buildingsPerImage.keySet().retainAll(l.subList(p1, p2)); 155 | System.err.println("\t Images: " + buildingsPerImage.size()); 156 | System.err.println("\t Elapsed Time: " + (System.currentTimeMillis() - t) + " ms"); 157 | System.err.println(); 158 | } catch (Exception e) { 159 | e.printStackTrace(); 160 | System.exit(-11); 161 | } 162 | } 163 | 164 | public static Map> readBuildingsCsv(File trainingCsv) { 165 | Map> buildingsPerImage = new HashMap>(); 166 | try { 167 | System.err.println("Reading Buildings CSV"); 168 | long t = System.currentTimeMillis(); 169 | int numLines = 0; 170 | int numBuildings = 0; 171 | int numPolys = 0; 172 | BufferedReader in = new BufferedReader(new FileReader(trainingCsv)); 173 | String line = in.readLine(); 174 | String s1 = "POLYGON (("; 175 | String s2 = "))"; 176 | while ((line = in.readLine()) != null) { 177 | numLines++; 178 | List cols = parseCols(line); 179 | String imageId = cols.get(0); 180 | if (imageId.toLowerCase().startsWith("pan_")) imageId = imageId.substring(4); 181 | List buildings = buildingsPerImage.get(imageId); 182 | if (buildings == null) buildingsPerImage.put(imageId, buildings = new ArrayList()); 183 | int buildingId = Integer.parseInt(cols.get(1)); 184 | if (buildingId == -1) continue; 185 | Building building = new Building(buildingId); 186 | buildings.add(building); 187 | 188 | String s = cols.get(2); 189 | int p1 = s.indexOf(s1); 190 | boolean first = true; 191 | while (p1 >= 0) { 192 | int p2 = s.indexOf(s2, p1 + s1.length()); 193 | String[] pts = s.substring(p1 + s1.length(), p2).split("\\),\\("); 194 | for (int i = 0; i < pts.length; i++) { 195 | String[] pt = pts[i].split(","); 196 | int[] x = new int[pt.length]; 197 | int[] y = new int[pt.length]; 198 | for (int j = 0; j < x.length; j++) { 199 | String[] coord = pt[j].split(" "); 200 | x[j] = (int) Math.round(Double.parseDouble(coord[0])); 201 | y[j] = (int) Math.round(Double.parseDouble(coord[1])); 202 | } 203 | Polygon poly = new Polygon(x, y, x.length); 204 | numPolys++; 205 | if (first) building.in.add(poly); 206 | else building.out.add(poly); 207 | } 208 | first = false; 209 | p1 = p2 + s2.length(); 210 | p1 = s.indexOf(s1, p1); 211 | } 212 | numBuildings++; 213 | } 214 | in.close(); 215 | System.err.println("\t Lines Read: " + numLines); 216 | System.err.println("\t Images: " + buildingsPerImage.size()); 217 | System.err.println("\t Buildings: " + numBuildings); 218 | System.err.println("\t Polygons: " + numPolys); 219 | System.err.println("\t Elapsed Time: " + (System.currentTimeMillis() - t) + " ms"); 220 | System.err.println(); 221 | } catch (Exception e) { 222 | e.printStackTrace(); 223 | System.exit(-1); 224 | } 225 | return buildingsPerImage; 226 | } 227 | 228 | public static double[][][] evalImage(MultiChannelImage mci, RandomForestPredictor buildingPredictor, RandomForestPredictor borderPredictor) { 229 | try { 230 | int w = mci.width; 231 | int h = mci.height; 232 | double[][] buildingValues = new double[h][w]; 233 | double[][] borderValues = new double[h][w]; 234 | for (int y = 0; y < h; y++) { 235 | double[] buy = buildingValues[y]; 236 | double[] boy = borderValues[y]; 237 | for (int x = 0; x < w; x++) { 238 | float[] features = BuildingFeatureExtractor.getFeatures(mci, x, y, w, h); 239 | buy[x] = buildingPredictor.predict(features); 240 | boy[x] = borderPredictor.predict(features); 241 | } 242 | } 243 | return new double[][][] { buildingValues, borderValues }; 244 | } catch (Exception e) { 245 | e.printStackTrace(); 246 | System.exit(-14); 247 | } 248 | return null; 249 | } 250 | 251 | private static double triangleArea(int p1x, int p1y, int p2x, int p2y, int p3x, int p3y) { 252 | return Math.abs(p1x * p2y + p1y * p3x + p2x * p3y - p3x * p2y - p1y * p2x - p1x * p3y) / 2; 253 | } 254 | 255 | public static double getArea(Polygon a) { 256 | double ret = 0; 257 | for (int i = 1; i < a.npoints - 1; i++) { 258 | ret += triangleArea(a.xpoints[0], a.ypoints[0], a.xpoints[i], a.ypoints[i], a.xpoints[i + 1], a.ypoints[i + 1]); 259 | } 260 | return ret; 261 | } 262 | 263 | public static List convexHull(List p) { 264 | if (p == null) return null; 265 | if (p.size() < 3) return null; 266 | Point base = p.get(0); 267 | for (Point curr : p) { 268 | if (curr.y < base.y || (curr.y == base.y && curr.x > base.x)) { 269 | base = curr; 270 | } 271 | } 272 | List hull = new ArrayList(); 273 | hull.add(base); 274 | double prevAng = -1; 275 | while (true) { 276 | Point next = null; 277 | double ang = Math.PI * 3; 278 | Point last = hull.get(hull.size() - 1); 279 | for (Point curr : p) { 280 | if (curr.equals(last)) continue; 281 | double ca = angle(last, curr); 282 | if (ca <= ang && ca >= prevAng) { 283 | ang = ca; 284 | next = curr; 285 | } 286 | } 287 | prevAng = ang; 288 | if (next == null || next.equals(base)) break; 289 | hull.add(next); 290 | } 291 | return hull; 292 | } 293 | 294 | public static double areaVal(Area shape) { 295 | PathIterator i = shape.getPathIterator(null); 296 | double a = 0.0; 297 | double[] coords = new double[6]; 298 | double startX = Double.NaN, startY = Double.NaN; 299 | Line2D segment = new Line2D.Double(Double.NaN, Double.NaN, Double.NaN, Double.NaN); 300 | while (!i.isDone()) { 301 | int segType = i.currentSegment(coords); 302 | double x = coords[0], y = coords[1]; 303 | switch (segType) { 304 | case PathIterator.SEG_CLOSE: 305 | segment.setLine(segment.getX2(), segment.getY2(), startX, startY); 306 | a += area(segment); 307 | startX = startY = Double.NaN; 308 | segment.setLine(Double.NaN, Double.NaN, Double.NaN, Double.NaN); 309 | break; 310 | case PathIterator.SEG_LINETO: 311 | segment.setLine(segment.getX2(), segment.getY2(), x, y); 312 | a += area(segment); 313 | break; 314 | case PathIterator.SEG_MOVETO: 315 | startX = x; 316 | startY = y; 317 | segment.setLine(Double.NaN, Double.NaN, x, y); 318 | break; 319 | } 320 | i.next(); 321 | } 322 | if (Double.isNaN(a)) { 323 | throw new IllegalArgumentException("PathIterator contains an open path"); 324 | } else { 325 | return 0.5 * Math.abs(a); 326 | } 327 | } 328 | 329 | private static double area(Line2D seg) { 330 | return seg.getX1() * seg.getY2() - seg.getX2() * seg.getY1(); 331 | } 332 | 333 | private static double angle(Point pc, Point p) { 334 | double ang = Math.atan2(p.y - pc.y, p.x - pc.x); 335 | if (ang < 0) ang += Math.PI * 2; 336 | return ang; 337 | } 338 | 339 | private static List parseCols(String line) { 340 | StringBuilder sb = new StringBuilder(); 341 | List cols = new ArrayList(); 342 | boolean inside = false; 343 | for (int i = 0; i < line.length(); i++) { 344 | char c = line.charAt(i); 345 | if (c == '\"') { 346 | inside = !inside; 347 | } else if (c == ',' && !inside) { 348 | cols.add(sb.toString()); 349 | sb.delete(0, sb.length()); 350 | } else { 351 | sb.append(c); 352 | } 353 | } 354 | cols.add(sb.toString()); 355 | return cols; 356 | } 357 | } -------------------------------------------------------------------------------- /2-wleite/src/visualizer/Utils.java: -------------------------------------------------------------------------------- 1 | package visualizer; 2 | 3 | import java.io.FileInputStream; 4 | import java.io.InputStream; 5 | import java.io.InputStreamReader; 6 | import java.io.LineNumberReader; 7 | import java.text.DecimalFormat; 8 | import java.text.DecimalFormatSymbols; 9 | import java.util.List; 10 | import java.util.Vector; 11 | 12 | public class Utils { 13 | 14 | private static DecimalFormat df; 15 | private static DecimalFormat df6; 16 | static { 17 | df = new DecimalFormat("0.000"); 18 | df6 = new DecimalFormat("0.000000"); 19 | DecimalFormatSymbols dfs = new DecimalFormatSymbols(); 20 | dfs.setDecimalSeparator('.'); 21 | df.setDecimalFormatSymbols(dfs); 22 | df6.setDecimalFormatSymbols(dfs); 23 | } 24 | 25 | /** 26 | * Pretty print a double 27 | */ 28 | public static String f(double d) { 29 | return df.format(d); 30 | } 31 | public static String f6(double d) { 32 | return df6.format(d).replace('.', ','); 33 | } 34 | 35 | // Gets the lines of a text file at the given path 36 | public static List readTextLines(String path) { 37 | List ret = new Vector<>(); 38 | try { 39 | InputStream is = new FileInputStream(path); 40 | InputStreamReader isr = new InputStreamReader(is, "UTF-8"); 41 | LineNumberReader lnr = new LineNumberReader(isr); 42 | while (true) { 43 | String line = lnr.readLine(); 44 | if (line == null) break; 45 | line = line.trim(); 46 | if (line.isEmpty() || line.startsWith("#")) continue; 47 | ret.add(line); 48 | } 49 | lnr.close(); 50 | } 51 | catch (Exception e) { 52 | e.printStackTrace(); 53 | } 54 | return ret; 55 | } 56 | } 57 | 58 | 59 | -------------------------------------------------------------------------------- /2-wleite/test.sh: -------------------------------------------------------------------------------- 1 | java -Xmx120G -cp bin:lib/imageio-ext-geocore-1.1.16.jar:lib/imageio-ext-streams-1.1.16.jar:lib/imageio-ext-tiff-1.1.16.jar:lib/imageio-ext-utilities-1.1.16.jar:lib/jai_codec-1.1.3.jar:lib/jai_core-1.1.3.jar:lib/jai_imageio-1.1.jar SpacenetMain test "$@" 2 | -------------------------------------------------------------------------------- /2-wleite/train.sh: -------------------------------------------------------------------------------- 1 | java -Xmx120G -cp bin:lib/imageio-ext-geocore-1.1.16.jar:lib/imageio-ext-streams-1.1.16.jar:lib/imageio-ext-tiff-1.1.16.jar:lib/imageio-ext-utilities-1.1.16.jar:lib/jai_codec-1.1.3.jar:lib/jai_core-1.1.3.jar:lib/jai_imageio-1.1.jar SpacenetMain train "$@" 2 | -------------------------------------------------------------------------------- /3-nofto/Dockerfile: -------------------------------------------------------------------------------- 1 | # To be built on a CPU-based system 2 | 3 | FROM ubuntu:16.04 4 | LABEL maintainer dlindenbaum 5 | 6 | # Install General Requirements 7 | RUN apt-get update && \ 8 | apt-get install -y --no-install-recommends \ 9 | apt-utils \ 10 | build-essential \ 11 | software-properties-common 12 | 13 | # Install Java 14 | RUN add-apt-repository ppa:webupd8team/java -y && \ 15 | apt-get update && \ 16 | echo oracle-java8-installer shared/accepted-oracle-license-v1-1 select true | /usr/bin/debconf-set-selections && \ 17 | apt-get install -y oracle-java8-installer && \ 18 | apt-get clean 19 | 20 | RUN mkdir round2Code 21 | 22 | # copy entire directory where docker file is into docker container at /opt/round2Code 23 | COPY . /opt/round2Code/ 24 | 25 | WORKDIR /opt/round2Code 26 | 27 | # create bin folder 28 | RUN mkdir bin 29 | 30 | # compile Java Code 31 | RUN javac -sourcepath src -cp bin -d bin src/*.java 32 | 33 | -------------------------------------------------------------------------------- /3-nofto/README.md: -------------------------------------------------------------------------------- 1 | **Marathon Match - Solution Description** 2 | 3 | 1. **1.**** Introduction** 4 | 5 | - **●●** Handle: nofto 6 | 7 | 1. **2.**** Solution Development** 8 | 9 | After the first round, in which I finished 7th, I was sure I cannot beat wleite's winning solution using my previous approach. Having some experience with wleite's code from the Lung Cancer Round 2, it was natural to start with his solution also in this match. My idea was to improve it. Sadly, almost nothing I tried was beneficial. Here is the list of things which _did not_ work (i. e., did not show improvement in the provisional score) 10 | 11 | - **●●** Changing the returned shapes from convex hulls to non-convex 12 | - **●●** Adding non-convex shapes to the convex hulls (that is, doubling the number of shapes which are processed in the second training phase) 13 | - **●●** Training on multiple cities, that is, trying to build a single model for all cities. 14 | - **●●** Adding new shape features (ratio of original area and convex hull area, distance from the image border, number of detected shapes in the current image, ratio of original perimeter and convex hull perimeter) 15 | - **●●** Adding new "global" pixel features (the original methods rectStatFeatures() and text() applied to the entire image frame) 16 | 17 | 1. **3.**** Final Approach** 18 | 19 | I will describe only the differences between the original wleite's and my solution. All these are only minor changes (or changes implied by the difference in data format between both rounds). 20 | 21 | - **●●** In the first round, there were 2 kinds of images, denoted by _3band_ and _8band_. The problem statement says that in the second round, _RGB-PanSharpen_ corresponds to _3band_ and _MUL_ corresponds to _8band_. However, I found out that better score is obtained when _MUL_ is replaced with _MUL-PanSharpen_ (around 10% gain). Since the resolution of _MUL-PanSharpen_ images is the same as the resolution of _RGB-PanSharpen_ (which was not the case for _8band_ and _3band_ images), it is necessary to adjust lines 28-29 in BuildingFeatureExtractor.java and lines 151-152 in PolygonFeatureExtractor.java. 22 | - **●●** The original solution uses entropy as impurity function for splitting nodes in random forest. I replaced it with sqrt(x\*(1-x)) – it should be computationally less intensive and in the past I got better results in several problems with this function when compared to entropy and/or Gini impurity. In this problem, the results were only slightly better with my function (1% gain), and I only compared it on one city. 23 | - **●●** The original solution uses 60% of data for the first phase of the training, 30% for the second phase of the training and 10% for offline testing. There is no sense to leave any data for offline testing in the final tests, so my split is 65/35/0 instead of 60/30/10. 24 | - **●●** I believe there is a small bug on line 89 of the original Util.java (line 92 of my version) – there should be "buildingsPolyBorder2" instead of "buildingsPolyBorder". 25 | - **●●** I change the code so that it fulfils the requirements for final testing. 26 | 27 | 1. **4.**** Open Source Resources, Frameworks and Libraries** 28 | 29 | My solution does not use any library or open source resource different from the one used by wleite in the first round. The jar files located in the "lib" directory are probably needed only to work with TIFF format, and I do not know its origin – I downloaded it as a part of the first-round winning solution. 30 | 31 | 1. **5.**** Potential Algorithm Improvements** 32 | 33 | I have no clear idea how the approach may be improved. I tried several things (mentioned above) which did not help. 34 | 35 | 1. **6.**** Algorithm Limitations** 36 | 37 | The results are good only if you test on the model which was trained on the same city. If you will detect buildings on a new city without training data, only with one of the four city models, the results will be poor. 38 | 39 | 1. **7.**** Deployment Guide** 40 | 41 | Follow the guide from the wleite's solution of the first match. I did not add any new source files, I only changed some of the original files. 42 | 43 | 1. **8.**** Final Verification** 44 | 45 | 1. **1.** Create directory structure as in the zip file in [https://www.dropbox.com/s/iov4wsgmutxt7ko/nofto-docker.zip?dl=1](https://www.dropbox.com/s/iov4wsgmutxt7ko/nofto-docker.zip?dl=1) 46 | 2. **2.** Execute "BuildingDetectorTrainer <directory>", which will produce a serialized random forest files named "rfBuilding.dat", "rfBorder.dat" and "rfDist.dat" in the corresponding models/city<n> directory. 47 | 3. **3.** Execute "PolygonMatcherTrainer <directory>", which will produce a serialized random forest file named "rfPolyMatch.dat" in the corresponding models/city<n> directory. 48 | 4. **4.** Execute "BuildingDetectorTester <directory> <output file>", which will produce the expected CSV file. -------------------------------------------------------------------------------- /3-nofto/lib/imageio-ext-geocore-1.1.16.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SpaceNetChallenge/BuildingDetectors_Round2/04778fdcdd95cc395ebc7e73942eda58143afb42/3-nofto/lib/imageio-ext-geocore-1.1.16.jar -------------------------------------------------------------------------------- /3-nofto/lib/imageio-ext-streams-1.1.16.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SpaceNetChallenge/BuildingDetectors_Round2/04778fdcdd95cc395ebc7e73942eda58143afb42/3-nofto/lib/imageio-ext-streams-1.1.16.jar -------------------------------------------------------------------------------- /3-nofto/lib/imageio-ext-tiff-1.1.16.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SpaceNetChallenge/BuildingDetectors_Round2/04778fdcdd95cc395ebc7e73942eda58143afb42/3-nofto/lib/imageio-ext-tiff-1.1.16.jar -------------------------------------------------------------------------------- /3-nofto/lib/imageio-ext-utilities-1.1.16.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SpaceNetChallenge/BuildingDetectors_Round2/04778fdcdd95cc395ebc7e73942eda58143afb42/3-nofto/lib/imageio-ext-utilities-1.1.16.jar -------------------------------------------------------------------------------- /3-nofto/lib/jai_codec-1.1.3.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SpaceNetChallenge/BuildingDetectors_Round2/04778fdcdd95cc395ebc7e73942eda58143afb42/3-nofto/lib/jai_codec-1.1.3.jar -------------------------------------------------------------------------------- /3-nofto/lib/jai_core-1.1.3.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SpaceNetChallenge/BuildingDetectors_Round2/04778fdcdd95cc395ebc7e73942eda58143afb42/3-nofto/lib/jai_core-1.1.3.jar -------------------------------------------------------------------------------- /3-nofto/lib/jai_imageio-1.1.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SpaceNetChallenge/BuildingDetectors_Round2/04778fdcdd95cc395ebc7e73942eda58143afb42/3-nofto/lib/jai_imageio-1.1.jar -------------------------------------------------------------------------------- /3-nofto/models/readme.txt: -------------------------------------------------------------------------------- 1 | Model files are not stored in git, they should be regenerated using train.sh -------------------------------------------------------------------------------- /3-nofto/src/Beam.java: -------------------------------------------------------------------------------- 1 | import java.util.Arrays; 2 | 3 | class Beam> { 4 | private int beamWidth; 5 | private Object[] items; 6 | private Object[] aux; 7 | private int size; 8 | 9 | Beam(int beamWidth) { 10 | this.beamWidth = beamWidth; 11 | items = new Object[beamWidth]; 12 | aux = new Object[beamWidth]; 13 | } 14 | 15 | void setWidth(int beamWidth) { 16 | this.beamWidth = beamWidth; 17 | if (items.length < beamWidth) { 18 | items = Arrays.copyOf(items, beamWidth); 19 | aux = new Object[beamWidth]; 20 | } 21 | if (size > beamWidth) size = beamWidth; 22 | } 23 | 24 | @SuppressWarnings("unchecked") 25 | boolean add(T item) { 26 | if (size >= beamWidth && ((Comparable) items[beamWidth - 1]).compareTo(item) <= 0) return false; 27 | int pos = Arrays.binarySearch(items, 0, size, item); 28 | if (pos < 0) pos = -pos - 1; 29 | else if (items[pos].equals(item)) return false; 30 | if (pos >= beamWidth) return false; 31 | if (size < beamWidth) size++; 32 | 33 | System.arraycopy(items, pos, aux, 0, size - pos - 1); 34 | System.arraycopy(aux, 0, items, pos + 1, size - pos - 1); 35 | items[pos] = item; 36 | return true; 37 | } 38 | 39 | @SuppressWarnings("unchecked") 40 | T get(int idx) { 41 | return (T) items[idx]; 42 | } 43 | 44 | @SuppressWarnings("unchecked") 45 | T last() { 46 | return (T) items[size - 1]; 47 | } 48 | 49 | int size() { 50 | return size; 51 | } 52 | 53 | boolean isFull() { 54 | return size == beamWidth; 55 | } 56 | 57 | void clear() { 58 | Arrays.fill(items, 0, size, null); 59 | size = 0; 60 | } 61 | 62 | void remove(int pos) { 63 | System.arraycopy(items, pos + 1, aux, 0, size - pos - 1); 64 | System.arraycopy(aux, 0, items, pos, size - pos - 1); 65 | items[--size] = null; 66 | } 67 | } -------------------------------------------------------------------------------- /3-nofto/src/Building.java: -------------------------------------------------------------------------------- 1 | import java.awt.Polygon; 2 | import java.awt.geom.Area; 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | public class Building { 7 | final int id; 8 | final List in = new ArrayList(); 9 | final List out = new ArrayList(); 10 | private Area area; 11 | private double areaVal = -1; 12 | // private Polygon rp; 13 | 14 | public Building(int id) { 15 | this.id = id; 16 | } 17 | 18 | /* 19 | * public Polygon getRectPolygon() { if (rp == null) rp = 20 | * Util.toRect(in.get(0)); return rp; } 21 | */ 22 | public Area getArea() { 23 | if (area == null) { 24 | area = new Area(); 25 | for (Polygon p : in) { 26 | area.add(new Area(p)); 27 | } 28 | for (Polygon p : out) { 29 | area.subtract(new Area(p)); 30 | } 31 | } 32 | return area; 33 | } 34 | 35 | public double getAreaVal() { 36 | if (areaVal == -1) 37 | areaVal = Util.areaVal(getArea()); 38 | return areaVal; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /3-nofto/src/BuildingDetectorTrainer.java: -------------------------------------------------------------------------------- 1 | import java.awt.Polygon; 2 | import java.awt.Rectangle; 3 | import java.awt.image.BufferedImage; 4 | import java.io.File; 5 | import java.util.ArrayList; 6 | import java.util.Arrays; 7 | import java.util.List; 8 | import java.util.Map; 9 | 10 | import javax.imageio.ImageIO; 11 | 12 | public class BuildingDetectorTrainer { 13 | private Map> buildingsPerImage; 14 | private final int numThreads = Math.max(1, Runtime.getRuntime().availableProcessors() * 95 / 100); 15 | private final int maxTrees = 60; 16 | private List features = new ArrayList(); 17 | private List borderClassif = new ArrayList(); 18 | private List buildingClassif = new ArrayList(); 19 | private List distValues = new ArrayList(); 20 | private Random rnd = new Random(22062012); 21 | 22 | public static void main(String[] args) { 23 | String dataDir = "data/AOI_3_Paris_Train"; 24 | if(args.length > 0) dataDir = args[0]; 25 | int position = dataDir.indexOf("AOI_"); 26 | if(position == -1){ 27 | System.err.println(""); 28 | System.err.println(" Input directory does not contain 'AOI_' substring! Exiting without doing anything..."); 29 | System.exit(1); 30 | } 31 | String test = dataDir.substring(position + 4, position + 5); 32 | String testName = dataDir.substring(position); 33 | String outDir = "models/city" + test + "/"; 34 | File trainingCsv = new File(dataDir + "/summaryData/" + testName + "_Building_Solutions.csv"); 35 | File folder3band = new File(dataDir + "/RGB-PanSharpen"); 36 | File folder8band = new File(dataDir + "/MUL-PanSharpen"); 37 | File rfBuilding = new File(outDir + "rfBuilding.dat"); 38 | File rfBorder = new File(outDir + "rfBorder.dat"); 39 | File rfDist = new File(outDir + "rfDist.dat"); 40 | rfBuilding.getParentFile().mkdirs(); 41 | System.err.println(""); 42 | System.err.println(" Training phase 1 for data '" + testName + "' succesfully started..."); 43 | System.err.println(""); 44 | new BuildingDetectorTrainer().train(trainingCsv, folder3band, folder8band, rfBuilding, rfBorder, rfDist); 45 | } 46 | 47 | public void train(File trainingCsv, File folder3band, File folder8band, File rfBuilding, File rfBorder, File rfDist) { 48 | buildingsPerImage = Util.readBuildingsCsv(trainingCsv); 49 | Util.splitImages(buildingsPerImage, new int[] {65,35,0}, 0); 50 | processImages(folder3band, folder8band); 51 | buildRandomForests(rfBuilding, rfBorder, rfDist); 52 | } 53 | 54 | private void buildRandomForests(File rfBuilding, File rfBorder, File rfDist) { 55 | try { 56 | System.err.println(" Building 3 Random Forests, each of " + maxTrees + " trees, with " + numThreads + " parallel threads..."); 57 | float[][] arrFeatures = new float[features.get(0).length][features.size()]; 58 | for (int i = 0; i < features.size(); i++) { 59 | float[] a = features.get(i); 60 | for (int j = 0; j < a.length; j++) { 61 | arrFeatures[j][i] = a[j]; 62 | } 63 | } 64 | features.clear(); 65 | boolean[] classif = null; 66 | float[] values = null; 67 | for (int k = 0; k <= 2; k++) { 68 | long t = System.currentTimeMillis(); 69 | if (k == 0 || k == 1) { 70 | List lClassif = k == 0 ? buildingClassif : borderClassif; 71 | classif = new boolean[lClassif.size()]; 72 | for (int i = 0; i < classif.length; i++) { 73 | classif[i] = lClassif.get(i).booleanValue(); 74 | } 75 | lClassif.clear(); 76 | } else if (k == 2) { 77 | List lValues = distValues; 78 | values = new float[lValues.size()]; 79 | for (int i = 0; i < classif.length; i++) { 80 | values[i] = lValues.get(i).floatValue(); 81 | } 82 | lValues.clear(); 83 | } 84 | File rfFile = k == 0 ? rfBuilding : k == 1 ? rfBorder : rfDist; 85 | if (!rfFile.getParentFile().exists()) rfFile.getParentFile().mkdirs(); 86 | if (k == 0 || k == 1) RandomForestBuilder.train(arrFeatures, classif, maxTrees, rfFile, numThreads); 87 | else RandomForestBuilder.train(arrFeatures, values, maxTrees, rfFile, numThreads); 88 | if (k == 0) { 89 | System.err.println(""); 90 | System.err.println("\t RF Building: " + rfBuilding.length() + " bytes "); 91 | System.err.println("\t Elapsed Time: " + (System.currentTimeMillis() - t) + " ms"); 92 | } else if (k == 1) { 93 | System.err.println(""); 94 | System.err.println("\t RF Border: " + rfBorder.length() + " bytes "); 95 | System.err.println("\t Elapsed Time: " + (System.currentTimeMillis() - t) + " ms"); 96 | } else if (k == 2) { 97 | System.err.println(""); 98 | System.err.println("\t RF Dist: " + rfDist.length() + " bytes "); 99 | System.err.println("\t Elapsed Time: " + (System.currentTimeMillis() - t) + " ms"); 100 | } 101 | } 102 | System.err.println(); 103 | } catch (Exception e) { 104 | e.printStackTrace(); 105 | } 106 | } 107 | 108 | private void processImages(final File folder3band, final File folder8band) { 109 | try { 110 | System.err.println(" Processing Images with " + numThreads + " parallel threads..."); 111 | long t = System.currentTimeMillis(); 112 | final List images = new ArrayList(buildingsPerImage.keySet()); 113 | Thread[] threads = new Thread[numThreads]; 114 | for (int i = 0; i < numThreads; i++) { 115 | final int start = i; 116 | threads[i] = new Thread() { 117 | public void run() { 118 | for (int j = start; j < images.size(); j += numThreads) { 119 | //if (j!=49) continue; 120 | String image = images.get(j); 121 | //File imageFile3band = new File(folder3band, "3band_" + image + ".tif"); 122 | //File imageFile8band = new File(folder8band, "8band_" + image + ".tif"); 123 | File imageFile3band = new File(folder3band, "RGB-PanSharpen_" + image + ".tif"); 124 | File imageFile8band = new File(folder8band, "MUL-PanSharpen_" + image + ".tif"); 125 | List buildings = buildingsPerImage.get(image); 126 | processImage(imageFile3band, imageFile8band, buildings); 127 | /*if (start == 0)*/ System.err.print("\r\t\t" + (j + 1) + "/" + images.size() + " "); 128 | } 129 | } 130 | }; 131 | threads[i].start(); 132 | threads[i].setPriority(Thread.MIN_PRIORITY); 133 | } 134 | for (int i = 0; i < numThreads; i++) { 135 | threads[i].join(); 136 | } 137 | System.err.println(""); 138 | System.err.println("\t Samples: " + buildingClassif.size()); 139 | System.err.println("\t Elapsed Time: " + (System.currentTimeMillis() - t) + " ms"); 140 | System.err.println(); 141 | } catch (Exception e) { 142 | e.printStackTrace(); 143 | } 144 | } 145 | 146 | private void processImage(File imageFile3band, File imageFile8band, List buildings) { 147 | try { 148 | BufferedImage img3band = ImageIO.read(imageFile3band); 149 | int w = img3band.getWidth(); 150 | int h = img3band.getHeight(); 151 | int[][] inside = new int[h][w]; 152 | boolean[][] border = new boolean[h][w]; 153 | int id = 0; 154 | for (Building building : buildings) { 155 | id++; 156 | for (int step = 0; step <= 1; step++) { 157 | int value = step == 0 ? id : 0; 158 | List polygons = step == 0 ? building.in : building.out; 159 | for (Polygon polygon : polygons) { 160 | Rectangle rc = polygon.getBounds(); 161 | int yMin = Math.max(rc.y, 0); 162 | int yMax = Math.min(rc.y + rc.height, h); 163 | int xMin = Math.max(rc.x, 0); 164 | int xMax = Math.min(rc.x + rc.width, w); 165 | for (int y = yMin; y < yMax; y++) { 166 | int[] iy = inside[y]; 167 | for (int x = xMin; x < xMax; x++) { 168 | if (polygon.contains(x, y)) iy[x] = value; 169 | } 170 | } 171 | } 172 | } 173 | } 174 | 175 | int maxDist = 10; 176 | int[][] dist = new int[h][w]; 177 | for (int y = 0; y < h; y++) { 178 | Arrays.fill(dist[y], maxDist + 1); 179 | } 180 | int[] queue = new int[w * h * 4]; 181 | int tot = 0; 182 | for (int y = 0; y < h; y++) { 183 | for (int x = 0; x < w; x++) { 184 | int v = inside[y][x]; 185 | boolean b = false; 186 | if (x > 0 && v != inside[y][x - 1]) b = true; 187 | else if (x < w - 1 && v != inside[y][x + 1]) b = true; 188 | else if (y > 0 && v != inside[y - 1][x]) b = true; 189 | else if (y < h - 1 && v != inside[y + 1][x]) b = true; 190 | if (b) { 191 | border[y][x] = true; 192 | dist[y][x] = 0; 193 | queue[tot++] = y * w + x; 194 | } 195 | } 196 | } 197 | 198 | int curr = 0; 199 | while (curr < tot) { 200 | int p = queue[curr++]; 201 | int x = p % w; 202 | int y = p / w; 203 | int nd = dist[y][x] + 1; 204 | if (nd > maxDist) continue; 205 | for (int i = 0; i < 4; i++) { 206 | int nx = i == 0 ? x + 1 : i == 1 ? x - 1 : x; 207 | if (nx < 0 || nx >= w) continue; 208 | int ny = i == 2 ? y + 1 : i == 3 ? y - 1 : y; 209 | if (ny < 0 || ny >= h) continue; 210 | if (dist[ny][nx] > nd) { 211 | dist[ny][nx] = nd; 212 | queue[tot++] = ny * w + nx; 213 | } 214 | } 215 | } 216 | 217 | BufferedImage img8band = ImageIO.read(imageFile8band); 218 | MultiChannelImage mci = new MultiChannelImage(img3band, img8band); 219 | int step = BuildingFeatureExtractor.featTrainingStep; 220 | if (id == 0) step *= 4; 221 | for (int y = rnd.nextInt(step); y + BuildingFeatureExtractor.featRectSize <= h; y += step) { 222 | for (int x = rnd.nextInt(step); x + BuildingFeatureExtractor.featRectSize <= w; x += step) { 223 | int ic = 0; 224 | int bc = 0; 225 | for (int ay = y; ay < y + BuildingFeatureExtractor.featRectSize; ay++) { 226 | int[] iay = inside[ay]; 227 | boolean[] bay = border[ay]; 228 | for (int ax = x; ax < x + BuildingFeatureExtractor.featRectSize; ax++) { 229 | if (bay[ax]) bc++; 230 | else if (iay[ax] > 0) ic++; 231 | } 232 | } 233 | float[] arrFeatures = BuildingFeatureExtractor.getFeatures(mci, x, y, w, h); 234 | synchronized (borderClassif) { 235 | buildingClassif.add(ic > 1); 236 | borderClassif.add(bc > 1); 237 | distValues.add((float) (dist[y][x] * (ic > 0 ? 1 : -1))); 238 | features.add(arrFeatures); 239 | } 240 | } 241 | } 242 | /* 243 | BufferedImage img2 = new BufferedImage(w * 2, h, BufferedImage.TYPE_INT_BGR); 244 | for (int y = 0; y < h; y++) { 245 | for (int x = 0; x < w; x++) { 246 | if (inside[y][x] != 0) img2.setRGB(x, y, 255); 247 | if (border[y][x]) img2.setRGB(x, y, 256 * 256 * 255); 248 | int d = dist[y][x]; 249 | if (inside[y][x] == 0) d=-d; 250 | int c = Color.HSBtoRGB((-d+maxDist)/(float)(3*maxDist+1), 1, 1); 251 | img2.setRGB(x, y, c); 252 | img2.setRGB(x + w, y, img3band.getRGB(x, y)); 253 | } 254 | } 255 | new ImgViewer(img2); 256 | */ 257 | } catch (Exception e) { 258 | e.printStackTrace(); 259 | } 260 | } 261 | } -------------------------------------------------------------------------------- /3-nofto/src/BuildingFeatureExtractor.java: -------------------------------------------------------------------------------- 1 | public class BuildingFeatureExtractor { 2 | public static final int featRectSize = 2; 3 | public static final int featTrainingStep = 5; //15;//10;//5; 4 | public static final int featTestingStep = 1; //2;//1; 5 | 6 | public static float[] getFeatures(MultiChannelImage image, int sx, int sy, int w, int h) { 7 | float[] ret = new float[5 * (3 * 4 + 2 * 4) + 8 * 2]; 8 | int k = 0; 9 | for (int i : new int[] {0,1,3,5,7}) { 10 | int rx = sx - i; 11 | int ry = sy - i; 12 | int rs = (i + 1) * 2; 13 | System.arraycopy(rectStatFeatures(image.h, rx, ry, rs, rs, w, h), 0, ret, k, 3); 14 | k += 3; 15 | System.arraycopy(rectStatFeatures(image.s, rx, ry, rs, rs, w, h), 0, ret, k, 3); 16 | k += 3; 17 | System.arraycopy(rectStatFeatures(image.l, rx, ry, rs, rs, w, h), 0, ret, k, 3); 18 | k += 3; 19 | System.arraycopy(rectStatFeatures(image.edge, rx, ry, rs, rs, w, h), 0, ret, k, 3); 20 | k += 3; 21 | System.arraycopy(text(image.l, rx, ry, rs, rs, w, h), 0, ret, k, 2); 22 | k += 2; 23 | System.arraycopy(text(image.edge, rx, ry, rs, rs, w, h), 0, ret, k, 2); 24 | k += 2; 25 | } 26 | int w8 = image.w8; 27 | int h8 = image.h8; 28 | int rx = sx / 1; 29 | int ry = sy / 1; 30 | int p = rx + ry * w8; 31 | if (ry < 0 || rx < 0 || rx >= w8 || ry >= h8) { 32 | for (int i = 0; i < 16; i++) { 33 | ret[k++] = -1; 34 | } 35 | } else { 36 | for (int i = 0; i < 8; i++) { 37 | ret[k++] = image.extraBands[i][p]; 38 | ret[k++] = image.extraEdges[i][p]; 39 | } 40 | } 41 | return ret; 42 | } 43 | 44 | private static float[] text(int[] arr, int rx, int ry, int rw, int rh, int width, int height) { 45 | final int[] cnt1 = new int[256]; 46 | final int[] cnt2 = new int[256]; 47 | final int[] dxy = new int[] {-width - 1,-1,width - 1,width,width + 1,1,-width + 1,-width}; 48 | int max1 = 0; 49 | int max2 = 0; 50 | int ret1 = -1; 51 | int ret2 = -1; 52 | for (int y = ry; y < ry + rh; y++) { 53 | if (y < 1 || y >= height - 1) continue; 54 | int yw = y * width; 55 | for (int x = rx; x < rx + rw; x++) { 56 | if (x < 1 || x >= width - 1) continue; 57 | int m = 0; 58 | int s = 0; 59 | int c = yw + x; 60 | int ac = arr[c]; 61 | int aq = arr[c + dxy[7]]; 62 | for (int i = 0; i < 8; i++) { 63 | int ap = arr[c + dxy[i]]; 64 | if (ap > aq) m |= 1 << i; 65 | if (ap > ac) s |= 1 << i; 66 | aq = ap; 67 | } 68 | if (++cnt1[m] > max1) max1 = cnt1[ret1 = m]; 69 | if (++cnt2[s] > max2) max2 = cnt2[ret2 = s]; 70 | } 71 | } 72 | return new float[] {ret1,ret2}; 73 | } 74 | 75 | private static float[] rectStatFeatures(int[] a, int rx, int ry, int rw, int rh, int width, int height) { 76 | double sum = 0; 77 | double sumSquares = 0; 78 | double sumCubes = 0; 79 | int cnt = 0; 80 | for (int y = ry; y < ry + rh; y++) { 81 | if (y < 0 || y >= height) continue; 82 | for (int x = rx; x < rx + rw; x++) { 83 | if (x < 0 || x >= width) continue; 84 | int off = y * width + x; 85 | double p = a[off]; 86 | //if (p == -1) continue;//???????????? 87 | sum += p; 88 | double pp = p * p; 89 | sumSquares += pp; 90 | sumCubes += pp * p; 91 | cnt++; 92 | } 93 | } 94 | float[] ret = new float[3]; 95 | if (cnt > 0) { 96 | double k3 = (sumCubes - 3 * sumSquares * sum / cnt + 2 * sum * sum * sum / cnt / cnt) / cnt; 97 | double k2 = (sumSquares - sum * sum / cnt) / cnt; 98 | ret[0] = (float) (sum / cnt); 99 | ret[1] = (float) k2; 100 | ret[2] = (float) (k2 == 0 ? 0 : k3 / Math.pow(k2, 1.5)); 101 | } 102 | return ret; 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /3-nofto/src/ClassificationNode.java: -------------------------------------------------------------------------------- 1 | 2 | public class ClassificationNode { 3 | ClassificationNode left, right; 4 | float splitVal; 5 | int classif, level, splitFeature, startRow, endRow, total; 6 | double impurity, average = Double.NaN; 7 | 8 | public ClassificationNode(int classif, int total, double impurtity, int level, int startRow, int endRow) { 9 | this.classif = classif; 10 | this.total = total; 11 | this.startRow = startRow; 12 | this.endRow = endRow; 13 | this.level = level; 14 | this.impurity = impurtity; 15 | } 16 | 17 | public ClassificationNode(int level, int startRow, int endRow, double average, double error) { 18 | this.startRow = startRow; 19 | this.endRow = endRow; 20 | this.level = level; 21 | this.average = average; 22 | this.impurity = error; 23 | } 24 | 25 | public float getValue() { 26 | return Double.isNaN(average) ? classif / (float) total : (float) average; 27 | } 28 | 29 | public boolean isLeaf() { 30 | return left == null && right == null; 31 | } 32 | 33 | public boolean isPure() { 34 | return classif == 0 || classif == total; 35 | } 36 | 37 | public int getNumRows() { 38 | return endRow - startRow + 1; 39 | } 40 | } -------------------------------------------------------------------------------- /3-nofto/src/ClassificationTree.java: -------------------------------------------------------------------------------- 1 | 2 | public class ClassificationTree { 3 | private static double[] imp = new double[1 << 26]; 4 | 5 | static { 6 | for (int tot = 0; tot < 1 << 13; tot++) { 7 | for (int cnt = 0; cnt <= tot; cnt++) { 8 | /*double val = 0; 9 | if (cnt > 0) { 10 | double p = cnt / (double) tot; 11 | val -= p * Math.log(p); 12 | } 13 | if (tot - cnt > 0) { 14 | double p = (tot - cnt) / (double) tot; 15 | val -= p * Math.log(p); 16 | }*/ 17 | imp[(tot << 13) | cnt] = Math.sqrt(cnt / (double) tot * (1.0 - cnt / (double) tot));//val; 18 | } 19 | } 20 | } 21 | 22 | private ClassificationNode root; 23 | private final Random rnd; 24 | 25 | ClassificationTree(float[][] features, boolean[] classif, int idx, int totalTrees) { 26 | long t = System.currentTimeMillis(); 27 | int SPLIT_STEPS = 7; 28 | int MIN_ROWS_PER_NODE = 16; 29 | int MAX_LEVEL = 24; 30 | int MAX_NODES = 1 << 20; 31 | ClassificationNode[] nodes = new ClassificationNode[MAX_NODES + 2]; 32 | 33 | rnd = new Random(197209091220L + idx); 34 | int numFeatures = features.length; 35 | int featuresSteps = (numFeatures + 1) / 2; 36 | int totRows = classif.length; 37 | int sampledRows = totRows;//Math.min(100000, totRows); 38 | int[] weight = new int[totRows]; 39 | for (int i = 0; i < sampledRows/*totRows*/; i++) { 40 | weight[rnd.nextInt(totRows)]++; 41 | } 42 | int numSel = 0; 43 | for (int i = 0; i < totRows; i++) { 44 | if (weight[i] > 0) numSel++; 45 | } 46 | int[] selRows = new int[numSel]; 47 | numSel = 0; 48 | int classifCount = 0; 49 | for (int i = 0; i < totRows; i++) { 50 | if (weight[i] > 0) { 51 | selRows[numSel++] = i; 52 | if (classif[i]) classifCount += weight[i]; 53 | } 54 | } 55 | root = new ClassificationNode(classifCount, sampledRows/*totRows*/, impurity(classifCount, sampledRows/*totRows*/), 1, 0, numSel - 1); 56 | int nodeCnt = 0; 57 | nodes[nodeCnt++] = root; 58 | float[] prevSplitVal = new float[SPLIT_STEPS]; 59 | for (int i = 0; i < nodeCnt && nodeCnt < MAX_NODES; i++) { 60 | ClassificationNode node = nodes[i]; 61 | if (node.isPure() || node.level >= MAX_LEVEL || node.total <= MIN_ROWS_PER_NODE) continue; 62 | 63 | double maxSplitGain = 0; 64 | float bestSplitVal = 0; 65 | int bestSplitFeature = -1; 66 | 67 | for (int j = 0; j < featuresSteps; j++) { 68 | int splitFeature = rnd.nextInt(numFeatures); 69 | float[] featuresSplitFeature = features[splitFeature]; 70 | NEXT: for (int k = 0; k < SPLIT_STEPS; k++) { 71 | float splitVal = prevSplitVal[k] = featuresSplitFeature[randomNodeRow(selRows, node, rnd)]; 72 | for (int l = 0; l < k; l++) { 73 | if (splitVal == prevSplitVal[l]) continue NEXT; 74 | } 75 | int leftTot = 0; 76 | int rightTot = 0; 77 | int leftClassif = 0; 78 | int rightClassif = 0; 79 | for (int r = node.startRow; r <= node.endRow; r++) { 80 | int row = selRows[r]; 81 | int w = weight[row]; 82 | if (featuresSplitFeature[row] < splitVal) { 83 | if (classif[row]) leftClassif += w; 84 | leftTot += w; 85 | } else { 86 | if (classif[row]) rightClassif += w; 87 | rightTot += w; 88 | } 89 | } 90 | if (leftTot < MIN_ROWS_PER_NODE || rightTot < MIN_ROWS_PER_NODE) continue; 91 | double splitGain = node.impurity - impurity(leftClassif, leftTot, rightClassif, rightTot); 92 | if (splitGain > maxSplitGain) { 93 | maxSplitGain = splitGain; 94 | bestSplitFeature = splitFeature; 95 | bestSplitVal = splitVal; 96 | } 97 | } 98 | } 99 | if (bestSplitFeature >= 0) { 100 | int leftTot = 0; 101 | int rightTot = 0; 102 | int leftClassif = 0; 103 | int rightClassif = 0; 104 | 105 | int endLeft = node.endRow; 106 | float[] featuresSplitFeature = features[bestSplitFeature]; 107 | for (int r = node.startRow; r <= endLeft; r++) { 108 | int row = selRows[r]; 109 | int w = weight[row]; 110 | if (featuresSplitFeature[row] < bestSplitVal) { 111 | if (classif[row]) leftClassif += w; 112 | leftTot += w; 113 | } else { 114 | if (classif[row]) rightClassif += w; 115 | rightTot += w; 116 | selRows[r--] = selRows[endLeft]; 117 | selRows[endLeft--] = row; 118 | } 119 | } 120 | node.left = new ClassificationNode(leftClassif, leftTot, impurity(leftClassif, leftTot), node.level + 1, node.startRow, endLeft); 121 | node.right = new ClassificationNode(rightClassif, rightTot, impurity(rightClassif, rightTot), node.level + 1, endLeft + 1, node.endRow); 122 | nodes[nodeCnt++] = node.left; 123 | nodes[nodeCnt++] = node.right; 124 | node.splitVal = bestSplitVal; 125 | node.splitFeature = bestSplitFeature; 126 | } 127 | } 128 | System.err.print("\r\t\t" + (idx+1) + " / " + totalTrees + "\t\t" + nodeCnt + " nodes\t" + (System.currentTimeMillis() - t) / 1000 + "s "); 129 | } 130 | 131 | ClassificationTree(float[][] features, float[] values, int idx, int totalTrees) { 132 | long t = System.currentTimeMillis(); 133 | int SPLIT_STEPS = 9; 134 | int MIN_ROWS_PER_NODE = 16; 135 | int MAX_LEVEL = 24; 136 | int MAX_NODES = 1 << 20; 137 | ClassificationNode[] nodes = new ClassificationNode[MAX_NODES + 2]; 138 | rnd = new Random(197209091220L + idx); 139 | int numFeatures = features.length; 140 | int featuresSteps = (int) (Math.sqrt(numFeatures) * 2 + 1); 141 | int totRows = values.length; 142 | int sampledRows = totRows; //Math.min(100000, totRows); 143 | int[] weight = new int[totRows]; 144 | for (int i = 0; i < sampledRows/*totRows*/; i++) { 145 | weight[rnd.nextInt(totRows)]++; 146 | } 147 | int numSel = 0; 148 | for (int i = 0; i < totRows; i++) { 149 | if (weight[i] > 0) numSel++; 150 | } 151 | int[] selRows = new int[numSel]; 152 | double sum = 0; 153 | double sumSquares = 0; 154 | int tw = 0; 155 | numSel = 0; 156 | for (int i = 0; i < totRows; i++) { 157 | double w = weight[i]; 158 | if (w > 0) { 159 | selRows[numSel++] = i; 160 | double v = values[i]; 161 | sum += v * w; 162 | sumSquares += v * v * w; 163 | tw += w; 164 | } 165 | } 166 | double average = sum / tw; 167 | double error = error(tw, sum, sumSquares); 168 | 169 | root = new ClassificationNode(1, 0, numSel - 1, average, error); 170 | int nodeCnt = 0; 171 | nodes[nodeCnt++] = root; 172 | float[] prevSplitVal = new float[SPLIT_STEPS]; 173 | for (int i = 0; i < nodeCnt && nodeCnt < MAX_NODES; i++) { 174 | ClassificationNode node = nodes[i]; 175 | if (node.impurity == 0 || node.level >= MAX_LEVEL || node.getNumRows() <= MIN_ROWS_PER_NODE) continue; 176 | 177 | double maxSplitGain = 0; 178 | float bestSplitVal = 0; 179 | int bestSplitFeature = -1; 180 | double be1 = 0; 181 | double be2 = 0; 182 | 183 | for (int j = 0; j < featuresSteps; j++) { 184 | int splitFeature = rnd.nextInt(numFeatures); 185 | float[] featuresSplitFeature = features[splitFeature]; 186 | NEXT: for (int k = 0; k < SPLIT_STEPS; k++) { 187 | float splitVal = prevSplitVal[k] = featuresSplitFeature[randomNodeRow(selRows, node, rnd)]; 188 | for (int l = 0; l < k; l++) { 189 | if (splitVal == prevSplitVal[l]) continue NEXT; 190 | } 191 | double sum1 = 0; 192 | double sum2 = 0; 193 | double sumSquares1 = 0; 194 | double sumSquares2 = 0; 195 | int w1 = 0; 196 | int w2 = 0; 197 | for (int r = node.startRow; r <= node.endRow; r++) { 198 | int row = selRows[r]; 199 | double v = values[row]; 200 | int w = weight[row]; 201 | double vw = v * w; 202 | if (featuresSplitFeature[row] < splitVal) { 203 | sum1 += vw; 204 | sumSquares1 += v * vw; 205 | w1 += w; 206 | } else { 207 | sum2 += vw; 208 | sumSquares2 += v * vw; 209 | w2 += w; 210 | } 211 | } 212 | if (w1 < MIN_ROWS_PER_NODE || w2 < MIN_ROWS_PER_NODE) continue; 213 | double e1 = error(w1, sum1, sumSquares1); 214 | double e2 = error(w2, sum2, sumSquares2); 215 | double splitGain = node.impurity - (e1 + e2); 216 | if (splitGain > maxSplitGain) { 217 | maxSplitGain = splitGain; 218 | bestSplitFeature = splitFeature; 219 | bestSplitVal = splitVal; 220 | be1 = e1; 221 | be2 = e2; 222 | } 223 | } 224 | } 225 | if (bestSplitFeature >= 0) { 226 | double sum1 = 0; 227 | double sum2 = 0; 228 | int w1 = 0; 229 | int w2 = 0; 230 | int endLeft = node.endRow; 231 | float[] featuresSplitFeature = features[bestSplitFeature]; 232 | for (int r = node.startRow; r <= endLeft; r++) { 233 | int row = selRows[r]; 234 | double v = values[row]; 235 | int w = weight[row]; 236 | double val = featuresSplitFeature[row]; 237 | if (val < bestSplitVal) { 238 | sum1 += v * w; 239 | w1 += w; 240 | } else { 241 | sum2 += v * w; 242 | w2 += w; 243 | selRows[r--] = selRows[endLeft]; 244 | selRows[endLeft--] = row; 245 | } 246 | } 247 | node.left = new ClassificationNode(node.level + 1, node.startRow, endLeft, sum1 / w1, be1); 248 | node.right = new ClassificationNode(node.level + 1, endLeft + 1, node.endRow, sum2 / w2, be2); 249 | nodes[nodeCnt++] = node.left; 250 | nodes[nodeCnt++] = node.right; 251 | node.splitVal = (float) Math.min(bestSplitVal, bestSplitVal); 252 | node.splitFeature = bestSplitFeature; 253 | } 254 | } 255 | System.err.print("\r\t\t" + (idx+1) + " / " + totalTrees + "\t\t" + nodeCnt + " nodes\t" + (System.currentTimeMillis() - t) / 1000 + "s "); 256 | } 257 | 258 | public ClassificationNode getRoot() { 259 | return root; 260 | } 261 | 262 | private final double error(int n, double sum, double sumSquares) { 263 | return n == 0 ? 0 : sumSquares - sum * sum / n; 264 | } 265 | 266 | private final double impurity(int cnt, int tot) { 267 | if (tot < 8192) return imp[(tot << 13) | cnt]; 268 | /*double val = 0; 269 | if (cnt > 0) { 270 | double p = cnt / (double) tot; 271 | val -= p * Math.log(p); 272 | } 273 | if (tot - cnt > 0) { 274 | double p = (tot - cnt) / (double) tot; 275 | val -= p * Math.log(p); 276 | }*/ 277 | return Math.sqrt(cnt / (double) tot * (1.0 - cnt / (double) tot));//val; 278 | } 279 | 280 | private final double impurity(int cnt1, int tot1, int cnt2, int tot2) { 281 | return (impurity(cnt1, tot1) * tot1 + impurity(cnt2, tot2) * tot2) / (tot1 + tot2); 282 | } 283 | 284 | private final int randomNodeRow(int[] rows, ClassificationNode node, Random rnd) { 285 | return rows[rnd.nextInt(node.endRow - node.startRow + 1) + node.startRow]; 286 | } 287 | } 288 | 289 | class Random { 290 | private static final long mask0 = 0x80000000L; 291 | private static final long mask1 = 0x7fffffffL; 292 | private static final long[] mult = new long[] {0,0x9908b0dfL}; 293 | private final long[] mt = new long[624]; 294 | private int idx = 0; 295 | 296 | Random(long seed) { 297 | init(seed); 298 | } 299 | 300 | private void init(long seed) { 301 | mt[0] = seed & 0xffffffffl; 302 | for (int i = 1; i < 624; i++) { 303 | mt[i] = 1812433253l * (mt[i - 1] ^ (mt[i - 1] >>> 30)) + i; 304 | mt[i] &= 0xffffffffl; 305 | } 306 | } 307 | 308 | private void generate() { 309 | for (int i = 0; i < 227; i++) { 310 | long y = (mt[i] & mask0) | (mt[i + 1] & mask1); 311 | mt[i] = mt[i + 397] ^ (y >> 1) ^ mult[(int) (y & 1)]; 312 | } 313 | for (int i = 227; i < 623; i++) { 314 | long y = (mt[i] & mask0) | (mt[i + 1] & mask1); 315 | mt[i] = mt[i - 227] ^ (y >> 1) ^ mult[(int) (y & 1)]; 316 | } 317 | long y = (mt[623] & mask0) | (mt[0] & mask1); 318 | mt[623] = mt[396] ^ (y >> 1) ^ mult[(int) (y & 1)]; 319 | } 320 | 321 | private long rand() { 322 | if (idx == 0) generate(); 323 | long y = mt[idx]; 324 | idx = (idx + 1) % 624; 325 | y ^= (y >> 11); 326 | y ^= (y << 7) & 0x9d2c5680l; 327 | y ^= (y << 15) & 0xefc60000l; 328 | return y ^ (y >> 18); 329 | } 330 | 331 | int nextInt(int n) { 332 | return (int) (rand() % n); 333 | } 334 | } -------------------------------------------------------------------------------- /3-nofto/src/ImgViewer.java: -------------------------------------------------------------------------------- 1 | import java.awt.Graphics; 2 | import java.awt.Graphics2D; 3 | import java.awt.RenderingHints; 4 | import java.awt.image.BufferedImage; 5 | 6 | import javax.swing.JFrame; 7 | import javax.swing.JPanel; 8 | 9 | public class ImgViewer extends JFrame { 10 | private static final long serialVersionUID = -5820105568092949073L; 11 | private static final Object lock = new Object(); 12 | private static int off = 0; 13 | 14 | public ImgViewer(final BufferedImage img) { 15 | this(img,""); 16 | } 17 | 18 | public ImgViewer(final BufferedImage img, String title) { 19 | setTitle(title); 20 | JPanel imagePanel = new JPanel() { 21 | private static final long serialVersionUID = -7037900892428152902L; 22 | 23 | public void paintComponent(Graphics g) { 24 | super.paintComponent(g); 25 | ((Graphics2D) g).setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); 26 | int w = img.getWidth(); 27 | int h = img.getHeight(); 28 | if (getWidth() * h > getHeight() * w) { 29 | h = getHeight(); 30 | w = img.getWidth() * getHeight() / img.getHeight(); 31 | } else { 32 | w = getWidth(); 33 | h = img.getHeight() * getWidth() / img.getWidth(); 34 | } 35 | ((Graphics2D) g).drawImage(img, 0, 0, w, h, null); 36 | } 37 | }; 38 | getContentPane().add(imagePanel); 39 | synchronized (lock) { 40 | setBounds(0 + off, off, 1620, 860); 41 | off += 24; 42 | if (off > 300) off = 0; 43 | } 44 | setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); 45 | setVisible(true); 46 | } 47 | } -------------------------------------------------------------------------------- /3-nofto/src/MatchPolygon.java: -------------------------------------------------------------------------------- 1 | import java.awt.Polygon; 2 | 3 | public class MatchPolygon implements Comparable { 4 | double val; 5 | Polygon polygon; 6 | 7 | public MatchPolygon(double val, Polygon poly) { 8 | this.val = val; 9 | this.polygon = poly; 10 | } 11 | 12 | public int compareTo(MatchPolygon o) { 13 | return Double.compare(o.val, val); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /3-nofto/src/MultiChannelImage.java: -------------------------------------------------------------------------------- 1 | import java.awt.Color; 2 | import java.awt.image.BufferedImage; 3 | import java.util.Arrays; 4 | 5 | public class MultiChannelImage { 6 | int[] h, s, l, edge; 7 | int[][] extraBands, extraEdges; 8 | int width, height, w8, h8; 9 | 10 | public MultiChannelImage(BufferedImage img3band, BufferedImage img8band) { 11 | width = img3band.getWidth(); 12 | height = img3band.getHeight(); 13 | int length = width * height; 14 | int[] r = new int[length]; 15 | int[] g = new int[length]; 16 | int[] b = new int[length]; 17 | h = new int[length]; 18 | s = new int[length]; 19 | l = new int[length]; 20 | int[] gray = new int[length]; 21 | float[] hsl = new float[3]; 22 | int[] rgbArray = new int[length]; 23 | img3band.getRGB(0, 0, width, height, rgbArray, 0, width); 24 | /* 25 | * if (rot != 0) { 26 | * rotate(rgbArray, width, height, rot); 27 | * if (rot == 1 || rot == 3) { 28 | * int aux = width; 29 | * width = height; 30 | * height = aux; 31 | * } 32 | * } 33 | */ 34 | for (int i = 0; i < length; i++) { 35 | int p = rgbArray[i]; 36 | r[i] = r(p); 37 | g[i] = g(p); 38 | b[i] = b(p); 39 | } 40 | /////////////////////////BLUR///////////// 41 | for (int channel = 0; channel < 3; channel++) { 42 | int[] a = channel == 0 ? r : channel == 1 ? g : b; 43 | int[] c = a.clone(); 44 | for (int y = 1; y < height - 1; y++) { 45 | int yw = y * width; 46 | int p1 = c[yw - width]; 47 | int p2 = c[yw]; 48 | int p3 = c[yw + width]; 49 | int p4 = c[yw - width + 1]; 50 | int p5 = c[yw + 1]; 51 | int p6 = c[yw + width + 1]; 52 | for (int x = 1; x < width - 1; x++) { 53 | int p7 = c[yw - width + x + 1]; 54 | int p8 = c[yw + x + 1]; 55 | int p9 = c[yw + width + x + 1]; 56 | a[yw + x] = (p1 + p3 + p7 + p9 + ((p2 + p4 + p6 + p8) << 1) + (p5 << 2)) >>> 4; 57 | p1 = p4; 58 | p2 = p5; 59 | p3 = p6; 60 | p4 = p7; 61 | p5 = p8; 62 | p6 = p9; 63 | } 64 | } 65 | } 66 | /////////////////////////BLUR///////////// 67 | 68 | for (int i = 0; i < length; i++) { 69 | int rr = r[i]; 70 | int gg = g[i]; 71 | int bb = b[i]; 72 | gray[i] = (rr * 299 + gg * 587 + bb * 114) / 1000; 73 | Color.RGBtoHSB(rr, gg, bb, hsl); 74 | h[i] = (((int) Math.round(hsl[0] * 999)) + 500) % 1000; 75 | s[i] = (int) Math.round(hsl[1] * 999); 76 | l[i] = (int) Math.round(hsl[2] * 999); 77 | } 78 | edge = new int[length]; 79 | h8 = img8band.getHeight(); 80 | w8 = img8band.getWidth(); 81 | extraBands = new int[8][w8 * h8]; 82 | extraEdges = new int[8][w8 * h8]; 83 | int[] extraPixels = new int[w8 * h8 * 8]; 84 | img8band.getRaster().getPixels(0, 0, w8, h8, extraPixels); 85 | int c = 0; 86 | int pos = 0; 87 | for (int i = 0; i < h8; i++) { 88 | for (int j = 0; j < w8; j++) { 89 | for (int k = 0; k < 8; k++) { 90 | extraBands[k][pos] = extraPixels[c++]; 91 | } 92 | pos++; 93 | } 94 | } 95 | /* 96 | * if (rot != 0) { 97 | * for (int i = 0; i < 8; i++) { 98 | * rotate(extraBands[i], w8, h8, rot); 99 | * } 100 | * rotate(rgbArray, width, height, rot); 101 | * if (rot == 1 || rot == 3) { 102 | * int aux = w8; 103 | * w8 = h8; 104 | * h8 = aux; 105 | * } 106 | * } 107 | */ 108 | for (int i = 0; i < 8; i++) { 109 | Arrays.fill(extraEdges[i], -1); 110 | int[] p = extraBands[i]; 111 | int[] e = extraEdges[i]; 112 | for (int y = 1; y < h8 - 1; y++) { 113 | int off = y * w8 + 1; 114 | for (int x = 1; x < w8 - 1; x++, off++) { 115 | int p1 = p[off - 1 - w8]; 116 | int p2 = p[off - 1]; 117 | int p3 = p[off - 1 + w8]; 118 | int p4 = p[off - w8]; 119 | int p5 = p[off + w8]; 120 | int p6 = p[off + 1 - w8]; 121 | int p7 = p[off + 1]; 122 | int p8 = p[off + 1 + w8]; 123 | if (p1 < 0 || p3 < 0 || p6 < 0 || p8 < 0) continue; 124 | int vert = Math.abs(p1 + 2 * p4 + p6 - p3 - 2 * p5 - p8); 125 | int horiz = Math.abs(p1 + 2 * p2 + p3 - p6 - 2 * p7 - p8); 126 | e[off] = (int) Math.sqrt((vert * vert + horiz * horiz) / 2); 127 | } 128 | } 129 | } 130 | Arrays.fill(edge, -1); 131 | for (int y = 1; y < height - 1; y++) { 132 | int off = y * width + 1; 133 | for (int x = 1; x < width - 1; x++, off++) { 134 | /* 135 | * p1 p4 p6 136 | * p2 p7 137 | * p3 p5 p8 138 | */ 139 | int p1 = gray[off - 1 - width]; 140 | int p2 = gray[off - 1]; 141 | int p3 = gray[off - 1 + width]; 142 | int p4 = gray[off - width]; 143 | int p5 = gray[off + width]; 144 | int p6 = gray[off + 1 - width]; 145 | int p7 = gray[off + 1]; 146 | int p8 = gray[off + 1 + width]; 147 | if (p1 < 0 || p3 < 0 || p6 < 0 || p8 < 0) continue; 148 | int vert = Math.abs(p1 + 2 * p4 + p6 - p3 - 2 * p5 - p8); 149 | int horiz = Math.abs(p1 + 2 * p2 + p3 - p6 - 2 * p7 - p8); 150 | edge[off] = (int) Math.sqrt((vert * vert + horiz * horiz) / 2); 151 | } 152 | } 153 | } 154 | 155 | /* 156 | * private static void rotate(int[] a, int w, int h, int rot) { 157 | * int[] b = a.clone(); 158 | * if (rot == 1) { 159 | * for (int y = 0; y < h; y++) { 160 | * for (int x = 0; x < w; x++) { 161 | * a[x * h + h - 1 - y] = b[y * w + x]; 162 | * } 163 | * } 164 | * } 165 | * } 166 | */ 167 | 168 | private static final int r(int rgb) { 169 | return (rgb >>> 16) & 255; 170 | } 171 | 172 | private static final int g(int rgb) { 173 | return (rgb >>> 8) & 255; 174 | } 175 | 176 | private static final int b(int rgb) { 177 | return rgb & 255; 178 | } 179 | } -------------------------------------------------------------------------------- /3-nofto/src/PolygonFeatureExtractor.java: -------------------------------------------------------------------------------- 1 | import java.awt.Polygon; 2 | import java.awt.Rectangle; 3 | import java.awt.geom.AffineTransform; 4 | import java.awt.geom.Point2D; 5 | import java.util.ArrayList; 6 | import java.util.Arrays; 7 | import java.util.List; 8 | 9 | public class PolygonFeatureExtractor { 10 | public static final double[] buildingsCuts = new double[] {0.09,0.12,0.15,0.18,0.21,0.24,0.27,0.30,0.33,0.36,0.39,0.42,0.45,0.48,0.51,0.54,0.57,0.60}; 11 | //{0.09, 0.18, 0.25, 0.32, 0.37, 0.44, 0.51, 0.60}; //{0.09,0.15,0.20,0.25,0.29,0.33,0.36,0.40,0.44,0.49,0.54,0.60}; //{0.09,0.12,0.15,0.18,0.21,0.24,0.27,0.30,0.33,0.36,0.39,0.42,0.45,0.48,0.51,0.54,0.57,0.60}; 12 | public static final double[] borderWeights = new double[] {1,0.5};//,2}; 13 | public static final int[] borderShifts = new int[] {0,-1};//,1}; 14 | public static final int buildingsPolyBorder = 5; 15 | public static final int buildingsPolyBorder2 = 4; 16 | 17 | public static float[] getFeatures(MultiChannelImage mci, Polygon polygon, double[][] buildingValues, double[][] borderValues, double[][] distValues) { 18 | float[] ret = new float[5 + 6 + 6 * (4 * 2 + 4 * 3 + 2 * 8 * 1)]; 19 | int k = 0; 20 | double area = Util.getArea(polygon); 21 | double[] r = rectLen(polygon); 22 | ret[k++] = (float) area; 23 | ret[k++] = (float) r[0]; 24 | ret[k++] = (float) r[1]; 25 | ret[k++] = (float) (r[1] == 0 ? 0 : r[0] / r[1]); 26 | ret[k++] = (float) (area == 0 ? 0 : r[0] * r[1] / area); 27 | 28 | Rectangle rc = polygon.getBounds(); 29 | int cnt = 0; 30 | double buildingSum = 0; 31 | double borderSum = 0; 32 | final int w = borderValues[0].length; 33 | final int h = borderValues.length; 34 | boolean[][] inside = new boolean[h][w]; 35 | for (int y = rc.y; y <= rc.y + rc.height; y++) { 36 | int x0 = rc.x; 37 | for (int x = rc.x; x <= rc.x + rc.width; x++) { 38 | if (polygon.contains(x, y)) { 39 | x0 = x; 40 | break; 41 | } 42 | } 43 | int x1 = rc.x + rc.width; 44 | for (int x = x1; x >= x0; x--) { 45 | if (polygon.contains(x, y)) { 46 | x1 = x; 47 | break; 48 | } 49 | } 50 | for (int x = x0; x <= x1; x++) { 51 | cnt++; 52 | if (x >= 0 && y >= 0 && x < w && y < h) { 53 | inside[y][x] = true; 54 | buildingSum += buildingValues[y][x]; 55 | borderSum += borderValues[y][x]; 56 | } 57 | } 58 | } 59 | ret[k++] = (float) borderSum; 60 | ret[k++] = (float) buildingSum; 61 | ret[k++] = cnt == 0 ? -1 : (float) (borderSum / cnt); 62 | ret[k++] = cnt == 0 ? -1 : (float) (buildingSum / cnt); 63 | ret[k++] = buildingSum == 0 ? -1 : (float) (borderSum / buildingSum); 64 | ret[k++] = (float) (buildingSum - borderSum); 65 | 66 | final int maxDist = 4; 67 | final int halfDist = maxDist / 2; 68 | int[][] dist = new int[h][w]; 69 | for (int y = 0; y < h; y++) { 70 | Arrays.fill(dist[y], 1000); 71 | } 72 | int y0 = Math.max(0, rc.y - maxDist); 73 | int y1 = Math.min(h - 1, rc.y + rc.height + maxDist); 74 | int x0 = Math.max(0, rc.x - maxDist); 75 | int x1 = Math.min(w - 1, rc.x + rc.width + maxDist); 76 | int[] queue = new int[(y1 - y0 + 1) * (x1 - x0 + 1) * 4]; 77 | int tot = 0; 78 | for (int y = y0; y <= y1; y++) { 79 | boolean[] iy = inside[y]; 80 | for (int x = x0; x <= x1; x++) { 81 | boolean v = iy[x]; 82 | boolean b = false; 83 | if (x > 0 && v != iy[x - 1]) b = true; 84 | else if (x < w - 1 && v != iy[x + 1]) b = true; 85 | else if (y > 0 && v != inside[y - 1][x]) b = true; 86 | else if (y < h - 1 && v != inside[y + 1][x]) b = true; 87 | if (b) { 88 | queue[tot++] = y * w + x; 89 | dist[y][x] = 0; 90 | } 91 | } 92 | } 93 | int curr = 0; 94 | while (curr < tot) { 95 | int p = queue[curr++]; 96 | int x = p % w; 97 | int y = p / w; 98 | int nd = dist[y][x] + 1; 99 | if (nd > maxDist) continue; 100 | for (int i = 0; i < 4; i++) { 101 | int nx = i == 0 ? x + 1 : i == 1 ? x - 1 : x; 102 | if (nx < 0 || nx >= w) continue; 103 | int ny = i == 2 ? y + 1 : i == 3 ? y - 1 : y; 104 | if (ny < 0 || ny >= h) continue; 105 | if (dist[ny][nx] > nd) { 106 | dist[ny][nx] = nd; 107 | queue[tot++] = ny * w + nx; 108 | } 109 | } 110 | } 111 | List> values3band = new ArrayList>(); 112 | int len = (y1-y0+2)*(x1-x0+2); 113 | for (int i = 0; i < 8 * 6; i++) { 114 | values3band.add(new ArrayList(len)); 115 | } 116 | len/=4; 117 | List> values8band = new ArrayList>(); 118 | for (int i = 0; i < 16 * 6; i++) { 119 | values8band.add(new ArrayList()); 120 | } 121 | for (int y = y0; y <= y1; y++) { 122 | boolean[] iy = inside[y]; 123 | int[] dy = dist[y]; 124 | double[] buy = buildingValues[y]; 125 | double[] boy = borderValues[y]; 126 | double[] dvy = distValues[y]; 127 | for (int x = x0; x <= x1; x++) { 128 | boolean v = iy[x]; 129 | int d = dy[x]; 130 | int idx = -1; 131 | if (d == 0) idx = 0; 132 | else if (d <= halfDist && v) idx = 1; 133 | else if (d <= halfDist && !v) idx = 2; 134 | else if (d <= maxDist && v) idx = 3; 135 | else if (d <= maxDist && !v) idx = 4; 136 | else if (d > maxDist && v) idx = 5; 137 | if (idx >= 0) { 138 | int p = y * w + x; 139 | double a = buy[x]; 140 | double b = boy[x]; 141 | double c = dvy[x]; 142 | int pos = idx * 8; 143 | values3band.get(pos++).add(a); 144 | values3band.get(pos++).add(b); 145 | values3band.get(pos++).add(a - b); 146 | values3band.get(pos++).add(c); 147 | values3band.get(pos++).add((double) mci.edge[p]); 148 | values3band.get(pos++).add((double) mci.h[p]); 149 | values3band.get(pos++).add((double) mci.s[p]); 150 | values3band.get(pos++).add((double) mci.l[p]); 151 | 152 | int x8 = x / 1; 153 | int y8 = y / 1; 154 | pos = idx * 16; 155 | if (x8 < mci.w8 && y8 < mci.h8) { 156 | p = y8 * mci.w8 + x8; 157 | for (int i = 0; i < 8; i++) { 158 | values8band.get(pos++).add((double) mci.extraBands[i][p]); 159 | values8band.get(pos++).add((double) mci.extraEdges[i][p]); 160 | } 161 | } 162 | } 163 | } 164 | } 165 | int idx = 0; 166 | for (List a : values3band) { 167 | int n = idx % 8 < 4 ? 3 : 2; 168 | System.arraycopy(stats(a), 0, ret, k, n); 169 | k += n; 170 | idx++; 171 | } 172 | for (List a : values8band) { 173 | ret[k++] = avg(a); 174 | } 175 | return ret; 176 | } 177 | 178 | private static float[] stats(List l) { 179 | double sum = 0; 180 | double sumSquares = 0; 181 | int cnt = 0; 182 | for (double p : l) { 183 | sum += p; 184 | double pp = p * p; 185 | sumSquares += pp; 186 | cnt++; 187 | } 188 | float[] ret = new float[3]; 189 | if (cnt > 0) { 190 | ret[0] = (float) (sum / cnt); 191 | ret[1] = (float) ((sumSquares - sum * sum / cnt) / cnt); 192 | ret[2] = (float) sum; 193 | } 194 | return ret; 195 | } 196 | 197 | private static float avg(List l) { 198 | double sum = 0; 199 | for (double p : l) { 200 | sum += p; 201 | } 202 | return l.size() == 0 ? -1 : (float) (sum / l.size()); 203 | } 204 | 205 | private static double[] rectLen(Polygon polygon) { 206 | Rectangle rc = polygon.getBounds(); 207 | double xc = rc.getCenterX(); 208 | double yc = rc.getCenterY(); 209 | Point2D[] org = new Point2D[polygon.npoints]; 210 | Point2D[] dst = new Point2D[polygon.npoints]; 211 | for (int i = 0; i < polygon.npoints; i++) { 212 | org[i] = new Point2D.Double(polygon.xpoints[i], polygon.ypoints[i]); 213 | } 214 | double minArea = 1e99; 215 | double[] ret = new double[2]; 216 | for (int a = 0; a < 90; a += 3) { 217 | AffineTransform.getRotateInstance(Math.toRadians(a), xc, yc).transform(org, 0, dst, 0, org.length); 218 | Point2D r = dst[0]; 219 | double xmin = r.getX(); 220 | double ymin = r.getY(); 221 | double xmax = xmin; 222 | double ymax = ymin; 223 | for (int i = 1; i < polygon.npoints; i++) { 224 | r = dst[i]; 225 | xmin = Math.min(xmin, r.getX()); 226 | xmax = Math.max(xmax, r.getX()); 227 | ymin = Math.min(ymin, r.getY()); 228 | ymax = Math.max(ymax, r.getY()); 229 | } 230 | double dx = xmax - xmin; 231 | double dy = ymax - ymin; 232 | double currArea = dx * dy; 233 | if (currArea < minArea) { 234 | minArea = currArea; 235 | ret[0] = Math.min(dx, dy); 236 | ret[1] = Math.max(dx, dy); 237 | } 238 | } 239 | return ret; 240 | } 241 | } -------------------------------------------------------------------------------- /3-nofto/src/PolygonMatcherTrainer.java: -------------------------------------------------------------------------------- 1 | import java.awt.Polygon; 2 | import java.awt.Rectangle; 3 | import java.awt.geom.Area; 4 | import java.awt.image.BufferedImage; 5 | import java.io.File; 6 | import java.util.ArrayList; 7 | import java.util.List; 8 | import java.util.Map; 9 | 10 | import javax.imageio.ImageIO; 11 | 12 | public class PolygonMatcherTrainer { 13 | private Map> buildingsPerImage; 14 | private final int numThreads = Math.max(1, Runtime.getRuntime().availableProcessors() * 95 / 100); 15 | private final int maxTrees = 60; 16 | private List polyMatchFeatures = new ArrayList(); 17 | private List polyMatchValues = new ArrayList(); 18 | private RandomForestPredictor buildingPredictor, borderPredictor, distPredictor; 19 | 20 | public static void main(String[] args) { 21 | String dataDir = "data/AOI_3_Paris_Train"; 22 | if(args.length > 0) dataDir = args[0]; 23 | int position = dataDir.indexOf("AOI_"); 24 | if(position == -1){ 25 | System.err.println(""); 26 | System.err.println(" Input directory does not contain 'AOI_' substring! Exiting without doing anything..."); 27 | System.exit(1); 28 | } 29 | String test = dataDir.substring(position + 4, position + 5); 30 | String testName = dataDir.substring(position); 31 | String outDir = "models/city" + test + "/"; 32 | File trainingCsv = new File(dataDir + "/summaryData/" + testName + "_Building_Solutions.csv"); 33 | File folder3band = new File(dataDir + "/RGB-PanSharpen"); 34 | File folder8band = new File(dataDir + "/MUL-PanSharpen"); 35 | File rfBuilding = new File(outDir + "rfBuilding.dat"); 36 | File rfBorder = new File(outDir + "rfBorder.dat"); 37 | File rfDist = new File(outDir + "rfDist.dat"); 38 | File rfPolyMatch = new File(outDir + "rfPolyMatch.dat"); 39 | System.err.println(""); 40 | System.err.println(" Training phase 2 for data '" + testName + "' succesfully started..."); 41 | System.err.println(""); 42 | rfPolyMatch.getParentFile().mkdirs(); 43 | new PolygonMatcherTrainer().train(trainingCsv, folder3band, folder8band, rfBuilding, rfBorder, rfDist, rfPolyMatch); 44 | } 45 | 46 | public void train(File trainingCsv, File folder3band, File folder8band, File rfBuilding, File rfBorder, File rfDist, File rfPolyMatch) { 47 | buildingsPerImage = Util.readBuildingsCsv(trainingCsv); 48 | Util.splitImages(buildingsPerImage, new int[] {65,35,0}, 1); 49 | loadPredictors(rfBuilding, rfBorder, rfDist); 50 | processImages(folder3band, folder8band); 51 | buildRandomForest(rfPolyMatch); 52 | } 53 | 54 | private void buildRandomForest(File rfPolyMatch) { 55 | try { 56 | System.err.println(" Building Random Forest of " + maxTrees + " trees with " + numThreads + " parallel threads..."); 57 | long t = System.currentTimeMillis(); 58 | float[] values = new float[polyMatchValues.size()]; 59 | for (int i = 0; i < values.length; i++) { 60 | values[i] = polyMatchValues.get(i).floatValue(); 61 | } 62 | polyMatchValues.clear(); 63 | //System.err.println("polyMatchFeatures.get(0).length = " + polyMatchFeatures.get(0).length + ", values.length = " + values.length); 64 | float[][] features = new float[polyMatchFeatures.get(0).length][values.length]; 65 | for (int i = 0; i < values.length; i++) { 66 | float[] a = polyMatchFeatures.get(i); 67 | for (int j = 0; j < a.length; j++) { 68 | features[j][i] = a[j]; 69 | } 70 | } 71 | polyMatchFeatures.clear(); 72 | RandomForestBuilder.train(features, values, maxTrees, rfPolyMatch, numThreads); 73 | System.err.println(""); 74 | System.err.println("\t RF Poly Match: " + rfPolyMatch.length() + " bytes "); 75 | System.err.println("\t Elapsed Time: " + (System.currentTimeMillis() - t) + " ms"); 76 | System.err.println(); 77 | } catch (Exception e) { 78 | e.printStackTrace(); 79 | } 80 | } 81 | 82 | private void processImages(final File folder3band, final File folder8band) { 83 | try { 84 | System.err.println(" Processing Images with " + numThreads + " parallel threads..."); 85 | long t = System.currentTimeMillis(); 86 | final List images = new ArrayList(buildingsPerImage.keySet()); 87 | Thread[] threads = new Thread[numThreads]; 88 | for (int i = 0; i < numThreads; i++) { 89 | final int start = i; 90 | threads[i] = new Thread() { 91 | public void run() { 92 | for (int j = start; j < images.size(); j += numThreads) { 93 | //if (j!=23) continue; 94 | String image = images.get(j); 95 | File imageFile3band = new File(folder3band, "RGB-PanSharpen_" + image + ".tif"); 96 | File imageFile8band = new File(folder8band, "MUL-PanSharpen_" + image + ".tif"); 97 | List buildings = buildingsPerImage.get(image); 98 | processImage(imageFile3band, imageFile8band, buildings); 99 | /*if (start == 0)*/ System.err.print("\r\t" + (j + 1) + "/" + images.size() + " "); 100 | } 101 | } 102 | }; 103 | threads[i].start(); 104 | threads[i].setPriority(Thread.MIN_PRIORITY); 105 | } 106 | for (int i = 0; i < numThreads; i++) { 107 | threads[i].join(); 108 | } 109 | System.err.println(""); 110 | System.err.println("\tPoly Match Samples: " + polyMatchValues.size()); 111 | System.err.println("\t Elapsed Time: " + (System.currentTimeMillis() - t) + " ms"); 112 | System.err.println(); 113 | } catch (Exception e) { 114 | e.printStackTrace(); 115 | } 116 | } 117 | 118 | private void processImage(File imageFile3band, File imageFile8band, List groundTruthBuildings) { 119 | try { 120 | BufferedImage img3band = ImageIO.read(imageFile3band); 121 | BufferedImage img8band = ImageIO.read(imageFile8band); 122 | MultiChannelImage mci = new MultiChannelImage(img3band, img8band); 123 | double[][][] vals = Util.evalImage(mci, buildingPredictor, borderPredictor, distPredictor); 124 | double[][] buildingValues = vals[0]; 125 | double[][] borderValues = vals[1]; 126 | double[][] distValues = vals[2]; 127 | 128 | List candidates = new ArrayList(); 129 | for (int borderShift : PolygonFeatureExtractor.borderShifts) { 130 | for (double borderWeight : PolygonFeatureExtractor.borderWeights) { 131 | for (double cut : PolygonFeatureExtractor.buildingsCuts) { 132 | candidates.addAll(Util.findBuildings(buildingValues, borderValues, distValues, cut, borderWeight, PolygonFeatureExtractor.buildingsPolyBorder+borderShift, PolygonFeatureExtractor.buildingsPolyBorder2+borderShift)); 133 | } 134 | } 135 | } 136 | for (Polygon polygon : candidates) { 137 | float value = (float) iou(polygon, groundTruthBuildings); 138 | float[] features = PolygonFeatureExtractor.getFeatures(mci, polygon, buildingValues, borderValues, distValues); 139 | synchronized (polyMatchValues) { 140 | polyMatchValues.add(value); 141 | polyMatchFeatures.add(features); 142 | } 143 | } 144 | /* 145 | int w = mci.width; 146 | int h = mci.height; 147 | BufferedImage img2 = new BufferedImage(w * 2, h, BufferedImage.TYPE_INT_BGR); 148 | Graphics2D g = img2.createGraphics(); 149 | for (int y = 0; y < h; y++) { 150 | for (int x = 0; x < w; x++) { 151 | //int v = (int) (255 * buildingValue[y][x]) + 256 * 256 * (int) (255 * borderValue[y][x]); 152 | int v = Color.HSBtoRGB((float)(distValue[y][x]+10)/(float)(30+1) + 0.5f, 1, 1); 153 | img2.setRGB(x, y, v); 154 | img2.setRGB(x + w, y, img3band.getRGB(x, y)); 155 | } 156 | } 157 | g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); 158 | g.setStroke(new BasicStroke(0.5f)); 159 | //for (Building building : groundTruthBuildings) { 160 | //Polygon poly = building.in.get(0); 161 | // g.setColor(Color.green); 162 | //g.draw(poly); 163 | //g.setColor(Color.yellow); 164 | //g.draw(building.getRectPolygon()); 165 | //} 166 | 167 | for (int i = 0; i < candidates.size(); i++) { 168 | g.setColor(Color.green); 169 | //g.draw(candidates.get(i).poly); 170 | g.setColor(Color.yellow); 171 | //g.draw(inner(candidates.get(i).poly)); 172 | //System.err.println(i + "\t" + eval(candidates.get(i).poly, w, h, buildingValue, borderValue, true)); 173 | } 174 | 175 | //Polygon poly = new Polygon(new int[] {281,356,380,305}, new int[] {166,332,323,158}, 4); 176 | //g.setColor(Color.green); 177 | //g.draw(poly); 178 | //g.setColor(Color.yellow); 179 | //g.draw(inner(poly)); 180 | //System.err.println(">>\t" + eval(poly, w, h, buildingValue, borderValue, true)); 181 | 182 | g.dispose(); 183 | new ImgViewer(img2); 184 | */ 185 | } catch (Exception e) { 186 | e.printStackTrace(); 187 | } 188 | } 189 | 190 | private double iou(Polygon poly, List buildings) { 191 | double best = 0; 192 | Area polyArea = new Area(poly); 193 | double polyAreaVal = Util.areaVal(polyArea); 194 | Rectangle polyRect = polyArea.getBounds(); 195 | for (Building building : buildings) { 196 | Area buildingArea = building.getArea(); 197 | Rectangle buildingRect = buildingArea.getBounds(); 198 | if (!buildingRect.intersects(polyRect)) continue; 199 | Area interArea = new Area(polyArea); 200 | interArea.intersect(buildingArea); 201 | double a = Util.areaVal(interArea); 202 | double curr = a / (polyAreaVal + building.getAreaVal() - a); 203 | if (curr > best) best = curr; 204 | } 205 | return best; 206 | } 207 | 208 | private void loadPredictors(File rfBuilding, File rfBorder, File rfDist) { 209 | try { 210 | System.err.println(" Loading Predictors..."); 211 | long t = System.currentTimeMillis(); 212 | buildingPredictor = RandomForestPredictor.load(rfBuilding, -1); 213 | borderPredictor = RandomForestPredictor.load(rfBorder, -1); 214 | distPredictor = RandomForestPredictor.load(rfDist, -1); 215 | System.err.println("\t Elapsed Time: " + (System.currentTimeMillis() - t) + " ms"); 216 | System.err.println(); 217 | } catch (Exception e) { 218 | e.printStackTrace(); 219 | } 220 | } 221 | } -------------------------------------------------------------------------------- /3-nofto/src/RandomForestBuilder.java: -------------------------------------------------------------------------------- 1 | 2 | import java.io.File; 3 | 4 | public class RandomForestBuilder { 5 | public static RandomForestPredictor train(final float[][] features, final boolean[] classif, final int maxTrees, final File out, final int maxThreads) { 6 | final RandomForestPredictor rf = new RandomForestPredictor(maxTrees); 7 | 8 | final int numThreads = Math.min(maxThreads, Runtime.getRuntime().availableProcessors()); 9 | Thread[] threads = new Thread[numThreads]; 10 | for (int i = 0; i < numThreads; i++) { 11 | final int idx = i; 12 | threads[i] = new Thread() { 13 | public void run() { 14 | for (int i = idx; i < maxTrees; i += numThreads) { 15 | ClassificationNode root = new ClassificationTree(features, classif, i, maxTrees).getRoot(); 16 | synchronized (rf) { 17 | rf.add(root); 18 | if (idx == 0) { 19 | try { 20 | rf.save(out); 21 | } catch (Exception e) { 22 | e.printStackTrace(); 23 | } 24 | } 25 | } 26 | } 27 | } 28 | }; 29 | threads[i].start(); 30 | threads[i].setPriority(Thread.MIN_PRIORITY); 31 | } 32 | try { 33 | for (int i = 0; i < numThreads; i++) { 34 | threads[i].join(); 35 | } 36 | } catch (InterruptedException e) { 37 | } 38 | try { 39 | rf.save(out); 40 | } catch (Exception e) { 41 | e.printStackTrace(); 42 | } 43 | return rf; 44 | } 45 | 46 | public static RandomForestPredictor train(final float[][] features, final float[] values, final int maxTrees, final File out, final int maxThreads) { 47 | final RandomForestPredictor rf = new RandomForestPredictor(maxTrees); 48 | final int numThreads = Math.min(maxThreads, Runtime.getRuntime().availableProcessors()); 49 | Thread[] threads = new Thread[numThreads]; 50 | for (int i = 0; i < numThreads; i++) { 51 | final int idx = i; 52 | threads[i] = new Thread() { 53 | public void run() { 54 | for (int i = idx; i < maxTrees; i += numThreads) { 55 | ClassificationNode root = new ClassificationTree(features, values, i, maxTrees).getRoot(); 56 | synchronized (rf) { 57 | rf.add(root); 58 | if (idx == 0) { 59 | try { 60 | rf.save(out); 61 | } catch (Exception e) { 62 | e.printStackTrace(); 63 | } 64 | } 65 | } 66 | } 67 | } 68 | }; 69 | threads[i].start(); 70 | threads[i].setPriority(Thread.MIN_PRIORITY); 71 | } 72 | try { 73 | for (int i = 0; i < numThreads; i++) { 74 | threads[i].join(); 75 | } 76 | } catch (InterruptedException e) { 77 | } 78 | try { 79 | rf.save(out); 80 | } catch (Exception e) { 81 | e.printStackTrace(); 82 | } 83 | return rf; 84 | } 85 | } -------------------------------------------------------------------------------- /3-nofto/src/RandomForestPredictor.java: -------------------------------------------------------------------------------- 1 | 2 | import java.io.BufferedInputStream; 3 | import java.io.BufferedOutputStream; 4 | import java.io.DataInputStream; 5 | import java.io.DataOutputStream; 6 | import java.io.File; 7 | import java.io.FileInputStream; 8 | import java.io.FileOutputStream; 9 | 10 | public class RandomForestPredictor { 11 | private final int[] roots; 12 | private final int[] nodeLeft; 13 | private final short[] splitFeature; 14 | private final float[] value; 15 | private int trees, free; 16 | 17 | public RandomForestPredictor(int maxTrees) { 18 | roots = new int[maxTrees]; 19 | int maxNodes = maxTrees * (1 << 20); 20 | nodeLeft = new int[maxNodes]; 21 | splitFeature = new short[maxNodes]; 22 | value = new float[maxNodes]; 23 | } 24 | 25 | private RandomForestPredictor(int trees, int nodes) { 26 | this.trees = trees; 27 | this.free = nodes; 28 | roots = new int[trees]; 29 | nodeLeft = new int[nodes]; 30 | splitFeature = new short[nodes]; 31 | value = new float[nodes]; 32 | } 33 | 34 | public synchronized void add(ClassificationNode root) { 35 | int rt = roots[trees++] = free; 36 | free++; 37 | expand(root, rt); 38 | } 39 | 40 | public int size() { 41 | return trees; 42 | } 43 | 44 | private void expand(ClassificationNode node, int pos) { 45 | if (node.left == null) { 46 | nodeLeft[pos] = -1; 47 | splitFeature[pos] = -1; 48 | value[pos] = node.getValue(); 49 | } else { 50 | int l = nodeLeft[pos] = free; 51 | free += 2; 52 | splitFeature[pos] = (short) node.splitFeature; 53 | value[pos] = node.splitVal; 54 | expand(node.left, l); 55 | expand(node.right, l + 1); 56 | } 57 | } 58 | 59 | public double predict(float[] features) { 60 | double ret = 0; 61 | for (int root : roots) { 62 | ret += classify(root, features); 63 | } 64 | return ret / roots.length; 65 | } 66 | 67 | private double classify(int pos, float[] features) { 68 | while (true) { 69 | int sf = splitFeature[pos]; 70 | if (sf < 0) return value[pos]; 71 | if (features[sf] < value[pos]) pos = nodeLeft[pos]; 72 | else pos = nodeLeft[pos] + 1; 73 | } 74 | } 75 | 76 | public void save(File file) throws Exception { 77 | DataOutputStream out = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(file))); 78 | out.writeInt(trees); 79 | for (int i = 0; i < trees; i++) { 80 | out.writeInt(roots[i]); 81 | } 82 | out.writeInt(free); 83 | for (int i = 0; i < free; i++) { 84 | out.writeShort(splitFeature[i]); 85 | out.writeInt(nodeLeft[i]); 86 | out.writeFloat(value[i]); 87 | } 88 | out.close(); 89 | } 90 | 91 | public static RandomForestPredictor load(File file, int maxTrees) throws Exception { 92 | DataInputStream in = new DataInputStream(new BufferedInputStream(new FileInputStream(file))); 93 | int trees = in.readInt(); 94 | int[] t = new int[trees]; 95 | for (int i = 0; i < trees; i++) { 96 | t[i] = in.readInt(); 97 | } 98 | int nodes = in.readInt(); 99 | if (maxTrees > 0 && trees > maxTrees) { 100 | trees = maxTrees; 101 | nodes = t[trees + 1]; 102 | } 103 | RandomForestPredictor predictor = new RandomForestPredictor(trees, nodes); 104 | System.arraycopy(t, 0, predictor.roots, 0, trees); 105 | byte[] bytes = new byte[nodes * 10]; 106 | in.read(bytes); 107 | 108 | int pos = 0; 109 | for (int i = 0; i < nodes; i++) { 110 | predictor.splitFeature[i] = (short) (((bytes[pos++] & 0xFF) << 8) + ((bytes[pos++] & 0xFF) << 0)); 111 | predictor.nodeLeft[i] = (((bytes[pos++] & 0xFF) << 24) + ((bytes[pos++] & 0xFF) << 16) + ((bytes[pos++] & 0xFF) << 8) + ((bytes[pos++] & 0xFF) << 0)); 112 | predictor.value[i] = Float.intBitsToFloat((((bytes[pos++] & 0xFF) << 24) + ((bytes[pos++] & 0xFF) << 16) + ((bytes[pos++] & 0xFF) << 8) + ((bytes[pos++] & 0xFF) << 0))); 113 | } 114 | in.close(); 115 | return predictor; 116 | } 117 | } -------------------------------------------------------------------------------- /3-nofto/test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | array=( $@ ) 3 | len=${#array[@]} 4 | _outfile=${array[$len-1]} 5 | _args=${array[@]:0:$len-1} 6 | 7 | [ -e "$_outfile" ] && rm "$_outfile" 8 | 9 | for var in $_args 10 | do 11 | ./test_one_city.sh "$var" "$_outfile" 12 | done 13 | -------------------------------------------------------------------------------- /3-nofto/test_one_city.sh: -------------------------------------------------------------------------------- 1 | java -Xmx160g -cp "./bin:./lib/*" BuildingDetectorTester "$1" "$2" 2 | -------------------------------------------------------------------------------- /3-nofto/train.sh: -------------------------------------------------------------------------------- 1 | for var in "$@" 2 | do 3 | ./train_one_city_a.sh "$var" 4 | ./train_one_city_b.sh "$var" 5 | done 6 | -------------------------------------------------------------------------------- /3-nofto/train_one_city_a.sh: -------------------------------------------------------------------------------- 1 | java -Xmx160g -cp "./bin:./lib/*" BuildingDetectorTrainer "$1" 2 | -------------------------------------------------------------------------------- /3-nofto/train_one_city_b.sh: -------------------------------------------------------------------------------- 1 | java -Xmx160g -cp "./bin:./lib/*" PolygonMatcherTrainer "$1" 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2017 DigitalGlobe Intelligence Solutions, Inc, All Rights Reserved. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## SpaceNet Round 2 Challenge Implementations 2 | For more information about the SpaceNet Challenge visit the [SpaceNet Challenge Website](https://spacenetchallenge.github.io/) 3 | 4 | ### [Read the Blog Post](https://medium.com/the-downlinq/2nd-spacenet-competition-winners-code-release-c7473eea7c11) 5 | 6 | 7 | ### Competition Specific information: 8 | #### [SpaceNet Challenges hosted on TopCoder](http://crowdsourcing.topcoder.com/spacenet) 9 | #### [Topcoder Competition Page](https://community.topcoder.com/longcontest/?module=ViewProblemStatement&rd=16892&pm=14551) 10 | Winning solutions: 11 | 12 | 1. [XD_XD](https://github.com/SpaceNetChallenge/BuildingDetectors_Round2/tree/master/1-XD_XD) 13 | 2. [wleite](https://github.com/SpaceNetChallenge/BuildingDetectors_Round2/tree/master/2-wleite) 14 | 3. [nofto](https://github.com/SpaceNetChallenge/BuildingDetectors_Round2/tree/master/3-nofto) 15 | 16 | 17 | For round 1 results please see the [Round 1 Repository](https://github.com/SpaceNetChallenge/BuildingDetectors) 18 | -------------------------------------------------------------------------------- /solution_descriptions/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SpaceNetChallenge/BuildingDetectors_Round2/04778fdcdd95cc395ebc7e73942eda58143afb42/solution_descriptions/.DS_Store -------------------------------------------------------------------------------- /solution_descriptions/1-XD_XD.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SpaceNetChallenge/BuildingDetectors_Round2/04778fdcdd95cc395ebc7e73942eda58143afb42/solution_descriptions/1-XD_XD.docx -------------------------------------------------------------------------------- /solution_descriptions/2-wleite.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SpaceNetChallenge/BuildingDetectors_Round2/04778fdcdd95cc395ebc7e73942eda58143afb42/solution_descriptions/2-wleite.docx -------------------------------------------------------------------------------- /solution_descriptions/3-nofto.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SpaceNetChallenge/BuildingDetectors_Round2/04778fdcdd95cc395ebc7e73942eda58143afb42/solution_descriptions/3-nofto.docx --------------------------------------------------------------------------------