Medir Distância com Visão Computacional

Medir distância com visão computacional é uma opção interessante mas não deve ser o meio principal de medição por não ser preciso. Mesmo assim, é possível identificar o objeto mais próximo da câmera em boa parte dos casos.

Lógica de Funcionamento

A principio, todo objeto identificado pela Inteligência Artificial (IA) fica contido em um retângulo ou quadrado, como se pode ver na imagem a seguir.

Princípio básico de reconhecimento de objetos com visão computacional

Distinção de Tamanho

Como a IA trabalha com forma geométrica, é possível por exemplo fazer uma distinção do tamanho com base na área de cada retângulo. Como se pode ver na imagem abaixo, o retângulo da esquerda é maior do que o da direita, pois possui uma área maior.

Comparação de distâncias com base no tamanho de áreas.

Se considerado que o mesmo objeto, em distâncias diferentes a área vai variar proporcionalmente, em boa parte dos caso. Por isso, os objetos mais próximos tendem a ter uma área maior.

Identificação do objeto mais próximo da câmera, após medir a distância com visão computacional

Como é se pode observar na imagem acima, o objeto mais a extrema esquerda tem quase o mesmo tamanho que o objeto identificado como o mais próximo (marcado em verde). Por isso, não se deve levar esse parâmetro para como meio de medição de precisão, muito menos deve implementar com mais de uma classe.

Montagem do Código

Continuando com a lógica para medir distância com visão computacional, foi necessário armazenar e comparar todas as áreas identificadas, para isso, o método mais simples foi o uso de um ranque. Dessa forma, é possível não só usar nesta aplicação mas também em uma aplicação “térmica” que varia a cor conforme a distância.



Como os demais processos de identificação já foi abordado em outro artigo aqui no site no qual foi usado o Maix bit, por isso não vai ser reexplicado. Ainda assim, para o ranque é necessário inicialmente salvar dentro de um array a área de cada objeto.

all_area[number_obj] = i.w() * i.h()

Ranque de Áreas

Em seguida fora do laço ‘for‘ é adicionado um outro laço ‘for‘ que vai fazer o papel do ranqueamento, a lógica é basicamente comparar a área de cada objeto armazenado e comparar com um outro ‘array‘ batizado de ‘rank_array[]‘.

id_area = 0
for j in range(number_obj):
    if all_area[j] > rank_array[0]:
        rank_array[0]= all_area[j]
        id_area = j

Identificar Área mais Próxima

Agora só falta identificar qual objeto é o mais próximo, para isso basta adicionar o mesmo algoritmo do post desse link aqui. Porém foi feito algumas modificações para atender esta aplicação e por isso as variáveis estão em ‘array‘ também. Com isso, é possível pegar posteriormente a localização do centro de cada área com o uso do ‘id_area‘.

a = img.draw_circle(location_x[id_area], location_y[id_area], 3, color=(255, 0, 0), fill=True)

A única diferença é que neste caso o ideal é está fora do laço ‘for‘ principal, caso contrário, irá haver uma marcação em mais de uma área.

Código Final

O código resultante de toda a lógica ficou assim, você pode copiar e colar na IDE ou pode baixar via Github por meio deste link.

import sensor,image,lcd,time
import KPU as kpu
import ujson

lcd.init(freq=15000000)
sensor.reset()
sensor.set_pixformat(sensor.RGB565)
sensor.set_framesize(sensor.QVGA)
sensor.set_vflip(1)
sensor.set_hmirror(1)
sensor.run(1)
clock = time.clock()
classes = ['aeroplane', 'bicycle', 'bird', 'boat', 'bottle', 'bus', 'car', 'cat', 'chair', 'cow', 'diningtable', 'dog', 'horse', 'motorbike', 'person', 'pottedplant', 'sheep', 'sofa', 'train', 'tvmonitor']
task = kpu.load("/sd/20class.kmodel") 
anchor = (1.889, 2.5245, 2.9465, 3.94056, 3.99987, 5.3658, 5.155437, 6.92275, 6.718375, 9.01025)
a = kpu.init_yolo2(task, 0.5, 0.3, 5, anchor)

while(True):
    clock.tick()
    img = sensor.snapshot()
    code = kpu.run_yolo2(task, img)
    
    rank_array = ([0 for x in range(80)])
    all_area   = ([0 for x in range(80)])
    location_x   = ([0 for x in range(80)])   
    location_y   = ([0 for x in range(80)])
    
    if code:
        number_obj = 0
        count = 0
        for i in code:
            a = img.draw_rectangle(i.rect())
            
            centroid_x = int(i.w() / 2)
            centroid_y = int(i.h() / 2)
            
            location_x[number_obj] = i.x() + centroid_x
            location_y[number_obj] = i.y() + centroid_y
            
            all_area[number_obj] = i.w() * i.h()
            
            #a = img.draw_circle(location_x[number_obj], location_y[number_obj], 3, color=(255, 255, 255), fill=True)
            
            a = lcd.display(img)
            number_obj += 1
            for i in code:
                lcd.draw_string(i.x(), i.y(), classes[i.classid()], lcd.RED, lcd.WHITE)
                lcd.draw_string(i.x(), i.y()+12, '%f'%i.value(), lcd.RED, lcd.WHITE)
    
        id_area = 0
        for j in range(number_obj):
            if all_area[j] > rank_array[0]:
                rank_array[0]= all_area[j]
                id_area = j

    if rank_array[0] > 0:
        a = img.draw_circle(location_x[id_area], location_y[id_area], 3, color=(255, 0, 0), fill=True)
        percent_location_x = int(location_x[id_area] * 100 / 320)
        percent_location_y = int(location_y[id_area] * 100 / 240)
        json_map = {}
        json_map["x"] = percent_location_x
        json_map["y"] = percent_location_y
        json_percent_location = ujson.dumps(json_map)   
        print(json_percent_location)
        count += 1
    a = lcd.display(img)
a = kpu.deinit(task)

Funcionamento

Como na capa do vídeo. o teste foi com o Minecraft por questão de praticidade e claro que a identificação não é da ‘bruxa‘ mas dentre os ‘mobs‘, foi a única que era reconhecida usando o modelo treinado de 20 classes. Por isso, não foi necessário treinar o Maix Bit.

Deixe uma resposta

Descubra mais sobre elcereza

Assine agora mesmo para continuar lendo e ter acesso ao arquivo completo.

Continue reading