class GeneradorEvidencia():
    def __init__(self, carpetaReporte, mifps=10, guardoRecortados=True):
        self.miReporte = MiReporte(levelLogging=logging.DEBUG, nombre=__name__)
        self.carpetaDeReporteActual = carpetaReporte
        self.framesPorSegundoEnVideo = mifps
        self.ventana = 5
        self.height, self.width = 240, 320
        self.guardoRecortados = guardoRecortados
        self.dicts_by_name = defaultdict(list)
        self.fourcc = cv2.VideoWriter_fourcc(*'XVID')

    def nuevoDia(self, carpetaReporte):
        self.carpetaDeReporteActual = carpetaReporte
        self.miReporte.setDirectory(directorioDeReporte)

    def inicializarEnCarpeta(self, carpetaReporte):
        self.carpetaDeReporteActual = carpetaReporte

    def generarVideo(self,
                     informacionTotal,
                     nombreInfraccion,
                     directorioActual,
                     nombreFrame,
                     inicio,
                     final,
                     observacion=''):
        aEntregar = cv2.VideoWriter(
            directorioActual + '/' + nombreInfraccion + '_' + nombreFrame +
            '_' + observacion + '.avi', self.fourcc,
            self.framesPorSegundoEnVideo, (self.width, self.height))
        excepcion = ''
        for indiceVideo in range(inicio, final):
            conteoErrores = 0
            try:
                aEntregar.write(informacionTotal[indiceVideo][nombreFrame])
            except Exception as e:
                conteoErrores += 1
                excepcion = e
        if conteoErrores >= 1:
            self.miReporte.warning('Tuve ' + str(conteoErrores) +
                                   ' missing al generar el video ' +
                                   str(excepcion))

        aEntregar.release()
        self.miReporte.info('\t\t' + 'Generada infr de: ' + nombreInfraccion +
                            ' de ' + str(inicio) + ' a ' + str(final))

    def generarReporteEnVideoDe(self,
                                informacionTotal,
                                infraccion,
                                debug=False):
        nombreInfraccion = infraccion['name']
        estado = infraccion['infraccion']
        directorioActual = self.carpetaDeReporteActual + '/' + nombreInfraccion
        if not os.path.exists(directorioActual):
            os.makedirs(directorioActual)
        inicio = infraccion['frameInicial'] - self.ventana
        final = infraccion['frameFinal'] + self.ventana
        self.generarVideo(informacionTotal, estado + '_' + infraccion['name'],
                          directorioActual, 'video', inicio, final,
                          infraccion['observacion'])
        #if debug:
        #	self.generarVideo(informacionTotal,infraccion['name'],directorioActual,'debug',inicio,final,infraccion['observacion'])

    def generarVideoDebugParaPruebas(self, informacionTotal):
        nombreInfraccion = datetime.datetime.now().strftime(
            '%Y-%m-%d_%H-%M-%S')
        directorioActual = self.carpetaDeReporteActual + '/' + nombreInfraccion
        if not os.path.exists(directorioActual):
            os.makedirs(directorioActual)
        inicio = min(informacionTotal)
        final = max(informacionTotal)
        self.generarVideo(informacionTotal, nombreInfraccion, directorioActual,
                          'video', inicio, final, 'debug')

    def generarReporteInfraccion(self,
                                 informacionTotal,
                                 infraccion=True,
                                 numero=0,
                                 debug=False):
        generandoDebugGlobal = False
        generarDobleVideo = debug
        try:
            nombreInfraccion = infraccion['name']
            generandoDebugGlobal = False
        except:
            nombreInfraccion = datetime.datetime.now().strftime(
                '%Y-%m-%d_%H-%M-%S') + '_{}i'.format(numero)
            if (numero == 0) & (len(informacionTotal) < 20):
                return 0
            generandoDebugGlobal = True

        directorioActual = self.carpetaDeReporteActual + '/' + nombreInfraccion

        if not os.path.exists(directorioActual):
            os.makedirs(directorioActual)

        if generandoDebugGlobal == False:
            frameInferior = infraccion['frameInicial'] - self.ventana
            frameSuperior = infraccion['frameFinal'] + self.ventana
            if generarDobleVideo:
                prueba = cv2.VideoWriter(
                    directorioActual + '/' + nombreInfraccion + '_debug.avi',
                    self.fourcc, self.framesPorSegundoEnVideo,
                    (self.width, self.height))
            entrega = cv2.VideoWriter(
                directorioActual + '/' + nombreInfraccion + '.avi',
                self.fourcc, self.framesPorSegundoEnVideo,
                (self.width, self.height))

            # Check valid frame
            if frameInferior < 1:
                inicio = 1
            else:
                inicio = frameInferior

            if frameSuperior > len(informacionTotal):
                final = len(informacionTotal)
            else:
                final = frameSuperior
            self.miReporte.info('\t\t' + 'Generada infr de: ' +
                                nombreInfraccion + ' de ' + str(inicio) +
                                ' a ' + str(final))
            if self.guardoRecortados:
                directorioRecorte = directorioActual + '/recorte'
                if not os.path.exists(directorioRecorte):
                    os.makedirs(directorioRecorte)
            for indiceVideo in range(inicio, final):
                if generarDobleVideo:
                    prueba.write(informacionTotal[indiceVideo]['debug'])
                entrega.write(informacionTotal[indiceVideo]['video'])
            if generarDobleVideo:
                prueba.release()
            entrega.release()

            # Vuelvo a iterar por la imagen mas grande:
            return 1
        else:
            prueba = cv2.VideoWriter(
                directorioActual + '/' + nombreInfraccion + '.avi',
                self.fourcc, self.framesPorSegundoEnVideo,
                (self.width, self.height))
            inicio = min(informacionTotal)
            final = max(informacionTotal)
            self.miReporte.info('Generado DEBUG de: ' + nombreInfraccion +
                                ' de ' + str(inicio) + ' ' + str(final) +
                                ' total lista: ' + str(len(informacionTotal)))
            for indiceVideo in range(inicio, final):
                try:
                    prueba.write(informacionTotal[indiceVideo]['debug'])
                except:
                    self.miReporte.error('No pude guardar frame: ' +
                                         str(indiceVideo))
            prueba.release()
            return 0
class PoliciaInfractor():
    """
	Entre las principales funciones de esta clase esta traducir la información de los frames entrantes a información mas manipulable y valiosa para un ser humano o para una inteligencia artificial
	Tambien se encarga de crear los objetos Vehiculos que pueden estar en estado cruce o infraccion
	También se encarga de proveer el estado actual del infractor
	"""
    def __init__(self,
                 imagenParaInicializar,
                 poligonoPartida,
                 poligonoLlegada,
                 poligonoDerecha,
                 poligonoIzquierda,
                 mifps=8,
                 directorioDeReporte=os.getenv('HOME') + '/' +
                 datetime.datetime.now().strftime('%Y-%m-%d') + '_reporte',
                 debug=False,
                 flujoAntiguo=False,
                 anguloCarril=0):
        # Tomo la imagen de inicialización y obtengo algunas caracteristicas de la misma
        self.directorioDeReporte = directorioDeReporte
        self.miReporte = MiReporte(levelLogging=logging.DEBUG, nombre=__name__)
        self.miGrabadora = GeneradorEvidencia(self.directorioDeReporte, mifps,
                                              False)
        self.reportarDebug = debug
        self.minimosFramesVideoNormalDebug = 1 * mifps  # minimo 1 segundos de debug

        # Se cargan las variables de creación de la clase
        self.imagenAuxiliar = cv2.cvtColor(imagenParaInicializar,
                                           cv2.COLOR_BGR2GRAY)
        self.areaDeResguardo = np.array(poligonoPartida)
        self.areaDeConfirmacion = np.array(poligonoLlegada)
        self.areaDeGiroDerecha = np.array(poligonoDerecha)
        self.areaDeGiroIzquierda = np.array(poligonoIzquierda)
        self.anguloCarril = -anguloCarril
        self.desplazamiento = np.array(
            [8 * math.cos(self.anguloCarril), 8 * math.sin(self.anguloCarril)])

        # CONDICIONES DE DESCARTE
        # En si un punto sale del carril valido (ensanchado debidamente) se descarta el punto individual

        self.carrilValido = self.generarCarrilValido(poligonoPartida,
                                                     poligonoLlegada,
                                                     poligonoDerecha,
                                                     poligonoIzquierda)
        self.maximoNumeroFramesParaDescarte = 80
        self.numeroDePuntosASeguirDeInicializacion = 4

        # la linea de referencia para tamanio sera del largo del paso de cebra, su longitud servira para descartar puntos que se alejen del resto
        self.maximaDistanciaEntrePuntos = self.tamanoVector(
            np.array(poligonoPartida[0]) - np.array(poligonoPartida[1]))

        # Se crea la clase correspondiente
        self.miFiltro = AnalisisOnda()
        self.angulo = 18

        # La linea de pintado LK y trasera son los puntos del paso de cebra
        self.lineaDePintadoLK = np.array(
            [poligonoPartida[0], poligonoPartida[3]])
        self.lineaTraseraLK = np.array(
            [poligonoPartida[1], poligonoPartida[2]])

        ditanciaEnX = self.lineaDePintadoLK[1][0] - self.lineaDePintadoLK[0][0]
        ditanciaEnY = self.lineaDePintadoLK[1][1] - self.lineaDePintadoLK[0][1]
        vectorParalelo = self.lineaDePintadoLK[1] - self.lineaDePintadoLK[0]
        self.vectorParaleloUnitario = (
            vectorParalelo) / self.tamanoVector(vectorParalelo)
        self.vectorPerpendicularUnitario = np.array(
            [self.vectorParaleloUnitario[1], -self.vectorParaleloUnitario[0]])
        self.numeroDePuntos = 15
        self.flujoAntiguo = False
        if flujoAntiguo == True:
            self.flujoAntiguo = True
            self.numeroDePuntos = 9
        self.stepX = ditanciaEnX / self.numeroDePuntos
        self.stepY = ditanciaEnY / self.numeroDePuntos
        self.lk_params = dict(winSize=(15, 15),
                              maxLevel=7,
                              criteria=(cv2.TERM_CRITERIA_EPS
                                        | cv2.TERM_CRITERIA_COUNT, 10, 0.03))

        self.lineaFijaDelantera = np.zeros((self.numeroDePuntos + 1, 1, 2))
        self.lineaDeResguardoDelantera = self.crearLineaDeResguardo()
        self.lineaDeResguardoAlteradaDelantera = self.lineaDeResguardoDelantera

        # Se inicializa la perspectiva con 1/9 del largo del paso de cebra como ancho

        self.areaFlujo = self.obtenerRegionFlujo(self.areaDeResguardo)
        self.miPerspectiva = Perspective(self.areaFlujo)
        self.anteriorFranja = self.miPerspectiva.transformarAMitad(
            self.imagenAuxiliar)
        self.optimalStep = 2

        self.listaVehiculos = []
        """
		Estados de vehiculos
		1. Previo
		2. Cruce
		3. Giro
		4. Ruido
		"""
        self.reestablecerEstado()

        if os.uname()[1] == 'raspberrypi':
            self.camaraAlta = ControladorCamara()

    def obtenerRegionFlujo(self, npArrayDePartida):
        punto01 = npArrayDePartida[0]
        punto02 = npArrayDePartida[1]
        punto03 = npArrayDePartida[2]
        punto04 = npArrayDePartida[3]
        unitario12 = (punto02 - punto01) / self.tamanoVector(punto02 - punto01)
        unitario43 = (punto03 - punto04) / self.tamanoVector(punto03 - punto04)
        largoPasoCebra = self.tamanoVector(punto04 - punto01)
        punto02 = punto01 + largoPasoCebra / 3 * unitario12
        punto03 = punto04 + largoPasoCebra / 3 * unitario43

        return np.array([punto01, punto02, punto03, punto04]).astype(int)

    def nuevoDia(self, directorioDeReporte):
        self.directorioDeReporte = directorioDeReporte
        self.miReporte.setDirectory(directorioDeReporte)
        self.miGrabadora.nuevoDia()
        self.reestablecerEstado()

    def generarCarrilValido(self, poligonoPartida, poligonoLlegada,
                            poligonoDerecha, poligonoIzquierda):
        # Input type: self.carrilValido = np.array([poligonoPartida[0],poligonoPartida[1],poligonoPartida[2],poligonoPartida[3],poligonoLlegada[2],poligonoLlegada[3],poligonoLlegada[0],poligonoLlegada[1]])
        # Se modifican los puntos
        # partida: 0-,3+
        # llegada: 1+,2-
        # La matriz de rotacion por un angulo de 15 grados
        carrilValido = np.array([
            poligonoPartida[0], poligonoPartida[1], poligonoPartida[2],
            poligonoPartida[3], poligonoLlegada[2], poligonoLlegada[3],
            poligonoLlegada[0], poligonoLlegada[1]
        ])
        self.angulo = 14
        cos15 = math.cos(self.angulo * math.pi / 180)
        sin15 = math.sin(self.angulo * math.pi / 180)
        rotacionNegativa = np.array([[cos15, sin15], [-sin15, cos15]])
        rotacionPositiva = np.array([[cos15, -sin15], [sin15, cos15]])
        llegada1_p7 = carrilValido[6] + rotacionPositiva.dot(carrilValido[7] -
                                                             carrilValido[6])
        llegada1_p4 = carrilValido[5] + rotacionNegativa.dot(carrilValido[4] -
                                                             carrilValido[5])

        llegada1_p0 = carrilValido[1] + rotacionNegativa.dot(carrilValido[0] -
                                                             carrilValido[1])
        llegada1_p3 = carrilValido[2] + rotacionPositiva.dot(carrilValido[3] -
                                                             carrilValido[2])

        carrilValido[0] = llegada1_p0
        carrilValido[3] = llegada1_p3
        carrilValido[4] = llegada1_p4
        carrilValido[7] = llegada1_p7

        # Hasta este punto se obtiene el carril valido simplemente ensanchado
        # Ahora concatenamos con los puntos de llegada a Derecha e Izquierda

        carrilValido = np.array([
            carrilValido[0], carrilValido[1], carrilValido[2], carrilValido[3],
            poligonoDerecha[2], poligonoDerecha[3], poligonoDerecha[0],
            poligonoDerecha[1], carrilValido[4], carrilValido[5],
            carrilValido[6], carrilValido[7], poligonoIzquierda[2],
            poligonoIzquierda[3], poligonoIzquierda[0], poligonoIzquierda[1]
        ])

        return carrilValido

    def tamanoVector(self, vector):
        # Metodo auxiliar por la recurencia de esta aplicacion
        return math.sqrt(vector[0]**2 + vector[1]**2)

    def puntoEstaEnRectangulo(self, punto, rectangulo):
        # Metodo auxiliar por la recurencia de esta aplicacion
        estadoARetornar = False
        if (punto[0] >
                rectangulo[0]) & (punto[0] < rectangulo[0] + rectangulo[2]) & (
                    punto[1] > rectangulo[1]) & (
                        punto[1] < rectangulo[1] + rectangulo[3]):
            estadoARetornar = True
        return estadoARetornar

    # No longer needed
    def inicializarAgente(self, ):
        """
		Resets the starting line to get ready to the next frame
		"""
        del self.listaVehiculos
        self.listaVehiculos = []

    def crearLineaDeResguardo(self):
        """
		La linea de resguardo es una linea preparada para entrar en el algoritmo de lucas Kanade y controlar el flujo o seguir objetos que crucen la zona de partida
		"""
        lineaAuxiliar = np.array([[self.lineaDePintadoLK[0]]])
        for numeroDePunto in range(1, self.numeroDePuntos + 1):
            lineaAuxiliar = np.append(lineaAuxiliar, [[
                self.lineaDePintadoLK[0] +
                numeroDePunto * np.array([self.stepX, self.stepY])
            ]],
                                      axis=0)
        self.lineaFijaDelantera = lineaAuxiliar
        self.lineaFijaDelantera = np.array(self.lineaFijaDelantera,
                                           dtype=np.float32)
        return lineaAuxiliar

    def seguirImagen(self,
                     numeroDeFrame,
                     imagenActual,
                     informacion=False,
                     colorSemaforo=1):
        """
		Metodo mas importante del infractor: Se encarga de:
		1. Crear vehiculos de ser necesario
		2. Seguir vehiculos
		3. Confirmar o descartar vehículos
		"""
        #print('>> 01 Inicio Seguir')
        if colorSemaforo == 1:
            self.estadoActual['colorSemaforo'] = 'Rojo'
        elif colorSemaforo == 0:
            self.estadoActual['colorSemaforo'] = 'Verde'
        elif colorSemaforo == 2:
            self.estadoActual['colorSemaforo'] = 'Amarillo'
        else:
            self.estadoActual['colorSemaforo'] = 'No hay semaforo'

        imagenActualEnGris = cv2.cvtColor(imagenActual, cv2.COLOR_BGR2GRAY)

        arrayAuxiliarParaVelocidad, activo, err = cv2.calcOpticalFlowPyrLK(
            self.imagenAuxiliar, imagenActualEnGris, self.lineaFijaDelantera,
            None, **self.lk_params)
        self.lineaDeResguardoAlteradaDelantera = arrayAuxiliarParaVelocidad

        if self.flujoAntiguo:
            velocidadEnBruto = self.obtenerMagnitudMovimientoEnRegion(
                self.miPerspectiva.transformarAMitad(imagenActualEnGris))
        else:
            velocidadEnBruto = self.obtenerMagnitudMovimiento(
                self.lineaFijaDelantera,
                self.lineaDeResguardoAlteradaDelantera)
        velocidadFiltrada, pulsoVehiculos = self.miFiltro.obtenerOndaFiltrada(
            velocidadEnBruto)

        # Se evoluciona el resto de vehiculos solo si son 'Previo'
        for infraccion in self.listaVehiculos:
            # Si es candidato evoluciona:
            if infraccion['estado'] == 'Previo':
                if (infraccion['infraccion'] == 'candidato') & (colorSemaforo
                                                                == 0):
                    #infraccion['observacion'] = 'LlegoEnVerde'
                    self.miReporte.info(
                        'Infraccion Descartada Por Llegar En Verde')
                    infraccion['infraccion'] = ''
                # Al principio descarto los puntos negativos o en los bordes (0,0), -(x,y)
                nuevaPosicionVehiculo, activo, err = cv2.calcOpticalFlowPyrLK(
                    self.imagenAuxiliar, imagenActualEnGris,
                    infraccion['desplazamiento'], None, **self.lk_params)

                # Si ya no hay puntos que seguir el anterior retorna NoneType, se determina como Giro,
                NoneType = type(None)
                if type(nuevaPosicionVehiculo) == NoneType:
                    infraccion['estado'] = 'Salio'
                    if infraccion['infraccion'] == 'candidato':
                        infraccion['infraccion'] = ''
                        self.eliminoCarpetaDeSerNecesario(infraccion)
                        # VALIDO SOLO PARA GIRO CONTROLADO POR SEMAFORO A PARTE
                    self.estadoActual['salio'] += 1
                    break
                # DESCARTE INDIVIDUAL POR PUNTO
                # Se descarta puntos individualmente, si un punto esta en el borde del frame o fuera de el entonces se lo mantiene congelado
                # PELIGRO, los frames congelados estan ingresando al calcOpticalFlow arriba, revisar
                # for otroIndice in range(len(infraccion['desplazamiento'])):
                #	controlVector = infraccion['desplazamiento'][otroIndice]
                #	if not self.puntoEstaEnRectangulo((controlVector[0][0],controlVector[0][1]),(0,0,320,240)):
                #		nuevaPosicionVehiculo[otroIndice] = infraccion['desplazamiento'][otroIndice]
                # DESCARTE POR TIEMPO, POR VEHICULO
                if (numeroDeFrame - infraccion['frameInicial']
                    ) > self.maximoNumeroFramesParaDescarte:
                    infraccion['estado'] = 'TimeOut'
                    infraccion['infraccion'] = ''
                    self.estadoActual['ruido'] += 1
                    self.eliminoCarpetaDeSerNecesario(infraccion)
                    break
                # Si es candidato y algun punto llego al final se confirma
                indicesValidos = []
                puntosQueLlegaron = 0
                puntosQueGiraronDerecha = 0
                puntosQueGiraronIzquierda = 0

                for indiceVector in range(len(nuevaPosicionVehiculo)):
                    # Para cada indice
                    vector = nuevaPosicionVehiculo[indiceVector]

                    xTest, yTest = vector[0][0], vector[0][1]
                    # hago una lista de los indices que aun son validos
                    if cv2.pointPolygonTest(
                            self.carrilValido, (xTest, yTest), True
                    ) >= 0:  # Si esta dentro del carril valido se mantiene el punto
                        indicesValidos.append(indiceVector)
                    # Confirmo la llegada de uno
                    if cv2.pointPolygonTest(
                            self.areaDeConfirmacion, (xTest, yTest), True
                    ) >= 0:  # Si esta dentro del espacio de llegada se confirma
                        puntosQueLlegaron += 1
                    if cv2.pointPolygonTest(
                            self.areaDeGiroDerecha, (xTest, yTest), True
                    ) >= 0:  # Si esta dentro del espacio de llegada se confirma
                        puntosQueGiraronDerecha += 1
                    if cv2.pointPolygonTest(
                            self.areaDeGiroIzquierda, (xTest, yTest), True
                    ) >= 0:  # Si esta dentro del espacio de llegada se confirma
                        puntosQueGiraronIzquierda += 1

                    if puntosQueLlegaron >= 2:
                        # Si llego al otro extremo, entonces cruzo:
                        infraccion['estado'] = 'Cruzo'
                        self.estadoActual['cruzo'] += 1
                        infraccion['frameFinal'] = numeroDeFrame
                        # Si era candidato y esta llegando en rojo o amarillo
                        # ESTO DESCARTA LAS LLEGADAS EN VERDE # Anulado por mala intensión
                        if (infraccion['infraccion'] == 'candidato'
                            ):  #&(colorSemaforo>=1):
                            infraccion['infraccion'] = 'CAPTURADO'
                            self.estadoActual['infraccion'] += 1

                        self.miReporte.info(infraccion['infraccion'] + '\t' +
                                            infraccion['estado'] +
                                            ' a horas ' + infraccion['name'] +
                                            ' (' +
                                            str(infraccion['frameInicial']) +
                                            '-' +
                                            str(infraccion['frameFinal']) +
                                            ')')
                        break

                    if puntosQueGiraronDerecha >= 2:
                        # Si llego al otro extremo, entonces cruzo:
                        infraccion['estado'] = 'Giro derecha'
                        self.estadoActual['derecha'] += 1
                        infraccion['frameFinal'] = numeroDeFrame
                        # Si era candidato y esta llegando en rojo o amarillo
                        # ESTO DESCARTA LAS LLEGADAS EN VERDE # Anulado por mala intensión
                        if (infraccion['infraccion'] == 'candidato'
                            ):  #&(colorSemaforo>=1):
                            infraccion['infraccion'] = 'CAPTURADO_DERECHA'
                            self.estadoActual['infraccion'] += 1

                        self.miReporte.info(infraccion['infraccion'] + '\t' +
                                            infraccion['estado'] +
                                            ' a horas ' + infraccion['name'] +
                                            ' (' +
                                            str(infraccion['frameInicial']) +
                                            '-' +
                                            str(infraccion['frameFinal']) +
                                            ')')
                        break

                    if puntosQueGiraronIzquierda >= 2:
                        # Si llego al otro extremo, entonces cruzo:
                        infraccion['estado'] = 'Giro izquierda'
                        self.estadoActual['izquierda'] += 1
                        infraccion['frameFinal'] = numeroDeFrame
                        # Si era candidato y esta llegando en rojo o amarillo
                        # ESTO DESCARTA LAS LLEGADAS EN VERDE # Anulado por mala intensión
                        if (infraccion['infraccion'] == 'candidato'
                            ):  #&(colorSemaforo>=1):
                            infraccion['infraccion'] = 'CAPTURADO_IZQUIERDA'
                            self.estadoActual['infraccion'] += 1

                        self.miReporte.info(infraccion['infraccion'] + '\t' +
                                            infraccion['estado'] +
                                            ' a horas ' + infraccion['name'] +
                                            ' (' +
                                            str(infraccion['frameInicial']) +
                                            '-' +
                                            str(infraccion['frameFinal']) +
                                            ')')
                        break
                # Se continuara solamente con los puntos validos
                infraccion['desplazamiento'] = nuevaPosicionVehiculo[
                    indicesValidos]

        if pulsoVehiculos == 1:
            # Se determina los mejores puntos para seguir para ser parte del objeto Vehiculo
            puntosMasMoviles = self.obtenerPuntosMoviles(
                self.lineaFijaDelantera,
                self.lineaDeResguardoAlteradaDelantera, informacion)

            # Cada vehiculo tiene un numbre que biene a xer ela fecja y hora de la infracción en cuestion
            nombreInfraccionYFolder = datetime.datetime.now().strftime(
                '%H-%M-%S'
            )  # Eliminada redundancia en nombre de archivo %Y-%m-%d_

            # CREACION NUEVO VEHICULO
            nuevoVehiculo = {
                'name': nombreInfraccionYFolder,
                'frameInicial': numeroDeFrame,
                'frameFinal': 0,
                'desplazamiento': puntosMasMoviles,
                'estado': 'Previo',
                'infraccion': '',
                'observacion': ''
            }

            # CREACION NUEVO CANDIDATO
            direccionDeGuardadoFotos = 'None'
            if colorSemaforo >= 1:
                nuevoVehiculo['infraccion'] = 'candidato'
                direccionDeGuardadoFotos = self.directorioDeReporte + '/' + nombreInfraccionYFolder
                if not os.path.exists(direccionDeGuardadoFotos):
                    os.makedirs(direccionDeGuardadoFotos)

                if os.uname()[1] == 'raspberrypi':
                    # AQUI!
                    self.camaraAlta.encenderCamaraEnSubDirectorio(
                        direccionDeGuardadoFotos)
                    #self.camaraAlta.encenderCamaraEnSubDirectorio(nombreInfraccionYFolder)

            self.listaVehiculos.append(nuevoVehiculo)
            self.miReporte.info('\t\tCreado vehiculo ' +
                                nuevoVehiculo['name'] + ' en frame ' +
                                str(nuevoVehiculo['frameInicial']) +
                                ' con nivel ' + nuevoVehiculo['infraccion'] +
                                ' guardado en ' +
                                direccionDeGuardadoFotos[19:])

        infraccionesConfirmadas = self.numeroInfraccionesConfirmadas()

        self.imagenAuxiliar = imagenActualEnGris
        #print(self.estadoActual)
        #sys.stdout.write("\033[F") # Cursor up one line
        return velocidadEnBruto, velocidadFiltrada, pulsoVehiculos, 0

    def reestablecerEstado(self):
        self.estadoActual = {
            'previo': 0,
            'cruzo': 0,
            'salio': 0,
            'derecha': 0,
            'izquierda': 0,
            'ruido': 0,
            'infraccion': 0,
            'infraccionAmarillo': 0,
            'colorSemaforo': 'Verde'
        }

    def numeroInfraccionesConfirmadas(self):
        contadorInfraccionesConfirmadas = 0
        for infraccion in self.listaVehiculos:
            if infraccion['estado'] == 'Cruzo':
                contadorInfraccionesConfirmadas += 1
        return contadorInfraccionesConfirmadas

    def numeroInfraccionesTotales(self):
        contadorInfracciones = 0
        for infraccion in self.listaVehiculos:
            contadorInfracciones += 1
        return contadorInfracciones

    def reportarPasoAPaso(self, historial):
        """
		Este metodo reporta un caso a la vez de existir el mismo en la base de datos de infracciones
		"""
        self.listaVehiculos = [
            vehiculosPendientes for vehiculosPendientes in self.listaVehiculos
            if vehiculosPendientes['estado'] == 'Previo'
            or vehiculosPendientes['infraccion'] == 'CAPTURADO'
            or vehiculosPendientes['infraccion'] == 'CAPTURADO DERECHA'
            or vehiculosPendientes['infraccion'] == 'CAPTURADO IZQUIERDA'
        ]
        listaInfracciones = [
            infraccion for infraccion in self.listaVehiculos
            if infraccion['infraccion'] == 'CAPTURADO'
        ]
        # Los cruces siguen evolucionando
        # Las infracciones en calidad de 'CAPTURADO' son generadas en video
        # Los cruces en ruido son eliminados
        if len(listaInfracciones) > 0:
            # Como python optimiza el copiado de listas de diccionarios la siguiente figura modifica la lista originalself.es
            infraccionActual = listaInfracciones[0]
            #infraccionActual = self.listaVehiculos[self.listaVehiculos.index(listaInfracciones[0])]
            infraccionActual['infraccion'] = 'REPORTADO'
            #self.miGrabadora.generarReporteInfraccion(historial, infraccionActual,debug = self.reportarDebug)
            self.miGrabadora.generarReporteEnVideoDe(historial,
                                                     infraccionActual,
                                                     debug=self.reportarDebug)

    def generarVideoMuestra(self, historial):
        if len(historial) > self.minimosFramesVideoNormalDebug:
            #self.miGrabadora.generarReporteInfraccion(historial, True,debug = self.reportarDebug)
            self.miGrabadora.generarVideoDebugParaPruebas(historial)

    def eliminoCarpetaDeSerNecesario(self, infraccion):
        try:
            carpetaABorrar = self.directorioDeReporte + '/' + infraccion['name']
            self.miReporte.info('\t\t> Borrando: ' + carpetaABorrar +
                                ' con estado ' + infraccion['estado'])
            shutil.rmtree(carpetaABorrar)
        except Exception as e:
            self.miReporte.warning('\t\t\tNo pude borrar carpeta fantasma: ' +
                                   infraccion['name'] + ' por ' + str(e))

    def popInfraccion(self):
        if self.numeroInfraccionesConfirmadas() != 0:
            variableARetornar = self.listaVehiculos.pop()
            while variableARetornar['estado'] != 'Cruzo':
                variableARetornar = self.listaVehiculos.pop()
            return variableARetornar
        else:
            return {}
        return variableARetornar

    def reporteActual(self):
        self.miReporte.info('Infracciones Sospechosas:')
        for infraccion in self.listaVehiculos:
            self.miReporte.info(infraccion['frameInicial'] + ' a ' +
                                str(infraccion['frameFinal']) +
                                ' con estado: ' + infraccion['estado'])
        self.miReporte.info('Infracciones Confirmadas:')
        for infraccion in self.listaVehiculos:
            self.miReporte.info(infraccion['name'] + ' de ' +
                                str(infraccion['frameInicial']) + ' a ' +
                                str(infraccion['frameFinal']) +
                                ' con estado: ' + infraccion['estado'])

    def obtenerLinea(self):
        """
		Returns the starting line in tuple format, ready to read or plot with opencv
		"""
        aDevolver = []
        for infraccion in self.listaVehiculos:
            if infraccion['estado'] == 'Previo':
                for punto in infraccion['desplazamiento']:
                    aDevolver.append(tuple(punto[0]))
        return aDevolver

    def obtenerLineasDeResguardo(self, alterada=False):
        aDevolver = []
        if alterada:
            for punto in self.lineaDeResguardoAlteradaDelantera:
                aDevolver.append(tuple(punto[0]))
        else:
            for punto in self.lineaDeResguardoDelantera:
                aDevolver.append(tuple(punto[0]))
        return aDevolver

    def obtenerVectorMovimiento(self, vectorAntiguo, nuevoVector):
        """
		Gets the movement vector of all points in the starting line, this is used more like an internal method
		"""
        x = 0
        y = 0
        for numeroDePunto in range(1, self.numeroDePuntos + 1):
            x += nuevoVector[numeroDePunto][0][0] - vectorAntiguo[
                numeroDePunto][0][0]
            y += nuevoVector[numeroDePunto][0][1] - vectorAntiguo[
                numeroDePunto][0][1]
        x = 10 * x / (self.numeroDePuntos + 1)
        y = 10 * y / (self.numeroDePuntos + 1)
        return (x, y)

    def obtenerPuntosMoviles(self,
                             vectorAntiguo,
                             nuevoVector,
                             informacion=False):
        """
		Gets center of movement as a tuple of three vectors
		"""
        ##### OJO AQUI TAMBIEN PUEDO FILTRAR RUIDO???
        dif2 = [
        ]  # Para todos los puntos de resguardo veo los que tienen mayor movimiento
        for numeroDePunto in range(1, self.numeroDePuntos + 1):
            x = nuevoVector[numeroDePunto][0][0] - vectorAntiguo[
                numeroDePunto][0][0]
            y = nuevoVector[numeroDePunto][0][1] - vectorAntiguo[
                numeroDePunto][0][1]
            dif2.append(x**2 + y**2)
        indiceDeMayores = []
        for numeroDePuntoASeguir in range(
                self.numeroDePuntosASeguirDeInicializacion):
            indice = dif2.index(max(dif2))
            indiceDeMayores.append(indice)
            dif2.pop(indice)

        listaNuevosPuntos = np.array(nuevoVector[indiceDeMayores])

        for indice in range(len(listaNuevosPuntos)):
            listaNuevosPuntos[indice][
                0] = listaNuevosPuntos[indice][0] + self.desplazamiento

        return listaNuevosPuntos  #np.array([[nuevoVector[indiceDeMayores[0]][0]],[nuevoVector[indiceDeMayores[1]][0]],[nuevoVector[indiceDeMayores[2]][0]]])

    def obtenerMagnitudMovimiento(self, vectorAntiguo, nuevoVector):
        """
		Gets the real magnitud of movement perpendicular to the starting point
		"""
        (x, y) = self.obtenerVectorMovimiento(vectorAntiguo, nuevoVector)
        moduloPerpendicular = self.vectorPerpendicularUnitario[
            0] * x + self.vectorPerpendicularUnitario[1] * y
        return moduloPerpendicular

    def obtenerMagnitudMovimientoEnRegion(self, nuevaFranja):
        flow = cv2.calcOpticalFlowFarneback(
            self.anteriorFranja, nuevaFranja, None, 0.5, 3, 15, 3, 5, 1.2, 0
        )  #(self.auxiliar_image, current_image, None, 0.7, 3, 9, 3, 5, 1.2, 0)

        #y, x = np.mgrid[self.optimalStep//2:nuevaFranja.shape[0]:self.optimalStep, self.optimalStep//2:nuevaFranja.shape[1]:self.optimalStep].reshape(2,-1)
        #y = np.int32(y)
        #x = np.int32(x)
        #fx, fy = flow[y,x].T
        #flowX = sum(fx)
        #flowY = sum(fy)

        flowX = 0
        flowY = 0

        for row in flow:
            for data in row:
                flowX += data[0]
                flowY += data[1]

        self.anteriorFranja = nuevaFranja
        # Se retorna el flujo en X invertido
        flujoPerpendicular = -10 * flowX / (
            (flow.shape[0] * flow.shape[1]) + 1)

        return flujoPerpendicular

    def apagarCamara(self):
        self.camaraAlta.apagarControlador()