3. 22/04/2015 - Pluviômetro Livre Baseado em Visão Computacional (Hermano)

O objetivo do projeto é desenvolver um pluviômetro que permita a medição da quantidade de chuva de uma maneira automatizada, possibilitando que, futuramente, os habitantes de áreas de risco possam ser informados caso os níveis medidos indiquem risco de desabamentos e\ou enchentes.

A proposta aqui apresentada é a de criar um pluviômetro baseado em visão computacional usando ferramentas livres. A biblioteca OpenCV seria utilizada para permitir o tratamento de imagens. Ela implementa algoritmos que possibilitam a identificação e o tracking (acompanhamento da variação da posição de um objeto no espaço em instantes diferentes do tempo) de objetos em imagens.

Supondo que um objeto marcador, isto é, um objeto que possa ter sua posição facilmente identificada com a utilização da biblioteca, esteja boiando sobre a superfície da água contida em um recipiente, e que esse recipiente receba a água da chuva sobre uma determinada área, seria possível medir a quantidade de precipitação através no nível da água no recipiente. A posição do objeto marcador serve de indicação do nível da água, já que este boia sobre a superfície. Com essa informação, e um pouco de telemetria, pode-se acompanhar o a variação do nível de água do recipiente para obter a quantidade de precipitação em um intervalo de tempo.

A figura 4 ilustra a idéia. Um recipiente, contendo um marcador. Sobre este recipiente há um captador que possibilita a acumulação da água no recipiente. Este recipiente estaria posicionado em frente a uma câmera (webcam) que faria a aquisição em tempo real de imagens. A câmera precisa estar conectada a algum sistema computacional no qual o software de controle do pluviômetro, desenvolvido com o uso da biblioteca OpenCV, faria o tratamento das imagens e as medições.

Figura 4. Uso de visão computacional para monitoramento do volume em um pluviômetro.

Uso de visão computacional para monitoramento do volume em um pluviômetro.

A medição exige que a posição do marcador dentro da imagem (dada em pixels) seja traduzida em mililitros (ou m3, dependendo da unidade usada). É necessário criar uma relação entre o tamanho da total da imagem (ou pelo menos o tamanho ocupado pelo recipiente) e o tamanho do recipiente. Supondo que o recipiente (figura 5) ocupe a posição que vai do pixel 0 até o pixel 480, podemos relacionar essas posições aos níveis 0 ml até 500 ml respectivamente. Usando essa relação é possível estimar o nível da água no recipiente a partir da posição do marcador na imagem.

Figura 5. Conversão de pixels em volume.

Conversão de pixels em volume.

3.1. 12/08/2015 - Sistema de Medição de Chuvas - Visão Geral

Em um nível mais alto de abstração podemos pensar em um sistema de medição de chuvas que possa incorporar diferentes técnicas para a aquisição dos dados. A utilização da OPENCV seria apenas mais uma das técnicas, o que significa que as funções do sistema devem ser independentes da maneira como os dados das medições serão obtidos.

Primeiro é preciso especificar quais são os requisitos funcionais, e então pensar nos componentes que irão implementar a funcionalidade. À príncipio, acho razoável pensar nos seguintes requisitos para o sistema:

  • O sistema deve adquirir através de componentes (que podemos chamar de sensores) os dados referentes aos níveis de precipitação em um determinado período de tempo.

  • O sistema deve permitir que esses níveis de precipitação sejam visualizados pelo usuário, de preferência, com uma taxa de atualização de poucos minutos, ou até de alguma vezes por minuto.

  • O sistema deve registrar os dados históricos, persistindo-os em arquivos, ou em um banco de dados. Uma vez que a medição é feita, é interessante manter os dados para possível utilização futura.

  • O sistema deve fornecer uma interface para a configuração dos sensores. Cada sensor possui suas próprias peculiaridades que podem precisar de ajustes do usuário. Por exemplo, o sensor baseado em OPENCV precisa ser calibrado para que o marcador seja identificado corretamente. Algum outro tipo de ajuste pode ser preciso para o pluviômetro baseado em Arduíno, ou outra maneira de realizar a medição que possamos ter.

Com esses requisitos definidos, podemos criar um diagrama para especificar os componentes de software envolvidos na resolução do problema. A imagem 6 mostra o diagrama conceitual do Sistema de Medição de Chuvas. Os componentes de softwares neste contexto são apenas unidades lógicas que implementam um conjunto de funcionalidades. Na prática, cada um desses componentes pode ser um conjunto de arquivos fonte e/ou binários executáveis, que irão prover a funcionalidade ao sistema.

Figura 6. Diagrama conceitual do sistema de medição de chuvas.

Diagrama conceitual do sistema de medição de chuvas.

O diagrama 6 mostra um componente principal, chamado de sistema de medição de chuvas. Esse sistema utiliza algum método para persistência dos dados, que pode ser um banco de dados ou mesmo arquivos simples não estruturados. Esse sistema deve estar conectado aos sensores através de algum protocolo. Este protocolo pode variar de acordo com as necessidades do sensor.

Podemos utilizar arquivos ou pipes para a comunicação entre componentes, no caso em que o sistema e o sensor estiverem sendo executados na mesma máquina. Podemos utilizar sockets (estratégia de mais baixo nível) ou mesmo o protocolo HTTP (supondo que o sistema de medição funcione através da web).

Os sensores devem ser inteligentes o suficiente para fornecer um mesmo tipo de informação ao módulo principal. Essa informação pode ser o volume de chuva em um intervalo especificado, ou alguma outra medição que possibilite o cálculo desse volume. Idealmente, as particularidades de cada forma de obtenção dos dados devem ser implementadas nos respectivos sensores, isto é, o sistema de medição é responsável apenas por receber as medições, exibi-las ao usuário e armazená-las. O sistema pode exibir os dados de diversas formas, até mesmo em um gráfico simples, e possivelmente manter uma interface para consultas de medições passadas.

3.2. 12/08/2015 - Pluviômetro OPENCV

O pluviomêtro baseado em OPENCV tem como base de seu funcionamento a detecção de um marcador que boia sobre a superfície da água contida em recipiente que recebe as águas da chuva. É possível notar que o erro associado a medição vai depender de diversos fatores, a saber:

  • Tamanho e a cor do marcador. Quanto mais destacado do resto do ambiente melhor.

  • Tamanho do recipiente. Um recipiente cuja área da base seja bem pequeno, de maneira que uma pequena mudança na altura indique também uma pequena alteração no volume da água contido no recipiente como um todo.

  • Condições de iluminação. Alterações bruscas na condições de iluminação podem dificultar as medições.

Uma técnica promissora para identificação do marcador envolve o uso da detecção de objetos baseado em cor. Isto é, dada uma imagem cujo espaço de cores está em HSV, nós especificamos uma faixa de valores para cada parâmetro (H = matiz, S= saturação e V= valor), e geramos um mapa de bits em que o valor do pixel é igual a 1 para os pixels encontrados dentro da faixa, e 0 para valores encontrados fora da faixa (exemplo na figura 7, imagem e mapa de bits em que o marcador é filtrado).

Figura 7. Imagem real x bitmap para identificação do marcador.

Imagem real x bitmap para identificação do marcador.

Uma vez obtido o bitmap com o marcador devidamente identificado, podemos obter sua posição na imagem com o cálculo do centro de gravidade. O centro de gravidade é obtido através do cálculo de uma média ponderada dos valores dos pixels da imagem. A biblioteca OPENCV oferece uma função chamada moments, que facilita o cálculo do centro de gravidade da imagem.

Além da posição do marcador, podemos solicitar ao usuário a posição do recipiente na imagem. Dessa forma, podemos saber a distância entre a altura do marcador, e a base do recipiente, o que nos permite estimar o nível de preenchimento, supondo que o marcador esteja flutuando sobre a superfície da água.

Fazendo medições em intervalos de tempo definidos, podemos gerar um gráfico da posição do marcador em cada instante de tempo. E partir desses dados, podemos obter informações referentes ao volume de água depositado no recipiente durante um período.

Em seguida o código em python, com comentários, contendo as operações descritas acima.

import cv2
import cv2.cv as cv
import numpy as np
import time

#Declaração de variáveis
hsv_min = []
hsv_max = []
container_min = 0
container_max = 0
container_area = 0
container_volume = 0

#Funcao para ler arquivo contendo os dados do container (posição na imagem e volume total)
def read_container(path):
	global container_min
	global container_max
	global container_volume
	global container_area

	file = open(path, 'r')
	line = file.readline()
	container_min = int(line)
	line = file.readline()
	container_max = int(line)
	line = file.readline()
	container_volume = int(line)
	file.close()

#Funcao para os parâmetros de configuraçãod a cor
def read_range(path):
	global hsv_min
	global hsv_max	
	
	file = open(path, 'r')

	line = file.readline()
	line = line.split(" ")
	hsv_min = np.array([int(numeric_string) for numeric_string in line], dtype=np.uint8)

	line = file.readline()
	line = line.split(" ")
	hsv_max = np.array([int(numeric_string) for numeric_string in line], dtype=np.uint8)

	
	file.close()	


#Funcao para calcular o volume de água
def calculate_water_level(marker_y):
	if marker_y >= container_min and marker_y <= container_max :
		total_height = container_max - container_min
		marker_height = marker_y - container_min 		
		
		return ((1-float(marker_height)/float(total_height))*container_volume)
	else :
		return 0
  
	
#Comeco do Programa

#Obtendo imagem da webcam
cap = cv2.VideoCapture(1)

t = 0
posX  = 0
posY  = 0
lastX = 0
lastY = 0
scribble = np.zeros((480,640,3), np.uint8)

#Laço de repetição infinito para realizar medições
while(True):

    frame = None

    # Capturar frame
    ret, frame = cap.read()

    #Ler arquivos de configuracao
    read_range("/var/www/plu/app/config_range")
    read_container("/var/www/plu/app/config_container")
   
    #Tratamento da imagem
    hsv_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)  
    thresholded = cv2.inRange(hsv_frame, hsv_min, hsv_max)
    thresholded = cv2.GaussianBlur(thresholded, (9, 9), 0)		
    
    #Calculando centro de gravidade
    M = cv2.moments(thresholded)
    if(M['m00'] != 0):
    	posX = int(M['m10']/M['m00'])
	posY = int(M['m01']/M['m00'])

    #Plotando linhas entre as duas ultimas posicoes	 	
    #if lastX>0 and lastY>0 and posX>0 and posY>0 :
    # 	 cv2.line(scribble, (lastX, lastY), (posX, posY), (255,0,255), 3)

    #Adicionando linhas ao frame principal
    #cv2.add(frame, scribble, frame) 
    cv2.circle(frame,(posX,posY),2,(0,0,255),3)

    #Salvando posicao do ultimo centro de gravidade
    lastX = posX
    lastY = posY
	
    #Escrevendo arquivo  
    print str(round(calculate_water_level(posY),2))+"ml no t= "+str(t)+"s"+str(posY)
    cv2.imshow('frame', frame)
	
    cv2.imwrite('/var/www/plu/plu.jpg', frame)	
    
    t = t+1
    #aguardand 1s
    time.sleep(1)	
		 
#Liberar Recursos
cap.release()
cv2.destroyAllWindows()