Skip to content
Projects
Groups
Snippets
Help
Loading...
Sign in
Toggle navigation
D
dlib
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
钟尚武
dlib
Commits
ad97e1f3
Commit
ad97e1f3
authored
Jul 03, 2015
by
Davis King
Browse files
Options
Browse Files
Download
Plain Diff
merged
parents
7e26d2cf
7aed6b3a
Hide whitespace changes
Inline
Side-by-side
Showing
13 changed files
with
680 additions
and
24 deletions
+680
-24
drectangle.h
dlib/geometry/drectangle.h
+24
-0
drectangle_abstract.h
dlib/geometry/drectangle_abstract.h
+30
-0
misc_api_kernel_abstract.h
dlib/misc_api/misc_api_kernel_abstract.h
+10
-1
misc_api_shared.h
dlib/misc_api/misc_api_shared.h
+13
-1
kkmeans.h
dlib/svm/kkmeans.h
+132
-0
kkmeans_abstract.h
dlib/svm/kkmeans_abstract.h
+41
-1
kmeans.cpp
dlib/test/kmeans.cpp
+48
-17
ml.xml
docs/docs/ml.xml
+18
-1
term_index.xml
docs/docs/term_index.xml
+1
-0
CMakeLists.txt
tools/imglab/CMakeLists.txt
+1
-0
cluster.cpp
tools/imglab/src/cluster.cpp
+246
-0
cluster.h
tools/imglab/src/cluster.h
+11
-0
main.cpp
tools/imglab/src/main.cpp
+105
-3
No files found.
dlib/geometry/drectangle.h
View file @
ad97e1f3
...
...
@@ -385,6 +385,15 @@ namespace dlib
return
drectangle
(
p
.
x
()
-
width
/
2
,
p
.
y
()
-
height
/
2
,
p
.
x
()
+
width
/
2
,
p
.
y
()
+
height
/
2
);
}
inline
drectangle
centered_drect
(
const
drectangle
&
rect
,
double
width
,
double
height
)
{
return
centered_drect
(
dcenter
(
rect
),
width
,
height
);
}
inline
const
drectangle
shrink_rect
(
const
drectangle
&
rect
,
double
num
...
...
@@ -419,6 +428,21 @@ namespace dlib
return
shrink_rect
(
rect
,
-
width
,
-
height
);
}
inline
drectangle
set_aspect_ratio
(
const
drectangle
&
rect
,
double
ratio
)
{
DLIB_ASSERT
(
ratio
>
0
,
"
\t
drectangle set_aspect_ratio()"
<<
"
\n\t
ratio: "
<<
ratio
);
const
double
h
=
std
::
sqrt
(
rect
.
area
()
/
ratio
);
const
double
w
=
h
*
ratio
;
return
centered_drect
(
rect
,
w
,
h
);
}
// ----------------------------------------------------------------------------------------
}
...
...
dlib/geometry/drectangle_abstract.h
View file @
ad97e1f3
...
...
@@ -503,6 +503,18 @@ namespace dlib
- R.height() == height
!*/
// ----------------------------------------------------------------------------------------
drectangle
centered_drect
(
const
drectangle
&
rect
,
double
width
,
double
height
);
/*!
ensures
- returns centered_drect(center(rect), width, height)
!*/
// ----------------------------------------------------------------------------------------
const
drectangle
shrink_rect
(
...
...
@@ -554,6 +566,24 @@ namespace dlib
(i.e. grows the given drectangle by expanding its border)
!*/
// ----------------------------------------------------------------------------------------
drectangle
set_aspect_ratio
(
const
drectangle
&
rect
,
double
ratio
);
/*!
requires
- ratio > 0
ensures
- This function reshapes the given rectangle so that it has the given aspect
ratio. In particular, this means we return a rectangle R such that the
following equations are true:
- R.width()/R.height() == ratio
- R.area() == rect.area()
- dcenter(rect) == dcenter(R)
!*/
// ----------------------------------------------------------------------------------------
}
...
...
dlib/misc_api/misc_api_kernel_abstract.h
View file @
ad97e1f3
...
...
@@ -90,7 +90,16 @@ namespace dlib
);
/*!
ensures
- calls set_current_dir(old_dir())
- if (revert() hasn't already been called) then
- calls set_current_dir(old_dir())
!*/
void
revert
(
);
/*!
ensures
- if (revert() hasn't already been called) then
- calls set_current_dir(old_dir())
!*/
};
...
...
dlib/misc_api/misc_api_shared.h
View file @
ad97e1f3
...
...
@@ -18,13 +18,14 @@ namespace dlib
const
std
::
string
&
new_dir
)
{
reverted
=
false
;
_old_dir
=
get_current_dir
();
set_current_dir
(
new_dir
);
}
~
locally_change_current_dir
()
{
set_current_dir
(
_old_dir
);
revert
(
);
}
const
std
::
string
&
old_dir
(
...
...
@@ -33,7 +34,18 @@ namespace dlib
return
_old_dir
;
}
void
revert
(
)
{
if
(
!
reverted
)
{
set_current_dir
(
_old_dir
);
reverted
=
true
;
}
}
private
:
bool
reverted
;
std
::
string
_old_dir
;
};
...
...
dlib/svm/kkmeans.h
View file @
ad97e1f3
...
...
@@ -477,6 +477,138 @@ namespace dlib
}
}
}
// ----------------------------------------------------------------------------------------
template
<
typename
array_type
,
typename
sample_type
,
typename
alloc
>
void
find_clusters_using_angular_kmeans
(
const
array_type
&
samples
,
std
::
vector
<
sample_type
,
alloc
>&
centers
,
unsigned
long
max_iter
=
1000
)
{
// make sure requires clause is not broken
DLIB_ASSERT
(
samples
.
size
()
>
0
&&
centers
.
size
()
>
0
,
"
\t
void find_clusters_using_angular_kmeans()"
<<
"
\n\t
You passed invalid arguments to this function"
<<
"
\n\t
samples.size(): "
<<
samples
.
size
()
<<
"
\n\t
centers.size(): "
<<
centers
.
size
()
);
#ifdef ENABLE_ASSERTS
{
const
long
nr
=
samples
[
0
].
nr
();
const
long
nc
=
samples
[
0
].
nc
();
for
(
unsigned
long
i
=
0
;
i
<
samples
.
size
();
++
i
)
{
DLIB_ASSERT
(
is_vector
(
samples
[
i
])
&&
samples
[
i
].
nr
()
==
nr
&&
samples
[
i
].
nc
()
==
nc
,
"
\t
void find_clusters_using_angular_kmeans()"
<<
"
\n\t
You passed invalid arguments to this function"
<<
"
\n\t
is_vector(samples[i]): "
<<
is_vector
(
samples
[
i
])
<<
"
\n\t
samples[i].nr(): "
<<
samples
[
i
].
nr
()
<<
"
\n\t
nr: "
<<
nr
<<
"
\n\t
samples[i].nc(): "
<<
samples
[
i
].
nc
()
<<
"
\n\t
nc: "
<<
nc
<<
"
\n\t
i: "
<<
i
);
}
}
#endif
typedef
typename
sample_type
::
type
scalar_type
;
sample_type
zero
(
centers
[
0
]);
set_all_elements
(
zero
,
0
);
unsigned
long
seed
=
0
;
// tells which center a sample belongs to
std
::
vector
<
unsigned
long
>
assignments
(
samples
.
size
(),
samples
.
size
());
std
::
vector
<
double
>
lengths
;
for
(
unsigned
long
i
=
0
;
i
<
samples
.
size
();
++
i
)
{
lengths
.
push_back
(
length
(
samples
[
i
]));
// If there are zero vectors in samples then just say their length is 1 so we
// can avoid a division by zero check later on. Also, this doesn't matter
// since zero vectors can be assigned to any cluster randomly as there is no
// basis for picking one based on angle.
if
(
lengths
.
back
()
==
0
)
lengths
.
back
()
=
1
;
}
// We will keep the centers as unit vectors at all times throughout the processing.
for
(
unsigned
long
i
=
0
;
i
<
centers
.
size
();
++
i
)
{
double
len
=
length
(
centers
[
i
]);
// Avoid having length 0 centers. If that is the case then pick another center
// at random.
while
(
len
==
0
)
{
centers
[
i
]
=
matrix_cast
<
scalar_type
>
(
gaussian_randm
(
centers
[
i
].
nr
(),
centers
[
i
].
nc
(),
seed
++
));
len
=
length
(
centers
[
i
]);
}
centers
[
i
]
/=
len
;
}
unsigned
long
iter
=
0
;
bool
centers_changed
=
true
;
while
(
centers_changed
&&
iter
<
max_iter
)
{
++
iter
;
centers_changed
=
false
;
// loop over each sample and see which center it is closest to
for
(
unsigned
long
i
=
0
;
i
<
samples
.
size
();
++
i
)
{
// find the best center for sample[i]
scalar_type
best_angle
=
std
::
numeric_limits
<
scalar_type
>::
max
();
unsigned
long
best_center
=
0
;
for
(
unsigned
long
j
=
0
;
j
<
centers
.
size
();
++
j
)
{
scalar_type
angle
=
-
dot
(
centers
[
j
],
samples
[
i
])
/
lengths
[
i
];
if
(
angle
<
best_angle
)
{
best_angle
=
angle
;
best_center
=
j
;
}
}
if
(
assignments
[
i
]
!=
best_center
)
{
centers_changed
=
true
;
assignments
[
i
]
=
best_center
;
}
}
// now update all the centers
centers
.
assign
(
centers
.
size
(),
zero
);
for
(
unsigned
long
i
=
0
;
i
<
samples
.
size
();
++
i
)
{
centers
[
assignments
[
i
]]
+=
samples
[
i
];
}
// Now length normalize all the centers.
for
(
unsigned
long
i
=
0
;
i
<
centers
.
size
();
++
i
)
{
double
len
=
length
(
centers
[
i
]);
// Avoid having length 0 centers. If that is the case then pick another center
// at random.
while
(
len
==
0
)
{
centers
[
i
]
=
matrix_cast
<
scalar_type
>
(
gaussian_randm
(
centers
[
i
].
nr
(),
centers
[
i
].
nc
(),
seed
++
));
len
=
length
(
centers
[
i
]);
centers_changed
=
true
;
}
centers
[
i
]
/=
len
;
}
}
}
// ----------------------------------------------------------------------------------------
...
...
dlib/svm/kkmeans_abstract.h
View file @
ad97e1f3
...
...
@@ -283,7 +283,7 @@ namespace dlib
- centers.size() > 0
- array_type == something with an interface compatible with std::vector
and it must contain row or column vectors capable of being stored in
sample_type objects
sample_type objects
.
- sample_type == a dlib::matrix capable of representing vectors
ensures
- performs regular old linear kmeans clustering on the samples. The clustering
...
...
@@ -293,6 +293,46 @@ namespace dlib
terminates.
!*/
// ----------------------------------------------------------------------------------------
template
<
typename
array_type
,
typename
sample_type
,
typename
alloc
>
void
find_clusters_using_angular_kmeans
(
const
array_type
&
samples
,
std
::
vector
<
sample_type
,
alloc
>&
centers
,
unsigned
long
max_iter
=
1000
);
/*!
requires
- samples.size() > 0
- samples == a bunch of row or column vectors and they all must be of the
same length.
- centers.size() > 0
- array_type == something with an interface compatible with std::vector
and it must contain row or column vectors capable of being stored in
sample_type objects.
- sample_type == a dlib::matrix capable of representing vectors
ensures
- performs linear kmeans clustering on the samples, except instead of using
Euclidean distance to compare samples to the centers it uses the angle
between a sample and a center (with respect to the origin). So we try to
cluster samples together if they have small angles with respect to each
other. The clustering begins with the initial set of centers given as an
argument to this function. When it finishes #centers will contain the
resulting centers.
- for all valid i:
- length(#centers[i]) == 1
(i.e. the output centers are scaled to be unit vectors since their
magnitude is irrelevant. Moreover, this makes it so you can use
functions like nearest_center() with #centers to find the cluster
assignments.)
- No more than max_iter iterations will be performed before this function
terminates.
!*/
// ----------------------------------------------------------------------------------------
template
<
...
...
dlib/test/kmeans.cpp
View file @
ad97e1f3
...
...
@@ -44,32 +44,63 @@ namespace
randomize_samples
(
samples
);
std
::
vector
<
sample_type
>
centers
;
pick_initial_centers
(
seed_centers
.
size
(),
centers
,
samples
,
linear_kernel
<
sample_type
>
());
{
std
::
vector
<
sample_type
>
centers
;
pick_initial_centers
(
seed_centers
.
size
(),
centers
,
samples
,
linear_kernel
<
sample_type
>
());
find_clusters_using_kmeans
(
samples
,
centers
);
find_clusters_using_kmeans
(
samples
,
centers
);
DLIB_TEST
(
centers
.
size
()
==
seed_centers
.
size
());
DLIB_TEST
(
centers
.
size
()
==
seed_centers
.
size
());
std
::
vector
<
int
>
hits
(
centers
.
size
(),
0
);
for
(
unsigned
long
i
=
0
;
i
<
samples
.
size
();
++
i
)
{
unsigned
long
best_idx
=
0
;
double
best_dist
=
1e100
;
for
(
unsigned
long
j
=
0
;
j
<
centers
.
size
();
++
j
)
std
::
vector
<
int
>
hits
(
centers
.
size
(),
0
);
for
(
unsigned
long
i
=
0
;
i
<
samples
.
size
();
++
i
)
{
if
(
length
(
samples
[
i
]
-
centers
[
j
])
<
best_dist
)
unsigned
long
best_idx
=
0
;
double
best_dist
=
1e100
;
for
(
unsigned
long
j
=
0
;
j
<
centers
.
size
();
++
j
)
{
best_dist
=
length
(
samples
[
i
]
-
centers
[
j
]);
best_idx
=
j
;
if
(
length
(
samples
[
i
]
-
centers
[
j
])
<
best_dist
)
{
best_dist
=
length
(
samples
[
i
]
-
centers
[
j
]);
best_idx
=
j
;
}
}
hits
[
best_idx
]
++
;
}
hits
[
best_idx
]
++
;
}
for
(
unsigned
long
i
=
0
;
i
<
hits
.
size
();
++
i
)
for
(
unsigned
long
i
=
0
;
i
<
hits
.
size
();
++
i
)
{
DLIB_TEST
(
hits
[
i
]
==
250
);
}
}
{
DLIB_TEST
(
hits
[
i
]
==
250
);
std
::
vector
<
sample_type
>
centers
;
pick_initial_centers
(
seed_centers
.
size
(),
centers
,
samples
,
linear_kernel
<
sample_type
>
());
find_clusters_using_angular_kmeans
(
samples
,
centers
);
DLIB_TEST
(
centers
.
size
()
==
seed_centers
.
size
());
std
::
vector
<
int
>
hits
(
centers
.
size
(),
0
);
for
(
unsigned
long
i
=
0
;
i
<
samples
.
size
();
++
i
)
{
unsigned
long
best_idx
=
0
;
double
best_dist
=
1e100
;
for
(
unsigned
long
j
=
0
;
j
<
centers
.
size
();
++
j
)
{
if
(
length
(
samples
[
i
]
-
centers
[
j
])
<
best_dist
)
{
best_dist
=
length
(
samples
[
i
]
-
centers
[
j
]);
best_idx
=
j
;
}
}
hits
[
best_idx
]
++
;
}
for
(
unsigned
long
i
=
0
;
i
<
hits
.
size
();
++
i
)
{
DLIB_TEST
(
hits
[
i
]
==
250
);
}
}
}
...
...
docs/docs/ml.xml
View file @
ad97e1f3
...
...
@@ -111,6 +111,7 @@ Davis E. King. <a href="http://jmlr.csail.mit.edu/papers/volume10/king09a/king09
<item>
pick_initial_centers
</item>
<item>
kkmeans
</item>
<item>
find_clusters_using_kmeans
</item>
<item>
find_clusters_using_angular_kmeans
</item>
<item>
nearest_center
</item>
<item>
newman_cluster
</item>
<item>
spectral_cluster
</item>
...
...
@@ -357,7 +358,23 @@ Davis E. King. <a href="http://jmlr.csail.mit.edu/papers/volume10/king09a/king09
<file>
dlib/clustering.h
</file>
<spec_file
link=
"true"
>
dlib/svm/kkmeans_abstract.h
</spec_file>
<description>
This is just a simple linear kmeans clustering implementation.
This is a simple linear kmeans clustering implementation.
It uses Euclidean distance to compare samples.
</description>
</component>
<!-- ************************************************************************* -->
<component>
<name>
find_clusters_using_angular_kmeans
</name>
<file>
dlib/clustering.h
</file>
<spec_file
link=
"true"
>
dlib/svm/kkmeans_abstract.h
</spec_file>
<description>
This is a simple linear kmeans clustering implementation.
To compare a sample to a cluster, it measures the angle between them
with respect to the origin. Therefore, it tries to find clusters
of points that all have small angles between each cluster member.
</description>
</component>
...
...
docs/docs/term_index.xml
View file @
ad97e1f3
...
...
@@ -424,6 +424,7 @@
<term
file=
"dlib/statistics/dpca_abstract.h.html"
name=
"discriminant_pca_error"
include=
"dlib/statistics.h"
/>
<term
file=
"ml.html"
name=
"kkmeans"
include=
"dlib/clustering.h"
/>
<term
file=
"ml.html"
name=
"find_clusters_using_kmeans"
include=
"dlib/clustering.h"
/>
<term
file=
"ml.html"
name=
"find_clusters_using_angular_kmeans"
include=
"dlib/clustering.h"
/>
<term
file=
"ml.html"
name=
"nearest_center"
include=
"dlib/clustering.h"
/>
<term
file=
"ml.html"
name=
"newman_cluster"
include=
"dlib/clustering.h"
/>
<term
file=
"ml.html"
name=
"spectral_cluster"
include=
"dlib/clustering.h"
/>
...
...
tools/imglab/CMakeLists.txt
View file @
ad97e1f3
...
...
@@ -24,6 +24,7 @@ ADD_EXECUTABLE(${target_name}
src/convert_idl.cpp
src/common.h
src/common.cpp
src/cluster.cpp
)
...
...
tools/imglab/src/cluster.cpp
0 → 100644
View file @
ad97e1f3
// Copyright (C) 2015 Davis E. King (davis@dlib.net)
// License: Boost Software License See LICENSE.txt for the full license.
#include "cluster.h"
#include <dlib/console_progress_indicator.h>
#include <dlib/image_io.h>
#include <dlib/data_io.h>
#include <dlib/image_transforms.h>
#include <dlib/misc_api.h>
#include <dlib/dir_nav.h>
#include <dlib/clustering.h>
#include <dlib/svm.h>
// ----------------------------------------------------------------------------------------
using
namespace
std
;
using
namespace
dlib
;
// ----------------------------------------------------------------------------
struct
assignment
{
unsigned
long
c
;
double
dist
;
unsigned
long
idx
;
bool
operator
<
(
const
assignment
&
item
)
const
{
return
dist
<
item
.
dist
;
}
};
std
::
vector
<
assignment
>
angular_cluster
(
std
::
vector
<
matrix
<
double
,
0
,
1
>
>
feats
,
const
unsigned
long
num_clusters
)
{
DLIB_CASSERT
(
feats
.
size
()
!=
0
,
"The dataset can't be empty"
);
for
(
unsigned
long
i
=
0
;
i
<
feats
.
size
();
++
i
)
{
DLIB_CASSERT
(
feats
[
i
].
size
()
==
feats
[
0
].
size
(),
"All feature vectors must have the same length."
);
}
// find the centroid of feats
matrix
<
double
,
0
,
1
>
m
;
for
(
unsigned
long
i
=
0
;
i
<
feats
.
size
();
++
i
)
m
+=
feats
[
i
];
m
/=
feats
.
size
();
// Now center feats and then project onto the unit sphere. The reason for projecting
// onto the unit sphere is so pick_initial_centers() works in a sensible way.
for
(
unsigned
long
i
=
0
;
i
<
feats
.
size
();
++
i
)
{
feats
[
i
]
-=
m
;
double
len
=
length
(
feats
[
i
]);
if
(
len
!=
0
)
feats
[
i
]
/=
len
;
}
// now do angular clustering of the points
std
::
vector
<
matrix
<
double
,
0
,
1
>
>
centers
;
pick_initial_centers
(
num_clusters
,
centers
,
feats
,
linear_kernel
<
matrix
<
double
,
0
,
1
>
>
(),
0.05
);
find_clusters_using_angular_kmeans
(
feats
,
centers
);
// and then report the resulting assignments
std
::
vector
<
assignment
>
assignments
;
for
(
unsigned
long
i
=
0
;
i
<
feats
.
size
();
++
i
)
{
assignment
temp
;
temp
.
c
=
nearest_center
(
centers
,
feats
[
i
]);
temp
.
dist
=
length
(
feats
[
i
]
-
centers
[
temp
.
c
]);
temp
.
idx
=
i
;
assignments
.
push_back
(
temp
);
}
return
assignments
;
}
// ----------------------------------------------------------------------------------------
bool
compare_first
(
const
std
::
pair
<
double
,
image_dataset_metadata
::
image
>&
a
,
const
std
::
pair
<
double
,
image_dataset_metadata
::
image
>&
b
)
{
return
a
.
first
<
b
.
first
;
}
// ----------------------------------------------------------------------------------------
double
mean_aspect_ratio
(
const
image_dataset_metadata
::
dataset
&
data
)
{
double
sum
=
0
;
double
cnt
=
0
;
for
(
unsigned
long
i
=
0
;
i
<
data
.
images
.
size
();
++
i
)
{
for
(
unsigned
long
j
=
0
;
j
<
data
.
images
[
i
].
boxes
.
size
();
++
j
)
{
rectangle
rect
=
data
.
images
[
i
].
boxes
[
j
].
rect
;
if
(
rect
.
area
()
==
0
)
continue
;
sum
+=
rect
.
width
()
/
(
double
)
rect
.
height
();
++
cnt
;
}
}
if
(
cnt
!=
0
)
return
sum
/
cnt
;
else
return
0
;
}
// ----------------------------------------------------------------------------------------
int
cluster_dataset
(
const
dlib
::
command_line_parser
&
parser
)
{
// make sure the user entered an argument to this program
if
(
parser
.
number_of_arguments
()
!=
1
)
{
cerr
<<
"The --cluster option requires you to give one XML file on the command line."
<<
endl
;
return
EXIT_FAILURE
;
}
const
unsigned
long
num_clusters
=
get_option
(
parser
,
"cluster"
,
2
);
const
unsigned
long
chip_size
=
get_option
(
parser
,
"size"
,
8000
);
image_dataset_metadata
::
dataset
data
;
set_current_dir
(
get_parent_directory
(
file
(
parser
[
0
])));
image_dataset_metadata
::
load_image_dataset_metadata
(
data
,
parser
[
0
]);
const
double
aspect_ratio
=
mean_aspect_ratio
(
data
);
dlib
::
array
<
array2d
<
rgb_pixel
>
>
images
;
std
::
vector
<
matrix
<
double
,
0
,
1
>
>
feats
;
console_progress_indicator
pbar
(
data
.
images
.
size
());
// extract all the object chips and HOG features.
cout
<<
"Loading image data..."
<<
endl
;
for
(
unsigned
long
i
=
0
;
i
<
data
.
images
.
size
();
++
i
)
{
pbar
.
print_status
(
i
);
if
(
data
.
images
[
i
].
boxes
.
size
()
==
0
)
continue
;
array2d
<
rgb_pixel
>
img
,
chip
;
load_image
(
img
,
data
.
images
[
i
].
filename
);
for
(
unsigned
long
j
=
0
;
j
<
data
.
images
[
i
].
boxes
.
size
();
++
j
)
{
if
(
data
.
images
[
i
].
boxes
[
j
].
ignore
)
continue
;
drectangle
rect
=
data
.
images
[
i
].
boxes
[
j
].
rect
;
rect
=
set_aspect_ratio
(
rect
,
aspect_ratio
);
extract_image_chip
(
img
,
chip_details
(
rect
,
chip_size
),
chip
);
feats
.
push_back
(
extract_fhog_features
(
chip
));
images
.
push_back
(
chip
);
}
}
if
(
feats
.
size
()
==
0
)
{
cerr
<<
"No non-ignored object boxes found in the XML dataset. You can't cluster an empty dataset."
<<
endl
;
return
EXIT_FAILURE
;
}
cout
<<
"
\n
Clustering objects..."
<<
endl
;
std
::
vector
<
assignment
>
assignments
=
angular_cluster
(
feats
,
num_clusters
);
// Now output each cluster to disk as an XML file.
for
(
unsigned
long
c
=
0
;
c
<
num_clusters
;
++
c
)
{
// We are going to accumulate all the image metadata for cluster c. We put it
// into idata so we can sort the images such that images with central chips
// come before less central chips. The idea being to get the good chips to
// show up first in the listing, making it easy to manually remove bad ones if
// that is desired.
std
::
vector
<
std
::
pair
<
double
,
image_dataset_metadata
::
image
>
>
idata
(
data
.
images
.
size
());
unsigned
long
idx
=
0
;
for
(
unsigned
long
i
=
0
;
i
<
data
.
images
.
size
();
++
i
)
{
if
(
data
.
images
[
i
].
boxes
.
size
()
==
0
)
continue
;
idata
[
i
].
first
=
std
::
numeric_limits
<
double
>::
infinity
();
idata
[
i
].
second
.
filename
=
data
.
images
[
i
].
filename
;
for
(
unsigned
long
j
=
0
;
j
<
data
.
images
[
i
].
boxes
.
size
();
++
j
)
{
idata
[
i
].
second
.
boxes
.
push_back
(
data
.
images
[
i
].
boxes
[
j
]);
if
(
data
.
images
[
i
].
boxes
[
j
].
ignore
)
continue
;
// If this box goes into cluster c then update the score for the whole
// image based on this boxes' score. Otherwise, mark the box as
// ignored.
if
(
assignments
[
idx
].
c
==
c
)
idata
[
i
].
first
=
std
::
min
(
idata
[
i
].
first
,
assignments
[
idx
].
dist
);
else
idata
[
i
].
second
.
boxes
.
back
().
ignore
=
true
;
++
idx
;
}
}
// now save idata to an xml file.
std
::
sort
(
idata
.
begin
(),
idata
.
end
(),
compare_first
);
image_dataset_metadata
::
dataset
cdata
;
cdata
.
comment
=
data
.
comment
+
"
\n\n
This file contains objects which were clustered into group "
+
cast_to_string
(
c
+
1
)
+
" of "
+
cast_to_string
(
num_clusters
)
+
" groups with a chip size of "
+
cast_to_string
(
chip_size
)
+
" by imglab."
;
cdata
.
name
=
data
.
name
;
for
(
unsigned
long
i
=
0
;
i
<
idata
.
size
();
++
i
)
{
// if this image has non-ignored boxes in it then include it in the output.
if
(
idata
[
i
].
first
!=
std
::
numeric_limits
<
double
>::
infinity
())
cdata
.
images
.
push_back
(
idata
[
i
].
second
);
}
string
outfile
=
"cluster_"
+
pad_int_with_zeros
(
c
+
1
,
3
)
+
".xml"
;
cout
<<
"Saving "
<<
outfile
<<
endl
;
save_image_dataset_metadata
(
cdata
,
outfile
);
}
// Now output each cluster to disk as a big tiled jpeg file. Sort everything so, just
// like in the xml file above, the best objects come first in the tiling.
std
::
sort
(
assignments
.
begin
(),
assignments
.
end
());
for
(
unsigned
long
c
=
0
;
c
<
num_clusters
;
++
c
)
{
dlib
::
array
<
array2d
<
rgb_pixel
>
>
temp
;
for
(
unsigned
long
i
=
0
;
i
<
assignments
.
size
();
++
i
)
{
if
(
assignments
[
i
].
c
==
c
)
temp
.
push_back
(
images
[
assignments
[
i
].
idx
]);
}
string
outfile
=
"cluster_"
+
pad_int_with_zeros
(
c
+
1
,
3
)
+
".jpg"
;
cout
<<
"Saving "
<<
outfile
<<
endl
;
save_jpeg
(
tile_images
(
temp
),
outfile
);
}
return
EXIT_SUCCESS
;
}
// ----------------------------------------------------------------------------------------
tools/imglab/src/cluster.h
0 → 100644
View file @
ad97e1f3
// Copyright (C) 2015 Davis E. King (davis@dlib.net)
// License: Boost Software License See LICENSE.txt for the full license.
#ifndef DLIB_IMGLAB_ClUSTER_H_
#define DLIB_IMGLAB_ClUSTER_H_
#include <dlib/cmd_line_parser.h>
int
cluster_dataset
(
const
dlib
::
command_line_parser
&
parser
);
#endif //DLIB_IMGLAB_ClUSTER_H_
tools/imglab/src/main.cpp
View file @
ad97e1f3
...
...
@@ -5,9 +5,11 @@
#include "convert_pascal_xml.h"
#include "convert_pascal_v1.h"
#include "convert_idl.h"
#include "cluster.h"
#include <dlib/cmd_line_parser.h>
#include <dlib/image_transforms.h>
#include <dlib/svm.h>
#include <dlib/console_progress_indicator.h>
#include <iostream>
#include <fstream>
...
...
@@ -17,7 +19,7 @@
#include <dlib/dir_nav.h>
const
char
*
VERSION
=
"1.
1
"
;
const
char
*
VERSION
=
"1.
2
"
;
...
...
@@ -314,6 +316,75 @@ void flip_dataset(const command_line_parser& parser)
#endif
}
// ----------------------------------------------------------------------------------------
int
tile_dataset
(
const
command_line_parser
&
parser
)
{
#if defined(DLIB_PNG_SUPPORT) && defined(DLIB_JPEG_SUPPORT)
if
(
parser
.
number_of_arguments
()
!=
1
)
{
cerr
<<
"The --tile option requires you to give one XML file on the command line."
<<
endl
;
return
EXIT_FAILURE
;
}
string
out_image
=
parser
.
option
(
"tile"
).
argument
();
string
ext
=
right_substr
(
out_image
,
"."
);
if
(
ext
!=
"png"
&&
ext
!=
"jpg"
)
{
cerr
<<
"The output image file must have either .png or .jpg extension."
<<
endl
;
return
EXIT_FAILURE
;
}
const
unsigned
long
chip_size
=
get_option
(
parser
,
"size"
,
8000
);
dlib
::
image_dataset_metadata
::
dataset
data
;
load_image_dataset_metadata
(
data
,
parser
[
0
]);
locally_change_current_dir
chdir
(
get_parent_directory
(
file
(
parser
[
0
])));
dlib
::
array
<
array2d
<
rgb_pixel
>
>
images
;
console_progress_indicator
pbar
(
data
.
images
.
size
());
for
(
unsigned
long
i
=
0
;
i
<
data
.
images
.
size
();
++
i
)
{
// don't even bother loading images that don't have objects.
if
(
data
.
images
[
i
].
boxes
.
size
()
==
0
)
continue
;
pbar
.
print_status
(
i
);
array2d
<
rgb_pixel
>
img
;
load_image
(
img
,
data
.
images
[
i
].
filename
);
// figure out what chips we want to take from this image
std
::
vector
<
chip_details
>
dets
;
for
(
unsigned
long
j
=
0
;
j
<
data
.
images
[
i
].
boxes
.
size
();
++
j
)
{
if
(
data
.
images
[
i
].
boxes
[
j
].
ignore
)
continue
;
rectangle
rect
=
data
.
images
[
i
].
boxes
[
j
].
rect
;
dets
.
push_back
(
chip_details
(
rect
,
chip_size
));
}
// Now grab all those chips at once.
dlib
::
array
<
array2d
<
rgb_pixel
>
>
chips
;
extract_image_chips
(
img
,
dets
,
chips
);
// and put the chips into the output.
for
(
unsigned
long
j
=
0
;
j
<
chips
.
size
();
++
j
)
images
.
push_back
(
chips
[
j
]);
}
chdir
.
revert
();
if
(
ext
==
"png"
)
save_png
(
tile_images
(
images
),
out_image
);
else
save_jpeg
(
tile_images
(
images
),
out_image
);
return
EXIT_SUCCESS
;
#else
throw
dlib
::
error
(
"imglab must be compiled with libpng and libjpeg if you want to use the --tile option."
);
#endif
}
// ----------------------------------------------------------------------------------------
int
main
(
int
argc
,
char
**
argv
)
...
...
@@ -332,9 +403,14 @@ int main(int argc, char** argv)
parser
.
add_option
(
"convert"
,
"Convert foreign image Annotations from <arg> format to the imglab format. "
"Supported formats: pascal-xml, pascal-v1, idl."
,
1
);
parser
.
set_group_name
(
"Viewing/Editing XML files"
);
parser
.
set_group_name
(
"Viewing XML files"
);
parser
.
add_option
(
"tile"
,
"Chip out all the objects and save them as one big image called <arg>."
,
1
);
parser
.
add_option
(
"size"
,
"When using --tile or --cluster, make each extracted object contain "
"about <arg> pixels (default 8000)."
,
1
);
parser
.
add_option
(
"l"
,
"List all the labels in the given XML file."
);
parser
.
add_option
(
"stats"
,
"List detailed statistics on the object labels in the given XML file."
);
parser
.
set_group_name
(
"Editing/Transforming XML files"
);
parser
.
add_option
(
"rename"
,
"Rename all labels of <arg1> to <arg2>."
,
2
);
parser
.
add_option
(
"parts"
,
"The display will allow image parts to be labeled. The set of allowable parts "
"is defined by <arg> which should be a space separated list of parts."
,
1
);
...
...
@@ -351,31 +427,47 @@ int main(int argc, char** argv)
"<arg2> files are modified."
,
2
);
parser
.
add_option
(
"flip"
,
"Read an XML image dataset from the <arg> XML file and output a left-right flipped "
"version of the dataset and an accompanying flipped XML file named flipped_<arg>."
,
1
);
parser
.
add_option
(
"cluster"
,
"Cluster all the objects in an XML file into <arg> different clusters and save "
"the results as cluster_###.xml and cluster_###.jpg files."
,
1
);
parser
.
parse
(
argc
,
argv
);
const
char
*
singles
[]
=
{
"h"
,
"c"
,
"r"
,
"l"
,
"convert"
,
"parts"
,
"rmdiff"
,
"seed"
,
"shuffle"
,
"split"
,
"add"
,
"flip"
};
const
char
*
singles
[]
=
{
"h"
,
"c"
,
"r"
,
"l"
,
"convert"
,
"parts"
,
"rmdiff"
,
"seed"
,
"shuffle"
,
"split"
,
"add"
,
"flip"
,
"tile"
,
"size"
,
"cluster"
};
parser
.
check_one_time_options
(
singles
);
const
char
*
c_sub_ops
[]
=
{
"r"
,
"convert"
};
parser
.
check_sub_options
(
"c"
,
c_sub_ops
);
parser
.
check_sub_option
(
"shuffle"
,
"seed"
);
const
char
*
size_parent_ops
[]
=
{
"tile"
,
"cluster"
};
parser
.
check_sub_options
(
size_parent_ops
,
"size"
);
parser
.
check_incompatible_options
(
"c"
,
"l"
);
parser
.
check_incompatible_options
(
"c"
,
"rmdiff"
);
parser
.
check_incompatible_options
(
"c"
,
"add"
);
parser
.
check_incompatible_options
(
"c"
,
"flip"
);
parser
.
check_incompatible_options
(
"c"
,
"rename"
);
parser
.
check_incompatible_options
(
"c"
,
"parts"
);
parser
.
check_incompatible_options
(
"c"
,
"tile"
);
parser
.
check_incompatible_options
(
"c"
,
"cluster"
);
parser
.
check_incompatible_options
(
"l"
,
"rename"
);
parser
.
check_incompatible_options
(
"l"
,
"add"
);
parser
.
check_incompatible_options
(
"l"
,
"parts"
);
parser
.
check_incompatible_options
(
"l"
,
"flip"
);
parser
.
check_incompatible_options
(
"add"
,
"flip"
);
parser
.
check_incompatible_options
(
"add"
,
"tile"
);
parser
.
check_incompatible_options
(
"flip"
,
"tile"
);
parser
.
check_incompatible_options
(
"cluster"
,
"tile"
);
parser
.
check_incompatible_options
(
"flip"
,
"cluster"
);
parser
.
check_incompatible_options
(
"add"
,
"cluster"
);
parser
.
check_incompatible_options
(
"shuffle"
,
"tile"
);
parser
.
check_incompatible_options
(
"convert"
,
"l"
);
parser
.
check_incompatible_options
(
"convert"
,
"rename"
);
parser
.
check_incompatible_options
(
"convert"
,
"parts"
);
parser
.
check_incompatible_options
(
"convert"
,
"cluster"
);
parser
.
check_incompatible_options
(
"rmdiff"
,
"rename"
);
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
(
"size"
,
10
*
10
,
1000
*
1000
);
if
(
parser
.
option
(
"h"
))
{
...
...
@@ -406,6 +498,16 @@ int main(int argc, char** argv)
return
EXIT_SUCCESS
;
}
if
(
parser
.
option
(
"tile"
))
{
return
tile_dataset
(
parser
);
}
if
(
parser
.
option
(
"cluster"
))
{
return
cluster_dataset
(
parser
);
}
if
(
parser
.
option
(
"c"
))
{
if
(
parser
.
option
(
"convert"
))
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment