m2b-gd-tp8/tp8-andré-colin.cpp

362 lines
7.9 KiB
C++
Raw Permalink Normal View History

2021-12-13 08:59:59 +01:00
/*
demo1.cpp - exemple d'utilisation de Astico2D
CC BY-SA Edouard.Thiel@univ-amu.fr - 22/08/2021
Usage :
$ make demo1
$ ./demo1 [-mag width height] [-thr seuil] image_in [image_out]
*/
/*
Pour le rendu de TP :
- renommez ce fichier tp<n°-de-la-planche>-<vos-noms>.cpp
- écrivez ci-dessous vos NOMS Prénoms et la date de la version :
ANDRÉ Jérémy et COLIN Cyril - version du 2021-12-03
*/
#include "astico2d.hpp"
struct ES {
size_t w, h;
std::vector<int> weights;
std::vector<cv::Point> offsets;
std::string name;
ES(size_t w, size_t h, std::vector<int> weights, std::string name="")
:w(w),
h(h),
weights(weights),
offsets(w * h),
name(name) {
assert(weights.size() == w * h);
for (size_t i = 0; i < weights.size(); i++) {
int x_off = (i % w) - w/2;
int y_off = (i / w) - h/2;
offsets[i] = {x_off, y_off};
}
}
ES() {}
static ES Square3(int weight=1) {
return ES(3, 3, std::vector<int>(3 * 3, weight), "Carré 3×3");
}
static ES Diamond3(int weight=1) {
return ES(3, 3, {0, weight, 0, weight, weight, weight, 0, weight, 0},
"Losange 3×3");
}
ES ccw_rotate() const {
ES ret = *this;
for (cv::Point &offset : ret.offsets) {
int tmp = offset.x;
offset.x = -offset.y;
offset.y = tmp;
}
return ret;
}
};
const int img_at_s(const cv::Mat &img, const cv::Point p, int fill=0) {
if (p.x < 0 || p.x >= img.cols
|| p.y < 0 || p.y >= img.rows)
return fill;
return img.at<int>(p);
}
//----------------------- T R A N S F O R M A T I O N S -----------------------
void erosion(cv::Mat &img, const ES &es, int vide=0) {
cv::Mat orig = img.clone();
img.forEach<int>([&](int &val, const int *position) {
cv::Point p {position[1], position[0]};
for (size_t i = 0; i < es.weights.size(); i++) {
int val2 = img_at_s(orig, p + es.offsets[i]);
if (es.weights[i] > 0 && val2 == vide) {
val = vide;
}
}
});
}
void dilatation(cv::Mat &img, const ES &es) {
erosion(img, es, 255);
}
void ouverture(cv::Mat &img, ES &es, int n_iter=1) {
for (int i = 0; i < n_iter; i++) erosion(img, es);
for (int i = 0; i < n_iter; i++) dilatation(img, es);
}
void fermeture(cv::Mat &img, ES &es, int n_iter=1) {
for (int i = 0; i < n_iter; i++) dilatation(img, es);
for (int i = 0; i < n_iter; i++) erosion(img, es);
}
void debruiter(cv::Mat &img, ES &es, int n_iter=1) {
ouverture(img, es, n_iter);
fermeture(img, es, n_iter);
}
void debruiter2(cv::Mat &img, ES &es, int n_iter=1) {
// il semble que debruiter2 inclus debruiter
fermeture(img, es, n_iter);
ouverture(img, es, n_iter);
}
void squelette(cv::Mat &img, ES &es) {
std::cout << " .-.\n (o.o)\n |=|\n __|__\n //.=|=.\\\\\n // .=|=. \\\\\n \\\\ .=|=. //\n \\\\(_=_)//\n (:| |:)\n || ||\n () ()\n || ||\n || ||\n ==' '==\n" << std::endl;
}
void squelette2(cv::Mat &img, ES &es) {
cv::Mat tmp = img.clone();
img.setTo(0);
cv::Mat ouv;
bool continuer;
int iter = 0;
do {
continuer = false;
ouv = tmp.clone();
ouverture(tmp, es);
ouv.forEach<int>([&](int &val, const int *position) {
cv::Point p {position[1], position[0]};
if (val != tmp.at<int>(p)) {
img.at<int>(p) = iter;
continuer = true;
}
});
erosion(tmp, es);
iter++;
} while (continuer);
}
void reconstruction(cv::Mat &img, ES &es) {
int max = 0;
img.forEach<int>([&](int &val, const int *position) {
if (val > max) max = val;
});
cv::Mat tmp = img.clone();
img.setTo(0);
for (int i = max; i > 0; i--) {
/* Keep only values >= max */
tmp.forEach<int>([&](int &val, const int *position) {
cv::Point p {position[1], position[0]};
if (val >= i) {
img.at<int>(p) = 255;
}
});
dilatation(img, es);
}
}
//----------------------------- TP8 -----------------------------
void debruiter_empreinte(cv::Mat &img) {
ES es(5, 3,
{0, 1, 1, 1, 0,
1, 1, 1, 1, 1,
0, 1, 1, 1, 0,
});
ouverture(img, es);
fermeture(img, es);
}
typedef ES TOR_mask;
template <typename F>
void hit(cv::Mat &img, const TOR_mask &mask, F func) {
cv::Mat tmp = img.clone();
tmp.forEach<int>([&](int &val, const int *position) {
cv::Point p {position[1], position[0]};
for (size_t i = 0; i < mask.offsets.size(); i++) {
int val2 = img_at_s(tmp, p + mask.offsets[i]);
if ((mask.weights[i] > 0 && val2 == 0)
|| (mask.weights[i] == 0 && val2 != 0)) {
return;
}
}
func(p);
});
}
void amincissement(cv::Mat &img,
const TOR_mask &mask_a, const TOR_mask &mask_b,
bool run_until_stable=true) {
TOR_mask masks[8];
{
size_t i = 0;
masks[i] = mask_a;
for (i++; i < 4; i++) {
masks[i] = masks[i - 1].ccw_rotate();
}
masks[i] = mask_b;
for (i++; i < 8; i++) {
masks[i] = masks[i - 1].ccw_rotate();
}
}
bool has_changed;
do {
has_changed = false;
for (size_t i = 0; i < 8; i++) {
hit(img, masks[i], [&](const cv::Point &p) {
has_changed = true;
img.at<int>(p) = 0;
});
}
} while (run_until_stable ? has_changed : false);
}
void elagage(cv::Mat &img, size_t n_iter=1) {
static const TOR_mask mask_a(3, 3, {
-1, 0, 0,
1, 1, 0,
-1, 0, 0,
});
static const TOR_mask mask_b(3, 3, {
0, 0, 0,
0, 1, 0,
1, 0, 0,
});
for (size_t i = 0; i < n_iter; i++) {
amincissement(img, mask_a, mask_b, false);
}
}
//----------------------------- I N T E R F A C E ----------------------------
class MonApp : public Astico2D {
const int n_iter_min = 1;
const int n_iter_max = 50;
int n_iter = n_iter_min;
public:
// Déclarez ici d'autres membres éventuels
MonApp (int argc, char **argv) :
Astico2D (argc, argv)
// initialisez ici vos classes membre éventuelles
{
if (!init_ok) return; // erreur dans l'initialisation
// Autres actions du constructeur
creer_slider ("Nb iter", &n_iter, n_iter_max);
}
void afficher_touches_clavier () override
{
// Ceci affiche l'aide de base
Astico2D::afficher_touches_clavier ();
// Indiquez ici les touches du clavier et vos transformations
std::cout <<
" 1 débruiter empreinte\n"
" 2 amincissement\n"
" 3 élagage\n"
<< std::endl;
}
bool traiter_touche_clavier (char key) override
{
switch (key) {
// Gérez ici les touches de flags.
// Rajoutez ici les touches pour effectuer_transformations.
case '1' :
case '2' :
case '3' :
case '4' :
case '5' :
case '6' :
case '7' :
case '8' :
case '9' :
// On mémorise la touche pressée
touche_transfo = key;
// On précise le mode : M_NIVEAUX ou M_COULEURS
mode_transfo = M_NIVEAUX;
break;
default : return false; // touche non traitée
}
return true; // touche traitée
}
void effectuer_transformations () override
{
// Appelez ici vos transformations selon touche_transfo et mode_transfo :
// - si mode_transfo est M_NIVEAUX, l'image d'entrée est l'image seuillée
// img_niv, de type CV_32SC1 ; à la fin, pour l'affichage, il faut la
// convertir en couleur dans img_coul, de type CV_8UC3, par exemple avec
// representer_en_couleurs_vga.
// - si mode_transfo est M_COULEURS, travaillez directement sur img_coul,
// de type CV_8UC3, elle sera affichée telle quelle.
ES es = ES::Diamond3();
TOR_mask mask_a(3, 3, {
0, 0, 0,
-1, 1, -1,
1, 1, 1,
});
TOR_mask mask_b(3, 3, {
-1, 0, 0,
1, 1, 0,
1, 1, -1,
});
switch (touche_transfo) {
case '1' :
debruiter_empreinte (img_niv);
representer_en_couleurs_vga (img_niv, img_coul);
break;
case '2' :
amincissement (img_niv, mask_a, mask_b);
representer_en_couleurs_vga (img_niv, img_coul);
break;
case '3' :
amincissement (img_niv, mask_a, mask_b);
elagage (img_niv, n_iter);
representer_en_couleurs_vga (img_niv, img_coul);
break;
}
}
};
//---------------------------------- M A I N ----------------------------------
int main (int argc, char **argv)
{
MonApp app (argc, argv);
return app.run ();
}