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