bmp2gcode.cc 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392
  1. #include <iostream>
  2. #include <fstream>
  3. #include <vector>
  4. #include <cmath>
  5. #include <iomanip>
  6. class BMP2Gcode
  7. {
  8. public:
  9. BMP2Gcode();
  10. void traitement(char* nom_fichier);
  11. void fichier_conf();
  12. private:
  13. int entete();
  14. int donnees();
  15. int sortie();
  16. unsigned int conversion(unsigned int valeur, unsigned int min, unsigned int max);
  17. unsigned int consecutif(unsigned int indice, unsigned int min, unsigned int max, unsigned int puissance, int sens);
  18. unsigned int BMP_largeur, BMP_hauteur, BMP_offset, BMP_profondeur, BMP_taille;
  19. double conf_taille_laser, conf_puissance_min, conf_puissance_max, conf_vitesse, conf_taille_image;
  20. std::vector<unsigned int> tab_donnees;
  21. std::string nom_fichier_bmp;
  22. };
  23. BMP2Gcode::BMP2Gcode()
  24. :BMP_largeur(0), BMP_hauteur(0), BMP_offset(0), BMP_profondeur(0), BMP_taille(0)
  25. {}
  26. void BMP2Gcode::traitement(char* nom_fichier)
  27. {
  28. nom_fichier_bmp = nom_fichier;
  29. if(entete() == 0)
  30. {
  31. if(donnees() == 0)
  32. {sortie();}
  33. }
  34. }
  35. int BMP2Gcode::entete()
  36. {
  37. //ouverture du fichier
  38. std::ifstream fichier(nom_fichier_bmp, std::ifstream::binary);
  39. //test de l'ouverture du fichier
  40. if(!fichier)
  41. {
  42. std::cerr << "impossible d'ouvrir le fichier" << std::endl;
  43. return 1;
  44. }
  45. /*lecture de l'en tête de 54 octets composé comme suit :
  46. -2 octets [0-1] pour la signature
  47. -4 octets [2-5] pour la taille du fichier en octets
  48. -4 octets [6-9] de champ réservé
  49. -4 octets [10-13] pour l'offset
  50. -4 octets [14-17] pour la taille de l'en-tête de l'image (28 octets pour windows)
  51. -4 octets [18-21] pour la largeur de l'image
  52. -4 octets [22-25] pour la hauteur de l'image
  53. -2 octets [26-27] pour le nombre de plans (valeur toujours à 1)
  54. -2 octets [28-29] pour la profondeur de l'encodage (nbr de bit pour la couleur)
  55. -4 octets [30-33] pour la métode de compression (0 non compressé, 1 RLE 8bits/pixel, 2 RLE 4bits/pixel, 3 bitfields)
  56. -4 octets [35-38] pour la taille de l'image
  57. -4 octets [39-42] pour la résolution horizontale
  58. -4 octets [43-46] pour la résolution verticale
  59. -4 octets [47-50] pour la palette de couleur
  60. -4 octets [50-53] pour le nombre de couleurs importantes.
  61. */
  62. char *entete = new char [54];
  63. fichier.read(entete, 54);
  64. if(entete[0] == 'B')
  65. {
  66. if(entete[1] == 'M')
  67. {
  68. std::cout << "Bitmap windows" << std::endl;
  69. }
  70. }
  71. /*Pour récupérer les informations on va utiliser l'astuce suivante :
  72. pour la valeur commençant à entete[indice] on va commencer par prendre l'adresse :
  73. &entete[indice]
  74. puis on va caster ça dans le type qui correspond bien, int * si c'est sur 4 octets,
  75. short * si c'est sur 2 octets.
  76. (int *)&entete[indice] correspond donc à un pointeur de type int *, il suffit alors
  77. de prendre sa valeur en faisant : *(int *)entete[indice]
  78. */
  79. BMP_offset = *(int*)&entete[10];
  80. BMP_largeur = *(int*)&entete[18];
  81. BMP_hauteur = *(int*)&entete[22];
  82. BMP_profondeur = *(short*)&entete[28];
  83. std::cout << "compression : " << *(int*)&entete[30] << std::endl;
  84. std::cout << "offset BMP : " << BMP_offset << std::endl;
  85. std::cout << "largeur BMP : " << BMP_largeur << std::endl;
  86. std::cout << "hauteur BMP : " << BMP_hauteur << std::endl;
  87. std::cout << "profondeur BMP : " << BMP_profondeur << std::endl;
  88. delete[] entete;
  89. fichier.close();
  90. return 0;
  91. }
  92. int BMP2Gcode::donnees()
  93. {
  94. //ouverture du fichier
  95. std::ifstream fichier(nom_fichier_bmp, std::ifstream::binary);
  96. //test de l'ouverture du fichier
  97. if(!fichier)
  98. {
  99. std::cerr << "impossible d'ouvrir le fichier" << std::endl;
  100. return 1;
  101. }
  102. if(BMP_offset == 0)
  103. {entete();}
  104. /* /!\ATTENTION/!\
  105. Contrairement à la plupart des formats d'images, les pixels de l'image sont codés en partant de la ligne inférieure de l'image. Chaque ligne (codée de gauche à droite) doit toujours occuper un nombre d'octets multiple de 4, excepté si l'image est compressée. Si la ligne ne possède pas un nombre d'octets multiple de 4, on ajoute FF, 00FF, ou 0000FF à la fin de chaque ligne
  106. Source : https://fr.wikipedia.org/wiki/Windows_bitmap#Disposition_des_donn%C3%A9es_de_l'image
  107. Il est donc nécessaire pour lire correctement les données de prévoir une limite qui est un multiple de 4
  108. mais également de sauter ces éventuels informations
  109. */
  110. int reste = BMP_largeur % 4, longueur_ligne = BMP_largeur;
  111. if(reste != 0)
  112. {
  113. //Si la longueur d'une ligne n'est un multiple de 4
  114. //il suffit alors d'ajouter à la ligne 4 - reste
  115. longueur_ligne += 4 - reste;
  116. }
  117. int limite = longueur_ligne*BMP_hauteur * BMP_profondeur/8 + BMP_offset;
  118. char *donnees = new char [ limite ];
  119. fichier.read(donnees, limite);
  120. std::cout << "données brutes : " << std::endl;
  121. int depart = BMP_offset;
  122. for(int i = 0 ; i < BMP_hauteur ; i++)
  123. {
  124. //on récupère une ligne
  125. for(int j = depart ; j < depart + BMP_largeur * BMP_profondeur/8; j += BMP_profondeur/8)
  126. {
  127. if(donnees[j] < 0)
  128. {tab_donnees.push_back(256 + (int)donnees[j]);}
  129. else
  130. {tab_donnees.push_back((int)donnees[j]);}
  131. }
  132. depart += longueur_ligne * BMP_profondeur/8;
  133. }
  134. delete[] donnees;
  135. fichier.close();
  136. return 0;
  137. }
  138. int BMP2Gcode::sortie()
  139. {
  140. std::string nom_fichier_gcode;
  141. nom_fichier_gcode = nom_fichier_bmp + ".gcode";
  142. std::ofstream fichier_sortie;
  143. fichier_sortie.open (nom_fichier_gcode);
  144. /****************************CONDITIONS INITIALES****************************/
  145. double taille_pixel = (double)(conf_taille_image)/(double)(BMP_largeur);
  146. int sens = 1, nbr_passage = 1, ligne = 0, colonne = 0;
  147. //on arrondi la taille du pixel pour quelle corresponde à un multiple de la taille du laser
  148. double rapport = ceil(taille_pixel/conf_taille_laser);
  149. taille_pixel = rapport * conf_taille_laser;
  150. std::cout << "***SORTIE***" << std::endl;
  151. std::cout << "largeur = " << BMP_largeur * taille_pixel << " mm" << std::endl;
  152. std::cout << "hauteur = " << BMP_hauteur * conf_taille_image/BMP_largeur << " mm" << std::endl;
  153. std::cout << "taille pixel = " << taille_pixel << " mm" << std::endl;
  154. //On passe en mode relatif
  155. fichier_sortie << "G91" << std::endl;
  156. //on s'assure que le laser est éteint
  157. fichier_sortie << "M106 P1 S0" << std::endl;
  158. //on défini la vitesse
  159. fichier_sortie << "G1 F" << conf_vitesse << std::endl;
  160. //on parcourt toutes les lignes
  161. while(ligne < BMP_hauteur)
  162. {
  163. //pour graver une ligne on fera des aller-retour tant que :
  164. do
  165. {
  166. fichier_sortie << ";colonne numéro : " << colonne << std::endl;
  167. fichier_sortie << ";ligne numéro : " << ligne << std::endl;
  168. fichier_sortie << ";sens : " << sens << std::endl;
  169. int indice = ligne * BMP_largeur + colonne;
  170. fichier_sortie << ";indice : " << indice << std::endl;
  171. unsigned int puissance = 0, indice_min = 0, indice_max = 0, nbr = 1;
  172. puissance = conversion(255 - tab_donnees[indice], conf_puissance_min, conf_puissance_max);
  173. indice_min = ligne * BMP_largeur;
  174. indice_max = indice_min + BMP_largeur - 1;
  175. //on détermine le nombre de pixels successifs pour lesquels la puissance du laser sera la même
  176. nbr = consecutif(indice, indice_min, indice_max, puissance, sens);
  177. fichier_sortie << "M106 P1 S" << puissance << std::endl;
  178. //on se déplace selon le sens
  179. fichier_sortie << "G1 X" << sens * taille_pixel * nbr << std::endl;
  180. colonne += sens * nbr;
  181. }
  182. while(colonne < BMP_largeur && colonne >= 0);
  183. /*arrivé ici on vient de tracer une ligne et le paramètre colonne est hors limite, il faut donc le rectifier.
  184. Il est peut être également nécessaire de faire un retour cela va dépendre du rapport
  185. entre la taille du pixel et la taille du laser*/
  186. if(colonne < 0)
  187. {colonne = 0;}
  188. if(colonne >= BMP_largeur)
  189. {colonne = BMP_largeur - 1;}
  190. if(taille_pixel > conf_taille_laser * nbr_passage)
  191. {
  192. /*si on a pas fait un nombre de passage suffisent pour égaler la taille du pixel
  193. alors on repart dans l'autre sens en prennant soin de décaler la colonne
  194. et on recommence*/
  195. nbr_passage ++;
  196. fichier_sortie << ";passage numéro : " << nbr_passage << std::endl;
  197. fichier_sortie << ";colonne numéro : " << colonne << std::endl;
  198. }
  199. else
  200. {
  201. /*si le nombre de passage permet d'égaler la taille du pixel
  202. alors on change de ligne et on recommence*/
  203. ligne ++;
  204. nbr_passage = 1;
  205. fichier_sortie << ";changement de ligne : " << ligne << std::endl;
  206. fichier_sortie << ";colonne numéro : " << colonne << std::endl;
  207. }
  208. /*Dans les 2 cas il est nécessaire de se déplacer sur l'axe Y
  209. et de changer de sens*/
  210. fichier_sortie << "G1 Y" << conf_taille_laser << std::endl;
  211. sens *= -1;
  212. }
  213. //il ne faut pas oublier d'éteindre le laser à la fini
  214. fichier_sortie << "M106 P1 S0" << std::endl;
  215. fichier_sortie.close();
  216. return 0;
  217. }
  218. unsigned int BMP2Gcode::conversion(unsigned int valeur, unsigned int min, unsigned int max)
  219. {
  220. /*pour une valeur de 0 la puissance du laser doit être égale à min
  221. *pour une valeur de 255 la puissance du laser doit être égal à max
  222. *pour trouver la valeur entre les deux on utilise une fonction affine
  223. y = ax + b
  224. pour x = 0 <=> y = b = min
  225. pour x = 255 <=> y = 255*a+min = max
  226. <=> a = (max - min)/255
  227. */
  228. return valeur * (max - min)/255 + min;
  229. }
  230. unsigned int BMP2Gcode::consecutif(unsigned int indice, unsigned int min, unsigned int max, unsigned int puissance, int sens)
  231. {
  232. /*Cette fonction va déterminer le nombre de pixel consécutif
  233. avec la même couleur que le premier pixel, dans un interval donné
  234. du fait de la calibration il se peut que des teintes de gris proches
  235. soient associées à la même puissance laser,
  236. il est donc nécessaire de comparer les puissance et non les données des pixel*/
  237. unsigned int nbr = 0;
  238. while(indice >= min && indice <= max)
  239. {
  240. if(puissance == conversion(255 - tab_donnees[indice], conf_puissance_min, conf_puissance_max))
  241. {nbr ++;}
  242. else
  243. {return nbr;}
  244. indice += sens;
  245. }
  246. return nbr;
  247. }
  248. void BMP2Gcode::fichier_conf()
  249. {
  250. //On regarde si le fichier de configuration existe déjà
  251. std::ifstream fichier_conf("laser.conf");
  252. //s'il n'existe pas
  253. if(!fichier_conf)
  254. {
  255. //alors on le crée
  256. std::ofstream fichier_conf;
  257. fichier_conf.open("laser.conf");
  258. fichier_conf << "taille_laser(mm) 0.2" << std::endl;
  259. fichier_conf << "puissance_min 0" << std::endl;
  260. fichier_conf << "puissance_max 255" << std::endl;
  261. fichier_conf << "vitesse(mm/min) 1300" << std::endl;
  262. fichier_conf << "taille_image_x(mm) 150" << std::endl;
  263. std::cout << "*************************************************" <<std::endl;
  264. std::cout << "ATTENTION, un fichier de configuration a été crée" <<std::endl;
  265. std::cout << "Il ne contient probablement pas les bonnes valeurs" <<std::endl;
  266. std::cout << "Pour les connaitre il est nécessaire dutiliser" <<std::endl;
  267. std::cout << "les scripts d'étalonnage" <<std::endl;
  268. std::cout << "*************************************************" <<std::endl;
  269. conf_taille_laser = 0.2;
  270. conf_puissance_min = 0;
  271. conf_puissance_max = 255;
  272. conf_vitesse = 1300;
  273. conf_taille_image = 150;
  274. }
  275. else
  276. {
  277. //s'il existe on récupère les valeurs
  278. std::string clef;
  279. double valeur;
  280. while(fichier_conf >> clef >> valeur)
  281. {
  282. if(clef == "taille_laser(mm)")
  283. {conf_taille_laser = valeur;}
  284. if(clef == "puissance_min")
  285. {conf_puissance_min = valeur;}
  286. if(clef == "puissance_max")
  287. {conf_puissance_max = valeur;}
  288. if(clef == "vitesse(mm/min)")
  289. {conf_vitesse = valeur;}
  290. if(clef == "taille_image_x(mm)")
  291. {conf_taille_image = valeur;}
  292. }
  293. std::cout << "***CONFIGURATION***" << std::endl;
  294. std::cout << "taille_laser = " << conf_taille_laser << " mm" << std::endl;
  295. std::cout << "puissance_min = " << conf_puissance_min << " PWM 0-255" << std::endl;
  296. std::cout << "puissance_max = " << conf_puissance_max << " PWM 0-255" << std::endl;
  297. std::cout << "vitesse = " << conf_vitesse << " mm/min" << std::endl;
  298. std::cout << "taille_image = " << conf_taille_image << " mm" << std::endl;
  299. }
  300. fichier_conf.close();
  301. }
  302. int main(int argc, char* argv[])
  303. {
  304. if(argc >= 2)
  305. {
  306. std::string entree = argv[1];
  307. BMP2Gcode image;
  308. image.fichier_conf();
  309. image.traitement(argv[1]);
  310. }
  311. else
  312. {std::cout << "Veuillez préciser un nom de fichier BMP" << std::endl;}
  313. return 1;
  314. }