Page 1 sur 1

shipout et erase()

Posté : lun. 10 déc. 2012, 20:08
par micercle
Bonjour,

J'utilise Asymptote pour traiter 62 caractères typo en 2400 points par pouce (en taille de 12 points) obtenus par ImageMagick au préalable au format pgm en noir et blanc. Je lis le fichier pgm et ne récupère que les coordonnées des pixels de l'oeil. Pour chaque caractère je crée une image png sur laquelle je colle l'oeil du caractère et j'ajoute sous forme de diagrammes les nombres de pixels verticalement et horizontalement et pour chaque diagramme je rajoute la dérivée.

En gros, j'ai l'oeil et quatre diagrammes sur la même figure png. Pour un A en Computer-Modern-Medium j'ai une image de 400x400 pixels. Le png ne fait qu'une 10aine de Ko ce qui semble logique.

Je fais une boucle dans laquelle je crée la figure je l'exporte par shipout et je l'efface avec erase(currentpicture). Cependant je remarque que la quantité de mémoire vive utilisée ne fait qu'augmenter jusqu'à parfois stopper la compilation. J'ai 8 Go de RAM. Je suis sous FreeBSD 9.0. (Proc Intel Core I5). Si je diminue le nombre de tâches graphiques (draw, dot...) la mémoire vive utilisée augmente quand même à chaque itération, mais je peux éviter d'utiliser le swap.

J'observe qu'il y a un fichier eps temporaire qui se crée (7 Mo environ) et qui disparait. J'ai l'impression que la libération de la mémoire après chaque tour de boucle ne se fait correctement. Question : ça va trop vite ? Il faut laisser le temps à Asymptote de souffler ? C'est un pb de ma config ?

J'utilise une seule picture (currentpicture) par itération, qui devrait disparaitre après le erase(currentpicture), non? Dans l'idéal, je devrais itérer doublement : une fois sur la fonte, et pour chaque fonte pour les 62 caractères.

Merci de vos lumières, le code est disponible, si besoin.

Micercle

Re: shipout et erase()

Posté : lun. 10 déc. 2012, 21:19
par GM
Bonsoir,

la demande est "pointue"...

  1. ... au point que j'ai dû chercher sur le net ce qu'on appelle "oeil" pour comprendre (à peu près) l'intro :mrgreen:)
  2. et compte tenu que c'est un problème de mémoire, avec lequel nous avons tous été confrontés à un moment donné ou à un autre (sans forcément savoir le résoudre).

micercle a écrit :Merci de vos lumières, le code est disponible, si besoin.

Je ne dis pas non personnellement...

... car nous pourrons peut-être être plusieurs à confirmer ou infirmer le problème, sur différentes plateformes, si on peut tenter de compiler le code.

Re: shipout et erase()

Posté : lun. 10 déc. 2012, 21:27
par GM
A propos de la fonction erase, on peut lire dans le changelog de la 2.16 :

What's new in Asymptote 2.16:
May 31st, 2012

· Ticks are no longer autoscaled when the number of major intervals is specified and autoscale is false.
· Manual tick scaling was fixed.
· A bug in the palette range was fixed.
· A division by zero in constructing curved arrows was fixed.
· A numerical underflow was fixed.
· A picture bound error was fixed.
· The current value of currentpen is now always respected in default arguments.
· A default viewportwidth is no longer imposed for attached images.
· A routine for computing camera positions was added.
· The format command is now more consistent with C++ printf formatting.
· Named arguments can now appear in function calls after rest arguments.
· The wheel example was improved to support PDF animations.
· The erase command no longer resets the machine state.
· Pipes are now used for xasy communication.
· A new mode parameter to input and output replaces xinput, xoutput, binput, and boutput.
· The icon directory path for 64-bit MSWindows systems was fixed.
· Compilation of native CYGWIN binaries is now supported.


Je ne sais pas trop ce qu'il faut penser de ce qui est dit pour la fonction erase.

Je serais assez tenté de suggérer de faire tout de suite une demande sur le forum officiel en anglais... car si c'est un bug de libération de mémoire, il n'y aura que les auteurs d'Asymptote pour comprendre rapidement d'où cela peut venir.

Re: shipout et erase()

Posté : lun. 10 déc. 2012, 21:39
par micercle
Oui, l'intro est peut être abrupte. L'oeil c'est la partie encrée du caractère qui lui-même est défini dans une boîte rectangulaire. Je récupère donc les pixels colorés (en inverse video) de l'oeil et je mets les coordonnées dans un tableau de pair. Je pose mes points "carrés" sur ma picture.

Voila mon script (visiblement il n'est pas possible de joindre un fichier)

Il faut aussi un fichier pgm qui contient le caractère.

J'espère que le code est assez clair.

Mais je vais poser la question sur le forum d'Asymptote.

Code : Tout sélectionner

import graph;
usepackage("amsmath");

struct profils {
  pair[] hist[];
  int[] dimhist;
}

struct image{
  string nom;
  int largeur,hauteur;
  pair[] valeurs;
  int hauteuroeil,largeuroeil;
  profils histogrammeV,histogrammeH;
}

image lect_image(string fichier)
{
int resolution = 2400;
int taille = 12;

  string fich = fichier + ".pgm";
  string[] nom = split(fichier,"/");
  image glyphe = new image;
  glyphe.nom = nom[(nom.length)-1]; //Nom du glyphe
  file fin=input(fich).line().word();
  string ligne=fin.line();
  int[] ligne=fin.line();
  int largeur = ligne[0]; //largeur de la boîte
  glyphe.largeur = largeur;
  int hauteur = ligne[1]; //hauteur de la boîte
  glyphe.hauteur = hauteur;
  string ligne=fin.line();
  int[] valeur;
  while (!(eof(fin)))
   {
      int[] ligne=(int[])split(fin.line()," ");
   valeur.append(ligne);
   }
   close(fin);
   int x[],y[];
   int cpt=0;
   for(int i = 0 ; i < valeur.length ; ++i)
   {
    if (valeur[i] != 0)
    {
      int lg = (int)(i/largeur);
      int co = i % largeur;
      x.push(co);
      y.push(hauteur-lg);
      cpt = cpt +1;
    }
   }
    pair[] zz = pairs(x,y);
   glyphe.valeurs = zz;
   return glyphe;
}   

// Dimension de la bbox lue dans le fichier pgm
int[] dimension(string fichier)
{
  string fich = fichier + ".pgm";
  file fin=input(fich).line().csv();
  string ligne;   
  for (int i=1; i<=2; ++i)
  {
    ligne=fin.line();
  }
  string[] dimension;
  dimension = split(ligne, " ");
  return (int[])dimension;
  close(fin);
}




// Génération du fichier png de l'enveloppe convexe des caractères
void envconv(string fichier,int largeur,int hauteur)
{
  string fich = fichier + "_env_conv.dat";
  file fin=input(fich).line().csv();
  real[][] A=fin.dimension(0,0);
  A = transpose(A);
  real[] abscisses= A[0], ordonnees= A[1];
  size(largeur,hauteur);
  real[] x,y;
  path cadre=nullpath;
  cadre = ((0,0)--(largeur,0)--(largeur,hauteur)--(0,hauteur)--cycle);
//   fill(cadre,white);
//   draw(cadre,red);
  path CH = nullpath;
  CH = (abscisses[0],ordonnees[0]);
  for (int i = 1; i <abscisses.length; ++i)
  {
    CH = CH --(abscisses[i],ordonnees[i]);
  } 
    CH = CH --cycle;
//     draw(CH,linewidth(1.5bp));
  //shipout(fichier + "_env_conv",format = "png");
  //erase();
}


// Structure récupérant le contenu des fichiers de données des caractères
struct data{
  string[] glyphe;
  real[] Soeil, Poeil;
  int[] chi;
  real[] complexite, Sboite, Pboite, inradius, centre_x, centre_y, Aire_CH, Perimetre_CH, Diametre;
}

data lect_data(string fonte)
{
int resolution = 2400;
int taille = 12;
  string chemin = "./"+fonte+"/"+fonte+"-"+(string)resolution+"/";
  string fichier=chemin+fonte+"-"+(string)resolution+"-"+string(taille);
  string fich = fichier + ".dat";
  data T = new data;
   file fin=input(fich).line().word();
   string[][] A=fin.dimension(1,0);
   A=fin.dimension(0,0);
  string[][] B = transpose(A);
    T.glyphe = B[0];
    T.Soeil = (real[])B[1];
    T.Poeil = (real[])B[2];
    T.chi = (int[])B[3];
    T.complexite = (real[])B[4];
    T.Sboite = (real[])B[5];
    T.Pboite = (real[])B[6];
    T.inradius = (real[])B[7];
    T.centre_x = (real[])B[8];
    T.centre_y = (real[])B[9];
    T.Aire_CH = (real[])B[10];
    T.Perimetre_CH = (real[])B[11];
    T.Diametre = (real[])B[12];
    close(fin);
  return T;
}   

 




// // Récupération des coordonnées du contour d'un caractère
pair[] contour(string fichier)//,int largeur,int hauteur)
{
  string fich = fichier + "_outline.dat";
  file fin=input(fich).line().csv();
  string titre;
  titre = fin.line();
  real[][] A=fin.dimension(0,0);
  A = transpose(A);
  real[] abscisses= A[0], ordonnees= A[1];
  pair[] z;
  for (int i = 0; i <abscisses.length; ++i)
  {
    z[i] = (abscisses[i],ordonnees[i]);
  }     
  close(fin);
  //shipout(fichier + "_outline",format = "png");
  //erase();
  return z;
}


pair[] env_conv(string fichier)//,int largeur,int hauteur)
{
  string fich = fichier + "_env_conv.dat";
  file fin=input(fich).line().csv();
  real[][] A=fin.dimension(0,0);
  A = transpose(A);
  real[] abscisses= A[0], ordonnees= A[1];
  pair[] z;
  for (int i = 0; i <abscisses.length; ++i)
  {
    z[i] = (abscisses[i],ordonnees[i]);
  } 
  //shipout(fichier + "_outline",format = "png");
  //erase();
  close(fin);
  return z;
}

// picture histV;
path proj(profils toto,int largeur) //profils : struct profils
{
path histo = (0,0);
for(int k=0; k<toto.dimhist.length; ++k )
{  histo = histo -- (k,toto.dimhist[k]);  }
histo = histo -- (largeur,0)--cycle;
return histo;
}

pair[] derivee(profils profil)
{
  pair[] resultat;
// for(int k=0; k < glyphe.histogrammeH.dimhist.length;++k)
for(int k=0; k < profil.dimhist.length;++k)
{int d;
  if (k == 0)
  { d = profil.dimhist[0]-profil.dimhist[profil.dimhist.length-1];}
  else
  {
    if(k==profil.dimhist.length-1)
    { d = profil.dimhist[profil.dimhist.length-1]-profil.dimhist[0];}
    else { d = profil.dimhist[k+1] - profil.dimhist[k];}
  } 
//   write(sortie,(string)k + ' ' +(string)glyphe.histogrammeH.dimhist[k]+' '+(string)d+'\n');
  resultat.push((k,d));
}
return resultat;
}

//string [] fontes ={"Courier","Cantarell-Regular","Times-Italic","Palatino-Italic" ,"AMS-Medium","Century-Schoolbook-Roman" ,"Times-Roman","Arial-Normal","AvantGarde-Book","Palatino-Roman","Helvetica-Oblique", "Computer-Modern-Medium","Helvetica","NewCenturySchlbk-Roman","NewCenturySchlbk-Italic","Bookman-Demi"};

string[] fontes = {"Computer-Modern-Medium"};
// [] fontes ={//"Courier","Cantarell-Regular","Times-Italic","Palatino-Italic" ,"AMS-Medium","Century-Schoolbook-Roman" ,
//"Times-Roman","Arial-Normal","AvantGarde-Book","Palatino-Roman",
//"Helvetica-Oblique","Computer-Modern-Medium","Helvetica","NewCenturySchlbk-Roman","NewCenturySchlbk-Italic","Bookman-Demi"};

string lettre = "NOPQRSTUVWXYZ0123456789";//abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";//";//
string extension[]= {"_outline.dat",".pgm","_outline.png"};
int resolution = 2400;
int taille = 12;


for (string fonte : fontes)
{
string chemin = "./"+fonte+"/"+fonte+"-"+(string)resolution+"/";
// write(chemin);

//   data T = lect_data(fonte);
  for (int i = 0; i<length(lettre); ++i)
  {
    string fichier=chemin+substr(lettre,i,1)+"_"+fonte+"-"+(string)resolution+"-"+string(taille);
    write('fichier : ', fichier);
    image glyphe = new image;
    glyphe = lect_image(fichier); //récupère les éléments de l'image du glyphe

  int[] x,y;
//     write('nombre de pixels : ', glyphe.valeurs.length);
    for(pair p : glyphe.valeurs)
    {
      x.push((int)xpart(p)); y.push((int)ypart(p));
    }
    int miny,maxy,minx,maxx;
    minx = min(x);
    maxx = max(x);
    miny = min(y);
    maxy = max(y);
    pair delta = (minx,miny);
    int h_oeil = (maxy - miny);
//     write('hauteur de l\'oeil : ',h_oeil);
    glyphe.hauteuroeil  = h_oeil;
   
    int l_oeil = (maxx - minx);
//     write('largeur de l\'oeil : ',l_oeil);
    glyphe.largeuroeil  = l_oeil;
   
//tracé du contour de la boite du glyphe ->  picture glyphe_plan
//pen ordinaire + rouge

    path cadre_glyphe = nullpath;
    pen stylo=makepen(scale(.8)*unitsquare);
   
    pen stylo_glyphe=makepen(scale(.8)*unitsquare);
   
    profils profglypheH = new profils;
   
    for(int lig=miny; lig <= maxy; ++lig)
     {
      pair[] ligneH;
      for(int indice=0; indice<glyphe.valeurs.length;++indice)
      {
   if (ypart(glyphe.valeurs[indice]) == lig)
   {
     ligneH.push(glyphe.valeurs[indice]);    
   }
      }
      profglypheH.hist.push(ligneH);
      profglypheH.dimhist.push(ligneH.length);
//      dot(ligneV,convex3);
      }

     profils profglypheV = new profils;
     for(int col=minx; col <= maxx; ++col)
     {
      pair[] ligneV;
      for(int indice=0; indice<glyphe.valeurs.length;++indice)
      {
   if (xpart(glyphe.valeurs[indice]) == col)
   {
     ligneV.push(glyphe.valeurs[indice]);    
   }
      }
      profglypheV.hist.push(ligneV);
      profglypheV.dimhist.push(ligneV.length);
//       dot(ligneH,convex3);
      }
     
      glyphe.histogrammeH = profglypheH;
      glyphe.histogrammeV = profglypheV;
     
  if (rfind("abcdefghijklmnopqrstuvwxyz", substr(lettre,i,1), pos=-1) != -1)
    {fichier=chemin+substr(lettre,i,1)+"_min_"+fonte+"-"+(string)resolution+"-"+string(taille);}
  else
    {fichier=chemin+substr(lettre,i,1)+"_"+fonte+"-"+(string)resolution+"-"+string(taille);}
   
    path boite=box((0,0),(glyphe.largeuroeil,glyphe.hauteuroeil+1));
    cadre_glyphe = shift(0,0)*((0,0)--(glyphe.largeuroeil,0)--(glyphe.largeuroeil,glyphe.hauteuroeil)--(0,glyphe.hauteuroeil)--cycle);
//     size(glyphe.largeur+2*minx,glyphe.hauteur+2*miny);   
    fill(currentpicture,shift(0,0)*boite,white);
   
    dot(currentpicture,shift(-minx,-miny)*glyphe.valeurs,stylo_glyphe+lightgray);
    draw(currentpicture,shift(0,-miny)*proj(profglypheV,glyphe.largeuroeil));
    draw(currentpicture,shift(glyphe.largeuroeil+1+minx,0)*rotate(90,(0,0))*proj(profglypheH,glyphe.hauteuroeil));
   
  string nom_sauv = fichier+"_profils.dat";
  file sortie= output(nom_sauv);
  write(sortie,substr(lettre,i,1)+"_"+fonte+"-"+(string)resolution+"-"+string(taille)+ '\n');
  write(sortie,"Profil vertical"+ '\n');
  write(sortie, "abscisse, ordonnée, pente"+ '\n');
  pair[] toto = derivee(glyphe.histogrammeV) ;
  for(int k = 0; k <toto.length; ++k)
  {
    write(sortie,(string)k + ' ' +(string)glyphe.histogrammeV.dimhist[k]+' '+(string)ypart(toto[k])+'\n');
    draw(currentpicture,shift(0,-miny)*((xpart(toto[k]),0)--(xpart(toto[k]),ypart(toto[k]))));
  }
  write(sortie, '\n');
  write(sortie,"Profil horizontal"+ '\n');
  pair[] toto = derivee(glyphe.histogrammeH) ;
  for(int k = 0; k <toto.length; ++k)
  {
    write(sortie,(string)k + ' ' +(string)glyphe.histogrammeH.dimhist[k]+' '+(string)ypart(toto[k])+'\n');
    draw(currentpicture,shift(glyphe.largeuroeil+1+minx,0)*rotate(90,(0,0))*((xpart(toto[k]),0)--(xpart(toto[k]),ypart(toto[k]))));
  }
  close(sortie);
  shipout(fichier + "_profils",format = "png");
  erase(currentpicture);
//   currentpicture = nullpicture
  write('fin',fichier);
//   write('fin');
 
}
}

Re: shipout et erase()

Posté : lun. 10 déc. 2012, 22:39
par micercle
Comme il faut générer les caractères et en lisant la doc (sisi, ça sert !) j'ai découvert avec joie et bonheur qu'ImageMagick peut être invoqué par le bout de code suivant : (les arguments sont ceux qui me sont utiles)

string args="-bordercolor white -border 30x30 -font Times-Roman -density 2400 -pointsize 12 +antialias label:A -negate -depth 8 -compress none";
string fichier="toto.pgm";
string extension ="pgm";
convert(args, fichier, extension);

Il suffit de savoir quelles sont les fontes installées sur la machine (convert -list font > liste_fontes.txt pour récupérer la liste)

Donc voila le type de fichier sur lequel je travaille, les pixels qui m'intéressent valent 255

Juste pour faire fonctionner le script du post précédent (avec la remarque que le troisième paramètre ne m'apparait pas d'une clarté éblouissante !)

Re: shipout et erase()

Posté : mar. 11 déc. 2012, 19:16
par GM
NB : je n'ignore pas certaines demandes... mais je suis submergé de travail... et donc en ce moment je ne réponds qu'aux messages où je n'ai pas à réfléchir, où je n'ai pas à faire de tests.

Re: shipout et erase()

Posté : ven. 14 déc. 2012, 22:43
par GM
Je n'ai pas pu tester car je n'ai pas trop compris comment générer le fichier pgm sur lequel tester le script.

Mais j'ai recherché le mot "memory" dans la doc pour tomber sur cette option que je n'ai jamais utilisée :

Code : Tout sélectionner

-compact                    Conserve memory at the expense of speed [false]

Cela donne à penser qu'en concédant de perdre un peu de vitesse... on peut peut-être gagner de la mémoire.

-------

Autre piste à tester : utiliser un script extérieur avec une boucle qui lance l'exécution d'asymptote sur un ficher asy en lui passant un ou plusieurs paramètres.
J'imagine alors qu'à chaque exécution... la mémoire sera libérée.

A tester :

Code : Tout sélectionner

asy -u x=2 nomdufichier

Code : Tout sélectionner

real x;
usersetting();
write(x);