362 lines
7.9 KiB
C++
362 lines
7.9 KiB
C++
/*
|
||
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 ();
|
||
}
|