miércoles, 15 de junio de 2011

Y dónde está cutil64.dll??

Muchos de los ejemplos que circulan por internet utilizan esta DLL. Tiene utilitarios generales para CUDA y viene dentro del "GPU Computing SDK examples". Para facilitar las cosas, es conveniente tomar esa librería (la versión correcta y para la plataforma correcta) de su carpeta en el SDK y colocarla dentro de la carpeta de instalación de CUDA. Lo mismo ocurre con los correspondientes archivos .lib.
Entonces, para un sistema 64 bits, copiamos:
  • <NVIDIA GPU Computing SDK Folder>\C\inc\cutil.h en <CUDA Folder>\include
  • <NVIDIA GPU Computing SDK Folder>\C\bin\win64\Release\cutil64.dll en <CUDA Folder>\bin64
  • <NVIDIA GPU Computing SDK Folder>\C\bin\win64\Debug\cutil64D.dll en <CUDA Folder>\bin64
  • <NVIDIA GPU Computing SDK Folder>\C\common\lib\cutil64.lib en <CUDA Folder>\lib64
  • <NVIDIA GPU Computing SDK Folder>\C\common\lib\cutil64D.lib en <CUDA Folder>\lib64
A modo anecdótico, dejo unos comentarios sobre la cutil.dll del release notes de una versión de CUDA:
      - Most samples link to a utility library called "cutil" whose source code
        is in "NVIDIA CUDA SDK\common". The release and emurelease versions of
        these samples link to cutil[32|64].lib and dynamically load
    cutil[32|64].dll. The debug and emudebug versions of these samples link
    to cutil[32D|64D].lib and dynamically load cutil[32D|64D].dll.
        To build the 32-bit and/or 64-bit, release and/or debug configurations
    of the cutil library, use the solution files located in
        "NVIDIA CUDA SDK\common". The output of the compilation goes to
        "NVIDIA CUDA SDK\common\lib":
         - cutil[32|64].lib and cutil[32D|64D].lib are the release and debug
       import libraries,
         - cutil[32|64].dll and cutil[32D|64D].dll are the release and debug
           dynamic-link libraries, which get also copied to
           "NVIDIA CUDA SDK\bin\win[32|64]\[release|emurelease]" and
           "NVIDIA CUDA SDK\bin\win[32|64]\[debug|emudebug]"
       respectively;

lunes, 13 de junio de 2011

Volviendo el tiempo atrás... CUDA 2.3

Lamentablemente, y aunque la sintaxis de CUDA 4 está muy buena, debemos volver a la versión 2.3 sobre Windows y compilar el código del Onera [1] con una resolución del Lucas-Kanade en GPU.

Lamentablemente, como suele suceder, aparecen nuevos problemas a salvar:
  • NVCC sin un compilador no funciona. Necesitamos el Visual Studio...
  • Visual Studio 2008 Express no tiene soporte para procesadores x64 con lo que hay que optar por una versión Trial del Professional.
  • Para compilar por línea de comandos nos quedamos con un ejemplo pequeño de archivo .cu y logramos compilarlo sólo luego de leer un post interesante [2] en el foro de nvidia: es necesario ejecutar un batch que setea el entorno del VS en modo amd64 y luego indicar algunos parámetros extras al NVCC.
  • Dentro del VS2008, NVCC sigue sin compilar. Es necesario actualizar el archivo nvcc.profile para que tenga un include extra del visual:
INCLUDES += "-I$(TOP)/include" "-I$(TOP)/include/cudart" "-IC:/Program Files (x86)/Microsoft Visual Studio 9.0/VC/include" $(_SPACE_)

[1] Onera - FOLKI GPU
[2] Post compilación x64

viernes, 10 de junio de 2011

Eligiendo la cámara - Conclusiones

Ganador momentaneo: PixelFly

Conclusiones generales:
  1. La Pulnix es demasiado lenta. Además no ofrece ninguna ventaja significativa.
  2. La PixelFly tiene un buen tiempo de transmisión con binning activado. Según las pruebas de concepto se obtendrían 10 ms entre frame y frame con lo cual se puede usar esta cámara.
  3. La PixelFly tiene además modo de doble shutter que no fue analizado pero brinda más alternativas.
  4. La SpeedCam, aún siendo la de mejor calidad y velocidad no tuvo buenos resultados. La transferencia resulta muy lenta con lo que queda descartada por el momento.
  5. Es importante aclarar que el análisis se basa en la necesidad de hacer un algoritmo lo más veloz posible y poder realiar cálculos en el momento. En caso de no ser esto así se puede reconsiderar los resultados y optar por la SpeedCam en lugar de la PixelFly, por ejemplo. Esto podría ocurrir en caso que se necesite tiempo extra para realizar procesamiento, no requerir 1 frame cada 10 ms sino 2 frames espaciados en 10 ms para, más tarde, repetir la toma de fotos y el proceso.

sábado, 4 de junio de 2011

Tiempos - Usando la SpeedCam programáticamente

En este punto, la experiencia se convirtió en algo bastante pesado. La SpeedCam no tiene una API de acceso programática. Tampoco permite sacar fotogramas de forma unitaria, sólo se puede sacar una ráfaga que es almacenada en el buffer interno de la cámara y luego transmitido a la PC.
La cámara posee código hecho en Java del cual se pueden obtener los .jar para emplearlos en un código de prueba.
Consultamos entonces a los fabricantes: nos responden que la compañía cambió de manos. Consultamos a los nuevos responsables y obtenemos como información que tenemos tres caminos:
  • utilizar una API a nivel registro con la cual se accede a la cámara enviando códigos de operación
  • revisar los archivos minivis.jar y minivis.dll
  • comprar otra cámara o adquirir el nuevo software.
Obviamente nos quedamos con la segunda por "simplicidad" (si es que se puede decir que esto sea simple). Entonces, armamos un archivo .Java y tratamos de adivinar cómo se usa la API que contiene el .jar.
Luego de varios días de revisar los resultados del Java Decompiler. Obtenemos una sucesión de pasos que permite obtener las imágenes con resultados bastante malos:
  • Snapshots tomados: 200
  • fps entre Snapshots: 2500
  • Tiempo Total: 7800ms (sólo en la transferencia, ya que la toma de imágenes se debe hacer previa a la este paso)
  • Tiempo Promedio: 39ms (es decir, 25 fps)
Conclusión: muy mala velocidad para una cámara tan potente. Claramente, no está optimizada para utilizarla de forma programática en entornos real-time. Por el contrario, el buffer parece consumir mucho tiempo de la transferencia.
Pero entonces surge una duda ¿Y si los tiempos son lentos por estar utilizando java? Para tratar de falsear esta teoría, recurrimos a código C++ que intente levantar las DLLs directamente y no utilizar minivis.jar. Nuevamente tenemos que decompilar, en este caso decompilamos el archivo minivis.dll y hacemos un matcheo contra lo observado en minivis.jar. Igualmente, debemos notar que ambos archivos se comunican gracias a que se empleó JNI en C++. Lamentablemente esto simplemente complica la tarea de decompilación y reuso de la DLL original.
Con una sola prueba de concepto nos queda claro que ese no es el mejor camino:

Run-Time Check Failure #0 - The value of ESP was not properly saved across a function call.  This is usually a result of calling a function declared with one calling convention with a function pointer declared with a different calling convention

Aquí el código Java:


   1:  class DummyConsumer implements CallbackConsumer {
   2:      public void modeChanged(int paramInt){
   3:      }
   4:      public void statusChanged(int paramInt) {
   5:      }
   6:      public void shutterChanged(boolean paramBoolean, short paramShort) {
   7:      }
   8:  }
   9:   
  10:  public class Application {
  11:      public static void main(String[] arguments) {
  12:          int numberOfImages = Integer.parseInt(arguments[0]);
  13:          
  14:          MinivisFactory factory = MinivisFactory.getInstance();
  15:          try {
  16:              factory.discover();
  17:              TypedNetAddress[] addresses = factory.getKnownDevices();
  18:              String mac = addresses.length > 0 ? addresses[0].getEntryName() : "00-50-C2-1D-7E-AB";
  19:              DummyConsumer callbackConsumer = new DummyConsumer();
  20:              Proxy camera = factory.getCamera(mac, callbackConsumer);
  21:              try {
  22:                  camera.connect();
  23:                  try {
  24:                      takeSnapshots(camera, numberOfImages, debug);
  25:                  }            
  26:                  finally {
  27:                      camera.disconnect();
  28:                  }
  29:              }
  30:              finally {
  31:                  camera.release();
  32:              }
  33:          }
  34:          catch(Exception e) {
  35:              e.printStackTrace();
  36:          }
  37:      }
  38:      private static void takeSnapshots(Proxy camera, int numberOfImages, boolean debug) throws IOException, MDriverException, Exception {
  39:          camera.setMode(4); //0 LIVE 1 RECORD 2 TRIGGERED 3 DRAM 4 LOWLIGHT
  40:          camera.trigger();
  41:          
  42:          //missing code here: wait for trigger to finish
  43:          
  44:          MFrameInfo frameInfo = new MFrameInfo();
  45:          for(int i = 0; i < numberOfImages; ++i) {
  46:              byte[] frameBytes = camera.getFrame(-1, -1  , frameInfo);
  47:          }
  48:      }
  49:  }

Aquí el código C++:


   1:   
   2:  #include "stdafx.h"
   3:  #include <iostream>
   4:   
   5:   
   6:  int _tmain(int argc, _TCHAR* argv[])
   7:  {
   8:      HMODULE hMod = LoadLibrary("minivis.dll");
   9:      typedef long (*MinivisFactory_Create)();
  10:      typedef long (*GetCamera)(long, char*, void*);
  11:      MinivisFactory_Create n_MinivisFactory_Create = (MinivisFactory_Create)GetProcAdress("_Java_com_artho_visart_plugins_minivis_internal_jni_MinivisFactory_n_1MinivisFactory_1Create@8");
  12:      GetCamera n_GetCamera = (GetCamera)GetProcAdress("_Java_com_artho_visart_plugins_minivis_internal_jni_MinivisFactory_n_1GetCamera@24");
  13:      
  14:      
  15:      long adapterPtr = n_MinivisFactory_Create();
  16:      void* consumer = NULL;
  17:      long pointer = n_GetCamera(adapterPtr, "00-50-C2-1D-7E-AB", consumer);
  18:      
  19:      std::cout << "Camera pointer:" << pointer << std::endl;
  20:      std::cout << "Press any key to continue..." << std::endl;
  21:      char character;
  22:      std::cin >> character;
  23:      return 0;
  24:  }