Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
__pycache__
43 changes: 43 additions & 0 deletions data/raw_to_dataset/mini_one_minute.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# Extracts train/valid/test for caldot1 and caldot2 datasets.
# This is not needed unless preparing video from scratch.

import os
import random
import subprocess
import sys


data_root = data_root = sys.argv[1]
video_path = '/data/processed/full-dataset/mini/videos/'

out_paths = [
os.path.join(data_root, 'dataset', 'mini', 'train/video/'),
os.path.join(data_root, 'dataset', 'mini', 'valid/video/'),
os.path.join(data_root, 'dataset', 'mini', 'test/video/'),
os.path.join(data_root, 'dataset', 'mini', 'tracker/video/'),
]

def get_duration(fname):
output = subprocess.check_output(['ffprobe', '-select_streams', 'v:0', '-show_entries', 'stream=duration', '-of', 'csv=s=,:p=0', fname])
print(f"duration is {float(output.strip())}")
return float(output.strip())

# list of tuples (fname, seconds)
segments = []

for fname in os.listdir(video_path):
if not fname.endswith('.mp4'):
continue
duration = int(get_duration(video_path+fname))
for skip in range(0, duration, 2):
segments.append((fname, skip))

random.shuffle(segments)
print('got {} segments'.format(len(segments)))

for out_path in out_paths:
cur_segments = segments[0:60]
segments = segments[60:]
for i, (fname, skip) in enumerate(cur_segments):
ffmpeg_args = ['ffmpeg', '-ss', str(skip), '-i', video_path+fname, '-t', '60', out_path+str(i)+'.mp4']
subprocess.call(ffmpeg_args)
2 changes: 1 addition & 1 deletion data/raw_to_dataset/one_minute_samples.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
files = random.sample(files, 60)
counter = 0
for label, id in files:
fnames = [os.path.join(video_path, label, '{}.mp4'.format(id+i) for i in range(skip)]
fnames = [os.path.join(video_path, label, '{}.mp4'.format(id+i)) for i in range(skip)]
if not all([os.path.exists(fname) for fname in fnames]):
print('skip {}/{} since some not exist'.format(label, id))
continue
Expand Down
4 changes: 3 additions & 1 deletion model/apply_dyn.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@
import subprocess
import sys
import tensorflow as tf
#tf.disable_eager_execution()
# tf.disable_eager_execution()
# import tensorflow.compat.v1 as tf
# tf.disable_v2_behavior()
import time

def eprint(s):
Expand Down
6 changes: 4 additions & 2 deletions model/model_dyn.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import numpy
import tensorflow as tf
#tf.disable_eager_execution()
# import tensorflow as tf
# tf.disable_eager_execution()
import tensorflow.compat.v1 as tf
# tf.disable_v2_behavior()
import os
import os.path
import random
Expand Down
2 changes: 1 addition & 1 deletion pipeline/eval.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ def track_one(args):

def track(label, in_path, out_path):
fnames = os.listdir(in_path)
if label in ['amsterdam', 'jackson', 'shibuya', 'caldot1', 'caldot2', 'warsaw', 'uav']:
if label in ['amsterdam', 'jackson', 'shibuya', 'caldot1', 'caldot2', 'warsaw', 'uav', 'mini']:
cls = 'car'
elif label == 'taipei':
cls = 'bus'
Expand Down
2 changes: 1 addition & 1 deletion pipeline2/10_detect.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package main

import (
"./lib"
"otif-pipeline/lib"

"encoding/json"
"fmt"
Expand Down
2 changes: 1 addition & 1 deletion pipeline2/30_speed_accuracy.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package main

import (
"./lib"
"otif-pipeline/lib"
"fmt"
"os"
"time"
Expand Down
2 changes: 1 addition & 1 deletion pipeline2/40_evaltest.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package main

import (
"./lib"
"otif-pipeline/lib"
"fmt"
"io/ioutil"
"os"
Expand Down
122 changes: 122 additions & 0 deletions pipeline2/README
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
Steps:

(0) Installation
(1) Prepare satellite imagery into individual tile images
(2) Convert road network map into .graph files
(3) Create tile list
(4) Train a model
(5) Infer roads in a new region


(0) Installation
----------------

These Python packages are required:
* tensorflow-gpu
* fiona
* rtree


(1) Prepare satellite imagery
-----------------------------

Satellite imagery is read from 4096x4096 pixel .png images.
These images form a 2D grid.
The filenames should be like:

imagery/REGION_0_0_sat.png
imagery/REGION_0_1_sat.png
imagery/REGION_1_0_sat.png
imagery/REGION_1_1_sat.png

Each filename is of the format REGION_X_Y_sat.png.

* REGION is a label for one portion of satellite imagery.
You can have multiple regions, like this:
imagery/region1_0_0_sat.png
imagery/region1_0_1_sat.png
imagery/region2_0_0_sat.png
imagery/region2_1_0_sat.png

* X and Y are the coordinates of the image in the grid.
For example, REGION_0_1 should be to the right of REGION_0_0.
Similarly, REGION_1_0 should be below REGION_0_0.
If we concatenate the images like this, the result should be a large coherent image:

REGION_0_0 REGION_0_1
REGION_1_0 REGION_1_1


(2) Convert road network map into .graph files
----------------------------------------------

The road network map must be represented as an undirected graph in a custom text format.

The format has two sections, vertices then edges, which together look like this:

100 200
100 400
100 600
200 400

0 1
1 0
1 2
2 1
1 3
3 1

The first four lines are vertices, while the last six are edges.
Each vertex line has two parts: the X coordinate and the Y coordinate.
These coordinates must be in the imagery coordinate system.

For example, because our images are 4096x4096, the coordinate (8192, 8192) is in
REGION_2_2_sat.png at (0, 0). Similarly, (6000, 1000) is in REGION_1_0_sat.png
at (1904, 1000).

Each edge line has two parts: a source vertex and a destination vertex.
The vertices are referenced by their 0-indexed line number. So "0 1" connects
vertex (100, 200) to vertex (100, 400).

The graph above looks like this:

(100, 200) -- (100, 400) -- (100, 600)
|
(200, 400)

util_shp_to_graph.py converts a shapefile to the graph file format.
However, you still need to convert the longitude-latitude coordinates to pixel coordinates.

There must be one graph file per region, and they must be stored in a folder like this:

graphs/region1.graph
graphs/region2.graph

The filenames (region1 and region2) should correspond to the regions in the imagery filenames.


(3) Create tile list
--------------------

The model training code needs to know which parts of the road network map to train on.
If you want to train the model everywhere where there is map data, then you can run:

$ python util_create_tile_list.py region1,region2 graphs/region1.graph,graphs/region2.graph tile_list.json


(4) Train a model
-----------------

$ mkdir model/ model/model_latest/ model/model_best/
$ python run_m5_point.py model/ imagery/ graphs/ tile_list.json


(5) Infer roads in a new region
-------------------------------

To infer roads in a new region, you need a basemap (also in the .graph file with
coordinates matching the imagery pixels) and the region label for the imagery.

Then just run:

$ python eval_m5_point.py model/model_best/model imagery/ graphs/ tile_list.json REGION basemap.graph
Empty file.
57 changes: 57 additions & 0 deletions pipeline2/discoverlib/coords.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import geom

import math

ORIGIN_SHIFT = 2 * math.pi * 6378137 / 2.0

def lonLatToMeters(p):
mx = p.x * ORIGIN_SHIFT / 180.0
my = math.log(math.tan((90 + p.y) * math.pi / 360.0)) / (math.pi / 180.0)
my = my * ORIGIN_SHIFT / 180.0
return geom.FPoint(mx, my)

def metersToLonLat(p):
lon = (p.x / ORIGIN_SHIFT) * 180.0
lat = (p.y / ORIGIN_SHIFT) * 180.0
lat = 180 / math.pi * (2 * math.atan(math.exp(lat * math.pi / 180.0)) - math.pi / 2.0)
return geom.FPoint(lon, lat)

def getMetersPerPixel(zoom):
return 2 * math.pi * 6378137 / (2**zoom) / 256

def lonLatToPixel(p, origin, zoom):
p = lonLatToMeters(p).sub(lonLatToMeters(origin))
p = p.scale(1 / getMetersPerPixel(zoom))
p = geom.FPoint(p.x, -p.y)
p = p.add(geom.FPoint(256, 256))
return p

def pixelToLonLat(p, origin, zoom):
p = p.sub(geom.FPoint(256, 256))
p = geom.FPoint(p.x, -p.y)
p = p.scale(getMetersPerPixel(zoom))
p = metersToLonLat(p.add(lonLatToMeters(origin)))
return p

def lonLatToMapboxTile(p, zoom):
n = 2**zoom
xtile = int((p.x + 180.0) / 360 * n)
ytile = int((1 - math.log(math.tan(p.y * math.pi / 180) + (1 / math.cos(p.y * math.pi / 180))) / math.pi) / 2 * n)
return (xtile, ytile)

def lonLatToMapbox(p, zoom, origin_tile):
n = 2**zoom
x = (p.x + 180.0) / 360 * n
y = (1 - math.log(math.tan(p.y * math.pi / 180) + (1 / math.cos(p.y * math.pi / 180))) / math.pi) / 2 * n
xoff = x - origin_tile[0]
yoff = y - origin_tile[1]
return geom.FPoint(xoff, yoff).scale(256)

def mapboxToLonLat(p, zoom, origin_tile):
n = 2**zoom
x = p.x / 256.0 + origin_tile[0]
y = p.y / 256.0 + origin_tile[1]
x = x * 360.0 / n - 180
y = math.atan(math.sinh(math.pi * (1 - 2.0 * y / n)))
y = y * 180 / math.pi
return geom.FPoint(x, y)
Loading