<?xml version="1.0" encoding="UTF-8" ?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:atom="http://www.w3.org/2005/Atom">
<channel>
	<title>/dev/log</title>
	<link>https://benjaminbillet.fr/blog/</link>
	<language>fr</language>
	<description>Blog technique de Benjamin Billet</description>
<atom:link xmlns:atom="http://www.w3.org/2005/Atom" rel="self" type="application/rss+xml" href="https://benjaminbillet.fr/blog/feed.php?rss" />
	<lastBuildDate>Wed, 03 Jun 2015 17:12:00 +0200</lastBuildDate>
	<generator>PluXml</generator>
	<item>
		<title>Ajouter le support d&#039;ImageMagick à CImg</title> 
		<link>https://benjaminbillet.fr/blog/index.php?article5/ajouter-le-support-d-imagemagick-a-cimg</link>
		<guid>https://benjaminbillet.fr/blog/index.php?article5/ajouter-le-support-d-imagemagick-a-cimg</guid>
		<description>Pour lire des fichiers gif, CImg peut utiliser Image Magick (libMagick), à condition de définir la constante &lt;em&gt;cimg_use_magick&lt;/em&gt;.
En ce qui concerne la configuration de g++, la commande &lt;em&gt;Magick++-config&lt;/em&gt; permet de générer les paramètres automatiquement, tout comme &lt;em&gt;wx-config&lt;/em&gt; pour wxWidgets :&lt;/p&gt;

&lt;pre&gt;
$&gt; Magick++-config --cxxflags
-fopenmp -DMAGICKCORE_HDRI_ENABLE=0 -DMAGICKCORE_QUANTUM_DEPTH=16 -I/usr/include/ImageMagick-6 
&lt;/pre&gt;
&lt;ins&gt;Remarque&lt;/ins&gt; : ne pas oublier d&#039;installer le paquet &lt;em&gt;pkg-config&lt;/em&gt; pour que &lt;em&gt;Magick++-config&lt;/em&gt; puisse fonctionner.

&lt;p&gt;La configuration d&#039;Eclipse se fait de manière analogue à &lt;em&gt;wx-config&lt;/em&gt; (se référer à &lt;a href=&quot;https://benjaminbillet.fr/blog/index.php?article3/compiler-wxwidget-avec-cygwin&quot;&gt;la note sur le sujet&lt;/a&gt;) :&lt;p&gt;

&lt;p&gt;&lt;strong&gt;C++ Compiler&lt;/strong&gt; &amp;emsp; Ajouter &lt;em&gt;`/bin/Magick++-config --cxxflags`&lt;/em&gt; à la commande g++ (adapter le chemin si nécessaire).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;C++ Linker&lt;/strong&gt; &amp;emsp; Changer le &lt;em&gt;Command Line Pattern&lt;/em&gt; en déplaçant le bloc FLAGS à la fin puis ajouter &lt;em&gt;`/bin/Magick++-config --ldflags`&lt;/em&gt; dans le champ &lt;em&gt;Linker flags&lt;/em&gt; de &lt;em&gt;Miscellaneous&lt;/em&gt;&lt;/p&gt;</description>
		<pubDate>Wed, 03 Jun 2015 17:12:00 +0200</pubDate>
		<dc:creator>Benjamin Billet</dc:creator>
	</item>
	<item>
		<title>Résoudre le conflit entre wxMSW (wxWidget) et libjpeg</title> 
		<link>https://benjaminbillet.fr/blog/index.php?article4/resoudre-le-conflit-entre-wxwidget-et-libjpeg</link>
		<guid>https://benjaminbillet.fr/blog/index.php?article4/resoudre-le-conflit-entre-wxwidget-et-libjpeg</guid>
		<description>&lt;p&gt;CImg et wxWidget entrent en conflit car CImg importe les entêtes de libjpeg et wxMSW importe les entêtes de l&#039;API WIN32 qui, toutes deux, définissent le type &lt;em&gt;boolean&lt;/em&gt; :&lt;/p&gt;
&lt;pre&gt;
/usr/include/jmorecfg.h:234:13: error: conflicting declaration &#039;typedef int boolean&#039;
 typedef int boolean;
             ^
...
/usr/include/w32api/rpcndr.h:65:25: note: previous declaration as &#039;typedef unsigned char boolean&#039;
   typedef unsigned char boolean;
                         ^
&lt;/pre&gt;

&lt;p&gt;libjpeg prévoit cette éventualité et permet d&#039;éviter la déclaration de &lt;em&gt;typedef int boolean&lt;/em&gt; en définissant la constante &lt;em&gt;HAVE_BOOLEAN&lt;/em&gt;. Toutefois, si cette constante est définie, libjpeg ne fonctionne plus correctement, provoquant une erreur dans CImg :&lt;/p&gt;
&lt;pre&gt;
[CImg] *** CImgIOException *** [instance(0,0,0,0,0x0,non-shared)] CImg&lt;unsigned char&gt;::load(): Failed to recognize format of file &#039;images\IMG_6225.jpg&#039;.
&lt;/pre&gt;

&lt;p&gt;Je n&#039;ai, pour le moment, pas trouvé de solution satisfaisante à ce problème, hormis modifier le fichier &lt;em&gt;rpcndr.h&lt;/em&gt; de façon à changer la déclaration du type boolean :&lt;/p&gt;
[C99]
typedef unsigned char boolean;
// devient
typedef int boolean;
[/C99]

&lt;p&gt;Ce changement entraîne un problème dans &lt;em&gt;wtypesbase.h&lt;/em&gt;, qui doit lui aussi être modifié pour commenter la déclaration de &lt;em&gt;BOOLEAN&lt;/em&gt; : &lt;/p&gt;
[C99]
// la ligne doit être commentée
typedef boolean BOOLEAN;
[/C99]

Si vous connaissez quelque chose de mieux, merci de m&#039;envoyer un mail et je l&#039;ajouterais ici.</description>
		<pubDate>Tue, 02 Jun 2015 22:09:00 +0200</pubDate>
		<dc:creator>Benjamin Billet</dc:creator>
	</item>
	<item>
		<title>wxWidget : résoudre l&#039;erreur de compilation &quot;undefined reference to `_IID_IPersistFile&#039;&quot;, &quot;undefined reference to `glFrustum&#039;&quot;, etc.</title> 
		<link>https://benjaminbillet.fr/blog/index.php?article6/resoudre-l-erreur-de-compilation-emsrc-common-filename-cpp-1602-undefined-reference-to-iid-ipersistfile-em</link>
		<guid>https://benjaminbillet.fr/blog/index.php?article6/resoudre-l-erreur-de-compilation-emsrc-common-filename-cpp-1602-undefined-reference-to-iid-ipersistfile-em</guid>
		<description>Lors de la compilation de wxWidgets (ici 3.0.2), il peut arriver que la compilation échoue sur l&#039;erreur suivante :
&lt;pre style=&quot;white-space: pre-wrap;&quot;&gt;
g++ -shared -o /home/win7/wxWidgets-3.0.2/lib/cygwxbase30u_gcc_custom-0.dll basedll_version_rc.o basedll_any.o basedll_appbase.o basedll_arcall.o basedll_arcfind.o basedll_archive.o basedll_arrstr.o basedll_base64.o basedll_clntdata.o basedll_cmdline.o basedll_config.o basedll_convauto.o basedll_datetime.o basedll_datetimefmt.o basedll_datstrm.o basedll_dircmn.o basedll_dynarray.o basedll_dynlib.o basedll_dynload.o basedll_encconv.o basedll_evtloopcmn.o basedll_extended.o basedll_ffile.o basedll_file.o basedll_fileback.o basedll_fileconf.o basedll_filefn.o basedll_filename.o basedll_filesys.o basedll_filtall.o basedll_filtfind.o basedll_fmapbase.o basedll_fs_arc.o basedll_fs_filter.o basedll_hash.o basedll_hashmap.o basedll_init.o basedll_intl.o basedll_ipcbase.o basedll_languageinfo.o basedll_list.o basedll_log.o basedll_longlong.o basedll_memory.o basedll_mimecmn.o basedll_module.o basedll_mstream.o basedll_numformatter.o basedll_object.o basedll_platinfo.o basedll_powercmn.o basedll_process.o basedll_regex.o basedll_stdpbase.o basedll_sstream.o basedll_stdstream.o basedll_stopwatch.o basedll_strconv.o basedll_stream.o basedll_string.o basedll_stringimpl.o basedll_stringops.o basedll_strvararg.o basedll_sysopt.o basedll_tarstrm.o basedll_textbuf.o basedll_textfile.o basedll_threadinfo.o basedll_common_time.o basedll_timercmn.o basedll_timerimpl.o basedll_tokenzr.o basedll_translation.o basedll_txtstrm.o basedll_unichar.o basedll_uri.o basedll_ustring.o basedll_variant.o basedll_wfstream.o basedll_wxcrt.o basedll_wxprintf.o basedll_xlocale.o basedll_xti.o basedll_xtistrm.o basedll_zipstrm.o basedll_zstream.o basedll_fswatchercmn.o basedll_fswatcherg.o basedll_basemsw.o basedll_crashrpt.o basedll_debughlp.o basedll_dde.o basedll_msw_dir.o basedll_dlmsw.o basedll_evtloopconsole.o basedll_msw_mimetype.o basedll_power.o basedll_regconf.o basedll_registry.o basedll_msw_snglinst.o basedll_msw_stackwalk.o basedll_msw_stdpaths.o basedll_msw_thread.o basedll_timer.o basedll_msw_utils.o basedll_msw_utilsexc.o basedll_fswatcher.o  basedll_event.o basedll_fs_mem.o basedll_msgout.o basedll_utilscmn.o basedll_main.o basedll_mslu.o basedll_volume.o     -L/home/win7/wxWidgets-3.0.2/lib  -Wl,--out-implib=/home/win7/wxWidgets-3.0.2/lib/libwx_baseu-3.0.dll.a      -lwxregexu-3.0  -lz -lrpcrt4 -loleaut32 -lole32 -luuid -lwinspool -lwinmm -lshell32 -lcomctl32 -lcomdlg32 -ladvapi32 -lwsock32 -lgdi32 -lkernel32 -luser32  -lz -lrpcrt4 -loleaut32 -lole32 -luuid -lwinspool -lwinmm -lshell32 -lcomctl32 -lcomdlg32 -ladvapi32 -lwsock32 -lgdi32 -lkernel32 -luser32
basedll_filename.o:filename.cpp:(.text+0x511c): undefined reference to `IID_IShellLinkW&#039;
basedll_filename.o:filename.cpp:(.text+0x5162): undefined reference to `IID_IPersistFile&#039;
collect2: error: ld returned 1 exit status
Makefile:15531: recipe for target &#039;/home/win7/wxWidgets-3.0.2/lib/cygwxbase30u_gcc_custom-0.dll&#039; failed
make: *** [/home/win7/wxWidgets-3.0.2/lib/cygwxbase30u_gcc_custom-0.dll] Error 1
&lt;/pre&gt;

&lt;p&gt;Cette erreur se produit lorsque &lt;em&gt;libuuid-devel&lt;/em&gt; est installé, conduisant l&#039;option &lt;em&gt;-luuid&lt;/em&gt; à lier &lt;em&gt;/usr/lib/libuuid.a&lt;/em&gt; au lieu de &lt;em&gt;/usr/lib/w32api/libuuid.a&lt;/em&gt;. Pour résoudre ce problème, il est nécessaire d&#039;éditer le fichier Makefile et d&#039;ajouter &lt;em&gt;-L /usr/lib/w32api&lt;/em&gt; à la variable &lt;em&gt;LDFLAGS&lt;/em&gt;. Ainsi, ayant connaissance du répertoire approprié, g++ va lier la bonne librairie.&lt;/p&gt;

&lt;p&gt;Une fois ce problème résolu, une autre erreur survient un peu plus tard, indiquant notamment que des éléments d&#039;OpenGL sont manquants :&lt;/p&gt;
&lt;pre style=&quot;white-space: pre-wrap;&quot;&gt;
g++ -shared -o /home/win7/wxWidgets-3.0.2/lib/cygwxmsw30u_gl_gcc_custom-0.dll gldll_version_rc.o gldll_glcmn.o gldll_msw_glcanvas.o    -L/home/win7/wxWidgets-3.0.2/lib  -Wl,--out-implib=/home/win7/wxWidgets-3.0.2/lib/libwx_mswu_gl-3.0.dll.a   -L /usr/lib/w32api  -lwxtiff-3.0 -lwxjpeg-3.0 -lwxpng-3.0    -lwxregexu-3.0 -lwxexpat-3.0 -lz -lrpcrt4 -loleaut32 -lole32 -luuid -lwinspool -lwinmm -lshell32 -lcomctl32 -lcomdlg32 -ladvapi32 -lwsock32 -lgdi32 -lkernel32 -luser32  -lwx_mswu_core-3.0 -lwx_baseu-3.0  -lopengl32 -lglu32 -lz -lrpcrt4 -loleaut32 -lole32 -luuid -lwinspool -lwinmm -lshell32 -lcomctl32 -lcomdlg32 -ladvapi32 -lwsock32 -lgdi32 -lkernel32 -luser32
gldll_glcmn.o:glcmn.cpp:(.text+0x363): undefined reference to `glFrustum&#039;
gldll_glcmn.o:glcmn.cpp:(.text+0x428): undefined reference to `glGetBooleanv&#039;
gldll_glcmn.o:glcmn.cpp:(.text+0x91a): undefined reference to `glColor3f&#039;
gldll_glcmn.o:glcmn.cpp:(.text+0xae1): undefined reference to `glIndexi&#039;
gldll_glcmn.o:glcmn.cpp:(.text+0x371): undefined reference to `glBegin&#039;
gldll_glcmn.o:glcmn.cpp:(.text+0x381): undefined reference to `glTexCoord2f&#039;
gldll_glcmn.o:glcmn.cpp:(.text+0x391): undefined reference to `glVertex3f&#039;
gldll_glcmn.o:glcmn.cpp:(.text+0x3a1): undefined reference to `glNormal3f&#039;
gldll_glcmn.o:glcmn.cpp:(.text+0x3b1): undefined reference to `glColor4f&#039;
gldll_glcmn.o:glcmn.cpp:(.text+0x3c1): undefined reference to `glColor3f&#039;
gldll_glcmn.o:glcmn.cpp:(.text+0x3d1): undefined reference to `glEnd&#039;
collect2: error: ld returned 1 exit status
Makefile:16086: recipe for target &#039;/home/win7/wxWidgets-3.0.2/lib/cygwxmsw30u_gl_gcc_custom-0.dll&#039; failed
make: *** [/home/win7/wxWidgets-3.0.2/lib/cygwxmsw30u_gl_gcc_custom-0.dll] Error 1
&lt;/pre&gt;

&lt;p&gt;Il semble que le lien avec OpenGL ne soit pas fait et doive être ajouté. De la même façon que le problème précédent, il est nécessaire d&#039;aller modifier le fichier Makefile. Il suffit de trouver la variable &lt;em&gt;EXTRALIBS_OPENGL&lt;/em&gt; et d&#039;y ajouter &lt;em&gt;-lGl&lt;/em&gt;.</description>
		<pubDate>Thu, 28 May 2015 21:00:00 +0200</pubDate>
		<dc:creator>Benjamin Billet</dc:creator>
	</item>
	<item>
		<title>Compiler wxWidget 3.0.2 avec cygwin 2.0.0 sous windows</title> 
		<link>https://benjaminbillet.fr/blog/index.php?article3/compiler-wxwidget-avec-cygwin</link>
		<guid>https://benjaminbillet.fr/blog/index.php?article3/compiler-wxwidget-avec-cygwin</guid>
		<description>&lt;p&gt;En premier lieu, il est nécessaire d&#039;installer &lt;a href=&quot;https://cygwin.com&quot;&gt;cygwin&lt;/a&gt; (2.0.0) avec les paquets suivants : automake, autoconf, make, gcc-g++.&lt;/p&gt;

&lt;p&gt;Concrètement, &lt;a href=&quot;http://www.wxwidgets.org&quot;&gt;wxWidgets&lt;/a&gt; spécifie un ensemble d&#039;API communes, qui sont implémentées sur &lt;a href=&quot;http://docs.wxwidgets.org/3.0.2/page_port.html&quot;&gt;différentes plateformes&lt;/a&gt;.
Pour windows, nous allons compiler &lt;a href=&quot;http://docs.wxwidgets.org/3.0.2/page_port.html#page_port_wxmsw&quot;&gt;wxMSW&lt;/a&gt; mais nous pourrions aussi utiliser &lt;a href=&quot;http://docs.wxwidgets.org/3.0.2/page_port.html#page_port_wxx11&quot;&gt;wxX11&lt;/a&gt; ou &lt;a href=&quot;http://docs.wxwidgets.org/3.0.2/page_port.html#page_port_wxgtk&quot;&gt;wxGTK&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Après s&#039;être placé dans le dossier des sources de wxWidgets-3.0.2 avec la console cygwin, la configuration/compilation est classique :&lt;/p&gt;
&lt;pre&gt;
$&gt; ./configure --with-msw --enable-unicode
$&gt; make
$&gt; make install
&lt;/pre&gt;
&lt;p&gt;Par défaut la compilation construit des librairies dynamiques, les librairies statiques pouvant être générées avec l&#039;option &lt;em&gt;--disable-shared&lt;/em&gt;. De nombreuses options sont disponibles pour le ./configure, la liste complète pouvant être obtenue avec :&lt;/p&gt;
&lt;pre&gt;
$&gt; ./configure --help
&lt;/pre&gt;
&lt;p&gt;A noter que wxWidgets est directement fourni avec le code source de ses dépendances, la configuration permettant de préciser s&#039;il est préférable d&#039;utiliser les versions fournies (p. ex. &lt;em&gt;--with-jpeg=builtin&lt;/em&gt;) ou les librairies éventuellement installées sur le système (p. ex. &lt;em&gt;--with-jpeg=sys&lt;/em&gt;).&lt;/p&gt;

&lt;p&gt;Une fois l&#039;installation terminée, la commande suivante devrait être disponible :&lt;/p&gt;
&lt;pre&gt;
$&gt; wx-config --version
3.0.2
&lt;/pre&gt;

&lt;p&gt;Pour un test plus poussé, il est possible de compiler les exemples fournis dans le dossier &quot;samples&quot; :&lt;/p&gt;
&lt;pre&gt;
$&gt; cd samples/treelist
$&gt; make
&lt;/pre&gt;

&lt;p&gt;Après avoir intégré les dll de wxWidgets, disponibles dans &lt;em&gt;wxWidgets-3.0.2/lib&lt;/em&gt;, et celles de cygwin (&lt;em&gt;/bin&lt;/em&gt;), l&#039;exécutable treelist.exe devrait s&#039;afficher ainsi :&lt;/p&gt;

&lt;a href=&quot;https://benjaminbillet.fr/blog/data/images/wxwidget-treelist-screenshot.png&quot;&gt;&lt;img src=&quot;https://benjaminbillet.fr/blog/data/images/wxwidget-treelist-screenshot.png&quot; alt=&quot;treelist-screen&quot; /&gt;&lt;/a&gt;

&lt;h2&gt;Environnement&lt;/h2&gt;
&lt;p&gt;J&#039;utilise Eclipse IDE for C/C++ pour gérer le projet mais, globalement, les informations suivantes s&#039;appliquent à tout projet basé sur wxWidget. Le type de projet C/C++ crée est un &lt;em&gt;Makefile project&lt;/em&gt; vide. Pour assurer que toutes les options de configurations soient disponibles (constantes, flags, includes, libs, etc.), il faut en outre activer la génération automatique du makefile (options du projet -&gt; C/C++ Build).&lt;br/&gt;
&lt;ins&gt;Remarque&lt;/ins&gt; : pour une détection de Cygwin par Eclipse, il suffit d&#039;ajouter simplement le dossier &lt;em&gt;chemin/vers/cygwin/bin&lt;/em&gt; au PATH de windows.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;C++ Compiler&lt;/strong&gt; &amp;emsp; Concrètement, la commande &lt;em&gt;wx-config&lt;/em&gt; génère les paramètres cohérents pour g++ :&lt;/p&gt;
&lt;pre&gt;
$&gt; wx-config --static=no --cxxflags --linkdeps
-I/usr/local/lib/wx/include/msw-unicode-3.0 -I/usr/local/include/wx-3.0 -D_FILE_OFFSET_BITS=64 -DWXUSINGDLL -D__WXMSW__ 
&lt;/pre&gt;
&lt;p&gt;Aussi, la configuration d&#039;Eclipse se fait simplement en ajoutant &lt;em&gt;`/usr/local/bin/wx-config --static=no --cxxflags --linkdeps`&lt;/em&gt; à la commande g++ (adapter le chemin si nécessaire). &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;C++ Linker&lt;/strong&gt; &amp;emsp; Ici, il est nécessaire de changer le &lt;em&gt;Command Line Pattern&lt;/em&gt; en déplaçant le bloc FLAGS à la fin :
&lt;pre&gt;
${COMMAND} ${FLAGS} ${OUTPUT_FLAG}${OUTPUT_PREFIX}${OUTPUT} ${INPUTS}
devient
${COMMAND} ${OUTPUT_FLAG}${OUTPUT_PREFIX}${OUTPUT} ${INPUTS} ${FLAGS}
&lt;/pre&gt;
En effet, nous utilisons les flags pour qu&#039;Eclipse construise le makefile avec la commande &lt;em&gt;wx-config&lt;/em&gt; à la fin de la commande g++. Pour ce faire, dans &lt;em&gt;Miscellaneous&lt;/em&gt;, nous spécifions &lt;em&gt;`/usr/local/bin/wx-config --libs --static=no`&lt;/em&gt; dans le champ &lt;em&gt;Linker flags&lt;/em&gt;. En outre les paramètres suivants peuvent être ajoutés à ce champ : &lt;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;-mwindows&lt;/em&gt; : ne pas afficher de console à l&#039;ouverture de l&#039;application&lt;/li&gt;
&lt;li&gt;&lt;em&gt;-Wl,--enable-auto-import&lt;/em&gt; n&#039;est pas toujours nécessaire car activé par défaut dans cygwin : &lt;a href=&quot;https://sourceware.org/binutils/docs-2.17/ld/WIN32.html&quot;&gt;https://sourceware.org/binutils/docs-2.17/ld/WIN32.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;Utiliser &lt;em&gt;windres&lt;/em&gt; pour avoir un rendu visuel conforme à Window&lt;/h2&gt;
&lt;p&gt;Si l&#039;on crée un nouveau projet vierge à partir des fichiers &lt;em&gt;treelist.cpp&lt;/em&gt; et &lt;em&gt;treelist.h&lt;/em&gt; fourni dans l&#039;exemple &lt;/em&gt;samples/treelist&lt;/em&gt; et que l&#039;on compile, l&#039;application s&#039;affiche comme suit :&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://benjaminbillet.fr/blog/data/images/wxwidget-treelist-screenshot2.png&quot;&gt;&lt;img src=&quot;https://benjaminbillet.fr/blog/data/images/wxwidget-treelist-screenshot2.png&quot; alt=&quot;treelist-screen&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Contrairement à la première capture, les composants graphiques s&#039;affichent ici comme si l&#039;application était exécutée sous Windows 95. Pour résoudre ce problème, il est nécessaire de créer un fichier de ressource (voir &lt;a href=&quot;http://fr.wikipedia.org/wiki/Ressources_%28Windows%29&quot;&gt;Ressource Windows&lt;/a&gt;), de le &quot;compiler&quot; avec &lt;a href=&quot;https://sourceware.org/binutils/docs/binutils/windres.html&quot;&gt;windres&lt;/a&gt; (fourni avec wxWidgets) et de linker la ressource compilée à notre projet.&lt;/p&gt;

&lt;p&gt;wxWidgets fournit un fichier de ressource basique, &lt;em&gt;samples/sample.rc&lt;/em&gt; :&lt;/p&gt;
[C99]
/////////////////////////////////////////////////////////////////////////////
// Name:        samples/samples.rc
// Purpose:     a standard Win32 .rc file for the wxWindows samples
// Author:      Vadim Zeitlin
// Modified by:
// Created:     04.08.03
// Copyright:   (c) 2003 Vadim Zeitlin &lt;vadim@wxwindows.org&gt;
// Licence:     wxWindows licence
/////////////////////////////////////////////////////////////////////////////

// this minimal resource file is all what is needed for most of the wxWindows
// samples

// note that the icon used by the Explorer (i.e. the programs icon) is the
// first icon in the executable and the icons are sorted both by their order
// (Win9x) and by alphabetically (!) (NT), so put this icon first and give it
// a name starting with &quot;a&quot;
aaaaaaaa ICON &quot;sample.ico&quot;

// this icon is used with wxFrame::SetIcon()
sample ICON &quot;sample.ico&quot;

// set this to 1 if you don&#039;t want to use manifest resource (manifest resource
// is needed to enable visual styles on Windows XP - see docs/msw/winxp.txt
// for more information)
#define wxUSE_NO_MANIFEST 0

// this is not always needed but doesn&#039;t hurt (except making the executable
// very slightly larger): this file contains the standard icons, cursors, ...
#include &quot;wx/msw/wx.rc&quot;
[/C99]

&lt;p&gt;Ce fichier doit être ajouté aux sources du projet Eclipse et sa compilation par windres est spécifiée dans les &lt;em&gt;Pre Build Steps&lt;/em&gt; du projet. Il est nécessaire de préciser explicitement le chemin vers les headers de wxWidgets ainsi que le chemin vers le fichier ressource (ici, my_resource_file.rc) : &lt;em&gt;windres -i&quot;../src/my_resource_file.rc&quot; -omy_resource_file_rc.o -I&quot;/usr/local/include/wx-3.0&quot;&lt;/em&gt;.
Enfin, dans la configuration du &lt;em&gt;C++ Linker&lt;/em&gt;, nous ajoutons &lt;em&gt;my_resource_file_rc.o&lt;/em&gt; à la liste &lt;em&gt;Other Objects&lt;/em&gt; de &lt;em&gt;Miscellaneous&lt;/em&gt;.&lt;/p&gt;</description>
		<pubDate>Tue, 26 May 2015 21:01:00 +0200</pubDate>
		<dc:creator>Benjamin Billet</dc:creator>
	</item>
	<item>
		<title>Extraire l&#039;essentiel de pHash 0.9.6</title> 
		<link>https://benjaminbillet.fr/blog/index.php?article2/extraire-l-essentiel-de-phash</link>
		<guid>https://benjaminbillet.fr/blog/index.php?article2/extraire-l-essentiel-de-phash</guid>
		<description>L&#039;implémentation de pHash 0.9.6 fournit différentes fonctionnalités :
&lt;ul&gt;
&lt;li&gt;image hash (requiert CImg 1.3+)&lt;/li&gt;
&lt;li&gt;video hash (requiert CImg 1.3+ et FFmpeg)&lt;/li&gt;
&lt;li&gt;audio hash (requiert libsndfile, libsamplerate, libmpg123)&lt;/li&gt;
&lt;/ul&gt;

Mon problème de recherche d&#039;images similaires ne requiert que la partie image de la librairie pHash, c&#039;est-à-dire une centaine de ligne sur les 2000+ du projet. Concrètement, seules trois fonctions sont nécessaires et peuvent directement être extraites de pHash.cpp :
[C99]
// calcul d&#039;un hash à partir d&#039;un fichier
int ph_dct_imagehash(const char* file,ulong64 &amp;hash);
// calcul de la distance entre deux hash
int ph_hamming_distance(const ulong64 hash1,const ulong64 hash2); 
// utilisé par ph_dct_imagehash
CImg&lt;float&gt;* ph_dct_matrix(const int N);
[/C99]

Qui plus est, le bloc permettant d&#039;assurer l&#039;existence du type ulong64 (utilisé pour stocker les hash) doit être récupéré dans pHash.h :
[C99]
#if defined( _MSC_VER) || defined(_BORLANDC_)
typedef unsigned _uint64 ulong64;
typedef signed _int64 long64;
#else
typedef unsigned long long ulong64;
typedef signed long long long64;
#endif
[/C99]

&lt;ins&gt;Remarque&lt;/ins&gt; : pour afficher un ulong64 avec un printf, %llu doit être utilisé.

&lt;h2&gt;Environnement&lt;/h2&gt;
&lt;a href=&quot;http://cimg.sourceforge.net&quot;&gt;CImg&lt;/a&gt; se présente sous la forme d&#039;un unique fichier CImg.h qui doit être intégré statiquement au projet. Il est nécessaire de définir les constantes suivante, au niveau du Makefile, du projet Eclipse ou directement dans le code :
&lt;ul&gt;
&lt;li&gt;Selon les formats d&#039;image désirés, il sera nécessaire d&#039;installer les librairies correspondantes : dans mon cas, libpng, libjpeg et libtiff seront utilisées (installer les versions *-devel). Pour que CImg puisse exploiter ces librairies, les constantes &lt;em&gt;cimg_use_png&lt;/em&gt;, &lt;em&gt;cimg_use_jpeg&lt;/em&gt; et &lt;em&gt;cimg_use_tiff&lt;/em&gt; doivent être définies.&lt;/li&gt;
&lt;li&gt;Les constantes &lt;em&gt;WIN32&lt;/em&gt;, &lt;em&gt;WIN64&lt;/em&gt; ou &lt;em&gt;unix&lt;/em&gt; doivent être définies en fonction du système d&#039;exploitation pour assurer que CImg charge les bons entêtes. Ici, nous opterons plutôt pour &lt;em&gt;__CYGWIN__&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;Comme nous allons uniquement utiliser les fonctions de lecture et de traitement d&#039;images de CImg, nous pouvons définir &lt;em&gt;cimg_display = 0&lt;/em&gt; pour désactiver les fonctionnalités de création/manipulation de fenêtres.&lt;/li&gt;
&lt;/ul&gt;

[C99]
#define cimg_display 0
#define cimg_use_png
#define cimg_use_jpeg
#define cimg_use_tiff
#define __CYGWIN__
[/C99]

&lt;h2&gt;Crash de pHash avec les PNG transparent&lt;/h2&gt;
Lors de mes premiers tests, pHash crashait systématiquement sur les fichiers PNG avec transparence. Après lecture du code, il apparaît qu&#039;il s&#039;agit d&#039;un bug dans &lt;em&gt;ph_dct_imagehash&lt;/em&gt;. Le crash est simplement du au fait que les variables &lt;em&gt;width&lt;/em&gt;, &lt;em&gt;height&lt;/em&gt; et &lt;em&gt;depth&lt;/em&gt; du test &lt;em&gt;src.spectrum() == 4&lt;/em&gt; sont assignées à partir de l&#039;image vide &lt;em&gt;img&lt;/em&gt; au lieu de l&#039;image à hasher &lt;em&gt;src&lt;/em&gt;. 

[C99]
int ph_dct_imagehash(const char* file,ulong64 &amp;hash) {
    ...
    CImg&lt;float&gt; img; // img est vide
    if (src.spectrum() == 3) {
        img = src.RGBtoYCbCr().channel(0).get_convolve(meanfilter);
    } else if (src.spectrum() == 4) {
        int width = img.width(); // width = 0
        int height = img.height(); // height = 0
        int depth = img.depth(); // depth = 0
        // les trois variables étant nulles, width-1, height-1 et depth-1 équivalent à -1 =&gt; crash de CImg
        img = src.crop(0,0,0,0,width-1,height-1,depth-1,2).RGBtoYCbCr().channel(0).get_convolve(meanfilter);
    } else {
        img = src.channel(0).get_convolve(meanfilter);
    }
    ...
[/C99]

Concrètement, la modification à effectuer est la suivante :
[C99]
// remplacer ces lignes :
int width = img.width();
int height = img.height();
int depth = img.depth();
// par ces lignes :
int width = src.width();
int height = src.height();
int depth = src.depth();
[/C99]


&lt;h2&gt;Gérer les warnings de libpng et libtiff&lt;/h2&gt;
Lors de l&#039;exécution de &lt;em&gt;ph_dct_imagehash&lt;/em&gt;, des warnings peuvent apparaître dans la console :
&lt;pre&gt;
TIFFReadDirectory: Warning, Unknown field with tag 20624 (0x5090) encountered.
TIFFReadDirectory: Warning, Unknown field with tag 20625 (0x5091) encountered.
TIFFReadDirectory: Warning, Unknown field with tag 33434 (0x829a) encountered.
...
libpng warning: iCCP: known incorrect sRGB profile
libpng warning: iCCP: cHRM chunk does not match sRGB
&lt;/pre&gt;

&lt;ul&gt;
&lt;li&gt;Les warnings de libtiff sont levés dès lors qu&#039;un fichier .tif utilise des tags spécifiques à un logiciel ou à une extension du standard. La règle est d&#039;ignorer ces tags, ce que fait libtiff en levant toutefois des warnings. &lt;/li&gt;
&lt;li&gt;Les warnings de libpng sont levés, depuis la version 1.6, dès lors qu&#039;un fichier .png utilise un profil sRGB trop ancien.&lt;/li&gt;
&lt;/ul&gt;

Une méthode simple pour ne plus polluer la console avec ces warnings consiste à rediriger stderr vers un fichier :
[C99]
freopen(&quot;stderr.log&quot;, &quot;a&quot;, stderr); // a = appending, w = overwrite
[/C99]

&lt;p&gt;
Si l&#039;on souhaite aller un peu plus en profondeur et filtrer explicitement les messages, libtiff et libpng proposent tous deux de définir des fonctions qui vont capturer les warnings. Cependant, si libtiff permet de définir un handler global, libpng associe un handler à un &lt;em&gt;png_structp&lt;/em&gt;, typiquement lors de la création de la structure en lecture ou en écriture. Aussi, dans le cas de libpng, nous devrons modifier le code de CImg.
&lt;/p&gt;

&lt;strong&gt;Cas libtiff (enregistrement global en début de programme) &lt;/strong&gt;
[C99]
void my_tiff_warning_handler(const char *module, const char *fmt, va_list ap) {
    // voir http://libtiff.org/man/TIFFError.3t.html
}

int main(int argc, const char* argv[]) {
    ...
    // définir le nouveau handler
    TIFFErrorHandler oldHandler = TIFFSetWarningHandler(&amp;(my_tiff_warning_handler));
    ...
}
[/C99]

&lt;strong&gt;Cas libpng (enregistrement local dans CImg, au moment où un &lt;em&gt;png_structp&lt;/em&gt; pour la lecture est crée)&lt;/strong&gt;
[C99]
png_voidp user_error_ptr = 0;
// remplacer cette ligne :
png_error_ptr user_error_fn = 0, user_warning_fn = 0;
// par cette ligne :
png_error_ptr user_error_fn = 0, user_warning_fn = &amp;(my_png_warning_handler);
png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,user_error_ptr,user_error_fn,user_warning_fn);
[/C99]

En créant au préalable la fonction vide &lt;em&gt;my_warning_handler&lt;/em&gt;.
[C99]
void my_png_warning_handler(png_structp png_ptr, png_const_charp warning_message) {
    // voir png_error_ptr dans http://www.libpng.org/pub/png/libpng-manual.txt
}
[/C99]

&lt;h2&gt;Sources&lt;/h2&gt;
J&#039;ai intégré ce code avec une interface ligne de commande basique, voir la page du projet &lt;a href=&quot;http://benjaminbillet.fr/wiki/doku.php?id=similarity-finder&quot;&gt;similarity-finder&lt;/a&gt;.</description>
		<pubDate>Sat, 23 May 2015 19:47:00 +0200</pubDate>
		<dc:creator>Benjamin Billet</dc:creator>
	</item>
	<item>
		<title>Le problème de la détection d&#039;images similaires</title> 
		<link>https://benjaminbillet.fr/blog/index.php?article1/detection-images-similaires</link>
		<guid>https://benjaminbillet.fr/blog/index.php?article1/detection-images-similaires</guid>
		<description>&lt;p&gt;A la base de ce projet viens un besoin simple : j&#039;ai accumulé au fil du temps de nombreuses images qui sont dispersées à travers mon disque dur et je veux trouver quelles images sont en double, même avec des tailles et des résolutions différentes. Dans le cas des photos, je souhaite aussi trouver les images dont le cadrage, la colorimétrie ou l&#039;exposition sont légèrement différents.&lt;/p&gt;

&lt;p&gt;S&#039;il existe effectivement des logiciels pour trouver des doublons et des images similaires, par exemple &lt;a href=&quot;http://www.anti-twin.com/&quot;&gt;Anti-Twin&lt;/a&gt; et &lt;a href=&quot;http://www.visipics.info&quot;&gt;VisiPics&lt;/a&gt;, ils ont toutefois pour inconvénient d&#039;être plutôt lents lorsque le nombre d&#039;images à analyser devient grand. Typiquement, Anti-Twin met plusieurs heures pour analyser un dossier de 25000 images, ce qui m&#039;a conduit à m&#039;intéresser aux algorithmes de comparaison d&#039;images.&lt;/p&gt;

&lt;p&gt;Pour détecter des images identiques bit à bit, le calcul d&#039;une empreinte MD5 ou SHA1 suffit, par contre pour détecter des images légèrement différentes nous devons utiliser un algorithme de &lt;em&gt;hashage perceptuel&lt;/em&gt; : au lieu de calculer une empreinte à partir des bits qui composent l&#039;image, ces algorithmes calculent une empreinte à partir de ses caractéristiques visuelles. De fait, deux images visuellement proches auront des empreintes proches.&lt;/p&gt;


&lt;h2&gt;Quelques algorithmes de hashage perceptuel&lt;/h2&gt;

&lt;h3&gt;Histogramme des couleurs&lt;/h3&gt;
&lt;p&gt;Un histogramme des couleurs représente la distribution des couleurs dans l&#039;image, c&#039;est-à-dire le nombre de pixels dont la couleur appartient à une plage donnée, les différentes plages couvrant la totalité de l&#039;espace colorimétrique (voir cet &lt;a href=&quot;http://en.wikipedia.org/wiki/Color_histogram#Example&quot;&gt;exemple très parlant&lt;/a&gt;). L&#039;histogramme nous donne donc une représentation statistique de l&#039;image, deux images étant analysées en comparant leurs histogrammes. Cette méthode, bien que très simple à implémenter, présente toutefois le désavantage d&#039;être purement colorimétrique : en pratique, deux images aux sujets très différents peuvent avoir des histogrammes très proches.&lt;/p&gt;

&lt;h3&gt;Moyenne par bloc&lt;/h3&gt;
&lt;p&gt;Cette méthode est elle aussi assez simple et consiste à diviser l&#039;image en blocs et à calculer la moyenne colorimétrique de chaque bloc. La comparaison se fait en calculant la moyenne des différences de couleur entre les blocs de chaque image, sous réserve que les deux images aient été hashées avec le même nombre de blocs. Cette méthode détecte bien les images ayant été redimensionnée ou recompressée, mais pas les images retaillées.&lt;/p&gt;


&lt;h3&gt;&lt;a href=&quot;http://www.ostertag.name/HowTo/findimagedupes.shtml&quot;&gt;findimagedupes.pl&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Ce script applique des transformations à l&#039;image au moyen de la librairie &lt;a href=&quot;http://www.imagemagick.org&quot;&gt;Image Magick&lt;/a&gt;, de façon à extraire une empreinte. Les opérations sont les suivantes, extraites depuis le code du script :
&lt;ol&gt;
&lt;li&gt;$image-&gt;Sample(&quot;160x160!&quot;); # redimensionner en 160x160px &lt;/li&gt;
&lt;li&gt;$image-&gt;Modulate(saturation=&gt;-100); # convertir en niveaux de gris &lt;/li&gt;
&lt;li&gt;$image-&gt;Blur(radius=&gt;5); # appliquer un flou gaussien pour réduire l&#039;impact du bruit dans la comparaison&lt;/li&gt;
&lt;li&gt;$image-&gt;Normalize(); # redistribue l&#039;histogramme des gris (a pour effet d&#039;augmenter le contraste)&lt;/li&gt;
&lt;li&gt;$image-&gt;Equalize(); # égalise l&#039;histogramme des gris (idem)&lt;/li&gt;
&lt;li&gt;$image-&gt;Sample(&quot;16x16&quot;); # redimensionner en 16x16 &lt;/li&gt;
&lt;li&gt;$image-&gt;Threshold(); # réduire à un bit par pixel (bpp) : blanc ou noir&lt;/li&gt;
&lt;li&gt;$image-&gt;Set(magick=&gt;&#039;mono&#039;); # convertit au format MONO : &quot;bi-level bitmap in least-significant-byte first order&quot;&lt;/li&gt;
&lt;/ol&gt;
L&#039;empreinte de l&#039;image transformée est construite à partir des 32 premiers bits. Comparer deux images consiste à appliquer un XOR bit à bit sur les deux empreintes, le score de similarité étant donné par le nombre de bits actifs dans le résultat.&lt;/p&gt;


&lt;h3&gt;&lt;a href=&quot;http://phash.org/docs/pubs/thesis_zauner.pdf&quot;&gt;Empreinte fréquentielle&lt;/a&gt; (pHash)&lt;/h3&gt;
&lt;p&gt;Cette approche consiste à filtrer les hautes &lt;a href=&quot;http://fr.wikipedia.org/wiki/Fr%C3%A9quence_spatiale&quot;&gt;fréquences spatiales&lt;/a&gt;, qui représentent les détails, pour ne garder que les basses fréquences, qui représentent la structure basique de l&#039;image. L&#039;algorithme produit une empreinte de 64 bits et se détaille ainsi :
&lt;ol&gt;
&lt;li&gt;transformer l&#039;image en niveaux de gris.&lt;/li&gt;
&lt;li&gt;redimensionner l&#039;image en 32x32 pixels.&lt;/li&gt;
&lt;li&gt;calculer la &lt;a href=&quot;http://fr.wikipedia.org/wiki/Transform%C3%A9e_en_cosinus_discr%C3%A8te&quot;&gt;transformée en cosinus discrète (DCT)&lt;/a&gt;, qui produit une image 2D (fréquence et scalaire) en niveaux de gris.&lt;/li&gt;
&lt;li&gt;extraire les 8x8 pixels du coin supérieur gauche de l&#039;image, cette portion représentant les basses fréquences.&lt;/li&gt;
&lt;li&gt;calculer la valeur médiane de ce bloc.&lt;/li&gt;
&lt;li&gt;l&#039;empreinte est composée de 64 bits, chaque bit étant mis à 1 si le pixel correspondant a une valeur inférieure à la médiane.&lt;/li&gt;
&lt;/ol&gt;
Pour comparer deux images, la &lt;a href=&quot;http://fr.wikipedia.org/wiki/Distance_de_Hamming&quot;&gt;distance de hamming&lt;/a&gt; doit être calculée entre les deux empreintes. En pratique, d&#039;après mes expériences, deux images très différentes ont une distance supérieure à 14 et deux images fortement similaires ont une distance inférieure à 6.&lt;/p&gt;


&lt;h3&gt;&lt;a href=&quot;http://grail.cs.washington.edu/projects/query/mrquery.pdf&quot;&gt;Empreinte par ondelettes&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Cette approche exploite la &lt;a href=&quot;http://en.wikipedia.org/wiki/Discrete_wavelet_transform&quot;&gt;transformée par ondelette discrète&lt;/a&gt; basée sur l&#039;ondelette formulée par le mathématicien Alfréd Haar. Si l&#039;on se penche sur l&#039;implémentation fournie dans &lt;a href=&quot;http://www.imgseek.net&quot;&gt;ImgSeek&lt;/a&gt;, on trouve les opérations suivantes :
&lt;ol&gt;
&lt;li&gt;redimensionner en 128x128.&lt;/li&gt;
&lt;li&gt;convertir dans l&#039;espace &lt;a href=&quot;http://fr.wikipedia.org/wiki/YIQ&quot;&gt;YIQ&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;décomposer en trois canaux distincts Y, I et Q.&lt;/li&gt;
&lt;li&gt;calculer la transformée de Haar, qui produit un tableau de 128x128 coefficients par canal.&lt;/li&gt;
&lt;li&gt;sélectionner les 40 coefficients les plus élevés (valeur absolue) de chaque canal, qui forment l&#039;empreinte.&lt;/li&gt;
&lt;/ol&gt;
Comparer deux images consiste à calculer un score correspondant à la somme pondérée des coefficients identiques sur les deux images. Voir le code complet pour le détail de la méthode et la liste des poids utilisés.&lt;/p&gt;

&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;En pratique, les empreintes fréquentielles et les empreintes par ondelettes obtiennent des résultats assez similaires. J&#039;ai plutôt choisi, dans un premier lieu, de me tourner vers le premier, étant donné l&#039;existence d&#039;une implémentation open source autonome (&lt;a href=&quot;http://phash.org&quot;&gt;http://phash.org&lt;/a&gt;). Selon les résultats, il sera peut être aussi intéressant d&#039;extraire le code du calcul d&#039;empreinte par ondelette depuis les sources d&#039;ImgSeek.</description>
		<pubDate>Sat, 16 May 2015 18:22:00 +0200</pubDate>
		<dc:creator>Benjamin Billet</dc:creator>
	</item>
</channel>
</rss>