├── HoughTransform_lib.c ├── HoughTransform_lib.so ├── LICENSE ├── README.md ├── africa ├── .ipynb_checkpoints │ └── 9_preprocessing_road_and_building_data-checkpoint.ipynb ├── 10_road_modeling_and_pole_prediction.py ├── 11_dijkstria_algorithm.py ├── 12_line_prediction_test_overall.py ├── 1_search_area_GSV.py ├── 2_download_area_GSV.py ├── 3_predict_line_CAM_pytorch.py ├── 4_CAM_to_line_directions.py ├── 5_merge_line_directions.py ├── 6_predict_pole_CAM_pytorch.py ├── 7_CAM_to_pole_LOBs.py ├── 8_LOB_to_pole_locations.py ├── 9_preprocessing_road_and_building_data.ipynb ├── HoughTransform_lib.c ├── HoughTransform_lib.so ├── __init__.py ├── __pycache__ │ ├── census_tract_locator.cpython-36.pyc │ ├── dijkstra.cpython-36.pyc │ ├── evaluation_utils.cpython-36.pyc │ ├── feature_engineering.cpython-36.pyc │ ├── image_dataset.cpython-36.pyc │ ├── inception_modified.cpython-36.pyc │ ├── region_locator.cpython-36.pyc │ ├── streetView.cpython-36.pyc │ └── utils.cpython-36.pyc ├── census_tract_locator.py ├── dijkstra.py ├── evaluation_utils.py ├── feature_engineering.py ├── image_dataset.py ├── inception_modified.py ├── region_locator.py ├── streetView.py └── utils.py ├── california ├── .ipynb_checkpoints │ ├── 12_line_prediction_model_development-checkpoint.ipynb │ └── 9_preprocessing_road_and_building_data-checkpoint.ipynb ├── 10_road_modeling_and_pole_prediction.py ├── 11_dijkstria_algorithm.py ├── 12_line_prediction_model_development.ipynb ├── 12_line_prediction_test_overall.py ├── 13_connect_underground.py ├── 1_search_area_GSV.py ├── 2_download_area_GSV.py ├── 3_predict_line_CAM_pytorch.py ├── 4_CAM_to_line_directions.py ├── 5_merge_line_directions.py ├── 6_predict_pole_CAM_pytorch.py ├── 7_CAM_to_pole_LOBs.py ├── 8_LOB_to_pole_locations.py ├── 9_preprocessing_road_and_building_data.ipynb ├── HoughTransform_lib.c ├── HoughTransform_lib.so ├── __init__.py ├── __pycache__ │ ├── census_tract_locator.cpython-36.pyc │ ├── dijkstra.cpython-36.pyc │ ├── evaluation_utils.cpython-36.pyc │ ├── feature_engineering.cpython-36.pyc │ ├── image_dataset.cpython-36.pyc │ ├── inception_modified.cpython-36.pyc │ └── utils.cpython-36.pyc ├── census_tract_locator.py ├── dijkstra.py ├── evaluation_utils.py ├── feature_engineering.py ├── image_dataset.py ├── inception_modified.py ├── model_selection.py ├── streetView.py └── utils.py ├── model_dev ├── __init__.py ├── __pycache__ │ ├── image_dataset.cpython-36.pyc │ └── inception_modified.cpython-36.pyc ├── image_dataset.py ├── inception_modified.py ├── test_CAM_branch_pytorch.py ├── test_classification_pytorch.py ├── train_CAM_branch_pytorch.py └── train_classification_pytorch.py └── requirements.txt /HoughTransform_lib.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #define M_PI 3.14159265358979323846 4 | #define IMGSIZE 77 5 | #define CAMSIZE (IMGSIZE * IMGSIZE) 6 | #define THETARANGE 180 7 | #define LINEMAX 10 8 | #define maskDepth 9999 9 | #define maskWidth 3 10 | 11 | #ifdef _MSC_VER 12 | #define DLL_EXPORT __declspec(dllexport) 13 | #else 14 | #define DLL_EXPORT 15 | #endif 16 | 17 | double *cos_t, *sin_t; 18 | double *thetas, *rhos; 19 | int* accumulator; 20 | int theta_len, diag_len; 21 | int weight, height; 22 | double* cam; 23 | int* cam_regular; 24 | double* lines; 25 | int lines_len; 26 | 27 | struct Points { 28 | int x1, y1, x2, y2; 29 | }; 30 | 31 | struct Points polar2cart(double rho, double theta) { 32 | double a = cos(theta); 33 | double b = sin(theta); 34 | double x0 = a * rho; 35 | double y0 = b * rho; 36 | struct Points p; 37 | p.x1 = (int)(x0 + 1000 * (-b)); 38 | p.y1 = (int)(y0 + 1000 * (a)); 39 | p.x2 = (int)(x0 - 1000 * (-b)); 40 | p.y2 = (int)(y0 - 1000 * (a)); 41 | return p; 42 | } 43 | 44 | int getMaxRank(int* array, int len) { 45 | int rank = 0; 46 | int maxValue = array[0]; 47 | for (int i = 1; i < len; i++) { 48 | if (array[i] > maxValue) { 49 | maxValue = array[i]; 50 | rank = i; 51 | } 52 | } 53 | return rank; 54 | } 55 | 56 | int getMax(int* array, int len) { 57 | return array[getMaxRank(array, len)]; 58 | } 59 | 60 | void CAMRegular() { 61 | int level1 = 10; 62 | int level2 = 1; 63 | int level1_thre = 80; 64 | int level2_thre = 10; 65 | for (int i = 0; i < CAMSIZE; i++) { 66 | if (cam[i] > level1_thre) 67 | cam_regular[i] = level1; 68 | else if (cam[i] > level2_thre) 69 | cam_regular[i] = level2; 70 | else 71 | cam_regular[i] = 0; 72 | } 73 | } 74 | 75 | int capsule(double px, 76 | double py, 77 | double ax, 78 | double ay, 79 | double bx, 80 | double by, 81 | double r) { 82 | double pax = px - ax, pay = py - ay, bax = bx - ax, bay = by - ay; 83 | double h = fmaxf( 84 | fminf((pax * bax + pay * bay) / (bax * bax + bay * bay), 1.0f), 0.0f); 85 | double dx = pax - bax * h, dy = pay - bay * h; 86 | return dx * dx + dy * dy < r * r; 87 | } 88 | 89 | void lineMask(double* img, struct Points pts, int width) { 90 | double* p = img; 91 | for (int y = 0; y < IMGSIZE; y++) 92 | for (int x = 0; x < IMGSIZE; x++, p += 1) 93 | *p = *p - capsule(x, y, pts.x1, pts.y1, pts.x2, pts.y2, width) * 94 | maskDepth; 95 | return; 96 | } 97 | 98 | DLL_EXPORT void init() { 99 | theta_len = THETARANGE + 1; 100 | weight = IMGSIZE; 101 | height = IMGSIZE; 102 | diag_len = (int)(ceil(sqrt(weight * weight + height * height))); 103 | thetas = (double*)malloc(sizeof(double) * theta_len); 104 | cos_t = (double*)malloc(sizeof(double) * theta_len); 105 | sin_t = (double*)malloc(sizeof(double) * theta_len); 106 | rhos = (double*)malloc(sizeof(double) * 2 * diag_len); 107 | accumulator = (int*)malloc(sizeof(int) * 2 * diag_len * theta_len); 108 | cam = (double*)malloc(sizeof(double) * CAMSIZE); 109 | cam_regular = (int*)malloc(sizeof(int) * CAMSIZE); 110 | lines = (double*)malloc(sizeof(double) * 3 * LINEMAX); 111 | for (int i = 0; i < theta_len; i++) { 112 | thetas[i] = (i - 90) * M_PI / 180; 113 | cos_t[i] = cos(thetas[i]); 114 | sin_t[i] = sin(thetas[i]); 115 | } 116 | double rhos_delta = 2 * diag_len / (2 * diag_len - 1); 117 | for (int i = 0; i < 2 * diag_len; i++) 118 | rhos[i] = -diag_len + i * rhos_delta; 119 | } 120 | 121 | DLL_EXPORT int* HoughTransform(int* img) { 122 | for (int i = 0; i < 2 * diag_len; i++) 123 | for (int j = 0; j < theta_len; j++) 124 | accumulator[i * theta_len + j] = 0; 125 | 126 | for (int x = 0; x < weight; x++) 127 | for (int y = 0; y < height; y++) { 128 | if (img[y * weight + x] == 0) 129 | return; 130 | for (int t = 0; t < theta_len; t++) { 131 | int rho = (int)(round(x * cos_t[t] + y * sin_t[t])) + diag_len; 132 | accumulator[rho * theta_len + t] += img[y * weight + x]; 133 | } 134 | } 135 | return accumulator; 136 | } 137 | DLL_EXPORT double* getth() { 138 | return thetas; 139 | } 140 | DLL_EXPORT double* getrh() { 141 | return rhos; 142 | } 143 | DLL_EXPORT int getthlen() { 144 | return theta_len; 145 | } 146 | DLL_EXPORT int getrhlen() { 147 | return 2 * diag_len; 148 | } 149 | DLL_EXPORT void destory() { 150 | free(thetas); 151 | free(sin_t); 152 | free(cos_t); 153 | free(rhos); 154 | free(accumulator); 155 | free(cam); 156 | free(cam_regular); 157 | free(lines); 158 | } 159 | 160 | DLL_EXPORT int cam2lines(double* cam_data) { 161 | for (int i = 0; i < CAMSIZE; i++) 162 | cam[i] = cam_data[i]; 163 | // memcpy(cam, cam_data, sizeof(double) * CAMSIZE); 164 | lines_len = 0; 165 | int line_thre = 80; 166 | for (int i = 0; i < LINEMAX; i++) { 167 | CAMRegular(); 168 | int* hf = HoughTransform(cam_regular); 169 | if (getMax(hf, 2 * diag_len * theta_len) < line_thre) 170 | break; 171 | int hfLine = getMaxRank(hf, 2 * diag_len * theta_len); 172 | int hfLine_r = hfLine / theta_len; 173 | int hfLine_t = hfLine % theta_len; 174 | double rho = rhos[hfLine_r]; 175 | double theta = thetas[hfLine_t]; 176 | lines[i * 3 + 0] = rho; 177 | lines[i * 3 + 1] = theta; 178 | lines[i * 3 + 2] = hf[hfLine]; 179 | lines_len++; 180 | struct Points p = polar2cart(rho, theta); 181 | lineMask(cam, p, maskWidth); 182 | } 183 | return lines_len; 184 | } 185 | 186 | DLL_EXPORT double* getlines() { 187 | return lines; 188 | } 189 | -------------------------------------------------------------------------------- /HoughTransform_lib.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangzhecheng/GridMapping/e5f6360132dea5b67c97a6fb0e82fe85d3f63352/HoughTransform_lib.so -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Zhecheng Wang 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Geospatial Mapping of Distribution Grid with Machine Learning and Publicly-Accessible Multi-Modal Data 2 | 3 | This is the code repository for the following paper. To use this repo, please cite: 4 | 5 | * Wang, Z., Majumdar, A., & Rajagopal, R. (2023). Geospatial Mapping of Distribution Grid with Machine Learning and Publicly-Accessible Multi-Modal Data. To appear in Nature Communications. 6 | 7 | The operating system for developing this code repo is Ubuntu 16.04, but it should also be able to run in other environments. The Python version used for developing this code repo is Python 3.6. 8 | 9 | ## Install required packages 10 | 11 | Run the following command line: 12 | 13 | ``` 14 | $ pip install -r requirements.txt 15 | ``` 16 | 17 | ## Download data and model checkpoints 18 | 19 | Run the following command lines to download the ZIP files right under the code repo directory. Alternatively, they can be downloaded from the Figshare repository https://doi.org/10.6084/m9.figshare.22723171. 20 | 21 | ``` 22 | $ curl -O https://opendatasharing.s3.us-west-2.amazonaws.com/GridMapping/checkpoint.zip 23 | $ curl -O https://opendatasharing.s3.us-west-2.amazonaws.com/GridMapping/data.zip 24 | $ curl -O https://opendatasharing.s3.us-west-2.amazonaws.com/GridMapping/results.zip 25 | $ curl -O https://opendatasharing.s3.us-west-2.amazonaws.com/GridMapping/ground_truth.zip 26 | ``` 27 | 28 | Unzip them such that the directory structure looks like: 29 | 30 | ``` 31 | GridMapping/checkpoint/... 32 | GridMapping/data/... 33 | GridMapping/results/... 34 | GridMapping/ground_truth/... 35 | ``` 36 | 37 | **Note 1**: To run Jupyter Notebook, the default kernel/environment is "conda_tensorflow_p36", which does not necessarily exist in your computer. Please change the kernel to the one where all required packages are installed. 38 | 39 | **Note 2**: Street view images in the test areas are not shared directly due to the restriction of image data source, but can be retrieved by running the code in this code repo. 40 | 41 | ## Functionality of each script/notebook 42 | 43 | ### Model development (CNN model training and testing) 44 | 45 | This part is under `model_dev`. It is for training and testing the classification and Class Activation Map (CAM) generation for both line detector and pole detector. First, go to the directory: 46 | 47 | ``` 48 | $ cd model_dev 49 | ``` 50 | 51 | Below are the functionalities for each of the scripts. All scripts are run on a Nvidia Tesla K80 GPU by default. For each script, set `target = "line"` for line detection and `target = "pole"` for pole detection. 52 | 53 | `test_classification_pytorch.py`: This script is for running the line/pole classification model on the street view image test set and reporting the image-level metrics including precision and recall. For line identification, the expected image-level precision and recall under the given threshold 0.5 are 0.982 and 0.937; for pole identificaion, the expected image-level precision and recall under the given threshold 0.5 are 0.982 and 0.850, respectively. It takes ~1 min to run on a Nvidia Tesla K80 GPU. 54 | 55 | `train_classification_pytorch.py`: This script is for training the line/pole classification model on the street view image training set and saving the model with the best performance on the validation set. A pre-trained model can be specified. The default pre-trained model is the model pretrained on ImageNet dataset: `checkpoint/inception_v3_google-1a9a5a14.pth`. 56 | 57 | `train_CAM_branch_pytorch.py`: This script is for training the CAM branch of the line/pole model on the street view image training set and saving the model with the best performance on the validation set. CAM branch is used for estimating pole orientation/line directions and it is trained when the main branch (classification branch) is frozen. It must take a classification model checkpoint outputted by `train_classification_pytorch.py` as input for model initialization. 58 | 59 | `test_CAM_branch_pytorch.py`: This script is for running the line/pole model on the street view image test set to generate the Class Activation Maps (CAMs) for lines/poles. The CAMs will be saved into a pickle file at the given destination for further visualization. 60 | 61 | ### Model running and benchmarking (for California or Africa test areas) 62 | 63 | For model running and benchmarking in the California test areas: San Carlos (`SanCarlos`), Newark (`Newark`), Santa Cruz (`SantaCruz`), Yuba City (`Yuba`), Pacific Grove (`Monterey`), and Salinas (`Salinas`): 64 | 65 | ``` 66 | $ cd california 67 | ``` 68 | 69 | For model running and benchmarking in the Sub-Saharan Africa (SSA) test areas: Ntinda, Kampala, Uganda (`Kampala_Ntinda`), Kololo, Kampala, Uganda (`Kampala_Kololo`), Highridge, Nairobi, Kenya (`Nairobi_Highridge`), Ngara, Nairobi, Kenya (`Nairobi_Ngara`), Ikeja, Lagos, Nigeria (`Lagos_Ikeja2`). 70 | 71 | ``` 72 | $ cd africa 73 | ``` 74 | 75 | They share the same pipeline for model running and benchmarking: 76 | 77 | #### Step 1: Retrieve the meta data of street view images 78 | A prerequisite of retrieving street view imagery meta data (presence or absence of street view images at a given location) is Google API keys. Please go to [Google Cloud Console](https://console.cloud.google.com/) to create private API keys (which is associated with Google account and cannot be shared). Add API key strings to the placeholder in `streetView.py`. Given a region (specified by its name, e.g., "Salinas"), we can run the following script for retrieving the street view meta data in this region. 79 | 80 | ``` 81 | $ python 1_search_area_GSV.py 82 | ``` 83 | 84 | #### Step 2: Get street view images 85 | This is to get available street view images in a given region (e.g., "Salinas"). 86 | ``` 87 | $ python 2_download_area_GSV.py 88 | ``` 89 | Note that API keys are also required for this step. 90 | 91 | #### Step 3: Line detection and CAM generation 92 | This script is for running the line detetor on street view images. Classification results and CAMs are generated and saved: 93 | ``` 94 | $ python 3_predict_line_CAM_pytorch.py 95 | ``` 96 | 97 | #### Step 4: Extract line directions 98 | This script is for using Hough transform to extract line directions from the CAMs generated in step 3: 99 | ``` 100 | $ python 4_CAM_to_line_directions.py 101 | ``` 102 | 103 | #### Step 5: Merge similar line directions 104 | This script is used for merging similar line directions (i.e., parallel power lines with different phases) estimated in each CAM: 105 | ``` 106 | $ python 5_merge_line_directions.py 107 | ``` 108 | 109 | #### Step 6: Pole detection and CAM generation 110 | This script is for running the pole detetor on street view images. Classification results and CAMs are generated and saved: 111 | ``` 112 | $ python 6_predict_pole_CAM_pytorch.py 113 | ``` 114 | 115 | #### Step 7: Extract pole orientations 116 | This script is for extracting pole orientations from the CAMs generated in step 6 and obtaining the Line of Bearings (LOBs, i.e., rays that represent pole orientations): 117 | ``` 118 | $ python 7_CAM_to_pole_LOBs.py 119 | ``` 120 | 121 | #### Step 8: Pole localization 122 | This script is for intersecting the Lines of Bearings (LOBs) obtained from step 7 to obtain the pole locations. 123 | ``` 124 | $ python 8_LOB_to_pole_locations.py 125 | ``` 126 | 127 | #### Step 9: Pre-processing road and building data 128 | Run the Jupyter Notebook `9_preprocessing_road_and_building_data.ipynb` for filtering roads and buildings that are located in the given region. Two road segments are merged into a single segment if there is no road intersection between them. This is used for telling whether two detected poles are along the same roads. 129 | 130 | #### Step 10: Attach poles to nearby roads and generate the map of poles 131 | This script is for attaching detected poles to the nearby road when there is a nearby road to that pole, and potentially inserting additional poles between two predicted poles that are too far apart in order to reduce the number of poles missed by the pole detection model. It finally generates the geospatial map of utility poles. 132 | ``` 133 | $ python 10_road_modeling_and_pole_prediction.py 134 | ``` 135 | 136 | #### Step 11: Dijkstra's algorithm to connect poles 137 | This script is for running the modified Dijkstra's algorithm to predict line connections between poles. This algorithm greedily seeks the paths to connect all predicted poles with minimum total weight. Each cell on the raster map is assigned with a weight. The Dijkstra's algorithm is adapted from: https://github.com/facebookresearch/many-to-many-dijkstra. The prediction of this algorithm is used as a feature input to the link prediction model. 138 | ``` 139 | $ python 11_dijkstria_algorithm.py 140 | ``` 141 | 142 | #### Step 12: Link prediction 143 | This script is for using link prediction model to predict whether there is a power line between two utility poles by leveraging the predicted pole/line information as well as road information. 144 | ``` 145 | $ python 12_line_prediction_test_overall.py 146 | ``` 147 | New link prediction models can be trained and compared with cross-validation by running the Jupyter Notebook `12_line_prediction_model_development.ipynb` under `california` directory (using "San Carlos" as a development set). However, training new models are not required for running the above script as the models used in the paper are already provided. 148 | 149 | #### Step 13: Predict underground grids 150 | This script is for predicting the underground grid on top of the predicted overhead grid by leveraging the modified Dijkstra's algorithm. This algorithm greedily seeks the paths with minimum total weight to connect all buildings that cannot be reached by predicted overhead grid within a certain distance. Each cell in the raster is assigned with a weight. The Dijkstra's algorithm is adapted from: https://github.com/facebookresearch/many-to-many-dijkstra. The entire predicted grid map (overhead + underground) are benchmarked against the ground truth data. 151 | ``` 152 | $ python 13_connect_underground.py 153 | ``` 154 | **Note**: step 13 does not apply to the Sub-Saharan Africa test areas because the 100% building electricity access assumption does not necessarily hold in Sub-Saharan Africa. 155 | 156 | 157 | 158 | 159 | 160 | 161 | -------------------------------------------------------------------------------- /africa/1_search_area_GSV.py: -------------------------------------------------------------------------------- 1 | import math 2 | from matplotlib.path import Path 3 | import matplotlib.pyplot as plt 4 | import numpy as np 5 | import pickle 6 | import os 7 | from os.path import join, exists 8 | from tqdm import tqdm 9 | from streetView import StreetView 10 | 11 | from shapely.geometry import Polygon, Point 12 | from shapely.ops import cascaded_union 13 | import shapely 14 | 15 | """ 16 | This script is for retrieving the street view meta data for each test area. 17 | """ 18 | 19 | region = 'Kampala_Kololo' 20 | start_i = 0 21 | 22 | def determine_root_dir(): 23 | """ 24 | This function is used to locate the root dir back to the parent directory, 25 | i.e., "GridMapping" directory. 26 | """ 27 | root_dir = os.path.abspath(os.path.join(os.getcwd(), os.pardir)) 28 | assert root_dir.strip('/')[-11:] == 'GridMapping' 29 | return root_dir 30 | 31 | root_dir = determine_root_dir() 32 | # change the root dir to "GridMapping" 33 | os.chdir(root_dir) 34 | 35 | fips_list_dict = { 36 | 'Kampala_Kololo': ['U000005', 'U000006', 'U000007', 'U000012'], 37 | 'Nairobi_Highridge': ['K101060303'], 38 | 'Kampala_Ntinda': ['U000069'], 39 | 'Nairobi_Ngara': ['K101010501', 'K101010502'], 40 | } 41 | 42 | with open('data/boundary_list_NigeriaGhana_2.pickle', 'rb') as f: 43 | fips_list_dict_2 = pickle.load(f) 44 | for x in fips_list_dict_2: 45 | if not x in fips_list_dict: 46 | fips_list_dict[x] = fips_list_dict_2[x] 47 | 48 | boundaries_keys = fips_list_dict[region] 49 | 50 | 51 | if not exists(join('results', region)): 52 | os.mkdir(join('results', region)) 53 | 54 | boundaries_file_filepath = 'data/africa_boundaries.pickle' 55 | GSV_points_filepath = join('results', region, 'validGSVpoints.pickle') 56 | fips_mapping_path = join('results', region, 'GSVpoint2fips.pickle') 57 | save_per_points = 5000 58 | 59 | # %% set boundary paths 60 | with open(boundaries_file_filepath, 'rb') as f: 61 | boundaries_raw = pickle.load(f, encoding='latin1') 62 | f.close() 63 | 64 | boundaries = [] 65 | for key in boundaries_keys: 66 | if type(key) == str: 67 | boundaries.append(boundaries_raw[key]) 68 | else: 69 | boundaries.append(Polygon(np.flip(key))) 70 | 71 | # %% get bounds for the scan area 72 | lat_max_list = [] 73 | lon_max_list = [] 74 | lat_min_list = [] 75 | lon_min_list = [] 76 | for boundary in boundaries: 77 | lat_max_list.append(boundary.bounds[3]) 78 | lat_min_list.append(boundary.bounds[1]) 79 | lon_max_list.append(boundary.bounds[2]) 80 | lon_min_list.append(boundary.bounds[0]) 81 | lat_min = np.min(lat_min_list) 82 | lon_min = np.min(lon_min_list) 83 | lat_max = np.max(lat_max_list) 84 | lon_max = np.max(lon_max_list) 85 | 86 | delta_lat = 0.000089 87 | delta_lon = delta_lat / np.cos(np.deg2rad((lat_max + lat_min) / 2)) 88 | print('delta lat: ' + str(delta_lat) + ' delta lon: ' + str(delta_lon)) 89 | lat_range = np.arange(lat_min, lat_max, delta_lat) 90 | lon_range = np.arange(lon_min, lon_max, delta_lon) 91 | lat_points, lon_points = np.meshgrid(lat_range, lon_range) 92 | lat_points = np.reshape(lat_points, np.size(lat_points)) 93 | lon_points = np.reshape(lon_points, np.size(lon_points)) 94 | 95 | # scan GSV meta 96 | # %% scan on the mesh to get the points which contained by the path 97 | print('Collect all points within the target region ...') 98 | points = [] 99 | fips_mapping = {} 100 | for i in tqdm(range(0, np.size(lat_points))): 101 | point = (lat_points[i], lon_points[i]) 102 | pp = Point(point[1], point[0]) 103 | for j, boundary in enumerate(boundaries): 104 | if (boundary.contains(pp)): 105 | points.append(point) 106 | if type(boundaries_keys[j]) == str: 107 | fips_mapping[point] = boundaries_keys[j] 108 | else: 109 | fips_mapping[point] = str(j) 110 | break 111 | 112 | # %% search the points to get Google API meta locations 113 | print('Check meta data ...') 114 | sv = StreetView() 115 | if exists(GSV_points_filepath) and exists(fips_mapping_path): 116 | with open(GSV_points_filepath, 'rb') as f: 117 | validPoints = set(pickle.load(f)) 118 | with open(fips_mapping_path, 'rb') as f: 119 | fips_mapping_sub = pickle.load(f) 120 | print('Loaded existing valid points and FIPS mapping ...') 121 | else: 122 | validPoints = set() 123 | fips_mapping_sub = {} 124 | 125 | def save_found_points(): 126 | with open(GSV_points_filepath, 'wb') as f: 127 | pickle.dump(list(validPoints), f) 128 | f.close() 129 | with open(fips_mapping_path, 'wb') as f: 130 | pickle.dump(fips_mapping_sub, f) 131 | f.close() 132 | 133 | 134 | for i, point in tqdm(list(enumerate(points))): 135 | if i < start_i: 136 | continue 137 | status, meta = sv.getMetaStatus(point, radius='10', returnMeta=True) 138 | if (status == 'OK'): 139 | validPoints.add((meta['location']['lat'], meta['location']['lng'])) 140 | fips_mapping_sub[(meta['location']['lat'], meta['location']['lng'])] = fips_mapping[point] 141 | elif (status == 'ZERO_RESULTS'): 142 | pass 143 | else: 144 | print('ERROR: ' + status) 145 | # break 146 | if (i % save_per_points == 0): 147 | save_found_points() 148 | else: 149 | print('Search Finish!') 150 | save_found_points() 151 | -------------------------------------------------------------------------------- /africa/2_download_area_GSV.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import pickle 3 | from tqdm import tqdm 4 | from streetView import StreetView 5 | import os 6 | from os.path import join, exists 7 | import urllib.error 8 | # import skimage.io 9 | 10 | """ 11 | This script is for downloading upward street view images for each test area. 12 | """ 13 | 14 | region = 'Kampala_Kololo' 15 | 16 | def determine_root_dir(): 17 | """ 18 | This function is used to locate the root dir back to the parent directory, 19 | i.e., "GridMapping" directory. 20 | """ 21 | root_dir = os.path.abspath(os.path.join(os.getcwd(), os.pardir)) 22 | assert root_dir.strip('/')[-11:] == 'GridMapping' 23 | return root_dir 24 | 25 | root_dir = determine_root_dir() 26 | # change the root dir to "GridMapping" 27 | os.chdir(root_dir) 28 | 29 | fips_list_dict = { 30 | 'Kampala_Kololo': ['U000005', 'U000006', 'U000007', 'U000012'], 31 | 'Nairobi_Highridge': ['K101060303'], 32 | 'Kampala_Ntinda': ['U000069'], 33 | 'Nairobi_Ngara': ['K101010501', 'K101010502'], 34 | } 35 | 36 | with open('data/boundary_list_NigeriaGhana_2.pickle', 'rb') as f: 37 | fips_list_dict_2 = pickle.load(f) 38 | for x in fips_list_dict_2: 39 | if not x in fips_list_dict: 40 | fips_list_dict[x] = fips_list_dict_2[x] 41 | 42 | boundaries_keys = fips_list_dict[region] 43 | 44 | start_i = 0 45 | 46 | GSV_points_filepath = join('results', region, 'validGSVpoints.pickle') 47 | fips_mapping_path = join('results', region, 'GSVpoint2fips.pickle') 48 | 49 | # directory to save street view images 50 | GSV_image_path = join('data/GSV_images', region) 51 | 52 | boundaries_keys_set = set() 53 | for i, key in enumerate(boundaries_keys): 54 | if type(key) == str: 55 | boundaries_keys_set.add(key) 56 | else: 57 | boundaries_keys_set.add(str(i)) 58 | 59 | if not exists(join('results', region)): 60 | os.mkdir(join('results', region)) 61 | if not exists(GSV_image_path): 62 | os.mkdir(GSV_image_path) 63 | 64 | with open(GSV_points_filepath, 'rb') as f: 65 | validPoints = pickle.load(f) 66 | f.close() 67 | 68 | with open(fips_mapping_path, 'rb') as f: 69 | fips_mapping = pickle.load(f) 70 | f.close() 71 | 72 | sv = StreetView() 73 | fov = '120' 74 | error_list = [] 75 | 76 | for i, point in tqdm(list(enumerate(validPoints))): 77 | if i < start_i: 78 | continue 79 | if fips_mapping[point] in boundaries_keys_set: 80 | filename = GSV_image_path + os.sep + 'SC%08dNH.jpg' % i 81 | try: 82 | sv.getStreetView(point, filepath=filename, heading='0', 83 | pitch='90', fov=fov, radius='10', uncheck=False) 84 | except urllib.error.HTTPError: 85 | # skimage.io.imsave(filename, np.zeros((640, 640, 3)).astype(int)) 86 | error_list.append(i) 87 | print(error_list) 88 | -------------------------------------------------------------------------------- /africa/3_predict_line_CAM_pytorch.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function 2 | from __future__ import division 3 | import torch 4 | import torch.nn as nn 5 | import torch.optim as optim 6 | from torch.utils.data import Dataset, DataLoader 7 | import torchvision 8 | from torchvision import datasets, models, transforms, utils 9 | import torchvision.transforms.functional as TF 10 | 11 | from tqdm import tqdm 12 | import numpy as np 13 | import json 14 | import pandas as pd 15 | import pickle 16 | import matplotlib.pyplot as plt 17 | # import skimage 18 | # import skimage.io 19 | # import skimage.transform 20 | from PIL import Image 21 | import time 22 | import os 23 | from os.path import join, exists 24 | import copy 25 | import random 26 | from collections import OrderedDict 27 | from sklearn.metrics import r2_score 28 | 29 | from torch.nn import functional as F 30 | from torchvision.models import Inception3 31 | 32 | from inception_modified import InceptionSegmentation 33 | from image_dataset import ImagePredictDataset 34 | 35 | """ 36 | This script is for running the line detetor on street view images. 37 | Classification results and CAMs are generated and saved. 38 | Note: this must be run after running step 2: "2_download_area_GSV.py". 39 | """ 40 | 41 | region = 'Kampala_Kololo' 42 | 43 | def determine_root_dir(): 44 | """ 45 | This function is used to locate the root dir back to the parent directory, 46 | i.e., "GridMapping" directory. 47 | """ 48 | root_dir = os.path.abspath(os.path.join(os.getcwd(), os.pardir)) 49 | assert root_dir.strip('/')[-11:] == 'GridMapping' 50 | return root_dir 51 | 52 | root_dir = determine_root_dir() 53 | # change the root dir to "GridMapping" 54 | os.chdir(root_dir) 55 | 56 | # Configuration 57 | data_dir = join('data/GSV_images', region) 58 | save_dir = join('data/CAM_images', region) 59 | old_ckpt_path = 'checkpoint/deepGrid_seg_pretrained.tar' 60 | CAM_save_path = join('results', region, 'CAM_info_line.pickle') 61 | 62 | if not exists(save_dir): 63 | os.mkdir(save_dir) 64 | 65 | device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu") 66 | input_size = 640 67 | batch_size = 1 # must be 1 for testing segmentation 68 | threshold = 0.5 # threshold probability to identify am image as positive 69 | level = 1 70 | 71 | save_separated = True 72 | 73 | 74 | def predict_model(model, dataloader, threshold): 75 | model.eval() 76 | CAM_list = [] 77 | for inputs, paths in tqdm(dataloader): 78 | inputs = inputs.to(device) 79 | with torch.set_grad_enabled(False): 80 | # CAM is a 1 x 35 x 35 activation map 81 | _, outputs, CAM = model(inputs, testing=True) 82 | prob = F.softmax(outputs, dim=1) 83 | preds = prob[:, 1] >= threshold 84 | 85 | # transform tensor into numpy array 86 | CAM = CAM.squeeze(0).cpu().numpy() 87 | for i in range(preds.size(0)): 88 | predicted_label = preds[i] 89 | label = predicted_label.cpu().item() 90 | if label: 91 | # only use the generated CAM if it is predicted to be 1 92 | CAM_list.append((CAM, paths[i], label)) 93 | else: 94 | # otherwise the CAM is a totally black one 95 | CAM_list.append((np.zeros_like(CAM), paths[i], label)) 96 | 97 | return CAM_list 98 | 99 | 100 | transform_test = transforms.Compose([ 101 | transforms.Resize(input_size), 102 | transforms.ToTensor(), 103 | transforms.Normalize([0.5, 0.5, 0.5], [0.5, 0.5, 0.5]) 104 | ]) 105 | 106 | if __name__ == '__main__': 107 | # data 108 | dataset_test = ImagePredictDataset(data_dir, transform_test) 109 | dataloader_test = DataLoader( 110 | dataset_test, batch_size=batch_size, shuffle=False, num_workers=4) 111 | # model 112 | model = InceptionSegmentation(num_outputs=2, level=level) 113 | model.load_existing_params(old_ckpt_path) 114 | model = model.to(device) 115 | CAM_list = predict_model(model, dataloader_test, threshold=threshold) 116 | 117 | print('Save\n') 118 | info_list = [] 119 | if(save_separated): 120 | for cam, path, label in tqdm(CAM_list): 121 | if (label): 122 | filename = save_dir + os.sep + path[-16:-3] + 'cam' 123 | with open(filename, 'wb') as f: 124 | np.save(f, cam) 125 | f.close() 126 | info_list.append([path, label]) 127 | with open(CAM_save_path, 'wb') as f: 128 | pickle.dump(info_list, f) 129 | else: 130 | with open(CAM_save_path, 'wb') as f: 131 | pickle.dump(CAM_list, f) 132 | -------------------------------------------------------------------------------- /africa/4_CAM_to_line_directions.py: -------------------------------------------------------------------------------- 1 | import ctypes 2 | import numpy as np 3 | import pickle 4 | from tqdm import tqdm 5 | import os 6 | from os.path import join, exists 7 | 8 | """ 9 | This script is for using Hough transform to extract line directions 10 | from CAMs. 11 | Note: this must be run after running step 3: "3_predict_line_CAM_pytorch.py". 12 | """ 13 | 14 | region = 'Kampala_Kololo' 15 | 16 | def determine_root_dir(): 17 | """ 18 | This function is used to locate the root dir back to the parent directory, 19 | i.e., "GridMapping" directory. 20 | """ 21 | root_dir = os.path.abspath(os.path.join(os.getcwd(), os.pardir)) 22 | assert root_dir.strip('/')[-11:] == 'GridMapping' 23 | return root_dir 24 | 25 | root_dir = determine_root_dir() 26 | # change the root dir to "GridMapping" 27 | os.chdir(root_dir) 28 | 29 | # ctypes initial 30 | HoughTransform_lib = ctypes.cdll.LoadLibrary( 31 | 'HoughTransform_lib.so') 32 | HoughTransform_lib.init() 33 | cam2lines = HoughTransform_lib.cam2lines 34 | cam2lines.argtypes = [np.ctypeslib.ndpointer( 35 | ctypes.c_double, flags='C_CONTIGUOUS')] 36 | cam2lines.restype = ctypes.c_int 37 | getlines = HoughTransform_lib.getlines 38 | getlines.argtypes = [] 39 | getlines.restype = ctypes.POINTER(ctypes.c_double) 40 | 41 | # rh & th 42 | ''' 43 | getth = HoughTransform_lib.getth 44 | getth.restype = ctypes.POINTER(ctypes.c_double) 45 | getrh = HoughTransform_lib.getrh 46 | getrh.restype = ctypes.POINTER(ctypes.c_double) 47 | th_raw = getth() 48 | rh_raw = getrh() 49 | th_len = HoughTransform_lib.getthlen() 50 | rh_len = HoughTransform_lib.getrhlen() 51 | th = np.fromiter(th_raw, dtype=np.double, count=th_len) 52 | rh = np.fromiter(rh_raw, dtype=np.double, count=rh_len) 53 | ''' 54 | 55 | CAMinfopath = join('results', region, 'CAM_info_line.pickle') 56 | rawdatapath = join('data/GSV_images', region) 57 | CAMdatapath = join('data/CAM_images', region) 58 | savefile = join('results', region, 'line_info_raw.pickle') 59 | #savepath = projectPath+'data/CAM2Lines_new2' 60 | #os.makedirs(savepath, exist_ok=True) 61 | 62 | with open(CAMinfopath, 'rb') as info: 63 | caminfos = pickle.load(info) 64 | info.close() 65 | 66 | linesDictionary = dict() 67 | 68 | 69 | for path, label in tqdm(caminfos): 70 | if (label == 0): 71 | linesDictionary[path] = [] 72 | continue 73 | with open(CAMdatapath+os.sep+path[-16:-3]+'cam', 'rb') as f: 74 | cam = np.load(f) 75 | f.close() 76 | #cam = np.reshape(cam, np.size(cam)) 77 | cam_array = np.ascontiguousarray(cam, dtype=ctypes.c_double) 78 | #cam_p = cam_array.ctypes.data_as(ctypes.POINTER(ctypes.c_double)) 79 | lines_len = cam2lines(cam_array) 80 | lines_raw = getlines() 81 | lines = np.fromiter(lines_raw, dtype=np.double, count=3 * lines_len) 82 | lines = np.resize(lines, (lines_len, 3)) 83 | 84 | linesDictionary[path] = lines 85 | 86 | with open(savefile, 'wb') as f: 87 | pickle.dump(linesDictionary, f) 88 | f.close() 89 | 90 | 91 | HoughTransform_lib.destory() 92 | -------------------------------------------------------------------------------- /africa/5_merge_line_directions.py: -------------------------------------------------------------------------------- 1 | import pickle 2 | import numpy as np 3 | import os 4 | from tqdm import tqdm 5 | from os.path import join, exists 6 | 7 | """ 8 | This script is used for merging similar line directions (i.e., parallel power lines with 9 | different phases) estimated in each CAM. 10 | """ 11 | 12 | region = 'Kampala_Kololo' 13 | 14 | def determine_root_dir(): 15 | """ 16 | This function is used to locate the root dir back to the parent directory, 17 | i.e., "GridMapping" directory. 18 | """ 19 | root_dir = os.path.abspath(os.path.join(os.getcwd(), os.pardir)) 20 | assert root_dir.strip('/')[-11:] == 'GridMapping' 21 | return root_dir 22 | 23 | root_dir = determine_root_dir() 24 | # change the root dir to "GridMapping" 25 | os.chdir(root_dir) 26 | 27 | line_directions_filepath = join('results', region, 'line_info_raw.pickle') 28 | merged_directions_filepath = join('results', region, 'line_info_merged.pickle') 29 | Point_info_path = join('results', region, 'validGSVpoints.pickle') 30 | 31 | with open(line_directions_filepath, 'rb') as f: 32 | line_info_raw = pickle.load(f) 33 | 34 | with open(Point_info_path, 'rb') as f: 35 | point_infos = pickle.load(f) 36 | f.close() 37 | 38 | directionInfo = [] 39 | for cam_path in line_info_raw: 40 | rank = int(cam_path[-14:-6]) 41 | location = point_infos[rank] 42 | directionInfo.append((location, line_info_raw[cam_path], cam_path)) 43 | 44 | 45 | def lineSimilar(theta1, theta2): 46 | threhold = 20 47 | delta_theta = abs(theta1-theta2) 48 | if (delta_theta < threhold): 49 | return 1 50 | elif ((180 - delta_theta) < threhold): 51 | return 2 52 | else: 53 | return 0 54 | 55 | 56 | def lineMean(line1, line2): 57 | if (lineSimilar(line1[1], line2[1]) == 1): 58 | lineM = (line1[1] * line1[2] + line2[1] * 59 | line2[2]) / (line1[2] + line2[2]) 60 | elif (lineSimilar(line1[1], line2[1]) == 2): 61 | if (line1[1] < 0): 62 | line1_s = line1[1] + 180 63 | line2_s = line2[1] 64 | else: 65 | line1_s = line1[1] 66 | line2_s = line2[1] + 180 67 | lineM = (line1_s * line1[2] + line2_s * 68 | line2[2]) / (line1[2] + line2[2]) 69 | if (lineM > 90): 70 | lineM = lineM - 90 71 | return lineM 72 | 73 | 74 | def lineWeight(line): 75 | return line[2] 76 | 77 | 78 | mergedInfo = list() 79 | 80 | for location, lines, path in tqdm(directionInfo): 81 | # combine 82 | lineBuffer = list() 83 | for line in lines: 84 | # transform from sky view to ground view 85 | theta = -line[1] * 180 / np.pi 86 | line_regular = [1, theta, line[2]] 87 | if (lineBuffer == []): 88 | lineBuffer.append(line_regular) 89 | else: 90 | join = False 91 | for i, bufferedLine in enumerate(lineBuffer): 92 | if (lineSimilar(line_regular[1], bufferedLine[1]) > 0): 93 | lineBuffer[i][0] += line_regular[0] 94 | lineBuffer[i][1] = lineMean(line_regular, bufferedLine) 95 | lineBuffer[i][2] += line_regular[2] 96 | join = True 97 | break 98 | if(join == False): 99 | lineBuffer.append(line_regular) 100 | lineBuffer.sort(key=lineWeight, reverse=True) 101 | 102 | mergedInfo.append([location, lineBuffer]) 103 | 104 | with open(merged_directions_filepath, 'wb') as f: 105 | pickle.dump(mergedInfo, f) 106 | -------------------------------------------------------------------------------- /africa/6_predict_pole_CAM_pytorch.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function 2 | from __future__ import division 3 | import torch 4 | import torch.nn as nn 5 | import torch.optim as optim 6 | from torch.utils.data import Dataset, DataLoader 7 | import torchvision 8 | from torchvision import datasets, models, transforms, utils 9 | import torchvision.transforms.functional as TF 10 | 11 | from tqdm import tqdm 12 | import numpy as np 13 | import json 14 | import pandas as pd 15 | import pickle 16 | import matplotlib.pyplot as plt 17 | # import skimage 18 | # import skimage.io 19 | # import skimage.transform 20 | from PIL import Image 21 | import time 22 | import os 23 | from os.path import join, exists 24 | import copy 25 | import random 26 | from collections import OrderedDict 27 | from sklearn.metrics import r2_score 28 | 29 | from torch.nn import functional as F 30 | from torchvision.models import Inception3 31 | 32 | from inception_modified import InceptionSegmentation 33 | from image_dataset import ImagePredictDataset 34 | 35 | """ 36 | This script is for running the pole detetor on street view images. 37 | Classification results and CAMs are generated and saved. 38 | Note: this must be run after running step 2: "2_download_area_GSV.py". 39 | """ 40 | 41 | region = 'Kampala_Kololo' 42 | pole_model = 'ori0.2' # the model using 0.2 as the classification decision threshold 43 | 44 | def determine_root_dir(): 45 | """ 46 | This function is used to locate the root dir back to the parent directory, 47 | i.e., "GridMapping" directory. 48 | """ 49 | root_dir = os.path.abspath(os.path.join(os.getcwd(), os.pardir)) 50 | assert root_dir.strip('/')[-11:] == 'GridMapping' 51 | return root_dir 52 | 53 | root_dir = determine_root_dir() 54 | # change the root dir to "GridMapping" 55 | os.chdir(root_dir) 56 | 57 | pole_model_configs = { 58 | 'ori0.2': { 59 | 'ckpt_path': 'checkpoint/deepGrid_DPNH2seg_pretrained.tar', 60 | 'threshold': 0.2, 61 | } 62 | } 63 | 64 | # Configuration 65 | # directory for loading training/validation/test data 66 | data_dir = join('data/GSV_images', region) 67 | save_dir = join('data/CAM_images_for_poles', pole_model, region) 68 | old_ckpt_path = pole_model_configs[pole_model]['ckpt_path'] 69 | CAM_save_path = join('results', region, pole_model, 'CAM_info_pole.pickle') 70 | 71 | if not exists(join('data/CAM_images_for_poles', pole_model)): 72 | os.mkdir(join('data/CAM_images_for_poles', pole_model)) 73 | if not exists(save_dir): 74 | os.mkdir(save_dir) 75 | if not exists(join('results', region, pole_model)): 76 | os.mkdir(join('results', region, pole_model)) 77 | 78 | device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu") 79 | input_size = 640 80 | batch_size = 1 # must be 1 for testing segmentation 81 | threshold = pole_model_configs[pole_model]['threshold'] # threshold probability to identify am image as positive 82 | level = 1 83 | 84 | save_separated = True 85 | 86 | 87 | def predict_model(model, dataloader, threshold): 88 | model.eval() 89 | CAM_list = [] 90 | for inputs, paths in tqdm(dataloader): 91 | inputs = inputs.to(device) 92 | with torch.set_grad_enabled(False): 93 | # CAM is a 1 x 35 x 35 activation map 94 | _, outputs, CAM = model(inputs, testing=True) 95 | prob = F.softmax(outputs, dim=1) 96 | preds = prob[:, 1] >= threshold 97 | 98 | # transform tensor into numpy array 99 | CAM = CAM.squeeze(0).cpu().numpy() 100 | for i in range(preds.size(0)): 101 | predicted_label = preds[i] 102 | label = predicted_label.cpu().item() 103 | if label: 104 | # only use the generated CAM if it is predicted to be 1 105 | CAM_list.append((CAM, paths[i], label)) 106 | else: 107 | # otherwise the CAM is a totally black one 108 | CAM_list.append((np.zeros_like(CAM), paths[i], label)) 109 | 110 | return CAM_list 111 | 112 | 113 | transform_test = transforms.Compose([ 114 | transforms.Resize(input_size), 115 | transforms.ToTensor(), 116 | transforms.Normalize([0.5, 0.5, 0.5], [0.5, 0.5, 0.5]) 117 | ]) 118 | 119 | if __name__ == '__main__': 120 | 121 | # data 122 | dataset_test = ImagePredictDataset(data_dir, transform_test) 123 | dataloader_test = DataLoader( 124 | dataset_test, batch_size=batch_size, shuffle=False, num_workers=4) 125 | # model 126 | print('Decision threshold: ' + str(threshold)) 127 | model = InceptionSegmentation(num_outputs=2, level=level) 128 | model.load_existing_params(old_ckpt_path) 129 | model = model.to(device) 130 | CAM_list = predict_model(model, dataloader_test, threshold=threshold) 131 | 132 | print('Save\n') 133 | info_list = [] 134 | if(save_separated): 135 | for cam, path, label in tqdm(CAM_list): 136 | if (label): 137 | filename = save_dir + os.sep + path[-16:-3] + 'cam' 138 | with open(filename, 'wb') as f: 139 | np.save(f, cam) 140 | f.close() 141 | info_list.append([path, label]) 142 | with open(CAM_save_path, 'wb') as f: 143 | pickle.dump(info_list, f) 144 | else: 145 | with open(CAM_save_path, 'wb') as f: 146 | pickle.dump(CAM_list, f) 147 | -------------------------------------------------------------------------------- /africa/7_CAM_to_pole_LOBs.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import pickle 3 | from tqdm import tqdm 4 | import os 5 | from math import atan2, pi, sin, cos 6 | import matplotlib.pyplot as plt 7 | from os.path import join, exists 8 | 9 | """ 10 | This script is for extracting pole orientations from CAMs and obtaining the 11 | Line of Bearings (LOBs, rays that represent pole orientations) 12 | Multiple regions can be run all at once. 13 | Note: this must be run after running step 6: "6_predict_pole_CAM_pytorch.py". 14 | """ 15 | 16 | region_list = ['Kampala_Kololo'] # 'Kampala_Ntinda', 'Kampala_Kololo', 'Nairobi_Highridge', 'Nairobi_Ngara', 'Lagos_Ikeja2' 17 | 18 | pole_model_list = ['ori0.2'] 19 | 20 | def determine_root_dir(): 21 | """ 22 | This function is used to locate the root dir back to the parent directory, 23 | i.e., "GridMapping" directory. 24 | """ 25 | root_dir = os.path.abspath(os.path.join(os.getcwd(), os.pardir)) 26 | assert root_dir.strip('/')[-11:] == 'GridMapping' 27 | return root_dir 28 | 29 | root_dir = determine_root_dir() 30 | # change the root dir to "GridMapping" 31 | os.chdir(root_dir) 32 | 33 | for region in region_list: 34 | for pole_model in pole_model_list: 35 | print('-'*20) 36 | print('Region: ' + region + '. Pole model: ' + pole_model) 37 | # datapath 38 | CAMdatapath = join('data/CAM_images_for_poles', pole_model, region) 39 | Line_CAM_info_path = join('results', region, 'CAM_info_line.pickle') 40 | Pole_CAM_info_path = join('results', region, pole_model, 'CAM_info_pole.pickle') 41 | Point_info_path = join('results', region, 'validGSVpoints.pickle') 42 | LOB_savefile = join('results', region, pole_model, 'pole_LOB_raw.pickle') 43 | threshold = 100 44 | 45 | with open(Pole_CAM_info_path, 'rb') as f: 46 | pole_infos = pickle.load(f) 47 | f.close() 48 | 49 | with open(Line_CAM_info_path, 'rb') as f: 50 | line_infos = pickle.load(f) 51 | f.close() 52 | 53 | with open(Point_info_path, 'rb') as f: 54 | point_infos = pickle.load(f) 55 | f.close() 56 | 57 | pole_directions = list() 58 | for i in tqdm(range(len(pole_infos))): 59 | path_pole, label_pole = pole_infos[i] 60 | path_line, label_line = line_infos[i] 61 | rank = int(path_pole[-14:-6]) 62 | location = point_infos[rank] 63 | if ((label_line == 0) or (label_pole == 0)): 64 | continue 65 | with open(os.path.join(CAMdatapath, path_pole[-16:-3] + 'cam'), 'rb') as f: 66 | # cam = np.fromfile(f, dtype=np.float32) 67 | # cam = np.reshape(cam, (77, 77)) 68 | cam = np.load(f) 69 | f.close() 70 | imgsize_y, imgsize_x = np.shape(cam) 71 | thetas = np.zeros(360) 72 | for i in range(imgsize_y): 73 | for j in range(imgsize_x): 74 | dy = i - int(imgsize_y / 2) 75 | dx = j - int(imgsize_x / 2) 76 | alpha = int(round(atan2(dy, dx) * 180 / pi)) 77 | thetas[alpha] += cam[i, j] 78 | theta = np.argmax(thetas) 79 | pole_directions.append([location, theta]) 80 | 81 | with open(LOB_savefile, 'wb') as f: 82 | pickle.dump(pole_directions, f) 83 | -------------------------------------------------------------------------------- /africa/8_LOB_to_pole_locations.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import pickle 3 | from tqdm import tqdm 4 | import os 5 | from math import atan2, sqrt, pi, sin, cos, tan 6 | import matplotlib.pyplot as plt 7 | from os.path import join, exists 8 | 9 | """ 10 | This script is for intersecting the Lines of Bearings (LOBs) obtained from step 7 11 | to obtain the pole locations. 12 | Multiple regions can be run all at once. 13 | """ 14 | 15 | region_list = ['Kampala_Kololo'] # 'Kampala_Ntinda', 'Kampala_Kololo', 'Nairobi_Highridge', 'Nairobi_Ngara', 'Lagos_Ikeja2' 16 | pole_model_list = ['ori0.2'] 17 | 18 | 19 | def determine_root_dir(): 20 | """ 21 | This function is used to locate the root dir back to the parent directory, 22 | i.e., "GridMapping" directory. 23 | """ 24 | root_dir = os.path.abspath(os.path.join(os.getcwd(), os.pardir)) 25 | assert root_dir.strip('/')[-11:] == 'GridMapping' 26 | return root_dir 27 | 28 | root_dir = determine_root_dir() 29 | # change the root dir to "GridMapping" 30 | os.chdir(root_dir) 31 | 32 | def getDistance(pt1, pt2): 33 | #latitude, longitude 34 | lat1, lon1 = pt1 35 | lat2, lon2 = pt2 36 | lat = (lat1 + lat2) / 2 * pi / 180 37 | dlat = (lat2 - lat1) * pi / 180 38 | dlon = (lon2 - lon1) * pi / 180 39 | R = 6378.137 # km 40 | dl = R * sqrt(dlat ** 2 + (cos(lat) * dlon) ** 2) 41 | return dl * 1000 42 | 43 | 44 | def getIntersection(LOB1, LOB2): 45 | eps = 0.5 46 | location1, direction1 = LOB1 47 | location2, direction2 = LOB2 48 | lat1, lon1 = location1 49 | lat2, lon2 = location2 50 | y1, x1 = location1 51 | y2, x2 = location2 52 | lat = (lat1 + lat2) / 2 * pi / 180 53 | 54 | if (direction1 == direction2): 55 | # parallel 56 | return [] 57 | 58 | beta1 = direction1 * pi / 180 59 | beta2 = direction2 * pi / 180 60 | tb1 = tan(beta1) * cos(lat) 61 | tb2 = tan(beta2) * cos(lat) 62 | 63 | if tb1 - tb2 == 0: 64 | # print(direction1, direction2) 65 | return [] 66 | 67 | xt = (x1 * tb1 - x2 * tb2 + y2 - y1) / (tb1 - tb2) 68 | yt = ((x1 * tb1 - y1) * tb2 - (x2 * tb2 - y2) * tb1) / (tb1 - tb2) 69 | 70 | beta1_t = atan2(yt - y1, xt - x1) 71 | beta2_t = atan2(yt - y2, xt - x2) 72 | beta1_t = (beta1_t + 2 * pi) if (beta1_t < 0) else beta1_t 73 | beta2_t = (beta2_t + 2 * pi) if (beta2_t < 0) else beta2_t 74 | 75 | if ((abs(beta1_t - beta1) > eps) or (abs(beta2_t - beta2) > eps)): 76 | # no intersection, wrong direction 77 | return [] 78 | pt = [yt, xt] 79 | 80 | if (getDistance(pt, location1) > area_threshold): 81 | return [] 82 | if (getDistance(pt, location2) > area_threshold): 83 | return [] 84 | return pt 85 | 86 | 87 | for region in region_list: 88 | for pole_model in pole_model_list: 89 | print('-'*20) 90 | print('Region: ' + region + '. Pole model: ' + pole_model) 91 | # datapath 92 | pole_directions_path = join('results', region, pole_model, 'pole_LOB_raw.pickle') 93 | savefile = join('results', region, pole_model, 'pole_locations.pickle') 94 | attached_location_file = join('results', region, pole_model, 'pole_attached_GSVs.pickle') 95 | 96 | with open(pole_directions_path, 'rb') as f: 97 | pole_directions = pickle.load(f) 98 | f.close() 99 | 100 | distance_threshold = 40 101 | cluster_threshold = 10 102 | area_threshold = 20 103 | 104 | clusters = list() 105 | # [lat,lon,combo] 106 | LOB_len = len(pole_directions) 107 | attached_location = list() 108 | for i in tqdm(range(LOB_len)): 109 | location, direction = pole_directions[i] 110 | for j in range(i + 1, LOB_len): 111 | location2, direction2 = pole_directions[j] 112 | if (getDistance(location, location2) > distance_threshold): 113 | continue 114 | pt = getIntersection([location, direction], [location2, direction2]) 115 | if (pt == []): 116 | continue 117 | # cluster insert 118 | for k, cluster in enumerate(clusters): 119 | lat0, lon0, combo = cluster 120 | lat1, lon1 = pt 121 | if (getDistance([lat0, lon0], pt) < cluster_threshold): 122 | lat_new = (lat0 * combo + lat1) / (combo + 1) 123 | lon_new = (lon0 * combo + lon1) / (combo + 1) 124 | combo_new = combo + 1 125 | clusters[k] = [lat_new, lon_new, combo_new] 126 | attached_location[k].extend([location, location2]) 127 | break 128 | else: 129 | clusters.append([pt[0], pt[1], 1]) 130 | attached_location.append([location, location2]) 131 | 132 | with open(savefile, 'wb') as f: 133 | pickle.dump(clusters, f) 134 | 135 | with open(attached_location_file, 'wb') as f: 136 | pickle.dump(attached_location, f) 137 | -------------------------------------------------------------------------------- /africa/HoughTransform_lib.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #define M_PI 3.14159265358979323846 4 | #define IMGSIZE 77 5 | #define CAMSIZE (IMGSIZE * IMGSIZE) 6 | #define THETARANGE 180 7 | #define LINEMAX 10 8 | #define maskDepth 9999 9 | #define maskWidth 3 10 | 11 | #ifdef _MSC_VER 12 | #define DLL_EXPORT __declspec(dllexport) 13 | #else 14 | #define DLL_EXPORT 15 | #endif 16 | 17 | double *cos_t, *sin_t; 18 | double *thetas, *rhos; 19 | int* accumulator; 20 | int theta_len, diag_len; 21 | int weight, height; 22 | double* cam; 23 | int* cam_regular; 24 | double* lines; 25 | int lines_len; 26 | 27 | struct Points { 28 | int x1, y1, x2, y2; 29 | }; 30 | 31 | struct Points polar2cart(double rho, double theta) { 32 | double a = cos(theta); 33 | double b = sin(theta); 34 | double x0 = a * rho; 35 | double y0 = b * rho; 36 | struct Points p; 37 | p.x1 = (int)(x0 + 1000 * (-b)); 38 | p.y1 = (int)(y0 + 1000 * (a)); 39 | p.x2 = (int)(x0 - 1000 * (-b)); 40 | p.y2 = (int)(y0 - 1000 * (a)); 41 | return p; 42 | } 43 | 44 | int getMaxRank(int* array, int len) { 45 | int rank = 0; 46 | int maxValue = array[0]; 47 | for (int i = 1; i < len; i++) { 48 | if (array[i] > maxValue) { 49 | maxValue = array[i]; 50 | rank = i; 51 | } 52 | } 53 | return rank; 54 | } 55 | 56 | int getMax(int* array, int len) { 57 | return array[getMaxRank(array, len)]; 58 | } 59 | 60 | void CAMRegular() { 61 | int level1 = 10; 62 | int level2 = 1; 63 | int level1_thre = 80; 64 | int level2_thre = 10; 65 | for (int i = 0; i < CAMSIZE; i++) { 66 | if (cam[i] > level1_thre) 67 | cam_regular[i] = level1; 68 | else if (cam[i] > level2_thre) 69 | cam_regular[i] = level2; 70 | else 71 | cam_regular[i] = 0; 72 | } 73 | } 74 | 75 | int capsule(double px, 76 | double py, 77 | double ax, 78 | double ay, 79 | double bx, 80 | double by, 81 | double r) { 82 | double pax = px - ax, pay = py - ay, bax = bx - ax, bay = by - ay; 83 | double h = fmaxf( 84 | fminf((pax * bax + pay * bay) / (bax * bax + bay * bay), 1.0f), 0.0f); 85 | double dx = pax - bax * h, dy = pay - bay * h; 86 | return dx * dx + dy * dy < r * r; 87 | } 88 | 89 | void lineMask(double* img, struct Points pts, int width) { 90 | double* p = img; 91 | for (int y = 0; y < IMGSIZE; y++) 92 | for (int x = 0; x < IMGSIZE; x++, p += 1) 93 | *p = *p - capsule(x, y, pts.x1, pts.y1, pts.x2, pts.y2, width) * 94 | maskDepth; 95 | return; 96 | } 97 | 98 | DLL_EXPORT void init() { 99 | theta_len = THETARANGE + 1; 100 | weight = IMGSIZE; 101 | height = IMGSIZE; 102 | diag_len = (int)(ceil(sqrt(weight * weight + height * height))); 103 | thetas = (double*)malloc(sizeof(double) * theta_len); 104 | cos_t = (double*)malloc(sizeof(double) * theta_len); 105 | sin_t = (double*)malloc(sizeof(double) * theta_len); 106 | rhos = (double*)malloc(sizeof(double) * 2 * diag_len); 107 | accumulator = (int*)malloc(sizeof(int) * 2 * diag_len * theta_len); 108 | cam = (double*)malloc(sizeof(double) * CAMSIZE); 109 | cam_regular = (int*)malloc(sizeof(int) * CAMSIZE); 110 | lines = (double*)malloc(sizeof(double) * 3 * LINEMAX); 111 | for (int i = 0; i < theta_len; i++) { 112 | thetas[i] = (i - 90) * M_PI / 180; 113 | cos_t[i] = cos(thetas[i]); 114 | sin_t[i] = sin(thetas[i]); 115 | } 116 | double rhos_delta = 2 * diag_len / (2 * diag_len - 1); 117 | for (int i = 0; i < 2 * diag_len; i++) 118 | rhos[i] = -diag_len + i * rhos_delta; 119 | } 120 | 121 | DLL_EXPORT int* HoughTransform(int* img) { 122 | for (int i = 0; i < 2 * diag_len; i++) 123 | for (int j = 0; j < theta_len; j++) 124 | accumulator[i * theta_len + j] = 0; 125 | 126 | for (int x = 0; x < weight; x++) 127 | for (int y = 0; y < height; y++) { 128 | if (img[y * weight + x] == 0) 129 | return; 130 | for (int t = 0; t < theta_len; t++) { 131 | int rho = (int)(round(x * cos_t[t] + y * sin_t[t])) + diag_len; 132 | accumulator[rho * theta_len + t] += img[y * weight + x]; 133 | } 134 | } 135 | return accumulator; 136 | } 137 | DLL_EXPORT double* getth() { 138 | return thetas; 139 | } 140 | DLL_EXPORT double* getrh() { 141 | return rhos; 142 | } 143 | DLL_EXPORT int getthlen() { 144 | return theta_len; 145 | } 146 | DLL_EXPORT int getrhlen() { 147 | return 2 * diag_len; 148 | } 149 | DLL_EXPORT void destory() { 150 | free(thetas); 151 | free(sin_t); 152 | free(cos_t); 153 | free(rhos); 154 | free(accumulator); 155 | free(cam); 156 | free(cam_regular); 157 | free(lines); 158 | } 159 | 160 | DLL_EXPORT int cam2lines(double* cam_data) { 161 | for (int i = 0; i < CAMSIZE; i++) 162 | cam[i] = cam_data[i]; 163 | // memcpy(cam, cam_data, sizeof(double) * CAMSIZE); 164 | lines_len = 0; 165 | int line_thre = 80; 166 | for (int i = 0; i < LINEMAX; i++) { 167 | CAMRegular(); 168 | int* hf = HoughTransform(cam_regular); 169 | if (getMax(hf, 2 * diag_len * theta_len) < line_thre) 170 | break; 171 | int hfLine = getMaxRank(hf, 2 * diag_len * theta_len); 172 | int hfLine_r = hfLine / theta_len; 173 | int hfLine_t = hfLine % theta_len; 174 | double rho = rhos[hfLine_r]; 175 | double theta = thetas[hfLine_t]; 176 | lines[i * 3 + 0] = rho; 177 | lines[i * 3 + 1] = theta; 178 | lines[i * 3 + 2] = hf[hfLine]; 179 | lines_len++; 180 | struct Points p = polar2cart(rho, theta); 181 | lineMask(cam, p, maskWidth); 182 | } 183 | return lines_len; 184 | } 185 | 186 | DLL_EXPORT double* getlines() { 187 | return lines; 188 | } 189 | -------------------------------------------------------------------------------- /africa/HoughTransform_lib.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangzhecheng/GridMapping/e5f6360132dea5b67c97a6fb0e82fe85d3f63352/africa/HoughTransform_lib.so -------------------------------------------------------------------------------- /africa/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangzhecheng/GridMapping/e5f6360132dea5b67c97a6fb0e82fe85d3f63352/africa/__init__.py -------------------------------------------------------------------------------- /africa/__pycache__/census_tract_locator.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangzhecheng/GridMapping/e5f6360132dea5b67c97a6fb0e82fe85d3f63352/africa/__pycache__/census_tract_locator.cpython-36.pyc -------------------------------------------------------------------------------- /africa/__pycache__/dijkstra.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangzhecheng/GridMapping/e5f6360132dea5b67c97a6fb0e82fe85d3f63352/africa/__pycache__/dijkstra.cpython-36.pyc -------------------------------------------------------------------------------- /africa/__pycache__/evaluation_utils.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangzhecheng/GridMapping/e5f6360132dea5b67c97a6fb0e82fe85d3f63352/africa/__pycache__/evaluation_utils.cpython-36.pyc -------------------------------------------------------------------------------- /africa/__pycache__/feature_engineering.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangzhecheng/GridMapping/e5f6360132dea5b67c97a6fb0e82fe85d3f63352/africa/__pycache__/feature_engineering.cpython-36.pyc -------------------------------------------------------------------------------- /africa/__pycache__/image_dataset.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangzhecheng/GridMapping/e5f6360132dea5b67c97a6fb0e82fe85d3f63352/africa/__pycache__/image_dataset.cpython-36.pyc -------------------------------------------------------------------------------- /africa/__pycache__/inception_modified.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangzhecheng/GridMapping/e5f6360132dea5b67c97a6fb0e82fe85d3f63352/africa/__pycache__/inception_modified.cpython-36.pyc -------------------------------------------------------------------------------- /africa/__pycache__/region_locator.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangzhecheng/GridMapping/e5f6360132dea5b67c97a6fb0e82fe85d3f63352/africa/__pycache__/region_locator.cpython-36.pyc -------------------------------------------------------------------------------- /africa/__pycache__/streetView.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangzhecheng/GridMapping/e5f6360132dea5b67c97a6fb0e82fe85d3f63352/africa/__pycache__/streetView.cpython-36.pyc -------------------------------------------------------------------------------- /africa/__pycache__/utils.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangzhecheng/GridMapping/e5f6360132dea5b67c97a6fb0e82fe85d3f63352/africa/__pycache__/utils.cpython-36.pyc -------------------------------------------------------------------------------- /africa/census_tract_locator.py: -------------------------------------------------------------------------------- 1 | from os import listdir 2 | from os.path import join, exists 3 | import pandas as pd 4 | import numpy as np 5 | import pickle 6 | from bisect import bisect_left, bisect_right 7 | import matplotlib.path as mplPath 8 | 9 | 10 | class CensusTract(object): 11 | def __init__(self, tract_data_dir=None): 12 | self.tract_data = None 13 | if tract_data_dir is not None: 14 | if exists(tract_data_dir): 15 | with open(tract_data_dir, 'rb') as f: 16 | self.tract_data = pickle.load(f) 17 | pass 18 | 19 | def save_tract_data(self, boundary_file_dir, tract_data_dir=None): 20 | 21 | raw_tract_names = [] 22 | for bf in listdir(boundary_file_dir): 23 | # File name example: 1400000US01097001400_polylatlon1.csv 24 | if bf[-4:] == '.csv': 25 | tract_name = bf[9:20] 26 | raw_tract_names.append(tract_name) 27 | 28 | tract_names = sorted(raw_tract_names) 29 | 30 | tract_boundaries = dict() 31 | 32 | lms = ('lat_min', 'lat_max', 'lon_min', 'lon_max') 33 | lm_list = dict() 34 | for lm in lms: 35 | lm_list[lm] = [] 36 | 37 | max_lat_dist = -1 38 | max_lon_dist = -1 39 | 40 | for tract_name in tract_names: 41 | bf = ''.join(['1400000US', tract_name, '_polylatlon1.csv']) 42 | lat_lon = pd.read_csv(join(boundary_file_dir, bf), header=-1).values 43 | 44 | tract_boundaries[tract_name] = lat_lon 45 | 46 | bf_lms = { 47 | 'lat_min': np.min(lat_lon[:, 0]), 48 | 'lat_max': np.max(lat_lon[:, 0]), 49 | 'lon_min': np.min(lat_lon[:, 1]), 50 | 'lon_max': np.max(lat_lon[:, 1]) 51 | } 52 | 53 | max_lat_dist = max(max_lat_dist, bf_lms['lat_max'] - bf_lms['lat_min']) 54 | max_lon_dist = max(max_lon_dist, bf_lms['lon_max'] - bf_lms['lon_min']) 55 | 56 | for lm in lms: 57 | lm_list[lm].append(bf_lms[lm]) 58 | 59 | lm_argsort = dict() 60 | lm_sort = dict() 61 | for lm in lms: 62 | lm_list[lm] = np.array(lm_list[lm]) 63 | lm_argsort[lm] = np.argsort(lm_list[lm]) 64 | lm_sort[lm] = lm_list[lm][lm_argsort[lm]] 65 | 66 | tract_data = dict() 67 | tract_data['tract_names'] = tract_names 68 | tract_data['tract_boundaries'] = tract_boundaries 69 | tract_data['lm_list'] = lm_list 70 | tract_data['lm_argsort'] = lm_argsort 71 | tract_data['lm_sort'] = lm_sort 72 | tract_data['max_lat_dist'] = max_lat_dist 73 | tract_data['max_lon_dist'] = max_lon_dist 74 | 75 | if self.tract_data is None: 76 | self.tract_data = tract_data 77 | 78 | if tract_data_dir is not None: 79 | if not exists(tract_data_dir): 80 | with open(tract_data_dir, 'wb') as f: 81 | pickle.dump(tract_data, f) 82 | 83 | def get_tract(self, lat, lon): 84 | 85 | ll_arg = {'lat': lat, 'lon': lon} 86 | 87 | idx_dict = dict() 88 | 89 | tracts = None 90 | 91 | for ll in ['lat', 'lon']: 92 | idx_dict[ll] = dict() 93 | 94 | for mm in ['min', 'max']: 95 | 96 | idx_dict[ll][mm] = dict() 97 | for lr in ['left', 'right']: 98 | 99 | if lr == 'left' and mm == 'min': 100 | ref_coord = ll_arg[ll] - self.tract_data[''.join(('max_', ll, '_dist'))] 101 | elif (lr == 'right' and mm == 'min') or (lr == 'left' and mm == 'max'): 102 | ref_coord = ll_arg[ll] 103 | else: 104 | ref_coord = ll_arg[ll] + self.tract_data[''.join(('max_', ll, '_dist'))] 105 | 106 | if lr == 'left': 107 | idx_dict[ll][mm][lr] = bisect_left( 108 | self.tract_data['lm_sort']['_'.join((ll, mm))], 109 | ref_coord 110 | ) 111 | else: 112 | idx_dict[ll][mm][lr] = bisect_right( 113 | self.tract_data['lm_sort']['_'.join((ll, mm))], 114 | ref_coord 115 | ) 116 | 117 | temp_tracts = \ 118 | [self.tract_data['tract_names'][idx] for idx in self.tract_data['lm_argsort']['_'.join((ll, mm))][ 119 | idx_dict[ll][mm]['left']:idx_dict[ll][mm]['right'] 120 | ]] 121 | 122 | if tracts is None: 123 | tracts = set(temp_tracts) 124 | else: 125 | tracts = tracts.intersection(set(temp_tracts)) 126 | 127 | for tract in list(tracts): 128 | bb_path = mplPath.Path(self.tract_data['tract_boundaries'][tract]) 129 | if bb_path.contains_point((lat, lon)): 130 | return tract 131 | 132 | return -1 133 | 134 | if __name__ == '__main__': 135 | ct = CensusTract() 136 | ct.save_tract_data(boundary_file_dir='./Tracts') 137 | ct.get_tract(lat=1, lon=1) 138 | ct.get_tract(lat=32, lon=-86) 139 | ct.get_tract(lat=32.48, lon=-86.5) 140 | 141 | 142 | 143 | -------------------------------------------------------------------------------- /africa/dijkstra.py: -------------------------------------------------------------------------------- 1 | from __future__ import absolute_import 2 | from __future__ import division 3 | from __future__ import print_function 4 | from __future__ import unicode_literals 5 | from __future__ import with_statement 6 | import heapq 7 | import os 8 | import sys 9 | import time 10 | import pickle 11 | import pandas as pd 12 | import matplotlib 13 | matplotlib.use('Agg') 14 | import matplotlib.pyplot as plt 15 | # from numba import autojit 16 | import numpy as np 17 | from tqdm import tqdm 18 | 19 | 20 | def seek(origins, targets=None, weights=None, path_handling='link', debug=False, film=False, 21 | frame_dirname='frames', frame_rate=1000000, early_stop=False): 22 | if weights is None: 23 | weights = np.ones(origins.shape) 24 | if targets is None: 25 | targets = np.zeros(origins.shape, dtype=np.int8) 26 | assert targets.shape == origins.shape 27 | assert targets.shape == weights.shape 28 | path_handling = path_handling.lower() 29 | assert path_handling in ['none', 'n', 'assimilate', 'a', 'link', 'l'] 30 | n_rows, n_cols = origins.shape 31 | if path_handling[0] == 'n': 32 | path_handling = 0 33 | elif path_handling[0] == 'a': 34 | path_handling = 1 35 | elif path_handling[0] == 'l': 36 | path_handling = 2 37 | 38 | iteration = 0 39 | not_visited = 9999999999. 40 | 41 | if film: 42 | # frame_rate = int(1e6) 43 | frame_counter = 100000 44 | # frame_dirname = 'frames' 45 | try: 46 | os.mkdir(frame_dirname) 47 | except Exception: 48 | # NBD 49 | pass 50 | 51 | cwd = os.getcwd() 52 | try: 53 | os.chdir(frame_dirname) 54 | for filename in os.listdir('.'): 55 | os.remove(filename) 56 | except Exception: 57 | print('Frame deletion failed') 58 | finally: 59 | os.chdir(cwd) 60 | 61 | rendering = 1. / (2. * weights) 62 | rendering = np.minimum(rendering, 1.) 63 | target_locations = np.where(targets) 64 | n_targets = target_locations[0].size 65 | n_targets_remaining = n_targets 66 | n_targets_remaining_update = n_targets 67 | for i_target, row in enumerate(target_locations[0]): 68 | col = target_locations[1][i_target] 69 | wid = 8 70 | rendering[ 71 | row - wid: 72 | row + wid + 1, 73 | col - wid: 74 | col + wid + 1] = .5 75 | 76 | # The distance array shows the shortest weighted distance from 77 | # each point in the grid to the nearest origin point. 78 | distance = np.ones((n_rows, n_cols)) * not_visited 79 | origin_locations = np.where(origins != 0) 80 | distance[origin_locations] = 0. 81 | 82 | # The paths array shows each of the paths that are discovered 83 | # from targets to their nearest origin point. 84 | paths = np.zeros((n_rows, n_cols), dtype=np.int8) 85 | 86 | # The halo is the set of points under evaluation. They surround 87 | # the origin points and expand outward, forming a growing halo 88 | # around the set of origins that eventually enevlops targets. 89 | # It is implemented using a heap queue, so that the halo point 90 | # nearest to an origin is always the next one that gets evaluated. 91 | halo = [] 92 | for i, origin_row in enumerate(origin_locations[0]): 93 | origin_col = origin_locations[1][i] 94 | heapq.heappush(halo, (0., (origin_row, origin_col))) 95 | 96 | # The temporary array for tracking locations to add to the halo. 97 | # This gets overwritten with each iteration. 98 | new_locs = np.zeros((int(1e6), 3)) 99 | n_new_locs = 0 100 | 101 | edges = [] ##### 102 | 103 | while len(halo) > 0: 104 | iteration += 1 105 | if debug: 106 | if (n_targets_remaining > n_targets_remaining_update or 107 | iteration % 1e4 == 0.): 108 | n_targets_remaining = n_targets_remaining_update 109 | print('\r {num} targets of {total} reached, {rem} remaining, {halo_len} to try ' 110 | .format( 111 | num=n_targets - n_targets_remaining, 112 | total=n_targets, 113 | rem=n_targets_remaining, 114 | halo_len=len(halo), 115 | ), end='') 116 | sys.stdout.flush() 117 | if film: 118 | if iteration % frame_rate == 0: 119 | frame_counter = render( 120 | distance, 121 | frame_counter, 122 | frame_dirname, 123 | not_visited, 124 | rendering, 125 | ) 126 | 127 | # Reinitialize locations to add. 128 | new_locs[:n_new_locs, :] = 0. 129 | n_new_locs = 0 130 | 131 | # Retrieve and check the location with shortest distance. 132 | (distance_here, (row_here, col_here)) = heapq.heappop(halo) 133 | n_new_locs, n_targets_remaining_update = nb_loop( 134 | col_here, 135 | distance, 136 | distance_here, 137 | n_cols, 138 | n_new_locs, 139 | n_rows, 140 | n_targets_remaining, 141 | new_locs, 142 | not_visited, 143 | origins, 144 | path_handling, 145 | paths, 146 | row_here, 147 | targets, 148 | weights, 149 | edges 150 | ) 151 | for i_loc in range(n_new_locs): 152 | loc = (int(new_locs[i_loc, 1]), int(new_locs[i_loc, 2])) 153 | heapq.heappush(halo, (new_locs[i_loc, 0], loc)) 154 | 155 | if early_stop and n_targets_remaining_update == 0: ##### 156 | break 157 | 158 | if debug: 159 | print('\r ', end='') 160 | sys.stdout.flush() 161 | print('') 162 | # Add the newfound paths to the visualization. 163 | rendering = 1. / (1. + distance / 10.) 164 | rendering[np.where(origins)] = 1. 165 | rendering[np.where(paths)] = .8 166 | results = {'paths': paths, 'distance': distance, 'rendering': rendering, 'edges': edges} 167 | return results 168 | 169 | 170 | def render( 171 | distance, 172 | frame_counter, 173 | frame_dirname, 174 | not_visited, 175 | rendering, 176 | ): 177 | """ 178 | Turn the progress of the algorithm into a pretty picture. 179 | """ 180 | progress = rendering.copy() 181 | visited_locs = np.where(distance < not_visited) 182 | progress[visited_locs] = 1. / (1. + distance[visited_locs] / 10.) 183 | filename = 'pathfinder_frame_' + str(frame_counter) + '.png' 184 | cmap = 'inferno' 185 | dpi = 1200 186 | plt.figure(33374) 187 | plt.clf() 188 | plt.imshow( 189 | np.flip(progress.transpose(), axis=0), 190 | origin='higher', 191 | interpolation='nearest', 192 | cmap=plt.get_cmap(cmap), 193 | vmax=1., 194 | vmin=0., 195 | ) 196 | filename_full = os.path.join(frame_dirname, filename) 197 | plt.tight_layout() 198 | plt.savefig(filename_full, dpi=dpi) 199 | frame_counter += 1 200 | return frame_counter 201 | 202 | 203 | def nb_trace_back( 204 | distance, 205 | n_new_locs, 206 | new_locs, 207 | not_visited, 208 | origins, 209 | path_handling, 210 | paths, 211 | target, 212 | weights, 213 | edges ##### 214 | ): 215 | """ 216 | Connect each found electrified target to the grid through 217 | the shortest available path. 218 | """ 219 | # Handle the case where you find more than one target. 220 | path = [] 221 | distance_remaining = distance[target] 222 | current_location = target 223 | while distance_remaining > 0.: 224 | path.append(current_location) 225 | (row_here, col_here) = current_location 226 | # Check each of the neighbors for the lowest distance to grid. 227 | neighbors = [ 228 | ((row_here - 1, col_here), 1.), 229 | ((row_here + 1, col_here), 1.), 230 | ((row_here, col_here + 1), 1.), 231 | ((row_here, col_here - 1), 1.), 232 | ((row_here - 1, col_here - 1), 2.**.5), 233 | ((row_here + 1, col_here - 1), 2.**.5), 234 | ((row_here - 1, col_here + 1), 2.**.5), 235 | ((row_here + 1, col_here + 1), 2.**.5), 236 | ] 237 | lowest_distance = not_visited 238 | # It's confusing, but keep in mind that 239 | # distance[neighbor] is the distance from the neighbor position 240 | # to the grid, while neighbor_distance is 241 | # the distance *through* 242 | # the neighbor position to the grid. It is distance[neighbor] 243 | # plus the distance to the neighbor from the current position. 244 | for (neighbor, scale) in neighbors: 245 | if neighbor not in path: 246 | distance_from_neighbor = scale * weights[current_location] 247 | neighbor_distance = (distance[neighbor] + 248 | distance_from_neighbor) 249 | if neighbor_distance < lowest_distance: 250 | lowest_distance = neighbor_distance 251 | best_neighbor = neighbor 252 | 253 | # This will fail if caught in a local minimum. 254 | if distance_remaining < distance[best_neighbor]: 255 | distance_remaining = 0. 256 | continue 257 | 258 | distance_remaining = distance[best_neighbor] 259 | current_location = best_neighbor 260 | 261 | # Add this new path. 262 | for i_loc, loc in enumerate(path): 263 | paths[loc] = 1 264 | # If paths are to be linked, include the entire paths as origins and 265 | # add them to new_locs. If targets are to be assimilated, just add 266 | # the target (the first point on the path) to origins and new_locs. 267 | if path_handling == 2 or ( 268 | path_handling == 1 and i_loc == 0): 269 | origins[loc] = 1 270 | distance[loc] = 0. 271 | new_locs[n_new_locs, 0] = 0. 272 | new_locs[n_new_locs, 1] = loc[0] 273 | new_locs[n_new_locs, 2] = loc[1] 274 | n_new_locs += 1 275 | edges.append((path[0], path[-1])) ##### (target, origin) 276 | 277 | return n_new_locs 278 | 279 | 280 | def nb_loop( 281 | col_here, 282 | distance, 283 | distance_here, 284 | n_cols, 285 | n_new_locs, 286 | n_rows, 287 | n_targets_remaining, 288 | new_locs, 289 | not_visited, 290 | origins, 291 | path_handling, 292 | paths, 293 | row_here, 294 | targets, 295 | weights, 296 | edges ##### 297 | ): 298 | """ 299 | This is the meat of the computation. 300 | Pull the computationally expensive operations from seek() 301 | out into their own function that can be pre-compiled using numba. 302 | """ 303 | # Calculate the distance for each of the 8 neighbors. 304 | neighbors = [ 305 | ((row_here - 1, col_here), 1.), 306 | ((row_here + 1, col_here), 1.), 307 | ((row_here, col_here + 1), 1.), 308 | ((row_here, col_here - 1), 1.), 309 | ((row_here - 1, col_here - 1), 2.**.5), 310 | ((row_here + 1, col_here - 1), 2.**.5), 311 | ((row_here - 1, col_here + 1), 2.**.5), 312 | ((row_here + 1, col_here + 1), 2.**.5), 313 | ] 314 | 315 | for (neighbor, scale) in neighbors: 316 | weight = scale * weights[neighbor] 317 | neighbor_distance = distance_here + weight 318 | 319 | if distance[neighbor] == not_visited: 320 | if targets[neighbor]: 321 | n_new_locs = nb_trace_back( 322 | distance, 323 | n_new_locs, 324 | new_locs, 325 | not_visited, 326 | origins, 327 | path_handling, 328 | paths, 329 | neighbor, 330 | weights, 331 | edges 332 | ) 333 | targets[neighbor] = 0 334 | n_targets_remaining -= 1 335 | if neighbor_distance < distance[neighbor]: 336 | distance[neighbor] = neighbor_distance 337 | if (neighbor[0] > 0 and 338 | neighbor[0] < n_rows - 1 and 339 | neighbor[1] > 0 and 340 | neighbor[1] < n_cols - 1): 341 | new_locs[n_new_locs, 0] = distance[neighbor] 342 | new_locs[n_new_locs, 1] = neighbor[0] 343 | new_locs[n_new_locs, 2] = neighbor[1] 344 | n_new_locs += 1 345 | return n_new_locs, n_targets_remaining 346 | 347 | -------------------------------------------------------------------------------- /africa/image_dataset.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function 2 | from __future__ import division 3 | import torch 4 | import torch.nn as nn 5 | import torch.optim as optim 6 | from torch.utils.data import Dataset, DataLoader 7 | import torchvision 8 | from torchvision import datasets, models, transforms, utils 9 | import torchvision.transforms.functional as TF 10 | 11 | import numpy as np 12 | import json 13 | import pandas as pd 14 | import pickle 15 | import matplotlib.pyplot as plt 16 | # import skimage 17 | # import skimage.io 18 | # import skimage.transform 19 | from PIL import Image 20 | import time 21 | import os 22 | from os.path import join, exists 23 | import copy 24 | import random 25 | from collections import OrderedDict 26 | 27 | 28 | class ImageFolderModified(Dataset): 29 | def __init__(self, root_dir, transform): 30 | self.root_dir = root_dir 31 | self.transform = transform 32 | self.idx2dir = [] 33 | self.path_list = [] 34 | for subdir in sorted(os.listdir(self.root_dir)): 35 | if not os.path.isfile(subdir): 36 | self.idx2dir.append(subdir) 37 | for class_idx, subdir in enumerate(self.idx2dir): 38 | class_dir = os.path.join(self.root_dir, subdir) 39 | for f in os.listdir(class_dir): 40 | if f[-4:] in ['.png', '.jpg', 'JPEG', 'jpeg']: 41 | self.path_list.append( 42 | [os.path.join(class_dir, f), class_idx]) 43 | 44 | def __len__(self): 45 | return len(self.path_list) 46 | 47 | def __getitem__(self, idx): 48 | img_path, class_idx = self.path_list[idx] 49 | image = Image.open(img_path) 50 | if not image.mode == 'RGB': 51 | image = image.convert('RGB') 52 | image = self.transform(image) 53 | sample = [image, class_idx, img_path] 54 | return sample 55 | 56 | 57 | class ImagePredictDataset(Dataset): 58 | def __init__(self, root_dir, transform): 59 | self.root_dir = root_dir 60 | self.transform = transform 61 | self.idx2dir = [] 62 | self.path_list = [] 63 | for f in os.listdir(root_dir): 64 | if f[-4:] in ['.png', '.jpg', 'JPEG', 'jpeg']: 65 | self.path_list.append(os.path.join(root_dir, f)) 66 | 67 | def __len__(self): 68 | return len(self.path_list) 69 | 70 | def __getitem__(self, idx): 71 | img_path = self.path_list[idx] 72 | image = Image.open(img_path) 73 | if not image.mode == 'RGB': 74 | image = image.convert('RGB') 75 | image = self.transform(image) 76 | sample = [image, img_path] 77 | return sample 78 | -------------------------------------------------------------------------------- /africa/inception_modified.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function 2 | from __future__ import division 3 | import torch 4 | import torch.nn as nn 5 | import torch.optim as optim 6 | from torch.utils.data import Dataset, DataLoader 7 | import torchvision 8 | from torchvision import datasets, models, transforms, utils 9 | import torchvision.transforms.functional as TF 10 | 11 | from tqdm import tqdm 12 | import numpy as np 13 | import json 14 | import pandas as pd 15 | import pickle 16 | import matplotlib.pyplot as plt 17 | # import skimage 18 | # import skimage.io 19 | # import skimage.transform 20 | from PIL import Image 21 | import time 22 | import os 23 | from os.path import join, exists 24 | import copy 25 | import random 26 | from collections import OrderedDict 27 | from sklearn.metrics import r2_score 28 | 29 | 30 | import torch.nn.functional as F 31 | from torchvision.models import Inception3 32 | from collections import namedtuple 33 | 34 | _InceptionOuputs = namedtuple('InceptionOuputs', ['logits', 'aux_logits']) 35 | 36 | 37 | class InceptionSegmentation(nn.Module): 38 | def __init__(self, num_outputs=2, level=1): 39 | super(InceptionSegmentation, self).__init__() 40 | assert level in [1, 2] 41 | self.level = level 42 | self.inception3 = Inception3_modified(num_classes=num_outputs, aux_logits=False, transform_input=False) 43 | self.convolution1 = nn.Conv2d(288, 512, bias=True, kernel_size=3, padding=1) 44 | if self.level == 1: 45 | self.linear1 = nn.Linear(512, num_outputs, bias=False) 46 | else: 47 | self.convolution2 = nn.Conv2d(512, 512, bias=True, kernel_size=3, padding=1) 48 | self.linear2 = nn.Linear(512, num_outputs, bias=False) 49 | 50 | def forward(self, x, testing=False): 51 | logits, intermediate = self.inception3(x) 52 | feature_map = self.convolution1(intermediate) # N x 512 x 35 x 35 53 | feature_map = F.relu(feature_map) # N x 512 x 35 x 35 54 | if self.level == 1: 55 | y = F.adaptive_avg_pool2d(feature_map, (1, 1)) 56 | y = y.view(y.size(0), -1) # N x 512 57 | y = self.linear1(y) # N x 2 58 | if testing: 59 | CAM = self.linear1.weight.data[1, :] * feature_map.permute(0, 2, 3, 1) 60 | CAM = CAM.sum(dim=3) 61 | else: 62 | feature_map = self.convolution2(feature_map) # N x 512 x 35 x 35 63 | feature_map = F.relu(feature_map) # N x 512 x 35 x 35 64 | y = F.adaptive_avg_pool2d(feature_map, (1, 1)) 65 | y = y.view(y.size(0), -1) # N x 512 66 | y = self.linear2(y) # N x 2 67 | if testing: 68 | CAM = self.linear2.weight.data[1, :] * feature_map.permute(0, 2, 3, 1) 69 | CAM = CAM.sum(dim=3) 70 | if testing: 71 | return y, logits, CAM 72 | else: 73 | return y 74 | 75 | def load_basic_params(self, model_path, device=torch.device('cpu')): 76 | """Only load the parameters from main branch.""" 77 | old_params = torch.load(model_path, map_location=device) 78 | if model_path[-4:] == '.tar': # The file is not a model state dict, but a checkpoint dict 79 | old_params = old_params['model_state_dict'] 80 | self.inception3.load_state_dict(old_params, strict=False) 81 | print('Loaded basic model parameters from: ' + model_path) 82 | 83 | def load_existing_params(self, model_path, device=torch.device('cpu')): 84 | """Load the parameters of main branch and parameters of level-1 layers (and perhaps level-2 layers.)""" 85 | old_params = torch.load(model_path, map_location=device) 86 | if model_path[-4:] == '.tar': # The file is not a model state dict, but a checkpoint dict 87 | old_params = old_params['model_state_dict'] 88 | self.load_state_dict(old_params, strict=False) 89 | print('Loaded existing model parameters from: ' + model_path) 90 | 91 | 92 | class Inception3_modified(Inception3): 93 | def forward(self, x): 94 | if self.transform_input: 95 | x_ch0 = torch.unsqueeze(x[:, 0], 1) * (0.229 / 0.5) + (0.485 - 0.5) / 0.5 96 | x_ch1 = torch.unsqueeze(x[:, 1], 1) * (0.224 / 0.5) + (0.456 - 0.5) / 0.5 97 | x_ch2 = torch.unsqueeze(x[:, 2], 1) * (0.225 / 0.5) + (0.406 - 0.5) / 0.5 98 | x = torch.cat((x_ch0, x_ch1, x_ch2), 1) 99 | # N x 3 x 299 x 299 100 | x = self.Conv2d_1a_3x3(x) 101 | # N x 32 x 149 x 149 102 | x = self.Conv2d_2a_3x3(x) 103 | # N x 32 x 147 x 147 104 | x = self.Conv2d_2b_3x3(x) 105 | # N x 64 x 147 x 147 106 | x = F.max_pool2d(x, kernel_size=3, stride=2) 107 | # N x 64 x 73 x 73 108 | x = self.Conv2d_3b_1x1(x) 109 | # N x 80 x 73 x 73 110 | x = self.Conv2d_4a_3x3(x) 111 | # N x 192 x 71 x 71 112 | intermediate = x.clone() 113 | x = F.max_pool2d(x, kernel_size=3, stride=2) 114 | # N x 192 x 35 x 35 115 | x = self.Mixed_5b(x) 116 | # N x 256 x 35 x 35 117 | x = self.Mixed_5c(x) 118 | # N x 288 x 35 x 35 119 | x = self.Mixed_5d(x) 120 | intermediate = x.clone() 121 | # N x 288 x 35 x 35 122 | x = self.Mixed_6a(x) 123 | # N x 768 x 17 x 17 124 | x = self.Mixed_6b(x) 125 | # N x 768 x 17 x 17 126 | x = self.Mixed_6c(x) 127 | # N x 768 x 17 x 17 128 | x = self.Mixed_6d(x) 129 | # N x 768 x 17 x 17 130 | x = self.Mixed_6e(x) 131 | # N x 768 x 17 x 17 132 | if self.training and self.aux_logits: 133 | aux = self.AuxLogits(x) 134 | # N x 768 x 17 x 17 135 | x = self.Mixed_7a(x) 136 | # N x 1280 x 8 x 8 137 | x = self.Mixed_7b(x) 138 | # N x 2048 x 8 x 8 139 | x = self.Mixed_7c(x) 140 | # N x 2048 x 8 x 8 141 | # Adaptive average pooling 142 | x = F.adaptive_avg_pool2d(x, (1, 1)) 143 | # N x 2048 x 1 x 1 144 | x = F.dropout(x, training=self.training) 145 | # N x 2048 x 1 x 1 146 | x = x.view(x.size(0), -1) 147 | # N x 2048 148 | x = self.fc(x) 149 | # N x 1000 (num_classes) 150 | if self.training and self.aux_logits: 151 | return _InceptionOuputs(x, aux) 152 | return x, intermediate 153 | -------------------------------------------------------------------------------- /africa/region_locator.py: -------------------------------------------------------------------------------- 1 | from os.path import join, exists 2 | import pandas as pd 3 | import numpy as np 4 | import pickle 5 | from bisect import bisect_left, bisect_right 6 | import os 7 | 8 | from shapely.geometry import Polygon, Point 9 | import shapely 10 | 11 | 12 | class RegionLocator(object): 13 | def __init__(self, locator_data_dir=None): 14 | self.locator_data = None 15 | if locator_data_dir is not None: 16 | if exists(locator_data_dir): 17 | with open(locator_data_dir, 'rb') as f: 18 | self.locator_data = pickle.load(f) 19 | 20 | def save_locator_data(self, boundary_file_dir, locator_data_dir=None): 21 | with open(boundary_file_dir, 'rb') as f: 22 | boundaries = pickle.load(f) 23 | region_names = list(boundaries.keys()) 24 | lms = ('lat_min', 'lat_max', 'lon_min', 'lon_max') 25 | lm_list = dict() 26 | for lm in lms: 27 | lm_list[lm] = [] 28 | max_lat_dist = -1 29 | max_lon_dist = -1 30 | for region in region_names: 31 | bd = boundaries[region] 32 | bf_lms = { 33 | 'lat_min': bd.bounds[1], 34 | 'lat_max': bd.bounds[3], 35 | 'lon_min': bd.bounds[0], 36 | 'lon_max': bd.bounds[2] 37 | } 38 | max_lat_dist = max(max_lat_dist, bf_lms['lat_max'] - bf_lms['lat_min']) 39 | max_lon_dist = max(max_lon_dist, bf_lms['lon_max'] - bf_lms['lon_min']) 40 | 41 | for lm in lms: 42 | lm_list[lm].append(bf_lms[lm]) 43 | 44 | lm_argsort = dict() 45 | lm_sort = dict() 46 | for lm in lms: 47 | lm_list[lm] = np.array(lm_list[lm]) 48 | lm_argsort[lm] = np.argsort(lm_list[lm]) 49 | lm_sort[lm] = lm_list[lm][lm_argsort[lm]] 50 | 51 | locator_data = dict() 52 | locator_data['region_names'] = region_names 53 | locator_data['boundaries'] = boundaries 54 | locator_data['lm_list'] = lm_list 55 | locator_data['lm_argsort'] = lm_argsort 56 | locator_data['lm_sort'] = lm_sort 57 | locator_data['max_lat_dist'] = max_lat_dist 58 | locator_data['max_lon_dist'] = max_lon_dist 59 | 60 | if self.locator_data is None: 61 | self.locator_data = locator_data 62 | 63 | if locator_data_dir is not None: 64 | if not exists(locator_data_dir): 65 | with open(locator_data_dir, 'wb') as f: 66 | pickle.dump(locator_data, f) 67 | 68 | def locate(self, lat, lon): 69 | ll_arg = {'lat': lat, 'lon': lon} 70 | idx_dict = dict() 71 | regions = None 72 | for ll in ['lat', 'lon']: 73 | idx_dict[ll] = dict() 74 | for mm in ['min', 'max']: 75 | idx_dict[ll][mm] = dict() 76 | for lr in ['left', 'right']: 77 | if lr == 'left' and mm == 'min': 78 | ref_coord = ll_arg[ll] - self.locator_data[''.join(('max_', ll, '_dist'))] 79 | elif (lr == 'right' and mm == 'min') or (lr == 'left' and mm == 'max'): 80 | ref_coord = ll_arg[ll] 81 | else: 82 | ref_coord = ll_arg[ll] + self.locator_data[''.join(('max_', ll, '_dist'))] 83 | 84 | if lr == 'left': 85 | idx_dict[ll][mm][lr] = bisect_left( 86 | self.locator_data['lm_sort']['_'.join((ll, mm))], 87 | ref_coord 88 | ) 89 | else: 90 | idx_dict[ll][mm][lr] = bisect_right( 91 | self.locator_data['lm_sort']['_'.join((ll, mm))], 92 | ref_coord 93 | ) 94 | 95 | temp_regions = \ 96 | [self.locator_data['region_names'][idx] for idx in self.locator_data['lm_argsort']['_'.join((ll, mm))][ 97 | idx_dict[ll][mm]['left']:idx_dict[ll][mm]['right'] 98 | ]] 99 | 100 | if regions is None: 101 | regions = set(temp_regions) 102 | else: 103 | regions = regions.intersection(set(temp_regions)) 104 | 105 | for region in list(regions): 106 | poly = self.locator_data['boundaries'][region] 107 | if poly.contains(Point(lon, lat)): 108 | return region 109 | return -1 110 | 111 | -------------------------------------------------------------------------------- /africa/streetView.py: -------------------------------------------------------------------------------- 1 | import urllib.request 2 | import urllib 3 | import json 4 | 5 | 6 | class StreetView(): 7 | """Google Street View API application class""" 8 | 9 | def __init__(self): 10 | self.api_key_list = [ 11 | '***************************************', # Your API key 1 12 | '***************************************', # Your API key 2 13 | '***************************************', # Your API key 3 14 | ] 15 | self.key_rank = 0 16 | self.API_KEY = self.api_key_list[self.key_rank] 17 | 18 | def getMetaStatus(self, location, heading='0', pitch='0', fov='90', 19 | radius='50', returnMeta=False): 20 | # retrieve the meta data through meta API. The returned variable is a dictionary. 21 | # If the image exists at that point, meta['status'] = 'OK', otherwise: meta['status'] = 'ZERO_RESULTS' 22 | # For every coordinates, please run this to check existence before downloading image!! 23 | self.setParameters(location=location, heading=heading, 24 | pitch=pitch, fov=fov, radius=radius) 25 | # print(self.parameters) 26 | meta_url = 'https://maps.googleapis.com/maps/api/streetview/metadata?'+self.parameters 27 | response = urllib.request.urlopen(meta_url) 28 | meta = response.read() 29 | meta = json.loads(meta) 30 | if (returnMeta): 31 | self.nextKey() 32 | return meta['status'], meta 33 | else: 34 | return meta['status'] 35 | 36 | def getStreetView(self, location, filepath='dafault.jpg', heading='0', pitch='0', fov='90', radius='50', uncheck=True): 37 | # download images from image_url to local path 38 | # You need to specify "size", "location", "fov", "heading", "pitch", see details in Google API's documentation 39 | # in Python 3, you may use urllib.request.urlretrieve instead of urllib.urlretrieve 40 | # uncheck==False: download without check meta 41 | # filepath : image save path 42 | if(uncheck): 43 | status = self.getMetaStatus( 44 | location=location, heading=heading, pitch=pitch, fov=fov, radius=radius) 45 | else: 46 | self.setParameters(location=location, heading=heading, 47 | pitch=pitch, fov=fov, radius=radius) 48 | status = 'OK' 49 | 50 | if(status == 'OK'): 51 | image_url = 'https://maps.googleapis.com/maps/api/streetview?' + self.parameters 52 | local_path = filepath 53 | urllib.request.urlretrieve(image_url, local_path) 54 | self.nextKey() 55 | # print('Success!') 56 | return True 57 | elif (status == 'ZERO_RESULTS'): 58 | # not found 59 | # print('ZERO_RESULTS') 60 | return False 61 | else: 62 | print('Error:'+status) 63 | return False 64 | 65 | def nextKey(self): 66 | self.key_rank += 1 67 | if (self.key_rank >= len(self.api_key_list)): 68 | self.key_rank = 0 69 | self.API_KEY = self.api_key_list[self.key_rank] 70 | 71 | def setParameters(self, location, heading, pitch, fov, radius): 72 | size = ['640', '640'] 73 | #latitude, longitude 74 | location = [str(location[0]), str(location[1])] 75 | self.parameters = 'size=' + size[0] + 'x' + size[1] + '&location=' + location[0] + ',' + location[1] + \ 76 | '&fov=' + fov + '&heading=' + heading + '&pitch=' + pitch + \ 77 | '&radius=' + radius + '&key=' + self.API_KEY 78 | -------------------------------------------------------------------------------- /california/11_dijkstria_algorithm.py: -------------------------------------------------------------------------------- 1 | from __future__ import absolute_import 2 | from __future__ import division 3 | from __future__ import print_function 4 | from __future__ import unicode_literals 5 | from __future__ import with_statement 6 | import heapq 7 | import os 8 | import sys 9 | import time 10 | import pickle 11 | import pandas as pd 12 | import matplotlib 13 | matplotlib.use('Agg') 14 | import matplotlib.pyplot as plt 15 | # from numba import autojit 16 | import numpy as np 17 | import census_tract_locator 18 | from tqdm import tqdm 19 | from os.path import join, exists 20 | from utils import * 21 | from dijkstra import * 22 | 23 | """ 24 | This scripts is for running the modified Dijkstra's algorithm to predict 25 | the connections between poles. This algorithm greedily seeks the paths to 26 | connect all predicted poles with minimum total weight. Each cell in the 27 | raster is assigned with a weight. 28 | The Dijkstra's algorithm is adapted from: 29 | https://github.com/facebookresearch/many-to-many-dijkstra 30 | """ 31 | 32 | pole_model_list = ['ori0.5'] 33 | region_list = ['SanCarlos', 'Newark', 'SantaCruz', 'Yuba', 'Monterey', 'Salinas'] 34 | save_raw_results = False 35 | 36 | def determine_root_dir(): 37 | """ 38 | This function is used to locate the root dir back to the parent directory, 39 | i.e., "GridMapping" directory. 40 | """ 41 | root_dir = os.path.abspath(os.path.join(os.getcwd(), os.pardir)) 42 | assert root_dir.strip('/')[-11:] == 'GridMapping' 43 | return root_dir 44 | 45 | root_dir = determine_root_dir() 46 | # change the root dir to "GridMapping" 47 | os.chdir(root_dir) 48 | 49 | # model parameters 50 | weight_scheme = {'motorway': 1/10, 'motorway_link': 1/10, 51 | 'primary': 1/8, 'primary_link': 1/8, 52 | 'secondary': 1/7, 'secondary_link': 1/7, 53 | 'tertiary': 1/6, 'tertiary_link': 1/6, 54 | 'unclassified': 1/5, 55 | 'residential': 1/4, 'living_street': 1/4, 'cycleway': 1/4, 56 | 'service': 1/3, 'footway': 1/3, 'track': 1/3, 'pedestrian': 1/3, 57 | 'path': 1/2, 'highway': 2/3 58 | } 59 | 60 | # the point to start 61 | start_lat_lon_dict = { 62 | 'SanCarlos': (37.4996447, -122.2440506), 63 | 'Newark': (37.54784131, -122.04436975), 64 | 'SantaCruz': (36.97630256, -121.9646515), 65 | 'Yuba': (39.13770213, -121.6090814), 66 | 'Monterey': (36.61717734, -121.91922238), 67 | 'Salinas': (36.6738711, -121.62761453), 68 | } 69 | 70 | 71 | # functions 72 | def find_nearest_road_point(ring_xy_diff_dict, pole_point, road_filled_dict, max_search_radius=5): 73 | assert 0 <= max_search_radius <= 200 74 | x, y = pole_point 75 | for radius in range(0, max_search_radius + 1): 76 | for dx, dy in ring_xy_diff_dict[radius]: 77 | if (x+dx, y+dy) in road_filled_dict: 78 | return (x+dx, y+dy) 79 | return -1 80 | 81 | 82 | def scale_weight_with_street_view(ring_xy_diff_dict, road_weight_matrix, sv_pos_filled_grid, sv_neg_filled_grid, 83 | expand_radius=3, pos_scale=1.0, neg_scale=1.0): 84 | """scale the weights of road_weight_matrix with a factor that depends on whether there is a street view points, 85 | and whether the street view is positive (contains line) or negative (contains no line).""" 86 | assert pos_scale <= 1 and neg_scale >= 1 87 | x_max = road_weight_matrix.shape[0] - 1 88 | y_max = road_weight_matrix.shape[1] - 1 89 | if pos_scale != 1: 90 | for xy in sv_pos_filled_grid: 91 | x, y = xy 92 | for radius in range(0, expand_radius + 1): 93 | for dx, dy in ring_xy_diff_dict[radius]: 94 | if 0 <= x + dx <= x_max and 0 <= y + dy <= y_max and road_weight_matrix[x+dx, y+dy] < 1: 95 | road_weight_matrix[x+dx, y+dy] = road_weight_matrix[x+dx, y+dy] * pos_scale 96 | if neg_scale != 1: 97 | for xy in sv_neg_filled_grid: 98 | x, y = xy 99 | for radius in range(0, expand_radius + 1): 100 | for dx, dy in ring_xy_diff_dict[radius]: 101 | if 0 <= x + dx <= x_max and 0 <= y + dy <= y_max and road_weight_matrix[x+dx, y+dy] < 1: 102 | road_weight_matrix[x+dx, y+dy] = min(road_weight_matrix[x+dx, y+dy] * neg_scale, 1.0) 103 | return road_weight_matrix 104 | 105 | 106 | def load_road_data(region): 107 | with open(join('data/road_info', region, 108 | 'way_coord_dict_processed.pickle'), 'rb') as f: 109 | way_coord_dict_sub = pickle.load(f) 110 | with open(join('data/road_info', region, 'way_tag_dict_local.pickle'), 111 | 'rb') as f: 112 | way_tag_dict_sub = pickle.load(f) 113 | return way_coord_dict_sub, way_tag_dict_sub 114 | 115 | 116 | def load_discretization_params(region): 117 | # discretization parameters 118 | with open('results/' + region + '/discretization_parameters.pickle', 'rb') as f: 119 | discretization_params = pickle.load(f) 120 | lat_s = discretization_params['lat_s'] 121 | lat_n = discretization_params['lat_n'] 122 | lon_w = discretization_params['lon_w'] 123 | lon_e = discretization_params['lon_e'] 124 | dlat0 = discretization_params['dlat0'] 125 | dlon0 = discretization_params['dlon0'] 126 | return lat_s, lat_n, lon_w, lon_e, dlat0, dlon0 127 | 128 | 129 | def load_result_data(region, pole_model): 130 | with open(join('results', region, pole_model, 'all_predicted_poles.pickle'), 'rb') as f: 131 | all_predicted_poles = pickle.load(f) 132 | # line prediction 133 | with open(join('results', region, 'line_info_merged.pickle'), 'rb') as f: 134 | sv2line = pickle.load(f) 135 | idx2sv_pos = [] 136 | idx2sv_neg = [] 137 | for i in range(len(sv2line)): 138 | coord, line_list = sv2line[i] 139 | coord = tuple(coord) 140 | if len(line_list) > 0: 141 | idx2sv_pos.append(coord) 142 | else: 143 | idx2sv_neg.append(coord) 144 | return all_predicted_poles, sv2line, idx2sv_pos, idx2sv_neg 145 | 146 | 147 | def discretize(sh2, way_coord_dict_sub, all_predicted_poles, idx2sv_pos, idx2sv_neg): 148 | # discretize roads 149 | road_grid = Grid(sh2) 150 | road_grid.construct_from_way_coord_dict(way_coord_dict_sub) 151 | 152 | # discretize predicted poles 153 | all_predicted_pole_list = [] 154 | for idx in all_predicted_poles: 155 | lat, lon = all_predicted_poles[idx] 156 | all_predicted_pole_list.append((lat, lon, idx)) 157 | 158 | all_pole_grid = Grid(sh2) 159 | all_pole_grid.construct_from_coord_list(all_predicted_pole_list) 160 | 161 | # discretize street view points 162 | sv_grid = Grid(sh2) 163 | sv_grid.construct_from_coord_list(idx2sv_pos) 164 | 165 | sv_grid_neg = Grid(sh2) 166 | sv_grid_neg.construct_from_coord_list(idx2sv_neg) 167 | return road_grid, all_pole_grid, sv_grid, sv_grid_neg, all_predicted_pole_list 168 | 169 | 170 | def attach_poles_to_roads(ring_xy_diff_dict, road_grid, all_pole_grid): 171 | # attached poles 172 | max_search_radius = 5 # in number of grids, instead of meters!! 173 | unattached = 0 174 | attached_pole_dict = {} 175 | attached_pole_dict_inversed = {} 176 | for pole_point in all_pole_grid.filled_dict: 177 | nearest_road_point = find_nearest_road_point(ring_xy_diff_dict, pole_point, road_grid.filled_dict, 178 | max_search_radius=max_search_radius) 179 | if nearest_road_point == -1: # unattached 180 | unattached += 1 181 | else: 182 | attached_pole_dict[pole_point] = nearest_road_point 183 | attached_pole_dict_inversed[nearest_road_point] = pole_point 184 | print('# poles unattached to the road:', unattached) 185 | return attached_pole_dict, attached_pole_dict_inversed 186 | 187 | 188 | def get_road_matrix(sh2, road_grid, way_tag_dict_sub): 189 | x_max = sh2.x_max 190 | y_max = sh2.y_max 191 | road_weight_matrix_original = np.ones((x_max + 1, y_max + 1)) 192 | for xy in tqdm(road_grid.filled_dict): 193 | x, y = xy 194 | w = 1.0 195 | for way_id in road_grid.filled_dict[xy]: 196 | if not len(way_tag_dict_sub[way_id]) == 0: 197 | for tag in way_tag_dict_sub[way_id][0]: 198 | if tag in weight_scheme: 199 | w = min(w, weight_scheme[tag]) 200 | road_weight_matrix_original[x, y] = w 201 | return road_weight_matrix_original 202 | 203 | 204 | def get_target_and_origin_matrix(start_lat, start_lon, x_max, y_max, all_predicted_pole_list, all_pole_grid, 205 | attached_pole_dict, attached=False): 206 | dist_list = [] 207 | for i, p in enumerate(all_predicted_pole_list): 208 | lat, lon, idx = p 209 | dist = calculate_dist(lat, lon, start_lat, start_lon) 210 | dist_list.append((i, dist, idx)) 211 | dist_list = sorted(dist_list, key=lambda x: x[1]) 212 | start_pole = dist_list[0][2] 213 | 214 | for xy in all_pole_grid.filled_dict: 215 | if start_pole in all_pole_grid.filled_dict[xy]: 216 | start_x, start_y = xy 217 | break 218 | print('start_x: ', start_x, 'start_y', start_y) 219 | 220 | if attached: 221 | ############# Attached case ############# 222 | target_matrix = np.zeros((x_max + 1, y_max + 1)) 223 | for xy in all_pole_grid.filled_dict: 224 | x, y = xy 225 | v = sorted(all_pole_grid.filled_dict[xy])[0] + 5 226 | if xy in attached_pole_dict: 227 | x, y = attached_pole_dict[xy] 228 | target_matrix[x, y] = v 229 | 230 | # configure start point and construct origin matrix 231 | start_x_road, start_y_road = attached_pole_dict[(start_x, start_y)] 232 | origin_matrix = np.zeros((x_max + 1, y_max + 1)) 233 | origin_matrix[start_x, start_y] = target_matrix[start_x_road, start_y_road] 234 | target_matrix[start_x, start_y] = 0 235 | else: 236 | ############# Unattached case ############# 237 | target_matrix = np.zeros((x_max + 1, y_max + 1)) 238 | for xy in all_pole_grid.filled_dict: 239 | x, y = xy 240 | v = sorted(all_pole_grid.filled_dict[xy])[0] + 5 241 | target_matrix[x, y] = v 242 | 243 | # configure start point and construct origin matrix 244 | origin_matrix = np.zeros((x_max + 1, y_max + 1)) 245 | origin_matrix[start_x, start_y] = target_matrix[start_x, start_y] 246 | target_matrix[start_x, start_y] = 0 247 | return target_matrix, origin_matrix 248 | 249 | 250 | def get_edge_set(results, all_pole_grid, ring_xy_diff_dict, all_predicted_pole_list): 251 | edge_set = set() 252 | for xy1, xy2 in results['edges']: 253 | i1_set = all_pole_grid.filled_dict[xy1] 254 | if xy2 not in all_pole_grid.filled_dict: 255 | nearest = find_nearest_road_point(ring_xy_diff_dict, xy2, all_pole_grid.filled_dict, max_search_radius=1) 256 | i2_set = all_pole_grid.filled_dict[nearest] 257 | else: 258 | i2_set = all_pole_grid.filled_dict[xy2] 259 | for i1 in i1_set: 260 | for i2 in i2_set: 261 | pid1 = all_predicted_pole_list[i1][2] 262 | pid2 = all_predicted_pole_list[i2][2] 263 | if type(pid1) == type(pid2): 264 | edge = sorted((pid1, pid2)) 265 | elif type(pid1) == str and type(pid2) == int: 266 | edge = (pid2, pid1) 267 | else: 268 | edge = (pid1, pid2) 269 | edge_set.add(tuple(edge)) 270 | return edge_set 271 | 272 | if __name__ == '__main__': 273 | # ring (used for obtaining all cells within a certain radius of the center on a raster map) 274 | with open('data/ring_xy_diff_dict_200.pickle', 'rb') as f: 275 | ring_xy_diff_dict = pickle.load(f) 276 | 277 | for region in region_list: 278 | # load road data 279 | way_coord_dict_sub, way_tag_dict_sub = load_road_data(region) 280 | print('# roads:', len(way_coord_dict_sub)) 281 | 282 | lat_s, lat_n, lon_w, lon_e, dlat0, dlon0 = load_discretization_params(region) 283 | 284 | for pole_model in pole_model_list: 285 | print('-' * 20 + ' Region: ' + region + ', Pole model: ' + pole_model + ' ' + '-' * 20) 286 | 287 | # load results from previous steps 288 | all_predicted_poles, sv2line, idx2sv_pos, idx2sv_neg = load_result_data(region, pole_model) 289 | print('# predicted poles in total (detected + inserted):', len(all_predicted_poles)) 290 | print('# street view points', len(sv2line)) 291 | print('# positive street view points', len(idx2sv_pos)) 292 | print('# negative street view points', len(idx2sv_neg)) 293 | 294 | sh2 = SpatialHashing(unit=2, lat_s=lat_s, lon_w=lon_w, lat_n=lat_n, lon_e=lon_e, 295 | dlat0=dlat0, dlon0=dlon0) 296 | 297 | # discretize roads, poles, and street view points 298 | road_grid, all_pole_grid, sv_grid, sv_grid_neg, all_predicted_pole_list = discretize(sh2, 299 | way_coord_dict_sub, 300 | all_predicted_poles, 301 | idx2sv_pos, idx2sv_neg) 302 | 303 | # attach poles to nearest roads 304 | attached_pole_dict, _ = attach_poles_to_roads(ring_xy_diff_dict, road_grid, all_pole_grid) 305 | 306 | # construct road_weight_matrix 307 | road_weight_matrix = get_road_matrix(sh2, road_grid, way_tag_dict_sub) 308 | 309 | road_weight_matrix = scale_weight_with_street_view(ring_xy_diff_dict, road_weight_matrix, sv_grid.filled_dict, 310 | sv_grid_neg.filled_dict, expand_radius=5) 311 | 312 | start_lat, start_lon = start_lat_lon_dict[region] 313 | 314 | target_matrix, origin_matrix = get_target_and_origin_matrix(start_lat, start_lon, sh2.x_max, sh2.y_max, 315 | all_predicted_pole_list, all_pole_grid, 316 | attached_pole_dict, attached=False) 317 | 318 | origin_matrix_update = origin_matrix.copy() 319 | target_matrix_update = target_matrix.copy() 320 | 321 | results = seek(origins=origin_matrix_update, 322 | targets=target_matrix_update, 323 | weights=road_weight_matrix, 324 | path_handling='assimilate', 325 | debug=True, 326 | film=False, 327 | frame_dirname=None, 328 | frame_rate=100000, 329 | early_stop=True) 330 | 331 | edge_set = get_edge_set(results, all_pole_grid, ring_xy_diff_dict, all_predicted_pole_list) 332 | 333 | print('number of edges connected by dijkstra:', len(edge_set)) 334 | 335 | with open(join('results', region, pole_model, 'dijkstra_edge_set_assimilate_unattached_noscaling.pickle'), 336 | 'wb') as f: 337 | pickle.dump(edge_set, f) 338 | 339 | if save_raw_results: 340 | with open(join('results', region, pole_model, 341 | 'dijkstra_raw_results_assimilate_unattached_noscaling.pickle'), 342 | 'wb') as f: 343 | pickle.dump(results, f) 344 | 345 | print('Done.') 346 | -------------------------------------------------------------------------------- /california/1_search_area_GSV.py: -------------------------------------------------------------------------------- 1 | import math 2 | from matplotlib.path import Path 3 | import matplotlib.pyplot as plt 4 | import numpy as np 5 | import pickle 6 | import os 7 | from os.path import join, exists 8 | from tqdm import tqdm 9 | from streetView import StreetView 10 | 11 | """ 12 | This script is for retrieving the street view meta data for each test area. 13 | """ 14 | 15 | region = 'Salinas' 16 | start_i = 0 17 | 18 | # census tract list for each test area 19 | fips_list_dict = { 20 | 'SanCarlos': [6081609100, 6081609201, 6081609202, 6081609300, 6081609400, 6081609500, 6081609601, 6081609602, 6081609603], 21 | 'Newark': [6001444100, 6001444200, 6001444301, 6001444400, 6001444500, 6001444601], 22 | 'SantaCruz': [6087121300, 6087121401, 6087121402, 6087121403, 6087121700], 23 | 'Yuba': [6101050201, 6101050202, 6101050301, 6101050302], 24 | 'Monterey': [6053012401, 6053012402, 6053012302, 6053012200, 6053012100, 6053012000], 25 | 'Salinas': [6053000800, 6053000600, 6053000701, 6053000702], 26 | } 27 | 28 | boundaries_keys = fips_list_dict[region] 29 | 30 | def determine_root_dir(): 31 | """ 32 | This function is used to locate the root dir back to the parent directory, 33 | i.e., "GridMapping" directory. 34 | """ 35 | root_dir = os.path.abspath(os.path.join(os.getcwd(), os.pardir)) 36 | assert root_dir.strip('/')[-11:] == 'GridMapping' 37 | return root_dir 38 | 39 | root_dir = determine_root_dir() 40 | # change the root dir to "GridMapping" 41 | os.chdir(root_dir) 42 | 43 | if not exists(join('results', region)): 44 | os.mkdir(join('results', region)) 45 | 46 | boundaries_file_filepath = '/home/ubuntu/projects/DeepGrid_run_data/tract_boundaries_us.pickle' 47 | GSV_points_filepath = join('results', region, 'validGSVpoints.pickle') 48 | fips_mapping_path = join('results', region, 'GSVpoint2fips.pickle') 49 | save_per_points = 5000 50 | 51 | # %% set boundary paths 52 | with open(boundaries_file_filepath, 'rb') as f: 53 | boundaries_raw = pickle.load(f, encoding='latin1') 54 | f.close() 55 | 56 | boundaries = [] 57 | for key in boundaries_keys: 58 | boundaries.append(boundaries_raw[str.format('%011d' % key)]) 59 | 60 | # %% get scan area mesh points 61 | bd = [] 62 | paths = [] 63 | for boundary in boundaries: 64 | bd.extend(boundary) 65 | paths.append(Path(boundary)) 66 | lat_min, lon_min = np.min(bd, axis=0) 67 | lat_max, lon_max = np.max(bd, axis=0) 68 | delta_lat = 0.000089 69 | delta_lon = delta_lat / np.cos(np.deg2rad((lat_max + lat_min) / 2)) 70 | print('delta lat: ' + str(delta_lat) + ' delta lon: ' + str(delta_lon)) 71 | lat_range = np.arange(lat_min, lat_max, delta_lat) 72 | lon_range = np.arange(lon_min, lon_max, delta_lon) 73 | lat_points, lon_points = np.meshgrid(lat_range, lon_range) 74 | lat_points = np.reshape(lat_points, np.size(lat_points)) 75 | lon_points = np.reshape(lon_points, np.size(lon_points)) 76 | 77 | # scan GSV meta 78 | # %% scan on the mesh to get the points which contained by the path 79 | print('Collect all points within the target region ...') 80 | points = [] 81 | fips_mapping = {} 82 | for i in tqdm(range(0, np.size(lat_points))): 83 | point = (lat_points[i], lon_points[i]) 84 | for j, path in enumerate(paths): 85 | if (path.contains_points([point])): 86 | points.append(point) 87 | fips_mapping[point] = boundaries_keys[j] 88 | break 89 | 90 | # %% search the points to get Google API meta locations 91 | print('Check meta data ...') 92 | sv = StreetView() 93 | if exists(GSV_points_filepath) and exists(fips_mapping_path): 94 | with open(GSV_points_filepath, 'rb') as f: 95 | validPoints = set(pickle.load(f)) 96 | with open(fips_mapping_path, 'rb') as f: 97 | fips_mapping_sub = pickle.load(f) 98 | print('Loaded existing valid points and FIPS mapping ...') 99 | else: 100 | validPoints = set() 101 | fips_mapping_sub = {} 102 | 103 | def save_found_points(): 104 | with open(GSV_points_filepath, 'wb') as f: 105 | pickle.dump(list(validPoints), f) 106 | f.close() 107 | with open(fips_mapping_path, 'wb') as f: 108 | pickle.dump(fips_mapping_sub, f) 109 | f.close() 110 | 111 | 112 | for i, point in tqdm(list(enumerate(points))): 113 | if i < start_i: 114 | continue 115 | status, meta = sv.getMetaStatus(point, radius='10', returnMeta=True) 116 | if (status == 'OK'): 117 | validPoints.add((meta['location']['lat'], meta['location']['lng'])) 118 | fips_mapping_sub[(meta['location']['lat'], meta['location']['lng'])] = fips_mapping[point] 119 | elif (status == 'ZERO_RESULTS'): 120 | pass 121 | else: 122 | print('ERROR: ' + status) 123 | # break 124 | if (i % save_per_points == 0): 125 | save_found_points() 126 | else: 127 | print('Search Finish!') 128 | save_found_points() 129 | -------------------------------------------------------------------------------- /california/2_download_area_GSV.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import pickle 3 | from tqdm import tqdm 4 | from streetView import StreetView 5 | import os 6 | from os.path import join, exists 7 | import urllib.error 8 | # import skimage.io 9 | 10 | """ 11 | This script is for downloading upward street view images for each test area. 12 | """ 13 | 14 | region = 'Salinas' 15 | 16 | # census tract list for each test area 17 | fips_list_dict = { 18 | 'SanCarlos': [6081609100, 6081609201, 6081609202, 6081609300, 6081609400, 6081609500, 6081609601, 6081609602, 6081609603], 19 | 'Newark': [6001444100, 6001444200, 6001444301, 6001444400, 6001444500, 6001444601], 20 | 'SantaCruz': [6087121300, 6087121401, 6087121402, 6087121403, 6087121700], 21 | 'Yuba': [6101050201, 6101050202, 6101050301, 6101050302], 22 | 'Monterey': [6053012401, 6053012402, 6053012302, 6053012200, 6053012100, 6053012000], 23 | 'Salinas': [6053000800, 6053000600, 6053000701, 6053000702], 24 | } 25 | 26 | boundaries_keys = fips_list_dict[region] 27 | 28 | def determine_root_dir(): 29 | """ 30 | This function is used to locate the root dir back to the parent directory, 31 | i.e., "GridMapping" directory. 32 | """ 33 | root_dir = os.path.abspath(os.path.join(os.getcwd(), os.pardir)) 34 | assert root_dir.strip('/')[-11:] == 'GridMapping' 35 | return root_dir 36 | 37 | root_dir = determine_root_dir() 38 | # change the root dir to "GridMapping" 39 | os.chdir(root_dir) 40 | 41 | start_i = 0 42 | 43 | GSV_points_filepath = join('results', region, 'validGSVpoints.pickle') 44 | fips_mapping_path = join('results', region, 'GSVpoint2fips.pickle') 45 | 46 | # directory to save street view images 47 | GSV_image_path = join('data/GSV_images', region) 48 | 49 | boundaries_keys_set = set(boundaries_keys) 50 | 51 | if not exists(join('results', region)): 52 | os.mkdir(join('results', region)) 53 | if not exists(GSV_image_path): 54 | os.mkdir(GSV_image_path) 55 | 56 | with open(GSV_points_filepath, 'rb') as f: 57 | validPoints = pickle.load(f) 58 | f.close() 59 | 60 | with open(fips_mapping_path, 'rb') as f: 61 | fips_mapping = pickle.load(f) 62 | f.close() 63 | 64 | sv = StreetView() 65 | fov = '120' 66 | error_list = [] 67 | 68 | for i, point in tqdm(list(enumerate(validPoints))): 69 | if i < start_i: 70 | continue 71 | if fips_mapping[point] in boundaries_keys_set: 72 | filename = GSV_image_path + os.sep + 'SC%08dNH.jpg' % i 73 | try: 74 | sv.getStreetView(point, filepath=filename, heading='0', 75 | pitch='90', fov=fov, radius='10', uncheck=False) 76 | except urllib.error.HTTPError: 77 | # skimage.io.imsave(filename, np.zeros((640, 640, 3)).astype(int)) 78 | error_list.append(i) 79 | print(error_list) 80 | -------------------------------------------------------------------------------- /california/3_predict_line_CAM_pytorch.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function 2 | from __future__ import division 3 | import torch 4 | import torch.nn as nn 5 | import torch.optim as optim 6 | from torch.utils.data import Dataset, DataLoader 7 | import torchvision 8 | from torchvision import datasets, models, transforms, utils 9 | import torchvision.transforms.functional as TF 10 | 11 | from tqdm import tqdm 12 | import numpy as np 13 | import json 14 | import pandas as pd 15 | import pickle 16 | import matplotlib.pyplot as plt 17 | # import skimage 18 | # import skimage.io 19 | # import skimage.transform 20 | from PIL import Image 21 | import time 22 | import os 23 | from os.path import join, exists 24 | import copy 25 | import random 26 | from collections import OrderedDict 27 | from sklearn.metrics import r2_score 28 | 29 | from torch.nn import functional as F 30 | from torchvision.models import Inception3 31 | 32 | from inception_modified import InceptionSegmentation 33 | from image_dataset import ImagePredictDataset 34 | 35 | """ 36 | This script is for running the line detetor on street view images. 37 | Classification results and CAMs are generated and saved. 38 | Note: this must be run after running step 2: "2_download_area_GSV.py". 39 | """ 40 | 41 | region = 'Salinas' 42 | 43 | def determine_root_dir(): 44 | """ 45 | This function is used to locate the root dir back to the parent directory, 46 | i.e., "GridMapping" directory. 47 | """ 48 | root_dir = os.path.abspath(os.path.join(os.getcwd(), os.pardir)) 49 | assert root_dir.strip('/')[-11:] == 'GridMapping' 50 | return root_dir 51 | 52 | root_dir = determine_root_dir() 53 | # change the root dir to "GridMapping" 54 | os.chdir(root_dir) 55 | 56 | # Configuration 57 | data_dir = join('data/GSV_images', region) 58 | save_dir = join('data/CAM_images', region) 59 | old_ckpt_path = 'checkpoint/deepGrid_seg_pretrained.tar' 60 | CAM_save_path = join('results', region, 'CAM_info_line.pickle') 61 | 62 | if not exists(join('results', region)): 63 | os.mkdir(join('results', region)) 64 | if not exists(save_dir): 65 | os.mkdir(save_dir) 66 | 67 | device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu") 68 | input_size = 640 69 | batch_size = 1 # must be 1 for testing segmentation 70 | threshold = 0.5 # threshold probability to identify am image as positive 71 | level = 1 72 | 73 | save_separated = True 74 | 75 | 76 | def predict_model(model, dataloader, threshold): 77 | model.eval() 78 | CAM_list = [] 79 | for inputs, paths in tqdm(dataloader): 80 | inputs = inputs.to(device) 81 | with torch.set_grad_enabled(False): 82 | # CAM is a 1 x 35 x 35 activation map 83 | _, outputs, CAM = model(inputs, testing=True) 84 | prob = F.softmax(outputs, dim=1) 85 | preds = prob[:, 1] >= threshold 86 | 87 | # transform tensor into numpy array 88 | CAM = CAM.squeeze(0).cpu().numpy() 89 | for i in range(preds.size(0)): 90 | predicted_label = preds[i] 91 | label = predicted_label.cpu().item() 92 | if label: 93 | # only use the generated CAM if it is predicted to be 1 94 | CAM_list.append((CAM, paths[i], label)) 95 | else: 96 | # otherwise the CAM is a totally black one 97 | CAM_list.append((np.zeros_like(CAM), paths[i], label)) 98 | 99 | return CAM_list 100 | 101 | 102 | transform_test = transforms.Compose([ 103 | transforms.Resize(input_size), 104 | transforms.ToTensor(), 105 | transforms.Normalize([0.5, 0.5, 0.5], [0.5, 0.5, 0.5]) 106 | ]) 107 | 108 | if __name__ == '__main__': 109 | # data 110 | dataset_test = ImagePredictDataset(data_dir, transform_test) 111 | dataloader_test = DataLoader( 112 | dataset_test, batch_size=batch_size, shuffle=False, num_workers=4) 113 | # model 114 | model = InceptionSegmentation(num_outputs=2, level=level) 115 | model.load_existing_params(old_ckpt_path) 116 | model = model.to(device) 117 | CAM_list = predict_model(model, dataloader_test, threshold=threshold) 118 | 119 | print('Save\n') 120 | info_list = [] 121 | if(save_separated): 122 | for cam, path, label in tqdm(CAM_list): 123 | if (label): 124 | filename = save_dir + os.sep + path[-16:-3] + 'cam' 125 | with open(filename, 'wb') as f: 126 | np.save(f, cam) 127 | f.close() 128 | info_list.append([path, label]) 129 | with open(CAM_save_path, 'wb') as f: 130 | pickle.dump(info_list, f) 131 | else: 132 | with open(CAM_save_path, 'wb') as f: 133 | pickle.dump(CAM_list, f) 134 | -------------------------------------------------------------------------------- /california/4_CAM_to_line_directions.py: -------------------------------------------------------------------------------- 1 | import ctypes 2 | import numpy as np 3 | import pickle 4 | from tqdm import tqdm 5 | import os 6 | from os.path import join, exists 7 | 8 | """ 9 | This script is for using Hough transform to extract line directions 10 | from CAMs. 11 | Note: this must be run after running step 3: "3_predict_line_CAM_pytorch.py". 12 | """ 13 | 14 | region = 'Salinas' 15 | 16 | def determine_root_dir(): 17 | """ 18 | This function is used to locate the root dir back to the parent directory, 19 | i.e., "GridMapping" directory. 20 | """ 21 | root_dir = os.path.abspath(os.path.join(os.getcwd(), os.pardir)) 22 | assert root_dir.strip('/')[-11:] == 'GridMapping' 23 | return root_dir 24 | 25 | root_dir = determine_root_dir() 26 | # change the root dir to "GridMapping" 27 | os.chdir(root_dir) 28 | 29 | # ctypes initial 30 | HoughTransform_lib = ctypes.cdll.LoadLibrary( 31 | 'HoughTransform_lib.so') 32 | HoughTransform_lib.init() 33 | cam2lines = HoughTransform_lib.cam2lines 34 | cam2lines.argtypes = [np.ctypeslib.ndpointer( 35 | ctypes.c_double, flags='C_CONTIGUOUS')] 36 | cam2lines.restype = ctypes.c_int 37 | getlines = HoughTransform_lib.getlines 38 | getlines.argtypes = [] 39 | getlines.restype = ctypes.POINTER(ctypes.c_double) 40 | 41 | # rh & th 42 | ''' 43 | getth = HoughTransform_lib.getth 44 | getth.restype = ctypes.POINTER(ctypes.c_double) 45 | getrh = HoughTransform_lib.getrh 46 | getrh.restype = ctypes.POINTER(ctypes.c_double) 47 | th_raw = getth() 48 | rh_raw = getrh() 49 | th_len = HoughTransform_lib.getthlen() 50 | rh_len = HoughTransform_lib.getrhlen() 51 | th = np.fromiter(th_raw, dtype=np.double, count=th_len) 52 | rh = np.fromiter(rh_raw, dtype=np.double, count=rh_len) 53 | ''' 54 | 55 | CAMinfopath = join('results', region, 'CAM_info_line.pickle') 56 | rawdatapath = join('data/GSV_images', region) 57 | CAMdatapath = join('data/CAM_images', region) 58 | savefile = join('results', region, 'line_info_raw.pickle') 59 | #savepath = projectPath+'data/CAM2Lines_new2' 60 | #os.makedirs(savepath, exist_ok=True) 61 | 62 | with open(CAMinfopath, 'rb') as info: 63 | caminfos = pickle.load(info) 64 | info.close() 65 | 66 | linesDictionary = dict() 67 | 68 | 69 | for path, label in tqdm(caminfos): 70 | if (label == 0): 71 | linesDictionary[path] = [] 72 | continue 73 | with open(CAMdatapath+os.sep+path[-16:-3]+'cam', 'rb') as f: 74 | cam = np.load(f) 75 | f.close() 76 | #cam = np.reshape(cam, np.size(cam)) 77 | cam_array = np.ascontiguousarray(cam, dtype=ctypes.c_double) 78 | #cam_p = cam_array.ctypes.data_as(ctypes.POINTER(ctypes.c_double)) 79 | lines_len = cam2lines(cam_array) 80 | lines_raw = getlines() 81 | lines = np.fromiter(lines_raw, dtype=np.double, count=3 * lines_len) 82 | lines = np.resize(lines, (lines_len, 3)) 83 | 84 | linesDictionary[path] = lines 85 | 86 | with open(savefile, 'wb') as f: 87 | pickle.dump(linesDictionary, f) 88 | f.close() 89 | 90 | 91 | HoughTransform_lib.destory() 92 | -------------------------------------------------------------------------------- /california/5_merge_line_directions.py: -------------------------------------------------------------------------------- 1 | import pickle 2 | import numpy as np 3 | import os 4 | from tqdm import tqdm 5 | from os.path import join, exists 6 | 7 | """ 8 | This script is used for merging similar line directions (i.e., parallel power lines with 9 | different phases) estimated in each CAM. 10 | """ 11 | 12 | region = 'Salinas' 13 | 14 | def determine_root_dir(): 15 | """ 16 | This function is used to locate the root dir back to the parent directory, 17 | i.e., "GridMapping" directory. 18 | """ 19 | root_dir = os.path.abspath(os.path.join(os.getcwd(), os.pardir)) 20 | assert root_dir.strip('/')[-11:] == 'GridMapping' 21 | return root_dir 22 | 23 | root_dir = determine_root_dir() 24 | # change the root dir to "GridMapping" 25 | os.chdir(root_dir) 26 | 27 | line_directions_filepath = join('results', region, 'line_info_raw.pickle') 28 | merged_directions_filepath = join('results', region, 'line_info_merged.pickle') 29 | Point_info_path = join('results', region, 'validGSVpoints.pickle') 30 | 31 | with open(line_directions_filepath, 'rb') as f: 32 | line_info_raw = pickle.load(f) 33 | 34 | with open(Point_info_path, 'rb') as f: 35 | point_infos = pickle.load(f) 36 | f.close() 37 | 38 | directionInfo = [] 39 | for cam_path in line_info_raw: 40 | rank = int(cam_path[-14:-6]) 41 | location = point_infos[rank] 42 | directionInfo.append((location, line_info_raw[cam_path], cam_path)) 43 | 44 | 45 | def lineSimilar(theta1, theta2): 46 | threhold = 20 47 | delta_theta = abs(theta1-theta2) 48 | if (delta_theta < threhold): 49 | return 1 50 | elif ((180 - delta_theta) < threhold): 51 | return 2 52 | else: 53 | return 0 54 | 55 | 56 | def lineMean(line1, line2): 57 | if (lineSimilar(line1[1], line2[1]) == 1): 58 | lineM = (line1[1] * line1[2] + line2[1] * 59 | line2[2]) / (line1[2] + line2[2]) 60 | elif (lineSimilar(line1[1], line2[1]) == 2): 61 | if (line1[1] < 0): 62 | line1_s = line1[1] + 180 63 | line2_s = line2[1] 64 | else: 65 | line1_s = line1[1] 66 | line2_s = line2[1] + 180 67 | lineM = (line1_s * line1[2] + line2_s * 68 | line2[2]) / (line1[2] + line2[2]) 69 | if (lineM > 90): 70 | lineM = lineM - 90 71 | return lineM 72 | 73 | 74 | def lineWeight(line): 75 | return line[2] 76 | 77 | 78 | mergedInfo = list() 79 | 80 | for location, lines, path in tqdm(directionInfo): 81 | # combine 82 | lineBuffer = list() 83 | for line in lines: 84 | # transform from sky view to ground view 85 | theta = -line[1] * 180 / np.pi 86 | line_regular = [1, theta, line[2]] 87 | if (lineBuffer == []): 88 | lineBuffer.append(line_regular) 89 | else: 90 | join = False 91 | for i, bufferedLine in enumerate(lineBuffer): 92 | if (lineSimilar(line_regular[1], bufferedLine[1]) > 0): 93 | lineBuffer[i][0] += line_regular[0] 94 | lineBuffer[i][1] = lineMean(line_regular, bufferedLine) 95 | lineBuffer[i][2] += line_regular[2] 96 | join = True 97 | break 98 | if(join == False): 99 | lineBuffer.append(line_regular) 100 | lineBuffer.sort(key=lineWeight, reverse=True) 101 | 102 | mergedInfo.append([location, lineBuffer]) 103 | 104 | with open(merged_directions_filepath, 'wb') as f: 105 | pickle.dump(mergedInfo, f) 106 | -------------------------------------------------------------------------------- /california/6_predict_pole_CAM_pytorch.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function 2 | from __future__ import division 3 | import torch 4 | import torch.nn as nn 5 | import torch.optim as optim 6 | from torch.utils.data import Dataset, DataLoader 7 | import torchvision 8 | from torchvision import datasets, models, transforms, utils 9 | import torchvision.transforms.functional as TF 10 | 11 | from tqdm import tqdm 12 | import numpy as np 13 | import json 14 | import pandas as pd 15 | import pickle 16 | import matplotlib.pyplot as plt 17 | # import skimage 18 | # import skimage.io 19 | # import skimage.transform 20 | from PIL import Image 21 | import time 22 | import os 23 | from os.path import join, exists 24 | import copy 25 | import random 26 | from collections import OrderedDict 27 | from sklearn.metrics import r2_score 28 | 29 | from torch.nn import functional as F 30 | from torchvision.models import Inception3 31 | 32 | from inception_modified import InceptionSegmentation 33 | from image_dataset import ImagePredictDataset 34 | 35 | """ 36 | This script is for running the pole detetor on street view images. 37 | Classification results and CAMs are generated and saved. 38 | Note: this must be run after running step 2: "2_download_area_GSV.py". 39 | """ 40 | 41 | region = 'Salinas' 42 | pole_model = 'ori0.5' # the model using 0.5 as the classification decision threshold 43 | 44 | def determine_root_dir(): 45 | """ 46 | This function is used to locate the root dir back to the parent directory, 47 | i.e., "GridMapping" directory. 48 | """ 49 | root_dir = os.path.abspath(os.path.join(os.getcwd(), os.pardir)) 50 | assert root_dir.strip('/')[-11:] == 'GridMapping' 51 | return root_dir 52 | 53 | root_dir = determine_root_dir() 54 | # change the root dir to "GridMapping" 55 | os.chdir(root_dir) 56 | 57 | pole_model_configs = { 58 | 'ori0.5': { 59 | 'ckpt_path': 'checkpoint/deepGrid_DPNH2seg_pretrained.tar', 60 | 'threshold': 0.5, 61 | }, 62 | } 63 | 64 | # Configuration 65 | # directory for loading training/validation/test data 66 | data_dir = join('data/GSV_images', region) 67 | save_dir = join('data/CAM_images_for_poles', pole_model, region) 68 | old_ckpt_path = pole_model_configs[pole_model]['ckpt_path'] 69 | CAM_save_path = join('results', region, pole_model, 'CAM_info_pole.pickle') 70 | 71 | if not exists(join('data/CAM_images_for_poles', pole_model)): 72 | os.mkdir(join('data/CAM_images_for_poles', pole_model)) 73 | if not exists(save_dir): 74 | os.mkdir(save_dir) 75 | if not exists(join('results', region, pole_model)): 76 | os.mkdir(join('results', region, pole_model)) 77 | 78 | device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu") 79 | input_size = 640 80 | batch_size = 1 # must be 1 for testing segmentation 81 | threshold = pole_model_configs[pole_model]['threshold'] # threshold probability to identify am image as positive 82 | level = 1 83 | 84 | save_separated = True 85 | 86 | 87 | def predict_model(model, dataloader, threshold): 88 | model.eval() 89 | CAM_list = [] 90 | for inputs, paths in tqdm(dataloader): 91 | inputs = inputs.to(device) 92 | with torch.set_grad_enabled(False): 93 | # CAM is a 1 x 35 x 35 activation map 94 | _, outputs, CAM = model(inputs, testing=True) 95 | prob = F.softmax(outputs, dim=1) 96 | preds = prob[:, 1] >= threshold 97 | 98 | # transform tensor into numpy array 99 | CAM = CAM.squeeze(0).cpu().numpy() 100 | for i in range(preds.size(0)): 101 | predicted_label = preds[i] 102 | label = predicted_label.cpu().item() 103 | if label: 104 | # only use the generated CAM if it is predicted to be 1 105 | CAM_list.append((CAM, paths[i], label)) 106 | else: 107 | # otherwise the CAM is a totally black one 108 | CAM_list.append((np.zeros_like(CAM), paths[i], label)) 109 | 110 | return CAM_list 111 | 112 | 113 | transform_test = transforms.Compose([ 114 | transforms.Resize(input_size), 115 | transforms.ToTensor(), 116 | transforms.Normalize([0.5, 0.5, 0.5], [0.5, 0.5, 0.5]) 117 | ]) 118 | 119 | if __name__ == '__main__': 120 | 121 | # data 122 | dataset_test = ImagePredictDataset(data_dir, transform_test) 123 | dataloader_test = DataLoader( 124 | dataset_test, batch_size=batch_size, shuffle=False, num_workers=4) 125 | # model 126 | model = InceptionSegmentation(num_outputs=2, level=level) 127 | model.load_existing_params(old_ckpt_path) 128 | model = model.to(device) 129 | CAM_list = predict_model(model, dataloader_test, threshold=threshold) 130 | 131 | print('Save\n') 132 | info_list = [] 133 | if(save_separated): 134 | for cam, path, label in tqdm(CAM_list): 135 | if (label): 136 | filename = save_dir + os.sep + path[-16:-3] + 'cam' 137 | with open(filename, 'wb') as f: 138 | np.save(f, cam) 139 | f.close() 140 | info_list.append([path, label]) 141 | with open(CAM_save_path, 'wb') as f: 142 | pickle.dump(info_list, f) 143 | else: 144 | with open(CAM_save_path, 'wb') as f: 145 | pickle.dump(CAM_list, f) 146 | -------------------------------------------------------------------------------- /california/7_CAM_to_pole_LOBs.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import pickle 3 | from tqdm import tqdm 4 | import os 5 | from math import atan2, pi, sin, cos 6 | import matplotlib.pyplot as plt 7 | from os.path import join, exists 8 | 9 | """ 10 | This script is for extracting pole orientations from CAMs and obtaining the 11 | Line of Bearings (LOBs, rays that represent pole orientations) 12 | Multiple regions can be run all at once. 13 | Note: this must be run after running step 6: "6_predict_pole_CAM_pytorch.py". 14 | """ 15 | 16 | region_list = ['Salinas'] # 'SanCarlos', 'Newark', 'SantaCruz', 'Yuba', 'Monterey', 'Salinas' 17 | 18 | pole_model_list = ['ori0.5'] 19 | 20 | def determine_root_dir(): 21 | """ 22 | This function is used to locate the root dir back to the parent directory, 23 | i.e., "GridMapping" directory. 24 | """ 25 | root_dir = os.path.abspath(os.path.join(os.getcwd(), os.pardir)) 26 | assert root_dir.strip('/')[-11:] == 'GridMapping' 27 | return root_dir 28 | 29 | root_dir = determine_root_dir() 30 | # change the root dir to "GridMapping" 31 | os.chdir(root_dir) 32 | 33 | for region in region_list: 34 | for pole_model in pole_model_list: 35 | print('-'*20) 36 | print('Region: ' + region + '. Pole model: ' + pole_model) 37 | # datapath 38 | CAMdatapath = join('data/CAM_images_for_poles', pole_model, region) 39 | Line_CAM_info_path = join('results', region, 'CAM_info_line.pickle') 40 | Pole_CAM_info_path = join('results', region, pole_model, 'CAM_info_pole.pickle') 41 | Point_info_path = join('results', region, 'validGSVpoints.pickle') 42 | LOB_savefile = join('results', region, pole_model, 'pole_LOB_raw.pickle') 43 | threshold = 100 44 | 45 | with open(Pole_CAM_info_path, 'rb') as f: 46 | pole_infos = pickle.load(f) 47 | f.close() 48 | 49 | with open(Line_CAM_info_path, 'rb') as f: 50 | line_infos = pickle.load(f) 51 | f.close() 52 | 53 | with open(Point_info_path, 'rb') as f: 54 | point_infos = pickle.load(f) 55 | f.close() 56 | 57 | pole_directions = list() 58 | for i in tqdm(range(len(pole_infos))): 59 | path_pole, label_pole = pole_infos[i] 60 | path_line, label_line = line_infos[i] 61 | rank = int(path_pole[-14:-6]) 62 | location = point_infos[rank] 63 | if ((label_line == 0) or (label_pole == 0)): 64 | continue 65 | with open(os.path.join(CAMdatapath, path_pole[-16:-3] + 'cam'), 'rb') as f: 66 | # cam = np.fromfile(f, dtype=np.float32) 67 | # cam = np.reshape(cam, (77, 77)) 68 | cam = np.load(f) 69 | f.close() 70 | imgsize_y, imgsize_x = np.shape(cam) 71 | thetas = np.zeros(360) 72 | for i in range(imgsize_y): 73 | for j in range(imgsize_x): 74 | dy = i - int(imgsize_y / 2) 75 | dx = j - int(imgsize_x / 2) 76 | alpha = int(round(atan2(dy, dx) * 180 / pi)) 77 | thetas[alpha] += cam[i, j] 78 | theta = np.argmax(thetas) 79 | pole_directions.append([location, theta]) 80 | 81 | with open(LOB_savefile, 'wb') as f: 82 | pickle.dump(pole_directions, f) 83 | -------------------------------------------------------------------------------- /california/8_LOB_to_pole_locations.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import pickle 3 | from tqdm import tqdm 4 | import os 5 | from math import atan2, sqrt, pi, sin, cos, tan 6 | import matplotlib.pyplot as plt 7 | from os.path import join, exists 8 | 9 | """ 10 | This script is for intersecting the Lines of Bearings (LOBs) obtained from step 7 11 | to obtain the pole locations. 12 | Multiple regions can be run all at once. 13 | """ 14 | 15 | region_list = ['Salinas'] # 'SanCarlos', 'Newark', 'SantaCruz', 'Yuba', 'Monterey', 'Salinas' 16 | pole_model_list = ['ori0.5'] 17 | 18 | 19 | def determine_root_dir(): 20 | """ 21 | This function is used to locate the root dir back to the parent directory, 22 | i.e., "GridMapping" directory. 23 | """ 24 | root_dir = os.path.abspath(os.path.join(os.getcwd(), os.pardir)) 25 | assert root_dir.strip('/')[-11:] == 'GridMapping' 26 | return root_dir 27 | 28 | root_dir = determine_root_dir() 29 | # change the root dir to "GridMapping" 30 | os.chdir(root_dir) 31 | 32 | def getDistance(pt1, pt2): 33 | #latitude, longitude 34 | lat1, lon1 = pt1 35 | lat2, lon2 = pt2 36 | lat = (lat1 + lat2) / 2 * pi / 180 37 | dlat = (lat2 - lat1) * pi / 180 38 | dlon = (lon2 - lon1) * pi / 180 39 | R = 6378.137 # km 40 | dl = R * sqrt(dlat ** 2 + (cos(lat) * dlon) ** 2) 41 | return dl * 1000 42 | 43 | 44 | def getIntersection(LOB1, LOB2): 45 | eps = 0.5 46 | location1, direction1 = LOB1 47 | location2, direction2 = LOB2 48 | lat1, lon1 = location1 49 | lat2, lon2 = location2 50 | y1, x1 = location1 51 | y2, x2 = location2 52 | lat = (lat1 + lat2) / 2 * pi / 180 53 | 54 | if (direction1 == direction2): 55 | # parallel 56 | return [] 57 | 58 | beta1 = direction1 * pi / 180 59 | beta2 = direction2 * pi / 180 60 | tb1 = tan(beta1) * cos(lat) 61 | tb2 = tan(beta2) * cos(lat) 62 | 63 | if tb1 - tb2 == 0: 64 | # print(direction1, direction2) 65 | return [] 66 | 67 | xt = (x1 * tb1 - x2 * tb2 + y2 - y1) / (tb1 - tb2) 68 | yt = ((x1 * tb1 - y1) * tb2 - (x2 * tb2 - y2) * tb1) / (tb1 - tb2) 69 | 70 | beta1_t = atan2(yt - y1, xt - x1) 71 | beta2_t = atan2(yt - y2, xt - x2) 72 | beta1_t = (beta1_t + 2 * pi) if (beta1_t < 0) else beta1_t 73 | beta2_t = (beta2_t + 2 * pi) if (beta2_t < 0) else beta2_t 74 | 75 | if ((abs(beta1_t - beta1) > eps) or (abs(beta2_t - beta2) > eps)): 76 | # no intersection, wrong direction 77 | return [] 78 | pt = [yt, xt] 79 | 80 | if (getDistance(pt, location1) > area_threshold): 81 | return [] 82 | if (getDistance(pt, location2) > area_threshold): 83 | return [] 84 | return pt 85 | 86 | 87 | for region in region_list: 88 | for pole_model in pole_model_list: 89 | print('-'*20) 90 | print('Region: ' + region + '. Pole model: ' + pole_model) 91 | # datapath 92 | pole_directions_path = join('results', region, pole_model, 'pole_LOB_raw.pickle') 93 | savefile = join('results', region, pole_model, 'pole_locations.pickle') 94 | attached_location_file = join('results', region, pole_model, 'pole_attached_GSVs.pickle') 95 | 96 | with open(pole_directions_path, 'rb') as f: 97 | pole_directions = pickle.load(f) 98 | f.close() 99 | 100 | distance_threshold = 40 101 | cluster_threshold = 10 102 | area_threshold = 20 103 | 104 | clusters = list() 105 | # [lat,lon,combo] 106 | LOB_len = len(pole_directions) 107 | attached_location = list() 108 | for i in tqdm(range(LOB_len)): 109 | location, direction = pole_directions[i] 110 | for j in range(i + 1, LOB_len): 111 | location2, direction2 = pole_directions[j] 112 | if (getDistance(location, location2) > distance_threshold): 113 | continue 114 | pt = getIntersection([location, direction], [location2, direction2]) 115 | if (pt == []): 116 | continue 117 | # cluster insert 118 | for k, cluster in enumerate(clusters): 119 | lat0, lon0, combo = cluster 120 | lat1, lon1 = pt 121 | if (getDistance([lat0, lon0], pt) < cluster_threshold): 122 | lat_new = (lat0 * combo + lat1) / (combo + 1) 123 | lon_new = (lon0 * combo + lon1) / (combo + 1) 124 | combo_new = combo + 1 125 | clusters[k] = [lat_new, lon_new, combo_new] 126 | attached_location[k].extend([location, location2]) 127 | break 128 | else: 129 | clusters.append([pt[0], pt[1], 1]) 130 | attached_location.append([location, location2]) 131 | 132 | with open(savefile, 'wb') as f: 133 | pickle.dump(clusters, f) 134 | 135 | with open(attached_location_file, 'wb') as f: 136 | pickle.dump(attached_location, f) 137 | -------------------------------------------------------------------------------- /california/HoughTransform_lib.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #define M_PI 3.14159265358979323846 4 | #define IMGSIZE 77 5 | #define CAMSIZE (IMGSIZE * IMGSIZE) 6 | #define THETARANGE 180 7 | #define LINEMAX 10 8 | #define maskDepth 9999 9 | #define maskWidth 3 10 | 11 | #ifdef _MSC_VER 12 | #define DLL_EXPORT __declspec(dllexport) 13 | #else 14 | #define DLL_EXPORT 15 | #endif 16 | 17 | double *cos_t, *sin_t; 18 | double *thetas, *rhos; 19 | int* accumulator; 20 | int theta_len, diag_len; 21 | int weight, height; 22 | double* cam; 23 | int* cam_regular; 24 | double* lines; 25 | int lines_len; 26 | 27 | struct Points { 28 | int x1, y1, x2, y2; 29 | }; 30 | 31 | struct Points polar2cart(double rho, double theta) { 32 | double a = cos(theta); 33 | double b = sin(theta); 34 | double x0 = a * rho; 35 | double y0 = b * rho; 36 | struct Points p; 37 | p.x1 = (int)(x0 + 1000 * (-b)); 38 | p.y1 = (int)(y0 + 1000 * (a)); 39 | p.x2 = (int)(x0 - 1000 * (-b)); 40 | p.y2 = (int)(y0 - 1000 * (a)); 41 | return p; 42 | } 43 | 44 | int getMaxRank(int* array, int len) { 45 | int rank = 0; 46 | int maxValue = array[0]; 47 | for (int i = 1; i < len; i++) { 48 | if (array[i] > maxValue) { 49 | maxValue = array[i]; 50 | rank = i; 51 | } 52 | } 53 | return rank; 54 | } 55 | 56 | int getMax(int* array, int len) { 57 | return array[getMaxRank(array, len)]; 58 | } 59 | 60 | void CAMRegular() { 61 | int level1 = 10; 62 | int level2 = 1; 63 | int level1_thre = 80; 64 | int level2_thre = 10; 65 | for (int i = 0; i < CAMSIZE; i++) { 66 | if (cam[i] > level1_thre) 67 | cam_regular[i] = level1; 68 | else if (cam[i] > level2_thre) 69 | cam_regular[i] = level2; 70 | else 71 | cam_regular[i] = 0; 72 | } 73 | } 74 | 75 | int capsule(double px, 76 | double py, 77 | double ax, 78 | double ay, 79 | double bx, 80 | double by, 81 | double r) { 82 | double pax = px - ax, pay = py - ay, bax = bx - ax, bay = by - ay; 83 | double h = fmaxf( 84 | fminf((pax * bax + pay * bay) / (bax * bax + bay * bay), 1.0f), 0.0f); 85 | double dx = pax - bax * h, dy = pay - bay * h; 86 | return dx * dx + dy * dy < r * r; 87 | } 88 | 89 | void lineMask(double* img, struct Points pts, int width) { 90 | double* p = img; 91 | for (int y = 0; y < IMGSIZE; y++) 92 | for (int x = 0; x < IMGSIZE; x++, p += 1) 93 | *p = *p - capsule(x, y, pts.x1, pts.y1, pts.x2, pts.y2, width) * 94 | maskDepth; 95 | return; 96 | } 97 | 98 | DLL_EXPORT void init() { 99 | theta_len = THETARANGE + 1; 100 | weight = IMGSIZE; 101 | height = IMGSIZE; 102 | diag_len = (int)(ceil(sqrt(weight * weight + height * height))); 103 | thetas = (double*)malloc(sizeof(double) * theta_len); 104 | cos_t = (double*)malloc(sizeof(double) * theta_len); 105 | sin_t = (double*)malloc(sizeof(double) * theta_len); 106 | rhos = (double*)malloc(sizeof(double) * 2 * diag_len); 107 | accumulator = (int*)malloc(sizeof(int) * 2 * diag_len * theta_len); 108 | cam = (double*)malloc(sizeof(double) * CAMSIZE); 109 | cam_regular = (int*)malloc(sizeof(int) * CAMSIZE); 110 | lines = (double*)malloc(sizeof(double) * 3 * LINEMAX); 111 | for (int i = 0; i < theta_len; i++) { 112 | thetas[i] = (i - 90) * M_PI / 180; 113 | cos_t[i] = cos(thetas[i]); 114 | sin_t[i] = sin(thetas[i]); 115 | } 116 | double rhos_delta = 2 * diag_len / (2 * diag_len - 1); 117 | for (int i = 0; i < 2 * diag_len; i++) 118 | rhos[i] = -diag_len + i * rhos_delta; 119 | } 120 | 121 | DLL_EXPORT int* HoughTransform(int* img) { 122 | for (int i = 0; i < 2 * diag_len; i++) 123 | for (int j = 0; j < theta_len; j++) 124 | accumulator[i * theta_len + j] = 0; 125 | 126 | for (int x = 0; x < weight; x++) 127 | for (int y = 0; y < height; y++) { 128 | if (img[y * weight + x] == 0) 129 | return; 130 | for (int t = 0; t < theta_len; t++) { 131 | int rho = (int)(round(x * cos_t[t] + y * sin_t[t])) + diag_len; 132 | accumulator[rho * theta_len + t] += img[y * weight + x]; 133 | } 134 | } 135 | return accumulator; 136 | } 137 | DLL_EXPORT double* getth() { 138 | return thetas; 139 | } 140 | DLL_EXPORT double* getrh() { 141 | return rhos; 142 | } 143 | DLL_EXPORT int getthlen() { 144 | return theta_len; 145 | } 146 | DLL_EXPORT int getrhlen() { 147 | return 2 * diag_len; 148 | } 149 | DLL_EXPORT void destory() { 150 | free(thetas); 151 | free(sin_t); 152 | free(cos_t); 153 | free(rhos); 154 | free(accumulator); 155 | free(cam); 156 | free(cam_regular); 157 | free(lines); 158 | } 159 | 160 | DLL_EXPORT int cam2lines(double* cam_data) { 161 | for (int i = 0; i < CAMSIZE; i++) 162 | cam[i] = cam_data[i]; 163 | // memcpy(cam, cam_data, sizeof(double) * CAMSIZE); 164 | lines_len = 0; 165 | int line_thre = 80; 166 | for (int i = 0; i < LINEMAX; i++) { 167 | CAMRegular(); 168 | int* hf = HoughTransform(cam_regular); 169 | if (getMax(hf, 2 * diag_len * theta_len) < line_thre) 170 | break; 171 | int hfLine = getMaxRank(hf, 2 * diag_len * theta_len); 172 | int hfLine_r = hfLine / theta_len; 173 | int hfLine_t = hfLine % theta_len; 174 | double rho = rhos[hfLine_r]; 175 | double theta = thetas[hfLine_t]; 176 | lines[i * 3 + 0] = rho; 177 | lines[i * 3 + 1] = theta; 178 | lines[i * 3 + 2] = hf[hfLine]; 179 | lines_len++; 180 | struct Points p = polar2cart(rho, theta); 181 | lineMask(cam, p, maskWidth); 182 | } 183 | return lines_len; 184 | } 185 | 186 | DLL_EXPORT double* getlines() { 187 | return lines; 188 | } 189 | -------------------------------------------------------------------------------- /california/HoughTransform_lib.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangzhecheng/GridMapping/e5f6360132dea5b67c97a6fb0e82fe85d3f63352/california/HoughTransform_lib.so -------------------------------------------------------------------------------- /california/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangzhecheng/GridMapping/e5f6360132dea5b67c97a6fb0e82fe85d3f63352/california/__init__.py -------------------------------------------------------------------------------- /california/__pycache__/census_tract_locator.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangzhecheng/GridMapping/e5f6360132dea5b67c97a6fb0e82fe85d3f63352/california/__pycache__/census_tract_locator.cpython-36.pyc -------------------------------------------------------------------------------- /california/__pycache__/dijkstra.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangzhecheng/GridMapping/e5f6360132dea5b67c97a6fb0e82fe85d3f63352/california/__pycache__/dijkstra.cpython-36.pyc -------------------------------------------------------------------------------- /california/__pycache__/evaluation_utils.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangzhecheng/GridMapping/e5f6360132dea5b67c97a6fb0e82fe85d3f63352/california/__pycache__/evaluation_utils.cpython-36.pyc -------------------------------------------------------------------------------- /california/__pycache__/feature_engineering.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangzhecheng/GridMapping/e5f6360132dea5b67c97a6fb0e82fe85d3f63352/california/__pycache__/feature_engineering.cpython-36.pyc -------------------------------------------------------------------------------- /california/__pycache__/image_dataset.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangzhecheng/GridMapping/e5f6360132dea5b67c97a6fb0e82fe85d3f63352/california/__pycache__/image_dataset.cpython-36.pyc -------------------------------------------------------------------------------- /california/__pycache__/inception_modified.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangzhecheng/GridMapping/e5f6360132dea5b67c97a6fb0e82fe85d3f63352/california/__pycache__/inception_modified.cpython-36.pyc -------------------------------------------------------------------------------- /california/__pycache__/utils.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangzhecheng/GridMapping/e5f6360132dea5b67c97a6fb0e82fe85d3f63352/california/__pycache__/utils.cpython-36.pyc -------------------------------------------------------------------------------- /california/census_tract_locator.py: -------------------------------------------------------------------------------- 1 | from os import listdir 2 | from os.path import join, exists 3 | import pandas as pd 4 | import numpy as np 5 | import pickle 6 | from bisect import bisect_left, bisect_right 7 | import matplotlib.path as mplPath 8 | 9 | 10 | class CensusTract(object): 11 | def __init__(self, tract_data_dir=None): 12 | self.tract_data = None 13 | if tract_data_dir is not None: 14 | if exists(tract_data_dir): 15 | with open(tract_data_dir, 'rb') as f: 16 | self.tract_data = pickle.load(f) 17 | pass 18 | 19 | def save_tract_data(self, boundary_file_dir, tract_data_dir=None): 20 | 21 | raw_tract_names = [] 22 | for bf in listdir(boundary_file_dir): 23 | # File name example: 1400000US01097001400_polylatlon1.csv 24 | if bf[-4:] == '.csv': 25 | tract_name = bf[9:20] 26 | raw_tract_names.append(tract_name) 27 | 28 | tract_names = sorted(raw_tract_names) 29 | 30 | tract_boundaries = dict() 31 | 32 | lms = ('lat_min', 'lat_max', 'lon_min', 'lon_max') 33 | lm_list = dict() 34 | for lm in lms: 35 | lm_list[lm] = [] 36 | 37 | max_lat_dist = -1 38 | max_lon_dist = -1 39 | 40 | for tract_name in tract_names: 41 | bf = ''.join(['1400000US', tract_name, '_polylatlon1.csv']) 42 | lat_lon = pd.read_csv(join(boundary_file_dir, bf), header=-1).values 43 | 44 | tract_boundaries[tract_name] = lat_lon 45 | 46 | bf_lms = { 47 | 'lat_min': np.min(lat_lon[:, 0]), 48 | 'lat_max': np.max(lat_lon[:, 0]), 49 | 'lon_min': np.min(lat_lon[:, 1]), 50 | 'lon_max': np.max(lat_lon[:, 1]) 51 | } 52 | 53 | max_lat_dist = max(max_lat_dist, bf_lms['lat_max'] - bf_lms['lat_min']) 54 | max_lon_dist = max(max_lon_dist, bf_lms['lon_max'] - bf_lms['lon_min']) 55 | 56 | for lm in lms: 57 | lm_list[lm].append(bf_lms[lm]) 58 | 59 | lm_argsort = dict() 60 | lm_sort = dict() 61 | for lm in lms: 62 | lm_list[lm] = np.array(lm_list[lm]) 63 | lm_argsort[lm] = np.argsort(lm_list[lm]) 64 | lm_sort[lm] = lm_list[lm][lm_argsort[lm]] 65 | 66 | tract_data = dict() 67 | tract_data['tract_names'] = tract_names 68 | tract_data['tract_boundaries'] = tract_boundaries 69 | tract_data['lm_list'] = lm_list 70 | tract_data['lm_argsort'] = lm_argsort 71 | tract_data['lm_sort'] = lm_sort 72 | tract_data['max_lat_dist'] = max_lat_dist 73 | tract_data['max_lon_dist'] = max_lon_dist 74 | 75 | if self.tract_data is None: 76 | self.tract_data = tract_data 77 | 78 | if tract_data_dir is not None: 79 | if not exists(tract_data_dir): 80 | with open(tract_data_dir, 'wb') as f: 81 | pickle.dump(tract_data, f) 82 | 83 | def get_tract(self, lat, lon): 84 | 85 | ll_arg = {'lat': lat, 'lon': lon} 86 | 87 | idx_dict = dict() 88 | 89 | tracts = None 90 | 91 | for ll in ['lat', 'lon']: 92 | idx_dict[ll] = dict() 93 | 94 | for mm in ['min', 'max']: 95 | 96 | idx_dict[ll][mm] = dict() 97 | for lr in ['left', 'right']: 98 | 99 | if lr == 'left' and mm == 'min': 100 | ref_coord = ll_arg[ll] - self.tract_data[''.join(('max_', ll, '_dist'))] 101 | elif (lr == 'right' and mm == 'min') or (lr == 'left' and mm == 'max'): 102 | ref_coord = ll_arg[ll] 103 | else: 104 | ref_coord = ll_arg[ll] + self.tract_data[''.join(('max_', ll, '_dist'))] 105 | 106 | if lr == 'left': 107 | idx_dict[ll][mm][lr] = bisect_left( 108 | self.tract_data['lm_sort']['_'.join((ll, mm))], 109 | ref_coord 110 | ) 111 | else: 112 | idx_dict[ll][mm][lr] = bisect_right( 113 | self.tract_data['lm_sort']['_'.join((ll, mm))], 114 | ref_coord 115 | ) 116 | 117 | temp_tracts = \ 118 | [self.tract_data['tract_names'][idx] for idx in self.tract_data['lm_argsort']['_'.join((ll, mm))][ 119 | idx_dict[ll][mm]['left']:idx_dict[ll][mm]['right'] 120 | ]] 121 | 122 | if tracts is None: 123 | tracts = set(temp_tracts) 124 | else: 125 | tracts = tracts.intersection(set(temp_tracts)) 126 | 127 | for tract in list(tracts): 128 | bb_path = mplPath.Path(self.tract_data['tract_boundaries'][tract]) 129 | if bb_path.contains_point((lat, lon)): 130 | return tract 131 | 132 | return -1 133 | 134 | if __name__ == '__main__': 135 | ct = CensusTract() 136 | ct.save_tract_data(boundary_file_dir='./Tracts') 137 | ct.get_tract(lat=1, lon=1) 138 | ct.get_tract(lat=32, lon=-86) 139 | ct.get_tract(lat=32.48, lon=-86.5) 140 | 141 | 142 | 143 | -------------------------------------------------------------------------------- /california/dijkstra.py: -------------------------------------------------------------------------------- 1 | from __future__ import absolute_import 2 | from __future__ import division 3 | from __future__ import print_function 4 | from __future__ import unicode_literals 5 | from __future__ import with_statement 6 | import heapq 7 | import os 8 | import sys 9 | import time 10 | import pickle 11 | import pandas as pd 12 | import matplotlib 13 | matplotlib.use('Agg') 14 | import matplotlib.pyplot as plt 15 | # from numba import autojit 16 | import numpy as np 17 | from tqdm import tqdm 18 | 19 | 20 | def seek(origins, targets=None, weights=None, path_handling='link', debug=False, film=False, 21 | frame_dirname='frames', frame_rate=1000000, early_stop=False): 22 | if weights is None: 23 | weights = np.ones(origins.shape) 24 | if targets is None: 25 | targets = np.zeros(origins.shape, dtype=np.int8) 26 | assert targets.shape == origins.shape 27 | assert targets.shape == weights.shape 28 | path_handling = path_handling.lower() 29 | assert path_handling in ['none', 'n', 'assimilate', 'a', 'link', 'l'] 30 | n_rows, n_cols = origins.shape 31 | if path_handling[0] == 'n': 32 | path_handling = 0 33 | elif path_handling[0] == 'a': 34 | path_handling = 1 35 | elif path_handling[0] == 'l': 36 | path_handling = 2 37 | 38 | iteration = 0 39 | not_visited = 9999999999. 40 | 41 | if film: 42 | # frame_rate = int(1e6) 43 | frame_counter = 100000 44 | # frame_dirname = 'frames' 45 | try: 46 | os.mkdir(frame_dirname) 47 | except Exception: 48 | # NBD 49 | pass 50 | 51 | cwd = os.getcwd() 52 | try: 53 | os.chdir(frame_dirname) 54 | for filename in os.listdir('.'): 55 | os.remove(filename) 56 | except Exception: 57 | print('Frame deletion failed') 58 | finally: 59 | os.chdir(cwd) 60 | 61 | rendering = 1. / (2. * weights) 62 | rendering = np.minimum(rendering, 1.) 63 | target_locations = np.where(targets) 64 | n_targets = target_locations[0].size 65 | n_targets_remaining = n_targets 66 | n_targets_remaining_update = n_targets 67 | for i_target, row in enumerate(target_locations[0]): 68 | col = target_locations[1][i_target] 69 | wid = 8 70 | rendering[ 71 | row - wid: 72 | row + wid + 1, 73 | col - wid: 74 | col + wid + 1] = .5 75 | 76 | # The distance array shows the shortest weighted distance from 77 | # each point in the grid to the nearest origin point. 78 | distance = np.ones((n_rows, n_cols)) * not_visited 79 | origin_locations = np.where(origins != 0) 80 | distance[origin_locations] = 0. 81 | 82 | # The paths array shows each of the paths that are discovered 83 | # from targets to their nearest origin point. 84 | paths = np.zeros((n_rows, n_cols), dtype=np.int8) 85 | 86 | # The halo is the set of points under evaluation. They surround 87 | # the origin points and expand outward, forming a growing halo 88 | # around the set of origins that eventually enevlops targets. 89 | # It is implemented using a heap queue, so that the halo point 90 | # nearest to an origin is always the next one that gets evaluated. 91 | halo = [] 92 | for i, origin_row in enumerate(origin_locations[0]): 93 | origin_col = origin_locations[1][i] 94 | heapq.heappush(halo, (0., (origin_row, origin_col))) 95 | 96 | # The temporary array for tracking locations to add to the halo. 97 | # This gets overwritten with each iteration. 98 | new_locs = np.zeros((int(1e6), 3)) 99 | n_new_locs = 0 100 | 101 | edges = [] ##### 102 | 103 | while len(halo) > 0: 104 | iteration += 1 105 | if debug: 106 | if (n_targets_remaining > n_targets_remaining_update or 107 | iteration % 1e4 == 0.): 108 | n_targets_remaining = n_targets_remaining_update 109 | print('\r {num} targets of {total} reached, {rem} remaining, {halo_len} to try ' 110 | .format( 111 | num=n_targets - n_targets_remaining, 112 | total=n_targets, 113 | rem=n_targets_remaining, 114 | halo_len=len(halo), 115 | ), end='') 116 | sys.stdout.flush() 117 | if film: 118 | if iteration % frame_rate == 0: 119 | frame_counter = render( 120 | distance, 121 | frame_counter, 122 | frame_dirname, 123 | not_visited, 124 | rendering, 125 | ) 126 | 127 | # Reinitialize locations to add. 128 | new_locs[:n_new_locs, :] = 0. 129 | n_new_locs = 0 130 | 131 | # Retrieve and check the location with shortest distance. 132 | (distance_here, (row_here, col_here)) = heapq.heappop(halo) 133 | n_new_locs, n_targets_remaining_update = nb_loop( 134 | col_here, 135 | distance, 136 | distance_here, 137 | n_cols, 138 | n_new_locs, 139 | n_rows, 140 | n_targets_remaining, 141 | new_locs, 142 | not_visited, 143 | origins, 144 | path_handling, 145 | paths, 146 | row_here, 147 | targets, 148 | weights, 149 | edges 150 | ) 151 | for i_loc in range(n_new_locs): 152 | loc = (int(new_locs[i_loc, 1]), int(new_locs[i_loc, 2])) 153 | heapq.heappush(halo, (new_locs[i_loc, 0], loc)) 154 | 155 | if early_stop and n_targets_remaining_update == 0: ##### 156 | break 157 | 158 | if debug: 159 | print('\r ', end='') 160 | sys.stdout.flush() 161 | print('') 162 | # Add the newfound paths to the visualization. 163 | rendering = 1. / (1. + distance / 10.) 164 | rendering[np.where(origins)] = 1. 165 | rendering[np.where(paths)] = .8 166 | results = {'paths': paths, 'distance': distance, 'rendering': rendering, 'edges': edges} 167 | return results 168 | 169 | 170 | def render( 171 | distance, 172 | frame_counter, 173 | frame_dirname, 174 | not_visited, 175 | rendering, 176 | ): 177 | """ 178 | Turn the progress of the algorithm into a pretty picture. 179 | """ 180 | progress = rendering.copy() 181 | visited_locs = np.where(distance < not_visited) 182 | progress[visited_locs] = 1. / (1. + distance[visited_locs] / 10.) 183 | filename = 'pathfinder_frame_' + str(frame_counter) + '.png' 184 | cmap = 'inferno' 185 | dpi = 1200 186 | plt.figure(33374) 187 | plt.clf() 188 | plt.imshow( 189 | np.flip(progress.transpose(), axis=0), 190 | origin='higher', 191 | interpolation='nearest', 192 | cmap=plt.get_cmap(cmap), 193 | vmax=1., 194 | vmin=0., 195 | ) 196 | filename_full = os.path.join(frame_dirname, filename) 197 | plt.tight_layout() 198 | plt.savefig(filename_full, dpi=dpi) 199 | frame_counter += 1 200 | return frame_counter 201 | 202 | 203 | def nb_trace_back( 204 | distance, 205 | n_new_locs, 206 | new_locs, 207 | not_visited, 208 | origins, 209 | path_handling, 210 | paths, 211 | target, 212 | weights, 213 | edges ##### 214 | ): 215 | """ 216 | Connect each found electrified target to the grid through 217 | the shortest available path. 218 | """ 219 | # Handle the case where you find more than one target. 220 | path = [] 221 | distance_remaining = distance[target] 222 | current_location = target 223 | while distance_remaining > 0.: 224 | path.append(current_location) 225 | (row_here, col_here) = current_location 226 | # Check each of the neighbors for the lowest distance to grid. 227 | neighbors = [ 228 | ((row_here - 1, col_here), 1.), 229 | ((row_here + 1, col_here), 1.), 230 | ((row_here, col_here + 1), 1.), 231 | ((row_here, col_here - 1), 1.), 232 | ((row_here - 1, col_here - 1), 2.**.5), 233 | ((row_here + 1, col_here - 1), 2.**.5), 234 | ((row_here - 1, col_here + 1), 2.**.5), 235 | ((row_here + 1, col_here + 1), 2.**.5), 236 | ] 237 | lowest_distance = not_visited 238 | # It's confusing, but keep in mind that 239 | # distance[neighbor] is the distance from the neighbor position 240 | # to the grid, while neighbor_distance is 241 | # the distance *through* 242 | # the neighbor position to the grid. It is distance[neighbor] 243 | # plus the distance to the neighbor from the current position. 244 | for (neighbor, scale) in neighbors: 245 | if neighbor not in path: 246 | distance_from_neighbor = scale * weights[current_location] 247 | neighbor_distance = (distance[neighbor] + 248 | distance_from_neighbor) 249 | if neighbor_distance < lowest_distance: 250 | lowest_distance = neighbor_distance 251 | best_neighbor = neighbor 252 | 253 | # This will fail if caught in a local minimum. 254 | if distance_remaining < distance[best_neighbor]: 255 | distance_remaining = 0. 256 | continue 257 | 258 | distance_remaining = distance[best_neighbor] 259 | current_location = best_neighbor 260 | 261 | # Add this new path. 262 | for i_loc, loc in enumerate(path): 263 | paths[loc] = 1 264 | # If paths are to be linked, include the entire paths as origins and 265 | # add them to new_locs. If targets are to be assimilated, just add 266 | # the target (the first point on the path) to origins and new_locs. 267 | if path_handling == 2 or ( 268 | path_handling == 1 and i_loc == 0): 269 | origins[loc] = 1 270 | distance[loc] = 0. 271 | new_locs[n_new_locs, 0] = 0. 272 | new_locs[n_new_locs, 1] = loc[0] 273 | new_locs[n_new_locs, 2] = loc[1] 274 | n_new_locs += 1 275 | edges.append((path[0], path[-1])) ##### (target, origin) 276 | 277 | return n_new_locs 278 | 279 | 280 | def nb_loop( 281 | col_here, 282 | distance, 283 | distance_here, 284 | n_cols, 285 | n_new_locs, 286 | n_rows, 287 | n_targets_remaining, 288 | new_locs, 289 | not_visited, 290 | origins, 291 | path_handling, 292 | paths, 293 | row_here, 294 | targets, 295 | weights, 296 | edges ##### 297 | ): 298 | """ 299 | This is the meat of the computation. 300 | Pull the computationally expensive operations from seek() 301 | out into their own function that can be pre-compiled using numba. 302 | """ 303 | # Calculate the distance for each of the 8 neighbors. 304 | neighbors = [ 305 | ((row_here - 1, col_here), 1.), 306 | ((row_here + 1, col_here), 1.), 307 | ((row_here, col_here + 1), 1.), 308 | ((row_here, col_here - 1), 1.), 309 | ((row_here - 1, col_here - 1), 2.**.5), 310 | ((row_here + 1, col_here - 1), 2.**.5), 311 | ((row_here - 1, col_here + 1), 2.**.5), 312 | ((row_here + 1, col_here + 1), 2.**.5), 313 | ] 314 | 315 | for (neighbor, scale) in neighbors: 316 | weight = scale * weights[neighbor] 317 | neighbor_distance = distance_here + weight 318 | 319 | if distance[neighbor] == not_visited: 320 | if targets[neighbor]: 321 | n_new_locs = nb_trace_back( 322 | distance, 323 | n_new_locs, 324 | new_locs, 325 | not_visited, 326 | origins, 327 | path_handling, 328 | paths, 329 | neighbor, 330 | weights, 331 | edges 332 | ) 333 | targets[neighbor] = 0 334 | n_targets_remaining -= 1 335 | if neighbor_distance < distance[neighbor]: 336 | distance[neighbor] = neighbor_distance 337 | if (neighbor[0] > 0 and 338 | neighbor[0] < n_rows - 1 and 339 | neighbor[1] > 0 and 340 | neighbor[1] < n_cols - 1): 341 | new_locs[n_new_locs, 0] = distance[neighbor] 342 | new_locs[n_new_locs, 1] = neighbor[0] 343 | new_locs[n_new_locs, 2] = neighbor[1] 344 | n_new_locs += 1 345 | return n_new_locs, n_targets_remaining 346 | 347 | -------------------------------------------------------------------------------- /california/image_dataset.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function 2 | from __future__ import division 3 | import torch 4 | import torch.nn as nn 5 | import torch.optim as optim 6 | from torch.utils.data import Dataset, DataLoader 7 | import torchvision 8 | from torchvision import datasets, models, transforms, utils 9 | import torchvision.transforms.functional as TF 10 | 11 | import numpy as np 12 | import json 13 | import pandas as pd 14 | import pickle 15 | import matplotlib.pyplot as plt 16 | # import skimage 17 | # import skimage.io 18 | # import skimage.transform 19 | from PIL import Image 20 | import time 21 | import os 22 | from os.path import join, exists 23 | import copy 24 | import random 25 | from collections import OrderedDict 26 | 27 | 28 | class ImageFolderModified(Dataset): 29 | def __init__(self, root_dir, transform): 30 | self.root_dir = root_dir 31 | self.transform = transform 32 | self.idx2dir = [] 33 | self.path_list = [] 34 | for subdir in sorted(os.listdir(self.root_dir)): 35 | if not os.path.isfile(subdir): 36 | self.idx2dir.append(subdir) 37 | for class_idx, subdir in enumerate(self.idx2dir): 38 | class_dir = os.path.join(self.root_dir, subdir) 39 | for f in os.listdir(class_dir): 40 | if f[-4:] in ['.png', '.jpg', 'JPEG', 'jpeg']: 41 | self.path_list.append( 42 | [os.path.join(class_dir, f), class_idx]) 43 | 44 | def __len__(self): 45 | return len(self.path_list) 46 | 47 | def __getitem__(self, idx): 48 | img_path, class_idx = self.path_list[idx] 49 | image = Image.open(img_path) 50 | if not image.mode == 'RGB': 51 | image = image.convert('RGB') 52 | image = self.transform(image) 53 | sample = [image, class_idx, img_path] 54 | return sample 55 | 56 | 57 | class ImagePredictDataset(Dataset): 58 | def __init__(self, root_dir, transform): 59 | self.root_dir = root_dir 60 | self.transform = transform 61 | self.idx2dir = [] 62 | self.path_list = [] 63 | for f in os.listdir(root_dir): 64 | if f[-4:] in ['.png', '.jpg', 'JPEG', 'jpeg']: 65 | self.path_list.append(os.path.join(root_dir, f)) 66 | 67 | def __len__(self): 68 | return len(self.path_list) 69 | 70 | def __getitem__(self, idx): 71 | img_path = self.path_list[idx] 72 | image = Image.open(img_path) 73 | if not image.mode == 'RGB': 74 | image = image.convert('RGB') 75 | image = self.transform(image) 76 | sample = [image, img_path] 77 | return sample 78 | -------------------------------------------------------------------------------- /california/inception_modified.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function 2 | from __future__ import division 3 | import torch 4 | import torch.nn as nn 5 | import torch.optim as optim 6 | from torch.utils.data import Dataset, DataLoader 7 | import torchvision 8 | from torchvision import datasets, models, transforms, utils 9 | import torchvision.transforms.functional as TF 10 | 11 | from tqdm import tqdm 12 | import numpy as np 13 | import json 14 | import pandas as pd 15 | import pickle 16 | import matplotlib.pyplot as plt 17 | # import skimage 18 | # import skimage.io 19 | # import skimage.transform 20 | from PIL import Image 21 | import time 22 | import os 23 | from os.path import join, exists 24 | import copy 25 | import random 26 | from collections import OrderedDict 27 | from sklearn.metrics import r2_score 28 | 29 | 30 | import torch.nn.functional as F 31 | from torchvision.models import Inception3 32 | from collections import namedtuple 33 | 34 | _InceptionOuputs = namedtuple('InceptionOuputs', ['logits', 'aux_logits']) 35 | 36 | 37 | class InceptionSegmentation(nn.Module): 38 | def __init__(self, num_outputs=2, level=1): 39 | super(InceptionSegmentation, self).__init__() 40 | assert level in [1, 2] 41 | self.level = level 42 | self.inception3 = Inception3_modified(num_classes=num_outputs, aux_logits=False, transform_input=False) 43 | self.convolution1 = nn.Conv2d(288, 512, bias=True, kernel_size=3, padding=1) 44 | if self.level == 1: 45 | self.linear1 = nn.Linear(512, num_outputs, bias=False) 46 | else: 47 | self.convolution2 = nn.Conv2d(512, 512, bias=True, kernel_size=3, padding=1) 48 | self.linear2 = nn.Linear(512, num_outputs, bias=False) 49 | 50 | def forward(self, x, testing=False, classify_only=False): 51 | logits, intermediate = self.inception3(x) 52 | if classify_only: 53 | return logits 54 | feature_map = self.convolution1(intermediate) # N x 512 x 35 x 35 55 | feature_map = F.relu(feature_map) # N x 512 x 35 x 35 56 | if self.level == 1: 57 | y = F.adaptive_avg_pool2d(feature_map, (1, 1)) 58 | y = y.view(y.size(0), -1) # N x 512 59 | y = self.linear1(y) # N x 2 60 | if testing: 61 | CAM = self.linear1.weight.data[1, :] * feature_map.permute(0, 2, 3, 1) 62 | CAM = CAM.sum(dim=3) 63 | else: 64 | feature_map = self.convolution2(feature_map) # N x 512 x 35 x 35 65 | feature_map = F.relu(feature_map) # N x 512 x 35 x 35 66 | y = F.adaptive_avg_pool2d(feature_map, (1, 1)) 67 | y = y.view(y.size(0), -1) # N x 512 68 | y = self.linear2(y) # N x 2 69 | if testing: 70 | CAM = self.linear2.weight.data[1, :] * feature_map.permute(0, 2, 3, 1) 71 | CAM = CAM.sum(dim=3) 72 | if testing: 73 | return y, logits, CAM 74 | else: 75 | return y 76 | 77 | def load_basic_params(self, model_path, device=torch.device('cpu')): 78 | """Only load the parameters from main branch.""" 79 | old_params = torch.load(model_path, map_location=device) 80 | if model_path[-4:] == '.tar': # The file is not a model state dict, but a checkpoint dict 81 | old_params = old_params['model_state_dict'] 82 | self.inception3.load_state_dict(old_params, strict=False) 83 | print('Loaded basic model parameters from: ' + model_path) 84 | 85 | def load_existing_params(self, model_path, device=torch.device('cpu')): 86 | """Load the parameters of main branch and parameters of level-1 layers (and perhaps level-2 layers.)""" 87 | old_params = torch.load(model_path, map_location=device) 88 | if model_path[-4:] == '.tar': # The file is not a model state dict, but a checkpoint dict 89 | old_params = old_params['model_state_dict'] 90 | self.load_state_dict(old_params, strict=False) 91 | print('Loaded existing model parameters from: ' + model_path) 92 | 93 | 94 | class Inception3_modified(Inception3): 95 | def forward(self, x): 96 | if self.transform_input: 97 | x_ch0 = torch.unsqueeze(x[:, 0], 1) * (0.229 / 0.5) + (0.485 - 0.5) / 0.5 98 | x_ch1 = torch.unsqueeze(x[:, 1], 1) * (0.224 / 0.5) + (0.456 - 0.5) / 0.5 99 | x_ch2 = torch.unsqueeze(x[:, 2], 1) * (0.225 / 0.5) + (0.406 - 0.5) / 0.5 100 | x = torch.cat((x_ch0, x_ch1, x_ch2), 1) 101 | # N x 3 x 299 x 299 102 | x = self.Conv2d_1a_3x3(x) 103 | # N x 32 x 149 x 149 104 | x = self.Conv2d_2a_3x3(x) 105 | # N x 32 x 147 x 147 106 | x = self.Conv2d_2b_3x3(x) 107 | # N x 64 x 147 x 147 108 | x = F.max_pool2d(x, kernel_size=3, stride=2) 109 | # N x 64 x 73 x 73 110 | x = self.Conv2d_3b_1x1(x) 111 | # N x 80 x 73 x 73 112 | x = self.Conv2d_4a_3x3(x) 113 | # N x 192 x 71 x 71 114 | intermediate = x.clone() 115 | x = F.max_pool2d(x, kernel_size=3, stride=2) 116 | # N x 192 x 35 x 35 117 | x = self.Mixed_5b(x) 118 | # N x 256 x 35 x 35 119 | x = self.Mixed_5c(x) 120 | # N x 288 x 35 x 35 121 | x = self.Mixed_5d(x) 122 | intermediate = x.clone() 123 | # N x 288 x 35 x 35 124 | x = self.Mixed_6a(x) 125 | # N x 768 x 17 x 17 126 | x = self.Mixed_6b(x) 127 | # N x 768 x 17 x 17 128 | x = self.Mixed_6c(x) 129 | # N x 768 x 17 x 17 130 | x = self.Mixed_6d(x) 131 | # N x 768 x 17 x 17 132 | x = self.Mixed_6e(x) 133 | # N x 768 x 17 x 17 134 | if self.training and self.aux_logits: 135 | aux = self.AuxLogits(x) 136 | # N x 768 x 17 x 17 137 | x = self.Mixed_7a(x) 138 | # N x 1280 x 8 x 8 139 | x = self.Mixed_7b(x) 140 | # N x 2048 x 8 x 8 141 | x = self.Mixed_7c(x) 142 | # N x 2048 x 8 x 8 143 | # Adaptive average pooling 144 | x = F.adaptive_avg_pool2d(x, (1, 1)) 145 | # N x 2048 x 1 x 1 146 | x = F.dropout(x, training=self.training) 147 | # N x 2048 x 1 x 1 148 | x = x.view(x.size(0), -1) 149 | # N x 2048 150 | x = self.fc(x) 151 | # N x 1000 (num_classes) 152 | if self.training and self.aux_logits: 153 | return _InceptionOuputs(x, aux) 154 | return x, intermediate 155 | -------------------------------------------------------------------------------- /california/model_selection.py: -------------------------------------------------------------------------------- 1 | from __future__ import absolute_import 2 | from __future__ import division 3 | from __future__ import print_function 4 | from __future__ import unicode_literals 5 | from __future__ import with_statement 6 | import heapq 7 | import os 8 | import sys 9 | import time 10 | import pickle 11 | import pandas as pd 12 | import matplotlib 13 | from matplotlib.path import Path 14 | matplotlib.use('Agg') 15 | import matplotlib.pyplot as plt 16 | # from numba import autojit 17 | import numpy as np 18 | import census_tract_locator 19 | from tqdm import tqdm 20 | import geojson 21 | import copy 22 | from os.path import exists, join 23 | 24 | from utils import * 25 | from feature_engineering import * 26 | from evaluation_utils import * 27 | 28 | from sklearn.linear_model import LogisticRegression 29 | from sklearn.tree import DecisionTreeClassifier 30 | from sklearn.ensemble import RandomForestClassifier 31 | from sklearn.svm import SVC 32 | import xgboost as xgb 33 | 34 | 35 | all_feature_names = [ 36 | 'distance', 'on_same_road', 'adjacent', 'sv_pos_rate', 'min_sv_pole_angle_diff', 'avg_sv_pole_angle_diff', 37 | 'angle_diff_between_poles','dijkstra1', 'dijkstra2', 'dijkstra_and', 'dijkstra_sum', 'isdetected1', 'isdetected2', 'both_detected', 'at_intersection_1', 38 | 'at_intersection_2', 'both_at_intersection', 'at_same_intersections', 'sv_pos_rate_adj', 'sv_pos_rate_adj2', 39 | 'min_sv_pole_angle_diff_adj', 'avg_sv_pole_angle_diff_adj', 'angle_diff_between_poles_adj', 40 | 'either_at_intersection_nonadjacent' 41 | ] 42 | 43 | 44 | feature_set_candidates = { 45 | # 'all_features': all_feature_names, 46 | # 'feature_set_1': ['distance', 'on_same_road', 'adjacent', 'sv_pos_rate', 'dijkstra1', 'isdetected1', 'isdetected2'], 47 | # 'feature_set_2': ['distance', 'on_same_road', 'adjacent', 'sv_pos_rate_adj', 'dijkstra1', 'both_detected', 48 | # 'at_same_intersections', 'avg_sv_pole_angle_diff'], 49 | 'feature_set_3': ['distance', 'on_same_road', 'adjacent', 'sv_pos_rate', 'min_sv_pole_angle_diff', 'avg_sv_pole_angle_diff', 50 | 'angle_diff_between_poles','dijkstra1', 'isdetected1', 'isdetected2', 'both_detected', 'at_intersection_1', 51 | 'at_intersection_2', 'both_at_intersection', 'at_same_intersections', 'sv_pos_rate_adj', 'sv_pos_rate_adj2', 52 | 'min_sv_pole_angle_diff_adj', 'avg_sv_pole_angle_diff_adj', 'angle_diff_between_poles_adj', 53 | 'either_at_intersection_nonadjacent'] 54 | } 55 | 56 | 57 | def metrics(y_true, y_pred): 58 | TP = np.sum((y_true == 1) * (y_pred == 1)) 59 | TN = np.sum((y_true == 0) * (y_pred == 0)) 60 | FP = np.sum((y_true == 0) * (y_pred == 1)) 61 | FN = np.sum((y_true == 1) * (y_pred == 0)) 62 | precision = (TP + 1e-8) / (TP + FP + 1e-8) 63 | recall = (TP + 1e-8) / (TP + FN + 1e-8) 64 | F1 = 2 * precision * recall / (precision + recall) 65 | return round(precision, 4), round(recall, 4), round(F1, 4) 66 | 67 | 68 | class XGBoost: 69 | def __init__(self, max_depth=None, eta=None, gamma=None, Lambda=None, num_round=5, threshold=0.5): 70 | self.param = {'objective': 'binary:logistic'} 71 | if max_depth is not None: 72 | self.param['max_depth'] = max_depth 73 | if eta is not None: 74 | self.param['eta'] = eta 75 | if gamma is not None: 76 | self.param['gamma'] = gamma 77 | if Lambda is not None: 78 | self.param['lambda'] = Lambda 79 | self.num_round = num_round 80 | self.threshold = threshold 81 | self.bst = None 82 | 83 | def fit(self, X, y): 84 | dtrain = xgb.DMatrix(X, label=y) 85 | self.bst = xgb.train(self.param, dtrain, self.num_round) 86 | 87 | def predict(self, X): 88 | dtest = xgb.DMatrix(X) 89 | return self.bst.predict(dtest) >= self.threshold 90 | 91 | 92 | def get_average_test_perf(selected_feature_names, model, feature_dict_by_fips, label_dict_by_fips, idx2line, all_predicted_poles, ntrials=1): 93 | processed_feature_dict_by_fips = {} 94 | for fips in feature_dict_by_fips: 95 | processed_feature_dict_by_fips[fips] = [] 96 | for pole_pair in feature_dict_by_fips[fips]: 97 | raw_feature_list = feature_dict_by_fips[fips][pole_pair] 98 | feature_list = feature_transformation_flexible(pole_pair, raw_feature_list, idx2line, all_predicted_poles, selected_feature_names) 99 | processed_feature_dict_by_fips[fips].append(feature_list) 100 | avg_F1_test = 0 101 | boundaries_keys = sorted(feature_dict_by_fips.keys()) 102 | for n in range(ntrials): 103 | for test_fips in boundaries_keys: 104 | training_fips_list = copy.deepcopy(boundaries_keys) 105 | training_fips_list.remove(test_fips) 106 | training_feature_list = [] 107 | training_label_list = [] 108 | for fips in training_fips_list: 109 | training_feature_list += processed_feature_dict_by_fips[fips] 110 | training_label_list += label_dict_by_fips[fips] 111 | X_train = np.array(training_feature_list) 112 | y_train = np.array(training_label_list) 113 | X_test = np.array(processed_feature_dict_by_fips[test_fips]) 114 | y_test = np.array(label_dict_by_fips[test_fips]) 115 | model_use = copy.deepcopy(model) 116 | model_use.fit(X_train, y_train) 117 | # y_train_pred = model_use.predict(X_train) 118 | y_test_pred = model_use.predict(X_test) 119 | # precision_train, recall_train, F1_train = metrics(y_train, y_train_pred) 120 | precision_test, recall_test, F1_test = metrics(y_test, y_test_pred) 121 | avg_F1_test += F1_test 122 | avg_F1_test = avg_F1_test / (ntrials * len(boundaries_keys)) 123 | # print(model_use.feature_importances_) 124 | return avg_F1_test 125 | 126 | 127 | with open('results/SanCarlos_intermediate_data_for_model_selection.pickle', 'rb') as f: 128 | intermediate_result_dict = pickle.load(f) 129 | feature_dict_by_fips = intermediate_result_dict['feature_dict_by_fips'] 130 | label_dict_by_fips = intermediate_result_dict['label_dict_by_fips'] 131 | idx2line = intermediate_result_dict['idx2line'] 132 | all_predicted_poles = intermediate_result_dict['all_predicted_poles'] 133 | 134 | perf_dict = {} 135 | 136 | # Decision Tree 137 | # print('----------------- Decision Tree') 138 | # perf_dict['DT'] = {} 139 | # for max_depth in [2, 3, 4, 5, 6, 7]: 140 | # print(max_depth) 141 | # perf_dict['DT'][max_depth] = {} 142 | # for feature_set in feature_set_candidates: 143 | # selected_feature_names = feature_set_candidates[feature_set] 144 | # model = DecisionTreeClassifier(max_depth=max_depth) 145 | # f1 = get_average_test_perf(selected_feature_names, model, feature_dict_by_fips, label_dict_by_fips, 146 | # idx2line, all_predicted_poles, ntrials=1) 147 | # perf_dict['DT'][max_depth][feature_set] = f1 148 | # 149 | # with open('results/model_selection_performance_dict.pickle', 'wb') as f: 150 | # pickle.dump(perf_dict, f) 151 | 152 | # Logistic Regression 153 | # print('----------------- Logistic Regression') 154 | # perf_dict['LR'] = {} 155 | # for l2_term in [1, 10, 100, 1000]: 156 | # print(l2_term) 157 | # perf_dict['LR'][l2_term] = {} 158 | # for feature_set in feature_set_candidates: 159 | # selected_feature_names = feature_set_candidates[feature_set] 160 | # model = LogisticRegression(C=l2_term) 161 | # f1 = get_average_test_perf(selected_feature_names, model, feature_dict_by_fips, label_dict_by_fips, 162 | # idx2line, all_predicted_poles, ntrials=1) 163 | # perf_dict['LR'][l2_term][feature_set] = f1 164 | # 165 | # with open('results/model_selection_performance_dict.pickle', 'wb') as f: 166 | # pickle.dump(perf_dict, f) 167 | 168 | 169 | # SVC 170 | # print('----------------- SVC') 171 | # perf_dict['SVC'] = {} 172 | # for kernel in ['linear', 'poly', 'rbf', 'sigmoid']: 173 | # print(kernel) 174 | # perf_dict['SVC'][kernel] = {} 175 | # for C in [0.1, 1, 3, 10]: 176 | # perf_dict['SVC'][kernel][C] = {} 177 | # for feature_set in feature_set_candidates: 178 | # selected_feature_names = feature_set_candidates[feature_set] 179 | # model = SVC(C=C, kernel=kernel) 180 | # f1 = get_average_test_perf(selected_feature_names, model, feature_dict_by_fips, label_dict_by_fips, 181 | # idx2line, all_predicted_poles, ntrials=1) 182 | # perf_dict['SVC'][kernel][C][feature_set] = f1 183 | # 184 | # with open('results/model_selection_performance_dict.pickle', 'wb') as f: 185 | # pickle.dump(perf_dict, f) 186 | 187 | # Gradient Boosting 188 | print('----------------- Gradient Boosting') 189 | perf_dict['GB'] = {} 190 | for max_depth in [2, 3, 4, 5, 6]: 191 | perf_dict['GB'][max_depth] = {} 192 | for eta in [0.1, 0.2, 0.4, 0.6, 0.8]: 193 | print(max_depth, eta) 194 | perf_dict['GB'][max_depth][eta] = {} 195 | for Lambda in [0, 1, 2, 5]: 196 | perf_dict['GB'][max_depth][eta][Lambda] = {} 197 | for num_round in [11, 13, 15]: 198 | perf_dict['GB'][max_depth][eta][Lambda][num_round] = {} 199 | for threshold in [0.25, 0.3, 0.35, 0.4]: 200 | perf_dict['GB'][max_depth][eta][Lambda][num_round][threshold] = {} 201 | for feature_set in feature_set_candidates: 202 | selected_feature_names = feature_set_candidates[feature_set] 203 | model = XGBoost(max_depth=max_depth, eta=eta, gamma=0, Lambda=Lambda, 204 | num_round=num_round, threshold=threshold) 205 | f1 = get_average_test_perf(selected_feature_names, model, feature_dict_by_fips, 206 | label_dict_by_fips, 207 | idx2line, all_predicted_poles, ntrials=1) 208 | perf_dict['GB'][max_depth][eta][Lambda][num_round][threshold][feature_set] = f1 209 | 210 | # with open('results/model_selection_performance_dict.pickle', 'wb') as f: 211 | # pickle.dump(perf_dict, f) 212 | 213 | # Random Forest 214 | # print('----------------- Random Forest') 215 | # perf_dict['RF'] = {} 216 | # for n_estimators in [10, 30, 100, 150]: 217 | # perf_dict['RF'][n_estimators] = {} 218 | # for max_depth in [2, 3, 4, 5, 6]: 219 | # print(n_estimators, perf_dict) 220 | # perf_dict['RF'][n_estimators][max_depth] = {} 221 | # for feature_set in feature_set_candidates: 222 | # selected_feature_names = feature_set_candidates[feature_set] 223 | # model = RandomForestClassifier(n_estimators=n_estimators, max_depth=max_depth) 224 | # f1 = get_average_test_perf(selected_feature_names, model, feature_dict_by_fips, label_dict_by_fips, 225 | # idx2line, all_predicted_poles, ntrials=6) 226 | # perf_dict['RF'][n_estimators][max_depth][feature_set] = f1 227 | 228 | with open('results/model_selection_performance_dict_GBonly_2.pickle', 'wb') as f: 229 | pickle.dump(perf_dict, f) 230 | 231 | -------------------------------------------------------------------------------- /california/streetView.py: -------------------------------------------------------------------------------- 1 | import urllib.request 2 | import urllib 3 | import json 4 | 5 | 6 | class StreetView(): 7 | """Google Street View API application class""" 8 | 9 | def __init__(self): 10 | self.api_key_list = [ 11 | '***************************************', # Your API key 1 12 | '***************************************', # Your API key 2 13 | '***************************************', # Your API key 3 14 | ] 15 | self.key_rank = 0 16 | self.API_KEY = self.api_key_list[self.key_rank] 17 | 18 | def getMetaStatus(self, location, heading='0', pitch='0', fov='90', 19 | radius='50', returnMeta=False): 20 | # retrieve the meta data through meta API. The returned variable is a dictionary. 21 | # If the image exists at that point, meta['status'] = 'OK', otherwise: meta['status'] = 'ZERO_RESULTS' 22 | # For every coordinates, please run this to check existence before downloading image!! 23 | self.setParameters(location=location, heading=heading, 24 | pitch=pitch, fov=fov, radius=radius) 25 | # print(self.parameters) 26 | meta_url = 'https://maps.googleapis.com/maps/api/streetview/metadata?'+self.parameters 27 | response = urllib.request.urlopen(meta_url) 28 | meta = response.read() 29 | meta = json.loads(meta) 30 | if (returnMeta): 31 | self.nextKey() 32 | return meta['status'], meta 33 | else: 34 | return meta['status'] 35 | 36 | def getStreetView(self, location, filepath='dafault.jpg', heading='0', pitch='0', fov='90', radius='50', uncheck=True): 37 | # download images from image_url to local path 38 | # You need to specify "size", "location", "fov", "heading", "pitch", see details in Google API's documentation 39 | # in Python 3, you may use urllib.request.urlretrieve instead of urllib.urlretrieve 40 | # uncheck==False: download without check meta 41 | # filepath : image save path 42 | if(uncheck): 43 | status = self.getMetaStatus( 44 | location=location, heading=heading, pitch=pitch, fov=fov, radius=radius) 45 | else: 46 | self.setParameters(location=location, heading=heading, 47 | pitch=pitch, fov=fov, radius=radius) 48 | status = 'OK' 49 | 50 | if(status == 'OK'): 51 | image_url = 'https://maps.googleapis.com/maps/api/streetview?' + self.parameters 52 | local_path = filepath 53 | urllib.request.urlretrieve(image_url, local_path) 54 | self.nextKey() 55 | # print('Success!') 56 | return True 57 | elif (status == 'ZERO_RESULTS'): 58 | # not found 59 | # print('ZERO_RESULTS') 60 | return False 61 | else: 62 | print('Error:'+status) 63 | return False 64 | 65 | def nextKey(self): 66 | self.key_rank += 1 67 | if (self.key_rank >= len(self.api_key_list)): 68 | self.key_rank = 0 69 | self.API_KEY = self.api_key_list[self.key_rank] 70 | 71 | def setParameters(self, location, heading, pitch, fov, radius): 72 | size = ['640', '640'] 73 | #latitude, longitude 74 | location = [str(location[0]), str(location[1])] 75 | self.parameters = 'size=' + size[0] + 'x' + size[1] + '&location=' + location[0] + ',' + location[1] + \ 76 | '&fov=' + fov + '&heading=' + heading + '&pitch=' + pitch + \ 77 | '&radius=' + radius + '&key=' + self.API_KEY 78 | -------------------------------------------------------------------------------- /model_dev/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangzhecheng/GridMapping/e5f6360132dea5b67c97a6fb0e82fe85d3f63352/model_dev/__init__.py -------------------------------------------------------------------------------- /model_dev/__pycache__/image_dataset.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangzhecheng/GridMapping/e5f6360132dea5b67c97a6fb0e82fe85d3f63352/model_dev/__pycache__/image_dataset.cpython-36.pyc -------------------------------------------------------------------------------- /model_dev/__pycache__/inception_modified.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangzhecheng/GridMapping/e5f6360132dea5b67c97a6fb0e82fe85d3f63352/model_dev/__pycache__/inception_modified.cpython-36.pyc -------------------------------------------------------------------------------- /model_dev/image_dataset.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function 2 | from __future__ import division 3 | import torch 4 | import torch.nn as nn 5 | import torch.optim as optim 6 | from torch.utils.data import Dataset, DataLoader 7 | import torchvision 8 | from torchvision import datasets, models, transforms, utils 9 | import torchvision.transforms.functional as TF 10 | 11 | import numpy as np 12 | import json 13 | import pandas as pd 14 | import pickle 15 | import matplotlib.pyplot as plt 16 | # import skimage 17 | # import skimage.io 18 | # import skimage.transform 19 | from PIL import Image 20 | import time 21 | import os 22 | from os.path import join, exists 23 | import copy 24 | import random 25 | from collections import OrderedDict 26 | 27 | 28 | class ImageFolderModified(Dataset): 29 | def __init__(self, root_dir, transform): 30 | self.root_dir = root_dir 31 | self.transform = transform 32 | self.idx2dir = [] 33 | self.path_list = [] 34 | for subdir in sorted(os.listdir(self.root_dir)): 35 | if not os.path.isfile(subdir): 36 | self.idx2dir.append(subdir) 37 | for class_idx, subdir in enumerate(self.idx2dir): 38 | class_dir = os.path.join(self.root_dir, subdir) 39 | for f in os.listdir(class_dir): 40 | if f[-4:] in ['.png', '.jpg', 'JPEG', 'jpeg']: 41 | self.path_list.append( 42 | [os.path.join(class_dir, f), class_idx]) 43 | 44 | def __len__(self): 45 | return len(self.path_list) 46 | 47 | def __getitem__(self, idx): 48 | img_path, class_idx = self.path_list[idx] 49 | image = Image.open(img_path) 50 | if not image.mode == 'RGB': 51 | image = image.convert('RGB') 52 | image = self.transform(image) 53 | sample = [image, class_idx, img_path] 54 | return sample 55 | 56 | 57 | class ImagePredictDataset(Dataset): 58 | def __init__(self, root_dir, transform): 59 | self.root_dir = root_dir 60 | self.transform = transform 61 | self.idx2dir = [] 62 | self.path_list = [] 63 | for f in os.listdir(root_dir): 64 | if f[-4:] in ['.png', '.jpg', 'JPEG', 'jpeg']: 65 | self.path_list.append(os.path.join(root_dir, f)) 66 | 67 | def __len__(self): 68 | return len(self.path_list) 69 | 70 | def __getitem__(self, idx): 71 | img_path = self.path_list[idx] 72 | image = Image.open(img_path) 73 | if not image.mode == 'RGB': 74 | image = image.convert('RGB') 75 | image = self.transform(image) 76 | sample = [image, img_path] 77 | return sample 78 | 79 | 80 | class FolderDirsDataset(Dataset): 81 | def __init__(self, dirs_list, transform, return_path=False): 82 | """ 83 | :param dirs_list: list. Length: number of classes. Each entries is a list of directories belonging to its class. 84 | """ 85 | self.sample_list = [] 86 | self.transform = transform 87 | self.return_path = return_path 88 | for i, dirs in enumerate(dirs_list): 89 | for dir in dirs: 90 | for f in os.listdir(dir): 91 | if f[-4:] in ['.png', '.jpg', 'JPEG', 'jpeg']: 92 | self.sample_list.append((join(dir, f), i)) 93 | 94 | def __len__(self): 95 | return len(self.sample_list) 96 | 97 | def __getitem__(self, idx): 98 | img_path, class_idx = self.sample_list[idx] 99 | image = Image.open(img_path) 100 | if not image.mode == 'RGB': 101 | image = image.convert('RGB') 102 | image = self.transform(image) 103 | if self.return_path: 104 | sample = [image, class_idx, img_path] 105 | else: 106 | sample = [image, class_idx] 107 | return sample 108 | -------------------------------------------------------------------------------- /model_dev/inception_modified.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function 2 | from __future__ import division 3 | import torch 4 | import torch.nn as nn 5 | import torch.optim as optim 6 | from torch.utils.data import Dataset, DataLoader 7 | import torchvision 8 | from torchvision import datasets, models, transforms, utils 9 | import torchvision.transforms.functional as TF 10 | 11 | from tqdm import tqdm 12 | import numpy as np 13 | import json 14 | import pandas as pd 15 | import pickle 16 | import matplotlib.pyplot as plt 17 | # import skimage 18 | # import skimage.io 19 | # import skimage.transform 20 | from PIL import Image 21 | import time 22 | import os 23 | from os.path import join, exists 24 | import copy 25 | import random 26 | from collections import OrderedDict 27 | from sklearn.metrics import r2_score 28 | 29 | 30 | import torch.nn.functional as F 31 | from torchvision.models import Inception3 32 | from collections import namedtuple 33 | 34 | _InceptionOuputs = namedtuple('InceptionOuputs', ['logits', 'aux_logits']) 35 | 36 | 37 | class InceptionSegmentation(nn.Module): 38 | def __init__(self, num_outputs=2, level=1): 39 | super(InceptionSegmentation, self).__init__() 40 | assert level in [1, 2] 41 | self.level = level 42 | self.inception3 = Inception3_modified(num_classes=num_outputs, aux_logits=False, transform_input=False) 43 | self.convolution1 = nn.Conv2d(288, 512, bias=True, kernel_size=3, padding=1) 44 | if self.level == 1: 45 | self.linear1 = nn.Linear(512, num_outputs, bias=False) 46 | else: 47 | self.convolution2 = nn.Conv2d(512, 512, bias=True, kernel_size=3, padding=1) 48 | self.linear2 = nn.Linear(512, num_outputs, bias=False) 49 | 50 | def forward(self, x, testing=False, classify_only=False): 51 | logits, intermediate = self.inception3(x) 52 | if classify_only: 53 | return logits 54 | feature_map = self.convolution1(intermediate) # N x 512 x 35 x 35 55 | feature_map = F.relu(feature_map) # N x 512 x 35 x 35 56 | if self.level == 1: 57 | y = F.adaptive_avg_pool2d(feature_map, (1, 1)) 58 | y = y.view(y.size(0), -1) # N x 512 59 | y = self.linear1(y) # N x 2 60 | if testing: 61 | CAM = self.linear1.weight.data[1, :] * feature_map.permute(0, 2, 3, 1) 62 | CAM = CAM.sum(dim=3) 63 | else: 64 | feature_map = self.convolution2(feature_map) # N x 512 x 35 x 35 65 | feature_map = F.relu(feature_map) # N x 512 x 35 x 35 66 | y = F.adaptive_avg_pool2d(feature_map, (1, 1)) 67 | y = y.view(y.size(0), -1) # N x 512 68 | y = self.linear2(y) # N x 2 69 | if testing: 70 | CAM = self.linear2.weight.data[1, :] * feature_map.permute(0, 2, 3, 1) 71 | CAM = CAM.sum(dim=3) 72 | if testing: 73 | return y, logits, CAM # y: prediction of segmentation branch, logits: prediction of main branch, CAM: class activation map 74 | else: 75 | return y 76 | 77 | def load_basic_params(self, model_path, device=torch.device('cpu')): 78 | """Only load the parameters from main branch.""" 79 | old_params = torch.load(model_path, map_location=device) 80 | if model_path[-4:] == '.tar': # The file is not a model state dict, but a checkpoint dict 81 | old_params = old_params['model_state_dict'] 82 | self.inception3.load_state_dict(old_params, strict=False) 83 | print('Loaded basic model parameters from: ' + model_path) 84 | 85 | def load_existing_params(self, model_path, device=torch.device('cpu')): 86 | """Load the parameters of main branch and parameters of level-1 layers (and perhaps level-2 layers.)""" 87 | old_params = torch.load(model_path, map_location=device) 88 | if model_path[-4:] == '.tar': # The file is not a model state dict, but a checkpoint dict 89 | old_params = old_params['model_state_dict'] 90 | self.load_state_dict(old_params, strict=False) 91 | print('Loaded existing model parameters from: ' + model_path) 92 | 93 | 94 | class Inception3_modified(Inception3): 95 | def forward(self, x): 96 | if self.transform_input: 97 | x_ch0 = torch.unsqueeze(x[:, 0], 1) * (0.229 / 0.5) + (0.485 - 0.5) / 0.5 98 | x_ch1 = torch.unsqueeze(x[:, 1], 1) * (0.224 / 0.5) + (0.456 - 0.5) / 0.5 99 | x_ch2 = torch.unsqueeze(x[:, 2], 1) * (0.225 / 0.5) + (0.406 - 0.5) / 0.5 100 | x = torch.cat((x_ch0, x_ch1, x_ch2), 1) 101 | # N x 3 x 299 x 299 102 | x = self.Conv2d_1a_3x3(x) 103 | # N x 32 x 149 x 149 104 | x = self.Conv2d_2a_3x3(x) 105 | # N x 32 x 147 x 147 106 | x = self.Conv2d_2b_3x3(x) 107 | # N x 64 x 147 x 147 108 | x = F.max_pool2d(x, kernel_size=3, stride=2) 109 | # N x 64 x 73 x 73 110 | x = self.Conv2d_3b_1x1(x) 111 | # N x 80 x 73 x 73 112 | x = self.Conv2d_4a_3x3(x) 113 | # N x 192 x 71 x 71 114 | intermediate = x.clone() 115 | x = F.max_pool2d(x, kernel_size=3, stride=2) 116 | # N x 192 x 35 x 35 117 | x = self.Mixed_5b(x) 118 | # N x 256 x 35 x 35 119 | x = self.Mixed_5c(x) 120 | # N x 288 x 35 x 35 121 | x = self.Mixed_5d(x) 122 | intermediate = x.clone() 123 | # N x 288 x 35 x 35 124 | x = self.Mixed_6a(x) 125 | # N x 768 x 17 x 17 126 | x = self.Mixed_6b(x) 127 | # N x 768 x 17 x 17 128 | x = self.Mixed_6c(x) 129 | # N x 768 x 17 x 17 130 | x = self.Mixed_6d(x) 131 | # N x 768 x 17 x 17 132 | x = self.Mixed_6e(x) 133 | # N x 768 x 17 x 17 134 | if self.training and self.aux_logits: 135 | aux = self.AuxLogits(x) 136 | # N x 768 x 17 x 17 137 | x = self.Mixed_7a(x) 138 | # N x 1280 x 8 x 8 139 | x = self.Mixed_7b(x) 140 | # N x 2048 x 8 x 8 141 | x = self.Mixed_7c(x) 142 | # N x 2048 x 8 x 8 143 | # Adaptive average pooling 144 | x = F.adaptive_avg_pool2d(x, (1, 1)) 145 | # N x 2048 x 1 x 1 146 | x = F.dropout(x, training=self.training) 147 | # N x 2048 x 1 x 1 148 | x = x.view(x.size(0), -1) 149 | # N x 2048 150 | x = self.fc(x) 151 | # N x 1000 (num_classes) 152 | if self.training and self.aux_logits: 153 | return _InceptionOuputs(x, aux) 154 | return x, intermediate 155 | -------------------------------------------------------------------------------- /model_dev/test_CAM_branch_pytorch.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function 2 | from __future__ import division 3 | import torch 4 | import torch.nn as nn 5 | import torch.optim as optim 6 | from torch.utils.data import Dataset, DataLoader 7 | import torchvision 8 | from torchvision import datasets, models, transforms, utils 9 | import torchvision.transforms.functional as TF 10 | 11 | from tqdm import tqdm 12 | import numpy as np 13 | import json 14 | import pandas as pd 15 | import pickle 16 | import matplotlib.pyplot as plt 17 | # import skimage 18 | # import skimage.io 19 | # import skimage.transform 20 | from PIL import Image 21 | import time 22 | import os 23 | from os.path import join, exists 24 | import copy 25 | import random 26 | from collections import OrderedDict 27 | from sklearn.metrics import r2_score 28 | 29 | from torch.nn import functional as F 30 | from torchvision.models import Inception3 31 | 32 | from inception_modified import InceptionSegmentation 33 | from image_dataset import * 34 | 35 | """ 36 | This script is for running the line/pole model on the street view image 37 | test set to generate the Class Activation Maps (CAMs) for lines/poles. 38 | It takes ~1 min to run on a Nvidia Tesla K80 GPU. 39 | Note: if the script is run on CPU instead of GPU, please replace ".cpu().item()" 40 | with ".item()". E.g., line 130. 41 | """ 42 | 43 | target = "line" # target object to identify: "line" or "pole" 44 | pretrained_models = { 45 | "pole": 'deepGrid_DPNH2seg_pretrained.tar', 46 | "line": 'deepGrid_seg_pretrained.tar', 47 | } 48 | dataset_dirs = { 49 | "pole": "pole_image_dataset_demo", 50 | "line": "line_image_dataset_demo", 51 | } 52 | 53 | def determine_root_dir(): 54 | """ 55 | This function is used to locate the root dir back to the parent directory, 56 | i.e., "GridMapping" directory. 57 | """ 58 | root_dir = os.path.abspath(os.path.join(os.getcwd(), os.pardir)) 59 | assert root_dir.strip('/')[-11:] == 'GridMapping' 60 | return root_dir 61 | 62 | root_dir = determine_root_dir() 63 | 64 | # Configuration 65 | # directory for loading training/validation/test data 66 | test_dir_list = [ 67 | [ 68 | join(root_dir, 'data', dataset_dirs[target], 'test', '0'), # negative samples 69 | ], 70 | [ 71 | join(root_dir, 'data', dataset_dirs[target], 'test', '1'), # positive samples 72 | ] 73 | ] 74 | 75 | # old model checkpoint for the entire model (main branch + CAM branch) 76 | old_ckpt_path = join(root_dir, 'checkpoint', pretrained_models[target]) 77 | # save the derived CAMs into a pickle file 78 | CAM_save_path = join(root_dir, 'results', 'CAM_test', 'CAMs_test_set_' + target + '.pickle') 79 | 80 | device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu") 81 | input_size = 640 82 | batch_size = 1 # must be 1 for testing segmentation 83 | threshold = 0.5 # threshold probability to identify am image as positive 84 | level = 1 85 | 86 | preview = False 87 | 88 | 89 | def metrics(stats): 90 | """stats: {'TP': TP, 'FP': FP, 'TN': TN, 'FN': FN} 91 | return: must be a single number """ 92 | precision = (stats['TP'] + 0.00001) * 1.0 / \ 93 | (stats['TP'] + stats['FP'] + 0.00001) 94 | recall = (stats['TP'] + 0.00001) * 1.0 / \ 95 | (stats['TP'] + stats['FN'] + 0.00001) 96 | # print('precision:%.4f recall:%.4f' % (precision, recall)) 97 | return 2 * precision * recall / (precision + recall) 98 | 99 | 100 | def test_model(model, dataloader, metrics, threshold): 101 | stats = {'TP': 0, 'FP': 0, 'TN': 0, 'FN': 0} 102 | model.eval() 103 | CAM_list = [] 104 | for inputs, labels, paths in tqdm(dataloader): 105 | inputs = inputs.to(device) 106 | labels = labels.to(device) 107 | with torch.set_grad_enabled(False): 108 | # CAM is a 1 x 35 x 35 activation map 109 | _, outputs, CAM = model(inputs, testing=True) 110 | prob = F.softmax(outputs, dim=1) 111 | preds = prob[:, 1] >= threshold 112 | 113 | # transform tensor into numpy array 114 | CAM = CAM.squeeze(0).cpu().numpy() 115 | for i in range(preds.size(0)): 116 | predicted_label = preds[i] 117 | label = predicted_label.cpu().item() 118 | if(preview): 119 | CAM_list.append((CAM, paths[i], label)) 120 | else: 121 | if label: 122 | # only use the generated CAM if it is predicted to be 1 123 | CAM_list.append((CAM, paths[i], label)) 124 | else: 125 | # otherwise the CAM is a totally black one 126 | CAM_list.append((np.zeros_like(CAM), paths[i], label)) 127 | 128 | stats['TP'] += torch.sum((preds == 1) * (labels == 1)).cpu().item() 129 | stats['TN'] += torch.sum((preds == 0) * (labels == 0)).cpu().item() 130 | stats['FP'] += torch.sum((preds == 1) * (labels == 0)).cpu().item() 131 | stats['FN'] += torch.sum((preds == 0) * (labels == 1)).cpu().item() 132 | 133 | metric_value = metrics(stats) 134 | return stats, metric_value, CAM_list 135 | 136 | 137 | transform_test = transforms.Compose([ 138 | transforms.Resize(input_size), 139 | transforms.ToTensor(), 140 | transforms.Normalize([0.5, 0.5, 0.5], [0.5, 0.5, 0.5]) 141 | ]) 142 | 143 | if __name__ == '__main__': 144 | # data 145 | dataset_test = FolderDirsDataset(test_dir_list, transform_test, return_path=True) 146 | dataloader_test = DataLoader( 147 | dataset_test, batch_size=batch_size, shuffle=False, num_workers=4) 148 | print('Test set size: ' + str(len(dataset_test))) 149 | # model 150 | model = InceptionSegmentation(num_outputs=2, level=level) 151 | model.load_existing_params(old_ckpt_path) 152 | 153 | model = model.to(device) 154 | 155 | stats, metric_value, CAM_list = test_model( 156 | model, dataloader_test, metrics, threshold=threshold) 157 | precision = (stats['TP'] + 0.00001) * 1.0 / \ 158 | (stats['TP'] + stats['FP'] + 0.00001) 159 | recall = (stats['TP'] + 0.00001) * 1.0 / \ 160 | (stats['TP'] + stats['FN'] + 0.00001) 161 | print('metric value: '+str(metric_value)) 162 | print('precision: ' + str(round(precision, 4))) 163 | print('recall: ' + str(round(recall, 4))) 164 | 165 | with open(CAM_save_path, 'wb') as f: 166 | pickle.dump(CAM_list, f) 167 | -------------------------------------------------------------------------------- /model_dev/test_classification_pytorch.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function 2 | from __future__ import division 3 | import torch 4 | import torch.nn as nn 5 | import torch.optim as optim 6 | from torch.utils.data import Dataset, DataLoader 7 | import torchvision 8 | from torchvision import datasets, models, transforms, utils 9 | import torchvision.transforms.functional as TF 10 | 11 | from tqdm import tqdm 12 | import numpy as np 13 | import json 14 | import pandas as pd 15 | import pickle 16 | import matplotlib.pyplot as plt 17 | # import skimage 18 | # import skimage.io 19 | # import skimage.transform 20 | from PIL import Image 21 | import time 22 | import os 23 | from os.path import join, exists 24 | import copy 25 | import random 26 | from collections import OrderedDict 27 | from sklearn.metrics import r2_score 28 | 29 | from torch.nn import functional as F 30 | from torchvision.models import Inception3 31 | from image_dataset import * 32 | 33 | """ 34 | This script is for running the line/pole identification model on the 35 | street view image test set and reporting the image-level metrics including 36 | precision and recall. 37 | It takes ~1 min to run on a Nvidia Tesla K80 GPU. 38 | Note: if the script is run on CPU instead of GPU, please replace ".cpu().item()" 39 | with ".item()". E.g., line 107. 40 | """ 41 | 42 | target = "line" # target object to identify: "line" or "pole" 43 | pretrained_models = { 44 | "pole": 'deepGrid_DPNH2seg_pretrained.tar', 45 | "line": 'deepGrid_seg_pretrained.tar', 46 | } 47 | dataset_dirs = { 48 | "pole": "pole_image_dataset_demo", 49 | "line": "line_image_dataset_demo", 50 | } 51 | 52 | def determine_root_dir(): 53 | """ 54 | This function is used to locate the root dir back to the parent directory, 55 | i.e., "GridMapping" directory. 56 | """ 57 | root_dir = os.path.abspath(os.path.join(os.getcwd(), os.pardir)) 58 | assert root_dir.strip('/')[-11:] == 'GridMapping' 59 | return root_dir 60 | 61 | root_dir = determine_root_dir() 62 | 63 | # Configuration 64 | # directory for loading training/validation/test data 65 | test_dir_list = [ 66 | [ 67 | join(root_dir, 'data', dataset_dirs[target], 'test', '0'), # negative samples 68 | ], 69 | [ 70 | join(root_dir, 'data', dataset_dirs[target], 'test', '1'), # positive samples 71 | ] 72 | ] 73 | 74 | old_ckpt_path = join(root_dir, 'checkpoint', pretrained_models[target]) # path to model's old checkpoint 75 | 76 | initialize_from_seg_model = True # if the model is initialized/loaded from a segmentation model (i.e. an instance of InceptionSegmentation), 77 | # then the layer names in model_state_dict should be renamed. 78 | device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu") 79 | input_size = 640 80 | batch_size = 16 81 | given_threshold = 0.5 # threshold probability to identify am image as positive 82 | threshold_list = np.linspace(0.0, 1.0, 101).tolist() + [given_threshold] 83 | 84 | def metrics(stats): 85 | """stats: {'TP': TP, 'FP': FP, 'TN': TN, 'FN': FN} 86 | return: must be a single number """ 87 | precision = (stats['TP'] + 0.00001) * 1.0 / \ 88 | (stats['TP'] + stats['FP'] + 0.00001) 89 | recall = (stats['TP'] + 0.00001) * 1.0 / \ 90 | (stats['TP'] + stats['FN'] + 0.00001) 91 | return 2 * precision * recall / (precision + recall) 92 | 93 | 94 | def test_model(model, dataloader, metrics, threshold_list): 95 | # stats = {'TP': 0, 'FP': 0, 'TN': 0, 'FN': 0} 96 | stats = {x: {'TP': 0, 'FP': 0, 'TN': 0, 'FN': 0} for x in threshold_list} 97 | model.eval() 98 | for inputs, labels in tqdm(dataloader, ascii=True): 99 | inputs = inputs.to(device) 100 | labels = labels.to(device) 101 | with torch.set_grad_enabled(False): 102 | outputs = model(inputs) 103 | prob = F.softmax(outputs, dim=1) 104 | for threshold in threshold_list: 105 | preds = prob[:, 1] >= threshold 106 | stats[threshold]['TP'] += torch.sum((preds == 1) * (labels == 1)).cpu().item() 107 | stats[threshold]['TN'] += torch.sum((preds == 0) * (labels == 0)).cpu().item() 108 | stats[threshold]['FP'] += torch.sum((preds == 1) * (labels == 0)).cpu().item() 109 | stats[threshold]['FN'] += torch.sum((preds == 0) * (labels == 1)).cpu().item() 110 | 111 | best_threshold = 0.0 112 | max_metrics = 0.0 113 | for threshold in threshold_list: 114 | metric_value = metrics(stats[threshold]) 115 | if metric_value > max_metrics: 116 | best_threshold = threshold 117 | max_metrics = metric_value 118 | # metric_value = metrics(stats) 119 | return stats, max_metrics, best_threshold 120 | 121 | 122 | transform_test = transforms.Compose([ 123 | transforms.Resize(input_size), 124 | transforms.ToTensor(), 125 | transforms.Normalize([0.5, 0.5, 0.5], [0.5, 0.5, 0.5]) 126 | ]) 127 | 128 | if __name__ == '__main__': 129 | # data 130 | dataset_test = FolderDirsDataset(test_dir_list, transform_test) 131 | dataloader_test = DataLoader( 132 | dataset_test, batch_size=batch_size, shuffle=True, num_workers=4) 133 | print('Test set size: ' + str(len(dataset_test))) 134 | # model 135 | model = Inception3(num_classes=2, aux_logits=True, transform_input=False) 136 | model = model.to(device) 137 | # load old parameters 138 | checkpoint = torch.load(old_ckpt_path, map_location=device) 139 | # it is a checkpoint dictionary rather than just model parameters 140 | if old_ckpt_path[-4:] == '.tar': 141 | if not initialize_from_seg_model: 142 | model.load_state_dict(checkpoint['model_state_dict']) 143 | else: 144 | model_state_dict_main_branch = OrderedDict() 145 | for k in checkpoint['model_state_dict'].keys(): 146 | if k[:11] == 'inception3.': 147 | model_state_dict_main_branch[k[11:]] = checkpoint['model_state_dict'][k] 148 | model.load_state_dict(model_state_dict_main_branch, strict=False) 149 | else: 150 | model.load_state_dict(checkpoint) 151 | print('Old checkpoint loaded: ' + old_ckpt_path) 152 | stats, best_metric_value, best_threshold = test_model( 153 | model, dataloader_test, metrics, threshold_list=threshold_list) 154 | precision = (stats[best_threshold]['TP'] + 0.00001) * 1.0 / \ 155 | (stats[best_threshold]['TP'] + stats[best_threshold]['FP'] + 0.00001) 156 | recall = (stats[best_threshold]['TP'] + 0.00001) * 1.0 / \ 157 | (stats[best_threshold]['TP'] + stats[best_threshold]['FN'] + 0.00001) 158 | for thres in threshold_list: 159 | prec = (stats[thres]['TP'] + 0.00001) * 1.0 / \ 160 | (stats[thres]['TP'] + stats[thres]['FP'] + 0.00001) 161 | rec = (stats[thres]['TP'] + 0.00001) * 1.0 / \ 162 | (stats[thres]['TP'] + stats[thres]['FN'] + 0.00001) 163 | print(thres, 'precision: ', prec, 'recall: ', rec, 'metrics: ', metrics(stats[thres])) 164 | print('best threshold: '+str(best_threshold)) 165 | print('best metric value: '+str(best_metric_value)) 166 | print('precision: ' + str(round(precision, 4))) 167 | print('recall: ' + str(round(recall, 4))) 168 | print('metric value under given threshold ' +str(given_threshold) + ': '+str(metrics(stats[given_threshold]))) 169 | precision_given = (stats[given_threshold]['TP'] + 0.00001) * 1.0 / \ 170 | (stats[given_threshold]['TP'] + stats[given_threshold]['FP'] + 0.00001) 171 | recall_given = (stats[given_threshold]['TP'] + 0.00001) * 1.0 / \ 172 | (stats[given_threshold]['TP'] + stats[given_threshold]['FN'] + 0.00001) 173 | print('precision under given threshold: ' + str(round(precision_given, 4))) 174 | print('recall under given threshold: ' + str(round(recall_given, 4))) 175 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | tqdm 2 | matplotlib 3 | simplekml 4 | numpy == 1.19.5 5 | scipy == 1.1.0 6 | Pillow == 5.2.0 7 | pandas == 0.24.2 8 | shapely == 1.7.1 9 | geopandas == 0.8.2 10 | geojson == 2.5.0 11 | scikit-learn == 0.22 12 | statsmodels == 0.9.0 13 | plotly == 4.14.3 14 | pyshp == 2.1.2 15 | torch == 1.1.0 16 | torchvision == 0.2.2 17 | xgboost == 1.3.3 --------------------------------------------------------------------------------