Skip to content

Commit dd7f178

Browse files
author
Gin
committed
add shortcut support to label image program
1 parent 2990239 commit dd7f178

File tree

4 files changed

+141
-109
lines changed

4 files changed

+141
-109
lines changed

SerialPrograms/Source/ML/Programs/ML_LabelImages.cpp

Lines changed: 75 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include <fstream>
2121
#include <filesystem>
2222
#include <cmath>
23+
#include <iomanip>
2324
#include "CommonFramework/Globals.h"
2425
#include "Common/Cpp/BitmapConversion.h"
2526
#include "Common/Cpp/Json/JsonArray.h"
@@ -108,79 +109,6 @@ JsonObject object_annotation_to_json(const ObjectAnnotation& object_annotation){
108109
}
109110

110111

111-
DrawnBoundingBox::DrawnBoundingBox(LabelImages_Widget& widget, VideoOverlay& overlay)
112-
: m_widget(widget)
113-
, m_overlay(overlay)
114-
{
115-
auto& program = m_widget.m_program;
116-
program.X.add_listener(*this);
117-
program.Y.add_listener(*this);
118-
program.WIDTH.add_listener(*this);
119-
program.HEIGHT.add_listener(*this);
120-
overlay.add_listener(*this);
121-
}
122-
123-
DrawnBoundingBox::~DrawnBoundingBox(){
124-
detach();
125-
}
126-
127-
// called when drawn bounding box changed
128-
void DrawnBoundingBox::on_config_value_changed(void* object){
129-
auto& program = m_widget.m_program;
130-
std::lock_guard<std::mutex> lg(m_lock);
131-
program.update_rendered_objects(m_widget.m_overlay_set);
132-
}
133-
void DrawnBoundingBox::on_mouse_press(double x, double y){
134-
auto& program = m_widget.m_program;
135-
program.WIDTH.set(0);
136-
program.HEIGHT.set(0);
137-
program.X.set(x);
138-
program.Y.set(y);
139-
m_mouse_start.emplace();
140-
m_mouse_start->first = x;
141-
m_mouse_start->second = y;
142-
}
143-
void DrawnBoundingBox::on_mouse_release(double, double){
144-
m_mouse_start.reset();
145-
auto& m_program = m_widget.m_program;
146-
auto& m_overlay_set = m_widget.m_overlay_set;
147-
148-
m_program.compute_mask(m_overlay_set);
149-
}
150-
151-
void DrawnBoundingBox::on_mouse_move(double x, double y){
152-
auto& program = m_widget.m_program;
153-
if (!m_mouse_start){
154-
return;
155-
}
156-
157-
double xl = m_mouse_start->first;
158-
double xh = x;
159-
double yl = m_mouse_start->second;
160-
double yh = y;
161-
162-
if (xl > xh){
163-
std::swap(xl, xh);
164-
}
165-
if (yl > yh){
166-
std::swap(yl, yh);
167-
}
168-
169-
program.X.set(xl);
170-
program.Y.set(yl);
171-
program.WIDTH.set(xh - xl);
172-
program.HEIGHT.set(yh - yl);
173-
}
174-
175-
void DrawnBoundingBox::detach(){
176-
auto& program = m_widget.m_program;
177-
m_overlay.remove_listener(*this);
178-
program.X.remove_listener(*this);
179-
program.Y.remove_listener(*this);
180-
program.WIDTH.remove_listener(*this);
181-
program.HEIGHT.remove_listener(*this);
182-
}
183-
184112

185113
LabelImages_Descriptor::LabelImages_Descriptor()
186114
: PanelDescriptor(
@@ -444,10 +372,26 @@ void LabelImages::compute_embeddings_for_folder(const std::string& image_folder_
444372
ML::compute_embeddings_for_folder(embedding_model_path, image_folder_path);
445373
}
446374

375+
void LabelImages::delete_last_annotation(){
376+
if (m_annotations.size() > 0){
377+
m_annotations.pop_back();
378+
}
379+
if (m_annotations.size() > 0){
380+
m_last_object_idx = m_annotations.size() - 1;
381+
}
382+
}
447383

448384

449385
LabelImages_Widget::~LabelImages_Widget(){
386+
m_program.X.remove_listener(*this);
387+
m_program.Y.remove_listener(*this);
388+
m_program.WIDTH.remove_listener(*this);
389+
m_program.HEIGHT.remove_listener(*this);
450390
m_program.FORM_LABEL.remove_listener(*this);
391+
392+
m_display_session.overlay().remove_listener(*this);
393+
m_display_session.video_session().remove_state_listener(*this);
394+
451395
delete m_image_display_widget;
452396
}
453397
LabelImages_Widget::LabelImages_Widget(
@@ -459,9 +403,14 @@ LabelImages_Widget::LabelImages_Widget(
459403
, m_program(program)
460404
, m_display_session(m_program.m_display_session)
461405
, m_overlay_set(m_display_session.overlay())
462-
, m_drawn_box(*this, m_display_session.overlay())
463406
{
407+
m_program.X.add_listener(*this);
408+
m_program.Y.add_listener(*this);
409+
m_program.WIDTH.add_listener(*this);
410+
m_program.HEIGHT.add_listener(*this);
464411
m_program.FORM_LABEL.add_listener(*this);
412+
413+
m_display_session.overlay().add_listener(*this);
465414
m_display_session.video_session().add_state_listener(*this);
466415

467416
m_embedding_info_label = new QLabel(this);
@@ -479,7 +428,7 @@ LabelImages_Widget::LabelImages_Widget(
479428
QVBoxLayout* scroll_layout = new QVBoxLayout(scroll_inner);
480429
scroll_layout->setAlignment(Qt::AlignTop);
481430

482-
m_image_display_widget = new ImageAnnotationDisplayWidget(*this, m_display_session, 0);
431+
m_image_display_widget = new ImageAnnotationDisplayWidget(*this, m_display_session, this);
483432
scroll_layout->addWidget(m_image_display_widget);
484433

485434
QHBoxLayout* embedding_info_row = new QHBoxLayout();
@@ -491,12 +440,7 @@ LabelImages_Widget::LabelImages_Widget(
491440
scroll_layout->addWidget(button);
492441
connect(button, &QPushButton::clicked, this, [this](bool){
493442
auto& program = this->m_program;
494-
if (program.m_annotations.size() > 0){
495-
program.m_annotations.pop_back();
496-
}
497-
if (program.m_annotations.size() > 0){
498-
program.m_last_object_idx = program.m_annotations.size() - 1;
499-
}
443+
program.delete_last_annotation();
500444
program.update_rendered_objects(this->m_overlay_set);
501445
});
502446

@@ -527,8 +471,8 @@ void LabelImages_Widget::on_config_value_changed(void* object){
527471
if (m_program.m_annotations.size() > 0 && m_program.m_last_object_idx < m_program.m_annotations.size()){
528472
std::string& cur_label = m_program.m_annotations[m_program.m_last_object_idx].label;
529473
cur_label = m_program.FORM_LABEL.slug();
530-
m_program.update_rendered_objects(m_overlay_set);
531474
}
475+
m_program.update_rendered_objects(m_overlay_set);
532476
}
533477

534478
// This callback function will be called whenever the display source (the image source) is loaded or reloaded:
@@ -565,7 +509,56 @@ void LabelImages_Widget::post_startup(VideoSource* source){
565509
m_program.update_rendered_objects(m_overlay_set);
566510
}
567511

512+
void LabelImages_Widget::key_release(QKeyEvent* event){
513+
const auto key = Qt::Key(event->key());
514+
if (key == Qt::Key::Key_Delete || key == Qt::Key::Key_Backspace){
515+
m_program.delete_last_annotation();
516+
m_program.update_rendered_objects(m_overlay_set);
517+
}
518+
}
519+
520+
void LabelImages_Widget::on_mouse_press(double x, double y){
521+
m_program.WIDTH.set(0);
522+
m_program.HEIGHT.set(0);
523+
m_program.X.set(x);
524+
m_program.Y.set(y);
525+
m_mouse_start.emplace();
526+
m_mouse_end.emplace();
527+
m_mouse_start->first = m_mouse_end->first = x;
528+
m_mouse_start->second = m_mouse_end->second = y;
529+
}
568530

531+
void LabelImages_Widget::on_mouse_release(double x, double y){
532+
m_mouse_start.reset();
533+
m_mouse_end.reset();
534+
m_program.compute_mask(m_overlay_set);
535+
}
536+
537+
void LabelImages_Widget::on_mouse_move(double x, double y){
538+
if (!m_mouse_start){
539+
return;
540+
}
541+
542+
m_mouse_end->first = x;
543+
m_mouse_end->second = y;
544+
545+
double xl = m_mouse_start->first;
546+
double yl = m_mouse_start->second;
547+
double xh = x;
548+
double yh = y;
549+
550+
if (xl > xh){
551+
std::swap(xl, xh);
552+
}
553+
if (yl > yh){
554+
std::swap(yl, yh);
555+
}
556+
557+
m_program.X.set(xl);
558+
m_program.Y.set(yl);
559+
m_program.WIDTH.set(xh - xl);
560+
m_program.HEIGHT.set(yh - yl);
561+
}
569562

570563
}
571564
}

SerialPrograms/Source/ML/Programs/ML_LabelImages.h

Lines changed: 31 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include "NintendoSwitch/Framework/NintendoSwitch_SwitchSystemOption.h"
2121
#include "NintendoSwitch/Framework/NintendoSwitch_SwitchSystemSession.h"
2222
#include "CommonFramework/VideoPipeline/VideoOverlayScopes.h"
23+
#include "CommonFramework/VideoPipeline/UI/VideoDisplayWidget.h"
2324

2425
#include "ML/DataLabeling/ML_SegmentAnythingModel.h"
2526
#include "ML/UI/ML_ImageAnnotationDisplayOption.h"
@@ -96,9 +97,11 @@ class LabelImages : public PanelInstance{
9697
// This can be very slow!
9798
void compute_embeddings_for_folder(const std::string& image_folder);
9899

100+
// Delete the last object annotation.
101+
void delete_last_annotation();
102+
99103
private:
100104
friend class LabelImages_Widget;
101-
friend class DrawnBoundingBox;
102105

103106
// image display options like what image file is loaded
104107
ImageAnnotationDisplayOption m_display_option;
@@ -132,28 +135,11 @@ class LabelImages : public PanelInstance{
132135
};
133136

134137

135-
class DrawnBoundingBox : public ConfigOption::Listener, public VideoOverlay::MouseListener{
136-
public:
137-
~DrawnBoundingBox();
138-
DrawnBoundingBox(LabelImages_Widget& widget, VideoOverlay& overlay);
139-
virtual void on_config_value_changed(void* object) override;
140-
virtual void on_mouse_press(double x, double y) override;
141-
virtual void on_mouse_release(double x, double y) override;
142-
virtual void on_mouse_move(double x, double y) override;
143-
144-
private:
145-
void detach();
146-
147-
private:
148-
LabelImages_Widget& m_widget;
149-
VideoOverlay& m_overlay;
150-
std::mutex m_lock;
151-
152-
std::optional<std::pair<double, double>> m_mouse_start;
153-
};
154-
155-
156-
class LabelImages_Widget : public PanelWidget, public ConfigOption::Listener, public VideoSession::StateListener{
138+
class LabelImages_Widget : public PanelWidget,
139+
public ConfigOption::Listener,
140+
public VideoSession::StateListener,
141+
public CommandReceiver,
142+
public VideoOverlay::MouseListener{
157143
public:
158144
~LabelImages_Widget();
159145
LabelImages_Widget(
@@ -165,24 +151,44 @@ class LabelImages_Widget : public PanelWidget, public ConfigOption::Listener, pu
165151
// called after loading a new image, clean up all internal data
166152
void clear_for_new_image();
167153

154+
// Overwrites ConfigOption::Listener::on_config_value_changed().
168155
virtual void on_config_value_changed(void* object) override;
169156

170157
// Overwrites VideoSession::StateListener::post_startup().
171158
virtual void post_startup(VideoSource* source) override;
172159

160+
// Overwrites CommandReceiver::key_press().
161+
virtual void key_press(QKeyEvent* event) override {}
162+
// Overwrites CommandReceiver::key_release().
163+
virtual void key_release(QKeyEvent* event) override;
164+
// Overwrites CommandReceiver::focus_in().
165+
virtual void focus_in(QFocusEvent* event) override {}
166+
// Overwrites CommandReceiver::focus_out().
167+
virtual void focus_out(QFocusEvent* event) override {}
168+
169+
// Overwrites VideoOverlay::MouseListener::on_mouse_press().
170+
virtual void on_mouse_press(double x, double y) override;
171+
// Overwrites VideoOverlay::MouseListener::on_mouse_release().
172+
virtual void on_mouse_release(double x, double y) override;
173+
// Overwrites VideoOverlay::MouseListener::on_mouse_move().
174+
virtual void on_mouse_move(double x, double y) override;
175+
173176
private:
174177
LabelImages& m_program;
175178
ImageAnnotationDisplaySession& m_display_session;
176179

177180
ImageAnnotationDisplayWidget* m_image_display_widget;
178181

179182
VideoOverlaySet m_overlay_set;
180-
DrawnBoundingBox m_drawn_box;
181183

184+
// show the info about the loaded image embedding data corresponding to the currently
185+
// displayed image
182186
QLabel* m_embedding_info_label = nullptr;
187+
// a UI widget that holds all the editable UI elements defined in LabelImage program.
183188
ConfigWidget* m_option_widget;
184189

185-
friend class DrawnBoundingBox;
190+
std::optional<std::pair<double, double>> m_mouse_start;
191+
std::optional<std::pair<double, double>> m_mouse_end;
186192
};
187193

188194

SerialPrograms/Source/ML/UI/ML_ImageAnnotationDisplayWidget.cpp

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,10 +40,11 @@ ImageAnnotationDisplayWidget::~ImageAnnotationDisplayWidget(){
4040
ImageAnnotationDisplayWidget::ImageAnnotationDisplayWidget(
4141
QWidget& parent,
4242
ImageAnnotationDisplaySession& session,
43-
uint64_t program_id
43+
CommandReceiver* command_receiver
4444
)
4545
: QWidget(&parent)
4646
, m_session(session)
47+
, m_command_receiver(command_receiver)
4748
{
4849

4950
QVBoxLayout* layout = new QVBoxLayout(this);
@@ -95,19 +96,31 @@ void ImageAnnotationDisplayWidget::update_ui(ProgramState state){
9596
void ImageAnnotationDisplayWidget::key_press(QKeyEvent* event){
9697
// cout << "press: " << event->nativeVirtualKey() << endl;
9798
m_command->on_key_press(*event);
99+
if (m_command_receiver){
100+
m_command_receiver->key_press(event);
101+
}
98102
}
99103

100104
void ImageAnnotationDisplayWidget::key_release(QKeyEvent* event){
101105
// cout << "release: " << event->nativeVirtualKey() << endl;
102106
m_command->on_key_release(*event);
107+
if (m_command_receiver){
108+
m_command_receiver->key_release(event);
109+
}
103110
}
104111

105112
void ImageAnnotationDisplayWidget::focus_in(QFocusEvent* event){
106113
m_command->set_focus(true);
114+
if (m_command_receiver){
115+
m_command_receiver->focus_in(event);
116+
}
107117
}
108118

109119
void ImageAnnotationDisplayWidget::focus_out(QFocusEvent* event){
110120
m_command->set_focus(false);
121+
if (m_command_receiver){
122+
m_command_receiver->focus_out(event);
123+
}
111124
}
112125

113126
void ImageAnnotationDisplayWidget::keyPressEvent(QKeyEvent* event){

0 commit comments

Comments
 (0)