jueves, febrero 07, 2013

Transformar ficheros RAW en Alfresco

Como ya sabemos, Alfresco tiene muchas posibilidades de manipulación y transformación de ficheros/documentos. Una de las cosas más flexibles es el uso de transformadores que puden ser de dos tipos básicamente:
  1. Llamadas directas a algún comando o utilidad del sistema
  2. Desarrollar una clase Java que realice la transformación
Aquí vamos a ver la primera para utilizar la transformación de fotografías tomadas en formato RAW (o bruto como generalmente se llama) a TIFF y a JPEG. Actualmente hago fotos con Nikon y Olympus y me interesa pasar el formato NEF (por poner el de Nikon) a TIFF y a JPEG. ImageMagick puede hacerlo pero me ha dado problemas tanto en el resultado (no se pueden ver) como en la propia conversión por lo que voy a usar otra utilidad que existe para Linux, Mac OS X y Windows.
Esta utilidad se llama dcraw y podemos bajarla de http://www.cybercom.net/~dcoffin/dcraw/ para el sistema operativo que tengamos. En este caso voy a utilizar la que hay para Windows compilada con MingW desde la dirección http://www.rawness.es/dcraw/?lang=es
Una vez instalada o descomprimida en el directorio que nos guste obtendremos el programa ejecutable dcraw.exe
Bien, ahora a configurar el transformador...
Toda la configuración se basa en un bean llamado RuntimeExecutableContentTransformer para versiones anteriore a la 3.2 de Alfresco o RuntimeExecutableContentTransformeWorker para las siguientes. A su vez este bean contiene las siguientes propiedades principales:
  • transformCommand: Define el comando y parámetros usando variables como ${source} y ${target}
  • checkCommand: Chequea si la sintaxis es correcta y si ocurre un error también se puede definir con errorCodes. Cuando no está disponible el método getReliability devuelve 0:0 este método se usa para utilizar el transformador más eficiente en caso de que haya varios definidos que conviertan de la misma fuente al mismo destino.
  • explicitTransformations establece el tipo MIME fuente y destino de forma explícita. 
Creamos el fichero nef-tiff-jpeg-transform-context.xml:
<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE beans PUBLIC '-//SPRING//DTD BEAN//EN' 'http://www.springframework.org/dtd/spring-beans.dtd'>
<beans>
    <bean id="transformer.worker.NEFtoTIFF" class="org.alfresco.repo.content.transform.RuntimeExecutableContentTransformerWorker">
        <property name="checkCommand">
            <bean class="org.alfresco.util.exec.RuntimeExec">
                <property name="commandMap">
                    <map>
                        <entry key=".*">
                            <value>${dcraw.exe} -i ${dcraw.home}/test.nef</value>
                        </entry>
                    </map>
                </property>
                <property name="errorCodes">
                    <value>1</value>
                </property>
            </bean>
        </property>
        <property name="transformCommand">
            <bean class="org.alfresco.util.exec.RuntimeExec">
                <property name="commandMap">
                    <map>
                        <entry key="Linux.*">                   
                            <value>${dcraw.exe} -T '${source}' '${target}'</value>
                        </entry>
                        <entry key="Mac OS X">
                            <value>${dcraw.exe} -T '${source}' '${target}'</value>
                        </entry>                   
                        <entry key="Windows.*">               
                            <value>${dcraw.exe} -T "${source}" "${target}"</value>
                        </entry>
                    </map>
                </property>
                <property name="waitForCompletion">
                    <value>true</value>
                </property>
            </bean>
        </property>
        <property name="explicitTransformations">
            <list>
                <bean class="org.alfresco.repo.content.transform.ExplictTransformationDetails" >
                    <property name="sourceMimetype"><value>image/x-nikon-nef</value></property>
                    <property name="targetMimetype"><value>image/tiff</value></property>
                </bean>
            </list>
        </property>
        <property name="mimetypeService">
            <ref bean="mimetypeService"/>
        </property>
    </bean>
    <bean id="transformer.NEFtoTIFF" class="org.alfresco.repo.content.transform.ProxyContentTransformer" parent="baseContentTransformer">
        <property name="worker">
            <ref bean="transformer.worker.NEFtoTIFF"/>
        </property>
    </bean>
    <bean id="transformer.complex.Nef.Jpg"
        class="org.alfresco.repo.content.transform.ComplexContentTransformer"
        parent="baseContentTransformer" >
        <property name="transformers">
            <list>
                <ref bean="transformer.NEFtoTIFF" />
                <ref bean="transformer.ImageMagick" />
            </list>
        </property>
        <property name="intermediateMimetypes">
            <list>
                <value>image/tiff</value>
            </list>
        </property>
    </bean>
</beans>

Como vemos hay además otro bean definido de la clase ComplexContentTransformer, este sirve para realizar transformaciones a partir de otras, es decir, si no podemos transformar una imagen del formato NEF al formato JPEG pero si podemos realizar la transformación de NEF a TIFF y de TIFF a JPEG podemos configurarlo a través de este bean en el que hay que poner los transformadores y el mimetype intermedio de la transformación.

 Con esto ya podemos transformar imágenes tomadas directamente en RAW (NEF en el caso de Nikon) a TIFF y a si vez y mediante ComplexContentTransformer directamente a JPEG.
Ahora con dcraw.exe...
Esta utilidad de línea de comandos al igual que convert.exe de ImageMagick tiene la siguiente sintaxis:
C:\Users\fernando.gonzalez\workspace\Common\MingW-release-orig-x32>dcraw.exe
Raw photo decoder "dcraw" v9.06
by Dave Coffin, dcoffin a cybercom o net
Usage:  dcraw.exe [OPTION]... [FILE]...
-v        Print verbose messages
-c        Write image data to standard output
-e        Extract embedded thumbnail image
-i        Identify files without decoding them
-i -v     Identify files and show metadata
-z        Change file dates to camera timestamp
-w        Use camera white balance, if possible
-a        Average the whole image for white balance
-A <x y w h> Average a grey box for white balance
-r <r g b g> Set custom white balance
+M/-M     Use/don't use an embedded color matrix
-C <r b>  Correct chromatic aberration
-P <file> Fix the dead pixels listed in this file
-K <file> Subtract dark frame (16-bit raw PGM)
-k <num>  Set the darkness level
-S <num>  Set the saturation level
-n <num>  Set threshold for wavelet denoising
-H [0-9]  Highlight mode (0=clip, 1=unclip, 2=blend, 3+=rebuild)
-t [0-7]  Flip image (0=none, 3=180, 5=90CCW, 6=90CW)
-o [0-5]  Output colorspace (raw,sRGB,Adobe,Wide,ProPhoto,XYZ)
-d        Document mode (no color, no interpolation)
-D        Document mode without scaling (totally raw)
-j        Don't stretch or rotate raw pixels
-W        Don't automatically brighten the image
-b <num>  Adjust brightness (default = 1.0)
-g <p ts> Set custom gamma curve (default = 2.222 4.5)
-q [0-3]  Set the interpolation quality
-h        Half-size color image (twice as fast as "-q 0")
-f        Interpolate RGGB as four colors
-m <num>  Apply a 3x3 median filter to R-G and B-G
-s [0..N-1] Select one raw image or "all" from each file
-6        Write 16-bit instead of 8-bit
-4        Linear 16-bit, same as "-6 -W -g 1 1"
-T        Write TIFF instead of PPM

Podemos observar tres cosas:
  1. Podemos usar el modificador -i para chequear la utilidad, solo hay que usar una imagen y llamarla por ejemplo test.nef
  2. Debemos usar el modificador -T para pasar las imágenes a TIFF en lugar de PPM que es lo que hace por defecto
  3. Tenemos un problema, el resultado de la transformación es siempre el mismo fichero pero con la extensión cambiada, es decir, no podemos obligar a guardar el resultado en otro fichero (bueno, si, con el modificador -c que saca la salida por consola y capturandola con una redirección ">" pero esto no funciona bien)
Como Alfresco utiliza en la transformación dos variables ${source} y ${target} y ambos son distintos (genera los nombres de estos ficheros distintas) nos encontramos con el problema de que no podemos llamarlo directamente por lo que hay que crear un script que llame correctamente al ejecutable con los modificadores adecuados.
Este script en formato BAT/CMD de Windows y llamado dcraw.cmd es el siguiente:
@ECHO OFF
set path_dcraw=%0
SET opts=%1
SET source=%2
SET target=%3
%path_dcraw:~,-3%exe %opts% %source%
IF "%opts%"=="-i" GOTO END
MOVE %source:~,-4%tiff" %target%
:END

Seguidamente vamos a crear las variables en el fichero de propiedades, en este caso utilizo el mismo alfresco-global.properties:
dcraw.home=C:/Users/fernando.gonzalez/workspace/Common/MingW-release-orig-x32
dcraw.exe=${dcraw.home}/dcraw.cmd

Y por último debemos declarar los tipos MIME (mimetypes) en el fichero mimetypes-extension-map.xml que está en <extensionRoot>/alfresco/extension/mimetype:

<alfresco-config area="mimetype-map">
    <config evaluator="string-compare" condition="Mimetype Map">
        <mimetypes>
            <mimetype mimetype="image/x-nikon-nef" display="Nikon Raw Image">
                <extension>nef</extension>
            </mimetype>
            </mimetype>
        </mimetypes>
    </config>
</alfresco-config>

 Listo, ya solo nos falta hacer las reglas necesarias para convertir NEF a TIFF o a JPEG directamente en nuesto estudio fotográfico y tener almacenadas y ordenadas todas nuestras fotografías. Además, podemos usar Alfresco de previsualizador de imágenes RAW aunque no tengamos nigún programa para ello como podemos ver en la siguiente imagen:
Para depurar podemos activar las siguientes líneas en Log4J (log4j.properties o custom-log4j.propeties en el extension):
log4j.logger.org.alfresco.repo.content.transform.TransformerDebug=debug
log4j.logger.org.alfresco.util.exec.RuntimeExec=debug
log4j.logger.org.alfresco.repo.content.transform.ContentTransformerRegistry=debug