1+ /* ML Label Images Overlay Manager
2+ *
3+ * From: https://github.com/PokemonAutomation/
4+ *
5+ * Manages image overlay rendering for program LabelImages.
6+ */
7+
8+ #include " Pokemon/Resources/Pokemon_PokemonForms.h"
9+ #include " ML_LabelImagesOverlayManager.h"
10+ #include " ML_LabelImages.h"
11+
12+ #include < iostream>
13+ using std::cout, std::endl;
14+
15+
16+ namespace PokemonAutomation {
17+ namespace ML {
18+
19+ inline size_t size_t_diff (size_t x, size_t y){
20+ return x >= y ? x - y : y - x;
21+ }
22+ inline size_t size_t_subtract_clamp (size_t x, size_t y){
23+ // max(x - y, 0)
24+ return x >= y ? x - y : 0 ;
25+ }
26+
27+ LabelImages_OverlayManager::LabelImages_OverlayManager (LabelImages& program)
28+ : m_program(program)
29+ , m_overlay_set(program.m_display_session.overlay())
30+ {
31+ m_inclusion_point_icon_template = ImageRGB32 (21 , 21 );
32+ m_inclusion_point_icon_template.fill (0 );
33+ const size_t strip_size = 5 , strip_start = (21 - 5 ) / 2 ;
34+ for (size_t y = 0 ; y < strip_size; y++){
35+ for (size_t x = 0 ; x < m_inclusion_point_icon_template.width (); x++){
36+ m_inclusion_point_icon_template.pixel (x, y+strip_start) = uint32_t (COLOR_RED);
37+ }
38+ }
39+ for (size_t y = 0 ; y < m_inclusion_point_icon_template.height (); y++){
40+ for (size_t x = 0 ; x < strip_size; x++){
41+ m_inclusion_point_icon_template.pixel (x+strip_start, y) = uint32_t (COLOR_RED);
42+ }
43+ }
44+
45+ m_exclusion_point_icon_template = ImageRGB32 (21 , 21 );
46+ m_exclusion_point_icon_template.fill (0 );
47+ const size_t center = m_exclusion_point_icon_template.height () / 2 ;
48+ const size_t d2_min_th = (center-2 )*(center-2 );
49+ const size_t d2_max_th = (center+2 )*(center+2 );
50+ for (size_t y = 0 ; y < m_exclusion_point_icon_template.height (); y++){
51+ for (size_t x = 0 ; x < m_exclusion_point_icon_template.width (); x++){
52+ const size_t dx = size_t_diff (center, x);
53+ const size_t dy = size_t_diff (center, y);
54+ const size_t d2 = dx*dx + dy*dy;
55+ if (d2 >= d2_min_th && d2 <= d2_max_th){
56+ // double offset = std::fabs(std::sqrt(d2) - (double)center); // 0-3
57+ // int color_magnitude = int(255.0 * (1.0 - std::max(0.0, offset-2)) + 0.5);
58+ // color_magnitude = std::max(std::min(color_magnitude, 255), 0);
59+ // uint32_t color = combine_argb(uint8_t(color_magnitude), 0, 0, uint8_t(color_magnitude));
60+ // cout << x << " " << y << " " << offset << " " << color_magnitude << endl;
61+ m_exclusion_point_icon_template.pixel (x, y) = combine_argb (255 , 0 , 0 , 200 ); // dark blue
62+ }
63+ }
64+ }
65+
66+ // m_inclusion_point_icon_template.save("./inclusion.png");
67+ // m_exclusion_point_icon_template.save("./exclusion.png");
68+ }
69+
70+ void LabelImages_OverlayManager::clear (){
71+ m_overlay_set.clear ();
72+
73+ m_inclusion_point_icon = ImageRGB32 ();
74+ m_exclusion_point_icon = ImageRGB32 ();
75+ }
76+
77+ void LabelImages_OverlayManager::set_image_size (){
78+ const size_t point_icon_size = std::max (size_t (9 ), std::min (m_program.source_image_height , m_program.source_image_width ) / 100 );
79+
80+ m_inclusion_point_icon = m_inclusion_point_icon_template.scale_to (point_icon_size, point_icon_size);
81+ m_exclusion_point_icon = m_exclusion_point_icon_template.scale_to (point_icon_size, point_icon_size);
82+ }
83+
84+
85+ void LabelImages_OverlayManager::update_rendered_annotations (){
86+ m_overlay_set.clear ();
87+ const size_t image_width = m_program.source_image_width ;
88+ const size_t image_height = m_program.source_image_height ;
89+ if (image_width <= 1 || image_height <= 1 ){
90+ return ;
91+ }
92+ if (m_program.WIDTH > 0.0 && m_program.HEIGHT > 0.0 ){
93+ m_overlay_set.add (COLOR_RED, {m_program.X , m_program.Y , m_program.WIDTH , m_program.HEIGHT });
94+ }
95+
96+ const auto & annotations = m_program.m_annotations ;
97+ const size_t & m_selected = m_program.m_selected_obj_idx ;
98+
99+ auto create_overlay_for_index = [&](size_t i_obj){
100+ const auto & obj = annotations[i_obj];
101+ // overlayset.add(COLOR_RED, pixelbox_to_floatbox(source_image_width, source_image_height, obj.user_box));
102+ const auto mask_float_box = pixelbox_to_floatbox (image_width, image_height, obj.mask_box );
103+ std::string label = obj.label ;
104+ const Pokemon::PokemonForm* form = Pokemon::get_pokemon_form (label);
105+ if (form != nullptr ){
106+ label = form->display_name ();
107+ }
108+ Color mask_box_color = (i_obj == m_selected) ? COLOR_BLACK : COLOR_BLUE;
109+ m_overlay_set.add (mask_box_color, mask_float_box, label);
110+ size_t mask_width = obj.mask_box .width ();
111+ size_t mask_height = obj.mask_box .height ();
112+ ImageRGB32 mask_image (mask_width, mask_height);
113+ // cout << "in render, mask_box " << obj.mask_box.min_x << " " << obj.mask_box.min_y << " " << obj.mask_box.max_x << " " << obj.mask_box.max_y << endl;
114+
115+ for (size_t y = 0 ; y < mask_height; y++){
116+ for (size_t x = 0 ; x < mask_width; x++){
117+ const bool mask = obj.mask [y*mask_width + x];
118+ uint32_t & pixel = mask_image.pixel (x, y);
119+ // if the pixel's mask value is true, set a semi-transparent 45-degree blue strip color
120+ // otherwise: fully transparent (alpha = 0)
121+ uint32_t color = 0 ;
122+ if (mask){
123+ color = (std::abs (int (x) - int (y)) % 4 <= 1 ) ? combine_argb (150 , 30 , 144 , 255 ) : combine_argb (150 , 0 , 0 , 60 );
124+ }
125+ pixel = color;
126+ }
127+ }
128+ // cout << " count " << count << endl;
129+ m_overlay_set.add (std::move (mask_image), mask_float_box);
130+
131+ if (i_obj == m_selected){
132+ const size_t icon_size = m_inclusion_point_icon.height ();
133+ const size_t icon_min_offset = icon_size / 2 ;
134+ const size_t icon_max_offset = icon_size - icon_min_offset;
135+ auto create_box = [&](const std::pair<size_t , size_t >& p) -> ImageFloatBox{
136+ size_t x_min = size_t_subtract_clamp (p.first , icon_min_offset);
137+ size_t x_max = std::min (p.first + icon_max_offset, image_width-1 );
138+ size_t y_min = size_t_subtract_clamp (p.second , icon_min_offset);
139+ size_t y_max = std::min (p.second + icon_max_offset, image_height-1 );
140+ ImagePixelBox box (x_min, y_min, x_max, y_max);
141+ return pixelbox_to_floatbox (image_width, image_height, box);
142+ };
143+ // the inclusion and exclusion points are only rendered for the current selected object:
144+ for (const auto & p : obj.inclusion_points ){
145+ m_overlay_set.add (m_inclusion_point_icon.copy (), create_box (p));
146+ }
147+ for (const auto & p : obj.exclusion_points ){
148+ m_overlay_set.add (m_exclusion_point_icon.copy (), create_box (p));
149+ }
150+ }
151+ };
152+ for (size_t i_obj = 0 ; i_obj < annotations.size (); i_obj++){
153+ if (i_obj == m_selected){
154+ // skip current selected annotation because we want to render it last so that
155+ // it will not be occluded by other annotations
156+ continue ;
157+ }
158+ create_overlay_for_index (i_obj);
159+ }
160+ if (m_selected < annotations.size ()){
161+ create_overlay_for_index (m_selected);
162+ }
163+ }
164+
165+
166+ }
167+ }
0 commit comments