<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>OCaml de pied en cap &#187; Biologie</title>
	<atom:link href="https://blog.developpez.com/ocamlblog/pcategory/biologie/feed" rel="self" type="application/rss+xml" />
	<link>https://blog.developpez.com/ocamlblog</link>
	<description></description>
	<lastBuildDate>Fri, 22 Mar 2013 03:12:26 +0000</lastBuildDate>
	<language>fr-FR</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>https://wordpress.org/?v=4.1.42</generator>
	<item>
		<title>Les questionnaires à choix multiples avec LaTeX</title>
		<link>https://blog.developpez.com/ocamlblog/p9437/biologie/les_questionnaires_a_choix_multiples_ave</link>
		<comments>https://blog.developpez.com/ocamlblog/p9437/biologie/les_questionnaires_a_choix_multiples_ave#comments</comments>
		<pubDate>Sun, 31 Oct 2010 16:31:13 +0000</pubDate>
		<dc:creator><![CDATA[Cacophrene]]></dc:creator>
				<category><![CDATA[Biologie]]></category>
		<category><![CDATA[LaTeX]]></category>

		<guid isPermaLink="false"></guid>
		<description><![CDATA[Bonjour ! Si vous avez des enseignants dans votre entourage, vous serez peut-être sollicités (si ce n&#8217;est déjà fait) pour créer des questionnaires à choix multiples (QCM). Les QCM constituent un outil commode pour évaluer des élèves ou des étudiants. Ils se présentent sous la forme de questions auxquelles plusieurs réponses possibles sont proposées. En général, une seule de ces réponses est correcte; les autres, appelées distracteurs, visent à éprouver la maîtrise du sujet par [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>Bonjour !</p>
<p><center><br />
<table>
<tr>
<td><a href="http://fr.wikipedia.org/wiki/LaTeX" target="_blank"><img src="http://blog.developpez.com/media/LaTeX_logo.png" width="300" height="125" alt="" /></a></td>
</tr>
</table>
<p></center></p>
<p>Si vous avez des enseignants dans votre entourage, vous serez peut-être sollicités (si ce n&rsquo;est déjà fait) pour créer des <strong>questionnaires à choix multiples</strong> (QCM). Les QCM constituent un outil commode pour évaluer des élèves ou des étudiants. Ils se présentent sous la forme de questions auxquelles plusieurs réponses possibles sont proposées. En général, une seule de ces réponses est correcte; les autres, appelées distracteurs, visent à éprouver la maîtrise du sujet par les candidats. À ce titre, elles se doivent d&rsquo;être plausibles.</p>
<p><span id="more-22"></span></p>
<p>En règle générale, l&rsquo;évaluateur souhaite produire deux versions de son questionnaire : une version vierge destinée aux candidats, et une version corrigée, souvent destinée à l&rsquo;impression sur transparent, pour l&rsquo;aider dans la correction des copies. Nous allons voir qu&rsquo;il est possible de satisfaire à toutes ces exigences en utilisant LaTeX sans aucune extension dédiée !</p>
<h3>Des paquets</h3>
<p>Comme toujours, on commence un document LaTeX par l&rsquo;écriture de son préambule. Dans le cas qui nous intéresse, nous allons rédiger un questionnaire en français à l&rsquo;aide de la classe article. Nous avons donc besoin du code suivant : </p>
<pre>\documentclass[11pt]{article}
\usepackage[utf8]{inputenc}
\usepackage[french]{babel}
\usepackage[T1]{fontenc}
\usepackage{lmodern}</pre>
<p>Les cases à cocher seront représentées par des symboles mathématiques, de sorte que nous devons aussi charger les paquets fournis par la <a href="http://fr.wikipedia.org/wiki/American_Mathematical_Society">société mathématique américaine</a> :</p>
<pre>\usepackage{amsmath}
\usepackage{amsfonts}
\usepackage{amssymb}</pre>
<p>Nous souhaitons en outre utiliser tout l&rsquo;espace disponible afin de réduire la longueur de l&rsquo;énoncé. Nous souhaitons donc écrire nos questions sur plusieurs colonnes (paquet <code class="codecolorer text default"><span class="text">multicol</span></code>). Nous aurons aussi besoin de personnaliser l&rsquo;en-tête et le pied de page (paquet <code class="codecolorer text default"><span class="text">fancyhdr</span></code>)&#8230; et vous verrez plus tard que les macros que nous allons écrire nécessitent le paquet <code class="codecolorer text default"><span class="text">ifthen</span></code> :</p>
<pre>\usepackage{ifthen}
\usepackage{multicol}
\usepackage{fancyhdr} </pre>
<h3>Des macros</h3>
<p>Afin de pouvoir passer facilement du mode &laquo;&nbsp;énoncé&nbsp;&raquo; au mode &laquo;&nbsp;corrigé&nbsp;&raquo;, nous allons définir une variable booléenne <code class="codecolorer text default"><span class="text">correction</span></code> et l&rsquo;initialiser à faux (<code class="codecolorer text default"><span class="text">false</span></code>) :</p>
<pre>\newboolean{correction}
\setboolean{correction}{false}</pre>
<p>Nous allons aussi définir un compteur pour les numéros des questions :</p>
<pre>\newcounter{QNumber}</pre>
<h4>Définir une question</h4>
<p>Nous allons ensuite écrire une macro <code class="codecolorer text default"><span class="text">\Question</span></code> qui permet de définir une nouvelle <em>question</em>. Cette macro reçoit deux arguments : le premier, facultatif, indique quel est le caractère à placer en fin de question (le plus souvent &lsquo;:&rsquo;, mais on peut aussi trouver un point d&rsquo;interrogation), et le second n&rsquo;est autre que la question elle-même. Nous écrivons donc :</p>
<pre>\newcommand{\Question}[2][:]{
  \stepcounter{QNumber}
  \noindent\textbf{Question \theQNumber} -- #2~#1
}</pre>
<h4>Ajouter une liste de choix</h4>
<p>Nous allons aussi définir un environnement <code class="codecolorer text default"><span class="text">Reponse</span></code> pour permettre d&rsquo;entrer les différents choix proposés à la sagacité des candidats :</p>
<pre>\newenvironment{Reponse}{
  \begin{list}{$\square$}{\leftmargin=5em}
}{
  \end{list}\vspace{1em}
}</pre>
<h4>Proposition correcte et distracteurs</h4>
<p>Il ne nous reste plus qu&rsquo;à définir la macro <code class="codecolorer text default"><span class="text">\Vrai</span></code> (resp. <code class="codecolorer text default"><span class="text">\Faux</span></code>) pour ajouter une proposition vraie (resp. fausse) :</p>
<pre>\newcommand{\Vrai}{
  \item[\ifthenelse{\boolean{correction}}{$\blacksquare$}{$\square$}]}
\newcommand{\Faux}{\item[$\square$]}</pre>
<p>Vous remarquerez que le symbole utilisé par la macro \Vrai dépend de l&rsquo;état de la variable booléenne correction. De cette façon, si correction est égal à true, les réponses justes seront marquées par un carré plein, ce qui révèle la correction du questionnaire.</p>
<h3>En-tête et pied de page</h3>
<p>On souhaite ajouter en en-tête de la première page des champs permettant au condidat d&rsquo;indiquer son nom et son prénom :</p>
<pre>\chead{\ifthenelse{\thepage=1}{
  \textbf{Nom :}
  \makebox[12em]{\dotfill}
  \hspace{2em}
  \textbf{Prénom :}
  \makebox[12em]{\dotfill}}{}} </pre>
<p>Si les questions continuent au verso, on veut que le recto de la première page se termine par la mention &laquo;&nbsp;<em>Tournez la page s&rsquo;il vous plaît</em>&nbsp;&raquo; :  </p>
<pre>\rfoot{\ifthenelse{\thepage=1}{\textit{(tournez la page s.v.p)}}{}}</pre>
<p>Je laisse au lecteur le soin de généraliser ceci à toutes les pages impaires dans le cas d&rsquo;un questionnaire comportant plus de deux pages pleines.</p>
<h3>Compilation du document</h3>
<p>Pour compiler votre document et produire un fichier PDF, il vous suffit d&rsquo;utiliser la commande <strong>pdflatex qcm.tex</strong>. En changeant la valeur du booléen <code class="codecolorer text default"><span class="text">correction</span></code>, vous obtenez ainsi un <a href="http://www.box.net/shared/dj68ol7bms">énoncé</a> et un <a href="http://www.box.net/shared/zgqc9c5nlq">corrigé</a>. <a href="http://pastebin.com/fpFpPJZa">Le code source est disponible ici</a>.</p>
<p>Cordialement,<br />
Cacophrène</p>
]]></content:encoded>
			<wfw:commentRss></wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>Aller simple pour le pays des fantômes</title>
		<link>https://blog.developpez.com/ocamlblog/p8646/informatique/aller_simple_pour_le_pays_des_fantomes</link>
		<comments>https://blog.developpez.com/ocamlblog/p8646/informatique/aller_simple_pour_le_pays_des_fantomes#comments</comments>
		<pubDate>Fri, 19 Feb 2010 20:04:04 +0000</pubDate>
		<dc:creator><![CDATA[Cacophrene]]></dc:creator>
				<category><![CDATA[Biologie]]></category>
		<category><![CDATA[Camlimages]]></category>
		<category><![CDATA[Informatique]]></category>
		<category><![CDATA[LablGTK2]]></category>
		<category><![CDATA[OCaml]]></category>

		<guid isPermaLink="false"></guid>
		<description><![CDATA[Après un long moment d&#8217;absence, je reviens pour vous parler d&#8217;une notion dont l&#8217;intérêt m&#8217;a été révélé par un article sur le blog de bluestorm. Il s&#8217;agit des types fantômes, dont je souhaite vous montrer ici tout le bien. Naturellement, je vais partir d&#8217;un cas concret autour de ce que je développe actuellement. Le contexte Une application pour microscopistes Je suis actuellement en train d&#8217;écrire les grandes lignes d&#8217;un programme destiné à des microscopistes amateurs, [&#8230;]]]></description>
				<content:encoded><![CDATA[<p><center></p>
<table>
<tr>
<td><a href="http://wwwfun.kurims.kyoto-u.ac.jp/soft/lsl/lablgtk.html" target="_blank"><img src="http://wwwfun.kurims.kyoto-u.ac.jp/soft/lsl/gtk-logo-half.gif"/></a></td>
<td><a href="http://pauillac.inria.fr/camlimages/" target="_blank"><img src="http://pauillac.inria.fr/camlimages/fumicaml2-small.jpg"/></a></td>
<td><a href="http://caml.inria.fr/" target="_blank"><img src="http://caml.inria.fr//pub/logos/caml-inria-fr.128x58.gif"/></a></td>
</tr>
</table>
<p></center></p>
<p>Après un long moment d&rsquo;absence, je reviens pour vous parler d&rsquo;une notion dont l&rsquo;intérêt m&rsquo;a été révélé par un article sur le blog de <a href="http://blog.huoc.org/7-en-bref-aussi.html" target="_blank">bluestorm</a>. Il s&rsquo;agit des <strong>types fantômes</strong>, dont je souhaite vous montrer ici tout le bien. Naturellement, je vais partir d&rsquo;un cas concret autour de ce que je développe actuellement.</p>
<p><span id="more-7"></span></p>
<h3>Le contexte</h3>
<h4>Une application pour microscopistes</h4>
<p>Je suis actuellement en train d&rsquo;écrire les grandes lignes d&rsquo;un programme destiné à des microscopistes amateurs, dont il existe déjà une version en Visual Basic (à réécrire et faire évoluer). Ce programme doit permettre de mettre en forme des images pour un forum en leur ajoutant un cartouche contenant diverses informations utiles (échelle, type de matériel, coloration éventuelle, etc.). Je vous donne une image ci-dessous pour vous faire une idée. Une caractéristique importante de ce logiciel réside dans sa capacité à dessiner des échelles à partir d&rsquo;une relation entre un nombre X de pixels et une taille Y en micromètres, déterminée sur une image étalon.</p>
<p><center><img src="http://blog.developpez.com/media/Helvella_leucomelaena-1_small.jpg" width="800" height="600" alt="" /><br />
<strong>Spores d&rsquo;helvelle après coloration.<br />
Objectif Zeiss Planapo 100/1.3</strong><br />
</center></p>
<h4>Image d&rsquo;étalon</h4>
<p>Une image étalon est une photo dont les éléments ont une taille connue, prise dans les mêmes conditions qu&rsquo;une image classique. Le plus souvent, il s&rsquo;agit d&rsquo;une « règle » dont les graduations sont espacées de 10 µm (la bagatelle de 100 graduations dans un millimètre&#8230;). Le programme charge l&rsquo;image d&rsquo;étalon, l&rsquo;analyse à la manière d&rsquo;un outil de reconnaissance de caractères (en beaucoup plus simple !), et en déduit la taille d&rsquo;une graduation, exprimée en pixels. De son côté, l&rsquo;utilisateur indique la taille réelle correspondante, en micromètres cette fois-ci <em>(cf. l&rsquo;image ci-dessous)</em>.</p>
<p><center><img src="http://blog.developpez.com/media/IMG_0029_small.JPG" width="800" height="533" alt="Micromètre" /><br />
<strong>Image d&rsquo;étalon (1 graduation correspond à 10 µm).<br />
Objectif Zeiss Plan Neofluar 63/1.25</strong><br />
</center></p>
<h3>Manipulation d&rsquo;images</h3>
<p>Pour automatiser la reconnaissance des graduations, on souhaite implémenter l&rsquo;algorithme suivant (plutôt une recette de cuisine) :</p>
<ul>
<li>Charger l&rsquo;image en couleur (24 bits).</li>
<li>La transformer en niveaux de gris.</li>
<li>Augmenter la luminosité pour éclaircir le fond.</li>
<li>Passer en noir et blanc.</li>
<li>Effacer les poussières pour uniformiser le fond.</li>
<li>Reboucher les trous dans les traits de graduations.</li>
<li>Mesurer les traits de graduation et les espaces qu&rsquo;ils délimitent.</li>
<li>En déduire la taille d&rsquo;une graduation (un espace + un trait).</li>
</ul>
<p>Pour y parvenir, nous allons utiliser <a href="http://wwwfun.kurims.kyoto-u.ac.jp/soft/lsl/lablgtk.html">LablGTK</a> et <a href="http://pauillac.inria.fr/camlimages/">CamlImages</a>, deux bibliothèques qui dialoguent plutôt bien. Nous allons nous fixer comme objectif d&rsquo;écrire un type <code class="codecolorer text default"><span class="text">picture</span></code> polymorphe. Nous nous en servirons pour distinguer les images en couleur (24 bits), en niveaux de gris et en noir et blanc. Les fonctions telles que <code class="codecolorer text default"><span class="text">get_width</span></code> ou <code class="codecolorer text default"><span class="text">get_pixbuf</span></code> s&rsquo;appliqueront à tout type d&rsquo;image. </p>
<h3>Choix d&rsquo;une interface</h3>
<p>Pour faire court, nous voudrions quelque chose comme ceci :</p>
<pre><strong>type</strong> 'a picture

<strong>type</strong> fullcolor
<strong>type</strong> grayscale
<strong>type</strong> monochrom

<strong>val</strong> from_file : string -> fullcolor picture
<em>(** Charge une image en couleurs. *)</em>

<em>(** Fonctions usuelles, valables pour tout type d'image. *)</em>
<strong>val</strong> copy : 'a picture -> 'a picture
<strong>val</strong> get_width : 'a picture -> int
<strong>val</strong> get_height : 'a picture -> int
<strong>val</strong> get_pixbuf : 'a picture -> GdkPixbuf.pixbuf

<strong>val</strong> grayscale : 
  ?filter:(Color.rgb -> int) -> 
  fullcolor picture -> grayscale picture
<em>(** Conversion en niveaux de gris d'une image couleur (24 bits). *)</em>

<strong>val</strong> black_and_white :
  ?threshold:int ->
  grayscale picture -> monochrom picture
<em>(** Conversion en noir et blanc d'une image en niveaux de gris. *)</em>

<strong>val</strong> noise_reduction :
  ?threshold:int ->
  monochrom picture -> monochrom picture
<em>(** Effacement des poussières sur une image en noir et blanc. *)</em>

<strong>val</strong> fill_gaps :
  ?threshold:int ->
  monochrom picture -> monochrom picture
<em>(** Rebouchage des trous sur une image en noir et blanc. *)</em></pre>
<p>Pour que cet algorithme ne reste pas nébuleux, voici <a href="http://www.box.net/shared/88aynv0ory" target="_blank">un PDF d&rsquo;illustration</a>.</p>
<h3>Du côté de l&rsquo;implémentation</h3>
<p>Après l&rsquo;interface, voyons l&rsquo;implémentation. <em>A priori</em>, cela peut sembler difficile. Pourtant, grâce aux types fantômes, nous n&rsquo;avons presque rien à faire ! Définissons d&rsquo;abord le type <code class="codecolorer text default"><span class="text">picture</span></code> :</p>
<pre><strong>type</strong> 'a picture = {pict: OImages.rgb24_class}</pre>
<p>Ne vous étonnez pas de trouver ici un record alors qu&rsquo;il paraît inutile. Le vrai type <code class="codecolorer text default"><span class="text">picture</span></code>, que j&rsquo;utilise dans mon application, contient d&rsquo;autres champs (mais <strong>aucun</strong> n&rsquo;est de type <code class="codecolorer text default"><span class="text">'a</span></code>, c&rsquo;est très important). Il a été simplifié ici par souci de pédagogie.</p>
<p>Définissons maintenant trois types fantômes :</p>
<pre><strong>type</strong> fullcolor
<strong>type</strong> grayscale
<strong>type</strong> monochrom</pre>
<p><font color="#000080">Un <strong>type fantôme</strong> est un <strong>type utilisé comme paramètre d&rsquo;un autre type</strong> (à la manière du type <code class="codecolorer text default"><span class="text">int</span></code> dans <code class="codecolorer text default"><span class="text">int list</span></code>), mais <strong>qui ne sert pas dans la définition de ce type</strong> (comme <code class="codecolorer text default"><span class="text">'a</span></code> dans <code class="codecolorer text default"><span class="text">'a picture</span></code> ci-dessus). Le type fantôme vous permet d&rsquo;ajouter une information (une <em>contrainte</em>) qui sera propagée correctement par l&rsquo;algorithme d&rsquo;inférence de types, sans impact sur l&rsquo;implémentation. Dans mon cas, il s&rsquo;agit surtout de s&rsquo;assurer que l&rsquo;on passe bien une image en niveaux de gris à la fonction <code class="codecolorer text default"><span class="text">black_and_white</span></code>, par exemple.</font></p>
<p>Nous pouvons maintenant écrire la fonction de création :</p>
<pre><strong>let</strong> from_file str = {pict = OImages.rgb24 (OImages.load path [])}</pre>
<p>puis les fonctions utilitaires :</p>
<pre><strong>let</strong> get_width t = t.pict#width
<strong>let</strong> get_height t = t.pict#height
<strong>let</strong> get_pixbuf t = Imagegdk.to_pixbuf t.pict#coerce
<strong>let</strong> copy t = {t with pict = Oo.copy t.pict}</pre>
<p>Nous pouvons maintenant en venir aux fonctions qui agissent directement sur l&rsquo;image et, comme vous allez le constater, on ne se doute de rien en lisant cette partie du code  :</p>
<pre><strong>let</strong> grayscale ?(filter = Color.brightness) pic =
  <strong>let</strong> res = copy pic <strong>in</strong>
  <strong>let</strong> img = res.pict <strong>in</strong>
  <strong>for</strong> x = 0 <strong>to</strong> img#width - 1 <strong>do</strong>
    <strong>for</strong> y = 0 <strong>to</strong> img#height - 1 <strong>do</strong>
      <strong>let</strong> res = filter (img#unsafe_get x y) <strong>in</strong>
      img#unsafe_set x y {Color.r = res; g = res; b = res}
    <strong>done</strong>
  <strong>done</strong>;
  res

<strong>let</strong> black = {Color.r = 0; g = 0; b = 0}
<strong>let</strong> white = {Color.r = 255; g = 255; b = 255}

<strong>let</strong> black_and_white ?(threshold = 0x90) pic =
  <strong>let</strong> res = copy pic <strong>in</strong>
  <strong>let</strong> img = res.pict <strong>in</strong>
  <strong>for</strong> x = 0 <strong>to</strong> img#width - 1 <strong>do</strong>
    <strong>for</strong> y = 0 <strong>to</strong> img#height - 1 <strong>do</strong>
      <strong>let</strong> rgb = img#unsafe_get x y <strong>in</strong>
      img#unsafe_set x y (<strong>if</strong> rgb.Color.r > threshold <strong>then</strong> white <strong>else</strong> black)
    <strong>done</strong>
  <strong>done</strong>;
  res</pre>
<p>et ainsi de suite (le détail d&rsquo;implémentation des fonctions <code class="codecolorer text default"><span class="text">&nbsp;</span></code> et <code class="codecolorer text default"><span class="text">fill_gaps</span></code> sort du cadre de ce billet; je peux développer sur demande, mais il ne me semble pas pertinent de le faire ici). Avec un appel à <code class="codecolorer text default"><span class="text">ocamlc -i</span></code>, toutes ces fonctions accepteraient des images de type <code class="codecolorer text default"><span class="text">'a picture</span></code>. La contrainte que nous avons posée dans l&rsquo;interface est à la fois valide et transparente du côté de l&rsquo;implémentation.</p>
<p>Il est intéressant de noter que les types fantômes apportent une information supplémentaire sans impact sur l&rsquo;implémentation. Cela renforce la sûreté du code sans nuire aux performances (imaginez la lourdeur qui consisterait à s&rsquo;assurer d&rsquo;abord que tous les pixels sont bien des teintes de gris !).</p>
<h3>En guise de conclusion&#8230;</h3>
<p>Je parlais d&rsquo;un <em>aller simple</em> vers le monde des fantômes. Vous savez maintenant pourquoi : quand on y a goûté, on ne peut plus s&rsquo;en passer. <strong>Les types fantômes, c&rsquo;est bien, mangez-en !</strong></p>
<p>À bientôt,<br />
Cacophrène</p>
]]></content:encoded>
			<wfw:commentRss></wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
	</channel>
</rss>
