Newer
Older
import json
from typing import Dict
import numpy as np
DEFAULT_BED_CONTOURS_FILENAME = "bed_contours.json"
def load_contours(filename: str, as_ndarry: bool = True) -> Dict[int, np.ndarray]:
"""
Load contours from a json file.
The structure of the file is a dict where the key is the id of the according
image and the value is a numpy array of the contour.
:param filename: filename of a json file containing contours
:param as_ndarry: directly parse contours as numpy arrays
:return: a dict mapping ids to contours
"""
with open(filename, mode="r") as f:
contours = json.load(f)
_map = map(lambda item: (int(item[0]), np.array(item[1]).astype(int)), contours.items())
return dict(_map)
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
if __name__ == "__main__":
import argparse
from enum import Enum
import os
import cv2 as cv
from mu_map.data.datasets import MuMapDataset
from mu_map.util import to_grayscale, COLOR_BLACK, COLOR_WHITE
parser = argparse.ArgumentParser(
description="draw and save contours to exclude the bed from mu maps",
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
)
parser.add_argument(
"dataset_dir", type=str, help="the directory containing the dataset"
)
parser.add_argument(
"--output_file",
type=str,
default=DEFAULT_BED_CONTOURS_FILENAME,
help="default file in dataset dir where the drawn contours are stored",
)
args = parser.parse_args()
args.output_file = os.path.join(args.dataset_dir, args.output_file)
controls = """
Controls:
Left click to add points to the current contour.
q: exit
d: delete last point
n: save contour and go to the next image
v: change the visual mode between drawing contours and hiding the are within
"""
print(controls)
print()
# set bed contours file to None so that existing contours are not used
dataset = MuMapDataset(args.dataset_dir, bed_contours_file=None)
# TODO: implement that existing contours are loaded so that labeling can be continued?
if os.path.isfile(args.output_file):
try:
bed_contours = load_contours(args.output_file, as_ndarry=False)
except:
print(f"JSON file {args.output_file} is corrupted! Create a new one.")
bed_contours = {}
else:
bed_contours = {}
class VisualMode(Enum):
DRAW_CONTOURS = 1
HIDE_BED = 2
window_name = "Bed Removal"
for i, (_, mu_map) in enumerate(dataset):
_id = str(int(dataset.table.loc[i, "id"]))
if _id in bed_contours:
print(f"Skip {_id} because file already contains these contours")
continue
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
print(f"Image {str(i + 1):>{len(str(len(dataset)))}}/{len(dataset)}", end="\r")
# select the center slice for display (the bed location is constant over all slices)
mu_map = mu_map[mu_map.shape[0] // 2]
# save the points of the contour in a list and defined a mouse callback
points = []
def mouse_callback(event, x, y, flags, param):
if event == cv.EVENT_LBUTTONUP:
points.append((x, y))
# create a window for display
cv.namedWindow(window_name, cv.WINDOW_NORMAL)
cv.resizeWindow(window_name, 1024, 1024)
cv.setMouseCallback(window_name, mouse_callback)
# set initial visual mode
visual_mode = VisualMode.DRAW_CONTOURS
while True:
# compute image to display
to_show = to_grayscale(mu_map)
if visual_mode == VisualMode.DRAW_CONTOURS:
# draw lines between all points
for p1, p2 in zip(points[:-1], points[1:]):
to_show = cv.line(to_show, p1, p2, color=COLOR_WHITE, thickness=1)
# close the contour
if len(points) > 0:
to_show = cv.line(
to_show, points[0], points[-1], color=COLOR_WHITE, thickness=1
)
# draw all points as circles
for point in points:
to_show = cv.circle(
to_show, point, radius=2, color=COLOR_BLACK, thickness=-1
)
to_show = cv.circle(
to_show, point, radius=2, color=COLOR_WHITE, thickness=1
)
else:
# eliminate area inside the contour
_points = np.array(points).astype(int)
to_show = cv.drawContours(
to_show, [_points], -1, COLOR_BLACK, thickness=-1
)
# visualize image and handle inputs
cv.imshow(window_name, to_show)
key = cv.waitKey(100)
if key == ord("q"):
# write current contours to output file
with open(args.output_file, mode="w") as f:
f.write(json.dumps(bed_contours, indent=2, sort_keys=True))
exit(0)
elif key == ord("d"):
points = points[:-1]
elif key == ord("n"):
break
elif key == ord("v"):
visual_mode = (
VisualMode.DRAW_CONTOURS
if visual_mode == VisualMode.HIDE_BED
else VisualMode.HIDE_BED
)
# remove current window
cv.destroyWindow(window_name)
# save current contour in dict
# write contours to output file
with open(args.output_file, mode="w") as f:
f.write(json.dumps(bed_contours, indent=2, sort_keys=True))