Utiliser ImageJ dans un notebook Sage, un exemple d'appel de Java depuis Python

(archive du 2009.11.10)

Sage est un logiciel formidable : un grand ensemble de langages et de librairies de mathématiques, avec Python pour faire la glue. Tout cela est assez remarquable, mais j’utilise beaucoup Java pour développer mes applications de traitement et d’analyse d’images. En particulier j’exploite la plateforme ImageJ qui permet de construire des plugins assez facilement distribuables. Il s’agit donc de pouvoir utiliser (facilement) une librairie Java avec Python, donc Sage. La méthode complète est décrite dans ce billet, permettant d’exploiter ImageJ dans Sage.

Comme Sage s’appuie sur Python, il suffit de trouver une méthode pour appeler du code Java depuis Python. Plusieurs solutions existent (si j’exclue les trucs bricolables avec du RMI):

  • Javaclass : pas réussi à le faire fonctionner. Le passage de valeurs me semblant de toute façon nécessiter du travail.
  • JCC : un peu lourd à mettre en place, en particulier le nécessité d’ajouter des options longues comme le bras au lancement de python.
  • JPype : le plus simple, se présentant comme un module python et d’une utilisation très aisée.

Installation de JPype

L’installation de JPype est assez classique :

unzip JPype-0.5.4.1.zip
cd JPype-0.5.4.1
sage -python setup.py build

Ici cela peut coincer car JPype utilise le module sets qui n’est plus inclu dans les versions récentes de python. Il n’y a pas beaucoup de modifications à apporter. Vous pouvez télécharger la version modifiée. Attention il faut tout de même suivre les instructions données dans le README.TXT pour donner le chemin vers votre installation JDK. Si la compilation se passe bien (sinon m’envoyer un mail), il suffit d’installer le module :

sage -python setup.py install

Maintenant nous allons travailler dans un notebook Sage (téléchargeable ici).

Utilisation de JPype

C’est assez direct :

  1. importer le module,
  2. lancer la machine virtuelle,
  3. importer un ou plusieurs packages java.

Comments

fmn: Après quelques discussions avec des collègues, ce genre de transfert est bien le point faible de JNI. Si j’ai le temps, je regarderais les performances de JNA qui indique : Java array and NIO Buffer arguments (primitive types and pointers) as pointer-to-buffer. A voir.

Laurent: Je n’ai pas trouvé d’autres solutions. Finalement, je m’en remets à l’échange d’images par lecture/écriture : les problèmes de synchronisation et de gestion d’erreurs entre les deux machines sont gérés par jpype, et les performances sont correctes, sauf pour l’écriture par imagej qui semble prendre environ 0.5 sec sur un pc avec peu de ram (1 Go) alors que ce chiffre tombe à 0.05 sur un autre pc avec 2 Go de ram, ou lors du premier chargement de la machine java. C’est un comportement étrange qui semble lié à la mise en cache de la machine. Merci pour tout. Laurent

Laurent: Hum, exact. Bien vu pour le coup du 2D python vers 1D java. J’étais passé à côté. Grand merci.

fmn: Il semble que la conversion dans les deux cas ne porte pas sur le même nombre d’éléments :

im = np.zeros((1024,1024), dtype = np.uint8)
len(JArray(JByte, 1)(im)

renvoit la valeur 1024. Mais le tableau renvoyé ne contient que des uint8. Comme en Java, un tableau 2D est un tableau de tableau, seuls les premiers éléments soient retenus. Par contre:

im = np.zeros((1024,1024), dtype = np.uint8)
len(JArray(JByte, 1)(im.flatten())

donne la bonne valeur 1048576, mais pour une durée ~0.67s. Sinon mes essais actuels pour affecter le tableau ainsi créer à un ByteProcessor echouent également. Je continue…

Laurent: J’ai fait un nouveau test, en utilisant les wrappers de jpype. Et je me suis aperçu de la chose suivante : (image est de type numpy.array de 2 dimensions, 1024*1024, dtype=uint8) JArray(JInt,2)(image) : 4 sec. Permet de transférer vers un objet ByteProcessor de imagej avec la méthode setIntArray de ImageProcessor JArray(JInt,1)(image) : 0.003 sec !!! Mais je n’ai pas trouvé de possibilité, dans l’api imagej, de transférer vers un objet ImageProcessor. JArray(JByte,1)(image) : 0.0012 sec. Je pensais pouvoir transférer les données vers un objet ByteProcessor grâce à la méthode setPixels. Mais jpype me retourne une erreur d’exception alors que le code de cette méthode devrait marcher (pour ce que je connais de java). Conclusion : je ne sais pas ce qui entraine ce facteur 1000 entre les deux types de conversion (1D ou 2D). Le code python ou le code java ? En outre, je ne trouve pas de point d’entrer dans le code imagej pour convertir JArray(JByte,1) vers ByteProcessor. C’est vraiment frustrant, car là je sens que j’approche d’une solution. Une solution serait de créer un plugin imagej permettant ce transfert. Mais là je touche à mes limites : je ne suis pas codeur java. Je vous tiens au courant si je progresse.

fmn: Il semble que jni permette d’obtenir depuis C un pointeur sur un tableau (voir http://java.sun.com/docs/books/jni/html/objtypes.html#27346). Mais je n’ai jamais testé cela. En fait pour moi l’intérêt de JPype est d’accéder à java, sans mettre les mains dans JNI. Mais il semble qu’il y ait un certain prix à ce confort. Peut-être que les autres méthodes que je mentionne en début de billet (JCC et JavaClass) sont plus performantes? En tout cas, merci d’avoir sur ce problème et je suis preneur d’infos sur ce point. FMN.

Laurent: J’ai aussi fait un test sur une machine plus musclée d’un collègue. Le temps passe de 5 à 3 secondes. Donc pas de miracle. Je voulais éviter le mécanisme d’échanges de données sur disque, car, pour assurer une fiabilité correcte, il faut prévoir et coder un “vrai" protocole d’échange (synchronisation des deux logiciels, gestion de message d’erreur, adaptation des chemins si distribution du logiciel, “handshake" etc…) entre les deux programmes. Et ce, des deux côtés ce qui implique de coder un plugins imagej dédié. Effectivement, l’auteur indique bien des problèmes de performance mais je n’aurais jamais imaginé que cela pouvait tomber si bas (1024x1024 octects ce n’est jamais qu’un objet “petit" objet de 1 Mo). J’ai regardé s’il y avait moyen de partager entre imagej et python l’espace mémoire directement sans copie préalable (transmission simple de pointeurs). Mais, de ce point de vue, java et l’api imagej ne semblent pas offrir cette possibilité. Enfin, il y a encore une possibilité : je regarde s’il est possible de transférer non pas l’image brute mais déjà compressée en mémoire. Mais cela semble aussi complexe. Je suis donc sur le point de renoncer et de m’en remettre à la conversion de plugins java en C au sein d’une dll. En tout cas merci d’avoir pris le temps de jeter un oeil à ces problèmes. Si jamais je trouve une astuce, je ne manquerai pas de la partager sur votre blog. Laurent

fmn: Laurent, effectivement avec java il faut inclure dans le classpath, le chemin de toutes les librairies. Sinon pour le problème de transfert. Mes temps de conversions sont du même ordre. Par exemple

ar = im.getProcessor().getIntArray()
ar_python = np.array(ar)

donne un temps d’env. 5s (pour une image 1024 par 1024). Par contre en ajoutant une indication du type du tableau crée:

ar = im.getProcessor().getIntArray()
ar_python = np.array(ar, dtype=np.uint8)

le temps de conversion “tombe" à ~2s. Ce qui reste assez inexploitable si des transferts fréquents sont nécessaire. L’auteur de JPype (voir Performances dans la doc) est conscient de cette inéfficacité, due à jni semble-t-il. La comparaison avec le transfert en utilisant ctypes est forcément en faveur de ctypes, puisque l’on reste dans le même “monde". Ici il y a un transfert forcé de/vers la jvm. Dans ton cas, je procéderai autrement. Des tests rapides montrent qu’en passant par une écriture/lecture disque, on peut transférer une image beaucoup plus rapidement. Par exemple, avec:

ij.IJ.save(im, 'tmp.gif')
ar_python = pylab.imread('tmp.gif')

le temps de transfert est de 0,08s.

fmn: Laurent, je suis ravi d’apprendre que ce tutoriel est profitable. Concernant l’appel de plugins, quelle est exactement la difficulté : appeler des plugins depuis python ou appeler des plugins tout court ?

Laurent: Bonjour et merci de la réponse. La difficulté était d’appeler des plugins ImageJ depuis Python. Pour une raison simple (voir après) mais mal indiquée par le module, je n’arrivais pas à accéder aux classes de ces plugins. En fait, pour un plugin imagej donné, il faut fournir non seulement le chemin de son .jar mais aussi ceux de toutes les classes dont il dépend dans le path java. Le message d’erreur de jpype ne m’a pas beaucoup aidé, puisqu’il indiquait toujours qu’il ne trouvait pas la classe principale que je tentais de lier, alors qu’en fait c’était les dépendances qui n’étaient pas trouver. Ce problème est donc résolu. Par contre, au final, je suis très déçu : les échanges de donnés entre python et imagej sont très très lents. Sur mon pc, une image de 1024x1024 octets met un temps > 5 sec pour transiter d’un objet numpy.array vers un objet ByteProcessor, et vice versa (voir votre exemple avec une image 1024x1024). Je pensais accéder à toute la puissance des traitements disponibles sur imagej depuis le projet que je développe en Python. C’est le cas, mais cette lenteur est, pour mon projet, inacceptable : je vise un temps de traitement global inférieur à la seconde (ce qui est déjà énorme sur les ordinateurs d’aujourd’hui). Et je voulais éviter de devoir coder les plugins imagej qui m’intéressaient de java vers C et créer une dll appelable depuis Python. Savez-vous si cette lenteur de transfert est normal et s’il n’y a pas moyen d’accélérer les choses ? Pouvez-vous faire l’essai avec votre exemple et une image de 1024x1024 pixels d’un octet ? A titre de comparaison, le transfert en python et une dll à l’aide de ctypes est quasi-instantané. Merci encore une fois de partager votre expérience.


Laurent: Merci beaucoup pour ce tutoriel qui est une excellente mise en jambe et ouvre à Python, par ce biais un champ immense de possibilités en traitement d’image. Votre exemple marche parfaitement sous Windows aussi. Par contre, je me casse les dents sur l’appel de classes de plugins installés sur ImageJ. Si vous avez surmonté cette difficulté, un tuyau serait le bienvenu. Dans tous les cas, merci beaucoup d’avoir partagé votre précieuse expérience.

Exploitation des métriques à référence réduite et sans référence dans les systèmes d'imagerie (journée GDR)

(archive du 2012.09.05)

Comme je suis co-organisateur (avec C. Larabi et D. Nicholson) de cette journée, je profite de mon blog pour en faire un peu de publicité. La journée aura lieu le 20 septembre 2012 à Telecom ParisTech - Amphi Saphir.

Le pitch : La mesure de la qualité des images/vidéos est une problématique de recherche à laquelle se trouvent confrontées bon nombre d’équipes à la fois académiques et industrielles. Une solution consiste à réaliser une notation subjective de la qualité d’une image (par rapport à une référence ou sans référence) par un panel d’observateurs. Or la mise en place de tels tests psychophysiques est fastidieuse et est en pratique peu appliquée, car elle doit respecter des normes clairement établies. Des solutions algorithmiques (i.e. mesures objectives) ont alors été développées afin d’éviter le recours à de tels tests. Certaines de ces mesures intègrent à divers degrés des modélisations du système visuel humain (SVH) afin de reproduire le plus fidèlement possible le comportement d’un observateur.

Le programme :
  • Tutorial : à confirmer (10:00-11:00)
  • 11:00 - 11:30 - Détermination locale de la netteté d’un système imageur par analyse en ondelettes - Lâmân Lelégard (MATIS, IGN)
  • 11:30-12:00 - Indices de netteté par mesure de cohérence de phase - Arthur Leclaire (MAP5, Université Paris Descartes)
  • 12:00 - 12:30 - Image Quality Estimation based on Distortion Classification - Aladine Chetouani (L2TI, Université Paris 13)
  • 14:00-14:30 - Caractérisation des distorsions par l’exploitation des points d’intérêt - Michaël Nauge (XLIM-SIC, Université de Poitiers)
  • 14:30-15:00 - Mesure agnostique de la qualité des images sans référence - Christophe Charrier (GREyC, Université de Caen)
  • 15:00-15:30 - Mesure sans référence de la qualité des vidéos télédiffusées à l'aide des cartes de saillance - Hugo Boujut (LABRI, Université de Bordeaux)
  • 15:45-16:15 - T-V-Model, un model paramétrique standardisé pour estimer la qualité audiovisuelle des services IPTV - Marie-Neige Garcia (Telekom Innovation Laboratories & TU Berlin/AIPA, Allemagne)
  • 16:15-16:45 - Repousser les limites de l’identification faciale en contexte de vidéo-surveillance - Cécile Fiche (GIPSA-lab, Grenoble)
  • 16:45-17h15 - Détection automatique d’erreurs de segmentation pour la reconnaissance par l’iris - Thierry Lefevre (Thales / Télécom SudParis)
  • 17:15-17:30 - Conclusion (plus d'informations sur le site du GDR ISIS)

Cabotages internautes

(archive du 07.09.2012)

Inauguration de billets consacrés à des nouvelles et articles du net, liés au traitement et à l'analyse d'images - Pour ce premier essai, je case un certain nombre d'entrées qui peuvent un peu dater (quelques mois).

  • Why We Created Julia : une proposition d'un langage dédié au calcul numérique assez ambitieux. Je n'ai pas encore pris le temps de fouiller en détail, mais le programme est alléchant : We want the speed of C with the dynamism of Ruby. We want a language that’s homoiconic, with true macros like Lisp, but with obvious, familiar mathematical notation like Matlab. We want something as usable for general programming as Python, as easy for statistics as R, as natural for string processing as Perl, as powerful for linear algebra as Matlab, as good at gluing programs together as the shell. Something that is dirt simple to learn, yet keeps the most serious hackers happy. We want it interactive and we want it compiled.
  • Livre: Programming Computer Vision with Python chez O'Reilly. J'aime Python et la vision artificielle, j'ai donc acheté ce livre (en numérique). Pas encore lu en détail, mais la table des matières et un survol des pages me laissent penser qu'il y a une bonne explication de l'utilisation de Python en analyse/traitement d'image.
  • Hany Farid: Image Search for Image Forensics. J'aime beaucoup les écrits d'H. Farid, chercheur en imagerie légale (image forensics). Ici il explique clairement comment des systèmes de recherche d'images par le contenu peuvent être utilisés pour détecter des falsifications. En particulier, la présence d'une image (ou d'une partie de) dans d'autres contextes peut être une preuve de falsification.
  • The Curiosity Super-Resolution Challenge. Igor Carron nous propose un défi lié à l'actualité. Il s'agit de fabriquer un panorama haute-résolution du cratère Gale à l'aide d'un dictionnaire construit à partir d'images et en faisant de l'inpainting de la version basse-résolution du panorama. (Note perso : comment traduire inpainting ?)
  • What defines a functional programming language?. Article très sympa à lire qui cherche à définir l'essence d'un langage fonctionnel. Après avoir éliminé les définitions classiques (présence de fonctions de première classe, lambdas, ...), la définition retenue (que j'apprécie) est : A functional programming language is a language that emphasises programming with pure functions and immutable data.

C'est tout pour aujourd'hui.

Séminaire VIBOT : Local and non-metric similarities between images

(archive du 01.10.2012)

La semaine dernière, à l'occasion de ma participation au jury de thèse de J. Mitra (Multimodal Image Registration Applied to Magnetic Resonance and Ultrasound Prostatic Images - bravo à la nouvelle docteure), j'ai donné un séminaire dans le cadre du master VIBOT. Le sujet du séminaire était : les similarités entre images, locales et non-métrique. J'ai tenté de faire passer le message de l'utilité de mesures locales (bien construites) et de l'intérêt d'utiliser des mesures qui ne sont pas des distances. Le diaporama est disponible sur la page ... des diaporamas.

Cabotages internautes (spécial Python)

(archive du 28.08.2013)

[Pics] Manipulated Photos Of Notable Historic Figures Before The Digital Era (Before And After Images) | Fstoppers

Quelques exemples intéressants de falsification d'images non-numériques, qui mettent bien en évidence les enjeux de la détection de telles contrefaçons.
FMN.

(original article from Fstopper)
[Pics] Manipulated Photos Of Notable Historic Figures Before The Digital Era (Before And After Images)

Manipulating photos happened way before Photoshop was around. This series shows before and afters of famous and notable figures before the digital era began. Truly fascinating, you can't help but wonder what level of dedication it took to make these happen.

The process used was not only clever darkroom techniques but also stitching various images together to create the after shots you see. Though, I am certain there's more to it and it's much harder than it sounds. Many of these are hard to do even in the digital era with Photoshop!

A photo of President Lincoln's head was placed on the image of Southern politician John Calhoun to create the portrait on the left.

Adolf Hitler had Joseph Goebbels (second from the right) removed from the original photograph after a falling out.

Perhaps in an attempt to not have his presence dwarfed, Canadian Prime Minister William Lyon Mackenzie King had King George VI removed from the original photograph of him and Queen Elizabeth.

The horse handler was removed from this portrait of Italian dictator Benito Mussolini to appear more heroic.

Russian magazine Ogoniok had the watch removed from a WWII soldier's arm as to avoid accusations it may have been stolen (as he is wearing another watch on left wrist as well).

The photograph of Ulysses S. Grant on the left is actually a composite of these three different photographs pieced together.

General Francis P. Blair (on the far right end) was added to this Matthew Brady photograph at a later date.

Like Hitler, Russian dictator Josef Stalin had a commissar removed from the original photograph after a falling out.

[Via Yahoo! News]

From Pratik:

I'd love to hear from you! Stop by and get in touch with me via my facebook page or my website, http://www.solsticeretouch.com.


IPOL Journal · Image Processing On Line

Je viens de découvrir le journal IPOL : Image Processing On Line.
L'idée est de considérer qu'un article scientifique seul, sans le code associé qui a permis de construire et valider les résultats, n'est que la partie émergée de l'iceberg.
Je trouve que ceci est particulièrement vrai en analyse et traitement des images.

Ce journal vise à publier conjointement code source et article. Les deux étant relus et testés. Soumissions et consultations sont gratuites.

Nouveaux sites pour mes cours

Grace à la facilité d'utilisation de postach.io, chacun de mes cours sera accompagné d'un site dédié. Pour ce début d'année, deux modules sont concernés :

Sur ces sites : syllabus, contenus, diapositives, lectures obligatoires, énoncés de TD, ... Bref tous les documents utilisés pendant le semestre.

En Migration

Pixel Shaker est en cours de migration et sera bientôt de retour.