OTVision 0.5.3__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. OTVision/__init__.py +30 -0
  2. OTVision/application/__init__.py +0 -0
  3. OTVision/application/configure_logger.py +23 -0
  4. OTVision/application/detect/__init__.py +0 -0
  5. OTVision/application/detect/get_detect_cli_args.py +9 -0
  6. OTVision/application/detect/update_detect_config_with_cli_args.py +95 -0
  7. OTVision/application/get_config.py +25 -0
  8. OTVision/config.py +754 -0
  9. OTVision/convert/__init__.py +0 -0
  10. OTVision/convert/convert.py +318 -0
  11. OTVision/dataformat.py +70 -0
  12. OTVision/detect/__init__.py +0 -0
  13. OTVision/detect/builder.py +48 -0
  14. OTVision/detect/cli.py +166 -0
  15. OTVision/detect/detect.py +296 -0
  16. OTVision/detect/otdet.py +103 -0
  17. OTVision/detect/plugin_av/__init__.py +0 -0
  18. OTVision/detect/plugin_av/rotate_frame.py +37 -0
  19. OTVision/detect/yolo.py +277 -0
  20. OTVision/domain/__init__.py +0 -0
  21. OTVision/domain/cli.py +42 -0
  22. OTVision/helpers/__init__.py +0 -0
  23. OTVision/helpers/date.py +26 -0
  24. OTVision/helpers/files.py +538 -0
  25. OTVision/helpers/formats.py +139 -0
  26. OTVision/helpers/log.py +131 -0
  27. OTVision/helpers/machine.py +71 -0
  28. OTVision/helpers/video.py +54 -0
  29. OTVision/track/__init__.py +0 -0
  30. OTVision/track/iou.py +282 -0
  31. OTVision/track/iou_util.py +140 -0
  32. OTVision/track/preprocess.py +451 -0
  33. OTVision/track/track.py +422 -0
  34. OTVision/transform/__init__.py +0 -0
  35. OTVision/transform/get_homography.py +156 -0
  36. OTVision/transform/reference_points_picker.py +462 -0
  37. OTVision/transform/transform.py +352 -0
  38. OTVision/version.py +13 -0
  39. OTVision/view/__init__.py +0 -0
  40. OTVision/view/helpers/OTC.ico +0 -0
  41. OTVision/view/view.py +90 -0
  42. OTVision/view/view_convert.py +128 -0
  43. OTVision/view/view_detect.py +146 -0
  44. OTVision/view/view_helpers.py +417 -0
  45. OTVision/view/view_track.py +131 -0
  46. OTVision/view/view_transform.py +140 -0
  47. otvision-0.5.3.dist-info/METADATA +47 -0
  48. otvision-0.5.3.dist-info/RECORD +50 -0
  49. otvision-0.5.3.dist-info/WHEEL +4 -0
  50. otvision-0.5.3.dist-info/licenses/LICENSE +674 -0
@@ -0,0 +1,146 @@
1
+ """
2
+ OTVision gui module for detect.py
3
+ """
4
+
5
+ # Copyright (C) 2022 OpenTrafficCam Contributors
6
+ # <https://github.com/OpenTrafficCam
7
+ # <team@opentrafficcam.org>
8
+ #
9
+ # This program is free software: you can redistribute it and/or modify
10
+ # it under the terms of the GNU General Public License as published by
11
+ # the Free Software Foundation, either version 3 of the License, or
12
+ # (at your option) any later version.
13
+ #
14
+ # This program is distributed in the hope that it will be useful,
15
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
16
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17
+ # GNU General Public License for more details.
18
+ #
19
+ # You should have received a copy of the GNU General Public License
20
+ # along with this program. If not, see <https://www.gnu.org/licenses/>.
21
+
22
+
23
+ import logging
24
+ import tkinter as tk
25
+ import tkinter.ttk as ttk
26
+
27
+ from OTVision.config import CONFIG, PAD
28
+ from OTVision.detect.detect import main as detect
29
+ from OTVision.helpers.files import get_files, replace_filetype
30
+ from OTVision.helpers.log import LOGGER_NAME
31
+ from OTVision.view.view_helpers import FrameRun
32
+
33
+ log = logging.getLogger(LOGGER_NAME)
34
+
35
+
36
+ class FrameDetect(tk.LabelFrame):
37
+ def __init__(self, **kwargs):
38
+ super().__init__(**kwargs)
39
+ self.frame_options = FrameDetectOptions(
40
+ master=self
41
+ ) # Always name this "frame_options"
42
+ self.frame_options.pack(**PAD, fill="both", expand=1, anchor="n")
43
+ self.frame_run = FrameRunDetection(master=self)
44
+ self.frame_run.pack(**PAD, side="left", fill="both", expand=1, anchor="s")
45
+
46
+
47
+ class FrameDetectOptions(tk.Frame):
48
+ def __init__(self, **kwargs):
49
+ super().__init__(**kwargs)
50
+ # Weights
51
+ self.label_weights = tk.Label(master=self, text="Weights")
52
+ self.label_weights.grid(row=0, column=0, sticky="w")
53
+ self.combo_weights = ttk.Combobox(
54
+ master=self, values=CONFIG["DETECT"]["YOLO"]["AVAILABLEWEIGHTS"], width=8
55
+ )
56
+ self.combo_weights.grid(row=0, column=1, sticky="w")
57
+ self.combo_weights.set(CONFIG["DETECT"]["YOLO"]["WEIGHTS"])
58
+ # Confidence
59
+ self.label_conf = tk.Label(master=self, text="Confidence")
60
+ self.label_conf.grid(row=1, column=0, sticky="sw")
61
+ self.scale_conf = tk.Scale(
62
+ master=self, from_=0, to=1, resolution=0.01, orient="horizontal"
63
+ )
64
+ self.scale_conf.grid(row=1, column=1, sticky="w")
65
+ self.scale_conf.set(CONFIG["DETECT"]["YOLO"]["CONF"])
66
+ # IOU
67
+ self.label_iou = tk.Label(master=self, text="IOU")
68
+ self.label_iou.grid(row=2, column=0, sticky="sw")
69
+ self.scale_iou = tk.Scale(
70
+ master=self, from_=0, to=1, resolution=0.01, orient="horizontal"
71
+ )
72
+ self.scale_iou.grid(row=2, column=1, sticky="w")
73
+ self.scale_iou.set(CONFIG["DETECT"]["YOLO"]["IOU"])
74
+ # Image size
75
+ self.label_imgsize = tk.Label(master=self, text="Image size")
76
+ self.label_imgsize.grid(row=3, column=0, sticky="sw")
77
+ self.scale_imgsize = tk.Scale(
78
+ master=self, from_=100, to=1000, resolution=10, orient="horizontal"
79
+ )
80
+ self.scale_imgsize.grid(row=3, column=1, sticky="w")
81
+ self.scale_imgsize.set(CONFIG["DETECT"]["YOLO"]["IMGSIZE"])
82
+ # Chunk size
83
+ self.label_chunksize = tk.Label(master=self, text="Chunk size")
84
+ self.label_chunksize.grid(row=4, column=0, sticky="sw")
85
+ self.scale_chunksize = tk.Scale(
86
+ master=self, from_=1, to=20, resolution=1, orient="horizontal"
87
+ )
88
+ self.scale_chunksize.grid(row=4, column=1, sticky="w")
89
+ self.scale_chunksize.set(CONFIG["DETECT"]["YOLO"]["CHUNKSIZE"])
90
+ # Normalized
91
+ self.checkbutton_normalized_var = tk.BooleanVar()
92
+ self.checkbutton_normalized = tk.Checkbutton(
93
+ master=self, text="Normalized", variable=self.checkbutton_normalized_var
94
+ )
95
+ self.checkbutton_normalized.grid(row=5, column=0, columnspan=2, sticky="w")
96
+ # self.checkbutton_overwrite.select() # BUG: Still selected
97
+ # Overwrite
98
+ self.checkbutton_overwrite_var = tk.BooleanVar()
99
+ self.checkbutton_overwrite = tk.Checkbutton(
100
+ master=self, text="Overwrite", variable=self.checkbutton_overwrite_var
101
+ )
102
+ self.checkbutton_overwrite.grid(row=6, column=0, columnspan=2, sticky="w")
103
+ if CONFIG["DETECT"]["OVERWRITE"]:
104
+ self.checkbutton_overwrite.select()
105
+
106
+
107
+ class FrameRunDetection(FrameRun):
108
+ def __init__(self, **kwargs):
109
+ super().__init__(**kwargs)
110
+ self.button_run.bind("<ButtonRelease-1>", self.run)
111
+ if CONFIG["DETECT"]["RUN_CHAINED"]:
112
+ self.checkbutton_run_chained.select()
113
+
114
+ def run(self, event):
115
+ input_filetype = f".{self.master.master.frame_files.combo_vid_filetype.get()}"
116
+
117
+ files = replace_filetype(
118
+ files=self.master.master.frame_files.get_tree_files(),
119
+ new_filetype=input_filetype,
120
+ )
121
+ files = get_files(
122
+ paths=files,
123
+ filetypes=[input_filetype],
124
+ )
125
+
126
+ weights = self.master.frame_options.combo_weights.get()
127
+ conf = self.master.frame_options.scale_conf.get()
128
+ iou = self.master.frame_options.scale_iou.get()
129
+ size = self.master.frame_options.scale_imgsize.get()
130
+ chunksize = self.master.frame_options.scale_chunksize.get()
131
+ normalized = self.master.frame_options.checkbutton_normalized_var.get()
132
+ overwrite = self.master.frame_options.checkbutton_overwrite_var.get()
133
+ log.info("Call detect from GUI")
134
+ detect(
135
+ paths=files,
136
+ filetypes=[input_filetype],
137
+ weights=weights,
138
+ conf=conf,
139
+ iou=iou,
140
+ size=size,
141
+ chunksize=chunksize,
142
+ normalized=normalized,
143
+ overwrite=overwrite,
144
+ )
145
+
146
+ self.master.master.frame_files.update_files_dict()
@@ -0,0 +1,417 @@
1
+ """
2
+ OTVision helper gui module
3
+ """
4
+
5
+ # Copyright (C) 2022 OpenTrafficCam Contributors
6
+ # <https://github.com/OpenTrafficCam
7
+ # <team@opentrafficcam.org>
8
+ #
9
+ # This program is free software: you can redistribute it and/or modify
10
+ # it under the terms of the GNU General Public License as published by
11
+ # the Free Software Foundation, either version 3 of the License, or
12
+ # (at your option) any later version.
13
+ #
14
+ # This program is distributed in the hope that it will be useful,
15
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
16
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17
+ # GNU General Public License for more details.
18
+ #
19
+ # You should have received a copy of the GNU General Public License
20
+ # along with this program. If not, see <https://www.gnu.org/licenses/>.
21
+
22
+
23
+ import logging
24
+ import tkinter as tk
25
+ import tkinter.ttk as ttk
26
+ from pathlib import Path
27
+ from tkinter import filedialog
28
+
29
+ from OTVision.config import CONFIG, PAD
30
+ from OTVision.helpers.files import get_files
31
+ from OTVision.helpers.log import LOGGER_NAME
32
+
33
+ log = logging.getLogger(LOGGER_NAME)
34
+
35
+
36
+ class FrameFileTree(tk.LabelFrame):
37
+ def __init__(self, **kwargs):
38
+ super().__init__(**kwargs)
39
+
40
+ # File dict
41
+ self.files_dict = {}
42
+ self.vid_filetype = CONFIG["DEFAULT_FILETYPE"]["VID"].replace(".", "")
43
+
44
+ # Frame for controls
45
+ self.frame_controls = tk.Frame(master=self)
46
+ self.frame_controls.pack()
47
+
48
+ # Search subdir checkbox
49
+ self.checkbutton_subdir_var = tk.BooleanVar()
50
+ self.checkbutton_subdir = tk.Checkbutton(
51
+ master=self.frame_controls,
52
+ text="Search subfolders",
53
+ variable=self.checkbutton_subdir_var,
54
+ )
55
+ self.checkbutton_subdir.grid(row=0, column=0, sticky="w")
56
+ self.checkbutton_subdir_var.set(CONFIG["SEARCH_SUBDIRS"])
57
+
58
+ # File type dropdowns
59
+ self.label_vid_filetype = tk.Label(
60
+ master=self.frame_controls, text="Video file type"
61
+ )
62
+ self.label_vid_filetype.grid(row=0, column=5, sticky="w")
63
+ self.combo_vid_filetype = ttk.Combobox(
64
+ master=self.frame_controls,
65
+ values=[str.replace(".", "") for str in CONFIG["FILETYPES"]["VID"]],
66
+ width=5,
67
+ )
68
+ self.combo_vid_filetype.grid(row=0, column=6, sticky="w")
69
+ self.combo_vid_filetype.bind("<<ComboboxSelected>>", self.set_vid_filetype)
70
+ self.combo_vid_filetype.set(self.vid_filetype)
71
+
72
+ # File buttons
73
+ self.button_add_folder = tk.Button(
74
+ master=self.frame_controls, text="Add folder"
75
+ )
76
+ self.button_add_folder.bind("<ButtonRelease-1>", self.add_dirs)
77
+ self.button_add_folder.grid(row=0, column=1, sticky="ew")
78
+ self.button_add_file = tk.Button(master=self.frame_controls, text="Add files")
79
+ self.button_add_file.bind("<ButtonRelease-1>", self.add_files)
80
+ self.button_add_file.grid(row=0, column=2, sticky="ew")
81
+ self.button_rem_sel = tk.Button(
82
+ master=self.frame_controls, text="Remove selected"
83
+ )
84
+ self.button_rem_sel.bind("<ButtonRelease-1>", self.remove_selected)
85
+ self.button_rem_sel.grid(row=0, column=3, sticky="ew")
86
+ self.button_rem_all = tk.Button(master=self.frame_controls, text="Remove all")
87
+ self.button_rem_all.bind("<ButtonRelease-1>", self.remove_all)
88
+ self.button_rem_all.grid(row=0, column=4, sticky="ew")
89
+
90
+ # Frame for treeview
91
+ self.frame_tree = tk.Frame(master=self)
92
+ self.frame_tree.pack(
93
+ **PAD, fill="both", expand=True
94
+ ) # BUG: Treeview fills only to the left
95
+
96
+ # Files treeview
97
+ self.tree_files = ttk.Treeview(master=self.frame_tree, height=5)
98
+ self.tree_files.bind("<ButtonRelease-3>", self.deselect_tree_files)
99
+ tree_files_cols = {
100
+ "#0": "File",
101
+ "h264": "h264",
102
+ "video": self.vid_filetype,
103
+ "otdet": "otdet",
104
+ "ottrk": "ottrk",
105
+ "otrfpts": "otrfpts",
106
+ }
107
+ self.tree_files["columns"] = tuple(
108
+ {k: v for k, v in tree_files_cols.items() if k != "#0"}.keys()
109
+ )
110
+ for tree_files_col_id, tree_files_col_text in tree_files_cols.items():
111
+ if tree_files_col_id == "#0":
112
+ anchor = "w"
113
+ width = 2000
114
+ minwidth = 200
115
+ stretch = True
116
+ self.tree_files.column(
117
+ tree_files_col_id,
118
+ minwidth=minwidth,
119
+ stretch=stretch,
120
+ anchor=anchor,
121
+ )
122
+ else:
123
+ anchor = "center"
124
+ width = 45
125
+ minwidth = 45
126
+ stretch = False
127
+ self.tree_files.column(
128
+ tree_files_col_id,
129
+ width=width,
130
+ stretch=stretch,
131
+ anchor=anchor,
132
+ )
133
+ self.tree_files.heading(
134
+ tree_files_col_id, text=tree_files_col_text, anchor=anchor
135
+ )
136
+ self.tree_files.pack(side="left", fill="both", expand=True)
137
+
138
+ # Scrollbar for treeview
139
+ self.tree_scrollbar = ttk.Scrollbar(
140
+ master=self.frame_tree, orient="vertical", command=self.tree_files.yview
141
+ )
142
+ self.tree_scrollbar.pack(side="right", fill="y")
143
+ self.tree_files.configure(yscrollcommand=self.tree_scrollbar.set)
144
+
145
+ def set_vid_filetype(self, event):
146
+ self.vid_filetype = self.combo_vid_filetype.get()
147
+ for path in self.files_dict.keys():
148
+ self.update_files_dict_values(path)
149
+ self.tree_files.heading("video", text=self.vid_filetype)
150
+ self.update_tree_files()
151
+
152
+ def add_dirs(self, event):
153
+ new_dir = filedialog.askdirectory(title="Select a folder", mustexist=True)
154
+ # NOTE: h264 is only included on Windows for now
155
+ new_files = get_files(
156
+ [Path(new_dir)],
157
+ filetypes=[
158
+ ".h264",
159
+ f".{self.vid_filetype}",
160
+ CONFIG["DEFAULT_FILETYPE"]["DETECT"],
161
+ ],
162
+ search_subdirs=self.checkbutton_subdir_var.get(),
163
+ )
164
+
165
+ unique_new_files = []
166
+ for file in new_files:
167
+ if file.with_suffix("") not in [
168
+ f.with_suffix("") for f in unique_new_files
169
+ ]:
170
+ unique_new_files.append(file)
171
+ self.add_to_files_dict(unique_new_files)
172
+
173
+ def add_files(self, event):
174
+ # Show filedialog
175
+ new_paths_str = list(
176
+ filedialog.askopenfilenames(
177
+ title="Select one or multiple files",
178
+ # NOTE: h264 is only included on Windows for now
179
+ filetypes=[
180
+ (".h264", ".h264"),
181
+ (".mp4", ".mp4"),
182
+ (".otdet", ".otdet"),
183
+ (".ottrk", ".ottrk"),
184
+ ("all files", "*.*"),
185
+ ],
186
+ )
187
+ )
188
+ # Check paths
189
+ new_paths = [Path(path_str) for path_str in new_paths_str]
190
+ new_paths = get_files(new_paths)
191
+ self.add_to_files_dict(new_paths)
192
+
193
+ def remove_selected(self, event):
194
+ for item in self.tree_files.selection():
195
+ del self.files_dict[Path(self.tree_files.item(item)["text"])]
196
+ self.update_tree_files()
197
+
198
+ def remove_all(self, event):
199
+ self.files_dict = {}
200
+ self.update_tree_files()
201
+
202
+ def add_to_files_dict(self, paths):
203
+ for path in paths:
204
+ self.files_dict[path] = {}
205
+ self.update_files_dict_values(path)
206
+ self.update_tree_files()
207
+
208
+ def update_files_dict(self):
209
+ for path in self.files_dict.keys():
210
+ self.update_files_dict_values(path)
211
+ self.update_tree_files()
212
+
213
+ def update_files_dict_values(self, path):
214
+ TRUE_SYMBOL = "\u2705" # "\u2713" # "\u2714"
215
+ FALSE_SYMBOL = "\u274E" # "\u2717" # "\u2718"
216
+ self.files_dict[path]["filename"] = Path(path).stem
217
+ self.files_dict[path]["h264"] = (
218
+ TRUE_SYMBOL if Path(path).with_suffix(".h264").is_file() else FALSE_SYMBOL
219
+ )
220
+ self.files_dict[path]["video"] = (
221
+ TRUE_SYMBOL
222
+ if Path(path).with_suffix(f".{self.vid_filetype}").is_file()
223
+ else FALSE_SYMBOL
224
+ )
225
+ self.files_dict[path]["otdet"] = (
226
+ TRUE_SYMBOL if Path(path).with_suffix(".otdet").is_file() else FALSE_SYMBOL
227
+ )
228
+ self.files_dict[path]["ottrk"] = (
229
+ TRUE_SYMBOL if Path(path).with_suffix(".ottrk").is_file() else FALSE_SYMBOL
230
+ )
231
+ self.files_dict[path]["otrfpts"] = (
232
+ TRUE_SYMBOL
233
+ if Path(path).with_suffix(".otrfpts").is_file()
234
+ else FALSE_SYMBOL
235
+ )
236
+
237
+ def update_tree_files(self):
238
+ self.tree_files.delete(*self.tree_files.get_children())
239
+ for path, file_values in self.files_dict.items():
240
+ self.tree_files.insert(
241
+ parent="",
242
+ index="end",
243
+ text=str(path),
244
+ values=(
245
+ file_values["h264"],
246
+ file_values["video"],
247
+ file_values["otdet"],
248
+ file_values["ottrk"],
249
+ file_values["otrfpts"],
250
+ ),
251
+ )
252
+ self.update_other_gui_parts()
253
+
254
+ def update_other_gui_parts(self):
255
+ # Activate/deactivate buttons in FrameTransform
256
+ if self.files_dict:
257
+ self.master.frame_transform.frame_options.button_choose_refpts["state"] = (
258
+ tk.NORMAL
259
+ )
260
+ self.master.frame_transform.frame_options.button_click_refpts["state"] = (
261
+ tk.NORMAL
262
+ )
263
+ else:
264
+ self.master.frame_transform.frame_options.button_choose_refpts["state"] = (
265
+ tk.DISABLED
266
+ )
267
+ self.master.frame_transform.frame_options.button_click_refpts["state"] = (
268
+ tk.DISABLED
269
+ )
270
+
271
+ def get_tree_files(self):
272
+ return [
273
+ Path(self.tree_files.item(item)["text"])
274
+ for item in self.tree_files.get_children()
275
+ ]
276
+
277
+ def get_selected_files(self):
278
+ selected_files = [
279
+ self.tree_files.item(item)["text"] for item in self.tree_files.selection()
280
+ ]
281
+ log.debug("Selected files:")
282
+ log.debug(selected_files)
283
+ return selected_files
284
+
285
+ def deselect_tree_files(self, events):
286
+ for item in self.tree_files.selection():
287
+ self.tree_files.selection_remove(item)
288
+
289
+
290
+ class FrameFiles(tk.LabelFrame):
291
+ def __init__(self, filecategory, default_filetype, filetypes=None, **kwargs):
292
+ super().__init__(**kwargs)
293
+ # File type
294
+ self.default_filetype = default_filetype
295
+ self.filetype = self.default_filetype
296
+ if filetypes:
297
+ self.label_filetype = tk.Label(master=self, text="Input file type")
298
+ self.label_filetype.grid(row=0, column=0, sticky="w")
299
+ self.combo_filetype = ttk.Combobox(
300
+ master=self,
301
+ values=filetypes,
302
+ )
303
+ self.combo_filetype.grid(row=0, column=1, sticky="w")
304
+ self.combo_filetype.bind("<<ComboboxSelected>>", self.set_filetype)
305
+ self.combo_filetype.set(default_filetype)
306
+ # File list
307
+ self.file_list = [] # ?: Add test data?
308
+ self.filecategory = filecategory
309
+ self.filetypes = filetypes
310
+ # File buttons
311
+ self.button_add_folder = tk.Button(master=self, text="Add folder")
312
+ self.button_add_folder.bind("<ButtonRelease-1>", self.add_dirs)
313
+ self.button_add_folder.grid(row=1, column=0, sticky="ew")
314
+ self.button_add_file = tk.Button(master=self, text="Add files")
315
+ self.button_add_file.bind("<ButtonRelease-1>", self.add_files)
316
+ self.button_add_file.grid(row=1, column=1, sticky="ew")
317
+ self.button_rem_sel = tk.Button(master=self, text="Remove selected")
318
+ self.button_rem_sel.bind("<ButtonRelease-1>", self.remove_selected)
319
+ self.button_rem_sel.grid(row=1, column=2, sticky="ew")
320
+ self.button_rem_all = tk.Button(master=self, text="Remove all")
321
+ self.button_rem_all.bind("<ButtonRelease-1>", self.remove_all)
322
+ self.button_rem_all.grid(row=1, column=3, sticky="ew")
323
+ # File list
324
+ self.listbox_files = tk.Listbox(master=self, width=150, selectmode="extended")
325
+ self.listbox_files.yview()
326
+ self.listbox_files.grid(row=2, column=0, columnspan=4, sticky="ew")
327
+
328
+ def set_filetype(self, event):
329
+ self.filetype = self.combo_filetype.get()
330
+
331
+ def get_listbox_files(self):
332
+ return self.listbox_files.get(first=0, last=self.listbox_files.size() - 1)
333
+
334
+ def get_listbox_file_indices(self):
335
+ return self.listbox_files.get(first=0, last=self.listbox_files.size() - 1)
336
+
337
+ def add_files(self, event):
338
+ new_paths_str = list(
339
+ filedialog.askopenfilenames(
340
+ title=f"Select one or multiple {self.filecategory}",
341
+ filetypes=[
342
+ (self.filecategory, self.filetype),
343
+ ("all files", "*.*"),
344
+ ],
345
+ )
346
+ )
347
+ new_paths = [Path(path_str) for path_str in new_paths_str]
348
+ new_paths = get_files(new_paths, [self.filetype])
349
+ self.add_to_listbox(new_paths)
350
+
351
+ def add_dirs(self, event):
352
+ new_dir = filedialog.askdirectory(title="Select a folder")
353
+ new_paths = get_files([Path(new_dir)], [self.filetype])
354
+ self.add_to_listbox(new_paths)
355
+
356
+ def add_to_listbox(self, new_paths):
357
+ for new_path in new_paths:
358
+ if new_path not in self.get_listbox_files():
359
+ self.listbox_files.insert("end", new_path)
360
+
361
+ def remove_selected(self, event):
362
+ selection = self.listbox_files.curselection()
363
+ self.remove_from_listbox(selection)
364
+
365
+ def remove_all(self, event):
366
+ selection = range(self.listbox_files.size())
367
+ self.remove_from_listbox(selection)
368
+
369
+ def remove_from_listbox(self, selection):
370
+ for delta, selected_file in enumerate(selection):
371
+ file_to_remove = selected_file - delta
372
+ self.listbox_files.delete(first=file_to_remove)
373
+
374
+ def _debug(self, event):
375
+ log.debug(event)
376
+
377
+
378
+ class FrameRun(tk.Frame):
379
+ def __init__(self, button_label="Run!", **kwargs):
380
+ super().__init__(**kwargs)
381
+ # Run
382
+ self.button_run = tk.Button(master=self, text=button_label)
383
+ self.button_run.pack(fill="both")
384
+ # Include in chained run
385
+ self.checkbutton_run_chained_var = tk.BooleanVar()
386
+ self.checkbutton_run_chained = tk.Checkbutton(
387
+ master=self,
388
+ text="Include in chained run",
389
+ variable=self.checkbutton_run_chained_var,
390
+ )
391
+ self.checkbutton_run_chained.pack()
392
+ # self.checkbutton_run_chained.select()
393
+ # # Progress bar # TODO
394
+ # self.progress = ttk.Progressbar(master=self)
395
+ # self.progress.grid(row=1, column=0, sticky="ew")
396
+
397
+
398
+ class FrameRunChained(tk.LabelFrame):
399
+ def __init__(self, button_label="Run chained!", **kwargs):
400
+ super().__init__(**kwargs)
401
+ # Run
402
+ self.button_run = tk.Button(master=self, text=button_label)
403
+ self.button_run.pack(**PAD, fill="x", expand=1)
404
+ self.button_run.bind("<ButtonRelease-1>", self.run)
405
+ # # Progress bar # TODO
406
+ # self.progress = ttk.Progressbar(master=self)
407
+ # self.progress.grid(row=1, column=0, sticky="ew")
408
+
409
+ def run(self, event):
410
+ if self.master.frame_convert.frame_run.checkbutton_run_chained_var.get():
411
+ self.master.frame_convert.frame_run.run(event)
412
+ if self.master.frame_detect.frame_run.checkbutton_run_chained_var.get():
413
+ self.master.frame_detect.frame_run.run(event)
414
+ if self.master.frame_track.frame_run.checkbutton_run_chained_var.get():
415
+ self.master.frame_track.frame_run.run(event)
416
+ if self.master.frame_transform.frame_run.checkbutton_run_chained_var.get():
417
+ self.master.frame_transform.frame_run.run(event)
@@ -0,0 +1,131 @@
1
+ """
2
+ OTVision gui module for track.py
3
+ """
4
+
5
+ # Copyright (C) 2022 OpenTrafficCam Contributors
6
+ # <https://github.com/OpenTrafficCam
7
+ # <team@opentrafficcam.org>
8
+ #
9
+ # This program is free software: you can redistribute it and/or modify
10
+ # it under the terms of the GNU General Public License as published by
11
+ # the Free Software Foundation, either version 3 of the License, or
12
+ # (at your option) any later version.
13
+ #
14
+ # This program is distributed in the hope that it will be useful,
15
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
16
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17
+ # GNU General Public License for more details.
18
+ #
19
+ # You should have received a copy of the GNU General Public License
20
+ # along with this program. If not, see <https://www.gnu.org/licenses/>.
21
+
22
+
23
+ import logging
24
+ import tkinter as tk
25
+
26
+ from OTVision.config import CONFIG, PAD
27
+ from OTVision.helpers.files import get_files, replace_filetype
28
+ from OTVision.helpers.log import LOGGER_NAME
29
+ from OTVision.track.track import main as track
30
+ from OTVision.view.view_helpers import FrameRun
31
+
32
+ log = logging.getLogger(LOGGER_NAME)
33
+
34
+
35
+ class FrameTrack(tk.LabelFrame):
36
+ def __init__(self, **kwargs):
37
+ super().__init__(**kwargs)
38
+ self.frame_options = FrameTrackOptions(
39
+ master=self
40
+ ) # Always name this "frame_options"
41
+ self.frame_options.pack(**PAD, fill="both", expand=1, anchor="n")
42
+ self.frame_run = FrameRunTracking(master=self)
43
+ self.frame_run.pack(**PAD, side="left", fill="both", expand=1, anchor="s")
44
+
45
+
46
+ class FrameTrackOptions(tk.Frame):
47
+ def __init__(self, **kwargs):
48
+ super().__init__(**kwargs)
49
+ # Sigma l
50
+ self.label_sigma_l = tk.Label(master=self, text="sigma l")
51
+ self.label_sigma_l.grid(row=0, column=0, sticky="sw")
52
+ self.scale_sigma_l = tk.Scale(
53
+ master=self, from_=0, to=1, resolution=0.01, orient="horizontal"
54
+ )
55
+ self.scale_sigma_l.grid(row=0, column=1, sticky="w")
56
+ self.scale_sigma_l.set(CONFIG["TRACK"]["IOU"]["SIGMA_L"])
57
+ # Sigma h
58
+ self.label_sigma_h = tk.Label(master=self, text="sigma h")
59
+ self.label_sigma_h.grid(row=1, column=0, sticky="sw")
60
+ self.scale_sigma_h = tk.Scale(
61
+ master=self, from_=0, to=1, resolution=0.01, orient="horizontal"
62
+ )
63
+ self.scale_sigma_h.grid(row=1, column=1, sticky="w")
64
+ self.scale_sigma_h.set(CONFIG["TRACK"]["IOU"]["SIGMA_H"])
65
+ # Sigma IOU
66
+ self.label_sigma_iou = tk.Label(master=self, text="sigma IOU")
67
+ self.label_sigma_iou.grid(row=2, column=0, sticky="sw")
68
+ self.scale_sigma_iou = tk.Scale(
69
+ master=self, from_=0, to=1, resolution=0.01, orient="horizontal"
70
+ )
71
+ self.scale_sigma_iou.grid(row=2, column=1, sticky="w")
72
+ self.scale_sigma_iou.set(CONFIG["TRACK"]["IOU"]["SIGMA_IOU"])
73
+ # t min
74
+ self.label_t_min = tk.Label(master=self, text="t min")
75
+ self.label_t_min.grid(row=3, column=0, sticky="sw")
76
+ self.scale_t_min = tk.Scale(
77
+ master=self, from_=0, to=20, resolution=1, orient="horizontal"
78
+ )
79
+ self.scale_t_min.grid(row=3, column=1, sticky="w")
80
+ self.scale_t_min.set(CONFIG["TRACK"]["IOU"]["T_MIN"])
81
+ # t miss max
82
+ self.label_t_miss_max = tk.Label(master=self, text="t miss max")
83
+ self.label_t_miss_max.grid(row=4, column=0, sticky="sw")
84
+ self.scale_t_miss_max = tk.Scale(
85
+ master=self, from_=0, to=100, resolution=1, orient="horizontal"
86
+ )
87
+ self.scale_t_miss_max.grid(row=4, column=1, sticky="w")
88
+ self.scale_t_miss_max.set(CONFIG["TRACK"]["IOU"]["T_MISS_MAX"])
89
+ # Overwrite
90
+ self.checkbutton_overwrite_var = tk.BooleanVar()
91
+ self.checkbutton_overwrite = tk.Checkbutton(
92
+ master=self, text="Overwrite", variable=self.checkbutton_overwrite_var
93
+ )
94
+ self.checkbutton_overwrite.grid(row=6, column=0, columnspan=2, sticky="w")
95
+ if CONFIG["TRACK"]["OVERWRITE"]:
96
+ self.checkbutton_overwrite.select()
97
+
98
+
99
+ class FrameRunTracking(FrameRun):
100
+ def __init__(self, **kwargs):
101
+ super().__init__(**kwargs)
102
+ self.button_run.bind("<ButtonRelease-1>", self.run)
103
+ if CONFIG["TRACK"]["RUN_CHAINED"]:
104
+ self.checkbutton_run_chained.select()
105
+
106
+ def run(self, event):
107
+ files = replace_filetype(
108
+ files=self.master.master.frame_files.get_tree_files(),
109
+ new_filetype=CONFIG["DEFAULT_FILETYPE"]["DETECT"],
110
+ )
111
+ files = get_files(
112
+ paths=files,
113
+ filetypes=[CONFIG["DEFAULT_FILETYPE"]["DETECT"]],
114
+ )
115
+ sigma_l = self.master.frame_options.scale_sigma_l.get()
116
+ sigma_h = self.master.frame_options.scale_sigma_h.get()
117
+ sigma_iou = self.master.frame_options.scale_sigma_iou.get()
118
+ t_min = self.master.frame_options.scale_t_min.get()
119
+ t_miss_max = self.master.frame_options.scale_t_miss_max.get()
120
+ overwrite = self.master.frame_options.checkbutton_overwrite_var.get()
121
+ log.info("Call track from GUI")
122
+ track(
123
+ paths=files,
124
+ sigma_l=sigma_l,
125
+ sigma_h=sigma_h,
126
+ sigma_iou=sigma_iou,
127
+ t_min=t_min,
128
+ t_miss_max=t_miss_max,
129
+ overwrite=overwrite,
130
+ )
131
+ self.master.master.frame_files.update_files_dict()