imglab: chinese ("automatic") clustering, keyboard shortcuts for zooming (#2007)

* imglab: add support for using chinese whispers for more automatic clustering

* widgets: refactor out zooming from wheel handling

* tools/imglab/src/metadata_editor.cpp

imglab: add keyboard shortcuts for zooming
This commit is contained in:
martin 2020-02-29 14:31:28 +00:00 committed by GitHub
parent fc6992ac04
commit 4ff365a530
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 111 additions and 23 deletions

View File

@ -7065,25 +7065,9 @@ namespace dlib
// ----------------------------------------------------------------------------------------
void image_display::
on_wheel_up (
unsigned long state
zoom_in (
)
{
// disable mouse wheel if the user is drawing a rectangle
if (drawing_rect)
return;
// if CONTROL is not being held down
if ((state & base_window::CONTROL) == 0)
{
scrollable_region::on_wheel_up(state);
return;
}
if (rect.contains(lastx,lasty) == false || hidden || !enabled)
return;
if (zoom_in_scale < 100 && zoom_out_scale == 1)
{
const point mouse_loc(lastx, lasty);
@ -7119,7 +7103,7 @@ namespace dlib
// ----------------------------------------------------------------------------------------
void image_display::
on_wheel_down (
on_wheel_up (
unsigned long state
)
{
@ -7130,14 +7114,22 @@ namespace dlib
// if CONTROL is not being held down
if ((state & base_window::CONTROL) == 0)
{
scrollable_region::on_wheel_down(state);
scrollable_region::on_wheel_up(state);
return;
}
if (rect.contains(lastx,lasty) == false || hidden || !enabled)
return;
zoom_in();
}
// ----------------------------------------------------------------------------------------
void image_display::
zoom_out (
)
{
if (zoom_in_scale != 1)
{
const point mouse_loc(lastx, lasty);
@ -7170,6 +7162,30 @@ namespace dlib
}
}
// ----------------------------------------------------------------------------------------
void image_display::
on_wheel_down (
unsigned long state
)
{
// disable mouse wheel if the user is drawing a rectangle
if (drawing_rect)
return;
// if CONTROL is not being held down
if ((state & base_window::CONTROL) == 0)
{
scrollable_region::on_wheel_down(state);
return;
}
if (rect.contains(lastx,lasty) == false || hidden || !enabled)
return;
zoom_out();
}
// ----------------------------------------------------------------------------------------
// ----------------------------------------------------------------------------------------
// image_window member functions

View File

@ -3493,6 +3493,12 @@ namespace dlib
bool overlay_editing_is_enabled (
) const { auto_mutex M(m); return overlay_editing_enabled; }
void zoom_in (
);
void zoom_out (
);
private:
void draw (

View File

@ -10,6 +10,7 @@
#include <dlib/dir_nav.h>
#include <dlib/clustering.h>
#include <dlib/svm.h>
#include <dlib/statistics.h>
// ----------------------------------------------------------------------------------------
@ -72,6 +73,56 @@ std::vector<assignment> angular_cluster (
}
return assignments;
}
std::vector<assignment> chinese_cluster (
std::vector<matrix<double,0,1> > feats,
unsigned long &num_clusters
)
{
// try to find a good value to select if we should add a vertex in the graph
matrix<double,0,1> m;
for (unsigned long i = 0; i < feats.size(); ++i)
m += feats[i];
m /= feats.size();
for (unsigned long i = 0; i < feats.size(); ++i)
{
feats[i] -= m;
double len = length(feats[i]);
if (len != 0)
feats[i] /= len;
}
running_stats<double> rs;
for (size_t i = 0; i < feats.size(); ++i) {
for (size_t j = i; j < feats.size(); ++j) {
rs.add(length(feats[i] - feats[j]));
}
}
// add vertices for chinese whispers to find clusters
std::vector<sample_pair> edges;
for (size_t i = 0; i < feats.size(); ++i) {
for (size_t j = i; j < feats.size(); ++j) {
if (length(feats[i] - feats[j]) < rs.mean()) {
edges.push_back(sample_pair(i, j, length(feats[i] - feats[j])));
}
}
}
std::vector<unsigned long> labels;
num_clusters = chinese_whispers(edges, labels);
std::vector<assignment> assignments;
for (unsigned long i = 0; i < feats.size(); ++i)
{
assignment temp;
temp.c = labels[i];
temp.dist = length(feats[i]);
temp.idx = i;
assignments.push_back(temp);
}
return assignments;
}
// ----------------------------------------------------------------------------------------
@ -134,7 +185,7 @@ int cluster_dataset(
return EXIT_FAILURE;
}
const unsigned long num_clusters = get_option(parser, "cluster", 2);
unsigned long num_clusters = get_option(parser, "cluster", 0);
const unsigned long chip_size = get_option(parser, "size", 8000);
image_dataset_metadata::dataset data;
@ -177,7 +228,12 @@ int cluster_dataset(
}
cout << "\nClustering objects..." << endl;
std::vector<assignment> assignments = angular_cluster(feats, num_clusters);
std::vector<assignment> assignments;
if (num_clusters) {
assignments = angular_cluster(feats, num_clusters);
} else {
assignments = chinese_cluster(feats, num_clusters);
}
// Now output each cluster to disk as an XML file.

View File

@ -588,7 +588,7 @@ int main(int argc, char** argv)
"The parts are instead simply mirrored to the flipped dataset.", 1);
parser.add_option("rotate", "Read an XML image dataset and output a copy that is rotated counter clockwise by <arg> degrees. "
"The output is saved to an XML file prefixed with rotated_<arg>.",1);
parser.add_option("cluster", "Cluster all the objects in an XML file into <arg> different clusters and save "
parser.add_option("cluster", "Cluster all the objects in an XML file into <arg> different clusters (pass 0 to find automatically) and save "
"the results as cluster_###.xml and cluster_###.jpg files.",1);
parser.add_option("ignore", "Mark boxes labeled as <arg> as ignored. The resulting XML file is output as a separate file and the original is not modified.",1);
parser.add_option("rmlabel","Remove all boxes labeled <arg> and save the results to a new XML file.",1);
@ -704,7 +704,7 @@ int main(int argc, char** argv)
parser.check_incompatible_options("box-images", "ignore");
const char* convert_args[] = {"pascal-xml","pascal-v1","idl"};
parser.check_option_arg_range("convert", convert_args);
parser.check_option_arg_range("cluster", 2, 999);
parser.check_option_arg_range("cluster", 0, 999);
parser.check_option_arg_range("rotate", -360, 360);
parser.check_option_arg_range("size", 10*10, 1000*1000);
parser.check_option_arg_range("min-object-size", 1, 10000*10000);

View File

@ -343,6 +343,16 @@ on_keydown (
last_keyboard_jump_pos_update = 0;
}
if (key == '=')
{
display.zoom_in();
}
if (key == '-')
{
display.zoom_out();
}
if (key == 'd' && (state&base_window::KBD_MOD_ALT))
{
remove_selected_images();