jeudi 10 octobre 2013

ImageMagick et PowerShell

Voici la fin d'un périple d'automatisation de capture d'image pour notre catalogue :-) ...

Donc, après les découvertes suivantes:
  • la capture d'image automatisée sur un Nikon D5000 et 
  • la découverte d'ImageMagick (voir les deux posts précédents)
Voici un script PowerShell qui:
  • Réduit automatiquement l'image en 3 tailles différentes (600x600, 200x200, 100x100)... 
  • Sélectionne la partie centrale (pour avoir un beau carré)
  • Dégrade la qualité à 93% (le meilleur rapport entre taille et qualité)
prêtes à être intégrées dans notre base de donnée PostGreSql:-)
Le reste se fera en clipper compilé à l'aide d'Harbour Project (compilateur Clipper libre)


# ----------------------------------------
# Configuration
$srcfolder = "C:\Stock\Input\CatPhoto.in"
$destfolder = "C:\Stock\Input\CatPhoto.in"
$im_convert_exe = "C:\Program Files (x86)\ImageMagick-6.8.7-Q16\convert.exe"
$src_filter = "pict_*.jpg"
$dest_ext = "jpg"
# Image source = 4282 x 2848, target = ? x 600 (to be cropped at 600x600) 
# Width=903 <-- data-blogger-escaped-final="" data-blogger-escaped-height="600" data-blogger-escaped-source=""> ratio = 4.746 => Width for Height = 600 => 4282 / 4.746 => 903px
# Intermediate image would be resized to 903x600 BEFORE croping to 600x600
$optionsCatImage = "-resize 903 -crop 600x600+151+0 -quality 93" 
$optionsCatThumb = "-resize 302 -crop 200x200+51+0 -quality 93"
$optionsCatMiniThumb  = "-resize 150 -crop 100x100+25+0 -quality 93"
$logfile = "c:\Stock\Input\CatPhoto.in\convert_image.txt"
# ----------------------------------------

$fp = New-Item -ItemType file $logfile -force
$count=0
foreach ($srcitem in $(Get-ChildItem $srcfolder -include $src_filter -recurse))
{
    $srcname = $srcitem.fullname

    # Construct the filename and filepath for the output
    $partial = $srcitem.FullName.Substring( $srcfolder.Length )
    $destCatname = $destfolder + $partial.replace( "pict_", "cat_" )
    $destThumbname = $destfolder + $partial.replace( "pict_", "thumb_" )
    $destMiniThumbname = $destfolder + $partial.replace( "pict_", "mini_" )
    $destCatname= [System.IO.Path]::ChangeExtension( $destCatname , $dest_ext )
    $destThumbname= [System.IO.Path]::ChangeExtension( $destThumbname , $dest_ext )
    $destMiniThumbname= [System.IO.Path]::ChangeExtension( $destMiniThumbname , $dest_ext )
    $destpath = [System.IO.Path]::GetDirectoryName( $destCatname )

    # Create the destination path if it does not exist
    # if (-not (test-path $destpath))
    # {
    #     New-Item $destpath -type directory | Out-Null
    # }

    #--- CAT IMAGE ---
    # Perform the conversion by calling an external tool
    $cmdline =  "& `""+$im_convert_exe+"`""+ " `"" + $srcname  + "`" " + $optionsCatImage + " `"" + $destCatname + "`" "
    echo $cmdline
    invoke-expression -command $cmdline    

    # Get information about the output file    
    $destitem = Get-item $destCatname

    # Show and record information comparing the input and output files
    $info = [string]::Format( "{0} `t {1} `t {2} `t {3} `t {4} `t {5}", $count, 
 $partial, $srcname, $destCatname, $srcitem.Length ,  $destitem.Length)
    echo $info
    Add-Content $fp $info

    $count=$count+1

    #--- CAT THUMB ---
    # Perform the conversion by calling an external tool
    $cmdline =  "& `""+$im_convert_exe+"`""+ " `"" + $srcname  + "`" " + $optionsCatThumb + " `"" + $destThumbname + "`" "
    echo $cmdline
    invoke-expression -command $cmdline    

    # Get information about the output file    
    $destitem = Get-item $destThumbname

    # Show and record information comparing the input and output files
    $info = [string]::Format( "{0} `t {1} `t {2} `t {3} `t {4} `t {5}", $count, 
 $partial, $srcname, $destThumbname, $srcitem.Length ,  $destitem.Length)
    echo $info
    Add-Content $fp $info

    $count=$count+1

    #--- CAT MINI THUMB ---
    # Perform the conversion by calling an external tool
    $cmdline =  "& `""+$im_convert_exe+"`""+ " `"" + $srcname  + "`" " + $optionsCatMiniThumb + " `"" + $destMiniThumbname + "`" "
    echo $cmdline
    invoke-expression -command $cmdline    

    # Get information about the output file    
    $destitem = Get-item $destMiniThumbname

    # Show and record information comparing the input and output files
    $info = [string]::Format( "{0} `t {1} `t {2} `t {3} `t {4} `t {5}", $count, 
 $partial, $srcname, $destMiniThumbname, $srcitem.Length ,  $destitem.Length)
    echo $info
    Add-Content $fp $info

    $count=$count+1
    
}

Outils de transformation d'image ... en ligne de commande ou PowerShell

Introduction
Après le shooting automatique d'image pour notre catalogue (voir article précédent), il faut s'attarder un peu sur le traitement d'image.
Difficile de générer un catalogue en ligne ou papier avec des images qui font 4288x2848 qui font 4.8Mb pièce!!!à plus forte raison s'il y a 2500+ produits dans le catalogue préliminaire.

Gimp et Photoshop c'est bien, mais il n'est pas possible d'imaginer le traitement manuel de 2500 prises de vue.
L'utilisation d'un outil en ligne de commande ou OleAutomation pour automatisé le processus est donc primordial... a plus forte raison si le back-end est maintenu en Clipper.

Découvrez ImageMagick
Au cours de mes recherches, j'ai rencontré ImageMagick, une solution libre et franchement incroyable.

ImageMagick® est une suite logicielle pour créer, éditer, composer ou convertir des images bitmap. Il est capable de lire et d'écrire des images dans une variété de formats (plus de 100) incluant DPX, EXR, GIF, JPEG, JPEG-2000, PDF, PhotoCD, PNG, Postscript, SVG, and TIFF.

Utilisez ImageMagick pour des opérations de modification de taille, flip, mirroir, rotation, distortion, cisaillement et transformation d'images, ajuster les couleurs de l'image, appliquer des effets spéciaux, ou dessiner du texte, lignes, polygones, ellipses et courbes de Bézier.

ImageMagick ne fonctionne pas seulement en ligne de commande, mais aussi avec des objets d'automations et de nombreux Wrapper pour de nombreux langages (y compris Python).

Exemple
Voici une image traitée automatique avec ImageMagick.
La source est en 4288x2848 faisant 4.6Mb

Et son traitement à l'aide d'ImageMagick avec la commande suivante:
Réduction de taille (903x600, réduire la largeur à 903 adapte automatiquement la hauteur à 600), qualité (93%) et découpe de 600x600 au centre (donc éliminer les bords droit et gauche)
convert pict_20131010_104548_1.jpg -resize 903 -crop 600x600+151+0 -quality 93 final.jpg


On obtient:

Ressource

mercredi 9 octobre 2013

Récupérer les images d'un appareil photo en PowerShell

Introduction
La société où je travaille va remonter un vrai catalogue informatisé et nous investissons beaucoup de temps dans la structuration des informations... vive les bases de données.
Mais pour faire un catalogue, il faut aussi des images. 
Pourquoi, donc, ne pas essayer d'automatiser la saisie de ces images catalogue directement depuis notre logiciel (nous avons 2500+ produits au catalogue)..

On place l'objet a photographier sur le stand de photographie, on sélectionne l'article dans le programme et ont clique sur le bouton shoot! Prise de photo, traitement d'image (principalement réduction de taille et poids) et stockage peuvent facilement être automatiser pour un travail d'envergure.

Ha oui, pour corser le tout... le soft est maintenu en clipper compilé en 32 bit sous Windows à l'aide d'Harbour Project

Les appareils "moyen de gamme" c'est super
La plupart des appareil photo moyen de gamme sont reconnu comme de vrai lecteurs disques dès qu'il sont branchés en USB. Sur la plupart il est même possible de faire des prises de vues pendant qu'il est branché en USB.
Même, si dans ce cas, il faut faire les prises de vues en poussant sur le bouton, le fait de disposer d'un système de fichier standard est vachement pratique... car un simple "file copy" rempli toutes nos attentes.

Les appareils "haut de gamme" c'est le casse tête
Par contre, les prise de vue seront faite à l'aide d'un Nikon D5000.
Si l'appareil est super et produit des photographies a tomber par terre, les photos ne sont pas accessibles au travers d'un lecteurs.
Si vous branchez le cable USB, Windows reconnait l'appareil et vous pouvez naviguer à la recherche des photographie avec l'explorateur de fichier... faire un copier/coller.

Mais essayer seulement d'écrire un batch ou un programme qui fait un file copy des fichiers jpeg!!!! vous allez voir que les choses se corsent.

Les appareils haut de gamme sont souvent reconnu par Windows et y associe une interface de gestion "plus évoluée"... cela implique souvent la perte de fonctionnalités de base.

La solution se trouve dans l'interface ActiveX WIA.

Qu'est-ce que WIA
WIA signifie Windows Image Acquisition, c'est une interface d'automation qui permettra de capturer et transférer des images depuis différentes sources (Appareil Photo, WebCam, Scanner, etc).
Avec WIA il est même possible de déclencher la prise de vue sur l'appareil.
Vous trouverez probablement assez facilement des informations sur WIA en naviguant sur le net.

WIA et PowerShell
Il est possible d'utiliser WIA avec PowerShell... c'est bien pratique pour récupérer des images stockées sur notre Nikon D5000.
L'exemple MyShotDownloadPict.ps1 ci-dessous fait une capture d'image (prise de vue) puis rapatrie toutes les images dans un répertoire local du PC... finalement, il effaces ces images de la carte SD (pour la séance suivante).

// Script Powershell pour:
// 
// Utiliser WIA Windows Image Acquisition Automation afin de
// prendre commander le shoot d'une photo (avec Nikon D5000) 
// connecté en USB puis transférer toutes les photos (dont le
// shoot) dans un répertoire sur le PC.
// 
// Nécessite l'enregistrement de WIA 
//   RegSvr32 WIAAut.dll
//
// Source: 
//   http://helvick.blogspot.be/2007/08/camera-automation-with-powershell-part_19.html
//   http://msdn.microsoft.com/fr-fr/library/bb469890.aspx
//
$ErrorActionPreference="silentlycontinue"
$WIAManager = new-object -comobject WIA.DeviceManager
if (!$?) {
   write "Unable to Create a WIA Object"
   Eexit
}
$DeviceList = $WIAManager.DeviceInfos
if ($DeviceList.Count -gt 0) {
   $Device=$DeviceList.item($DeviceList.Count)
} else {
   write "No Device Connected"
}
$ConnectedDevice = $Device.connect()
if (!$?) {
   write "Unable to Connect to Device"
   Exit
}
$Commands = $ConnectedDevice.Commands
$TakeShot="Not Found"
foreach ($item in $Commands) {
   if ($item.name -match "take") {
       $TakeShot=$item.CommandID
   }
}


$Pdir="C:\Stock\Input\CatPhoto.In"
new-item $Pdir -itemtype dir
$ICompare="c:\Program Files\ImageMagick-6.3.5-Q16\compare.exe"
$ActiveCamera = $ConnectedDevice

# Fait un shot et télécharge l'image
$ActiveCamera.ExecuteCommand($TakeShot)
# Attendre 1 seconde pour que l'appareil
# Ai le temps d'enregistrer l'image sur la carte
sleep 1

# Télécharger le restant des images éventuellement présente
$iCount= $ActiveCamera.items.Count
Write "Camera has $iCount pictures"
for ($iCount=$ActiveCamera.items.Count;$iCount -gt 0;$iCount--) {
    
    $f=$ActiveCamera.items.item($iCount)
    $fg=$f.Transfer()
    $localFilename = "pict_"+$(get-date -f "yyyyMMdd_hhmmss")+"_$iCount.jpg"
    Write "Saving $localFilename..."
    $fg.SaveFile( $Pdir+"\"+$localFilename )

    # Retire l'image de la carte de l'appareil photo
    $ActiveCamera.items.Remove($iCount)
}

Voici donc un outil d'automatisation que je pourrais appeler depuis mon programme Clipper (ouf!)