|
|
|
|
\documentclass[a4paper,10pt]{article}
|
|
|
|
|
|
|
|
|
|
% \listfiles % pour le debug
|
|
|
|
|
|
|
|
|
|
\usepackage[french]{babel}
|
|
|
|
|
\usepackage[utf8]{inputenc}
|
|
|
|
|
\usepackage[T1]{fontenc}
|
|
|
|
|
\usepackage{makeidx}
|
|
|
|
|
\usepackage{listings}
|
|
|
|
|
|
|
|
|
|
% \lstset{frame=single} % dessin d'un cadre autour du listing
|
|
|
|
|
\lstset{basicstyle=\ttfamily\small}
|
|
|
|
|
\lstset{aboveskip=0.222em,belowskip=0.222em}
|
|
|
|
|
|
|
|
|
|
\usepackage{babel}
|
|
|
|
|
|
|
|
|
|
\usepackage{graphicx} % for gnuplot ylabel rotate
|
|
|
|
|
|
|
|
|
|
\usepackage{pifont} % caractères rigolos
|
|
|
|
|
\usepackage{enumitem}
|
|
|
|
|
\setitemize[1]{label={\ding{87}}}
|
|
|
|
|
\frenchbsetup{CompactItemize=false}
|
|
|
|
|
|
|
|
|
|
% \usepackage{color}
|
|
|
|
|
% \usepackage{url}
|
|
|
|
|
\usepackage{xspace}
|
|
|
|
|
\usepackage[verbose]{layout}
|
|
|
|
|
|
|
|
|
|
\setlength \parskip {0.333em}
|
|
|
|
|
|
|
|
|
|
\makeatletter
|
|
|
|
|
% exlpication de ce truc ?
|
|
|
|
|
\def\verbatim@font{\normalfont\ttfamily\small}
|
|
|
|
|
\makeatother
|
|
|
|
|
|
|
|
|
|
\setlength{\hoffset}{0em}
|
|
|
|
|
\setlength{\textheight}{640pt}
|
|
|
|
|
\setlength{\textwidth}{422pt}
|
|
|
|
|
\setlength{\marginparwidth}{10pt}
|
|
|
|
|
|
|
|
|
|
\makeindex
|
|
|
|
|
|
|
|
|
|
% ------ a few new commands
|
|
|
|
|
\newcommand{\interparagraphe { \vspace{60pt} } }
|
|
|
|
|
|
|
|
|
|
% -------------------------------------------------------------------
|
|
|
|
|
\title{Floating images processing}
|
|
|
|
|
\author{tTh}
|
|
|
|
|
|
|
|
|
|
\begin{document}
|
|
|
|
|
\maketitle
|
|
|
|
|
|
|
|
|
|
\section{Image flottante ?}
|
|
|
|
|
|
|
|
|
|
Mais de quoi parle-t-on exactement ?
|
|
|
|
|
|
|
|
|
|
% XXX XXX XXX\vspace{1em}
|
|
|
|
|
|
|
|
|
|
Traditionnellement, les valeurs des pixels dans les images
|
|
|
|
|
informatiques sont mémorisées sur 8 bits, un octet\index{octet},
|
|
|
|
|
soit 256 valeurs différentes.
|
|
|
|
|
Ceci dit, on trouve parfois des images codées sur 16 bits par
|
|
|
|
|
composante, mais c'est loin d'être le cas général.
|
|
|
|
|
J'ai donc souhaité aller plus loin, et coder chaque canal de
|
|
|
|
|
chaque pixel en virgule flottante sur 32bits, le type
|
|
|
|
|
\texttt{float}\index{float} du langage C. Ce qui correspond
|
|
|
|
|
à la norme IEEE 754-1985.
|
|
|
|
|
|
|
|
|
|
% XXX XXX XXX\vspace{1em}
|
|
|
|
|
|
|
|
|
|
Attention, tout le code que nous allons voir ensemble est en
|
|
|
|
|
perpétuelle évolution\footnote{voir page \pageref{TODO}},
|
|
|
|
|
et sa fiablité (surtout sur certains aspects mathématiques)
|
|
|
|
|
reste à démontrer\index{valgrind}.
|
|
|
|
|
Mais le service après-vente est assez réactif. Du moins
|
|
|
|
|
pour ceux qui suivent le canal \texttt{\#tetalab} sur le réseau
|
|
|
|
|
IRC de Freenode.
|
|
|
|
|
|
|
|
|
|
\textbf{Attention !} ce document commence par une bonne rafale
|
|
|
|
|
de technique parfois hardue\footnote{gni?}.
|
|
|
|
|
Vous avez parfaitement le droit de sauter directement à
|
|
|
|
|
la page \pageref{outils} pour quelque chose de plus concret.
|
|
|
|
|
|
|
|
|
|
% -------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
\setlength \parskip {0em}
|
|
|
|
|
|
|
|
|
|
\tableofcontents
|
|
|
|
|
\pagebreak
|
|
|
|
|
\setlength \parskip {0.40em}
|
|
|
|
|
|
|
|
|
|
% \layout \pagebreak
|
|
|
|
|
|
|
|
|
|
% -------------------------------------------------------------------
|
|
|
|
|
\section{Théorie}\index{théorie}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Pour le moment, seule la quête de l'empirisme absolu a été
|
|
|
|
|
visée. Les justifications mathématiques attendront le retour
|
|
|
|
|
du schmod777. Ceci dit, rien ne nous empêche d'aller consulter
|
|
|
|
|
Wikipedia~:
|
|
|
|
|
|
|
|
|
|
\begin{quotation}
|
|
|
|
|
An IEEE 754 32-bit base-2 floating-point variable has
|
|
|
|
|
a maximum value of
|
|
|
|
|
$(2 - 2^{-23}) \times 2^{127} \approx 3.4028235 \times 10^{38}$.
|
|
|
|
|
All integers with 7 or fewer decimal digits, and any $2^{n}$ for
|
|
|
|
|
a whole number $-149 \leq n \leq 127$, can be converted exactly into
|
|
|
|
|
an IEEE 754 single-precision floating-point value.
|
|
|
|
|
|
|
|
|
|
In the IEEE 754-2008 standard, the 32-bit base-2 format is
|
|
|
|
|
officially referred to as binary32; it was called single in
|
|
|
|
|
IEEE 754-1985.
|
|
|
|
|
\end{quotation}
|
|
|
|
|
|
|
|
|
|
Ce qui nous conduit à estimer qu'il est possible de cumuler environ
|
|
|
|
|
quelques milliers d'images standard à 256 niveaux, sans trop avoir
|
|
|
|
|
à se soucier des éventuelles pertes de précision. Mais ça demande
|
|
|
|
|
à être confirmé par des esprits supérieurs.
|
|
|
|
|
|
|
|
|
|
\subsection{Dynamique}\index{dynamique}
|
|
|
|
|
|
|
|
|
|
Dynamique, précision et macheps.
|
|
|
|
|
|
|
|
|
|
\subsection{Pixel négatif ?}
|
|
|
|
|
|
|
|
|
|
Il est très difficle d'imaginer une lumière négative.
|
|
|
|
|
|
|
|
|
|
% -------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
\section{Premier exemple}\index{exemple}\label{exemple}
|
|
|
|
|
|
|
|
|
|
\textsc{FloatImg} a débuté sous la forme de quelques fonctions
|
|
|
|
|
basiques en C, gérant la structure des données d'image en mémoire
|
|
|
|
|
et sur disque. Ça a été imaginé de façon presque empirique,
|
|
|
|
|
mais nous sommes tous là pour améliorer les choses, dans
|
|
|
|
|
la mesure de nos moyens.
|
|
|
|
|
Nous allons donc directement rentrer au cœur du problème,
|
|
|
|
|
en écrivant quelques lignes de code.
|
|
|
|
|
|
|
|
|
|
Pour commencer par quelque chose de simple,
|
|
|
|
|
nous allons créer une image RGB\index{RGB} complètement noire,
|
|
|
|
|
puis l'enregistrer dans un fichier \texttt{.fimg}\index{.fimg},
|
|
|
|
|
un format complètement inconnu, puisque je viens de l'inventer
|
|
|
|
|
à l'instant même.
|
|
|
|
|
|
|
|
|
|
Tout d'abord, nous devons déclarer et garnir quelques variables
|
|
|
|
|
pour gérer la machinerie interne.
|
|
|
|
|
|
|
|
|
|
\begin{lstlisting}
|
|
|
|
|
int width = 640, height = 480;
|
|
|
|
|
char *fname = "exemple.fimg";
|
|
|
|
|
FloatImg fimg;
|
|
|
|
|
\end{lstlisting}
|
|
|
|
|
|
|
|
|
|
Ensuite, nous enchainerons trois étapes : création de l'image
|
|
|
|
|
en mémoire centrale, initialisation des valeurs de chaque pixel à 0.0,
|
|
|
|
|
et pour conclure, enregistrement dans un fichier\footnote{Au format
|
|
|
|
|
ésotérique, mais très véloce.} binaire.
|
|
|
|
|
|
|
|
|
|
\begin{lstlisting}
|
|
|
|
|
foo = fimg_create(&fimg, width, height, FIMG_TYPE_RGB);
|
|
|
|
|
if (foo) {
|
|
|
|
|
fprintf(stderr, "create floatimg -> %d\n", foo);
|
|
|
|
|
exit(1);
|
|
|
|
|
}
|
|
|
|
|
fimg_clear(&fimg);
|
|
|
|
|
foo = fimg_dump_to_file(&fimg, fname, 0);
|
|
|
|
|
if (foo) {
|
|
|
|
|
fprintf(stderr, "dump fimg -> %d\n", foo);
|
|
|
|
|
exit(1);
|
|
|
|
|
}
|
|
|
|
|
\end{lstlisting}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Une fois ce code enrobé dans un \texttt{main()}, compilé et exécuté,
|
|
|
|
|
nous pouvons entrevoir, grâce au logiciel
|
|
|
|
|
\texttt{fimgstats} (voir page \pageref{fimgstats}),
|
|
|
|
|
le résultat sous forme de chiffres divers, et/ou inutiles~:
|
|
|
|
|
|
|
|
|
|
\begin{verbatim}
|
|
|
|
|
$ ./fimgstats quux.img
|
|
|
|
|
----------- numbers from 'quux.img' :
|
|
|
|
|
640 480 3 0x7f3718c4f010 0x7f3718d7b010 0x7f3718ea7010
|
|
|
|
|
surface 307200
|
|
|
|
|
mean values:
|
|
|
|
|
R 0.000000
|
|
|
|
|
G 0.000000
|
|
|
|
|
B 0.000000
|
|
|
|
|
A 0.000000
|
|
|
|
|
max value 0.000000
|
|
|
|
|
\end{verbatim}
|
|
|
|
|
|
|
|
|
|
Nous avons donc sous la main une mécanique qui ne demande qu'à
|
|
|
|
|
faire des trucs futiles et des images qui clignotent.
|
|
|
|
|
La suite vers la page \pageref{codaz}.
|
|
|
|
|
|
|
|
|
|
Vous trouverez dans le répertoire \texttt{tools/}\index{tools/}
|
|
|
|
|
d'autres exemples de mise en œuvre des fonctions disponibles
|
|
|
|
|
sous formes d'outils en ligne de commande,
|
|
|
|
|
lesquels sont décrits en page \pageref{outils}.
|
|
|
|
|
|
|
|
|
|
% -------------------------------------------------------------------
|
|
|
|
|
\section{Installation}
|
|
|
|
|
|
|
|
|
|
\textit{Attention, ça devient un peu gore. Myrys, punk, toussa\dots}
|
|
|
|
|
|
|
|
|
|
\subsection{Prérequis}
|
|
|
|
|
|
|
|
|
|
Vous devez, en dehors des outils classiques (bash, gcc, make\dots),
|
|
|
|
|
avoir quelques bibliothèques installées\footnote{Les \texttt{-dev}
|
|
|
|
|
pour Debian et dérivées}~:
|
|
|
|
|
\textsf{libv4l2, libpnglite, libtiff,
|
|
|
|
|
libnetpbm\footnote{package libnetpbm10-dev},
|
|
|
|
|
libz\footnote{package zlib1g-dev}},
|
|
|
|
|
éventuellement avec le \textsf{-dev} correspondant,
|
|
|
|
|
et probablement d'autres choses.
|
|
|
|
|
|
|
|
|
|
Il est même quasiment certain que Bash soit indispensable, tout
|
|
|
|
|
comme \textsc{gnu}/make\index{make}.
|
|
|
|
|
Une connaissance de base de l'utilisation du shell\index{shell}
|
|
|
|
|
et de l'écriture de Makefile's sera un plus.
|
|
|
|
|
|
|
|
|
|
Il faut aussi savoir où trouver le code.
|
|
|
|
|
|
|
|
|
|
\subsection{Compilation}
|
|
|
|
|
|
|
|
|
|
Un script \texttt{build.sh} permet de construire approximativement
|
|
|
|
|
le bouzin. Il est loin d'être parfait\footnote{Il doit être possible
|
|
|
|
|
de faire un Makefile récursif, mais\dots}.
|
|
|
|
|
Dans chacun des répertoires à traiter, ce script devrait trouver
|
|
|
|
|
un Makefile et un fichier \texttt{t.c} qui est le source de la cible
|
|
|
|
|
par défaut du make.
|
|
|
|
|
|
|
|
|
|
Pour le moment, la procédure d'installation est un peu rude,
|
|
|
|
|
pour ne pas dire clairement sommaire.
|
|
|
|
|
Si le résultat de l'étape compilation vous semble correct,
|
|
|
|
|
vous pouvez copier les deux fichiers \texttt{floatimg.h} et
|
|
|
|
|
\texttt{libfloatimg.a} dans un emplacement approprié, par exemple
|
|
|
|
|
\texttt{/usr/local/include} et \texttt{/usr/local/lib}.
|
|
|
|
|
|
|
|
|
|
Le script \texttt{install.sh}, à la racine du projet, est censé
|
|
|
|
|
faciliter un peu la chose. Il prend également en compte la copie
|
|
|
|
|
des divers binaires du dossier \texttt{tools/} (cf page \pageref{outils})
|
|
|
|
|
dans le répertoire prévu à cet effet : \texttt{/usr/local/bin}.
|
|
|
|
|
|
|
|
|
|
Il reste enfin quelques exemples d'utilisation des outils de la
|
|
|
|
|
ligne de commande depuis un shell
|
|
|
|
|
dans le répertoire \texttt{scripts/}.
|
|
|
|
|
Ils sont décrits plus en détail page
|
|
|
|
|
\pageref{scripts}, et c'est à vous de les adapter à votre \textsl{usecase}.
|
|
|
|
|
Faites-en ce que vous voulez.
|
|
|
|
|
|
|
|
|
|
% =================================================================
|
|
|
|
|
|
|
|
|
|
\section{Utilisation coté codeur}\label{codaz}
|
|
|
|
|
|
|
|
|
|
Classiquement, il y a un fichier \texttt{.h} à inclure dans chacun
|
|
|
|
|
de vos codes source,
|
|
|
|
|
\texttt{floatimg.h}, généralement logé dans \texttt{/usr/local/include}
|
|
|
|
|
contenant un certain nombre de définition de structures, de macros,
|
|
|
|
|
de constantes\footnote{À l'ancienne, via le pré-processeur}
|
|
|
|
|
et les prototypes des fonctions utilisables par vos logiciels.
|
|
|
|
|
|
|
|
|
|
Au niveau du code source, ces fonctions sont approximativement
|
|
|
|
|
classées en deux catégories : \texttt{lib/} et \texttt{funcs/}.
|
|
|
|
|
La première contient les choses qui sont relativement figées,
|
|
|
|
|
et la seconde celles qui risquent de bouger. Cette classification
|
|
|
|
|
est en fait arbitraire.
|
|
|
|
|
|
|
|
|
|
\subsection{Structures, macros\dots}
|
|
|
|
|
|
|
|
|
|
Les pixels flottants d'une image résidant en mémoire centrale
|
|
|
|
|
sont décrits par un ensemble
|
|
|
|
|
de données (certains appelent ça des \textsl{metadatas}) regroupées
|
|
|
|
|
dans une jolie structure que nous allons examiner dès maintenant.
|
|
|
|
|
|
|
|
|
|
\begin{lstlisting}
|
|
|
|
|
/* in memory descriptor */
|
|
|
|
|
typedef struct {
|
|
|
|
|
int width;
|
|
|
|
|
int height;
|
|
|
|
|
int type;
|
|
|
|
|
float fval;
|
|
|
|
|
int count;
|
|
|
|
|
float *R, *G, *B, *A;
|
|
|
|
|
int reserved;
|
|
|
|
|
} FloatImg;
|
|
|
|
|
\end{lstlisting}\index{FloatImg}
|
|
|
|
|
|
|
|
|
|
Les deux premiers champs sont \textsl{obvious}.
|
|
|
|
|
Le troisième est le type d'image : pour le moment, il y en a trois
|
|
|
|
|
qui sont définis\footnote{et plus ou moins bien gérés\dots} :
|
|
|
|
|
gris, rgb et rgba\index{rgba}.
|
|
|
|
|
Les constantes adéquates sont dans \texttt{floatimg.h}
|
|
|
|
|
|
|
|
|
|
\begin{lstlisting}
|
|
|
|
|
#define FIMG_TYPE_GRAY 1
|
|
|
|
|
#define FIMG_TYPE_RGB 3
|
|
|
|
|
#define FIMG_TYPE_RGBA 4
|
|
|
|
|
#define FIMG_TYPE_RGBZ 99
|
|
|
|
|
\end{lstlisting}
|
|
|
|
|
|
|
|
|
|
Un peu plus loin, nous avons les pointeurs vers les
|
|
|
|
|
différents \textsl{pixmaps} de l'image. En principe l'organisation
|
|
|
|
|
interne de ces zones est improbable, puisque elle dérive
|
|
|
|
|
d'idées approximatives. C'est cette utilisation constructive de larache
|
|
|
|
|
qui fait que seuls les champs documentés de cette structure ne sont
|
|
|
|
|
pas explosifs.
|
|
|
|
|
|
|
|
|
|
Mais revenons aux choses sérieuses\dots
|
|
|
|
|
Les deux champs suivants (fval et count) sont à la disposition du
|
|
|
|
|
\textsl{yuser}
|
|
|
|
|
qui peut jouer avec à loisir pour faire, par exemple, ce genre de
|
|
|
|
|
chose : imaginons un périphérique de capture qui nous fournisse des
|
|
|
|
|
images en gris sur 4 bits. Et que nous voulions cumuler\index{cumul}
|
|
|
|
|
quelques images...
|
|
|
|
|
|
|
|
|
|
Le champ \textsl{count} sera mis à 0 et
|
|
|
|
|
le champ \textsl{fval} sera initialisé à 15.0
|
|
|
|
|
(qui est la valeur maximale que peut renvoyer le capteur).
|
|
|
|
|
Ensuite, dans la boucle capture/cumul, \textsl{count} sera
|
|
|
|
|
incrémenté à chaque passe, et nous aurons donc, en finale,
|
|
|
|
|
toutes les informations nécessaires pour exploiter au mieux la dynamique
|
|
|
|
|
de notre image dans les étapes ultérieures, puisque la valeur
|
|
|
|
|
maximale théorique est égale à $fval * count$.
|
|
|
|
|
|
|
|
|
|
La fonction \texttt{fimg\_printhead(FloatImg *h)} affiche
|
|
|
|
|
sommairement le contenu de ce descripteur,
|
|
|
|
|
et \texttt{fimg\_describe(FloatImg *head, char *txt)} propose
|
|
|
|
|
un affichage plus détaillé. Ça aide parfois.
|
|
|
|
|
|
|
|
|
|
Une bonne partie des fonctions que nous allons voir est indéterministe.
|
|
|
|
|
Ce qui veut dire, en langage de tous les soirs, que ça risque de ne
|
|
|
|
|
pas être la même chose dans l'avenir.
|
|
|
|
|
|
|
|
|
|
% ----------------------------------
|
|
|
|
|
|
|
|
|
|
\subsection{Les fondations}\index{lib/}
|
|
|
|
|
|
|
|
|
|
La première chose que nous devons absolument voir est la gestion
|
|
|
|
|
dynamique de la mémoire qui sera occupée par tous ces pixels flottants,
|
|
|
|
|
ce qui est un sujet parfois délicat\footnote{GC or not GC ?}.
|
|
|
|
|
Elle est donc faite, à la base, par ces deux fonctions~:
|
|
|
|
|
|
|
|
|
|
\begin{lstlisting}
|
|
|
|
|
int fimg_create(FloatImg *fimg, int w, int h, int type);
|
|
|
|
|
int fimg_destroy(FloatImg *fimg);
|
|
|
|
|
\end{lstlisting}
|
|
|
|
|
|
|
|
|
|
L'appelant doit lui-même gérer le descripteur d'image (une structure
|
|
|
|
|
C décrite plus haut) en le considérant comme un type semi-opaque dont
|
|
|
|
|
la forme peut varier. Certains membres de cette structure sont
|
|
|
|
|
documentés dans ce document, et les autres sont dangereux à
|
|
|
|
|
toucher. Les types d'images actuellement gérés sont les trois grands
|
|
|
|
|
classiques : gray, rgb et rgba. et expliquées quelques lignes plus haut.
|
|
|
|
|
|
|
|
|
|
Comme vous allez le voir plus loin, il y a plein de fonctions qui
|
|
|
|
|
prennent en argument deux images: une source et une destination.
|
|
|
|
|
Et dans la plupart des cas, ces deux images doivent être compatibles,
|
|
|
|
|
c'est à dire même type et mêmes dimensions.
|
|
|
|
|
|
|
|
|
|
\begin{lstlisting}
|
|
|
|
|
/* return 0 if compatible */
|
|
|
|
|
int fimg_images_not_compatible(FloatImg *a, FloatImg *b);
|
|
|
|
|
\end{lstlisting}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
C'est bien beau d'être enfin résident en mémoire centrale, mais
|
|
|
|
|
pouvoir aussi exister à long terme en étant stocké dans la matrice
|
|
|
|
|
est tout aussi pertinent. Il y a deux opérations qui supportent le
|
|
|
|
|
reste des transits ram/ps.
|
|
|
|
|
|
|
|
|
|
\begin{lstlisting}
|
|
|
|
|
int fimg_dump_to_file(FloatImg *fimg, char *fname, int notused);
|
|
|
|
|
int fimg_load_from_dump(char *fname, FloatImg *where);
|
|
|
|
|
\end{lstlisting}
|
|
|
|
|
|
|
|
|
|
Recharger une image depuis un fichier nécessite que celle-ci et
|
|
|
|
|
l'image de destination en mémoire
|
|
|
|
|
ait précisément les mêmes caractéristiques
|
|
|
|
|
(taille, type...), donc l'image en ram doit être
|
|
|
|
|
pré-allouée. On peut connaitre ces valeurs en appelant
|
|
|
|
|
\texttt{int fimg\_fileinfos(char *fname, int datas[3])}.
|
|
|
|
|
|
|
|
|
|
Si tout s'est bien passé (valeur retournée égale à 0),
|
|
|
|
|
on va trouver la largeur dans \texttt{datas[0]},
|
|
|
|
|
la hauteur dans \texttt{datas[1]} et le type dans
|
|
|
|
|
\texttt{datas[2]}\footnote{La fonction
|
|
|
|
|
\texttt{fimg\_type\_is\_valid(int type)} peut vous aider\dots}.
|
|
|
|
|
|
|
|
|
|
Je sais aussi que certains d'entre vous aiment la facilité, aussi
|
|
|
|
|
je vais vous révéler l'existence d'un nouveau truc bien plus
|
|
|
|
|
simple, une fonction qui enchaine ces deux actions
|
|
|
|
|
(allocation, puis lecture), et s'utilise
|
|
|
|
|
comme ça :
|
|
|
|
|
|
|
|
|
|
\begin{lstlisting}
|
|
|
|
|
FloatImg head;
|
|
|
|
|
memset(&head, 0, sizeof(FloatImg));
|
|
|
|
|
foo = fimg_create_from_dump("lena.fimg", &head);
|
|
|
|
|
\end{lstlisting}
|
|
|
|
|
|
|
|
|
|
Si la valeur retournée est différente de 0, c'est que quelque
|
|
|
|
|
chose s'est mal passé.
|
|
|
|
|
Certains messages sont parfois parfois explicites.
|
|
|
|
|
|
|
|
|
|
% _________
|
|
|
|
|
|
|
|
|
|
\subsection{Dessiner}
|
|
|
|
|
|
|
|
|
|
Bon, vous avez une image latente, et vous souhaitez dessiner dessus
|
|
|
|
|
(ou dedans ?) avec vos encres flottantes ?
|
|
|
|
|
Il y a des fonctions pour ça, par exemple~:
|
|
|
|
|
|
|
|
|
|
\begin{lstlisting}
|
|
|
|
|
int fimg_plot_rgb(FloatImg *head, int x, int y, float r, float g, float b);
|
|
|
|
|
\end{lstlisting}
|
|
|
|
|
|
|
|
|
|
Les paramètres sont explicites, mais leur validité doit être
|
|
|
|
|
sévèrement controlée par l'appelant. Il y a une fonction
|
|
|
|
|
soeur, \texttt{fimg\_add\_rgb}\index{fimg\_add\_rgb},
|
|
|
|
|
qui ajoute du rgb à un pixel, laquelle fonction a d'ailleurs été
|
|
|
|
|
à la base de la seconde génération de la
|
|
|
|
|
photographie\index{photographie} en cumul\index{cumul}.
|
|
|
|
|
|
|
|
|
|
Inversement, la fonction
|
|
|
|
|
\texttt{fimg\_get\_rgb(FloatImg *head, int x, int y, float *rgb)}
|
|
|
|
|
permet de lire les valeurs des trois canaux d'un pixel donné.
|
|
|
|
|
Là aussi, il n'y a aucun contrôle sur la validité des valeurs
|
|
|
|
|
$x$ et $y$ de la demande.
|
|
|
|
|
|
|
|
|
|
% ----------------------------------
|
|
|
|
|
|
|
|
|
|
\subsection{Contraste}\index{contraste}\label{contraste}
|
|
|
|
|
|
|
|
|
|
Certaines opérations d'ajustement du contraste d'une image
|
|
|
|
|
semblent cohérents avec la notion d'image flottante.
|
|
|
|
|
Certains d'entre eux, les plus simples, sont disponibles.
|
|
|
|
|
Les autres sont à imaginer.
|
|
|
|
|
|
|
|
|
|
\begin{figure}[h]
|
|
|
|
|
\input{cos01.tex} % XXX XXX XXX
|
|
|
|
|
\caption{Correcteur cos01}
|
|
|
|
|
\end{figure}
|
|
|
|
|
|
|
|
|
|
Ils prennent chacun trois paramètres, d'abord les images
|
|
|
|
|
source et destination (\texttt{* FloatImg}), et le troisième
|
|
|
|
|
est un nombre en double précision donnant la valeur
|
|
|
|
|
maximale \textsl{supposée} de l'image source,
|
|
|
|
|
valeur qui peut être déterminée de plusieurs manières.
|
|
|
|
|
|
|
|
|
|
\begin{lstlisting}
|
|
|
|
|
/* source in lib/contrast.c */
|
|
|
|
|
int fimg_square_root(FloatImg *s, FloatImg *d, double maxval);
|
|
|
|
|
int fimg_power_2(FloatImg *s, FloatImg *d, double maxval);
|
|
|
|
|
int fimg_cos_01(FloatImg *s, FloatImg *d, double maxval);
|
|
|
|
|
int fimg_cos_010(FloatImg *s, FloatImg *d, double maxval);
|
|
|
|
|
\end{lstlisting}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Si vous souhaitez rajouter votre propre méthode de modification
|
|
|
|
|
de contraste, il y a quelques explication en page \pageref{exemplefunc}.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
\begin{figure}[h]
|
|
|
|
|
\input{cos010.tex} % XXX XXX XXX
|
|
|
|
|
\caption{Correcteur cos010}
|
|
|
|
|
\end{figure}
|
|
|
|
|
|
|
|
|
|
Rappelons qu'il est possible pour un logiciel applicatif
|
|
|
|
|
comme \texttt{grabvidseq} (cf page \pageref{grabvidseq})
|
|
|
|
|
de renseigner deux champs du descripteur d'image avec des
|
|
|
|
|
données pertinentes.
|
|
|
|
|
Ces deux champs sont \textit{fval} et \textit{count}.
|
|
|
|
|
Dans ce cas particulier, le premier indique la valeur
|
|
|
|
|
maximale du capteur, et le second sert à compter le
|
|
|
|
|
nombre de capture\footnote{Et c'est bien géré aussi
|
|
|
|
|
dans l'upscaling.} effectuées.
|
|
|
|
|
|
|
|
|
|
La fonction
|
|
|
|
|
\texttt{fimg\_normalize(FloatImg *fi, double maxima, int notused);}
|
|
|
|
|
tente de gérer ce cas d'utilisation. Son ajout au captureur d'images
|
|
|
|
|
floues sera probablement le bienvenue. Je me suis bien rendu compte
|
|
|
|
|
à l'usage\footnote{Une histoire de \textit{fonderie}, un logiciel
|
|
|
|
|
censé faire des films flous à partir d'images floues} en situation
|
|
|
|
|
festive qu'il manquait des données dans la chaine de traitement.
|
|
|
|
|
|
|
|
|
|
L'autre façon de procéder est d'explorer notre image à la
|
|
|
|
|
recherche de la valeur maximale.
|
|
|
|
|
La fonction \texttt{float fimg\_get\_maxvalue(\&fimg)} est
|
|
|
|
|
prévue pour ça de façon sommaire.
|
|
|
|
|
C'est actuellement la méthode utilisée par l'outil qui
|
|
|
|
|
sert à faire les modifications de contraste (page \pageref{fimgfx}).
|
|
|
|
|
On pourra aussi envisager d'utiliser
|
|
|
|
|
\texttt{fimg\_get\_minmax\_rgb(FloatImg *head, float mmvals[6])},
|
|
|
|
|
qui permet un contrôle bien plus fin des dérives.
|
|
|
|
|
|
|
|
|
|
La prochaine étape consistera à trouver une façon de faire
|
|
|
|
|
une égalisation\index{égalisation} par histogramme\index{histogramme}
|
|
|
|
|
qui respecte, dans toute sa futilité, le concept\index{concept}
|
|
|
|
|
de pixel flottant.
|
|
|
|
|
|
|
|
|
|
% ----------------------------------
|
|
|
|
|
|
|
|
|
|
\subsection{Géométrie}\index{géométrie}\label{geometrie}
|
|
|
|
|
|
|
|
|
|
Très prochainement, le retour du blitter\index{blitter}.
|
|
|
|
|
Et pour attendre, un truc improbable, voire même
|
|
|
|
|
inutile, en fait l'inverse de l'upscaling.
|
|
|
|
|
|
|
|
|
|
\begin{lstlisting}
|
|
|
|
|
int fimg_halfsize_0(FloatImg *src, FloatImg *dst, int notused);
|
|
|
|
|
\end{lstlisting}
|
|
|
|
|
|
|
|
|
|
Attention lors de l'appel, le descripteur \texttt{dst} ne doit pas
|
|
|
|
|
contenir d'image, et doit être effacé avec un bon
|
|
|
|
|
\texttt{memset(\&result, 0, sizeof(FloatImg));} bien senti.
|
|
|
|
|
Et le résultat est très moyen : il n'y a pas d'interpolation.
|
|
|
|
|
|
|
|
|
|
\begin{lstlisting}
|
|
|
|
|
int fimg_extract_0(FloatImg *src, FloatImg *dst, int x, int y);
|
|
|
|
|
\end{lstlisting}
|
|
|
|
|
|
|
|
|
|
Contrairement à la fonction précédente, celle-ci demande absolument une
|
|
|
|
|
image de destination initialisée aux dimensions (largeur et hauteur)
|
|
|
|
|
désirées.
|
|
|
|
|
|
|
|
|
|
\begin{lstlisting}
|
|
|
|
|
int fimg_rotate_90(FloatImg *src, FloatImg *dst, int notused);
|
|
|
|
|
\end{lstlisting}
|
|
|
|
|
|
|
|
|
|
Rotation\index{rotation} de 90 degrés dans le sens
|
|
|
|
|
horlogique\footnote{ou trigonométrique,le code et la doc
|
|
|
|
|
ne semblent pas d'accord.} d'une image RGB.
|
|
|
|
|
L'image de destination peut être soit vierge, soit pré-allouée
|
|
|
|
|
aux bonnes dimensions (échange W et H).
|
|
|
|
|
|
|
|
|
|
% ----------------------------------
|
|
|
|
|
|
|
|
|
|
\subsection{Exportation \& Importation}\index{export}\label{export}
|
|
|
|
|
|
|
|
|
|
Notre format de fichier étant totalement inconnu, il nous
|
|
|
|
|
faut bien exporter nos images en quelque chose de plus
|
|
|
|
|
connu. Bien entendu, c'est toujours affaire de compromis
|
|
|
|
|
entre précision de valeurs et taille des fichiers.
|
|
|
|
|
|
|
|
|
|
Et dans le sens inverse, il serait bien de savoir importer
|
|
|
|
|
le monde extérieur dans nos sombres caves à pixel.
|
|
|
|
|
Il faut quand même reconnaitre que c'est un peu la jungle dans les
|
|
|
|
|
formats de fichiers d'image, ce qui explique le retard
|
|
|
|
|
dans ce domaine\dots
|
|
|
|
|
|
|
|
|
|
\subsubsection{Vers PNM}\index{PNM}
|
|
|
|
|
|
|
|
|
|
Nous avons ici 16 bits par composante, soit 65536 valeurs différentes,
|
|
|
|
|
ce qui est bien au-delà de ce que peuvent percevoir vos yeux.
|
|
|
|
|
Hélas, c'est au prix
|
|
|
|
|
d'une taille énorme sur les fichiers. D'un autre coté,
|
|
|
|
|
l'utilisation du codage \textsc{ascii}\index{ascii}
|
|
|
|
|
(alors qu'on pourrait mettre du binaire, plus compact) y est
|
|
|
|
|
pour quelque chose.
|
|
|
|
|
|
|
|
|
|
\begin{lstlisting}
|
|
|
|
|
int fimg_save_as_pnm(FloatImg *head, char *fname, int flags);
|
|
|
|
|
\end{lstlisting}
|
|
|
|
|
|
|
|
|
|
Le bit \texttt{0} du paramètre \texttt{flags} mis à \texttt{1} demande
|
|
|
|
|
à la fonction de faire la mise à l'échelle avec le couple
|
|
|
|
|
\textsl{fvalue/count} décrit plus haut dans cette doc.
|
|
|
|
|
Et si il est à zéro, c'est la fonction de recherche de valeur
|
|
|
|
|
maximale (cf page \pageref{contraste}) qui est utilisée.
|
|
|
|
|
|
|
|
|
|
Le bit \texttt{1} permettra bientôt\index{vaporware} de demander
|
|
|
|
|
l'enregistrement de métadonnées\index{metadata} pertinentes, telle
|
|
|
|
|
que l'epochtime de l'enregistrement.
|
|
|
|
|
|
|
|
|
|
Les autres bits ne sont pas utilisés et doivent être à zéro.
|
|
|
|
|
|
|
|
|
|
\subsubsection{Vers PNG}\index{PNG}
|
|
|
|
|
|
|
|
|
|
Actuellement, on peut enregistrer uniquement en mode RGB, 8 bits par composante,
|
|
|
|
|
mais on a quand même une bonne compression, ça compense.
|
|
|
|
|
J'utilise \textsl{libpnglite} avec qui j'ai un peu de mal à suivre.
|
|
|
|
|
Mais je me soigne. Le mode 16 bits va bientôt arriver.
|
|
|
|
|
On peut aussi songer à l'export de metadatas.
|
|
|
|
|
|
|
|
|
|
\begin{lstlisting}
|
|
|
|
|
int fimg_save_as_png(FloatImg *src, char *outname, int flags);
|
|
|
|
|
\end{lstlisting}
|
|
|
|
|
|
|
|
|
|
Tous les flags doivent être à zéro. Sinon, ça foire parfois.
|
|
|
|
|
|
|
|
|
|
\subsubsection{Vers/depuis TIFF}\index{TIFF}
|
|
|
|
|
|
|
|
|
|
Le format canonique de la PAO\index{PAO} du siècle dernier. Il permet
|
|
|
|
|
de gérer une foultitude de formats numériques. C'est aussi un format
|
|
|
|
|
classique proposé par les gros scanners corporates.
|
|
|
|
|
|
|
|
|
|
To be done\index{XXX}
|
|
|
|
|
|
|
|
|
|
\subsubsection{Vers FITS}\index{FITS}
|
|
|
|
|
|
|
|
|
|
Ce format est essentiellement utilisé pour stocker des images
|
|
|
|
|
d'astronomie.
|
|
|
|
|
|
|
|
|
|
To be done\index{XXX}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
\subsection{Utilitaires}
|
|
|
|
|
|
|
|
|
|
Commençons par quelques petits trucs pour nous faciliter la vie
|
|
|
|
|
dans des domaines annexes,
|
|
|
|
|
tels que l'interprétation d'arguments dans la ligne de commande ou un
|
|
|
|
|
fichier de configuration.
|
|
|
|
|
|
|
|
|
|
\begin{lstlisting}
|
|
|
|
|
int parse_WxH(char *str, int *pw, int *ph)
|
|
|
|
|
int parse_double(char *str, double *dptr)
|
|
|
|
|
\end{lstlisting}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
La fonction \texttt{int format\_from\_extension(char *fname)} examine un
|
|
|
|
|
nom defichier tel que \texttt{lena.xxx}, et retourne, si la partie
|
|
|
|
|
\texttt{xxx} un éventuel nombre positif, dont les valeurs sont dans floatimg.h
|
|
|
|
|
le valeureux.
|
|
|
|
|
Les extensions actuellement connues sont : fimg, png, pnm et tiff.
|
|
|
|
|
|
|
|
|
|
To be continued\index{XXX}\dots
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
\subsection{Effets}\index{sfx}
|
|
|
|
|
|
|
|
|
|
Quelques routines qui servent futilement à \textsl{brotcher} les images.
|
|
|
|
|
|
|
|
|
|
\begin{lstlisting}
|
|
|
|
|
int fimg_killcolors_a(FloatImg *fimg, float fval);
|
|
|
|
|
int fimg_killcolors_b(FloatImg *fimg, float fval);
|
|
|
|
|
\end{lstlisting}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
% ----------------------------------
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
\subsection{Filtrages}\index{filtrage}
|
|
|
|
|
|
|
|
|
|
Pour commencer, il faut que je réfléchisse au traitement
|
|
|
|
|
des bordures des images.
|
|
|
|
|
Ensuite que je débuggue\index{bug} ces deux fonctions~:
|
|
|
|
|
|
|
|
|
|
\begin{lstlisting}
|
|
|
|
|
int fimg_lissage_2x2(FloatImg *img);
|
|
|
|
|
int fimg_killborders(FloatImg *img);
|
|
|
|
|
\end{lstlisting}
|
|
|
|
|
|
|
|
|
|
Bon, oké, ça marche ? Passons à l'tape suivante.
|
|
|
|
|
La convolution avec une matrice 3x3, c'est possible.
|
|
|
|
|
Et pas trop compliqué à faire.
|
|
|
|
|
Bon, il reste le souci avec les bordures, souci qui ne peut
|
|
|
|
|
être que temporaire, mais ésotérique à fixer.
|
|
|
|
|
|
|
|
|
|
Passons maintenant aux choses sérieuses, et définissons la
|
|
|
|
|
description d'un filtre 3x3.
|
|
|
|
|
|
|
|
|
|
\begin{lstlisting}
|
|
|
|
|
typedef struct {
|
|
|
|
|
float matrix[9];
|
|
|
|
|
float mult;
|
|
|
|
|
float offset;
|
|
|
|
|
} FimgFilter3x3;
|
|
|
|
|
\end{lstlisting}
|
|
|
|
|
|
|
|
|
|
L'usage des champs \texttt{mult} et \texttt{offset} n'est pas
|
|
|
|
|
clairement défini. Le prototype de la fonction de filtrage
|
|
|
|
|
non plus, mais assez simpe quand même. Source et destination
|
|
|
|
|
ne peuvent désigner la même image, et le champ \texttt{matrix}
|
|
|
|
|
du filtre doit contenir des valeurs cohérentes.
|
|
|
|
|
|
|
|
|
|
\begin{lstlisting}
|
|
|
|
|
int fimg_filter_3x3(FloatImg *src, FloatImg *dst, FimgFilter3x3 *filtr)
|
|
|
|
|
\end{lstlisting}
|
|
|
|
|
|
|
|
|
|
Comme dans la plupart des cas, la gestion des valeurs négatives
|
|
|
|
|
de pixel est laissé au hasard. Quoique, il doit bien exister
|
|
|
|
|
quelques solutions de contournement : clamping ou shift ?
|
|
|
|
|
|
|
|
|
|
\textsl{To be continued\index{XXX}\dots}
|
|
|
|
|
|
|
|
|
|
% ----------------------------------
|
|
|
|
|
|
|
|
|
|
\subsection{Exemple de fonction}\index{exemple}\label{exemplefunc}
|
|
|
|
|
|
|
|
|
|
Nous allons maintenant écrire une fonction intégrable dans
|
|
|
|
|
le répertoire \texttt{funcs/}.
|
|
|
|
|
Elle aura donc accès aux \textsl{internals}%
|
|
|
|
|
\footnote{que je peux décider de changer n'importe quand}
|
|
|
|
|
de \textsc{FloatImg},
|
|
|
|
|
une chose qui est en principe interdit aux programmes
|
|
|
|
|
\textsl{enduser}. Soyez prudents.
|
|
|
|
|
|
|
|
|
|
Cette fonction va faire quelque chose
|
|
|
|
|
à partir d'une image source et d'une valeur, et écrire le
|
|
|
|
|
résultat dans une image de destination.
|
|
|
|
|
Pour simplifier les choses, nous n'allons traiter que les
|
|
|
|
|
images de type \textsc{FIMG\_TYPE\_RGB}, de loin le plus
|
|
|
|
|
répandu par les temps qui courent.
|
|
|
|
|
|
|
|
|
|
\begin{lstlisting}
|
|
|
|
|
int fimg_example(FloatImg *s, FloatImg *d, float value)
|
|
|
|
|
{
|
|
|
|
|
int size, index;
|
|
|
|
|
|
|
|
|
|
if ( s->type!=FIMG_TYPE_RGB || d->type!=FIMG_TYPE_RGB) {
|
|
|
|
|
perror("fimg_example");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
size = s->width * s->height;
|
|
|
|
|
for (idx=0; idx<size; idx++) {
|
|
|
|
|
d->R[idx] = s->R[idx] - value;
|
|
|
|
|
d->G[idx] = s->G[idx] - value;
|
|
|
|
|
d->B[idx] = s->B[idx] - value;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|