miércoles, 25 de abril de 2018

Android Studio con Opencv 3.4.0 ejecutado en C++ (instalación y configuración)


Este tutorial se divide en 2 partes:


1. En la PRIMERA se crea un proyecto de Android Studio para utilizar C++, se configura Opencv 3.4.0 y se llama unas funciones de C++ que devuelven Strings

2. En la SEGUNDA se utiliza el proyecto de la PRIMERA, se configura la cámara para leer video, se llama a una función de procesamiento de Opencv en C++ que recibe y convierte cada cuadro del video a escala de grises.


El detalle es el siguiente:

#######################
PRIMERA PARTE Configurar un proyecto en Android Studio para ejecutar una funcion de C++ que utiliza Opencv
#######################


El siguiente es el tutorial en el que me base, el video puede ayudar a resolver algunas dudas

Tutorial 1. Android Compile OpenCv Native C++
https://www.youtube.com/watch?v=Vp20EdU5qjU




NOTA: Para usuarios de Windows la dirección de carpeta
/home/nombreusuario/    equivale a      C:/users/nombreusuario/




Requiere:

Android Studio instalado en su PC

SDK de Opencv para Android descargado y descomprimido, puede descargar la version 3.4.0 utilizada en este tutorial desde:
https://sourceforge.net/projects/opencvlibrary/files/opencv-android/3.4.0/




CREAR PROYECTO ANDROID para C++


Crear Nuevo Proyecto de Android

- Marcar Include C++ Support

- En Customize C++ support
-- Seleccionar C++11
-- Marcar Exceptions Support
-- Marcar Runtime Type Information Support






IMPORTAR EL MODULO DE OpenCV

Menu File/New/Import Module ...    (1:50 video)


- En source directory, seleccionar de la carpeta donde se descomprime el Opencv la que dice java, ejemplo: /home/nombreusuario/OpenCV-3.4.0-android-sdk/sdk/java

- Seleccionar el Module Name: openCVLibrary3.4.0

- Finish



CONFIGURAR build.gradle de la "app"  y del "modulo"

NOTA: Ambos archivos deben coincidir

Cambiar el modo de visualización del explorador de archivos de android. De Android a Project Files (3:02)

- Seleccionar carpeta app, buscar build.gradle y abrir archivo
- Seleccionar carpeta openCvLibrary340, buscar build.gradle y abrir archivo
- Los valores siguientes deben coincidir en los 2 archivos, sino corregir





######### Esta es la configuración de mis 2 archivos build.gradle
    compileSdkVersion 26
    buildToolsVersion "27.0.3"

    defaultConfig {
        minSdkVersion 19
        targetSdkVersion 26
    }
#########





AGREGAR bibliotecas nativas (libraries), (3:10 video)

- Crear nuevo directorio con el nombre "jniLibs" dentro de /src/main

- Pegar archivos de carpeta de OpenCV Android SDK /home/nombreusuario/OpenCV-3.4.0-android-sdk/sdk/native





CONFIGURAR dependencias del módulo


- Cambiar el modo de visualización del explorador de archivos de android de Project Files a Android (3:55)

- Seleccionar "app" con del botón derecho del mouse seleccionar Open Module Settings

-- Seleccionar pestaña Dependencies y presionar boton con signo + color verde para agregar la dependencia
-- Seleccionar openCVLibrary340







CONFIGURAR CMakeLists.txt para compilar archivos C++ (4:23 video)


- Modificar las rutas correctas de las Carpetas de tu proyecto

- Se anexa el código completo de mi proyecto



# For more information about using CMake with Android Studio, read the
# documentation: https://d.android.com/studio/projects/add-native-code.html

################# Agregado
set(pathToProject /home/nombreusuario/AndroidStudioProjects/NombreProyecto)
set(pathToOpenCv /home/nombreusuario/OpenCV-3.4.0-android-sdk)
#################

# Sets the minimum version of CMake required to build the native library.

cmake_minimum_required(VERSION 3.4.1)

################# Agregado
set (CMAKE_VERBOSE_MAKEFILE on)
set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++11")

include_directories(${pathToOpenCv}/sdk/native/jni/include)
#################


# Creates and names a library, sets it as either STATIC
# or SHARED, and provides the relative paths to its source code.
# You can define multiple libraries, and CMake builds them for you.
# Gradle automatically packages shared libraries with your APK.

add_library( # Sets the name of the library.
             native-lib

             # Sets the library as a shared library.
             SHARED

             # Provides a relative path to your source file(s).
             src/main/cpp/native-lib.cpp )

################# Agregado
add_library( lib_opencv SHARED IMPORTED)

set_target_properties(lib_opencv PROPERTIES IMPORTED_LOCATION ${pathToProject}/app/src/main/jniLibs/${ANDROID_ABI}/libopencv_java3.so)
#################

# Searches for a specified prebuilt library and stores the path as a
# variable. Because CMake includes system libraries in the search path by
# default, you only need to specify the name of the public NDK library
# you want to add. CMake verifies that the library exists before
# completing its build.

find_library( # Sets the name of the path variable.
              log-lib

              # Specifies the name of the NDK library that
              # you want CMake to locate.
              log )

# Specifies libraries CMake should link to your target library. You
# can link multiple libraries, such as libraries you define in this
# build script, prebuilt third-party libraries, or system libraries.

################# Agregado
target_link_libraries( native-lib $\{log-lib} lib_opencv)
#################







AGREGAR librería "opencv_java3" en archivo MainActivity.java dentro de "static"

NOTA: Al crear un proyecto nuevo con soporte para C++ agrega varias líneas por defecto




- Debe quedar de la siguiente manera

    static {
        System.loadLibrary("native-lib"); //native-lib es el archivo C++ native-lib.cpp
        System.loadLibrary("opencv_java3");
    }








MODIFICAR método OnCreate de archivo MainActivity.java

- Debe quedar :

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // Example of a call to a native method
        TextView tv = (TextView) findViewById(R.id.sample_text);
        tv.setText(stringFromJNI());

        if (!OpenCVLoader.initDebug()){
            tv.setText(tv.getText() + "\n OpenCv no Funciona");
        }else{
            tv.setText(tv.getText() + "\n OpenCv Funciona");
            tv.setText(tv.getText() + "\n" + validate());

        }


    }




- Al final de MainActivity.java deben quedar las siguientes llamadas a las funciones de C++




    /**
     * A native method that is implemented by the 'native-lib' native library,
     * which is packaged with this application.
     */
    public native String stringFromJNI(); // Encabezado de  función en C++

    public native String validate(); // Encabezado de  función en C++







EDITAR código de C++, editar y crear funciones

- Dentro de la carpeta app/cpp/native-lib se encuentra el archivo native-lib.cpp que se creo cuando inicio el nuevo proyecto


- Agregar la nueva función validate(), el código del archivo native-lib.cpp debe quedar:


NOTA: En el código siguiente, los nombres de las funciones "_com_apps_jjgarsal" corresponden a la ruta del proyecto en Android y "opencvwithc" es el nombre del proyecto en minúsculas, cambiar por los datos de tu proyecto




#include <jni.h>
#include <string>
#include <opencv2/core.hpp>


extern "C" {
JNIEXPORT jstring

JNICALL
Java_com_apps_jjgarsal_opencvwithc_MainActivity_stringFromJNI(
        JNIEnv *env,
        jobject/* this */) {
    std::string hello = "Hello from C++ En Opencv";
    return env->NewStringUTF(hello.c_str());
}

JNIEXPORT jstring
Java_com_apps_jjgarsal_opencvwithc_MainActivity_validate(
        JNIEnv * env, jobject/* this */) {
    cv::Rect();
    cv::Mat();  // Esta línea crea una matriz con opencv
    std::string hello2 = "Hello from validate";
    return env -> NewStringUTF(hello2.c_str()) ;
}




PROBAR APLICACIÓN


Al ejecutar la aplicación en el celular se mostraran los mensajes "Hello from C++ En Opencv" y   "Hello from validate"









#######################
SEGUNDA PARTE Leer imagen de cámara del dispositivo móvil y convertirla a escala de grises con Android Studio, llamando funciones C++ que utiliza Opencv
#######################


Basado en el tutorial "Using C++ OpenCV code with Android"
http://www.jayrambhia.com/blog/ndk-android-opencv
https://github.com/jayrambhia/nativecodeGray     (código fuente original)



Utilizando el tutorial de la PRIMERA PARTE se agrega la funcionalidad de la cámara, es decir, el proyecto (de la PRIMERA PARTE) ya se encuentra creado y configurado para utilizar Opencv desde C++






CONFIGURANDO AndroidManifest.xml para agregar permisos de uso de la cámara

- Agregar permisos en archivo AndroidManifest.xml




    <uses-permission android:name="android.permission.CAMERA"/>

    <uses-feature android:name="android.hardware.camera" android:required="false"/>
    <uses-feature android:name="android.hardware.camera.autofocus" android:required="false"/>
    <uses-feature android:name="android.hardware.camera.front" android:required="false"/>
    <uses-feature android:name="android.hardware.camera.front.autofocus" android:required="false"/>
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>





EDITAR archivo de layout activity_main.xml

NOTA: "com.apps.jjgarsal.opencvwithc" corresponde a la ruta y nombre del proyecto mio, hay que modificar para que corresponda con el tuyo


- Debe quedar como sigue el archivo activity_main.xml


<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:opencv="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context="com.apps.jjgarsal.opencvwithc.MainActivity">



        <TextView
            android:id="@+id/sample_text"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Hello World!" />


    <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="horizontal">

        <org.opencv.android.JavaCameraView
            android:id="@+id/opencv_part_native_surface_view"
            android:layout_width="fill_parent"
            android:layout_height="fill_parent"
            android:visibility="gone"
            opencv:camera_id="any"
            opencv:show_fps="true" />

        <org.opencv.android.JavaCameraView
            android:id="@+id/opencv_part_java_surface_view"
            android:layout_width="fill_parent"
            android:layout_height="fill_parent"
            android:visibility="gone"
            opencv:camera_id="any"
            opencv:show_fps="true" />
    </LinearLayout>
</LinearLayout>






EDITAR native-lib.cpp para agregar funciones C++ que convertirán la imagen recibida de la cámara del Celular y la devolverán en escala de grises


- El código de native-lib.cpp debe quedar:

NOTA: En el código siguiente, los nombres de las funciones "_com_apps_jjgarsal" corresponden a la ruta del proyecto en Android y "opencvwithc" es el nombre del proyecto en minúsculas





#include <jni.h>
#include <string>
#include <opencv2/core.hpp>

// Encabezados para trabajar con funciones de procesamiento de imágenes
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>


extern "C" {
JNIEXPORT jstring

JNICALL
Java_com_apps_jjgarsal_opencvwithc_MainActivity_stringFromJNI(
        JNIEnv *env,
        jobject/* this */) {
    std::string hello = "Hello from C++ En Opencv";
    return env->NewStringUTF(hello.c_str());
}

JNIEXPORT jstring
Java_com_apps_jjgarsal_opencvwithc_MainActivity_validate(
        JNIEnv * env, jobject/* this */) {
    cv::Rect();
    cv::Mat();  // Esta línea crea una matriz con opencv

    std::string hello2 = "Hello from validate";
    return env -> NewStringUTF(hello2.c_str()) ;
}

//############ Codigo para uso de cámara
using namespace std;
using namespace cv;

int toGray(Mat img, Mat& gray);

extern "C" {

JNIEXPORT jint JNICALL Java_com_apps_jjgarsal_opencvwithc_MainActivity_convertNativeGray(JNIEnv*, jobject, jlong addrRgba, jlong addrGray);

JNIEXPORT jint JNICALL Java_com_apps_jjgarsal_opencvwithc_MainActivity_convertNativeGray(JNIEnv*, jobject, jlong addrRgba, jlong addrGray) {

    Mat& mRgb = *(Mat*)addrRgba;
    Mat& mGray = *(Mat*)addrGray;

    int conv;
    jint retVal;

    conv = toGray(mRgb, mGray);
    retVal = (jint)conv;

    return retVal;

}

}

int toGray(Mat img, Mat& gray)
{
    cvtColor(img, gray, CV_RGBA2GRAY); // Assuming RGBA input

    if (gray.rows == img.rows && gray.cols == img.cols)
    {
        return (1);
    }
    return(0);
}

}








EDITAR código de MainActivity.java para uso de la cámara y conversión de la imagen de entrada a escala de grises, los cambios se añadieron al código del archivo de la primera parte de este tutorial

- El código debe quedar:



/**
 * Uso de Opencv con C++ en Android Studio
 *
 * Autor: Juan José Garza Saldaña
 * Basado en los siguientes tutoriales:
 *
 * 1.Android Compile OpenCv Native C++  (Video de Youtube)
 * https://www.youtube.com/watch?v=Vp20EdU5qjU
 *
 * 2. Using C++ OpenCV code with Android
 * http://www.jayrambhia.com/blog/ndk-android-opencv
 * https://github.com/jayrambhia/nativecodeGray     (código fuente original)
 *
 * Notas: Con relación a los tutoriales originales
 *  org.opencv.android.NativeCameraView      //No existe en opencv3 (versiones mas nuevas)
 *
 *
 */

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.TextView;


// ############# Agregado para trabajo con cámara

import org.opencv.android.BaseLoaderCallback;
import org.opencv.android.CameraBridgeViewBase;
import org.opencv.android.CameraBridgeViewBase.CvCameraViewFrame;
import org.opencv.android.CameraBridgeViewBase.CvCameraViewListener2;
import org.opencv.android.LoaderCallbackInterface;
import org.opencv.android.OpenCVLoader;


import org.opencv.core.*;

import android.util.Log;
import android.view.SurfaceView;
import android.view.WindowManager;
// #############


public class MainActivity extends AppCompatActivity implements CvCameraViewListener2 {
// ############# Agregado para trabajo con cámara

    private Mat mRgba;
    private Mat mGray;

    private CameraBridgeViewBase mOpenCvCameraView;

    private static final String TAG = "OCVSample::NDK";

// #############

    // Used to load the 'native-lib' library on application startup.
    static {
        System.loadLibrary("native-lib"); //native-lib es el archivo C++ native-lib.cpp
        System.loadLibrary("opencv_java3");
    }


// ############# Agregado para cámara

/* BaseLoaderCallback instala opencvManager desde Google Play sino esta instalado y si hay Internet,
sustituye al código de static { } de líneas arriba aquí deberían ser cargadas las librerías
*/

    private BaseLoaderCallback mLoaderCallback = new BaseLoaderCallback(this) {
        @Override
        public void onManagerConnected(int status) {
            switch (status) {
                case LoaderCallbackInterface.SUCCESS:
                {
//                    System.loadLibrary("native-lib"); //native-lib es el archivo C++ native-lib.cpp
//                    System.loadLibrary("opencv_java3");

                    Log.i(TAG, "OpenCV loaded successfully");
                    mOpenCvCameraView.enableView();
                } break;
                default:
                {
                    super.onManagerConnected(status);
                } break;
            }
        }
    };

// #############


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // Example of a call to a native method
        TextView tv = (TextView) findViewById(R.id.sample_text);
        tv.setText(stringFromJNI());

        if (!OpenCVLoader.initDebug()){
            tv.setText(tv.getText() + "\n OpenCv no Funciona");
        }else{
            tv.setText(tv.getText() + "\n OpenCv Funciona");
            tv.setText(tv.getText() + "\n" + validate());

        }

// ################## Agregado para cámara
        Log.i(TAG, "called onCreate");

        getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
        mOpenCvCameraView = (CameraBridgeViewBase) findViewById(R.id.opencv_part_java_surface_view);
        mOpenCvCameraView.setVisibility(SurfaceView.VISIBLE);
        mOpenCvCameraView.setCvCameraViewListener(this);
// ##################

    }

// ################## Agregado para cámara

    @Override
    public void onPause()
    {
        super.onPause();
        if (mOpenCvCameraView != null)
            mOpenCvCameraView.disableView();
    }

    @Override
    public void onResume()
    {
        super.onResume();
//        El tutorial original utiliza la version 2.4.8 de Opencv     
//        OpenCVLoader.initAsync(OpenCVLoader.OPENCV_VERSION_2_4_8, this, mLoaderCallback);
        OpenCVLoader.initAsync(OpenCVLoader.OPENCV_VERSION_3_4_0, this, mLoaderCallback);
    }

    public void onDestroy() {
        super.onDestroy();
        if (mOpenCvCameraView != null)
            mOpenCvCameraView.disableView();
    }

    public void onCameraViewStarted(int width, int height) {
        mRgba = new Mat();
        mGray = new Mat();
    }

    public void onCameraViewStopped() {
    }

    public Mat onCameraFrame(CvCameraViewFrame inputFrame) {
        mRgba = inputFrame.rgba(); // Frame de la cámara guardada en matriz
     
        // Envía el frame a la función de conversion convertNativeGray y obtiene la matriz
        // con la imagen en escala de grises mGray
        convertNativeGray(mRgba.getNativeObjAddr(), mGray.getNativeObjAddr());
        return mGray;
    }

// ##################


    /**
     * A native method that is implemented by the 'native-lib' native library,
     * which is packaged with this application.
     */
    public native String stringFromJNI(); //Encabezado de  función en C++

    public native String validate(); //Encabezado de  función en C++

    //Encabezado de  función en C++, función para convertir imagen a escala de grises
    public native int convertNativeGray(long matAddrRgba, long matAddrGray);
 
}




PROBAR LA APLICACIÓN


NOTA: Celulares con Android de versiones recientes requieren que al instalar la aplicación, se les configuren como activados  los permisos de cámara desde la configuración del dispositivo móvil. Este código no los hábilita automáticamente.


Al ejecutar mostrará los mensajes de la PRIMERA PARTE y mostrará una visualización de la cámara, originalmente debería mostrarse en color, pero debido al procesamiento de la SEGUNDA PARTE cada frame del video se muestra en escala de grises



viernes, 20 de abril de 2018

INSTALAR OPENCV 3.4.0 EN UBUNTU 16.04 PARA JAVA, C++ y PYTHON

Estos son los pasos que he recopilado de otros tutoriales para compilar e instalar Opencv 3.4.0, para utilizarse en JAVA, C++ y Python para el sistema operativo Ubuntu 16.04 LTS.

Probablemente para versiones subsecuentes funcione de manera similar, únicamente descargando el paquete correspondiente.



### Actualizar Ubuntu

sudo apt-get update
sudo apt-get upgrade


### NOTA PARA INSTALAR OPENCV, SOLAMENTE EN PYTHON, VE "AL FINAL"


##############################
### Para JAVA instalar antes ant y JDK
##############################
sudo apt-get install ant
sudo apt-get install default-jdk


### Debes configurar JAVA_PATH$ el link siguiente te puede ayudar
https://www.cyberciti.biz/faq/linux-unix-set-java_home-path-variable/


##############################
### Para C/C++ instalar dependencias
##############################

sudo apt-get install build-essential cmake git libgtk2.0-dev pkg-config libavcodec-dev libavformat-dev libswscale-dev

sudo apt-get install python3.5-dev python3-numpy libtbb2 libtbb-dev

sudo apt-get install libjpeg-dev libpng-dev libtiff5-dev libjasper-dev libdc1394-22-dev libeigen3-dev libtheora-dev libvorbis-dev libxvidcore-dev libx264-dev sphinx-common libtbb-dev yasm libfaac-dev libopencore-amrnb-dev libopencore-amrwb-dev libopenexr-dev libgstreamer-plugins-base1.0-dev libavutil-dev libavfilter-dev libavresample-dev


##############################
### DESCARGANDO FUENTES Y COMPILANDO
##############################

cd ~/Descargas

### Descargar OpenCV version 3.4.0

wget https://github.com/opencv/opencv/archive/3.4.0.zip -O opencv-3.4.0.zip
wget https://github.com/opencv/opencv_contrib/archive/3.4.0.zip -O opencv_contrib-3.4.0.zip

### Descomprimir archivos descargados
unzip opencv-3.4.0.zip
unzip opencv_contrib-3.4.0.zip


### Crear carpeta build en directorio descomprimido
cd opencv-3.4.0/
mkdir build
cd build/


### Crear archivo de configuración
cmake -D BUILD_SHARED_LIBS=OFF ..


### Si todo esta bien configurado para Java debe mostrar algo "parecido"
### al final de la ejecución de la línea anterior
--   Java:
--     ant:                         /usr/bin/ant (ver 1.9.6)
--     JNI:                         /usr/lib/jvm/java-8-oracle/include /usr/lib/jvm/java-8-oracle/include/linux /usr/lib/jvm/java-8-oracle/include
--     Java tests:                  YES


### Generando .jar y programas de instalación
make -j8


##############################
# PROBANDO GENERACION DE .JAR PARA JAVA
##############################

### Hasta este punto ya encontrarás creado el archivo opencv-340.jar
### para configurar Eclipse para java en directorio ~/Descargas/opencv-3.4.0/build
### Prueba el punto 3 de la siguiente liga:
### https://advancedweb.hu/2016/03/01/opencv_ubuntu/



### Instalación de Opencv para C/C++ en directorio /usr/local
sudo make install

### Ten paciencia puede tardar varios minutos ..... dependiendo de tu PC


### Reinicializa bibliotecas estáticas usando
sudo ldconfig


##############################
# PROBANDO INSTALACION EN C++
##############################
### Probando con la creación de un programa en C++ dentro de cpp_test

cd ~
mkdir cpp_test
cd cpp_test
touch main.cpp

### Copia un archivo de image con en nombre sample.jpeg en el directorio cpp_test

### Abre main.cpp
gedit main.cpp

### y agrega el siguiente código

#include <opencv2/highgui.hpp>
#include <iostream>

int main( int argc, char** argv ) {
 
  cv::Mat image;
  image = cv::imread("sample.jpeg" , CV_LOAD_IMAGE_COLOR);
 
  if(! image.data ) {
      std::cout <<  "Could not open or find the image" << std::endl ;
      return -1;
    }
 
  cv::namedWindow( "Display window", cv::WINDOW_AUTOSIZE );
  cv::imshow( "Display window", image );
 
  cv::waitKey(0);
  return 0;
}



### Compila el codigo con el comando siguiente
g++ main.cpp -o output `pkg-config --cflags --libs opencv`

### Ejecuta el programa con
 ./output

### Debe mostrar la imagen en una ventana

### presiona Esc para salir


##############################
### INSTALAR OPENCV 3.4.0 EN PYTHON CON UBUNTU 16.04
##############################

### Puedes instalar opencv usando el paquete oficial pre-construido de los fuentes

### Para instalar opencv únicamente

pip install opencv-python

### Para agregar el paquete contrib

pip install opencv-contrib-python

### Verifica la instalación de opencv entrando a la terminal con
python3.5

### Teclea en la terminal de python
import cv2
cv2.__version__

### Te debe mostrar la versión instalada de opencv


### Si deseas compilar tu mismo los fuentes puedes visitar:
### http://www.python36.com/how-to-install-opencv340-on-ubuntu1604/




##############################
### La información fue recopilada de los siguientes tutoriales
### ¡¡¡Gracias a todos ellos!!!
##############################

Getting started with OpenCV for Java on Ubuntu
https://advancedweb.hu/2016/03/01/opencv_ubuntu/

How to Install OpenCV in Ubuntu 16.04 LTS for C / C++
http://www.codebind.com/cpp-tutorial/install-opencv-ubuntu-cpp/

How to install OpenCV 3.4.0 on Ubuntu 16.04
http://www.python36.com/how-to-install-opencv340-on-ubuntu1604/



Proyectos 2024. Desarrollo de Aplicaciones Móviles. UAT-FIC

Profesor: Dr. Juan José Garza Saldaña   Facultad de Ingeniería y Ciencias Universidad Autónoma de Tamaulipas Los estudiantes de la materia d...