Introdução
Nossos smartphones estão ganhando cada vez mais poder de processamento, e com isso, surge a possibilidade de tirarmos algumas vantagens utilizando-os em tarefas que antes exigiam o uso de super-computadores. Como exemplo, temos o campo de visão computacional, que pode ser entendido como a capacidade de dispositivos capturarem, processarem, analisarem e entenderem imagens da mesma forma que o sistema de visão humana. Basicamente, nós podemos utilizar o grande poder de processamento dos smartphones modernos para processar imagens capturadas através da câmera do dispositivo. A melhor forma de fazer tal tarefa, é utilizando a biblioteca OpenCV.
OpenCV (acrônimo de Open Source Computer Vision Library) é uma biblioteca de código aberto (licença BSD) de visão computacional e aprendizado de máquina que apesar de ser desenvolvida em c/c++ tem suporte a maioria das plataformas de desenvolvimento, incluindo Android.
Neste post nós vamos aprender como integrar o OpenCV no nosso aplicativo android. Para isso será desenvolvido um aplicativo que aplica o detector de borda Canny na imagem exibida através da câmera do dispositivo.
Criando um Projeto Android
Este post foi desenvolvido utilizando no seguinte ambiente:
- Mac OS 10.9.5
- Android Studio 1.3.2
- JDK 1.7.0
Para o restante do post, nós vamos assumir que você já tenha o Android Studio e o JDK instalados em seu computador, por esse motivo a instalação do Android Studio não será abordada neste post. Caso você ainda não tenha instalado, baste seguir os passos recomendados pelo site oficial do Android Studio (https://developer.android.com/sdk/index.html).
Let’s go!
Abra o android Studio e clique em “Start a new Android Studio Project”
Na tela seguinte, preencha o nome do projeto. Clique em “next”
Na próxima tela selecione marque o campo “Phone and Tablets” e selecione o SDK Mínimo, no meu caso vou escolher a API 15, que oferece suporte para 90,4% dos dispositivos que estão atualmente registrados no Google Play, mas sinta-se a vontade para escolher o que melhor lhe atender. Clique em “next”
Na próxima tela selecione “Black Activity”. Clique em “next”.
Na próxima tela vou manter o nome da activity como “MainActivity”, sinta-se a vontade para alterar como quiser. Clique em “finish”.
Pronto, estamos prontos para integrar o OpenCV ao nosso novo projeto. Mas antes, vamos rodar o projeto já criado e ver o resultado. Para isso, plugue um dispositivo com android no computador e no menu “run” clique em “run app”. Aparecerá uma tela para você selecionar em qual dispositivo deseja rodar a aplicação, selecione o dispositivo desejado e clique em “OK”.
Como resultado, a frase “Hello World!” será exibido na tela do telefone.
Caso você tenha alguma dificuldade em utilizar um dispositivo real, na documentação oficial tem mais informações sobre como proceder. Opcionalmente você pode utilizar um emulador para testar seus aplicativos.
Agora que nosso primeiro aplicativo já está funcionado, é hora de integrá-lo ao OpenCV.
Integrando Aplicativo Android com o OpenCV
- O próximo passo a ser feito é baixar o OpenCV para android versão 3.0, que está disponível no site do OpenCV (http://opencv.org/downloads.html). Será feito o download de um arquivo de 163MB chamado OpenCV-3.0.0-android-sdk-1.zip. Salve-o em um diretório de sua preferência.
- Descompacte o arquivo OpenCV-3.0.0-android-sdk-1.zip.
- Volte ao Android Studio e clique em “File” > “New” > “Import Module”
- Selecione o diretório “sdk/java” que está dentro da pasta OpenCV-android-sdk que você descompactou. Clique em “next”.
- Na tela que aparece, mantenha todos os checkbox marcados e clique em “finish”.
- Atualize o arquivo MyOpencvApp/openCVLibrary300/build.gradle para que os valores dos campos: compileSdkVersion, buildToolsVersion, minSdkVersion e targetSdkVersion, coincidam com os do seu projeto. No meu caso ficou como na Figura abaixo:
- Adicione a dependência do módulo. Clique com botão direito sobre o projeto, no menu de contexto que vai abrir, selecione “Open Module Settings”.
- Na tela seguinte, selecione o módulo “app”, e em seguida clique no “+” para adicionar um “Module dependency”.
- Escolha o módulo “:openCVLibrary300” e clique em “ok”.
- Copie o diretório “libs” que está dentro de “sdk/native” do OpenCV para android que você fez o download para a pasta “/src/main” do seu projeto.
Seu projeto deve ficar organizado como o da Figura abaixo:
Com isso, nosso projeto está pronto para utilizar as bibliotecas do OpenCV e o próximo passo é implementar nosso detector de bordas.
Implementando o Detector de bordas
Agora vamos preparar a nossa interface para que possa exibir a imagem a partir da câmera do dispositivo. Para isso, vamos editar o arquivo “app/src/main/res/layout/activity_main.xml”, que deve ficar desta forma:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" xmlns:opencv="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent" > <org.opencv.android.JavaCameraView android:layout_width="match_parent" android:layout_height="match_parent" android:visibility="gone" android:id="@+id/HelloOpenCvView" opencv:show_fps="true" opencv:camera_id="any" /> </LinearLayout>
Após alterar o layout, adicione as seguinte permissões no seu “/app/src/main/AndroidManifest.xml”
<uses-permission android:name="android.permission.CAMERA"/> <uses-feature android:name="android.hardware.camera" android:required="false"/> <uses-feature android:name="android.hardware.camera.autofocus" android:required="false"/> <uses-feature android:name="android.hardware.camera.front" android:required="false"/> <uses-feature android:name="android.hardware.camera.front.autofocus" android:required="false"/>
Feita as alterações nos arquivos acima, vamos a implementação. Se durante a criação do projeto, você também colocou o nome da sua Activity principal como MainActivity como eu, o arquivo a ser editado é o MainActivity.java. Abra o este arquivo e faça a classe MainActivity implementar a interface CvCameraViewListeener2, como o código a seguir:
import org.opencv.android.CameraBridgeViewBase.CvCameraViewListener2; public class MainActivity extends AppCompatActivity implements CvCameraViewListener2 {
Aparecerá um erro falando que é necessário implementar os métodos abstratos da interface CvCameraViewListerner2, para isso, podemos utilizar um atalho do Android Studio. Clique com o botão direito em qualquer lugar na tela, no menu de contexto que abrir, escolha a opção “Generate…”, depois escolha “Implement methods…”, deixe os três métodos selecionados e clique em “OK”.
O método onCameraFrame é o que nos interessa. Ele é chamado quando é preciso ser feito a entrega de um frame, dentro deste método vamos receber o frame capturado pela câmera, fazer o processamento, e depois entregá-lo para ser exibido pela nossa aplicação. Mas antes de fazermos isto precisamos carregar o OpenCV. Para isso, precisamos implementar a classe abstrata BaseLoaderCallback que possuí o método onManagerConnected, que será chamado após o carregamento da biblioteca OpenCV.
BaseLoaderCallback mCallBack = new BaseLoaderCallback(this) { @Override public void onManagerConnected(int status) { switch (status){ case BaseLoaderCallback.SUCCESS: { Log.i(TAG, "OpenCV loaded successfully"); mOpenCvCameraView.enableView(); break; } default: { super.onManagerConnected(status); break; } } } };
O carregamento do OpenCV é feito no método onResume()
@Override protected void onResume() { super.onResume(); OpenCVLoader.initAsync(OpenCVLoader.OPENCV_VERSION_3_0_0,this,mCallBack); }
Pronto, agora podemos implementar o método onCameraFrame para fazer o processamento que desejamos. No início do post falamos que nossa aplicação implementaria o método de detecção de bordas Canny, para isso, nosso método deve ficar da seguinte forma:
@Override public Mat onCameraFrame(CameraBridgeViewBase.CvCameraViewFrame inputFrame) { Mat src = inputFrame.gray(); Mat cannyEdges = new Mat(); Imgproc.Canny(src, cannyEdges,10, 100); return cannyEdges; }
A classe MainActivity completa ficou da seguinte forma:
package br.com.badricio.myopencvapp; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.util.Log; import android.view.Menu; import android.view.MenuItem; import android.view.SurfaceView; import android.view.WindowManager; import org.opencv.android.BaseLoaderCallback; import org.opencv.android.CameraBridgeViewBase; import org.opencv.android.CameraBridgeViewBase.CvCameraViewListener2; import org.opencv.android.OpenCVLoader; import org.opencv.android.Utils; import org.opencv.core.Mat; import org.opencv.imgproc.Imgproc; public class MainActivity extends AppCompatActivity implements CvCameraViewListener2 { private static final String TAG = "MYAPP::OPENCV"; private CameraBridgeViewBase mOpenCvCameraView; BaseLoaderCallback mCallBack = new BaseLoaderCallback(this) { @Override public void onManagerConnected(int status) { switch (status){ case BaseLoaderCallback.SUCCESS: { Log.i(TAG, "OpenCV loaded successfully"); mOpenCvCameraView.enableView(); break; } default: { super.onManagerConnected(status); break; } } } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); setContentView(R.layout.activity_main); mOpenCvCameraView = (CameraBridgeViewBase) findViewById(R.id.HelloOpenCvView); mOpenCvCameraView.setVisibility(SurfaceView.VISIBLE); mOpenCvCameraView.setCvCameraViewListener(this); } @Override protected void onPause() { super.onPause(); if (mOpenCvCameraView != null) mOpenCvCameraView.disableView(); } @Override protected void onDestroy() { super.onDestroy(); if (mOpenCvCameraView != null) mOpenCvCameraView.disableView(); } @Override protected void onResume() { super.onResume(); OpenCVLoader.initAsync(OpenCVLoader.OPENCV_VERSION_3_0_0,this,mCallBack); } @Override public void onCameraViewStarted(int width, int height) { } @Override public void onCameraViewStopped() { } @Override public Mat onCameraFrame(CameraBridgeViewBase.CvCameraViewFrame inputFrame) { Mat src = inputFrame.gray(); Mat cannyEdges = new Mat(); Imgproc.Canny(src, cannyEdges,10, 100); return cannyEdges; } }
Resultados e considerações finais
Ao rodar a aplicação, se no dispositivo ainda não tiver a biblioteca OpenCV Manager, será exibido uma notificação perguntando ao usuário se ele deseja que a mesma instalada. A biblioteca OpenCV Manager está disponível no Google Play, e é o novo modelo de desenvolvimento para aplicativos que utilizam o OpenCV. Isso, porque traz alguns benefícios, que são: (i) Não há mais a necessidade de que cada aplicação traga uma cópia completa do OpenCV; (ii) As aplicações não precisam mais manter versões que são compatíveis com todas as plataformas; (iii) Evita a necessidade de que todas as aplicações clientes sejam atualizadas a cada nova versão do OpenCV. Mais informações sobre o OpenCV Manager.
O resultado obtido pelo aplicativo é o seguinte:
Observem que no canto superior esquerdo é exibido o número de frames por segundo que a aplicação consegue processar, essa string é exibida porque colocamos uma linha com “opencv:show_fps=true” no nosso arquivo de layout main_activity.xml.
Com este post entendemos os conceitos primários de como integrar o OpenCV ao nosso aplicativo android. Porém, aplicações de visão computacional geralmente exigem grande poder de processamento, o que tornar seu uso proibitivo em alguns cenários. Este gargalo é percebido quando tentamos percorrer uma imagem com o objetivo de processar cada pixel separadamente. Felizmente, para otimizar aplicações desta natureza o Android nos oferece outro recurso, que é usar o NDK para programar de baixo da camada de aplicações Java. Este, será o assunto dos próximos posts.
Referência
[1] http://docs.opencv.org/doc/tutorials/introduction/android_binary_package/dev_with_OCV_on_Android.html
[2] http://stackoverflow.com/questions/27406303/opencv-in-android-studio
[3] https://developer.android.com/index.html
[4] Joseph Howse. Android Application Programming with OpenCV 3. Packt Publishing, 2015
[5] Salil Kapur e Nisarg Thakkar. Mastering OpenCV Android Application Programming. Packt Publishing, 2015
ótimas dicas porem estou com problemas na implementação do código, você poderia enviar para meu e-mail. desde já grato
Simplesmente perfeito! Parabéns pelo material e muito obrigada XD
Ótimo, finalmente consegui fazer alguma coisa com opencv. Muito obrigado e aguardo ansiosamente o próximo post.
Parabéns pelo excelente post! Aguardando novos!
Perfeito parabéns executei esse projeto sem grandes problemas em um motog5 só que as permissões não aparecem em uma caixa de texto perguntando se desejas permitir o uso da câmera, Para contornar esse pequeno problema devesse ir em configurações/aplicações selecionar o app criado clicar em permissões e ativar o uso da câmera. só uma duvida tem algum tutorial ensinando integrar o opencv com o VR a API para realidade aumentada? se sim mande-me um e-mail por favor para moc.liamgnull@aicrag.sda.roinuj. abraços e obrigado pelo material me ajudou com um bug que estava tento ao adicionar as dependências do opencv. parabems
Tive o seguinte erro no debug do android:
No implementation found for void org.opencv.imgproc.Imgproc.Canny_3(long, long, double, double) (tried Java_org_opencv_imgproc_Imgproc_Canny_13 and Java_org_opencv_imgproc_Imgproc_Canny_13__JJDD)
Também tive esse mesmo erro.
ótimo post.
Poderia me ajudar em como recortar a área do retângulo detectado? Eu pedo as dimensões mas quando eu corto passando elas não fica no local correto.
Olá, tudo compilou direitinho, o problema encontrado foi na autorização da câmera, uma questão de permissão ou compatibilidade, poderia me ajudar?