: similar to the previous for the fill colour of the false positive polygons.
41 | * -fn-border and -fn-fill: as above for false negatives
42 | * -tp-border-solution, -tp-fill-solution, -tp-border-truth and -tp-fill-truth : as above for true positives, but here you can set different colours for the truth and solution polygons.
43 | * -run-mode : one of 'truth', 'solution' or 'image-directory' (without the quotes). Run mode specifies which images are loaded into the tool. If run mode is 'truth' then all images that are listed in the given truth file are loaded. Similarly for 'solution', all images that are present in the solution file are loaded. In 'image-directory' mode all images of the -image3-dir are loaded. Defaults to 'truth'.
44 |
45 | All these have proper defaults so you can leave them out.
46 |
47 | ### Operations
48 |
49 | Usage of the tool should be straightforward. Select the view type from the top drop down list: the 3 band image or one of your predefined band triplet combinations. Select the image to be displayed from the bottom drop down list. Note that you can also switch to another image by clicking the line containing an image name in the output log window.
50 | Solution and truth are compared automatically (if both truth and solution files are specified), scores are displayed in the log window and also in the command line.
51 | You can zoom in/out within the image view by the mouse wheel, and pan the view by dragging.
52 |
53 | ### Sample images
54 |
55 | Note that the sample images bundled together with the visualizer tool are not actual satellite images, these are only added for demonstration purposes. The 3-band images are created from aerial photography, the 8-band images are created by image processing manipulation of the 3-band images. (The Red, Green, Blue channels are correct but the Coastal, Near-IR1, etc. channels are fake.) You need to download the training data set of the contest to obtain real, ground-truthed satellite imagery. See the problem statement and [this link](https://aws.amazon.com/public-data-sets/spacenet) for details on how to access real data.
56 |
57 | ### Examples
58 |
59 | 1\. Typical usage: compare truth and solution files. Calculate score and show images.
60 |
61 | java -jar visualizer.jar -truth ./data/truth.csv -solution ./data/solution.csv
62 | -image3-dir ./data/3band -image8-dir ./data/8band
63 | -band-triplets ./data/band-triplets.txt
64 |
65 |
66 | 2\. As above, without GUI, does only the scoring.
67 |
68 | java -jar visualizer.jar -truth ./data/truth.csv -solution ./data/solution.csv
69 | -image3-dir ./data/3band -image8-dir ./data/8band
70 | -band-triplets ./data/band-triplets.txt -no-gui
71 |
72 |
73 | 3\. Just show all the images in a folder.
74 |
75 | java -jar visualizer.jar -image3-dir ./data/3band -image8-dir ./data/8band
76 | -band-triplets ./data/band-triplets.txt -run-mode image-directory
77 |
78 |
79 | 4\. Show truth data without comparing with solution.
80 |
81 | java -jar visualizer.jar -image3-dir ./data/3band -image8-dir ./data/8band
82 | -band-triplets ./data/band-triplets.txt -truth ./data/truth.csv
83 |
84 |
85 | 5\. Show your solution without truth data.
86 |
87 | java -jar visualizer.jar -image3-dir ./data/3band -image8-dir ./data/8band -band-triplets ./data/band-triplets.txt -solution ./data/solution.csv -run-mode solution
88 |
89 |
90 | ## Band extractor CLI
91 |
92 | The purpose of the band extractor application is to extract individual bands from 8-band GeoTiff files. Most image viewers and image processing libraries can't handle 8-band images so this application is provided as a file conversion convenience tool.
93 | Open a command window in the directory where you unzipped the package and execute
94 |
95 | java -cp visualizer.jar visualizer.BandExtractor -in -out
96 |
97 |
98 | where
99 |
100 | * is either a 8-band GeoTiff file or a directory containing such files. In the latter case all files within the directory are processed.
101 | * is a directory where the extracted images will be placed. The application creates output files having the same name as the input file appended with a -b[i] suffix, i = [1..8].
102 |
103 | There are other optional command line parameters you can use:
104 |
105 | * -type : one of 'png', 'tif', 'none' (without the quotes). Specifies the output file type, default is 'png'. If 'none' then no output file is generated, only pixel value statistics are output.
106 | * -max : an integer value, defaults to -1 meaning no maximum is set. See 'Pixel value scaling' for a description of how this value is used.
107 | * -bands : a string containing a list of integer values from the [1..8] range, meaning the set of bands you wish to extract. Defaults to '12345678' (without the quotes), meaning all bands. Don't use any separator between the numbers.
108 |
109 | ### Pixel value scaling
110 |
111 | The 8-band GeoTiff files contain 16-bit pixel intensity values, these have to be converted to standard 8-bit RGB images. You can specify an external maximum value with the max parameter, all values higher than that will be converted to a 255 grayscale value, values lower than that will be proportionally lower. If you don't specify a maximum value then the file's internal maximum will be used: this is the maximum pixel value found in all the selected bands (which may be a subset of all bands if you specified a other than '12345678'). Note that if you don't specify an external maximum then pixel intensities can not be meaningfully compared across images. When the tool runs the maximum pixel values for each band are displayed. If you want your extracted band images to have comparable values then study this output and select an appropriate maximum value that you specify for the conversion. Note that you can select different values for each band if you use both the -max and the -bands parameters during conversion.
112 |
113 | # Licenses
114 |
115 | The visualizer and band extractor tools use the imageio-ext library for reading multiband TIFF files. The imageio-ext library is LGPL licensed, see [here](https://github.com/geosolutions-it/imageio-ext/blob/master/LICENSE.txt) for its license text. See [here](https://github.com/geosolutions-it/imageio-ext) for details on the library.
116 | The sample images bundled together with the visualizer tool were created using [this](https://commons.wikimedia.org/wiki/File:Ortofoto_Citt%C3%A0_Alta,_Rocca.jpg) image. The file is licensed under the [Creative Commons Attribution-Share Alike 4.0 International](https://creativecommons.org/licenses/by-sa/4.0/) license. All imagery bundled with the tool is also licensed under the same license.
117 |
--------------------------------------------------------------------------------
/visualizer-1.1/bin/visualizer-1.1.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SpaceNetChallenge/BuildingDetectorVisualizer/3cf947c8ad8d399511df713fce2ca76576664eae/visualizer-1.1/bin/visualizer-1.1.zip
--------------------------------------------------------------------------------
/visualizer-1.1/data/3band/3band_sample_00.tif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SpaceNetChallenge/BuildingDetectorVisualizer/3cf947c8ad8d399511df713fce2ca76576664eae/visualizer-1.1/data/3band/3band_sample_00.tif
--------------------------------------------------------------------------------
/visualizer-1.1/data/3band/3band_sample_10.tif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SpaceNetChallenge/BuildingDetectorVisualizer/3cf947c8ad8d399511df713fce2ca76576664eae/visualizer-1.1/data/3band/3band_sample_10.tif
--------------------------------------------------------------------------------
/visualizer-1.1/data/3band/3band_sample_20.tif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SpaceNetChallenge/BuildingDetectorVisualizer/3cf947c8ad8d399511df713fce2ca76576664eae/visualizer-1.1/data/3band/3band_sample_20.tif
--------------------------------------------------------------------------------
/visualizer-1.1/data/3band/3band_sample_30.tif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SpaceNetChallenge/BuildingDetectorVisualizer/3cf947c8ad8d399511df713fce2ca76576664eae/visualizer-1.1/data/3band/3band_sample_30.tif
--------------------------------------------------------------------------------
/visualizer-1.1/data/3band/3band_sample_em.tif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SpaceNetChallenge/BuildingDetectorVisualizer/3cf947c8ad8d399511df713fce2ca76576664eae/visualizer-1.1/data/3band/3band_sample_em.tif
--------------------------------------------------------------------------------
/visualizer-1.1/data/8band/8band_sample_00.tif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SpaceNetChallenge/BuildingDetectorVisualizer/3cf947c8ad8d399511df713fce2ca76576664eae/visualizer-1.1/data/8band/8band_sample_00.tif
--------------------------------------------------------------------------------
/visualizer-1.1/data/8band/8band_sample_10.tif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SpaceNetChallenge/BuildingDetectorVisualizer/3cf947c8ad8d399511df713fce2ca76576664eae/visualizer-1.1/data/8band/8band_sample_10.tif
--------------------------------------------------------------------------------
/visualizer-1.1/data/8band/8band_sample_20.tif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SpaceNetChallenge/BuildingDetectorVisualizer/3cf947c8ad8d399511df713fce2ca76576664eae/visualizer-1.1/data/8band/8band_sample_20.tif
--------------------------------------------------------------------------------
/visualizer-1.1/data/8band/8band_sample_30.tif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SpaceNetChallenge/BuildingDetectorVisualizer/3cf947c8ad8d399511df713fce2ca76576664eae/visualizer-1.1/data/8band/8band_sample_30.tif
--------------------------------------------------------------------------------
/visualizer-1.1/data/8band/8band_sample_em.tif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SpaceNetChallenge/BuildingDetectorVisualizer/3cf947c8ad8d399511df713fce2ca76576664eae/visualizer-1.1/data/8band/8band_sample_em.tif
--------------------------------------------------------------------------------
/visualizer-1.1/data/band-triplets.txt:
--------------------------------------------------------------------------------
1 | # 1. Coastal: 400 - 450 nm
2 | # 2. Blue: 450 - 510 nm
3 | # 3. Green: 510 - 580 nm
4 | # 4. Yellow: 585 - 625 nm
5 | # 5. Red: 630 - 690 nm
6 | # 6. Red Edge: 705 - 745 nm
7 | # 7. Near-IR1: 770 - 895 nm
8 | # 8. Near-IR2: 860 - 1040 nm
9 |
10 | # Format: 3 integers in the 1..8 range, no spaces or any separators Name (spaces allowed)
11 |
12 | 753 Vegetation
13 | 875 Urban
14 | 781 Blackwater
15 | 777 Infra red 1
16 | 888 Infra red 2
--------------------------------------------------------------------------------
/visualizer-1.1/data/solution.csv:
--------------------------------------------------------------------------------
1 | ImageId,BuildingId,PolygonWKT_Pix,Confidence
2 | # for demo purposes this contains very similar data to the truth file
3 | # some buildings are removed, some added, some slightly modified
4 |
5 | #sample_00,1,"POLYGON ((74 0 0,64 19 0,107 42 0,127 18 0,90 0 0,74 0 0))",1
6 | #sample_00,2,"POLYGON ((212 145 0,184 256 0,180 256 0,148 399 0,334 399 0,362 253 0,399 255 0,399 229 0,391 228 0,396 206 0,347 195 0,333 216 0,333 215 0,338 174 0,254 159 0,256 152 0,212 145 0),(252 185 0,292 190 0,285 239 0,245 232 0,252 185 0),(233 291 0,265 300 0,265 304 0,286 309 0,291 317 0,280 379 0,215 367 0,233 291 0))",1
7 | #sample_00,3,"POLYGON ((162 0 0,162 10 0,182 46 0,218 0 0,162 0 0))",1
8 | sample_00,4,"POLYGON ((161 64 0,137 64 0,108 73 0,84 96 0,149 118 0,156 116 0,166 87 0,161 64 0))",1
9 | sample_00,5,"POLYGON ((107 107 0,100 130 0,74 118 0,76 93 0,68 93 0,61 139 0,134 161 0,149 117 0,107 107 0))",1
10 | sample_00,6,"POLYGON ((79 145 0,77 157 0,89 163 0,83 199 0,123 208 0,134 161 0,79 145 0))",1
11 | sample_00,7,"POLYGON ((0 104 0,0 126 0,8 108 0,0 104 0))",1
12 | sample_00,8,"POLYGON ((0 133 0,0 177 0,11 183 0,27 147 0,0 133 0))",1
13 | sample_00,9,"POLYGON ((42 130 0,30 154 0,36 157 0,18 187 0,44 202 0,42 206 0,63 215 0,67 198 0,82 199 0,84 187 0,76 185 0,82 162 0,75 158 0,78 145 0,42 130 0))",1
14 | sample_00,10,"POLYGON ((67 196 0,66 214 0 62 215 0,60 236 0,63 238 0,60 267 0,112 275 0,122 207 0,67 196 0))",1
15 | # modified
16 | sample_00,11,"POLYGON ((79 273 0, 75 301 0,111 306 0,116 279 0, 79 273 0))",1
17 | # modified
18 | sample_00,12,"POLYGON ((82 308 0,75 335 0,114 342 0,116 311 0,82 308 0))",1
19 | sample_00,13,"POLYGON ((75 329 0,71 345 0,103 359 0,103 333 0,75 329 0))",1
20 | sample_00,14,"POLYGON ((40 328 0,23 391 0,53 399 0,62 370 0, 74 381 0,60 400 0,80 400 0,97 383 0,102 360 0, 40 328 0))",1
21 | sample_00,15,"POLYGON ((0 349 0,0 387 0,15 356 0,0 349 0))",1
22 | sample_00,16,"POLYGON ((400 257 0,387 318 0,400 320 0,400 257 0))",1
23 | sample_00,17,"POLYGON ((383 323 0, 369 396 0,389 400 0,400 400 0,400 326 0, 383 323 0))",1
24 | sample_00,18,"POLYGON ((371 0 0,369 10 0,400 9 0, 400 0 0,371 0 0))",1
25 | # extra
26 | sample_00,19,"POLYGON ((286 148 0,320 150 0,321 130 0, 290 128 0,286 148 0))",1
27 |
28 | sample_10,1,"POLYGON ((118 18 0,129 36 0,122 41 0, 133 43 0,133 48 0,141 51 0,145 41 0,159 46 0,163 25 0,118 18 0))",1
29 | sample_10,2,"POLYGON ((122 41 0,112 50 0,112 67 0,124 66 0,131 76 0,133 43 0,122 41 0))",1
30 | sample_10,3,"POLYGON ((0 230 0,0 255 0,6 256 0,6 236 0,11 232 0,0 230 0))",1
31 | sample_10,4,"POLYGON ((0 255 0,0 320 0,13 321 0,21 273 0,10 257 0, 0 255 0))",1
32 | # sample_10,5,"POLYGON ((276 58 0,273 85 0, 292 87 0,292 84 0,310 84 0,311 61 0,276 58 0))",1
33 | sample_10,6,"POLYGON ((290 87 0, 287 112 0, 306 113 0,306 111 0,324 111 0,325 87 0,290 87 0))",1
34 | sample_10,7,"POLYGON ((303 113 0,301 139 0,318 139 0,319 133 0,332 131 0,333 115 0,303 113 0))",1
35 | sample_10,8,"POLYGON ((319 133 0,317 158 0,329 160 0,329 157 0,348 157 0,348 132 0,319 133 0))",1
36 | # modified
37 | sample_10,9,"POLYGON ((337 167 0,335 201 0,342 200 0,342 195 0,370 195 0,371 169 0,337 167 0))",1
38 | sample_10,10,"POLYGON ((340 185 0,338 213 0,372 216 0,375 187 0,340 185 0))",1
39 | sample_10,11,"POLYGON ((0 326 0,0 400 0,36 400 0,34 393 0,18 392 0,27 332 0,0 326 0))",1
40 |
41 | sample_20,1,"POLYGON ((169 41 0,164 73 0,180 74 0,185 65 0,188 41 0,169 41 0))",1
42 | sample_20,2,"POLYGON ((387 0 0,375 10 0,400 37 0,400 0 0,387 0 0))",1
43 | sample_20,3,"POLYGON ((306 125 0,342 157 0,300 193 0,325 224 0,340 211 0,348 219 0,352 214 0,367 232 0,361 247 0,368 255 0,400 228 0,400 142 0,386 156 0,324 104 0,306 125 0),(360 198 0,374 187 0,389 205 0,375 217 0,360 198 0))",1
44 | # modified
45 | sample_20,4,"POLYGON ((97 112 0,94 150 0,99 165 0,124 138 0,97 112 0))",1
46 | sample_30,1,"POLYGON ((0 0 0,0 40 0,6 45 0,53 0 0,0 0 0))",1
47 | sample_30,2,"POLYGON ((7 137 0,0 143 0,0 235 0,25 267 0,8 282 0,34 311 0,47 300 0,33 286 0,39 281 0,52 296 0,80 271 0,24 200 0,47 180 0, 7 137 0))",1
48 | sample_30,3,"POLYGON ((65 361 0,60 400 0,142 400 0,144 374 0,141 373 0,143 354 0,123 353 0,120 369 0,65 361 0))",1
49 | sample_em,-1,POLYGON EMPTY,1
--------------------------------------------------------------------------------
/visualizer-1.1/data/truth.csv:
--------------------------------------------------------------------------------
1 | ImageId,BuildingId,PolygonWKT_Pix,PolygonWKT_Geo
2 | sample_00,1,"POLYGON ((74 0 0,64 19 0,107 42 0,127 18 0,90 0 0,74 0 0))","POLYGON ((0 0 0))"
3 | sample_00,2,"POLYGON ((212 145 0,184 256 0,180 256 0,148 399 0,334 399 0,362 253 0,399 255 0,399 229 0,391 228 0,396 206 0,347 195 0,333 216 0,333 215 0,338 174 0,254 159 0,256 152 0,212 145 0),(252 185 0,292 190 0,285 239 0,245 232 0,252 185 0),(233 291 0,265 300 0,265 304 0,286 309 0,291 317 0,280 379 0,215 367 0,233 291 0))","POLYGON ((0 0 0))"
4 | sample_00,3,"POLYGON ((162 0 0,162 10 0,182 46 0,218 0 0,162 0 0))","POLYGON ((0 0 0))"
5 | sample_00,4,"POLYGON ((161 64 0,137 64 0,108 73 0,84 96 0,149 118 0,156 116 0,166 87 0,161 64 0))","POLYGON ((0 0 0))"
6 | sample_00,5,"POLYGON ((107 107 0,100 130 0,74 118 0,76 93 0,68 93 0,61 139 0,134 161 0,149 117 0,107 107 0))","POLYGON ((0 0 0))"
7 | sample_00,6,"POLYGON ((79 145 0,77 157 0,89 163 0,83 199 0,123 208 0,134 161 0,79 145 0))","POLYGON ((0 0 0))"
8 | sample_00,7,"POLYGON ((0 104 0,0 126 0,8 108 0,0 104 0))","POLYGON ((0 0 0))"
9 | sample_00,8,"POLYGON ((0 133 0,0 177 0,11 183 0,27 147 0,0 133 0))","POLYGON ((0 0 0))"
10 | sample_00,9,"POLYGON ((42 130 0,30 154 0,36 157 0,18 187 0,44 202 0,42 206 0,63 215 0,67 198 0,82 199 0,84 187 0,76 185 0,82 162 0,75 158 0,78 145 0,42 130 0))","POLYGON ((0 0 0))"
11 | sample_00,10,"POLYGON ((67 196 0,66 214 0 62 215 0,60 236 0,63 238 0,60 267 0,112 275 0,122 207 0,67 196 0))","POLYGON ((0 0 0))"
12 | sample_00,11,"POLYGON ((74 268 0, 70 296 0,106 301 0,111 274 0, 74 268 0))","POLYGON ((0 0 0))"
13 | sample_00,12,"POLYGON ((72 298 0,65 325 0,104 332 0,106 301 0,72 298 0))","POLYGON ((0 0 0))"
14 | sample_00,13,"POLYGON ((75 329 0,71 345 0,103 359 0,103 333 0,75 329 0))","POLYGON ((0 0 0))"
15 | sample_00,14,"POLYGON ((40 328 0,23 391 0,53 399 0,62 370 0, 74 381 0,60 400 0,80 400 0,97 383 0,102 360 0, 40 328 0))","POLYGON ((0 0 0))"
16 | sample_00,15,"POLYGON ((0 349 0,0 387 0,15 356 0,0 349 0))","POLYGON ((0 0 0))"
17 | sample_00,16,"POLYGON ((400 257 0,387 318 0,400 320 0,400 257 0))","POLYGON ((0 0 0))"
18 | sample_00,17,"POLYGON ((383 323 0, 369 396 0,389 400 0,400 400 0,400 326 0, 383 323 0))","POLYGON ((0 0 0))"
19 | sample_00,18,"POLYGON ((371 0 0,369 10 0,400 9 0, 400 0 0,371 0 0))","POLYGON ((0 0 0))"
20 | sample_10,1,"POLYGON ((118 18 0,129 36 0,122 41 0, 133 43 0,133 48 0,141 51 0,145 41 0,159 46 0,163 25 0,118 18 0))","POLYGON ((0 0 0))"
21 | sample_10,2,"POLYGON ((122 41 0,112 50 0,112 67 0,124 66 0,131 76 0,133 43 0,122 41 0))","POLYGON ((0 0 0))"
22 | sample_10,3,"POLYGON ((0 230 0,0 255 0,6 256 0,6 236 0,11 232 0,0 230 0))","POLYGON ((0 0 0))"
23 | sample_10,4,"POLYGON ((0 255 0,0 320 0,13 321 0,21 273 0,10 257 0, 0 255 0))","POLYGON ((0 0 0))"
24 | sample_10,5,"POLYGON ((276 58 0,273 85 0, 292 87 0,292 84 0,310 84 0,311 61 0,276 58 0))","POLYGON ((0 0 0))"
25 | sample_10,6,"POLYGON ((290 87 0, 287 112 0, 306 113 0,306 111 0,324 111 0,325 87 0,290 87 0))","POLYGON ((0 0 0))"
26 | sample_10,7,"POLYGON ((303 113 0,301 139 0,318 139 0,319 133 0,332 131 0,333 115 0,303 113 0))","POLYGON ((0 0 0))"
27 | sample_10,8,"POLYGON ((319 133 0,317 158 0,329 160 0,329 157 0,348 157 0,348 132 0,319 133 0))","POLYGON ((0 0 0))"
28 | sample_10,9,"POLYGON ((327 157 0,325 191 0,332 190 0,332 185 0,360 185 0,361 159 0,327 157 0))","POLYGON ((0 0 0))"
29 | sample_10,10,"POLYGON ((340 185 0,338 213 0,372 216 0,375 187 0,340 185 0))","POLYGON ((0 0 0))"
30 | sample_10,11,"POLYGON ((0 326 0,0 400 0,36 400 0,34 393 0,18 392 0,27 332 0,0 326 0))","POLYGON ((0 0 0))"
31 | sample_20,1,"POLYGON ((169 41 0,164 73 0,180 74 0,185 65 0,188 41 0,169 41 0))","POLYGON ((0 0 0))"
32 | sample_20,2,"POLYGON ((387 0 0,375 10 0,400 37 0,400 0 0,387 0 0))","POLYGON ((0 0 0))"
33 | sample_20,3,"POLYGON ((306 125 0,342 157 0,300 193 0,325 224 0,340 211 0,348 219 0,352 214 0,367 232 0,361 247 0,368 255 0,400 228 0,400 142 0,386 156 0,324 104 0,306 125 0),(360 198 0,374 187 0,389 205 0,375 217 0,360 198 0))","POLYGON ((0 0 0))"
34 | sample_20,4,"POLYGON ((97 112 0,74 140 0,89 155 0,114 128 0,97 112 0))","POLYGON ((0 0 0))"
35 | sample_30,1,"POLYGON ((0 0 0,0 40 0,6 45 0,53 0 0,0 0 0))","POLYGON ((0 0 0))"
36 | sample_30,2,"POLYGON ((7 137 0,0 143 0,0 235 0,25 267 0,8 282 0,34 311 0,47 300 0,33 286 0,39 281 0,52 296 0,80 271 0,24 200 0,47 180 0, 7 137 0))","POLYGON ((0 0 0))"
37 | sample_30,3,"POLYGON ((65 361 0,60 400 0,142 400 0,144 374 0,141 373 0,143 354 0,123 353 0,120 369 0,65 361 0))","POLYGON ((0 0 0))"
38 | sample_em,-1,POLYGON EMPTY,POLYGON_EMPTY
--------------------------------------------------------------------------------
/visualizer-1.1/src/visualizer/BandExtractor.java:
--------------------------------------------------------------------------------
1 | package visualizer;
2 |
3 | import java.awt.image.BufferedImage;
4 | import java.awt.image.Raster;
5 | import java.io.File;
6 | import java.util.List;
7 | import java.util.Vector;
8 |
9 | import javax.imageio.ImageIO;
10 |
11 | public class BandExtractor {
12 | private int[] i8 = new int[8];
13 | private String imageType;
14 | private List bandIndexes;
15 |
16 | public BandExtractor(String bands) {
17 | bandIndexes = new Vector<>();
18 | for (int i = 0; i < bands.length(); i++) {
19 | int b = Integer.parseInt("" + bands.charAt(i));
20 | bandIndexes.add(b-1);
21 | }
22 | }
23 |
24 | private void process(File inFile, File outDir, int externalMax) throws Exception {
25 | String name = inFile.getName();
26 | System.out.println(name);
27 | if (name.endsWith(".tif")) name = name.replace(".tif", "");
28 | else {
29 | System.out.println(" not a tiff file, skipping");
30 | return;
31 | }
32 |
33 | BufferedImage img = ImageIO.read(inFile);
34 | Raster r = img.getRaster();
35 | int w = img.getWidth(); int h = img.getHeight();
36 | BufferedImage[] bandImgs = new BufferedImage[8];
37 | for (int b: bandIndexes) bandImgs[b] = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
38 |
39 | int[] maxPerBand = new int[8];
40 | for (int i = 0; i < w; i++) for (int j = 0; j < h; j++) {
41 | int[] samples = r.getPixel(i, j, i8);
42 | for (int b: bandIndexes) {
43 | int m = samples[b];
44 | maxPerBand[b] = Math.max(maxPerBand[b], m);
45 | }
46 | }
47 | int max = -1;
48 | int maxIndex = 0;
49 | for (int b: bandIndexes) {
50 | System.out.println(" band " + (b+1) + " max: " + maxPerBand[b]);
51 | if (maxPerBand[b] > max) {
52 | max = maxPerBand[b];
53 | maxIndex = b;
54 | }
55 | }
56 | System.out.println(" global max: " + max + " at band " + (maxIndex+1));
57 | if (externalMax != -1) {
58 | max = externalMax;
59 | }
60 | System.out.println(" scaling to: " + max);
61 |
62 | if (imageType.equals("none")) return;
63 |
64 | if (max > 0) {
65 | for (int i = 0; i < w; i++) for (int j = 0; j < h; j++) {
66 | int[] samples = r.getPixel(i, j, new int[8]);
67 | for (int b: bandIndexes) {
68 | int m = samples[b];
69 | if (m > max) m = max;
70 | int c = (int)((double)m / max * 255);
71 | c = (c << 16) | (c << 8) | c;
72 | bandImgs[b].setRGB(i, j, c);
73 | }
74 | }
75 | }
76 | for (int b: bandIndexes) {
77 | String outName = name + "_b" + (b+1);
78 | outName += "." + imageType;
79 | File out = new File(outDir, outName);
80 | ImageIO.write(bandImgs[b], imageType, out);
81 | }
82 | }
83 |
84 | public static void main(String[] args) throws Exception {
85 | String type = "png";
86 | String in = null;//"c:/tmp/b/out8.tif";
87 | String out = null;//"c:/tmp/b";
88 | int max = -1;
89 | String bands = "12345678";
90 | for (int i = 0; i < args.length; i++) {
91 | if (args[i].equals("-in")) in = args[i+1];
92 | else if (args[i].equals("-out")) out = args[i+1];
93 | else if (args[i].equals("-type")) type = args[i+1];
94 | else if (args[i].equals("-max")) max = Integer.parseInt(args[i+1]);
95 | else if (args[i].equals("-bands")) bands = args[i+1];
96 | }
97 | if (in == null) exit("-in not set");
98 | if (out == null) exit("-out not set");
99 | File inFile = new File(in);
100 | if (!inFile.exists()) exit("Input file not found: " + in);
101 | File outDir = new File(out);
102 | if (!outDir.exists() || !outDir.isDirectory()) exit("Output file not found or not a directory: " + out);
103 |
104 | BandExtractor be = new BandExtractor(bands);
105 | be.imageType = type;
106 |
107 | if (inFile.isDirectory()) {
108 | for (File f: inFile.listFiles()) {
109 | be.process(f, outDir, max);
110 | }
111 | }
112 | else {
113 | be.process(inFile, outDir, max);
114 | }
115 | }
116 |
117 | private static void exit(String s) {
118 | System.out.println(s);
119 | System.exit(1);
120 | }
121 | }
122 |
--------------------------------------------------------------------------------
/visualizer-1.1/src/visualizer/Utils.java:
--------------------------------------------------------------------------------
1 | package visualizer;
2 |
3 | import java.io.FileInputStream;
4 | import java.io.InputStream;
5 | import java.io.InputStreamReader;
6 | import java.io.LineNumberReader;
7 | import java.text.DecimalFormat;
8 | import java.text.DecimalFormatSymbols;
9 | import java.util.List;
10 | import java.util.Vector;
11 |
12 | public class Utils {
13 |
14 | private static DecimalFormat df;
15 | private static DecimalFormat df6;
16 | static {
17 | df = new DecimalFormat("0.###");
18 | df6 = new DecimalFormat("0.######");
19 | DecimalFormatSymbols dfs = new DecimalFormatSymbols();
20 | dfs.setDecimalSeparator('.');
21 | df.setDecimalFormatSymbols(dfs);
22 | df6.setDecimalFormatSymbols(dfs);
23 | }
24 |
25 | /**
26 | * Pretty print a double
27 | */
28 | public static String f(double d) {
29 | return df.format(d);
30 | }
31 | public static String f6(double d) {
32 | return df6.format(d);
33 | }
34 |
35 | // Gets the lines of a text file at the given path
36 | public static List readTextLines(String path) {
37 | List ret = new Vector<>();
38 | try {
39 | InputStream is = new FileInputStream(path);
40 | InputStreamReader isr = new InputStreamReader(is, "UTF-8");
41 | LineNumberReader lnr = new LineNumberReader(isr);
42 | while (true) {
43 | String line = lnr.readLine();
44 | if (line == null) break;
45 | line = line.trim();
46 | if (line.isEmpty() || line.startsWith("#")) continue;
47 | ret.add(line);
48 | }
49 | lnr.close();
50 | }
51 | catch (Exception e) {
52 | e.printStackTrace();
53 | }
54 | return ret;
55 | }
56 | }
57 |
58 |
59 |
--------------------------------------------------------------------------------
/visualizer-1.1/src/visualizer/Visualizer.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Building Detector Visualizer and Offline Tester
3 | * by walrus71
4 | *
5 | * Version history:
6 | * ================
7 | * 1.1 (2016.11.16)
8 | * - Version at contest launch
9 | * - Minimum building area check added
10 | * - Other small fixes
11 | * 1.0 (2016.11.03)
12 | * - First public version
13 | */
14 | package visualizer;
15 |
16 | import static visualizer.Utils.f;
17 | import static visualizer.Utils.f6;
18 |
19 | import java.awt.BorderLayout;
20 | import java.awt.Color;
21 | import java.awt.Container;
22 | import java.awt.Cursor;
23 | import java.awt.Dimension;
24 | import java.awt.Font;
25 | import java.awt.Graphics;
26 | import java.awt.Graphics2D;
27 | import java.awt.GridBagConstraints;
28 | import java.awt.GridBagLayout;
29 | import java.awt.RenderingHints;
30 | import java.awt.event.ActionEvent;
31 | import java.awt.event.ActionListener;
32 | import java.awt.event.ItemEvent;
33 | import java.awt.event.ItemListener;
34 | import java.awt.event.MouseEvent;
35 | import java.awt.event.MouseListener;
36 | import java.awt.event.MouseMotionListener;
37 | import java.awt.event.MouseWheelEvent;
38 | import java.awt.event.MouseWheelListener;
39 | import java.awt.font.FontRenderContext;
40 | import java.awt.geom.AffineTransform;
41 | import java.awt.geom.Area;
42 | import java.awt.geom.Line2D;
43 | import java.awt.geom.Path2D;
44 | import java.awt.geom.PathIterator;
45 | import java.awt.geom.Rectangle2D;
46 | import java.awt.image.BufferedImage;
47 | import java.awt.image.Raster;
48 | import java.io.File;
49 | import java.io.FileReader;
50 | import java.io.LineNumberReader;
51 | import java.util.Arrays;
52 | import java.util.HashMap;
53 | import java.util.List;
54 | import java.util.Map;
55 | import java.util.Vector;
56 |
57 | import javax.imageio.ImageIO;
58 | import javax.swing.DefaultComboBoxModel;
59 | import javax.swing.JCheckBox;
60 | import javax.swing.JComboBox;
61 | import javax.swing.JFrame;
62 | import javax.swing.JLabel;
63 | import javax.swing.JPanel;
64 | import javax.swing.JScrollPane;
65 | import javax.swing.JTextArea;
66 |
67 | public class Visualizer implements ActionListener, ItemListener, MouseListener {
68 |
69 | private enum RunMode {
70 | TRUTH, SOLUTION, IMAGE_DIR
71 | }
72 | private RunMode runMode = RunMode.TRUTH;
73 | private boolean hasGui = true;
74 | private String[] imageIds;
75 | private String currentImageId;
76 | private String image3Dir;
77 | private String image8Dir;
78 | private String truthPath;
79 | private String solutionPath;
80 | private Map idToTruthPolygons;
81 | private Map idToSolutionPolygons;
82 | private double iouThreshold = 0.5;
83 | private static final double MIN_AREA = 20;
84 |
85 | private double scale; // data size / screen size (for 3-band images)
86 | private double x0 = 0, y0 = 0; // x0, y0: TopLeft corner of data is shown here (in screen space, applies to all views)
87 | private double ratio38; // scaling factor between 3-band and 8-band images
88 |
89 | private JFrame frame;
90 | private JPanel viewPanel, controlsPanel;
91 | private JCheckBox showTruthCb, showSolutionCb, showIouCb;
92 | private JComboBox viewSelectorComboBox;
93 | private JComboBox imageSelectorComboBox;
94 | private JTextArea logArea;
95 | private MapView mapView;
96 | private Font font = new Font("SansSerif", Font.BOLD, 14);
97 |
98 | private String bandTripletPath;
99 | private List bandTriplets;
100 | private BandTriplet currentBandTriplet;
101 |
102 | private Color textColor = Color.black;
103 | private Color tpBorderSolutionColor = new Color(255, 255, 255, 200);
104 | private Color tpFillSolutionColor = new Color(255, 255, 255, 50);
105 | private Color tpBorderTruthColor = new Color(255, 255, 255, 200);
106 | private Color tpFillTruthColor = new Color(255, 255, 255, 10);
107 | private Color fpBorderColor = new Color(255, 255, 0, 255);
108 | private Color fpFillColor = new Color(255, 255, 0, 100);
109 | private Color fnBorderColor = new Color( 0, 255, 255, 255);
110 | private Color fnFillColor = new Color( 0, 155, 255, 100);
111 |
112 | private void run() {
113 | idToSolutionPolygons = load(solutionPath, false);
114 | idToTruthPolygons = load(truthPath, true);
115 |
116 | if (runMode == RunMode.SOLUTION) {
117 | imageIds = idToSolutionPolygons.keySet().toArray(new String[0]);
118 | }
119 | else if (runMode == RunMode.TRUTH) {
120 | imageIds = idToTruthPolygons.keySet().toArray(new String[0]);
121 | }
122 | else {
123 | imageIds = collectImageIds();
124 | }
125 |
126 | Arrays.sort(imageIds);
127 | if (hasGui) {
128 | DefaultComboBoxModel cbm = new DefaultComboBoxModel<>(imageIds);
129 | imageSelectorComboBox.setModel(cbm);
130 | imageSelectorComboBox.setSelectedIndex(0);
131 | imageSelectorComboBox.addItemListener(this);
132 | }
133 |
134 | int tp = 0;
135 | int fp = 0;
136 | int fn = 0;
137 | String detailsMarker = "Details:";
138 | log(detailsMarker);
139 | for (String id: imageIds) {
140 | Metrics result = score(id);
141 | if (result != null) {
142 | tp += result.tp;
143 | fp += result.fp;
144 | fn += result.fn;
145 | log(id + "\n"
146 | + " TP : " + result.tp + "\n"
147 | + " FP : " + result.fp + "\n"
148 | + " FN : " + result.fn + "\n");
149 | }
150 | else {
151 | log(id + "\n - not scored");
152 | }
153 | }
154 |
155 | double precision = 0;
156 | double recall = 0;
157 | double fScore = 0;
158 | if ((tp + fp > 0) && (tp + fn > 0)) {
159 | precision = (double)tp / (tp + fp);
160 | recall = (double)tp / (tp + fn);
161 | if (precision + recall > 0) {
162 | fScore = 2 * precision * recall / (precision + recall);
163 | }
164 | String result = "Overall results:\n"
165 | + " TP : " + tp + "\n"
166 | + " FP : " + fp + "\n"
167 | + " FN : " + fn + "\n"
168 | + " Precision: " + f6(precision) + "\n"
169 | + " Recall : " + f6(recall) + "\n"
170 | + " F-score : " + f6(fScore);
171 | if (hasGui) { // display final result at the top
172 | String allText = logArea.getText();
173 | int pos = allText.indexOf(detailsMarker);
174 | String s1 = allText.substring(0, pos);
175 | String s2 = allText.substring(pos);
176 | allText = s1 + result + "\n\n" + s2;
177 | logArea.setText(allText);
178 | logArea.setCaretPosition(0);
179 | System.out.println(result);
180 | }
181 | else {
182 | log(result);
183 | }
184 |
185 | }
186 | else {
187 | log("Can't score.");
188 | }
189 |
190 | // the rest is for UI, not needed for scoring
191 | if (!hasGui) return;
192 |
193 | currentImageId = imageIds[0];
194 | loadMap();
195 | scale = (double)currentBandTriplet.mapData.W / mapView.getWidth();
196 | repaintMap();
197 | }
198 |
199 | private Metrics score(String id) {
200 | Metrics ret = new Metrics();
201 | Polygon[] truthPolygons = idToTruthPolygons.get(id);
202 | Polygon[] solutionPolygons = idToSolutionPolygons.get(id);
203 | if (truthPolygons == null || solutionPolygons == null) return null;
204 | if (truthPolygons.length == 0 && solutionPolygons.length == 0) {
205 | return ret;
206 | }
207 | int tp = 0;
208 | int fp = 0;
209 | int fn = 0;
210 | for (Polygon sP: solutionPolygons) {
211 | Polygon bestMatchingT = null;
212 | double maxScore = 0;
213 | for (Polygon tP: truthPolygons) {
214 | if (tP.match == Match.TP) continue; // matched already
215 | if (sP.minx > tP.maxx || sP.maxx < tP.minx) continue;
216 | if (sP.miny > tP.maxy || sP.maxy < tP.miny) continue;
217 | Area shape = new Area(sP.getShape());
218 | shape.intersect(tP.getShape());
219 | double overlap = Math.abs(area(shape));
220 | double score = overlap / (sP.area + tP.area - overlap);
221 | if (score > maxScore) {
222 | maxScore = score;
223 | bestMatchingT = tP;
224 | }
225 |
226 | }
227 | sP.iouScore = maxScore;
228 | if (maxScore > iouThreshold) {
229 | tp++;
230 | sP.match = Match.TP;
231 | bestMatchingT.match = Match.TP;
232 | }
233 | else {
234 | fp++;
235 | sP.match = Match.FP;
236 | }
237 | }
238 | for (Polygon tP: truthPolygons) {
239 | if (tP.match == Match.NOTHING) {
240 | fn++;
241 | tP.match = Match.FN;
242 | }
243 | }
244 | ret.tp = tp;
245 | ret.fp = fp;
246 | ret.fn = fn;
247 |
248 | return ret;
249 | }
250 |
251 | // based on http://stackoverflow.com/questions/2263272/how-to-calculate-the-area-of-a-java-awt-geom-area
252 | private double area(Area shape) {
253 | PathIterator i = shape.getPathIterator(null);
254 | double a = 0.0;
255 | double[] coords = new double[6];
256 | double startX = Double.NaN, startY = Double.NaN;
257 | Line2D segment = new Line2D.Double(Double.NaN, Double.NaN, Double.NaN, Double.NaN);
258 | while (! i.isDone()) {
259 | int segType = i.currentSegment(coords);
260 | double x = coords[0], y = coords[1];
261 | switch (segType) {
262 | case PathIterator.SEG_CLOSE:
263 | segment.setLine(segment.getX2(), segment.getY2(), startX, startY);
264 | a += area(segment);
265 | startX = startY = Double.NaN;
266 | segment.setLine(Double.NaN, Double.NaN, Double.NaN, Double.NaN);
267 | break;
268 | case PathIterator.SEG_LINETO:
269 | segment.setLine(segment.getX2(), segment.getY2(), x, y);
270 | a += area(segment);
271 | break;
272 | case PathIterator.SEG_MOVETO:
273 | startX = x;
274 | startY = y;
275 | segment.setLine(Double.NaN, Double.NaN, x, y);
276 | break;
277 | }
278 | i.next();
279 | }
280 | if (Double.isNaN(a)) {
281 | throw new IllegalArgumentException("PathIterator contains an open path");
282 | }
283 | else {
284 | return 0.5 * Math.abs(a);
285 | }
286 | }
287 |
288 | private double area(Line2D seg) {
289 | return seg.getX1() * seg.getY2() - seg.getX2() * seg.getY1();
290 | }
291 |
292 | private Map load(String path, boolean truth) {
293 | String what = truth ? "truth file" : "your solution";
294 | log(" - Reading " + what + " from " + path + " ...");
295 | if (path == null) {
296 | log(" Path not set, nothing loaded.");
297 | return new HashMap<>();
298 | }
299 |
300 | Map> idToList = new HashMap<>();
301 | String line = null;
302 | int lineNo = 0;
303 | try {
304 | LineNumberReader lnr = new LineNumberReader(new FileReader(path));
305 | while (true) {
306 | line = lnr.readLine();
307 | lineNo++;
308 | if (line == null) break;
309 | line = line.trim();
310 | if (line.isEmpty() || line.startsWith("#") ||
311 | line.toLowerCase().startsWith("imageid")) continue;
312 | // ImageId,BuildingId,PolygonWKT_Pix,PolygonWKT_Geo | confidence
313 | // 013022223130_Public_img140,1,"POLYGON ((124 364 0,...,124 364 0))","POLYGON ((-43 -22 0,...,-43 -22 0))"
314 | // - or
315 | // 013022223130_Public_img140,1,"POLYGON ((124 364 0,...,124 364 0))",0.9
316 | // - or
317 | // imgid,-1,POLYGON EMPTY
318 | // - or
319 | // imgid,-1,anything
320 |
321 | int pos1 = line.indexOf(",");
322 | String imageId = line.substring(0, pos1);
323 | int pos2 = line.indexOf(",", pos1 + 1);
324 | String buildingId = line.substring(pos1 + 1, pos2);
325 |
326 | List pList = idToList.get(imageId);
327 | if (pList == null) {
328 | pList = new Vector<>();
329 | idToList.put(imageId, pList);
330 | }
331 |
332 | boolean empty = line.contains("POLYGON EMPTY");
333 | if (!empty && buildingId.equals("-1")) {
334 | empty = true;
335 | }
336 |
337 | if (!empty) {
338 | pos1 = line.indexOf("((");
339 | if (pos1 != -1) {
340 | pos2 = line.indexOf("))", pos1);
341 | String pString = line.substring(pos1, pos2+2);
342 | Polygon p = new Polygon(pString);
343 | if (p.area <= 0) {
344 | if (!truth) {
345 | log("Warning: building area <= 0");
346 | log("Line #" + lineNo + ": " + line);
347 | }
348 | continue;
349 | }
350 | if (p.area < MIN_AREA) {
351 | continue;
352 | }
353 | String confS = line.substring(pos2 + 4);
354 | if (!truth) {
355 | p.confidence = Double.parseDouble(confS);
356 | }
357 |
358 | pList.add(p);
359 | }
360 | }
361 | }
362 | lnr.close();
363 | }
364 | catch (Exception e) {
365 | log("Error reading building polygons");
366 | log("Line #" + lineNo + ": " + line);
367 | e.printStackTrace();
368 | System.exit(0);
369 | }
370 | Map ret = new HashMap<>();
371 | for (String id: idToList.keySet()) {
372 | List pList = idToList.get(id);
373 | Polygon[] pArr = pList.toArray(new Polygon[0]);
374 | Arrays.sort(pArr);
375 | ret.put(id, pArr);
376 | }
377 | return ret;
378 | }
379 |
380 | private void loadMap() {
381 | // load 3-band file
382 | File f = new File(image3Dir, "3band_" + currentImageId + ".tif");
383 | if (!f.exists()) {
384 | log("Can't find image file: " + f.getAbsolutePath());
385 | return;
386 | }
387 | int w3 = 0;
388 | try {
389 | BufferedImage img = ImageIO.read(f);
390 | int w = img.getWidth();
391 | w3 = w;
392 | int h = img.getHeight();
393 | MapData md = new MapData(w, h);
394 | for (int i = 0; i < w; i++) for (int j = 0; j < h; j++) {
395 | int c = img.getRGB(i, j);
396 | md.rs[i][j] = (c >> 16) & 0x000000ff;
397 | md.gs[i][j] = (c >> 8) & 0x000000ff;
398 | md.bs[i][j] = (c >> 0) & 0x000000ff;
399 | }
400 | bandTriplets.get(0).mapData = md;
401 | }
402 | catch (Exception e) {
403 | log("Error reading image from " + f.getAbsolutePath());
404 | e.printStackTrace();
405 | }
406 |
407 | // load 8-band file into 8 arrays first
408 | f = new File(image8Dir, "8band_" + currentImageId + ".tif");
409 | if (!f.exists()) {
410 | log("Can't find image file: " + f.getAbsolutePath());
411 | return;
412 | }
413 | try {
414 | BufferedImage img = ImageIO.read(f);
415 | Raster raster = img.getRaster();
416 | int w = img.getWidth(); int h = img.getHeight();
417 | ratio38 = (double)w3 / w;
418 | double[][][] bandData = new double[8][w][h];
419 | int max = 0;
420 | for (int i = 0; i < w; i++) for (int j = 0; j < h; j++) {
421 | int[] samples = raster.getPixel(i, j, new int[8]);
422 | for (int b = 0; b < 8; b++) {
423 | int v = samples[b];
424 | max = Math.max(max, v);
425 | bandData[b][i][j] = v;
426 | }
427 | }
428 | if (max > 0) {
429 | for (int b = 0; b < 8; b++)
430 | for (int i = 0; i < w; i++)
431 | for (int j = 0; j < h; j++)
432 | bandData[b][i][j] /= max;
433 | }
434 |
435 | // create all needed combinations
436 | for (BandTriplet bt: bandTriplets) {
437 | if (bt.is3band) continue;
438 | MapData md = new MapData(w, h);
439 | if (max > 0) {
440 | for (int i = 0; i < w; i++) for (int j = 0; j < h; j++) {
441 | int r = (int)(255 * bandData[bt.bands[0]-1][i][j]);
442 | int g = (int)(255 * bandData[bt.bands[1]-1][i][j]);
443 | int b = (int)(255 * bandData[bt.bands[2]-1][i][j]);
444 | md.rs[i][j] = r;
445 | md.gs[i][j] = g;
446 | md.bs[i][j] = b;
447 | }
448 | }
449 | bt.mapData = md;
450 | }
451 | }
452 | catch (Exception e) {
453 | log("Error reading image from " + f.getAbsolutePath());
454 | e.printStackTrace();
455 | }
456 | }
457 |
458 | private String[] collectImageIds() {
459 | File dir = new File(image3Dir);
460 | List ids = new Vector<>();
461 | for (String s: dir.list()) {
462 | if (!s.endsWith(".tif")) continue;
463 | s = s.replace(".tif", "");
464 | s = s.replace("3band_", "");
465 | ids.add(s);
466 | }
467 | String[] ret = ids.toArray(new String[0]);
468 | Arrays.sort(ret);
469 | return ret;
470 | }
471 |
472 | private class P2 {
473 | public double x;
474 | public double y;
475 |
476 | public P2(double x, double y) {
477 | this.x = x; this.y = y;
478 | }
479 |
480 | @Override
481 | public String toString() {
482 | return f(x) + ", " + f(y);
483 | }
484 |
485 | @Override
486 | public boolean equals(Object o) {
487 | if (!(o instanceof P2)) return false;
488 | P2 p = (P2)o;
489 | double d2 = (x - p.x) * (x - p.x) + (y - p.y) * (y - p.y);
490 | return d2 < 1e-4;
491 | }
492 | }
493 |
494 | private class Metrics {
495 | public int tp;
496 | public int fp;
497 | public int fn;
498 | }
499 |
500 | private class MapData {
501 | public int W;
502 | public int H;
503 | public int[][] rs, gs, bs;
504 | public MapData(int w, int h) {
505 | W = w; H = h;
506 | rs = new int[W][H];
507 | gs = new int[W][H];
508 | bs = new int[W][H];
509 | }
510 | }
511 |
512 | private enum Match {
513 | NOTHING, TP, FP, FN
514 | }
515 |
516 | private class Polygon implements Comparable {
517 | public double confidence;
518 | public Match match = Match.NOTHING;
519 | public double minx, miny, maxx, maxy;
520 | public double iouScore;
521 | public double area = 0;
522 | private Area shape;
523 | public List rings = new Vector<>();
524 |
525 | public Polygon(String pString) {
526 | // ((124 364 0,...,124 364 0),(124 364 0,...,124 364 0))
527 | pString = pString.replace("),(", "x"); // ring separator
528 | pString = pString.replace(")", ""); // remove ) and (
529 | pString = pString.replace("(", "");
530 | String[] parts = pString.split("x");
531 | for (String p: parts) {
532 | Ring r = new Ring(p);
533 | rings.add(r);
534 | }
535 | makeBounds();
536 | getShape();
537 | }
538 |
539 | private void makeBounds() {
540 | minx = Double.MAX_VALUE;
541 | miny = Double.MAX_VALUE;
542 | maxx = -Double.MAX_VALUE;
543 | maxy = -Double.MAX_VALUE;
544 | for (Ring r: rings) {
545 | for (P2 p: r.points) {
546 | minx = Math.min(p.x, minx);
547 | maxx = Math.max(p.x, maxx);
548 | miny = Math.min(p.y, miny);
549 | maxy = Math.max(p.y, maxy);
550 | }
551 | }
552 | }
553 |
554 | public Area getShape() {
555 | if (shape == null) {
556 | shape = new Area();
557 | for (int rI = 0; rI < rings.size(); rI++) {
558 | Ring r = rings.get(rI);
559 | Path2D path = new Path2D.Double();
560 | path.setWindingRule(Path2D.WIND_EVEN_ODD);
561 |
562 | int n = r.points.length;
563 | path.moveTo(r.points[0].x, r.points[0].y);
564 | for(int i = 1; i < n; ++i) {
565 | path.lineTo(r.points[i].x, r.points[i].y);
566 | }
567 | path.closePath();
568 | Area ringArea = new Area(path);
569 | double a = Math.abs(r.area());
570 | if (rI == 0) { // first ring is positive
571 | shape.add(ringArea);
572 | area += a;
573 | }
574 | else {
575 | shape.subtract(ringArea);
576 | area -= a;
577 | }
578 | }
579 | }
580 | return shape;
581 | }
582 |
583 | @Override
584 | public int compareTo(Polygon o) {
585 | if (this.confidence > o.confidence) return -1;
586 | if (this.confidence < o.confidence) return 1;
587 | return 0;
588 | }
589 |
590 | @Override
591 | public String toString() {
592 | return f(minx) + "," + f(miny) + " - " +
593 | f(maxx) + "," + f(maxy) + " " + match.toString();
594 | }
595 | }
596 |
597 | private class Ring {
598 | public P2[] points;
599 |
600 | public Ring(String rs) {
601 | String[] parts = rs.split(",");
602 | int cnt = parts.length;
603 | double[] xs = new double[cnt];
604 | double[] ys = new double[cnt];
605 | for (int i = 0; i < cnt; i++) {
606 | String s = parts[i];
607 | s = s.trim();
608 | String[] coords = s.split(" ");
609 | xs[i] = Double.parseDouble(coords[0]);
610 | ys[i] = Double.parseDouble(coords[1]);
611 | }
612 |
613 | int n = xs.length;
614 | points = new P2[n];
615 | for (int i = 0; i < n; i++) points[i] = new P2(xs[i], ys[i]);
616 | if (n > 1 && !points[0].equals(points[n-1])) {
617 | log("Warning: ring not closed: " + rs);
618 | }
619 | }
620 |
621 | public double area() {
622 | // signed area calculated from the points
623 | double a = 0;
624 | for (int i = 1; i < points.length; i++) {
625 | a += (points[i-1].x + points[i].x) * (points[i-1].y - points[i].y);
626 | }
627 | return a / 2;
628 | }
629 | }
630 |
631 |
632 | /**************************************************************************************************
633 | *
634 | * THINGS BELOW THIS ARE UI-RELATED, NOT NEEDED FOR SCORING
635 | *
636 | **************************************************************************************************/
637 |
638 | public void setupGUI(int W) {
639 | if (!hasGui) return;
640 |
641 | loadBandTriplets();
642 |
643 | frame = new JFrame("Building Detector Visualizer");
644 | int H = W * 2 / 3;
645 | frame.setSize(W, H);
646 | frame.setResizable(false);
647 | frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
648 |
649 | Container cp = frame.getContentPane();
650 | cp.setLayout(new GridBagLayout());
651 |
652 | GridBagConstraints c = new GridBagConstraints();
653 |
654 | c.fill = GridBagConstraints.BOTH;
655 | c.gridx = 0;
656 | c.gridy = 0;
657 | c.weightx = 2;
658 | c.weighty = 1;
659 | viewPanel = new JPanel();
660 | viewPanel.setPreferredSize(new Dimension(H, H));
661 | cp.add(viewPanel, c);
662 |
663 | c.fill = GridBagConstraints.BOTH;
664 | c.gridx = 1;
665 | c.gridy = 0;
666 | c.weightx = 1;
667 | controlsPanel = new JPanel();
668 | cp.add(controlsPanel, c);
669 |
670 | viewPanel.setLayout(new BorderLayout());
671 | mapView = new MapView();
672 | viewPanel.add(mapView, BorderLayout.CENTER);
673 |
674 | controlsPanel.setLayout(new GridBagLayout());
675 | GridBagConstraints c2 = new GridBagConstraints();
676 |
677 | showTruthCb = new JCheckBox("Show truth polygons");
678 | showTruthCb.setSelected(true);
679 | showTruthCb.addActionListener(this);
680 | c2.fill = GridBagConstraints.BOTH;
681 | c2.gridx = 0;
682 | c2.gridy = 0;
683 | c2.weightx = 1;
684 | controlsPanel.add(showTruthCb, c2);
685 |
686 | showSolutionCb = new JCheckBox("Show solution polygons");
687 | showSolutionCb.setSelected(true);
688 | showSolutionCb.addActionListener(this);
689 | c2.gridy = 1;
690 | controlsPanel.add(showSolutionCb, c2);
691 |
692 | showIouCb = new JCheckBox("Show IOU scores");
693 | showIouCb.setSelected(true);
694 | showIouCb.addActionListener(this);
695 | c2.gridy = 2;
696 | controlsPanel.add(showIouCb, c2);
697 |
698 | int b = bandTriplets.size();
699 | String[] views = new String[b];
700 | for (int i = 0; i < b; i++) views[i] = bandTriplets.get(i).toString();
701 | viewSelectorComboBox = new JComboBox<>(views);
702 | viewSelectorComboBox.setSelectedIndex(0);
703 | viewSelectorComboBox.addItemListener(this);
704 | c2.gridy = 3;
705 | controlsPanel.add(viewSelectorComboBox, c2);
706 |
707 | imageSelectorComboBox = new JComboBox<>(new String[] {"..."});
708 | c2.gridy = 4;
709 | controlsPanel.add(imageSelectorComboBox, c2);
710 |
711 | JScrollPane sp = new JScrollPane();
712 | logArea = new JTextArea("", 10, 20);
713 | logArea.setFont(new Font("Monospaced", Font.PLAIN, 16));
714 | logArea.addMouseListener(this);
715 | sp.getViewport().setView(logArea);
716 | c2.gridy = 5;
717 | c2.weighty = 10;
718 | controlsPanel.add(sp, c2);
719 |
720 | frame.setVisible(true);
721 | }
722 |
723 | private void loadBandTriplets() {
724 | bandTriplets = new Vector<>();
725 | BandTriplet b3 = new BandTriplet();
726 | b3.is3band = true;
727 | b3.name = "3-band RGB";
728 | bandTriplets.add(b3);
729 | currentBandTriplet = b3;
730 |
731 | String line = null;
732 | int lineNo = 0;
733 | try {
734 | LineNumberReader lnr = new LineNumberReader(new FileReader(bandTripletPath));
735 | while (true) {
736 | line = lnr.readLine();
737 | if (line == null) break;
738 | lineNo++;
739 | line = line.trim();
740 | if (line.isEmpty() || line.startsWith("#")) continue;
741 | String[] parts = line.split("\t");
742 | BandTriplet b = new BandTriplet();
743 | b.is3band = false;
744 | b.name = parts[1];
745 | for (int i = 0; i < 3; i++) {
746 | b.bands[i] = Integer.parseInt(parts[0].substring(i, i+1));
747 | }
748 | bandTriplets.add(b);
749 | }
750 | lnr.close();
751 | }
752 | catch (Exception e) {
753 | log("Error reading band triplets from " + bandTripletPath);
754 | log("Line #" + lineNo + " : " + line);
755 | e.printStackTrace();
756 | System.exit(0);
757 | }
758 | }
759 |
760 | private class BandTriplet {
761 | public String name;
762 | public int[] bands = new int[3];
763 | public boolean is3band;
764 | public MapData mapData;
765 |
766 | @Override
767 | public String toString() {
768 | if (is3band) return name;
769 | return bands[0] + "," + bands[1] + "," + bands[2] + " : " + name;
770 | }
771 | }
772 |
773 | private void repaintMap() {
774 | if (mapView != null) mapView.repaint();
775 | }
776 |
777 | @SuppressWarnings("serial")
778 | private class MapView extends JLabel implements MouseListener, MouseMotionListener, MouseWheelListener {
779 |
780 | private int mouseX;
781 | private int mouseY;
782 | private Color invalidColor = new Color(150, 150, 200);
783 |
784 | public MapView() {
785 | super();
786 | this.addMouseListener(this);
787 | this.addMouseMotionListener(this);
788 | this.addMouseWheelListener(this);
789 | }
790 |
791 | @Override
792 | public void paint(Graphics gr) {
793 | if (currentBandTriplet == null || currentBandTriplet.mapData == null) return;
794 | MapData mapData = currentBandTriplet.mapData;
795 |
796 | Graphics2D g2 = (Graphics2D) gr;
797 | g2.setFont(font);
798 | g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
799 | g2.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
800 | int W = this.getWidth();
801 | int H = this.getHeight();
802 | for (int i = 0; i < W; i++) for (int j = 0; j < H; j++) {
803 | Color c = invalidColor;
804 | int mapI = (int)((i - x0) * scale);
805 | int mapJ = (int)((j - y0) * scale);
806 |
807 | if (mapI >= 0 && mapJ >= 0 && mapI < mapData.W && mapJ < mapData.H) {
808 | int r = mapData.rs[mapI][mapJ];
809 | int g = mapData.gs[mapI][mapJ];
810 | int b = mapData.bs[mapI][mapJ];
811 | c = new Color(r, g, b);
812 | }
813 | g2.setColor(c);
814 | g2.fillRect(i, j, 1, 1);
815 | }
816 |
817 | if (showTruthCb.isSelected()) {
818 | Polygon[] truthPolygons = idToTruthPolygons.get(currentImageId);
819 | if (truthPolygons != null) {
820 | for (Polygon p: truthPolygons) {
821 | Color border = p.match == Match.TP ? tpBorderTruthColor : fnBorderColor;
822 | Color fill = p.match == Match.TP ? tpFillTruthColor : fnFillColor;
823 | drawPoly(p, g2, border, fill, null);
824 | }
825 | }
826 | }
827 | if (showSolutionCb.isSelected()) {
828 | Polygon[] solutionPolygons = idToSolutionPolygons.get(currentImageId);
829 | if (solutionPolygons != null) {
830 | for (Polygon p: solutionPolygons) {
831 | String label = null;
832 | if (showIouCb.isSelected()) {
833 | label = f(p.iouScore);
834 | }
835 | Color border = p.match == Match.TP ? tpBorderSolutionColor : fpBorderColor;
836 | Color fill = p.match == Match.TP ? tpFillSolutionColor : fpFillColor;
837 | drawPoly(p, g2, border, fill, label);
838 | }
839 | }
840 | }
841 | }
842 |
843 | private void drawPoly(Polygon p, Graphics2D g2, Color border, Color fill, String label) {
844 | // polygon coordinates are in 3-band space so everything should be scaled if needed
845 | double r = currentBandTriplet.is3band ? 1 : ratio38;
846 |
847 | double minx = p.minx / r / scale + x0;
848 | if (minx > this.getWidth()) return;
849 | double maxx = p.maxx / r / scale + x0;
850 | if (maxx < 0) return;
851 | double miny = p.miny / r / scale + y0;
852 | if (miny > this.getHeight()) return;
853 | double maxy = p.maxy / r / scale + y0;
854 | if (maxy < 0) return;
855 |
856 | AffineTransform t = new AffineTransform();
857 | t.translate(x0, y0);
858 | t.scale(1 / (r * scale), 1 / (r * scale));
859 | Area a = p.getShape().createTransformedArea(t);
860 |
861 | g2.setColor(border);
862 | g2.draw(a);
863 | g2.setColor(fill);
864 | g2.fill(a);
865 |
866 | if (label != null) {
867 | int centerX = (int)((p.maxx / r + p.minx / r) / 2 / scale + x0);
868 | int centerY = (int)((p.maxy / r + p.miny / r) / 2 / scale + y0);
869 | int w = textWidth(label, g2);
870 | int h = font.getSize();
871 | g2.setColor(textColor);
872 | g2.drawString(label, centerX - w/2, centerY + h/2);
873 | }
874 | }
875 |
876 | private int textWidth(String text, Graphics2D g) {
877 | FontRenderContext context = g.getFontRenderContext();
878 | Rectangle2D r = font.getStringBounds(text, context);
879 | return (int) r.getWidth();
880 | }
881 |
882 | @Override
883 | public void mouseClicked(java.awt.event.MouseEvent e) {
884 | // nothing
885 | }
886 | @Override
887 | public void mouseReleased(java.awt.event.MouseEvent e) {
888 | repaintMap();
889 | }
890 | @Override
891 | public void mouseEntered(java.awt.event.MouseEvent e) {
892 | setCursor(Cursor.getPredefinedCursor(Cursor.CROSSHAIR_CURSOR));
893 | }
894 | @Override
895 | public void mouseExited(java.awt.event.MouseEvent e) {
896 | setCursor(Cursor.getDefaultCursor());
897 | }
898 |
899 | @Override
900 | public void mousePressed(java.awt.event.MouseEvent e) {
901 | int x = e.getX();
902 | int y = e.getY();
903 | mouseX = x;
904 | mouseY = y;
905 | repaintMap();
906 | }
907 |
908 | @Override
909 | public void mouseDragged(java.awt.event.MouseEvent e) {
910 | int x = e.getX();
911 | int y = e.getY();
912 | x0 += x - mouseX;
913 | y0 += y - mouseY;
914 | mouseX = x;
915 | mouseY = y;
916 | repaintMap();
917 | }
918 |
919 | @Override
920 | public void mouseMoved(java.awt.event.MouseEvent e) {
921 | // ignore
922 | }
923 |
924 | @Override
925 | public void mouseWheelMoved(MouseWheelEvent e) {
926 | mouseX = e.getX();
927 | mouseY = e.getY();
928 | double dataX = (mouseX - x0) * scale;
929 | double dataY = (mouseY - y0) * scale;
930 |
931 | double change = Math.pow(2, 0.5);
932 | if (e.getWheelRotation() > 0) scale *= change;
933 | if (e.getWheelRotation() < 0) scale /= change;
934 |
935 | x0 = mouseX - dataX / scale;
936 | y0 = mouseY - dataY / scale;
937 |
938 | repaintMap();
939 | }
940 | } // class MapView
941 |
942 |
943 | @Override
944 | public void actionPerformed(ActionEvent e) {
945 | // check boxes clicked
946 | repaintMap();
947 | }
948 |
949 | @Override
950 | public void itemStateChanged(ItemEvent e) {
951 | if (e.getStateChange() == ItemEvent.SELECTED) {
952 | if (e.getSource() == imageSelectorComboBox) {
953 | // new image selected
954 | currentImageId = (String) imageSelectorComboBox.getSelectedItem();
955 | loadMap();
956 | }
957 | else if (e.getSource() == viewSelectorComboBox) {
958 | BandTriplet old = currentBandTriplet;
959 | // new band triplet selected
960 | int i = viewSelectorComboBox.getSelectedIndex();
961 | currentBandTriplet = bandTriplets.get(i);
962 | // 3 -> 8
963 | if (old.is3band && !currentBandTriplet.is3band) scale /= ratio38;
964 | // 8 -> 3
965 | if (!old.is3band && currentBandTriplet.is3band) scale *= ratio38;
966 | }
967 | repaintMap();
968 | }
969 | }
970 |
971 | @Override
972 | public void mouseClicked(MouseEvent e) {
973 | if (e.getSource() != logArea) return;
974 | try {
975 | int lineIndex = logArea.getLineOfOffset(logArea.getCaretPosition());
976 | int start = logArea.getLineStartOffset(lineIndex);
977 | int end = logArea.getLineEndOffset(lineIndex);
978 | String line = logArea.getDocument().getText(start, end - start).trim();
979 | for (int i = 0; i < imageIds.length; i++) {
980 | if (imageIds[i].equals(line)) {
981 | currentImageId = imageIds[i];
982 | imageSelectorComboBox.setSelectedIndex(i);
983 | loadMap();
984 | repaintMap();
985 | }
986 | }
987 | }
988 | catch (Exception ex) {
989 | ex.printStackTrace();
990 | }
991 | }
992 |
993 | @Override
994 | public void mousePressed(MouseEvent e) {}
995 | @Override
996 | public void mouseReleased(MouseEvent e) {}
997 | @Override
998 | public void mouseEntered(MouseEvent e) {}
999 | @Override
1000 | public void mouseExited(MouseEvent e) {}
1001 |
1002 | private void log(String s) {
1003 | if (logArea != null) logArea.append(s + "\n");
1004 | System.out.println(s);
1005 | }
1006 |
1007 | public static void main(String[] args) throws Exception {
1008 | boolean setDefaults = true;
1009 | for (int i = 0; i < args.length; i++) { // to change settings easily from Eclipse
1010 | if (args[i].equals("-no-defaults")) setDefaults = false;
1011 | }
1012 |
1013 | Visualizer v = new Visualizer();
1014 | v.bandTripletPath = "../data/testdata/band-triplets.txt";
1015 | String dataDir;
1016 |
1017 | // These are just some default settings for local testing, can be ignored.
1018 |
1019 | // sample data
1020 | dataDir = "../data/testdata/";
1021 | v.truthPath = dataDir + "truth.csv";
1022 | v.solutionPath = dataDir + "solution.csv";
1023 | v.image3Dir = dataDir + "3band";
1024 | v.image8Dir = dataDir + "8band";
1025 | v.runMode = RunMode.IMAGE_DIR;
1026 |
1027 | // training data
1028 | // dataDir = "../data/spacenet/AOI_1_current/";
1029 | // v.truthPath = dataDir + "truth-training.csv";
1030 | // v.solutionPath = dataDir + "truth-example.csv";
1031 | // v.image3Dir = dataDir + "3band";
1032 | // v.image8Dir = dataDir + "8band";
1033 | // v.runMode = RunMode.TRUTH;
1034 |
1035 | // test data
1036 | // dataDir = "../data/spacenet/AOI_2_current/test/";
1037 | // v.truthPath = dataDir + "truth-provisional.csv";
1038 | // v.solutionPath = null;
1039 | // v.image3Dir = dataDir + "3band";
1040 | // v.image8Dir = dataDir + "8band";
1041 | // v.runMode = RunMode.IMAGE_DIR;
1042 |
1043 | // validation data
1044 | // dataDir = "../data/spacenet/AOI_2_current/validation/";
1045 | // v.truthPath = dataDir + "truth-validation.csv";
1046 | // v.solutionPath = null;
1047 | // v.image3Dir = dataDir + "3band";
1048 | // v.image8Dir = dataDir + "8band";
1049 | // v.runMode = RunMode.TRUTH;
1050 |
1051 | v.hasGui = true;
1052 | int w = 1500;
1053 |
1054 | if (setDefaults) {
1055 | v.hasGui = true;
1056 | w = 1500;
1057 | v.truthPath = null;
1058 | v.solutionPath = null;
1059 | v.image3Dir = null;
1060 | v.image8Dir = null;
1061 | v.bandTripletPath = null;
1062 | v.runMode = RunMode.TRUTH;
1063 | }
1064 |
1065 | for (int i = 0; i < args.length; i++) {
1066 | if (args[i].equals("-run-mode")) {
1067 | String m = args[i+1].toLowerCase();
1068 | if (m.equals("truth")) v.runMode = RunMode.TRUTH;
1069 | else if (m.equals("solution")) v.runMode = RunMode.SOLUTION;
1070 | else v.runMode = RunMode.IMAGE_DIR;
1071 | }
1072 | if (args[i].equals("-no-gui")) v.hasGui = false;
1073 | if (args[i].equals("-w")) w = Integer.parseInt(args[i+1]);
1074 | if (args[i].equals("-iou-threshold")) v.iouThreshold = Double.parseDouble(args[i+1]);
1075 | if (args[i].equals("-truth")) v.truthPath = args[i+1];
1076 | if (args[i].equals("-solution")) v.solutionPath = args[i+1];
1077 | if (args[i].equals("-image3-dir")) v.image3Dir = args[i+1];
1078 | if (args[i].equals("-image8-dir")) v.image8Dir = args[i+1];
1079 | if (args[i].equals("-band-triplets")) v.bandTripletPath = args[i+1];
1080 | if (args[i].equals("-tp-border-solution")) v.tpBorderSolutionColor = parseColor(args[i+1]);
1081 | if (args[i].equals("-tp-fill-solution")) v.tpFillSolutionColor = parseColor(args[i+1]);
1082 | if (args[i].equals("-tp-border-truth")) v.tpBorderTruthColor = parseColor(args[i+1]);
1083 | if (args[i].equals("-tp-fill-truth")) v.tpFillTruthColor = parseColor(args[i+1]);
1084 | if (args[i].equals("-fp-border")) v.fpBorderColor = parseColor(args[i+1]);
1085 | if (args[i].equals("-fp-fill")) v.fpFillColor = parseColor(args[i+1]);
1086 | if (args[i].equals("-fn-border")) v.fnBorderColor = parseColor(args[i+1]);
1087 | if (args[i].equals("-fn-fill")) v.fnFillColor = parseColor(args[i+1]);
1088 | }
1089 |
1090 | if (v.image3Dir == null && v.hasGui) exit("3-band image directory not set.");
1091 | if (v.image8Dir == null && v.hasGui) exit("8-band image directory not set.");
1092 |
1093 | v.setupGUI(w);
1094 | v.run();
1095 | }
1096 |
1097 | private static Color parseColor(String s) {
1098 | String[] parts = s.split(",");
1099 | int r = Integer.parseInt(parts[0]);
1100 | int g = Integer.parseInt(parts[1]);
1101 | int b = Integer.parseInt(parts[2]);
1102 | int a = parts.length > 3 ? Integer.parseInt(parts[3]) : 255;
1103 | return new Color(r, g, b, a);
1104 | }
1105 |
1106 | private static void exit(String s) {
1107 | System.out.println(s);
1108 | System.exit(1);
1109 | }
1110 |
1111 | }
1112 |
--------------------------------------------------------------------------------
/visualizer-1.1/visualizer.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SpaceNetChallenge/BuildingDetectorVisualizer/3cf947c8ad8d399511df713fce2ca76576664eae/visualizer-1.1/visualizer.jar
--------------------------------------------------------------------------------
/visualizer-1.1/visualizer_lib/imageio-ext-geocore-1.1.16.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SpaceNetChallenge/BuildingDetectorVisualizer/3cf947c8ad8d399511df713fce2ca76576664eae/visualizer-1.1/visualizer_lib/imageio-ext-geocore-1.1.16.jar
--------------------------------------------------------------------------------
/visualizer-1.1/visualizer_lib/imageio-ext-streams-1.1.16.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SpaceNetChallenge/BuildingDetectorVisualizer/3cf947c8ad8d399511df713fce2ca76576664eae/visualizer-1.1/visualizer_lib/imageio-ext-streams-1.1.16.jar
--------------------------------------------------------------------------------
/visualizer-1.1/visualizer_lib/imageio-ext-tiff-1.1.16.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SpaceNetChallenge/BuildingDetectorVisualizer/3cf947c8ad8d399511df713fce2ca76576664eae/visualizer-1.1/visualizer_lib/imageio-ext-tiff-1.1.16.jar
--------------------------------------------------------------------------------
/visualizer-1.1/visualizer_lib/imageio-ext-utilities-1.1.16.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SpaceNetChallenge/BuildingDetectorVisualizer/3cf947c8ad8d399511df713fce2ca76576664eae/visualizer-1.1/visualizer_lib/imageio-ext-utilities-1.1.16.jar
--------------------------------------------------------------------------------
/visualizer-1.1/visualizer_lib/jai_codec-1.1.3.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SpaceNetChallenge/BuildingDetectorVisualizer/3cf947c8ad8d399511df713fce2ca76576664eae/visualizer-1.1/visualizer_lib/jai_codec-1.1.3.jar
--------------------------------------------------------------------------------
/visualizer-1.1/visualizer_lib/jai_core-1.1.3.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SpaceNetChallenge/BuildingDetectorVisualizer/3cf947c8ad8d399511df713fce2ca76576664eae/visualizer-1.1/visualizer_lib/jai_core-1.1.3.jar
--------------------------------------------------------------------------------
/visualizer-1.1/visualizer_lib/jai_imageio-1.1.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SpaceNetChallenge/BuildingDetectorVisualizer/3cf947c8ad8d399511df713fce2ca76576664eae/visualizer-1.1/visualizer_lib/jai_imageio-1.1.jar
--------------------------------------------------------------------------------
/visualizer-1.1/visualizer_readme.txt:
--------------------------------------------------------------------------------
1 | Building Detector offline tools
2 | -------------------------------
3 | The package contains two tools bundled together: a visualizer GUI application and a band extractor command line tool.
4 |
5 | 1. Visualizer GUI
6 | -----------------
7 | The purpose of the visualizer application is to let you view 3-band and 8-band images, view ground truth building footprints and your solution's building footprint as overlays on these images, compare truth to solution and calculate your solution's score.
8 |
9 | Open a command window in the directory where you unzipped the package and execute
10 | java -jar visualizer.jar -truth -solution -image3-dir <3band_image_directory> -image8-dir <8band_image_directory> -band-triplets
11 |
12 | This assumes that you have Java (at least v1.7) installed and it is available on your path. The meaning of the above parameters are the following:
13 | * -truth specifies the location of to the truth file, see the ./data/truth.csv for an example,
14 | * -solution is your solution file, see ./data/solution.csv,
15 | * -image3-dir and -image8-dir are the directories containing the 3-band and 8-band imagery, respectively.
16 | * -band-triplets points to a file that defines the band index triplets used to generate RGB images from the 8-band imagery. See ./data/band-triplets.txt, it describes the required syntax of band triplet definitions.
17 | All file and directory parameters can be relative or absolute paths. The -truth and -solution paramaters are optional, the tool is able to run without them, see -run-mode below.
18 |
19 | For example a command line that will run the app with the supplied sample data:
20 | java -jar visualizer.jar -truth ./data/truth.csv -solution ./data/solution.csv -image3-dir ./data/3band -image8-dir ./data/8band -band-triplets ./data/band-triplets.txt
21 |
22 | There are some other optional command line parameters you can use:
23 | * -w : Width of the tool's screen. Defaults to 1500.
24 | * -iou-threshold : Defaults to 0.5.
25 | * -no-gui: if present then no GUI will be shown, the application just scores the supplied solution file in command line mode.
26 | * -fp-border : with this you can customize the colour of the border of the polygons representing false positives. The parameter should be 4 integers separated by commas, no spaces in between. E.g to set it to semi-transparent blue you can use: -fp-border 0,0,255,128
27 | * -fp-fill : similar to the previous for the fill colour of the false positive polygons.
28 | * -fn-border and -fn-fill: as above for false negatives
29 | * -tp-border-solution, -tp-fill-solution, -tp-border-truth and -tp-fill-truth : as above for true positives, but here you can set different colours for the truth and solution polygons.
30 | * -run-mode : one of 'truth', 'solution' or 'image-directory' (without the quotes). Run mode specifies which images are loaded into the tool. If run mode is 'truth' then all images that are listed in the given truth file are loaded. Similarly for 'solution', all images that are present in the solution file are loaded. In 'image-directory' mode all images of the -image3-dir are loaded. Defaults to 'truth'.
31 | All these have proper defaults so you can leave them out.
32 |
33 | Operations
34 | ----------
35 | Usage of the tool should be straightforward. Select the view type from the top drop down list: the 3 band image or one of your predefined band triplet combinations. Select the image to be displayed from the bottom drop down list. Note that you can also switch to another image by clicking the line containing an image name in the output log window.
36 |
37 | Solution and truth are compared automatically (if both truth and solution files are specified), scores are displayed in the log window and also in the command line.
38 |
39 | You can zoom in/out within the image view by the mouse wheel, and pan the view by dragging.
40 |
41 | Sample images
42 | -------------
43 | Note that the sample images bundled together with the visualizer tool are not actual satellite images, these are only added for demonstration purposes. The 3-band images are created from aerial photography, the 8-band images are created by image processing manipulation of the 3-band images. (The Red, Green, Blue channels are correct but the Coastal, Near-IR1, etc. channels are fake.) You need to download the training data set of the contest to obtain real, ground-truthed satellite imagery. See https://aws.amazon.com/public-data-sets/spacenet/ for details on how to access real data.
44 |
45 | Examples
46 | --------
47 | 1. Typical usage: compare truth and solution files. Calculate score and show images.
48 | java -jar visualizer.jar -truth ./data/truth.csv -solution ./data/solution.csv -image3-dir ./data/3band -image8-dir ./data/8band -band-triplets ./data/band-triplets.txt
49 |
50 | 2. As above, without GUI, does only the scoring.
51 | java -jar visualizer.jar -truth ./data/truth.csv -solution ./data/solution.csv -image3-dir ./data/3band -image8-dir ./data/8band -band-triplets ./data/band-triplets.txt -no-gui
52 |
53 | 3. Just show all the images in a folder.
54 | java -jar visualizer.jar -image3-dir ./data/3band -image8-dir ./data/8band -band-triplets ./data/band-triplets.txt -run-mode image-directory
55 |
56 | 4. Show truth data without comparing with solution.
57 | java -jar visualizer.jar -image3-dir ./data/3band -image8-dir ./data/8band -band-triplets ./data/band-triplets.txt -truth ./data/truth.csv
58 |
59 | 5. Show your solution without truth data.
60 | java -jar visualizer.jar -image3-dir ./data/3band -image8-dir ./data/8band -band-triplets ./data/band-triplets.txt -solution ./data/solution.csv -run-mode solution
61 |
62 | 2. Band extractor CLI
63 | ---------------------
64 | The purpose of the band extractor application is to extract individual bands from 8-band GeoTiff files. Most image viewers and image processing libraries can't handle 8-band images so this application is provided as a file conversion convenience tool.
65 |
66 | Open a command window in the directory where you unzipped the package and execute
67 | java -cp visualizer.jar visualizer.BandExtractor -in -out
68 |
69 | where
70 | * is either a 8-band GeoTiff file or a directory containing such files. In the latter case all files within the directory are processed.
71 | * is a directory where the extracted images will be placed. The application creates output files having the same name as the input file appended with a _b suffix, i = [1..8].
72 |
73 | There are other optional command line parameters you can use:
74 | * -type : one of 'png', 'tif', 'none' (without the quotes). Specifies the output file type, default is 'png'. If 'none' then no output file is generated, only pixel value statistics are output.
75 | * -max : an integer value, defaults to -1 meaning no maximum is set. See 'Pixel value scaling' for a description of how this value is used.
76 | * -bands : a string containing a list of integer values from the [1..8] range, meaning the set of bands you wish to extract. Defaults to '12345678' (without the quotes), meaning all bands. Don't use any separator between the numbers.
77 |
78 | Pixel value scaling
79 | The 8-band GeoTiff files contain 16-bit pixel intensity values, these have to be converted to standard 8-bit RGB images. You can specify an external maximum value with the max parameter, all values higher than that will be converted to a 255 grayscale value, values lower than that will be proportionally lower. If you don't specify a maximum value then the file's internal maximum will be used: this is the maximum pixel value found in all the selected bands (which may be a subset of all bands if you specified a other than '12345678'). Note that if you don't specify an external maximum then pixel intensities can not be meaningfully compared across images.
80 | When the tool runs the maximum pixel values for each band are displayed. If you want your extracted band images to have comparable values then study this output and select an appropriate maximum value that you specify for the conversion. Note that you can select different values for each band if you use both the -max and the -bands parameters during conversion.
81 |
82 | 3. Licenses
83 | -----------
84 | - The visualizer and band extractor tools use the imageio-ext library for reading multiband TIFF files. The imageio-ext library is LGPL licensed, see here for its license text: https://github.com/geosolutions-it/imageio-ext/blob/master/LICENSE.txt. See https://github.com/geosolutions-it/imageio-ext for details on the library.
85 |
86 | - The sample images bundled together with the visualizer tool were created using this image: https://commons.wikimedia.org/wiki/File:Ortofoto_Citt%C3%A0_Alta,_Rocca.jpg . The file is licensed under the Creative Commons Attribution-Share Alike 4.0 International license. All imagery bundled with the tool is also licensed under the same license.
--------------------------------------------------------------------------------