Implémentation de l'algorithme de knn
This commit is contained in:
parent
dfe200f290
commit
b641c5986a
5 changed files with 212 additions and 119 deletions
|
@ -4,6 +4,8 @@ project(miniprojet)
|
||||||
set(PROJECT_CFLAGS "-Wall -Wextra -Wno-missing-braces -std=c++1z")
|
set(PROJECT_CFLAGS "-Wall -Wextra -Wno-missing-braces -std=c++1z")
|
||||||
find_package(OpenCV REQUIRED)
|
find_package(OpenCV REQUIRED)
|
||||||
|
|
||||||
|
add_compile_options(-std=c++17)
|
||||||
|
|
||||||
add_subdirectory(src)
|
add_subdirectory(src)
|
||||||
add_subdirectory(examples)
|
add_subdirectory(examples)
|
||||||
add_subdirectory(jean-luc-collette)
|
add_subdirectory(jean-luc-collette)
|
||||||
|
|
|
@ -1,13 +1,7 @@
|
||||||
# file(GLOB headers *.hpp)
|
|
||||||
# file(GLOB lib_files *.cpp)
|
|
||||||
|
|
||||||
add_executable(traitement traitement.cpp)
|
add_executable(traitement traitement.cpp)
|
||||||
target_link_libraries(traitement ${OpenCV_LIBS})
|
target_link_libraries(traitement ${OpenCV_LIBS})
|
||||||
|
|
||||||
#add_executable(k_proches_voisins k_proches_voisins.cpp)
|
find_package(Boost COMPONENTS system filesystem REQUIRED)
|
||||||
#target_link_libraries(k_proches_voisins ${OpenCV_LIBS})
|
|
||||||
# target_include_directories(blk PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
|
|
||||||
# target_compile_options (blk PUBLIC -std=c++11 )
|
|
||||||
|
|
||||||
# install(TARGETS blk DESTINATION lib )
|
add_executable(knn knn.cpp)
|
||||||
# install(FILES ${headers} DESTINATION include/${CMAKE_PROJECT_NAME})
|
target_link_libraries(knn ${OpenCV_LIBS} ${Boost_FILESYSTEM_LIBRARY} ${Boost_SYSTEM_LIBRARY})
|
||||||
|
|
|
@ -1,87 +0,0 @@
|
||||||
#include <map>
|
|
||||||
#include "math.hpp"
|
|
||||||
#include <stdexcept>
|
|
||||||
|
|
||||||
double distance(math::csignal& v1, math::csignal& v2, int n){
|
|
||||||
if (v1.size() != v2.size()) {
|
|
||||||
throw std::runtime_error("les deux vecteurs doivent être de même longueur");
|
|
||||||
}
|
|
||||||
double d;
|
|
||||||
double di;
|
|
||||||
for (int i=0; i<v1.size(); ++i){
|
|
||||||
di = std::abs(v1[i] - v2[i]);
|
|
||||||
di = std::pow(di, n);
|
|
||||||
d = d + di;
|
|
||||||
};
|
|
||||||
return std::pow(d, 1/n);
|
|
||||||
};
|
|
||||||
|
|
||||||
int argmax(std::vector<int>& v){
|
|
||||||
int arg = 0;
|
|
||||||
int max = v[0];
|
|
||||||
for(int i = 1; i < v.size() ; ++i){
|
|
||||||
if (v[i]>max){
|
|
||||||
arg = i;
|
|
||||||
max = v[i];
|
|
||||||
};
|
|
||||||
};
|
|
||||||
return arg;
|
|
||||||
};
|
|
||||||
|
|
||||||
//int main(math::csignal new_vect, std::map< math::csignal, std::string > dico, int k){
|
|
||||||
int main(int argc, char** argv) {
|
|
||||||
std::vector<std::pair<double, math::csignal>> k_min;
|
|
||||||
std::map<math::csignal, std::string> dico;
|
|
||||||
math::csignal new_vect;
|
|
||||||
int k;
|
|
||||||
double d;
|
|
||||||
int avance = 0;
|
|
||||||
int arret = 0;
|
|
||||||
int droite = 0;
|
|
||||||
int gauche = 0;
|
|
||||||
int rejet = 0;
|
|
||||||
std::vector<int> vchoix;
|
|
||||||
for(auto& ref_vect : dico){
|
|
||||||
d = distance(new_vect, ref_vect.first);
|
|
||||||
if (k_min.size() < k ){
|
|
||||||
k_min.push_back({d, ref_vect.first});
|
|
||||||
} else if (d < k_min[k-1].first){
|
|
||||||
k_min.push_back({d, ref_vect.first});
|
|
||||||
sort(k_min.begin(), k_min.end());
|
|
||||||
k_min.pop_back();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
for(auto i = k_min.begin(); i != k_min.end(); i++) {
|
|
||||||
if (dico[k_min[i].second].second == "avance"){
|
|
||||||
avance = avance + 1
|
|
||||||
} else if (dico[k_min[i].second].second == "arret"){
|
|
||||||
arret = arret + 1
|
|
||||||
} else if (dico[k_min[i].second].second == "droite"){
|
|
||||||
arret = droite + 1
|
|
||||||
} else if (dico[k_min[i].second].second == "gauche"){
|
|
||||||
arret = gauche + 1
|
|
||||||
} else if (dico[k_min[i].second].second == "rejet"){
|
|
||||||
arret = rejet + 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
vchoix.push_back(avance);
|
|
||||||
vchoix.push_back(arret);
|
|
||||||
vchoix.push_back(droite);
|
|
||||||
vchoix.push_back(gauche);
|
|
||||||
vchoix.push_back(rejet);
|
|
||||||
|
|
||||||
int nchoix = argmax(vchoix);
|
|
||||||
std::string choix;
|
|
||||||
if (nchoix == 0){
|
|
||||||
choix = "avance"
|
|
||||||
} else if (nchoix == 1){
|
|
||||||
choix = "arret"
|
|
||||||
} else if (nchoix == 2){
|
|
||||||
choix = "droite"
|
|
||||||
} else if (nchoix == 3){
|
|
||||||
choix = "gauche"
|
|
||||||
} else if (nchoix == 4){
|
|
||||||
choix = "rejet"
|
|
||||||
}
|
|
||||||
};
|
|
139
tests/src/knn.cpp
Normal file
139
tests/src/knn.cpp
Normal file
|
@ -0,0 +1,139 @@
|
||||||
|
#include <map>
|
||||||
|
#include "math.hpp"
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <queue>
|
||||||
|
#include <opencv2/opencv.hpp>
|
||||||
|
#include <boost/filesystem.hpp>
|
||||||
|
#include <iterator>
|
||||||
|
#include <iostream>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
using dataset = std::vector<std::pair<math::csignal, std::string>>;
|
||||||
|
|
||||||
|
struct path_leaf_string {
|
||||||
|
std::string operator()(const boost::filesystem::directory_entry& entry) const
|
||||||
|
{
|
||||||
|
return entry.path().leaf().string();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
void read_directory(const std::string& name, std::vector<std::string>& v) {
|
||||||
|
boost::filesystem::path p(name);
|
||||||
|
boost::filesystem::directory_iterator start(p);
|
||||||
|
boost::filesystem::directory_iterator end;
|
||||||
|
std::transform(start, end, std::back_inserter(v), path_leaf_string());
|
||||||
|
}
|
||||||
|
|
||||||
|
double distance(math::csignal& v1, math::csignal& v2, int n){
|
||||||
|
if (v1.size() != v2.size()) {
|
||||||
|
throw std::runtime_error("les deux vecteurs doivent être de même longueur");
|
||||||
|
}
|
||||||
|
double d = 0;
|
||||||
|
|
||||||
|
auto v1_it = v1.begin();
|
||||||
|
auto v2_it = v2.begin();
|
||||||
|
|
||||||
|
while (v1_it != v1.end()) {
|
||||||
|
double dist = std::abs(*(v1_it++) - *(v2_it++));
|
||||||
|
d += std::pow(dist, n);
|
||||||
|
}
|
||||||
|
return std::pow(d, 1/n);
|
||||||
|
};
|
||||||
|
|
||||||
|
int argmax(std::vector<int>& v){
|
||||||
|
int arg = 0;
|
||||||
|
int max = v[0];
|
||||||
|
for(int i = 1; i < v.size() ; ++i){
|
||||||
|
if (v[i]>max){
|
||||||
|
arg = i;
|
||||||
|
max = v[i];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
return arg;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct pair_comp {
|
||||||
|
bool operator()(std::pair<double, std::string> a, std::pair<double, std::string> b) {
|
||||||
|
if (a.first == b.first) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (a.first > b.first) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
math::csignal img2desc(std::string filename, int cmax, int threshold) {
|
||||||
|
cv::Mat img = cv::imread(filename, CV_LOAD_IMAGE_COLOR);
|
||||||
|
return math::descriptors(img, cmax, threshold);
|
||||||
|
}
|
||||||
|
|
||||||
|
dataset get_data(std::string path, int size, int cmax, int threshold) {
|
||||||
|
dataset res;
|
||||||
|
std::vector<std::string> dirs;
|
||||||
|
read_directory(path, dirs);
|
||||||
|
for (auto dir: dirs) {
|
||||||
|
std::vector<std::string> files;
|
||||||
|
read_directory(path+"/"+dir, files);
|
||||||
|
|
||||||
|
std::string label = dir;
|
||||||
|
int count = 0;
|
||||||
|
for (int i=0; count<size/4 && i<files.size(); ++i) {
|
||||||
|
try {
|
||||||
|
math::csignal d = img2desc(path+"/"+dir+"/"+files[i], cmax, threshold);
|
||||||
|
res.push_back({d, label});
|
||||||
|
count++;
|
||||||
|
} catch (std::length_error& e) {
|
||||||
|
std::cout << "No contour: image skiped." << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
std::cout << res.size() << std::endl;
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char** argv) {
|
||||||
|
|
||||||
|
int k = 20;
|
||||||
|
int size = 100;
|
||||||
|
std::string path;
|
||||||
|
int cmax = 10;
|
||||||
|
int threshold = 20;
|
||||||
|
|
||||||
|
if (argc > 2) {
|
||||||
|
path = argv[1];
|
||||||
|
threshold = atoi(argv[2]);
|
||||||
|
} else {
|
||||||
|
std::cout << "Invalid number of arguments" << std::endl;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
dataset references = get_data(path, size, cmax, threshold);
|
||||||
|
math::csignal sample = img2desc(path+"/arret/arret0199.jpg", cmax, threshold);
|
||||||
|
std::priority_queue<std::pair<double, std::string>, std::vector<std::pair<double, std::string>>, pair_comp> neighbors;
|
||||||
|
std::map<std::string, int> labels;
|
||||||
|
|
||||||
|
for (auto desc: references) {
|
||||||
|
double d = distance(desc.first, sample, 1);
|
||||||
|
neighbors.push({d, desc.second});
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i=0; i<k; ++i) {
|
||||||
|
std::pair<double, std::string> nearest = neighbors.top();
|
||||||
|
neighbors.pop();
|
||||||
|
labels[nearest.second] += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int max = 0;
|
||||||
|
std::string label;
|
||||||
|
|
||||||
|
for (auto val: labels) {
|
||||||
|
if (val.second > max) {
|
||||||
|
max = val.second;
|
||||||
|
label = val.first;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cout << label << std::endl;
|
||||||
|
};
|
|
@ -5,6 +5,7 @@
|
||||||
#include <opencv2/opencv.hpp>
|
#include <opencv2/opencv.hpp>
|
||||||
#include <iterator>
|
#include <iterator>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
namespace math {
|
namespace math {
|
||||||
|
|
||||||
|
@ -284,29 +285,6 @@ namespace math {
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
csignal descriptors(const contour& cont, int cmax) {
|
|
||||||
csignal z = cont2sig(cont);
|
|
||||||
complex zm = mean(z);
|
|
||||||
csignal tfd = dft(diff(z, zm));
|
|
||||||
tfd /= z.size();
|
|
||||||
int cmin = -cmax;
|
|
||||||
csignal desc = extract(tfd, cmin, cmax);
|
|
||||||
|
|
||||||
if (std::abs(desc[desc.size()/2-1]) > std::abs(desc[desc.size()/2+1])) {
|
|
||||||
std::reverse(desc.begin(), desc.end());
|
|
||||||
}
|
|
||||||
|
|
||||||
double phy = std::arg(desc[desc.size()/2-1]*desc[desc.size()/2+1])/2;
|
|
||||||
desc *= std::exp(complex(0, -phy));
|
|
||||||
double theta = std::arg(desc[desc.size()/2+1]);
|
|
||||||
|
|
||||||
for (int k=0; k<desc.size(); ++k) {
|
|
||||||
desc[k] *= std::exp(complex(0, -theta*(k-cmin)));
|
|
||||||
}
|
|
||||||
desc /= std::abs(desc[desc.size()/2+1]);
|
|
||||||
|
|
||||||
return desc;
|
|
||||||
}
|
|
||||||
|
|
||||||
contour simplify_contour(const contour& cont, int cmax) {
|
contour simplify_contour(const contour& cont, int cmax) {
|
||||||
csignal z = cont2sig(cont);
|
csignal z = cont2sig(cont);
|
||||||
|
@ -346,4 +324,71 @@ namespace math {
|
||||||
}
|
}
|
||||||
return id;
|
return id;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
csignal descriptors(const cv::Mat& img, int cmax, int threshold) {
|
||||||
|
std::vector<std::vector<cv::Point>> contours;
|
||||||
|
|
||||||
|
cv::Mat binary(img.rows, img.cols, CV_8UC1);
|
||||||
|
cv::Mat blur_img;
|
||||||
|
cv::GaussianBlur(img, blur_img, cv::Size(7,7), 1.5, 1.5);
|
||||||
|
|
||||||
|
std::vector<cv::Vec4i> hierarchy;
|
||||||
|
|
||||||
|
math::filter(img, binary, threshold);
|
||||||
|
|
||||||
|
cv::findContours(binary, contours, hierarchy, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE);
|
||||||
|
if (contours.size() > 0) {
|
||||||
|
std::vector<std::vector<cv::Point>> contrs;
|
||||||
|
|
||||||
|
int id = max_cont(contours);
|
||||||
|
|
||||||
|
csignal z = cont2sig(contours[id]);
|
||||||
|
complex zm = mean(z);
|
||||||
|
csignal tfd = dft(diff(z, zm));
|
||||||
|
tfd /= z.size();
|
||||||
|
int cmin = -cmax;
|
||||||
|
csignal desc = extract(tfd, cmin, cmax);
|
||||||
|
|
||||||
|
if (std::abs(desc[desc.size()/2-1]) > std::abs(desc[desc.size()/2+1])) {
|
||||||
|
std::reverse(desc.begin(), desc.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
double phy = std::arg(desc[desc.size()/2-1]*desc[desc.size()/2+1])/2;
|
||||||
|
desc *= std::exp(complex(0, -phy));
|
||||||
|
double theta = std::arg(desc[desc.size()/2+1]);
|
||||||
|
|
||||||
|
for (int k=0; k<desc.size(); ++k) {
|
||||||
|
desc[k] *= std::exp(complex(0, -theta*(k-cmin)));
|
||||||
|
}
|
||||||
|
desc /= std::abs(desc[desc.size()/2+1]);
|
||||||
|
|
||||||
|
return desc;
|
||||||
|
} else {
|
||||||
|
throw std::length_error("No contour detected !");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
csignal descriptors(const contour& cont, int cmax) {
|
||||||
|
csignal z = cont2sig(cont);
|
||||||
|
complex zm = mean(z);
|
||||||
|
csignal tfd = dft(diff(z, zm));
|
||||||
|
tfd /= z.size();
|
||||||
|
int cmin = -cmax;
|
||||||
|
csignal desc = extract(tfd, cmin, cmax);
|
||||||
|
|
||||||
|
if (std::abs(desc[desc.size()/2-1]) > std::abs(desc[desc.size()/2+1])) {
|
||||||
|
std::reverse(desc.begin(), desc.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
double phy = std::arg(desc[desc.size()/2-1]*desc[desc.size()/2+1])/2;
|
||||||
|
desc *= std::exp(complex(0, -phy));
|
||||||
|
double theta = std::arg(desc[desc.size()/2+1]);
|
||||||
|
|
||||||
|
for (int k=0; k<desc.size(); ++k) {
|
||||||
|
desc[k] *= std::exp(complex(0, -theta*(k-cmin)));
|
||||||
|
}
|
||||||
|
desc /= std::abs(desc[desc.size()/2+1]);
|
||||||
|
|
||||||
|
return desc;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue