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

362 lines
7.9 KiB
C++
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
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 ();
}