sexta-feira, 19 de fevereiro de 2021

Ambiente individual num computador para processamento de Deep Learning

 

Aqui estão algumas instruções que consideram um computador para processamento de Deep Learning, que eu vou chamar de servidor remoto.

Esta é uma instrução para separação básica de ambientes, pois todos ainda vão depender da mesma versão do CUDA.

1- Conta de usuário

Crie uma conta para cada usuário o servidor e adicione ao grupo sudo, instruções completas aqui.

sudo adduser <nome do usuário>

sudo usermod -aG sudo <nome do usuário>

Efetue pelo menos um login na tela de entrada do Ubuntu com esta conta para criar o Home Directory


2- Instale o Miniconda e seu ambiente

Cada usuário, usando sua própria conta, deve instalar no servidor o Miniconda e seu ambiente de desenvolvimento, mais instruções aqui.

Faça o download do Miniconda

Para fazer o download por linha de comando no shell, primeiro obtenha o caminho completo do arquivo de instalação do Miniconda que você deseja, depois execute ao comando;

considerando que o shell esta em sua home e existe um diretório Downloads

curl <caminho completo> -o Downloads/<nome do arquivo>

exemplo

curl https://repo.anaconda.com/miniconda/Miniconda3-py39_4.9.2-Linux-x86_64.sh -o Downloads/Miniconda3-py39_4.9.2-Linux-x86_64.sh


cd Downloads

Permida a execução do arquivo que você fez o download

chmod 700 <nome do arquivo>

execute:
./<nome do arquivo>

Se você sá sabe como quer criar um ambiente, crie este ambiente e instale o que achar necessário

conda create -n <nome do ambiente>

Obs1.: Para listar ambientes existentes use: conda env list
Obs2.: Para remover um ambiente use: conda env remove --name <nome>

3- Dica: Mapeando um diretório com o SSHSF

Mais instruções aqui.


Para conectar a partir de um desktop local  com Linux:

crie um diretório para montar:
sudo mkdir /mnt/droplet

conecte como sshsf no diretório corresondente:
sudo sshfs -o allow_other,default_permissions <usuário>@<IP servidor remoto>:/ /mnt/droplet

Depois de usar, para desmontar execute:
sudo umount /mnt/droplet
sudo rmdir /mnt/droplet


Para conectar a partir de um desktop local com Windows 10:
Instale a versão estável do SSHFS-Win · SSHFS for Windows, no meu caso foi a 3.5.20357
Instale a versão estável do WinFsp · Windows File System Proxy, no meu caso foi a 1.8

No File Explorer (Explorador de Arquivos)
botão direito em Rede
Mapear unidade de rede
Para o root:
          \\sshfs\<usuário>@<IP servidor remoto>/../..
usando porta diferente:
     \\sshfs\<usuário>@<IP servidor remoto>!<porta>/../..
Ou

Para o home:
            \\sshfs\<usuário>@<IP servidor remoto>
usando porta diferente:
     \\sshfs\<usuário>@<IP servidor remoto>!<porta>

Mais opções aqui.

4- Dica: Acessando Jupyter Notebook remotamente

Informações mais detalhadas aqui.

Depois que seu ambiente de desenvolvimento estiver pronto no servidor remoto e você deseja acessar o Jupyter Notebook no navegador seu desktop local.

No desktop local em um sheel à parte execute um redirecionamento de portas:
ssh -N -f -L localhost:8889:localhost:8888 <usuário>@<IP servidor remoto>
    Obs: Este redirecionamento precisa ser feito novamente ser você reinicialr o desktop local.

No servidor remoto, (via ssh) :

Vá para o root se quiser acessar diretórios como /mnt (onde são montados outros discos):
cd /

Entre no ambiente correspondente:

conda activate <nome> 

e inicie o jupyter notebook:

jupyter notebook --no-browser

No navegador do desktop local chame o link que foi gerado, modificando a porta para 8889, o formato é pareceido com este, o token será outro.

http://localhost:8889/?token=<aqui vem uma sequência alfa numércia>

Obs.1: Configurei dessa forma para que fique a porta padrão 8888/tcp para os Jupyter Notebooks que estão executando no seu desktop local e a porta 8889/tcp para os Jupyter Notebooks que estão executando no servidor remoto.

Obs.2: Estando no raíz você pode acessar via Jupyter Notebook tanto HDs que estejam montados no /mnt ou acessar sua home em /home/<nome do usuário> 

5- Dica: Para seções que vão demorar muito

O Screen é uma aplicação de console que permite retomar uma seção que foi perdida, entre outras funções. Mais informações aqui.

Depois de conectar com o SSH execute, screen -S <palavra> e depois use screen -dr <palavra> para retomar a seção, exemplo:

screen -S hello

Execute os camandos que precisa, para testar feche a janela que está usando o SSH, conecte novamente e execute 

screen -dr hello

Assim você retoma o terminal que estava aberto. Em caso de execuções muito demoradas poder retomar a seção é fundamental.


Obs1.: Para sair do screen use: exit
Obs2.: Para visualizar as screens existentes use: screen -ls
Obs3.: Para terminar uma screen detached use: screen -X -S <numero.nome> quit


6- Dica: Monitorar GPUs

Simplesmente execute em um sheel remoto adicional :
nvidia-smi

Ou, crie um Jupyter Notebook e inclua a linha:
!nvidia-smi

Para ambos os casos o resultado será uma tela como esta:
Thu Mar 18 12:53:55 2021
+-----------------------------------------------------------------------------+
| NVIDIA-SMI 460.39       Driver Version: 460.39       CUDA Version: 11.2     |
|-------------------------------+----------------------+----------------------+
| GPU  Name        Persistence-M| Bus-Id        Disp.A | Volatile Uncorr. ECC |
| Fan  Temp  Perf  Pwr:Usage/Cap|         Memory-Usage | GPU-Util  Compute M. |
|                               |                      |               MIG M. |
|===============================+======================+======================|
|   0  GeForce RTX 3090    Off  | 00000000:04:00.0 Off |                  N/A |
| 30%   33C    P8    11W / 350W |      5MiB / 24268MiB |      0%      Default |
|                               |                      |                  N/A |
+-------------------------------+----------------------+----------------------+
|   1  GeForce RTX 3090    Off  | 00000000:09:00.0 Off |                  N/A |
| 30%   33C    P8    12W / 350W |     72MiB / 24259MiB |      0%      Default |
|                               |                      |                  N/A |
+-------------------------------+----------------------+----------------------+

+-----------------------------------------------------------------------------+
| Processes:                                                                  |
|  GPU   GI   CI        PID   Type   Process name                  GPU Memory |
|        ID   ID                                                   Usage      |
|=============================================================================|
|    0   N/A  N/A      1260      G   /usr/lib/xorg/Xorg                  4MiB |
|    1   N/A  N/A      1260      G   /usr/lib/xorg/Xorg                 56MiB |
|    1   N/A  N/A      1445      G   /usr/bin/gnome-shell               13MiB |
+-----------------------------------------------------------------------------+


Se quiser acompanhar passo-a-passo, abra um sheel remoto adicional e execute:

nvidia-smi --query-gpu=timestamp,name,pci.bus_id,pstate,temperature.gpu,utilization.gpu,utilization.memory --format=csv -l 20


Será apresentado:
Data
Nome da GPU
Id do PCI Bus (serve para você identificar mais de uma GPU)
Temperatura
% de utilização da GPU
% de utilização da memória da 
GPU

Mais informações e opções aqui.

Obs.: O último número do comando representa os segundos de atulização, dos jeito que está indica 20 segundos. Para execuções longas basta você pode usar 60 segundos (1 minuto) ou 3600 (1 hora).

outra alternativa:

pip install GPUtil

e

python -c "from GPUtil import showUtilization as gpu_usage; gpu_usage()"



7- Dica: Checando versões

No shell execute:

Python
python --version

Pytorch
python -c "import torch; print(torch.__version__)"

ou

pip freeze | grep torch


Cuda e Driver 
nvidia-smi


Transformers
python -c "import transformers; print(transformers.__version__)"

ou 

pip freeze | grep transformers

Dica sobre transformers:
Pode ocorrer conflito entre versões e o tipo de instalação, por exemplo nas duas formas acima aparencem versões diferentes, nete caso recomendo, desinstalar e instalar novamente:

conda uninstall -c huggingface transformers
pip uninstall transformers
pip install transformers

Jsonnet
pip freeze | grep _jsonnet

8-Dica: Limpeza do disco


Limpando a lixeira:
rm -rf ~/.local/share/Trash/*

Limpando cache do Huggingface:
rm -rf ~/.cache/huggingface/transformers/*
rm -rf ~/.cache/huggingface/hub/*

9-Dica: Jupyter Notebook 500 : Internal Server Error

conda uninstall nbconvert
pip uninstall nbconvert
conda install nbconvert==5.6.1 

Caso dê erro para abrir o Jupyter notebook reinstale usando conda:

conda install jupyter notebook

Para ver os caminhos do Jupyter:
jupyter --paths

Para ver a versão:
jupyter --version
pip freeze | grep nbconvert

Mais informações aqui.

terça-feira, 16 de fevereiro de 2021

Preparando um computador para processamento de Deep Learning

 Este post visa a descrever alguns passos básicos para disponibilizar um computador para processamento de modelos de aprendizado profundo (deep learning) que pode ser acessado remotamente por uma equipe que trabalha com Inteligência Artificial.

Este post é inspirado em: How to build a deep learning server based on Docker

Não vou tratar aspectos de hardware.

Vamos ao passo-a-passso.

1- Sistema operacional Linux

Irei usar o Ubuntu 20.04LTS desktop. Recomendo fortemente a versão que for LTS (Long-Term Support), pois isso irá garantir suporte por mais tempo. 

Obs.: Usar a versão server exige mais esforço administrativo, eu recomendo que seja usada quando você já sabe antecipadamente os usos do equipamento caso envolva pesquisa ou uso exploratório de deep learning a versão desktop facilita um pouco a vida.


2- Ativar Firewall

É altamente recomendavel ativar o Firewall UFW 

Primeiro vamos deixar permitr o acesso remoto via SSH:

sudo ufw allow ssh

Agora vamos deixar permitr o acesso remoto via NoMachine (mais informações abaixo):

sudo ufw allow 4000/tcp

sudo ufw allow 4443/tcp

Para ativar use:

sudo ufw enable

Dica:

Caso precise desabilitar o Firewall durante algum debug use: sudo ufw disable

Para saber o estatus  do Firewall use: sudo ufw status verbose

Para negar acesso use deny no lugar de allow.

3- Acesso Remoto SSH

Um equipamento com esta finalidade que vai ser acessado por uma equipe precisa ser acessado remotamente e a forma mais básica é com o shell e com segurança, para isso instale o Secure Shell (SSH):

sudo apt update

sudo apt install openssh-server

Instale também o sshfs, que permite a transferência de arquivos. Mais instruções aqui.

sudo apt-get install sshfs

Teste: em um outro computador Linux (funciona também a partir do Windows 10 command prompt ou PowerShell) use:

ssh username@ip_address


4- Instale o Screen

O Screen é uma aplicação de console que permite retomar uma seção que foi perdida, entre outras funções. Mais informações aqui.

sudo apt-get update

sudo apt-get install screen

5- Acesso Remoto Gráfico

Pela interface gráfica:

Download o NoMachine para Linux eu usei o NoMachine para Linux DEB amd64

Instale o NoMachine, com duplo clique sobre o arquivo, no meu caso o arquivo que baixei foi:

nomachine_7.1.3_1_amd64.deb

E clique em install.

Também instale no computador que você vai usar para acessar o equipamento de deep learning.


OU

Por linha de comando

https://www.nomachine.com/pt-pt/download/download&id=1

Download o NoMachine para Linux eu usei o NoMachine para Linux DEB amd64

e copiei na máquina (usando o sshfs), no meu caso eu baixei: 

nomachine_7.8.2_1_i686.tar.gz


sudo cp -p nomachine_7.8.2_1_i686.tar.gz /usr

cd /usr

sudo tar zxf nomachine_7.8.2_1_i686.tar.gz

sudo /usr/NX/nxserver --install


Mais informaçoes .: Install, update e uninstall via linha de comando no Ubuntu: 2.6. DEB Packages  


5- Duck DNS

Acesse DuckDNS efetue o login e crie um nome para o domínio.

Para instalar siga as intruções:

ps -ef | grep cr[o]n

crie um diretório duckdns:

cd /etc

sudo mkdir duckdns

sudo chmod 777 duckdns

Entre no diretório:

cd duckdns

Instale o curl:

sudo apt install curl

Crie um arquivo duck.sh no diretório duckdns incluindo o seguinte conteúdo, trocando o domain e o token pelos que você criou:

echo url="https://www.duckdns.org/update?domains=exampledomain&token=a7c4d0ad-114e-40ef-ba1d-d217904a50f2&ip=" | curl -k -o /etc/duckdns/duck.log -K -



Mude a permissão do arquivo para que ele possa ser executado

sudo chmod 777 duck.sh

Programe o cron para que o arquivo duck.sh seja executado a cada 5 min

crontab -e

Selecione o editor nano

copie o seguinte texto no final do arquivo:

*/5 * * * * /etc/duckdns/duck.sh >/dev/null 2>&1

pressione:

CRTL + o  para salvar

CRTL + x para sair


Para testar use o comando:

./duck.sh

Veja se o conteúdo do log é OK (quer dizer que funcionou) se for KO reveja o token e o dominio se estão corretos no arquivo duck.sh

cat duck.log

Estando OK, delete o arquivo duck.log e veja se ele é gerado novamente depois de 5 minutos, examine o conteúdo se é OK.

Neste ponto pode ser necessário fazer um redirecionamento de porta para que seja possível acessar um computador da rede interna com uma conexão da internet.


6- Instalando NVIDIA driver

Instruções aqui: guia de instalação.

Driver:

Faça o download do driver na própria máquina, pois ele detecta automaticamente.

O arquivo tem a exetensão NVIDIA-Linux-<alguma coisa>.run

chmod 700 NVIDIA-Linux-<alguma coisa>.run

sudo ./NVIDIA-Linux-<alguma coisa>.run


Para instalação do CUDA siga as instruções daqui.


7- Adicione Hard Disks

É comum em equipamentos como este ter HDs adicionais, aqui segue uma intrução básica para adiciona-los:

No Ubunto use o Disks que está em Aplications
Selecione o HD novo e format como Ext4
anote o caminho /dev/sd?

Crie o diretório:
sudo mkdir /mnt/<nome>

Para montar o HD de maneira permante é preciso adicionar linhas no  fstab, mas antes precisamos decobri o UUID correspondente ao HD  que se refere ao /dev/sd?, o blkid vai mostrar
uma linha equivalente a esta:
/dev/sd?: LABEL="files" UUID="<valor alfa numérico>" TYPE="ext4"

o valor alfa númerico tem este formato: 3dc00cf1-4663-45d2-a9c7-672e15335431

sudo blkid


sudo nano /etc/fstab
adicione no final uma linha como esta abaixo. Cuidado! Use o conteúdo do UUID que você obteve pelo blkid sem as "aspas" e indique o diretório que você criou em /mnt/<nome>, use CRTL+O para savar e CRTL+X para sair.

UUID=3dc00cf1-4663-45d2-a9c7-672e15335431 /mnt/<nome>      ext4    defaults        0       0


Permita o acesso, aqui é dado acesso total:
sudo chmod 777 /mnt/<nome>

8- Mudando nome do computador

É comum que o equipamento tenha um nome padronizado, para mudar o nome edite /etc/hostname e /etc/hosts

sudo nano /etc/hostname

use CRTL+O para savar e CRTL+X para sair.


sudo nano /etc/hosts

use CRTL+O para savar e CRTL+X para sair.

Para o novo nome valer é necessário reiniciar o computador:

sudo reboot


9- Mudando nome do usuário root

É comum que o usuário root tenha um nome e senha padronizados.

Com o usuário root mude a senha:

sudo passwd <nome atual>

Para mudar o nome do usuário root crie um outra conta que pretença o grupo sudo (veja o item 11- Crei as contas para cada usuário). Efetue o login com esta outra conta e execute, substituindo os campos marcados pelo nome novo e o nome atual


sudo usermod -l <nome novo> -d /home/<nome novo> -m <nome atual>

sudo reboot

O nome novo já está funcionando para uma conexão ssh, mas na tela inicial gráfica do Ubunto ainda não mudou.

Efetue o login com a conta nova (o nome atual ainda aparece) através da tela inicial gráfica do Ubuntu 

Em Settings / Users  troque o nome atual pelo novo.


10- Configure máquina para ligar automaticamente quando tiver queda de energia


A configuração na BIOS varia de fabricante para fabricante, mais informações aqui.

No meu caso a configuração foi a seguinte:
  1. Ligue a máquina e pressione Delete ou F2 - Entra configuração da BIOS
  2. F7 para entrar em Advanded Mode
  3. APM Configuration
  4. Restore AC Power Loss -> Last State  
Você pode escolher Power On, neste caso sempre que volta a energia a máquina liga sozinha, mesmo se ela estiver intensionamente desligada. O Last State é mais completo, a se a máquina estava ligada quando a energia volta ela liga sozinha e se estava desligada continua desligada.

Nota1: Isso tem a função de evitar um deslocamento para simplesmente ligar a máquina no caso de queda de energia.
Nota2: Para que a máquina continue operando em caso de queda de energia, você deve tem um NoBreak, mas lembre-se que ele suporta a máquina por pouco tempo. Em soluções profissionais usa-se NoBreaks e também Geradores. O NoBreak serve de segurança enquanto o Gerador entra em operação e estabiliza. Soluções completas assim exigem manutenção, testes periódios e garantir que o Gerador tenha seu combustível.

11- Crie as contas para cada usuário



terça-feira, 3 de março de 2020

Gerando tflite a partir de modelo já treinado (conversão para TensorFlow Lite)

https://www.tensorflow.org/install/pip?lang=python3


Instalando Tensorflow 1.13

Depois de instalado Python 3.6.x

Instalar pip (download https://bootstrap.pypa.io/get-pip.py)
python get-pip.py

Instala virtualenv

pip3 install -U pip virtualenv
vai para o raiz
cd\
cria o ambiente virtual DLenv (Deep Learnign Environment)
virtualenv --system-site-packages -p python ./DLenv
entra no diretório
cd DLenv\Scripts
activate
pip install --upgrade pip
pip install --upgrade tensorflow
pip install --upgrade https://storage.googleapis.com/tensorflow/windows/cpu/tensorflow-1.12.0-cp36-cp36m-win_amd64.whl

Tensorflow cd checando a versão:

python -c "import tensorflow as tf; from tensorflow.python.framework.versions import VERSION; print(tf.VERSION)"




https://www.tensorflow.org/lite/convert/python_api

Exporting a GraphDef from file

import tensorflow as tf

graph_def_file = "C:\tmp\my_object_detection\inference_graph_ssdlite_mobilenet_v2_coco\frozen_inference_graph.pb"
input_arrays = ["input"]
output_arrays = ["MobilenetV1/Predictions/Softmax"]

converter = tf.lite.TFLiteConverter.from_frozen_graph(
  graph_def_file, input_arrays, output_arrays)
tflite_model = converter.convert()
open("converted_model.tflite", "wb").write(tflite_model)



Post inspirado em:
https://medium.com/tensorflow/training-and-serving-a-realtime-mobile-object-detector-in-30-minutes-with-cloud-tpus-b78971cf1193

Sobre TensorFlow lite em Android:
https://www.tensorflow.org/lite/guide/android


Gerando tflite a apartir de .pb


set CONFIG_FILE=C:\tmp\my_object_detection\inference_graph_ssdlite_mobilenet_v2_coco\pipeline.config
set CHECKPOINT_PATH=C:\tmp\my_object_detection\inference_graph_ssdlite_mobilenet_v2_coco\model.ckpt
set OUTPUT_DIR=C:\tmp\my_object_detection\inference_graph_ssdlite_mobilenet_v2_coco\tflite

python C:\tensorflow1\models\research\object_detection\export_tflite_ssd_graph.py --pipeline_config_path=%CONFIG_FILE% --trained_checkpoint_prefix=%CHECKPOINT_PATH% --output_directory=%OUTPUT_DIR% --add_postprocessing_op=true



gerou arquivos:
tflite_graph.pb
tflite_graph.pbtxt





Capturando frames usando o DJI UX SDK

Existem situações en que é necessário capturar frames para realizar alguma analise ou processamento.

Existem exemplos da DJI que realizam esta tarefa, como o Android Video Stream Decoding Sample.

Na prática muitos desenvolvedores preferem utilizar o UX SDK, pois ele facilita o desenvolvimento com widgets interessantes.

O exemplo básico o uso do UX SDK é o Android-UXSDKDemo. Recomendo realizar o download e instalar no celular para conhecer o funcionamento deste demo original.

Aqui vou apresentar uma forma de extrair frames a partir deste exemplo.

Meu ambiente:

Android Studio 3.3
Build #AI-182.5107.16.33.5199772, built on December 25, 2018
JRE: 1.8.0_152-release-1248-b01 amd64
JVM: OpenJDK 64-Bit Server VM by JetBrains s.r.o
Windows 10 10.0

Códigos completos no GitHub:
Sem OpenCV: https://github.com/Marchanjo/UXSDKDemo-CaptureFrames
Com OpenCV: https://github.com/Marchanjo/UXSDKDemo-OpenCV

Passo 1 - Adicionar o OpenCV

a)
New Import OpenCV Module
C:\opencv-3.4.3\OpenCV-android-sdk\sdk\java

b)
Add no build.gradle
implementation project(':openCVLibrary343')


c)
Add Native Libraries
De: C:\opencv-3.4.3\OpenCV-android-sdk\sdk\native\libs
Para: C:\Archanjo\GitHub\UXSDKDemo-OpenCV\UXSDKDemo\app\src\main
renomeando libs para jniLibs

d)build.gradle do openCVLibrary343

 compileSdkVersion 28
    buildToolsVersion "28.0.3"

    defaultConfig {
        minSdkVersion 8
        targetSdkVersion 28

e)android.manifest do  openCVLibrary343
retirar linha do minSDK


Passo 2 - Ajustes no Layout


Iremos criar uma TextureView que não deve ser usada de maneira concomitante ao FPVWidget, pois ambas irão acessar os frames o que gera um excesso de processamento atrapalhando o funcionamento. Como não estaremos usando a FPVWidget os controles de Camera ficam sem sentido, logo podem ser retirados.

Retirar Widgets conflitantes.

Em activity_main.xml comentar:

dji.ux.widget.FPVWidget




Adicionar uma TextureView em RelativeLayout (na mesma seção onde foi comentado o dji.ux.widget.FPVWidget):

        android:id="@+id/livestream_preview"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_centerInParent="true"
        android:layout_gravity="center"
        android:alpha="50"
        android:visibility="visible"/>

       


Passo 3: Adicionar a classe: VideoDecodingApplication.java  com este conteúdo:

package com.dji.uxsdkdemo;

import android.app.Application;
import android.content.Context;

import dji.sdk.base.BaseProduct;
import dji.sdk.sdkmanager.DJISDKManager;

public class VideoDecodingApplication extends Application {

    private static BaseProduct mProduct;

    public static synchronized BaseProduct getProductInstance() {
        if (null == mProduct) {
            mProduct = DJISDKManager.getInstance().getProduct();
        }
        return mProduct;
    }

    public static synchronized void updateProduct(BaseProduct product) {
        mProduct = product;
    }

    @Override
    protected void attachBaseContext(Context base) {
        super.attachBaseContext(base);
        com.secneo.sdk.Helper.install(VideoDecodingApplication.this);
    }
}

Passo4: Adicionar a classe:  CaptureFrame.java com este conteúdo:



package com.dji.uxsdkdemo;

import android.content.Context;
import android.graphics.ImageFormat;
import android.graphics.Rect;
import android.graphics.SurfaceTexture;
import android.graphics.YuvImage;
import android.os.Environment;
import android.util.Log;
import android.view.TextureView;
import android.view.View;
import android.widget.ImageButton;
import android.widget.Toast;

import org.opencv.core.Mat;
import org.opencv.imgcodecs.Imgcodecs;
import org.opencv.imgproc.Imgproc;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.ByteBuffer;

import dji.common.camera.SettingsDefinitions;
import dji.common.error.DJIError;
import dji.common.product.Model;
import dji.common.util.CommonCallbacks;
import dji.sdk.base.BaseProduct;
import dji.sdk.camera.Camera;
import dji.sdk.camera.VideoFeeder;
import dji.sdk.codec.DJICodecManager;
import dji.thirdparty.afinal.core.AsyncTask;

import static org.opencv.core.CvType.CV_8UC1;
import static org.opencv.core.CvType.CV_8UC4;
import org.opencv.android.BaseLoaderCallback;
import org.opencv.android.LoaderCallbackInterface;
import org.opencv.android.OpenCVLoader;
import org.opencv.core.CvType;
import org.opencv.core.Size;

import static org.opencv.imgproc.Imgproc.cvtColor;

public class CaptureFrame {
    private static final String TAG = MainActivity.class.getName();
    private DJICodecManager mCodecManager;//Marcelo
    private VideoFeeder.VideoFeed standardVideoFeeder;//Marcelo
    protected VideoFeeder.VideoDataListener mReceivedVideoDataListener = null;//Marcelo
    private Camera mDroneCamera;//Marcelo
    private TextureView videostreamPreviewTtView;//Marcelo
    private int videoViewWidth;//Marcelo
    private int videoViewHeight;//Marcelo
    private ImageButton screenShot;//Marcelo
    private int  count;//Marcelo
    private Context appContext;

    public CaptureFrame(Context appContext, TextureView videostreamPreviewTtView) {
        this.appContext = appContext;
        this.videostreamPreviewTtView = videostreamPreviewTtView;
        videostreamPreviewTtView.setVisibility(View.VISIBLE);
        openCVStart();
    }


    public CaptureFrame(Context appContext,ImageButton screenShot, TextureView videostreamPreviewTtView) {
        this.appContext = appContext;
        this.screenShot = screenShot;
        screenShot.setSelected(false);
        screenShot.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                handleYUVClick();//Captura 1 frame a cada 30
                //handleYUVClickSingleFrame();//Captura somente um frame
            }
        });

        this.videostreamPreviewTtView = videostreamPreviewTtView;
        videostreamPreviewTtView.setVisibility(View.VISIBLE);
        openCVStart();
    }

    public void openCVStart() {
        if (!OpenCVLoader.initDebug()) {
            Log.d(TAG, "Internal OpenCV library not found. Using OpenCV Manager for initialization");
            OpenCVLoader.initAsync(OpenCVLoader.OPENCV_VERSION_3_4_0, appContext,mLoaderCallback);
        } else {
            Log.d(TAG, "OpenCV library found inside package. Using it!");
            mLoaderCallback.onManagerConnected(LoaderCallbackInterface.SUCCESS);
        }
    }

    private BaseLoaderCallback mLoaderCallback = new BaseLoaderCallback(appContext) {
        @Override
        public void onManagerConnected(int status) {
            switch (status) {
                case LoaderCallbackInterface.SUCCESS: {
                    Log.i(TAG, "OpenCV loaded successfully");
                    //mOpenCvCameraView.enableView();
                    //mOpenCvCameraView.setOnTouchListener(MainActivity.this);
                }
                break;
                default: {
                    super.onManagerConnected(status);
                }
                break;
            }
        }
    };

    public void onPause() {
        if (mDroneCamera != null) {
            if (VideoFeeder.getInstance().getPrimaryVideoFeed() != null) {
                VideoFeeder.getInstance().getPrimaryVideoFeed().removeVideoDataListener(mReceivedVideoDataListener);
            }
            if (standardVideoFeeder != null) {
                standardVideoFeeder.removeVideoDataListener(mReceivedVideoDataListener);
            }
        }
    }

    public void onDestroy() {
        if (mCodecManager != null) {
            mCodecManager.cleanSurface();
            mCodecManager.destroyCodec();
        }
    }

    public void onResume() {
        initSurfaceOrTextureView();
        notifyStatusChange();
    }


    private void showToast(String s) {
        Toast.makeText(videostreamPreviewTtView.getContext(), s, Toast.LENGTH_SHORT).show();
    }

    private long lastupdate;

    private void notifyStatusChange() {
        final BaseProduct product = VideoDecodingApplication.getProductInstance();
        Log.d(TAG, "notifyStatusChange: " + (product == null ? "Disconnect" : (product.getModel() == null ? "null model" : product.getModel().name())));

        if (product != null && product.isConnected() && product.getModel() != null) {
            showToast(product.getModel().name() + " Connected ");
        } else {
            showToast("Disconnected");
        }

        // The callback for receiving the raw H264 video data for camera live view
        mReceivedVideoDataListener = new VideoFeeder.VideoDataListener() {

            @Override
            public void onReceive(byte[] videoBuffer, int size) {
                if (System.currentTimeMillis() - lastupdate > 1000) {
                    Log.d(TAG, "camera recv video data size: " + size);
                    lastupdate = System.currentTimeMillis();
                }

                if (mCodecManager != null) {
                    mCodecManager.sendDataToDecoder(videoBuffer, size);

                }

            }
        };

        if (null == product || !product.isConnected()) {
            mDroneCamera = null;
            showToast("Disconnected");
        } else {
            if (!product.getModel().equals(Model.UNKNOWN_AIRCRAFT)) {
                mDroneCamera = product.getCamera();
                mDroneCamera.setMode(SettingsDefinitions.CameraMode.SHOOT_PHOTO, new CommonCallbacks.CompletionCallback() {
                    @Override
                    public void onResult(DJIError djiError) {
                        if (djiError != null) {
                            showToast("can't change mode of camera, error:" + djiError.getDescription());
                        }
                    }
                });


                if (VideoFeeder.getInstance().getPrimaryVideoFeed() != null) {
                    VideoFeeder.getInstance().getPrimaryVideoFeed().addVideoDataListener(mReceivedVideoDataListener);
                }

            }
        }
    }

    private void initSurfaceOrTextureView() {//Marcelo
        initPreviewerTextureView();
    }

    /**
     * Init a fake texture view to for the codec manager, so that the video raw data can be received
     * by the camera
     */
    private void initPreviewerTextureView() {
        videostreamPreviewTtView.setSurfaceTextureListener(new TextureView.SurfaceTextureListener() {
            @Override
            public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
                Log.d(TAG, "real onSurfaceTextureAvailable");
                videoViewWidth = width;
                videoViewHeight = height;
                Log.d(TAG, "real onSurfaceTextureAvailable: width " + videoViewWidth + " height " + videoViewHeight);
                if (mCodecManager == null) {
                    mCodecManager = new DJICodecManager(videostreamPreviewTtView.getContext(), surface, width, height);
                }
            }

            @Override
            public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
                videoViewWidth = width;
                videoViewHeight = height;
                Log.d(TAG, "real onSurfaceTextureAvailable2: width " + videoViewWidth + " height " + videoViewHeight);
            }

            @Override
            public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
                if (mCodecManager != null) {
                    mCodecManager.cleanSurface();
                }
                return false;
            }

            @Override
            public void onSurfaceTextureUpdated(SurfaceTexture surface) {

            }
        });
    }

  /*  public void onClick(View v) {
        if (v.getId() == R.id.activity_main_screen_shot) {
            handleYUVClick();
        }
    }*/

  /*  private void handleYUVClick() {
        if (screenShot.isSelected()) {
            screenShot.setText("Screen Shot");
            screenShot.setSelected(false);
            mCodecManager.enabledYuvData(false);
            mCodecManager.setYuvDataCallback(null);
        } else {//Começa a capturar frames
            screenShot.setText("Live Stream");
            screenShot.setSelected(true);
            mCodecManager.enabledYuvData(true);
            mCodecManager.setYuvDataCallback(this);
        }
    }*/

 //Captura 1 frame a cada 30 frames - funciona OK
    private void handleYUVClick() {
        if (screenShot.isSelected()) {
            showToast("Stop Capturing Frames ");
            screenShot.setImageResource(R.drawable.ic_burst_mode);
//            screenShot.setText("Screen Shot");
            screenShot.setSelected(false);
            mCodecManager.enabledYuvData(false);
            mCodecManager.setYuvDataCallback(null);
        } else {//Começa a capturar frames
            showToast("Capturing Frames ");
            screenShot.setImageResource(R.drawable.ic_action_playback_stop);
//            screenShot.setText("Live Stream");
            screenShot.setSelected(true);
            mCodecManager.enabledYuvData(true);
            mCodecManager.setYuvDataCallback(new DJICodecManager.YuvDataCallback() {
                @Override
                public void onYuvDataReceived(final ByteBuffer yuvFrame, int dataSize, final int width, final int height) {
                    //In this demo, we test the YUV data by saving it into JPG files.
                    //DJILog.d(TAG, "onYuvDataReceived " + dataSize);
                    if (count++ % 30 == 0 && yuvFrame != null) {
                        final byte[] bytes = new byte[dataSize];
                        yuvFrame.get(bytes);
                        Log.i(TAG, "SaveFrame: " + count);
                        AsyncTask.execute(new Runnable() {
                            @Override
                            public void run() {
                                saveYuvDataToJPEG(bytes, width, height);
                            }
                        });
                    }
                }
            });
        }
    }

//Captura um único frame
    public void handleYUVClickSingleFrame() {
            showToast("Frame Captured");
            mCodecManager.enabledYuvData(true);
        Log.i(TAG, "SaveFrame01");
            mCodecManager.setYuvDataCallback(new DJICodecManager.YuvDataCallback() {
                @Override
                public void onYuvDataReceived(final ByteBuffer yuvFrame, int dataSize, final int width, final int height) {
                    if (count++ == 30 && yuvFrame != null){
                        Log.i(TAG, "SaveFrame02");
                        final byte[] bytes = new byte[dataSize];
                        Log.i(TAG, "SaveFrame03");
                        yuvFrame.get(bytes);
                        Log.i(TAG, "SaveFrame04");
                        saveYuvDataToJPEG(bytes, width, height);
                        Log.i(TAG, "SaveFrame05"); //ele demora entre 1 e 2 e demora mais entre o 5 e o 6 e parece que falha na segunda captura

                        mCodecManager.enabledYuvData(false);
                        Log.i(TAG, "SaveFrame06");
                        mCodecManager.setYuvDataCallback(null);
                        Log.i(TAG, "SaveFrame07");
                    }


                }
            });

    }





/*
    private void handleYUVClick() {
        //if (!screenShot.isSelected()) {
          //  savedScreenShot=false;
            //screenShot.setText("Live Stream");
          //  screenShot.setSelected(true);
     Log.i(TAG, "SaveFrame1");
     saveOneFrame=true;
     mCodecManager.enabledYuvData(true);
     mCodecManager.setYuvDataCallback(new DJICodecManager.YuvDataCallback() {
         @Override
         public void onYuvDataReceived(ByteBuffer byteBuffer, int i, int i1, int i2) {
             teste isso
         }
     });

         //   while(savedScreenShot==false) {
         //       sleep(100);
         //   }
     //savedScreenShot=true;
     //screenShot.setText("Screen Shot");
     //screenShot.setSelected(false);

        veja como fazer com um único click
                talvez usar
        https://developer.android.com/reference/android/os/AsyncTask

     Log.i(TAG, "SaveFrame3");
     mCodecManager.enabledYuvData(false);
        Log.i(TAG, "SaveFrame4");
        mCodecManager.setYuvDataCallback(null);
        Log.i(TAG, "SaveFrame5");

       // }
    }
*/
 /* @Override
    public void onYuvDataReceived(final ByteBuffer yuvFrame, int dataSize, final int width, final int height) {
        //In this demo, we test the YUV data by saving it into JPG files.
        //DJILog.d(TAG, "onYuvDataReceived " + dataSize);
      if (count++ % 30 == 0 && yuvFrame != null) {//if (saveOneFrame == true && yuvFrame != null) {
            saveOneFrame=false;
            final byte[] bytes = new byte[dataSize];
            yuvFrame.get(bytes);
            Log.i(TAG, "SaveFrame: " + count);
            AsyncTask.execute(new Runnable() {
                @Override
                public void run() {
                    saveYuvDataToJPEG(bytes, width, height);
                }
            });
        }
    }*/

    private void saveYuvDataToJPEG(byte[] yuvFrame, int width, int height) {
        if (yuvFrame.length < width * height) {
            //DJILog.d(TAG, "yuvFrame size is too small " + yuvFrame.length);
            return;
        }

        byte[] y = new byte[width * height];
        byte[] u = new byte[width * height / 4];
        byte[] v = new byte[width * height / 4];
        byte[] nu = new byte[width * height / 4]; //
        byte[] nv = new byte[width * height / 4];

        System.arraycopy(yuvFrame, 0, y, 0, y.length);
        for (int i = 0; i < u.length; i++) {
            v[i] = yuvFrame[y.length + 2 * i];
            u[i] = yuvFrame[y.length + 2 * i + 1];
        }
        int uvWidth = width / 2;
        int uvHeight = height / 2;
        for (int j = 0; j < uvWidth / 2; j++) {
            for (int i = 0; i < uvHeight / 2; i++) {
                byte uSample1 = u[i * uvWidth + j];
                byte uSample2 = u[i * uvWidth + j + uvWidth / 2];
                byte vSample1 = v[(i + uvHeight / 2) * uvWidth + j];
                byte vSample2 = v[(i + uvHeight / 2) * uvWidth + j + uvWidth / 2];
                nu[2 * (i * uvWidth + j)] = uSample1;
                nu[2 * (i * uvWidth + j) + 1] = uSample1;
                nu[2 * (i * uvWidth + j) + uvWidth] = uSample2;
                nu[2 * (i * uvWidth + j) + 1 + uvWidth] = uSample2;
                nv[2 * (i * uvWidth + j)] = vSample1;
                nv[2 * (i * uvWidth + j) + 1] = vSample1;
                nv[2 * (i * uvWidth + j) + uvWidth] = vSample2;
                nv[2 * (i * uvWidth + j) + 1 + uvWidth] = vSample2;
            }
        }
        //nv21test
        byte[] bytes = new byte[yuvFrame.length];
        System.arraycopy(y, 0, bytes, 0, y.length);
        for (int i = 0; i < u.length; i++) {
            bytes[y.length + (i * 2)] = nv[i];
            bytes[y.length + (i * 2) + 1] = nu[i];
        }

        //Marcelo OpenCV
        Mat myuv = new Mat(height + height / 2, width, CV_8UC1);


        myuv.put(0,0,bytes);//carga da matriz

        Mat picBGR = new Mat(height, width, CV_8UC4);

        cvtColor(myuv, picBGR, Imgproc.COLOR_YUV2BGRA_NV21);

        Mat mOut = new Mat(picBGR.height(),picBGR.width(), CvType.CV_8UC4);
        Mat mIntermediate = new Mat(picBGR.height(),picBGR.width(), CvType.CV_8UC4);

        final String path = Environment.getExternalStorageDirectory() + "/DJI_ScreenShot" + "/ScreenShot_" + System.currentTimeMillis() +"_OpenCV.jpg";
        Log.i(TAG, "OpenCV path: " + path);


        Imgproc.blur(picBGR, mIntermediate, new Size(3, 3));
        Imgproc.Canny(mIntermediate, mOut, 80, 100);

        Imgcodecs.imwrite(path, mOut);
        //showImg(mOut);
        //fim Meu OpenCV

        Log.i(TAG, "SaveFrame 04a");
        screenShot(bytes, Environment.getExternalStorageDirectory() + "/DJI_ScreenShot", width, height);
        Log.i(TAG, "SaveFrame 04b");
    }
/* não funcionou
    private void showImg(Mat img) {
        Log.i(TAG, "OpenCV show 01: ");
        Bitmap bm = Bitmap.createBitmap(img.cols(), img.rows(),Bitmap.Config.ARGB_8888);
        Log.i(TAG, "OpenCV show 02: ");
        Utils.matToBitmap(img, bm);
        Log.i(TAG, "OpenCV show 03: ");
        //videostreamPreviewSf.setVisibility(View.GONE);quando ativei esta linha começou a dar problema,
        //videostreamPreviewTtView.setVisibility(View.GONE);
        //videostreamPreviewOpenCV.setVisibility(View.GONE);
        //dá problema quando usa imageView, veja se é conflito com textura ou a chamada neste ponto (testar uma das linhas acima e sem imagaView)
        //imageView.setVisibility(View.VISIBLE);quando ativei esta linha começou a dar problema,
        Log.i(TAG, "OpenCV show 04: ");
        //imageView.setImageBitmap(bm);quando ativei esta linha começou a dar problema,
        Log.i(TAG, "OpenCV show 05: ");
    }*/

    /**
     * Save the buffered data into a JPG image file
     */
    private void screenShot(byte[] buf, String shotDir, int width, int height) {
        File dir = new File(shotDir);
        if (!dir.exists() || !dir.isDirectory()) {
            dir.mkdirs();
        }
        YuvImage yuvImage = new YuvImage(buf,
                ImageFormat.NV21,
                width,
                height,
                null);
        OutputStream outputFile;
        final String path = dir + "/ScreenShot_" + System.currentTimeMillis() + ".jpg";
        try {
            outputFile = new FileOutputStream(new File(path));
        } catch (FileNotFoundException e) {
            Log.e(TAG, "test screenShot: new bitmap output file error: " + e);
            return;
        }
        if (outputFile != null) {
            yuvImage.compressToJpeg(new Rect(0,
                    0,
                    width,
                    height), 100, outputFile);
            //Log.e(TAG, "Ori path: " + path);
        }
        try {
            outputFile.close();
        } catch (IOException e) {
            Log.e(TAG, "test screenShot: compress yuv image error: " + e);
            e.printStackTrace();
        }
        /*runOnUiThread(new Runnable() {
            @Override
            public void run() {
                displayPath(path);
            }
        });*/
    }


}


Passo 4 - Adicionar chamadas no MainActivity.java


private CaptureFrame frameAccess;

dentro do onCreate:

frameAccess = new CaptureFrame(this, (TextureView) findViewById(R.id.livestream_preview));

@Override
    protected void onPause() {//Marcelo
        frameAccess.onPause();
        super.onPause();
    }

    @Override
    protected void onDestroy() { //Marcelo
        frameAccess.onDestroy();
        super.onDestroy();
    }

    @Override
    protected void onResume() {//Marcelo
        super.onResume();
        frameAccess.onResume();//depois do super.onResume
    }

Passo 5 - Capturar frame


frameAccess.handleYUVClickSingleFrame();//Captura somente um frame



Conceitos básicos em Deep Learning

Introdução



Antes de começar é preciso destacar alguns termos que serão necessários para o entendimento deste post, vou destacar os termos em inglês para ajudar em futuras buscas, pois existe muito material em inglês sobre este assunto.

O objetivo aqui é classificar (to classify) imagens, que significa apresentar um imagem (ou frame em um vídeo) e detectar algum objeto na imagem e classificá-lo. Por exemplo, encontrar um cachorro.

Em aprendizado de máquina (machine learning) para chegarmos no momento de classificar imagens precisamos primeiro treinar (to train) o modelo (model). Redes neurais profundas (deep neural network) são redes neurais que contém muitas camadas escondidas (hidden layers). É muito comum se usar o termo deep learning quando se está treinando redes neurais profundas, este é o nosso caso.

Treinar significa colocar etiquetas (labels) em fotos, muitas fotos 600, 800, 1000.... quanto mais melhor. Para colocar as etiquetas use o LabelImg

O tempo de treinamento é muito demorado e depende muito do computador que será usado, pode demorar muitos dias e até semanas. É comum utilizar computadores com GPUs (placas graficas NVidia são perferidas) para minimizar este tempo.

Uma estratégia amplamente utilizada é usar um modelo já treinado (pre-trained model) para uma base de imagens (por exemplo: COCO dataset, the Kitti dataset, the Open Images dataset) e re-treina-lo (retrain). Modelos pré-treinados podem ser encontrados no Tensorflow detection model zoo.

Depois que você tem um modelo re-treinado (ou se você simplesmente quer usar um modelo treinado) você faz a inferência (inference) (o inference graph é o arquivo .pb) que é o objetivo final: fazer predições (predictions) ou melhor classificar o conteúdo de uma imagem (ou frame em um vídeo).

Agora falta falar do framework, que é a plataforma em que se faz tanto o treinamento quanto a inferência. Existem alguns frameworks bastante utilizados: PyTorch, Caffe2 e Tensorflow. Nós iremos usar o Tensorflow.

Obs.: Glossário de machine learning.


terça-feira, 16 de abril de 2019

Usando modelo quantizado ssd mobilenet v1 no exemplo Android do Tensoflow Lite para Object Detection

colocando modelo quantizado ssd mobilenet v1 no Android
1- App
https://github.com/tensorflow/examples/tree/master/lite/examples/object_detection
Baixe no seu computador, abra com o Android Studio, e teste no seu celular

2- Download do modelo (ssd_mobilenet_v1_quantized_300x300_coco14_sync_2018_07_18):
https://github.com/tensorflow/models/blob/master/research/object_detection/g3doc/detection_model_zoo.md
Especificamente
http://download.tensorflow.org/models/object_detection/ssd_mobilenet_v1_0.75_depth_quantized_300x300_coco14_sync_2018_07_18.tar.gz


3- Convertendo
funcionou (4 outputs), QUANTIZED_UINT8 (tflite de 6,7MB): :
toco --graph_def_file=C:\tmp\my_object_detection\ssd_mobilenet_v1_quantized_300x300_coco14_sync_2018_07_18-ModelZoo\tflite_graph.pb --output_file=C:\tmp\my_object_detection\ssd_mobilenet_v1_quantized_300x300_coco14_sync_2018_07_18-ModelZoo\tflite\ssd_mobilenet_v1_quantized_300x300_coco14_sync_2018_07_18.tflite --input_shapes=1,300,300,3 --input_arrays=normalized_input_image_tensor --output_arrays=TFLite_Detection_PostProcess,TFLite_Detection_PostProcess:1,TFLite_Detection_PostProcess:2,TFLite_Detection_PostProcess:3 --inference_type=QUANTIZED_UINT8 --mean_values=128 --std_dev_values=128 --change_concat_input_ranges=false --allow_custom_ops

2-Labels (opcional, pois o labelmap.txt original serve para este modelo, mas vou colocar a instrução mesmo assim)
https://github.com/nightrome/cocostuff/blob/master/labels.txt
troque ": " por ";" mude a extensão para .csv, abra no Excel, delete a primeira coluna.
salve como txt.
substitue a primeira linha de "unlabeled" por "???" (sem aspas)
delete todas linhas depois da 91, O arquivo deve ter 91 linhas e a última label é toothbrush. (São 90 objetos fora a primeira linha que tem ???)
Salve o arquivo como: labels_map_ssd_mobilenet_v1_quantized_300x300_coco14_sync_2018_07_18.txt

3-Copie para:
C:\object_detection\app\src\main\assets
(este diretório só vai existir depois de abrir o Android Studio e sincronizar tudo)

4-Edite DetectorActivity.java para usar o modelo e as labels.

private static final String TF_OD_API_MODEL_FILE = "ssd_mobilenet_v1_quantized_300x300_coco14_sync_2018_07_18.tflite";

Se o arquivo com labels foi trocado use isso, senão use labelmap.txt original.
private static final String TF_OD_API_LABELS_FILE = "file:///android_asset/labels_map_ssd_mobilenet_v1_quantized_300x300_coco14_sync_2018_07_18.txt";

Para este modelo use o mesmo do exemplo para estas variáveis:
private static final int TF_OD_API_INPUT_SIZE = 300;
private static final boolean TF_OD_API_IS_QUANTIZED = true;

Transfer Learning (re-trainnig a deep learning model)

Retrain a deep learning model ssd_mobilenet_v1_0.75_depth_quantized_coco using a custom dataset.


Post based in: https://github.com/EdjeElectronics/TensorFlow-Object-Detection-API-Tutorial-Train-Multiple-Objects-Windows-10#2-set-up-tensorflow-directory-and-anaconda-virtual-environment


Instructions using Windows 10 and TensorFlow 1.13

Preparation


First, install TensorFlow according to these instructions.

Install Object Detection, instructions here.

Donwload de models/master and copy to your machine, see in https://github.com/tensorflow/models/releases the release that you download and rename models-master to tensorflow plus version

For instance, when I write this post de release was 1.11 (it is not the TensorFlow version), so I rename models-master to:
c:\tensorflow1.11  (I will assume this name until the end of this post)

Protobuf

Download the last version of Protocol Buffers:
https://developers.google.com/protocol-buffers/docs/downloads

In my case the current version is 3.7.1, I download and copy to my machine in:
C:\Program Files (x86)\protoc-3.7.1.

From: C:\tensorflow1.11\research

Execute the command (point to the protoc.exe in your machine):
"C:\Program Files (x86)\protoc-3.7.1\bin\protoc" --python_out=. .\object_detection\protos\anchor_generator.proto .\object_detection\protos\argmax_matcher.proto .\object_detection\protos\bipartite_matcher.proto .\object_detection\protos\box_coder.proto .\object_detection\protos\box_predictor.proto .\object_detection\protos\calibration.proto .\object_detection\protos\eval.proto .\object_detection\protos\faster_rcnn.proto .\object_detection\protos\faster_rcnn_box_coder.proto .\object_detection\protos\grid_anchor_generator.proto .\object_detection\protos\hyperparams.proto .\object_detection\protos\image_resizer.proto .\object_detection\protos\input_reader.proto .\object_detection\protos\losses.proto .\object_detection\protos\matcher.proto .\object_detection\protos\mean_stddev_box_coder.proto .\object_detection\protos\model.proto .\object_detection\protos\optimizer.proto .\object_detection\protos\pipeline.proto .\object_detection\protos\post_processing.proto .\object_detection\protos\preprocessor.proto .\object_detection\protos\region_similarity_calculator.proto .\object_detection\protos\square_box_coder.proto .\object_detection\protos\ssd.proto .\object_detection\protos\ssd_anchor_generator.proto .\object_detection\protos\string_int_label_map.proto .\object_detection\protos\train.proto .\object_detection\protos\keypoint_box_coder.proto .\object_detection\protos\multiscale_anchor_generator.proto .\object_detection\protos\graph_rewriter.proto


It will generate .py files.

Environment Variables

Create a variable named PYTHONPATH
and add
C:\tensorflow1.11\models
C:\tensorflow1.11\models\research
C:\tensorflow1.11\models\research\slim

also add in PATH:
C:\tensorflow1.11\models
C:\tensorflow1.11\models\research
C:\tensorflow1.11\models\research\slim

Obs: Point to your directories, the name used here correspond to my installation.


Setup 


From: C:\tensorflow1.11\research

python setup.py build

python setup.py install

Preparing the object_detection directory

Model

Download and extract the pre-trained model:
ssd_mobilenet_v1_0.75_depth_quantized_coco
More models here.

Copy the directory ssd_mobilenet_v1_0.75_depth_quantized_300x300_coco14_sync_2018_07_18 to:

C:\tensorflow1.11\research\object_detection

Training directory

Create an empty training directory:

C:\tensorflow1.11\research\object_detection\training

Copy 2 files, inside this directory:

labelmap.pbtxt (see item 5a)

Copy:
C:\tensorflow1\models\research\object_detection\inference_graph_ssdlite_mobilenet_v2_coco\pipeline.config

To:
 C:\tensorflow1\models\research\object_detection\training

Rename to:
ssd_mobilenet_v1_0.75_depth_quantized_300x300_coco14_sync_2018_07_18.config

And edit to your directories:

Edit ssd_mobilenet_v1_0.75_depth_quantized_300x300_coco14_sync_2018_07_18.config
in: train_config

batch_size: 1
fine_tune_checkpoint: "C:/tensorflow1.11/research/object_detection/ssd_mobilenet_v1_0.75_depth_quantized_300x300_coco14_sync_2018_07_18/model.ckpt"
from_detection_checkpoint: true
eplicas_to_aggregate: 1


train_input_reader
  label_map_path: "C:/tensorflow1.11/research/object_detection/training/labelmap.pbtxt"
  input_path: "C:/tensorflow1.11/research/object_detection/train.record"


eval_config:
  num_examples: 76     

eval_input_reader
  label_map_path: "C:/tensorflow1.11/research/object_detection/training/labelmap.pbtxt"
     input_path: "C:/tensorflow1.11/research/object_detection/test.record"


Dataset

Copy:
test.record
train.record


to:
 C:\tensorflow1.11\models\research\object_detection



Retraining

From:
C:\tensorflow1.11\research

Execute
python C:\tensorflow1.11\research\object_detection\legacy\train.py --logtostderr --train_dir=object_detection\training --pipeline_config_path=object_detection\training\ssd_mobilenet_v1_0.75_depth_quantized_300x300_coco14_sync_2018_07_18.config


After, open another prompt, activate your Tensorflow environment and from:
C:\tensorflow1.11\research

execute:
tensorboard --logdir=training





Export Inference Graph

Create a directory:
inference_graph
inside:
C:\tensorflow1.11\research\object_detection


python C:\tensorflow1.11\research\object_detection\export_inference_graph.py --input_type image_tensor --pipeline_config_path C:\tensorflow1.11\research\object_detection\training\pipeline.config --trained_checkpoint_prefix training/model.ckpt-XXXX --output_directory C:\tensorflow1.11\research\object_detection\inference_graph

Where XXXX must be the highest number in the model.ckpt files generated.

This command will generate the: frozen_inference_graph.pb and model.ckpt files.

For TensorFlow Lite

Export TensorFlow Lite SSD Graph

Create a directory:
tflite
inside:
C:\tensorflow1.11\research\object_detection\inference_graph


python C:\tensorflow1.11\research\object_detection\export_tflite_ssd_graph.py --pipeline_config_path=C:\tensorflow1.11\research\object_detection\training\ssd_mobilenet_v1_0.75_depth_quantized_300x300_coco14_sync_2018_07_18.config --trained_checkpoint_prefix=C:\tensorflow1.11\research\object_detection\inference_graph\model.ckpt --output_directory=C:\tensorflow1.11\research\object_detection\inference_graph\tflite --add_postprocessing_op=true

This command will generate the: tflite_graph.pb and tflite_graph.pbtxt



toco --graph_def_file=C:\tensorflow1.11\research\object_detection\inference_graph\tflite\tflite_graph.pb --output_file=C:\tensorflow1.11\research\object_detection\inference_graph\tflite\ssd_mobilenet_v1_quantized_300x300_coco14_sync_2018_07_18.tflite --input_shapes=1,300,300,3 --input_arrays=normalized_input_image_tensor --output_arrays=TFLite_Detection_PostProcess,TFLite_Detection_PostProcess:1,TFLite_Detection_PostProcess:2,TFLite_Detection_PostProcess:3 --inference_type=QUANTIZED_UINT8 --mean_values=128 --std_dev_values=128 --change_concat_input_ranges=false --allow_custom_ops

This command will generate the: ssd_mobilenet_v1_quantized_300x300_coco14_sync_2018_07_18.tflite






Errors

c_api.TF_GetCode(self.status.status))
tensorflow.python.framework.errors_impl.InvalidArgumentError: NewRandomAccessFile failed to Create/Open: C:     ensorfloesearch\object_detection        raining\labelmap.pbtxt : A sintaxe do nome do arquivo, do nome do diret\udcf3rio ou do r\udcf3tulo do volume est\udce1 incorreta.
; Unknown error

In .config file write paths with / not \ , example:
C:/tensorflow1.11/research/object_detection/training/labelmap.pbtxt

not:
C:\tensorflow1\models\research\object_detection\training\labelmap.pbtxt



ValueError: not enough values to unpack (expected 7, got 0)
Edit ssd_mobilenet_v1_0.75_depth_quantized_300x300_coco14_sync_2018_07_18.config
in: train_config
change: replicas_to_aggregate: 8
to:replicas_to_aggregate: 1




WARNING:root:Variable [MobilenetV1/Conv2d_0/BatchNorm/beta] is not available in checkpoint
https://github.com/tensorflow/models/issues/4862


Edit ssd_mobilenet_v1_0.75_depth_quantized_300x300_coco14_sync_2018_07_18.config
in: train_config
add: from_detection_checkpoint: true

More information: