[timeline] more bins, use trackingdata and hightlight user-labeled frames
This commit is contained in:
parent
98900ff480
commit
6f4ac1136b
@ -5,9 +5,10 @@ from PySide6.QtCore import Qt
|
||||
from PySide6.QtWidgets import QWidget, QVBoxLayout, QSizePolicy, QLabel
|
||||
from PySide6.QtWidgets import QGraphicsView, QGraphicsScene, QGraphicsItem, QGraphicsRectItem, QGraphicsLineItem
|
||||
from PySide6.QtCore import Qt, QRectF, QRectF
|
||||
from PySide6.QtGui import QBrush, QColor, QPen, QFont
|
||||
from PySide6.QtGui import QBrush, QColor, QPen, QFont, QPainter
|
||||
|
||||
from fixtracks.utils.signals import DetectionTimelineSignals
|
||||
from fixtracks.utils.trackingdata import TrackingData
|
||||
|
||||
|
||||
class Window(QGraphicsRectItem):
|
||||
@ -40,24 +41,23 @@ class Window(QGraphicsRectItem):
|
||||
self.signals.windowMoved.emit()
|
||||
|
||||
def setWindow(self, newx:float, newwidth:float):
|
||||
def setWindow(self, newx: float, newwidth: float):
|
||||
"""
|
||||
Update the window to the specified range.
|
||||
Parameters
|
||||
----------
|
||||
newx : float
|
||||
The new x-coordinate of the window.
|
||||
newwidth : float
|
||||
The new width of the window.
|
||||
Returns
|
||||
-------
|
||||
None
|
||||
"""
|
||||
|
||||
"""
|
||||
Update the window to the specified range.
|
||||
Parameters
|
||||
----------
|
||||
newx : float
|
||||
The new x-coordinate of the window.
|
||||
newwidth : float
|
||||
The new width of the window.
|
||||
Returns
|
||||
-------
|
||||
None
|
||||
"""
|
||||
logging.debug("timeline.window: update window to range %.5f to %.5f", newx, newwidth)
|
||||
self._width = newwidth
|
||||
r = self.rect()
|
||||
self.setRect(newx, r.y(), self._width, r.height())
|
||||
self.paint()
|
||||
self.signals.windowMoved.emit()
|
||||
|
||||
def mouseMoveEvent(self, event):
|
||||
@ -86,40 +86,44 @@ class Window(QGraphicsRectItem):
|
||||
class DetectionTimeline(QWidget):
|
||||
signals = DetectionTimelineSignals()
|
||||
|
||||
def __init__(self, detectiondata=None, trackone_id=1, tracktwo_id=2, parent=None):
|
||||
def __init__(self, trackone_id=1, tracktwo_id=2, parent=None):
|
||||
super().__init__(parent)
|
||||
self._trackone = trackone_id
|
||||
self._tracktwo = tracktwo_id
|
||||
self._data = detectiondata
|
||||
self._data = None
|
||||
self._rangeStart = 0.0
|
||||
self._rangeStop = 0.005
|
||||
self._total_width = 2000
|
||||
self._stepCount = 300
|
||||
self._stepCount = 1000
|
||||
self._bg_brush = QBrush(QColor(20, 20, 20, 255))
|
||||
transparent_brush = QBrush(QColor(200, 200, 200, 64))
|
||||
self._white_pen = QPen(QColor.fromString("white"))
|
||||
self._white_pen.setWidth(0.1)
|
||||
self._t1_pen = QPen(QColor.fromString("orange"))
|
||||
self._t1_pen.setWidth(2)
|
||||
self._t1_pen.setWidth(1)
|
||||
self._t2_pen = QPen(QColor(0, 255, 0, 255))
|
||||
self._t2_pen.setWidth(2)
|
||||
self._t2_pen.setWidth(1)
|
||||
self._other_pen = QPen(QColor.fromString("red"))
|
||||
self._other_pen.setWidth(2)
|
||||
axis_pen = QPen(QColor.fromString("white"))
|
||||
axis_pen.setWidth(2)
|
||||
self._other_pen.setWidth(1)
|
||||
window_pen = QPen(QColor.fromString("white"))
|
||||
window_pen.setWidth(2)
|
||||
self._user_brush = QBrush(QColor.fromString("white"))
|
||||
user_pen = QPen(QColor.fromString("white"))
|
||||
user_pen.setWidth(2)
|
||||
|
||||
font = QFont()
|
||||
font.setPointSize(15)
|
||||
font.setBold(False)
|
||||
font.setBold(True)
|
||||
|
||||
self._window = Window(0, 0, 100, 60, axis_pen, transparent_brush)
|
||||
self._window = Window(0, 0, 100, 60, window_pen, transparent_brush)
|
||||
self._window.signals.windowMoved.connect(self.on_windowMoved)
|
||||
|
||||
self._scene = QGraphicsScene(QRectF(0, 0, self._total_width, 65.))
|
||||
self._scene = QGraphicsScene(QRectF(0, 0, self._total_width, 85.))
|
||||
self._scene.setBackgroundBrush(self._bg_brush)
|
||||
self._scene.addItem(self._window)
|
||||
|
||||
self._view = QGraphicsView()
|
||||
# self._view.setRenderHints(QPainter.RenderHint.Antialiasing | QPainter.RenderHint.SmoothPixmapTransform);
|
||||
# self._view.setRenderHints(QPainter.RenderHint.Antialiasing | QPainter.RenderHint.SmoothPixmapTransform)
|
||||
self._view.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding)
|
||||
self._view.setScene(self._scene)
|
||||
self._view.fitInView(self._scene.sceneRect(), aspectRadioMode=Qt.AspectRatioMode.KeepAspectRatio)
|
||||
@ -136,6 +140,10 @@ class DetectionTimeline(QWidget):
|
||||
other_label.setFont(font)
|
||||
other_label.setDefaultTextColor(self._other_pen.color())
|
||||
other_label.setPos(200, 50)
|
||||
user_label = self._scene.addText("user-labeled", font)
|
||||
user_label.setFont(font)
|
||||
user_label.setDefaultTextColor(user_pen.color())
|
||||
user_label.setPos(350, 50)
|
||||
|
||||
self._position_label = QLabel("")
|
||||
f = self._position_label.font()
|
||||
@ -146,13 +154,10 @@ class DetectionTimeline(QWidget):
|
||||
layout.addWidget(self._view)
|
||||
layout.addWidget(self._position_label, Qt.AlignmentFlag.AlignRight)
|
||||
self.setLayout(layout)
|
||||
|
||||
if self._data is not None:
|
||||
self.draw_coverage()
|
||||
# self.setMaximumHeight(100)
|
||||
# self.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Fixed)
|
||||
|
||||
def setDetectionData(self, data):
|
||||
def setData(self, data:TrackingData):
|
||||
self._data = data
|
||||
for i in self._scene.items():
|
||||
if isinstance(i, QGraphicsLineItem):
|
||||
@ -160,37 +165,29 @@ class DetectionTimeline(QWidget):
|
||||
self.draw_coverage()
|
||||
|
||||
def draw_coverage(self):
|
||||
# FIXME this must be disentangled. timeline should not have to deal with two different ways of data storage
|
||||
if isinstance(self._data, pd.DataFrame):
|
||||
maxframe = np.max(self._data.frame.values)
|
||||
|
||||
bins = np.linspace(0, maxframe, self._stepCount)
|
||||
pos = np.linspace(0, self._scene.width(), self._stepCount)
|
||||
track1_frames = self._data.frame.values[self._data.track == self._trackone]
|
||||
track2_frames = self._data.frame.values[self._data.track == self._tracktwo]
|
||||
other_frames = self._data.frame.values[(self._data.track != self._trackone) &
|
||||
(self._data.track != self._tracktwo)]
|
||||
elif isinstance(self._data, dict):
|
||||
if isinstance(self._data, TrackingData):
|
||||
maxframe = np.max(self._data["frame"])
|
||||
bins = np.linspace(0, maxframe, self._stepCount)
|
||||
pos = np.linspace(0, self._scene.width(), self._stepCount)
|
||||
pos = np.linspace(0, self._scene.width(), self._stepCount) # of the vertical dashes is this correct?
|
||||
track1_frames = self._data["frame"][self._data["track"] == self._trackone]
|
||||
track2_frames = self._data["frame"][self._data["track"] == self._tracktwo]
|
||||
other_frames = self._data["frame"][(self._data["track"] != self._trackone) &
|
||||
(self._data["track"] != self._tracktwo)]
|
||||
userlabeled = self._data["frame"][self._data["userlabeled"]]
|
||||
else:
|
||||
print("Data is not trackingdata")
|
||||
return
|
||||
t1_coverage, _ = np.histogram(track1_frames, bins=bins)
|
||||
t1_coverage = t1_coverage > 0
|
||||
t2_coverage, _ = np.histogram(track2_frames, bins=bins)
|
||||
t2_coverage = t2_coverage > 0
|
||||
other_coverage, _ = np.histogram(other_frames, bins=bins)
|
||||
other_coverage = other_coverage > 0
|
||||
labeled_coverage, _ = np.histogram(userlabeled, bins=bins)
|
||||
|
||||
for i in range(len(t1_coverage)-1):
|
||||
for i in range(len(bins)-1):
|
||||
if t1_coverage[i]: self._scene.addLine(pos[i], 0, pos[i], 15., pen=self._t1_pen)
|
||||
if t2_coverage[i]: self._scene.addLine(pos[i], 17, pos[i], 32., pen=self._t2_pen)
|
||||
if other_coverage[i]: self._scene.addLine(pos[i], 34, pos[i], 49., pen=self._other_pen)
|
||||
if other_coverage[i]: self._scene.addLine(pos[i], 34, pos[i], 49., pen=self._other_pen)
|
||||
if labeled_coverage[i]: self._scene.addEllipse(pos[i]-2, 52, 4, 4, brush=self._user_brush)
|
||||
|
||||
def updatePositionLabel(self):
|
||||
start = np.round(self._rangeStart * 100, 4)
|
||||
@ -207,6 +204,7 @@ class DetectionTimeline(QWidget):
|
||||
|
||||
def fit_scene_to_view(self):
|
||||
"""Scale the image to fit the QGraphicsView."""
|
||||
logging.debug("Timeline: fit scene to view")
|
||||
self._view.fitInView(self._scene.sceneRect(), Qt.KeepAspectRatio)
|
||||
|
||||
def resizeEvent(self, event):
|
||||
@ -289,6 +287,11 @@ def main():
|
||||
view.setWindowPos(0.0)
|
||||
print(view.windowBounds())
|
||||
|
||||
def as_dict(df):
|
||||
d = {c: df[c].values for c in df.columns}
|
||||
d["index"] = df.index.values
|
||||
return d
|
||||
|
||||
import pickle
|
||||
import numpy as np
|
||||
from PySide6.QtWidgets import QApplication, QPushButton, QHBoxLayout
|
||||
@ -298,13 +301,18 @@ def main():
|
||||
datafile = PACKAGE_ROOT / "data/merged_small.pkl"
|
||||
with open(datafile, "rb") as f:
|
||||
df = pickle.load(f)
|
||||
|
||||
data = TrackingData()
|
||||
data.setData(as_dict(df))
|
||||
data.setUserSelection(np.arange(0,100, 1))
|
||||
data.setAssignmentStatus(True)
|
||||
start_x = 0.1
|
||||
app = QApplication([])
|
||||
window = QWidget()
|
||||
window.setMinimumSize(200, 75)
|
||||
|
||||
view = DetectionTimeline(df)
|
||||
view = DetectionTimeline()
|
||||
view.setData(data)
|
||||
|
||||
fwdBtn = QPushButton(">>")
|
||||
fwdBtn.clicked.connect(lambda: fwd(0.5))
|
||||
zeroBtn = QPushButton("0->|")
|
||||
|
Loading…
Reference in New Issue
Block a user