added better logging to python

add day / winter / night switcher
This commit is contained in:
Alex Bezdieniezhnykh
2025-02-17 18:41:18 +02:00
parent 2ecbc9bfd4
commit c314268d1e
14 changed files with 204 additions and 103 deletions
+4 -4
View File
@@ -120,8 +120,8 @@ public partial class Annotator
TbFolder.Text = _appConfig.DirectoriesConfig.VideosDirectory; TbFolder.Text = _appConfig.DirectoriesConfig.VideosDirectory;
AnnotationClasses = new ObservableCollection<DetectionClass>(_appConfig.AnnotationConfig.AnnotationClasses); AnnotationClasses = new ObservableCollection<DetectionClass>(_appConfig.AnnotationConfig.AnnotationClasses);
LvClasses.ItemsSource = AnnotationClasses; LvClasses.DetectionDataGrid.ItemsSource = AnnotationClasses;
LvClasses.SelectedIndex = 0; LvClasses.SelectNum(0);
if (LvFiles.Items.IsEmpty) if (LvFiles.Items.IsEmpty)
BlinkHelp(HelpTexts.HelpTextsDict[HelpTextEnum.Initial]); BlinkHelp(HelpTexts.HelpTextsDict[HelpTextEnum.Initial]);
@@ -176,9 +176,9 @@ public partial class Annotator
await _mediator.Publish(new AnnotatorControlEvent(PlaybackControlEnum.Play)); await _mediator.Publish(new AnnotatorControlEvent(PlaybackControlEnum.Play));
}; };
LvClasses.SelectionChanged += (_, _) => LvClasses.DetectionClassChanged += (_, args) =>
{ {
var selectedClass = (DetectionClass)LvClasses.SelectedItem; var selectedClass = args.DetectionClass;
Editor.CurrentAnnClass = selectedClass; Editor.CurrentAnnClass = selectedClass;
_mediator.Publish(new AnnClassSelectedEvent(selectedClass)); _mediator.Publish(new AnnClassSelectedEvent(selectedClass));
}; };
+5 -5
View File
@@ -53,12 +53,12 @@ public class AnnotatorEventHandler(
await Task.CompletedTask; await Task.CompletedTask;
} }
private void SelectClass(DetectionClass annClass) private void SelectClass(DetectionClass detClass)
{ {
mainWindow.Editor.CurrentAnnClass = annClass; mainWindow.Editor.CurrentAnnClass = detClass;
foreach (var ann in mainWindow.Editor.CurrentDetections.Where(x => x.IsSelected)) foreach (var ann in mainWindow.Editor.CurrentDetections.Where(x => x.IsSelected))
ann.DetectionClass = annClass; ann.DetectionClass = detClass;
mainWindow.LvClasses.SelectedIndex = annClass.Id; mainWindow.LvClasses.SelectNum(detClass.Id);
} }
public async Task Handle(KeyEvent keyEvent, CancellationToken cancellationToken = default) public async Task Handle(KeyEvent keyEvent, CancellationToken cancellationToken = default)
@@ -74,7 +74,7 @@ public class AnnotatorEventHandler(
if ((int)key >= (int)Key.NumPad1 && (int)key <= (int)Key.NumPad9) if ((int)key >= (int)Key.NumPad1 && (int)key <= (int)Key.NumPad9)
keyNumber = key - Key.NumPad1; keyNumber = key - Key.NumPad1;
if (keyNumber.HasValue) if (keyNumber.HasValue)
SelectClass((DetectionClass)mainWindow.LvClasses.Items[keyNumber.Value]!); SelectClass((DetectionClass)mainWindow.LvClasses.DetectionDataGrid.Items[keyNumber.Value]!);
if (_keysControlEnumDict.TryGetValue(key, out var value)) if (_keysControlEnumDict.TryGetValue(key, out var value))
await ControlPlayback(value, cancellationToken); await ControlPlayback(value, cancellationToken);
+104 -47
View File
@@ -1,49 +1,106 @@
<DataGrid x:Class="Azaion.Common.Controls.DetectionClasses" <UserControl x:Class="Azaion.Common.Controls.DetectionClasses"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d" mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300" d:DesignHeight="300" d:DesignWidth="300">
Background="Black" <UserControl.Resources>
RowBackground="#252525" <Style x:Key="ButtonRadioButtonStyle" TargetType="RadioButton">
Foreground="White" <Setter Property="Template">
RowHeaderWidth="0" <Setter.Value>
Padding="2 0 0 0" <ControlTemplate TargetType="RadioButton">
AutoGenerateColumns="False" <Border x:Name="Border" BorderBrush="{TemplateBinding BorderBrush}"
SelectionMode="Single" Background="{TemplateBinding Background}" BorderThickness="1"
CellStyle="{DynamicResource DataGridCellStyle1}" Padding="10,5" CornerRadius="2">
IsReadOnly="True" <ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
CanUserResizeRows="False" </Border>
CanUserResizeColumns="False"> <ControlTemplate.Triggers>
<DataGrid.Columns> <Trigger Property="IsChecked" Value="True">
<DataGridTemplateColumn <Setter TargetName="Border" Property="Background" Value="Gray"/>
Width="50" </Trigger>
Header="Клавіша" <Trigger Property="IsMouseOver" Value="True">
CanUserSort="False"> <Setter TargetName="Border" Property="Background" Value="DarkGray"/>
<DataGridTemplateColumn.HeaderStyle> </Trigger>
<Style TargetType="DataGridColumnHeader"> </ControlTemplate.Triggers>
<Setter Property="Background" Value="#252525"></Setter> </ControlTemplate>
</Style> </Setter.Value>
</DataGridTemplateColumn.HeaderStyle> </Setter>
<DataGridTemplateColumn.CellTemplate> <Setter Property="Background" Value="Transparent"/>
<DataTemplate> <Setter Property="BorderBrush" Value="White"/>
<Border Background="{Binding Path=ColorBrush}"> <Setter Property="Foreground" Value="White"/>
<TextBlock Text="{Binding Path=ClassNumber}"></TextBlock> </Style>
</Border> </UserControl.Resources>
</DataTemplate> <Grid Background="Black">
</DataGridTemplateColumn.CellTemplate> <Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<!-- Your DataGrid with detection classes -->
<DataGrid x:Name="DetectionDataGrid"
Grid.Row="0"
Background="Black"
RowBackground="#252525"
Foreground="White"
RowHeaderWidth="0"
Padding="2 0 0 0"
AutoGenerateColumns="False"
SelectionMode="Single"
CellStyle="{DynamicResource DataGridCellStyle1}"
IsReadOnly="True"
CanUserResizeRows="False"
CanUserResizeColumns="False"
SelectionChanged="DetectionDataGrid_SelectionChanged"
x:FieldModifier="public">
<DataGrid.Columns>
<DataGridTemplateColumn Width="50" Header="Клавіша" CanUserSort="False">
<DataGridTemplateColumn.HeaderStyle>
<Style TargetType="DataGridColumnHeader">
<Setter Property="Background" Value="#252525"/>
</Style>
</DataGridTemplateColumn.HeaderStyle>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Border Background="{Binding Path=ColorBrush}">
<TextBlock Text="{Binding Path=ClassNumber}"/>
</Border>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn> </DataGridTemplateColumn>
<DataGridTextColumn <DataGridTextColumn Width="*" Header="Назва" Binding="{Binding Path=Name}" CanUserSort="False">
Width="*" <DataGridTextColumn.HeaderStyle>
Header="Назва" <Style TargetType="DataGridColumnHeader">
Binding="{Binding Path=Name}" <Setter Property="Background" Value="#252525"/>
CanUserSort="False"> </Style>
<DataGridTextColumn.HeaderStyle> </DataGridTextColumn.HeaderStyle>
<Style TargetType="DataGridColumnHeader">
<Setter Property="Background" Value="#252525"></Setter>
</Style>
</DataGridTextColumn.HeaderStyle>
</DataGridTextColumn> </DataGridTextColumn>
</DataGrid.Columns> </DataGrid.Columns>
</DataGrid> </DataGrid>
<!-- StackPanel with mode switcher RadioButtons -->
<StackPanel x:Name="ModeSwitcherPanel"
Grid.Row="1"
Orientation="Horizontal"
HorizontalAlignment="Center"
Margin="0,5,0,5">
<RadioButton x:Name="NormalModeRadioButton"
Tag="0"
Content="Звичайний"
GroupName="Mode"
Checked="ModeRadioButton_Checked"
IsChecked="True"
Style="{StaticResource ButtonRadioButtonStyle}"/>
<RadioButton x:Name="EveningModeRadioButton"
Tag="1"
Content="Зима"
GroupName="Mode"
Checked="ModeRadioButton_Checked" Margin="10,0,0,0"
Style="{StaticResource ButtonRadioButtonStyle}"/>
<RadioButton x:Name="NightModeRadioButton"
Tag="2"
Content="Ніч"
GroupName="Mode"
Checked="ModeRadioButton_Checked" Margin="10,0,0,0"
Style="{StaticResource ButtonRadioButtonStyle}"/>
</StackPanel>
</Grid>
</UserControl>
@@ -1,9 +1,54 @@
namespace Azaion.Common.Controls; using System.Windows;
using System.Windows.Controls;
using Azaion.Common.DTO;
namespace Azaion.Common.Controls;
public class DetectionClassChangedEventArgs(DetectionClass? detectionClass, int classNumber) : EventArgs
{
public DetectionClass? DetectionClass { get; } = detectionClass;
public int ClassNumber { get; } = classNumber;
}
public partial class DetectionClasses public partial class DetectionClasses
{ {
public DetectionClasses() public event EventHandler<DetectionClassChangedEventArgs>? DetectionClassChanged;
public DetectionClasses() => InitializeComponent();
public int CurrentClassNumber { get; private set; } = 0;
public DetectionClass? CurrentDetectionClass { get; set; }
private void DetectionDataGrid_SelectionChanged(object sender, SelectionChangedEventArgs e) =>
RaiseDetectionClassChanged();
private void ModeRadioButton_Checked(object sender, RoutedEventArgs e) =>
RaiseDetectionClassChanged();
private void RaiseDetectionClassChanged()
{ {
InitializeComponent(); var detClass = (DetectionClass)DetectionDataGrid.SelectedItem;
var baseClassNumber = detClass?.Id ?? 0;
var modeAmplifier = 0;
foreach (var child in ModeSwitcherPanel.Children)
if (child is RadioButton { IsChecked: true } rb)
if (int.TryParse(rb.Tag?.ToString(), out int modeIndex))
{
if (detClass != null)
detClass.PhotoMode = (PhotoMode)modeIndex;
modeAmplifier += modeIndex * 20;
}
CurrentDetectionClass = detClass;
CurrentClassNumber = baseClassNumber + modeAmplifier;
DetectionClassChanged?.Invoke(this, new DetectionClassChangedEventArgs(detClass, CurrentClassNumber));
}
public void SelectNum(int keyNumber)
{
DetectionDataGrid.SelectedIndex = keyNumber;
} }
} }
@@ -11,8 +11,6 @@ public class AnnotationConfig
[JsonIgnore] [JsonIgnore]
public Dictionary<int, DetectionClass> DetectionClassesDict => _detectionClassesDict ??= AnnotationClasses.ToDictionary(x => x.Id); public Dictionary<int, DetectionClass> DetectionClassesDict => _detectionClassesDict ??= AnnotationClasses.ToDictionary(x => x.Id);
public int? LastSelectedExplorerClass { get; set; }
public List<string> VideoFormats { get; set; } = null!; public List<string> VideoFormats { get; set; } = null!;
public List<string> ImageFormats { get; set; } = null!; public List<string> ImageFormats { get; set; } = null!;
+10 -1
View File
@@ -11,12 +11,21 @@ public class DetectionClass
public string Name { get; set; } = null!; public string Name { get; set; } = null!;
public string ShortName { get; set; } = null!; public string ShortName { get; set; } = null!;
public PhotoMode PhotoMode { get; set; }
[JsonIgnore] [JsonIgnore]
public Color Color => Id.ToColor(); public Color Color => Id.ToColor();
[JsonIgnore] [JsonIgnore] //For UI
public int ClassNumber => Id + 1; public int ClassNumber => Id + 1;
[JsonIgnore] [JsonIgnore]
public SolidColorBrush ColorBrush => new(Color); public SolidColorBrush ColorBrush => new(Color);
}
public enum PhotoMode
{
Regular = 0,
Winter = 20,
Night = 40
} }
+10 -28
View File
@@ -93,38 +93,20 @@ public partial class DatasetExplorer
AllDetectionClasses = new ObservableCollection<DetectionClass>( AllDetectionClasses = new ObservableCollection<DetectionClass>(
new List<DetectionClass> { new() {Id = -1, Name = "All", ShortName = "All"}} new List<DetectionClass> { new() {Id = -1, Name = "All", ShortName = "All"}}
.Concat(_annotationConfig.AnnotationClasses)); .Concat(_annotationConfig.AnnotationClasses));
LvClasses.ItemsSource = AllDetectionClasses; LvClasses.DetectionDataGrid.ItemsSource = AllDetectionClasses;
LvClasses.MouseUp += async (_, _) => LvClasses.DetectionClassChanged += async (_, args) =>
{ {
var selectedClass = (DetectionClass)LvClasses.SelectedItem; ExplorerEditor.CurrentAnnClass = args.DetectionClass;
ExplorerEditor.CurrentAnnClass = selectedClass;
_annotationConfig.LastSelectedExplorerClass = selectedClass.Id;
if (Switcher.SelectedIndex == 0) if (Switcher.SelectedIndex == 0)
await ReloadThumbnails(); await ReloadThumbnails();
else else
foreach (var ann in ExplorerEditor.CurrentDetections.Where(x => x.IsSelected)) foreach (var ann in ExplorerEditor.CurrentDetections.Where(x => x.IsSelected))
ann.DetectionClass = selectedClass; ann.DetectionClass = args.DetectionClass;
}; };
LvClasses.SelectionChanged += (_, _) => ExplorerEditor.CurrentAnnClass = (DetectionClass)LvClasses.CurrentDetectionClass;
{
if (Switcher.SelectedIndex != 1)
return;
var selectedClass = (DetectionClass)LvClasses.SelectedItem;
if (selectedClass == null)
return;
ExplorerEditor.CurrentAnnClass = selectedClass;
foreach (var ann in ExplorerEditor.CurrentDetections.Where(x => x.IsSelected))
ann.DetectionClass = selectedClass;
};
LvClasses.SelectedIndex = _annotationConfig.LastSelectedExplorerClass ?? 0;
ExplorerEditor.CurrentAnnClass = (DetectionClass)LvClasses.SelectedItem;
await _dbFactory.Run(async db => await _dbFactory.Run(async db =>
{ {
@@ -256,18 +238,18 @@ public partial class DatasetExplorer
{ {
AnnotationsTab.Visibility = Visibility.Collapsed; AnnotationsTab.Visibility = Visibility.Collapsed;
EditorTab.Visibility = Visibility.Visible; EditorTab.Visibility = Visibility.Visible;
_tempSelectedClassIdx = LvClasses.SelectedIndex; _tempSelectedClassIdx = LvClasses.CurrentClassNumber;
LvClasses.ItemsSource = _annotationConfig.AnnotationClasses; LvClasses.DetectionDataGrid.ItemsSource = _annotationConfig.AnnotationClasses;
Switcher.SelectedIndex = 1; Switcher.SelectedIndex = 1;
LvClasses.SelectedIndex = Math.Max(0, _tempSelectedClassIdx - 1); LvClasses.SelectNum(Math.Max(0, _tempSelectedClassIdx - 1));
} }
else else
{ {
AnnotationsTab.Visibility = Visibility.Visible; AnnotationsTab.Visibility = Visibility.Visible;
EditorTab.Visibility = Visibility.Collapsed; EditorTab.Visibility = Visibility.Collapsed;
LvClasses.ItemsSource = AllDetectionClasses; LvClasses.DetectionDataGrid.ItemsSource = AllDetectionClasses;
LvClasses.SelectedIndex = _tempSelectedClassIdx; LvClasses.SelectNum(_tempSelectedClassIdx);
Switcher.SelectedIndex = 0; Switcher.SelectedIndex = 0;
} }
} }
@@ -42,7 +42,7 @@ public class DatasetExplorerEventHandler(
if ((int)key >= (int)Key.NumPad1 && (int)key <= (int)Key.NumPad9) keyNumber = key - Key.NumPad1; if ((int)key >= (int)Key.NumPad1 && (int)key <= (int)Key.NumPad9) keyNumber = key - Key.NumPad1;
if (keyNumber.HasValue) if (keyNumber.HasValue)
datasetExplorer.LvClasses.SelectedIndex = keyNumber.Value; datasetExplorer.LvClasses.SelectNum(keyNumber.Value);
else else
{ {
if (datasetExplorer.Switcher.SelectedIndex == 1 && _keysControlEnumDict.TryGetValue(key, out var value)) if (datasetExplorer.Switcher.SelectedIndex == 1 && _keysControlEnumDict.TryGetValue(key, out var value))
@@ -88,13 +88,11 @@ public class DatasetExplorerEventHandler(
public async Task Handle(AnnotationCreatedEvent notification, CancellationToken cancellationToken) public async Task Handle(AnnotationCreatedEvent notification, CancellationToken cancellationToken)
{ {
var annotation = notification.Annotation; var annotation = notification.Annotation;
var selectedClass = ((DetectionClass?)datasetExplorer.LvClasses.SelectedItem)?.Id; var selectedClass = datasetExplorer.LvClasses.CurrentClassNumber;
if (selectedClass == null)
return;
//TODO: For editing existing need to handle updates //TODO: For editing existing need to handle updates
datasetExplorer.AddAnnotationToDict(annotation); datasetExplorer.AddAnnotationToDict(annotation);
if (annotation.Classes.Contains(selectedClass.Value)) if (annotation.Classes.Contains(selectedClass))
{ {
var annThumb = new AnnotationThumbnail(annotation); var annThumb = new AnnotationThumbnail(annotation);
datasetExplorer.SelectedAnnotations.Add(annThumb); datasetExplorer.SelectedAnnotations.Add(annThumb);
+2 -1
View File
@@ -1,5 +1,6 @@
import json import json
import os import os
import time
from http import HTTPStatus from http import HTTPStatus
from uuid import UUID from uuid import UUID
import jwt import jwt
@@ -97,7 +98,7 @@ cdef class ApiClient:
stream = BytesIO(response.raw.read()) stream = BytesIO(response.raw.read())
data = Security.decrypt_to(stream, key) data = Security.decrypt_to(stream, key)
print(f'loaded file: {filename}, {len(data)} bytes') constants.log(<str>f'Downloaded file: {filename}, {len(data)} bytes')
return data return data
cdef load_ai_model(self): cdef load_ai_model(self):
+4 -1
View File
@@ -9,4 +9,7 @@ cdef str QUEUE_CONFIG_FILENAME # queue config filename to load from api
cdef str AI_MODEL_FILE # AI Model file cdef str AI_MODEL_FILE # AI Model file
cdef bytes DONE_SIGNAL cdef bytes DONE_SIGNAL
cdef int MODEL_BATCH_SIZE cdef int MODEL_BATCH_SIZE
cdef log(str log_message, bytes client_id=*)
+7
View File
@@ -1,3 +1,5 @@
import time
cdef str CONFIG_FILE = "config.yaml" # Port for the zmq cdef str CONFIG_FILE = "config.yaml" # Port for the zmq
cdef int QUEUE_MAXSIZE = 1000 # Maximum size of the command queue cdef int QUEUE_MAXSIZE = 1000 # Maximum size of the command queue
@@ -10,3 +12,8 @@ cdef str AI_MODEL_FILE = "azaion.onnx"
cdef bytes DONE_SIGNAL = b"DONE" cdef bytes DONE_SIGNAL = b"DONE"
cdef int MODEL_BATCH_SIZE = 4 cdef int MODEL_BATCH_SIZE = 4
cdef log(str log_message, bytes client_id=None):
local_time = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime())
client_str = '' if client_id is None else f' {client_id}'
print(f'[{local_time}{client_str}]: {log_message}')
-1
View File
@@ -29,7 +29,6 @@ cdef class CommandProcessor:
self.inference = Inference(self.api_client, self.on_annotation) self.inference = Inference(self.api_client, self.on_annotation)
def start(self): def start(self):
print('Started!')
while self.running: while self.running:
try: try:
command = self.inference_queue.get(timeout=0.5) command = self.inference_queue.get(timeout=0.5)
+2 -2
View File
@@ -13,8 +13,8 @@ cdef class RemoteCommand:
40: "STOP_INFERENCE", 40: "STOP_INFERENCE",
100: "EXIT" 100: "EXIT"
} }
data_str = f'. Data: {len(self.data)} bytes' if self.data else '' data_str = f'{len(self.data)} bytes' if self.data else ''
return f'{command_type_names[self.command_type]}: {data_str}' return f'{command_type_names[self.command_type]} ({data_str})'
@staticmethod @staticmethod
cdef from_msgpack(bytes data): cdef from_msgpack(bytes data):
+5 -3
View File
@@ -14,7 +14,8 @@ cdef class RemoteCommandHandler:
self._router.setsockopt(zmq.LINGER, 0) self._router.setsockopt(zmq.LINGER, 0)
with open(<str>constants.CONFIG_FILE, "r") as f: with open(<str>constants.CONFIG_FILE, "r") as f:
config = yaml.safe_load(f) config = yaml.safe_load(f)
self._router.bind(f'tcp://*:{config["zmq_port"]}') port = config["zmq_port"]
self._router.bind(f'tcp://*:{port}')
self._dealer = self._context.socket(zmq.DEALER) self._dealer = self._context.socket(zmq.DEALER)
self._dealer.setsockopt(zmq.LINGER, 0) self._dealer.setsockopt(zmq.LINGER, 0)
@@ -30,6 +31,7 @@ cdef class RemoteCommandHandler:
for _ in range(4): # 4 worker threads for _ in range(4): # 4 worker threads
worker = Thread(target=self._worker_loop, daemon=True) worker = Thread(target=self._worker_loop, daemon=True)
self._workers.append(worker) self._workers.append(worker)
print(f'Listening to commands on port {port}...')
cdef start(self): cdef start(self):
self._proxy_thread.start() self._proxy_thread.start()
@@ -60,7 +62,7 @@ cdef class RemoteCommandHandler:
client_id, message = worker_socket.recv_multipart() client_id, message = worker_socket.recv_multipart()
cmd = RemoteCommand.from_msgpack(<bytes> message) cmd = RemoteCommand.from_msgpack(<bytes> message)
cmd.client_id = client_id cmd.client_id = client_id
print(f'Received [{cmd}] from the client {client_id}') constants.log(<str>f'{cmd}', client_id)
self._on_command(cmd) self._on_command(cmd)
except Exception as e: except Exception as e:
if not self._shutdown_event.is_set(): if not self._shutdown_event.is_set():
@@ -74,7 +76,7 @@ cdef class RemoteCommandHandler:
with self._context.socket(zmq.DEALER) as socket: with self._context.socket(zmq.DEALER) as socket:
socket.connect("inproc://backend") socket.connect("inproc://backend")
socket.send_multipart([client_id, data]) socket.send_multipart([client_id, data])
print(f'{len(data)} bytes was sent to client {client_id}') constants.log(<str>f'Sent {len(data)} bytes.', client_id)
cdef stop(self): cdef stop(self):
self._shutdown_event.set() self._shutdown_event.set()