Skip to content

Commit

Permalink
added readme
Browse files Browse the repository at this point in the history
  • Loading branch information
matthmey committed Nov 28, 2019
1 parent 5cc4de6 commit 24aacae
Show file tree
Hide file tree
Showing 7 changed files with 308 additions and 43 deletions.
20 changes: 20 additions & 0 deletions ideas/annotation/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Idea: Integrated Annotation Tool For Images


## Quickstart
From the repositories base directory execute
```
python ideas/annotation/images.py
```

The data will be loaded from Azure. You have the option to either load sparse (in time) high-resolution images or low resolution images with a higher capture frequency. (This has been done to minimize transfer costs; all images are available in high-resolution upon request)
You can also download the data (6.7 GB) yourself and use it (again executed from the base directory).

```
python utils/download_files.py -f timelapse_images_fast.zip
python ideas/annotation/images.py --local
```

## What next?
There are many tools available for image labeling, but what if we want to label timeseries data? How do we even label timeseries data? Can we simply draw on the plot of a time series or are there certain characteristics which are hidden and can only revealed with another view on the data?
A way to plot annotations over time is presented in [timeseries.py](./timeseries.py)
57 changes: 36 additions & 21 deletions ideas/annotation/images.py
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,18 @@ def serve_layout():
id="date_indicator",
style={"width": "50%", "display": "inline-block"},
),
html.Div([
dcc.Input(
id="userid_input",
placeholder="Your ID",
type="number",
value="",
persistence=True,
),
],
style={"width": "50%", "display": "inline-block"},
),

]
),
html.Div(
Expand All @@ -254,7 +266,7 @@ def serve_layout():
options=[
{"label": bb_label_mapping[m], "value": m} for m in bb_label_mapping.keys()
],
value="red",
value="#1f77b4",
),
dcc.Dropdown(
id="static_label_dropdown",
Expand All @@ -270,16 +282,11 @@ def serve_layout():
className="six columns",
),
dcc.Markdown(
"Annotate by selecting per picture labels or draw bounding boxes with the rectangle tool"
"Note: Rotating bounding boxes will result in incorrect labels."
),
dcc.Input(
id="userid_input",
placeholder="Your user id",
type="number",
value="",
persistence=True,
"""Annotate by selecting per picture labels or draw bounding boxes with the rectangle tool
Note: Rotating bounding boxes will result in incorrect labels."""
),

],
style={"width": "50%"}, # Div
className="row",
Expand Down Expand Up @@ -308,10 +315,10 @@ def update_output(value):

@app.callback(
Output("my-date-picker-single", "date"),
[Input("canvas", "prev_trigger"), Input("canvas", "next_trigger")],
[Input("canvas", "prev_trigger"), Input("canvas", "next_trigger"), Input("userid_input","value")],
[State("index", "data")],
)
def reduce_help(prev_trigger, next_trigger, index):
def reduce_help(prev_trigger, next_trigger, userid_input_value, index):
""" Triggers on click on the arrow buttons. Changes the date of the date selection tool,
which triggers the loading of a new image.
Expand All @@ -337,13 +344,21 @@ def reduce_help(prev_trigger, next_trigger, index):
index = 0

if button_id == "canvas.prev_trigger":
index = index - 1
index = index - 2
if index < 0:
index = 0
elif button_id == "canvas.next_trigger":
index = index + 1
index = index + 2
if index >= len(data):
index = len(data) - 1
elif button_id == "userid_input.value":
if userid_input_value is not None:
max_user = 90
index = int(len(data)/max_user * (userid_input_value-1))
if index < 0:
index = 0
if index >= len(data):
index = len(data) - 1
else:
raise PreventUpdate

Expand Down Expand Up @@ -465,9 +480,9 @@ def to_csv(df, session_id, file_id=None, user_id=None):
stuett.to_csv_with_store(remote_store, filename, df, dict(index=False))


def read_csv(session_id, file_id):
def read_csv(session_id, file_id, user_id=None):
global local_store
filename = session_id + f"/{file_id}.csv"
filename = session_id + "-" + str(user_id) + f"/{file_id}.csv"
return stuett.read_csv_with_store(local_store, filename)


Expand All @@ -478,9 +493,9 @@ def read_csv(session_id, file_id):
Output("date_indicator", "children"),
Output("static_label_dropdown", "value"),
],
[Input("my-date-picker-single", "date"), Input("session-id", "children")],
[Input("my-date-picker-single", "date"), Input("session-id", "children"), Input("userid_input", "value")],
)
def update_output(date, session_id):
def update_output(date, session_id, user_id):
""" The callback is used to load a new image when the date has changed.
Date change can be triggered by the date selector box or indirectly by the arrow buttons,
which change the date selector box.
Expand Down Expand Up @@ -529,10 +544,10 @@ def update_output(date, session_id):
datetime = data.index[index]
file_id = datetime.strftime("%Y%m%d_%H%M%S")
try:
df = read_csv(session_id, file_id)
except:
df = read_csv(session_id, file_id, user_id)
except Exception as e:
print('Could not read annotation file',e)
df = None
pass

# Load the annotations from the server
try:
Expand Down
109 changes: 109 additions & 0 deletions ideas/annotation/timeseries.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
"""MIT License
Copyright (c) 2019, Swiss Federal Institute of Technology (ETH Zurich), Matthias Meyer, Romain Jacob
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE."""

import stuett
from stuett.global_config import get_setting, setting_exists
import argparse
from pathlib import Path

import pandas as pd
from plotly.subplots import make_subplots
import plotly.graph_objects as go
import pandas as pd

parser = argparse.ArgumentParser(description="Seismic time series and spectogram plot")
parser.add_argument(
"-p",
"--path",
type=str,
default=str(Path(__file__).absolute().parent.joinpath("..", "data/")),
help="The path to the folder containing the permafrost hackathon data",
)
parser.add_argument("-l", "--local", action="store_true", help="Load data from local storage")
args = parser.parse_args()

data_path = Path(args.path)
annotations_path = data_path.joinpath("annotations")

if not args.local:
account_name = (
get_setting("azure")["account_name"]
if setting_exists("azure")
else "storageaccountperma8980"
)
account_key = (
get_setting("azure")["account_key"] if setting_exists("azure") else None
)
annotation_store = stuett.ABSStore(
container="hackathon-on-permafrost",
prefix="annotations",
account_name=account_name,
account_key=account_key,
blob_service_kwargs={},
)
else:
annotation_store = stuett.DirectoryStore(annotations_path)


df = stuett.read_csv_with_store(annotation_store, "automatic_labels_mountaineers.csv")
df['start_time'] = pd.to_datetime(df['start_time'] )
df['end_time'] = pd.to_datetime(df['end_time'] )
df.index = df['start_time']
df = df['2017-08-01':'2017-08-02']


fig = go.Figure(
layout=dict(
xaxis={"type": "date"},
xaxis_range=[
pd.to_datetime("2017-08-01"),
pd.to_datetime("2017-08-02"),
],
)
)

for i,row in df.iterrows():
if(pd.isnull(row['__target'])):
continue
fig.add_trace(
go.Scatter(
x=[
row['start_time'],
row['end_time'],
row['end_time'],
row['start_time'],
],
y=[0, 0, 1, 1],
fill="toself",
fillcolor="darkviolet",
# marker={'size':0},
mode="lines",
hoveron="points+fills", # select where hover is active
line_color="darkviolet",
showlegend=False,
# line_width=0,
opacity=0.5,
text=str(row['__target']),
hoverinfo="text+x+y",
)
)
fig.show()
6 changes: 3 additions & 3 deletions ideas/machine_learning/datasets.py
Original file line number Diff line number Diff line change
Expand Up @@ -181,13 +181,13 @@ def __getitem__(self, idx):
if self.transform is not None:
data = self.transform(data)

# if "shape" in self.__dict__:
# if "shape" not in self.__dict__:
# self.shape = data.shape
# elif data.shape != self.shape:
# warnings.warn(f"Inconsistency in the data for item {indexers['time']}, its shape {data.shape} does not match shape {shape}")
# warnings.warn(f"Inconsistency in the data for item {indexers['time']}, its shape {data.shape} does not match shape {self.shape}")
# padded_data = torch.zeros(self.shape)
# pad = data.shape - self.shape
# padded_data = torch.nn.functional.pad(data)
# padded_data = torch.nn.functional.pad(data,pad)

return data, target

Expand Down
29 changes: 20 additions & 9 deletions tutorial/azure_integration.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,12 @@
import zarr


account_name = (
get_setting("azure")["account_name"]
if setting_exists("azure")
else "storageaccountperma8980"
)
# Get the account name which is "storageaccountperma8980" for the hackathon
# If you stored it in a config file you it will beloaded
if setting_exists("azure"):
account_name = get_setting("azure")["account_name"]
else:
account_name = "storageaccountperma8980"
account_key = get_setting("azure")["account_key"] if setting_exists("azure") else None

if account_key is not None:
Expand All @@ -42,22 +43,32 @@

# Create a blob service and list all data available
block_blob_service = BlockBlobService(
account_name="storageaccountperma8980", account_key=account_key
account_name=account_name, account_key=account_key
)
print("\nList blobs in the container")
generator = block_blob_service.list_blobs("hackathon-on-permafrost")
for blob in generator:
for i, blob in enumerate(generator):
print("\t Blob name: " + blob.name)
if i == 5:
break

# Create a zarr store and load the data from the
print("List some documents")
# In stuett we can use a a zarr store and load the data from there
store = stuett.ABSStore(
container="hackathon-on-permafrost",
prefix="dataset/",
prefix="docs/",
account_name=account_name,
account_key=account_key,
blob_service_kwargs={},
)

for i, key in enumerate(store.keys()):
print(key)
if i == 5:
break

# # Currently, stuett (or zarr in the backend) only support azure-storage-blob==2.1.0`
# # But a newer version is available which you can use independently
# # using azure-storage-blob==12.0.0
# # untested
# from azure.storage.blob import BlobServiceClient
Expand Down
14 changes: 4 additions & 10 deletions tutorial/howto_stuett.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@
SOFTWARE."""

import stuett
# What is stuett?
# A data management package developed for analysing and visualizing outdoor sensor deployments
# The name originates from the lower german word "stütt" which means "support"
# The tool revolves around a feedback workflow where analysis is tighly integrated with multi-modal input

import argparse
from pathlib import Path
Expand Down Expand Up @@ -106,13 +110,3 @@
# Note: The timezone used within stuett is UTC, but the datetime objects you receive are not timezone aware.
# Timezone issues are a recurring problem so be careful when working with the data.

# TODO: Seismic (as xarray and obspy)
# TODO: Images
# TODO: Rock temperature
# TODO: Wind
# TODO: Precipitation
# TODO: Radiation
# TODO: Mountaineer Annotations
# TODO: Annotations

#### Graph ####
Loading

0 comments on commit 24aacae

Please sign in to comment.