Vídeo do Cloud Object Detector no Raspberry Pi

Prólogo

Um vídeo está circulando na Internet mostrando como o piloto automático da Tesla vê a estrada.

Há muito tempo que estou ansioso para transmitir vídeo enriquecido com detector e em tempo real.

Vídeo do Cloud Object Detector no Raspberry Pi

O problema é que quero transmitir vídeo do Raspberry, e o desempenho do detector de rede neural nele deixa muito a desejar.

Stick de computador neural Intel

Considerei soluções diferentes.

В último artigo experimentei o Intel Neural Computer Stick. O hardware é poderoso, mas requer seu próprio formato de rede.

Embora a Intel forneça conversores para as principais estruturas, existem várias armadilhas.

Por exemplo, o formato da rede necessária pode ser incompatível e, se for compatível, algumas camadas podem não ser suportadas no dispositivo e, se forem suportadas, podem ocorrer erros durante o processo de conversão, como resultado dos quais obtemos algumas coisas estranhas na saída.

Em geral, se você deseja algum tipo de rede neural arbitrária, ela pode não funcionar com o NCS. Portanto, decidi tentar resolver o problema utilizando as ferramentas mais difundidas e acessíveis.

Cloud

A alternativa óbvia para uma solução de hardware local é ir para a nuvem.

Opções prontas - meus olhos correm arregalados.

Todos os líderes:

... E dezenas de outros menos conhecidos.

Escolher entre esta variedade não é nada fácil.

E decidi não escolher, mas embrulhar o bom e velho esquema de trabalho do OpenCV no Docker e executá-lo na nuvem.

A vantagem dessa abordagem é a flexibilidade e o controle - você pode alterar a rede neural, a hospedagem, o servidor - em geral, qualquer capricho.

Servidor

Vamos começar com um protótipo local.

Tradicionalmente eu uso Flask para API REST, OpenCV e rede MobileSSD.

Depois de instalar as versões atuais no Docker, descobri que o OpenCV 4.1.2 não funciona com Mobile SSD v1_coco_2018_01_28 e tive que reverter para o comprovado 11/06_2017.

No início do serviço, carregamos os nomes das classes e da rede:

def init():
    tf_labels.initLabels(dnn_conf.DNN_LABELS_PATH)
    return cv.dnn.readNetFromTensorflow(dnn_conf.DNN_PATH, dnn_conf.DNN_TXT_PATH)

Em um docker local (em um laptop não muito jovem), leva 0.3 segundos, no Raspberry - 3.5.

Vamos começar o cálculo:

def inference(img):
    net.setInput(cv.dnn.blobFromImage(img, 1.0/127.5, (300, 300), (127.5, 127.5, 127.5), swapRB=True, crop=False))
    return net.forward()

Docker - 0.2 seg, Raspberry - 1.7.

Transformando a exaustão do tensor em json legível:

def build_detection(data, thr, rows, cols):
    ret = []
    for detection in data[0,0,:,:]:
        score = float(detection[2])
        if score > thr:
            cls = int(detection[1])
            a = {"class" : cls, "name" : tf_labels.getLabel(cls),  "score" : score}
            a["x"] = int(detection[3] * cols)
            a["y"] = int(detection[4] * rows)
            a["w"] = int(detection[5] * cols ) - a["x"]
            a["h"] = int(detection[6] * rows) - a["y"]
            ret.append(a)
    return ret

Mais exporte esta operação via Flask(a entrada é uma imagem, a saída são os resultados do detector em json).

Uma opção alternativa, em que mais trabalho é transferido para o servidor: ele próprio circula os objetos encontrados e retorna a imagem finalizada.

Esta opção é boa quando não queremos arrastar o opencv para o servidor.

Docker

Coletamos a imagem.

O código é penteado e postado em GithubGenericName, o docker irá levá-lo diretamente de lá.

Como plataforma, usaremos o mesmo Debian Stretch do Raspberry - não nos desviaremos da pilha de tecnologia comprovada.

Você precisa instalar flask, protobuf, requests, opencv_python, baixar Mobile SSD, código do servidor do Github e iniciar o servidor.

FROM python:3.7-stretch

RUN pip3 install flask
RUN pip3 install protobuf
RUN pip3 install requests
RUN pip3 install opencv_python

ADD http://download.tensorflow.org/models/object_detection/ssd_mobilenet_v1_coco_11_06_2017.tar.gz /
RUN tar -xvf /ssd_mobilenet_v1_coco_11_06_2017.tar.gz

ADD https://github.com/tprlab/docker-detect/archive/master.zip /
RUN unzip /master.zip

EXPOSE 80

CMD ["python3", "/docker-detect-master/detect-app/app.py"]

Simples cliente detector com base em solicitações.

Publicando no Docker Hub

Os registros Docker estão se multiplicando a uma velocidade não menor que a dos detectores de nuvem.

Para não incomodar, iremos de forma conservadora Docker Hub.

  1. Registro
  2. Conecte-se:
    login da janela de encaixe
  3. Vamos encontrar um nome significativo:
    tag docker opencv-detect tprlab/opencv-detect-ssd
  4. Carregue a imagem no servidor:
    docker push tprlab/opencv-detect-ssd

Lançamos na nuvem

A escolha de onde executar o contêiner também é bastante ampla.

Todos os grandes players (Google, Microsoft, Amazon) oferecem uma microinstância gratuitamente durante o primeiro ano.
Depois de experimentar o Microsoft Azure e o Google Cloud, optei pelo último porque decolou mais rápido.

Não escrevi instruções aqui, pois esta parte é muito específica para o fornecedor selecionado.

Eu tentei diferentes opções de hardware,
Níveis baixos (compartilhados e dedicados) - 0.4 - 0.5 segundos.
Carros mais potentes - 0.25 - 0.3.
Bem, mesmo na pior das hipóteses, os ganhos são três vezes, você pode tentar.

Vídeo

Lançamos um streamer de vídeo OpenCV simples no Raspberry, detectando através do Google Cloud.
Para o experimento, foi utilizado um arquivo de vídeo que já foi filmado em um cruzamento aleatório.


def handle_frame(frame):
    return detect.detect_draw_img(frame)
       
def generate():
    while True:
        rc, frame = vs.read()
        outFrame = handle_frame(frame)
        if outFrame is None:
            (rc, outFrame) = cv.imencode(".jpg", frame)
        yield(b'--framern' b'Content-Type: image/jpegrnrn' + bytearray(outFrame) + b'rn')

@app.route("/stream")
def video_feed():
    return Response(generate(), mimetype = "multipart/x-mixed-replace; boundary=frame")

Com o detector não conseguimos mais que três frames por segundo, tudo acontece muito devagar.
Se você colocar uma máquina poderosa no GCloud, poderá detectar de 4 a 5 quadros por segundo, mas a diferença é quase invisível a olho nu, ainda é lenta.

Vídeo do Cloud Object Detector no Raspberry Pi

Os custos de nuvem e transporte não têm nada a ver com isso: o detector funciona em hardware comum e funciona a essa velocidade.

Computador Neural Stick

Não resisti e fiz o benchmark no NCS.

A velocidade do detector foi ligeiramente mais lenta que 0.1 segundos, em qualquer caso, 2 a 3 vezes mais rápida que a nuvem em uma máquina fraca, ou seja, 8 a 9 quadros por segundo.

Vídeo do Cloud Object Detector no Raspberry Pi

A diferença nos resultados é explicada pelo fato de o NCS estar executando a versão 2018_01_28 do Mobile SSD.

PS Além disso, experimentos mostraram que uma máquina desktop bastante poderosa com processador I7 apresenta resultados um pouco melhores e foi possível extrair 10 quadros por segundo nela.

Cluster

O experimento foi além e instalei o detector em cinco nós do Google Kubernetes.
Os próprios pods eram fracos e cada um deles não conseguia processar mais de 2 quadros por segundo.
Mas se você executar um cluster com N nós e analisar quadros em N threads, com um número suficiente de nós (5) poderá atingir os 10 quadros por segundo desejados.

def generate():
    while True:
        rc, frame = vs.read()
        if frame is not None:
            future = executor.submit(handle_frame, (frame.copy()))
            Q.append(future)

        keep_polling = len(Q) > 0
        while(keep_polling):            
            top = Q[0]
            if top.done():
                outFrame = top.result()
                Q.popleft()
                if outFrame:
                    yield(b'--framern' b'Content-Type: image/jpegrnrn' + bytearray(outFrame) + b'rn')
                keep_polling = len(Q) > 0
            else:
                keep_polling = len(Q) >= M

Veja o que aconteceu:

Vídeo do Cloud Object Detector no Raspberry Pi

Um pouco menos rápido do que com NCS, mas mais vigoroso do que em um stream.

O ganho, claro, não é linear - existem sobreposições para sincronização e cópia profunda de imagens opencv.

Conclusão

No geral, o experimento nos permite concluir que, se você tentar, poderá escapar com uma nuvem simples.

Mas um desktop poderoso ou hardware local permite obter melhores resultados e sem truques.

referências

Fonte: habr.com

Adicionar um comentário