SignalRT

Señal en Tiempo Real...

Primeros Pasos

Mi primer paso fue buscar una librería para utilizar TensorFlow desde C#

Encontré el proyecto TensorFlowSharp.  En el momento de escribir este articulo funciona con TensorFlow 1.9 y con esta librería traté de hacer por mi mismo una versión del ejemplo que incorpora de detección de objectos.

Primeramente cree una librería .NET Standar 2.0 para encapsular la lógica de detección de objetos y el uso de TensorFlow y posteriormente cree una aplicación de consola .NET Core que debía recibir un archivo con una imagen y escribir a disco un archivo con la imagen remarcando los objetos encontrados y mostrando la estimación de precisión sobre cada objeto detectado. Para el tratamiento y escritura de imágenes utilicé OpenCVSharp.

Mi primer problema fué encontrame con que la cantidad de objetos detectados utilizando el mismo modelo que con el ejemplo en python de TensorFlow, la cantidad de objetos detectados es mucho menor.

Por ejemplo utilizando un modelo rápido como ssd_mobilenet_v1_coco_2017_11_17 el resultado fue el siguiente:

No fui capaz de entender el problema, así que busqué en google y encontré un Blog que exponía este mismo problema, pero ninguna solución…

La solución

Después de un día entero de intentos por entender el problema, directamente simplifiqué el código como está en el ejemplo en Python. Hice los siguientes cambios:

Del ejemplo original ImageUtil.cs:

       
        // The inception model takes as input the image described by a Tensor in a very
        // specific normalized format (a particular image size, shape of the input tensor,
        // normalized pixel values etc.).
        //
        // This function constructs a graph of TensorFlow operations which takes as
        // input a JPEG-encoded string and returns a tensor suitable as input to the
        // inception model.
        //
        private static TFGraph ConstructGraphToNormalizeImage( out TFOutput input, 
                                                               out TFOutput output, 
                                                               TFDataType destinationDataType = TFDataType.Float)
        {
            // Some constants specific to the pre-trained model at:
            // https://storage.googleapis.com/download.tensorflow.org/models/inception5h.zip
            //
            // - The model was trained after with images scaled to 224x224 pixels.
            // - The colors, represented as R, G, B in 1-byte each were converted to
            //   float using (value - Mean)/Scale.
            //
            const int w = 224;
            const int h = 224;
            const float mean = 117;
            const float scale = 1;

            var graph = new TFGraph();

            input = graph.Placeholder(TFDataType.String);

            output = graph.Cast(graph.Div(
                x: graph.Sub(
                    x: graph.ResizeBilinear(
                        images: graph.ExpandDims(
                            input: graph.Cast(
                                graph.DecodeJpeg(contents: input, channels: 3), 
                                DstT: TFDataType.Float),
                            dim: graph.Const(0, "make_batch")),
                        size: graph.Const(new int[] { w, h }, "size")),
                    y: graph.Const(mean, "mean")),
                y: graph.Const(scale, "scale")), destinationDataType);

            return graph;

Cambié esta función por esta otra:


        // The inception model takes as input the image described by a Tensor in a very
        // specific normalized format (a particular image size, shape of the input tensor,
        // normalized pixel values etc.).
        //
        // This function constructs a graph of TensorFlow operations which takes as
        // input a JPEG-encoded string and returns a tensor suitable as input to the
        // inception model.
        //
        private static TFGraph ConstructGraphToNormalizeImage( out TFOutput input, 
                                                               out TFOutput output, 
                                                               TFDataType destinationDataType = TFDataType.Float)
        {
            var graph = new TFGraph();

            input = graph.Placeholder(TFDataType.String);

            output = graph.Cast(
                        graph.ExpandDims(
                                input: graph.Cast(graph.DecodeJpeg(contents: input, channels: 3), DstT: TFDataType.Float),
                                dim: graph.Const(0, "make_batch")
                       )
                       , destinationDataType
                    );

            return graph;
        }

Una vez que hice el cambio obtuve mejores resultados y los mismos que con el ejemplo de Python.

 

Siguiente paso

El siguiente paso será intentar hacer lo mismo sobre vídeo capturado de la cámara.

 

Leave comment