Integração e Uso do OpenCV
Introdução ao Uso da Biblioteca OpenCV
[editar | editar código]Aplicações imagéticas exigem técnicas computacionais, seja para aperfeiçoar a informação pictórica à percepção humana ou para facilitar a interpretação e compreensão da máquina a esse tipo de dado. Surge, portanto, o interesse em métodos de processamento de imagem e de visão computacional.
De modo a possibilitar tal manipulação imagética, o OpenCV (Open Source Computer Vision Library) se configura como a maior biblioteca de código aberto do mundo para esse fim, onde são abrigados mais de 2500 algoritmos[1] de visão computacional, aprendizado de máquina e processamento de imagens. Ele possui, oficialmente, suporte para C++, Python, Java e Matlab[2].
Aplicações de Processamento de Imagens e Visão Computacional
[editar | editar código]A própria biblioteca do OpenCV possui uma documentação, contendo uma descrição mais precisa e adequada para cada algoritmo, repartida em módulos de processamento de imagens, utilitários aplicativos, calibração de câmera e reconstrução 3D, detecção de objetos e redes neurais profundas (dnn)[3]. Ainda assim, nesse tutorial, serão apresentadas algumas aplicações para ilustrar a grande capacidade dessa biblioteca.
Suavização de Ruído
[editar | editar código]Naturalmente, imagens possuem ruídos, cujas razões técnicas são variadas (condições ambientais, problemas na sensorização, quantização, transmissão e processamento). Tais pertubações podem prejudicar na acurácia e aprendizado de um modelo de Machine Learning, dificultam a identificação de objetos celestes, no contexto astronômico, no diagnóstico de doenças, no contexto médico.[4]
Nesse viés, a biblioteca do OpenCV apresenta ferramentas para o tratamento adequado, visando a suavização e redução de ruído. Entre elas, estão o filtro da Média e da Gaussiana (lineares) e da Mediana (não-linear). A ideia principal é atribuir menos peso às intensidades de ruído, suavizando-as com as regiões desejadas.
A seguir, segue-se uma sequência de imagens tiradas do Museu do Ipiranga (São Paulo-SP) em 2024. A aplicação artificial do ruído sal-e-pimenta para fins práticos pode ser resolvida com a aplicação de filtros de suavização.
Melhoria de Contraste e Nitidez
[editar | editar código]Abordagens que visem o aumento do contraste e da nitidez podem ser observadas no OpenCV também. Há técnicas computacionais e estatísticas que manipulem o histograma de distribuição das intensidades dos pixels, tornando-o o mais uniforme possível.[5] Assim, no contexto imagético, tais metodologias possibilitam que detalhes antes pouco observáveis possam ser vistos com mais clareza e devidamente tratados.
Abaixo, segue-se uma sequência de fotografias tiradas panorâmicas do Pico do Jaraguá (São Paulo-SP) em 2023. Na imagem original, notam-se que as montanhas ao fundo estão, praticamente, fundidas ao céu, com poucos detalhes e baixo contraste. Com a aplicação do filtro de Equalização de Histograma, esses aspectos se tornam mais nítidos, evidenciando-se uma clara separação entre esses elementos imagéticos, além de tornar mais visível o detalhamento das nuvens.
Métodos de Realce
[editar | editar código]Algortimos de detecção de bordas são úteis, identificando e destacando objetos, ao realçar seus contornos em uma dada imagem. A biblioteca do OpenCV tem a capacidade de armazenar algumas dessas ferramentas que se baseiam no uso de derivadas, ao analisar as curvas e taxas de variação de intensidade dos pixels, como o Filtro de Sobel (que utiliza a primeira), o Laplaciano (a segunda) e o de Canny (que tem fundamento no kernel de suavização gaussiano).[6]
A sequência posterior ilustra as aplicações das ferramentas de realce para identificar as silhuetas das edificações e das nuvens na foto de panorama da Avenida Paulista (São Paulo-SP), tirada em 2025. A aplicação laplaciana denota bem os prédios, mas, possui menos contraste sobre as nuvens. Por outro lado, as aplicações em derivada de Sobel são características e marcantes ao seu eixo e realçam bem as nuvens. O filtro de magnitude de Sobel (que calcula a norma euclidiana das derivadas primeira dos eixos) é bem mais marcado que o Laplaciano, mas, ambos denotam bem o realce dos objetos imagéticos.
Reconhecimento de Faces e de Gestos
[editar | editar código]A detecção e identificação facial em imagens ou vídeos também são ténicas inclusas no pacote de ferramentas desenvolvidas na biblioteca do OpenCV. Em especial, ela emprega uma abordagem de aprendizado de máquina que envolve modelos pré-treinados, como o cascata Haar para esse fim, facilitando esse processamento computacional. É válido notar que tais modelos precisam de grandes quantidades de dados, que envolvam uma variabilidade de ângulos e iluminação dos objetos, de modo a melhorar a acurácia possível e prever a maior parte dos cenários possíveis reais.[7]
Não é raro encontrar em repositórios públicos, uma sequência de aplicações dessa funcionalidade, sobretudo, as que envolvem a detecção de gestos com as mãos em tempo real. Tais funcionalidades envolvem o controle de dispositivos, detecção de linguagem de sinais, interatividade com jogos virtuais.[8]
Nesse viés, a dupla de imagens ilustra uma aplicação do reconhecimento de faces. Na sequência de quadros ( Caipira Picando Fumo, por Almeida Júnior, 1893. Domínio público), tirados no museu da Pinacoteca (São Paulo-SP) em 2025, é possível verificar que o modelo identificou corretamente todos os rostos da imagens (das três pinturas), ainda que tenha associado outros objetos não relacionados a rostos também.
Integração em Aplicações em Kotlin
[editar | editar código]Embora existam vários tutoriais para integração da biblioteca do OpenCV a aplicações do Android Studio[9], a pretensão desse tutorial é mitigar os requisitos de hardware recomendados[10] e possibilitar que qualquer sistema escrito em Kotlin consiga ser integrada a essa biblioteca.
Aplicações em Python e Utilizados via ProcessBuilder()
[editar | editar código]A priori, uma forma de integrar aplicações em Kotlin à biblioteca OpenCV é o uso de scripts intermediários escritos em linguagens que possuem maior suporte, como o Python. Esse método facilita a integração e não exige tantos recursos computacionais.
Script em Python (Processing.py)
[editar | editar código]import numpy as np
import cv2
from matplotlib import pyplot as plt
import sys
import os
path_output = "/path/to/your/output/file"
def RecognizeFaces(path : str):
# Fine-tunning hyperparameters
scale_factor = 1.1
min_neighbor = 5
x_rec = 5
y_rec = 5
model = cv2.CascadeClassifier(cv2.data.haarcascades + 'haarcascade_frontalface_default.xml')
img = cv2.imread(path)
gray_img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
faces = model.detectMultiScale(gray_img, scaleFactor=scale_factor, minNeighbors=min_neighbor, minSize=(x_rec, y_rec))
for (x, y, w, h) in faces:
cv.rectangle(img, (x, y), (x + w, y + h), (0, 255, 0), 2)
cv.imwrite(f"{path_output}/recognizeFaces.png", img)
print('recognizeFaces.png')
if __name__ == "__main__":
if len(sys.argv) > 1:
path = sys.argv[1]
filter = sys.argv[2]
if filter == "RecognizeFaces":
RecognizeFaces()
Classe em Kotlin (Image.kt)
[editar | editar código]packend backend.models
import java.awt.*
import java.io.*
import javax.swing.*
import java.nio.file.Paths
import java.io.File
import java.io.BufferedReader
import java.io.InputStreamReader
import kotlinx.serialization.Serializable
import kotlinx.serialization.*
import kotlinx.serialization.Contextual
@Serializable
data class Image(private val path: String) {
@Contextual private val imageFile : File
private val languageScript : String
private val languageDest : String
init {
this.imageFile = File(this.path)
// Error
if (!imageFile.exists()) {
println("Image not found!")
}
// Settings
this.languageScript = "path/to/Pyhton/script"
this.languageDest = "python3" // Ou apenas "python", se você está usando ambientes Conda
}
private fun Executing(nameFunction : String) : String {
val process = ProcessBuilder(this.languageDest, this.languageScript, this.path, nameFunction).start()
val leitor = BufferedReader(InputStreamReader(process.inputStream))
val firstLine = leitor.readLine()
return firstLine
}
fun RecognizeFaces() : String {
val nameFunction : String = "RecognizeFaces"
return this.Executing(nameFunction)
}
}
Note que a classe Image.kt está executando o arquivo Processing.py, como se fosse um script, ao utilizar a função ProcessBuilder()[11] e criando um novo processo no Sistema Operacional. Isto é, o equivalente a execução da linha de comandos posteriormente descrita num Shell.
python3 Processing.py /path/to/input/file RecognizeFaces
Para receber valores de retorno do script em Python, também é possível utilizar as funções BufferedReader()[12] e InputStreamReader()[13], que obtém todo o stream de output padrão (stdout) do processo criado pelo ProcessBuilder(). Nesse caso, evidenciado pela função de print() no Python, que recebe o nome da nova imagem criada após a transformação.
A biblioteca pode ser instalada, via Python, ao uso do seguinte comando.
pip install opencv-python
Contudo, é importante ter atenção a alguns detalhes. A fim de se evitar problemas com conflitos, deve-se remover versões antigas e/ou manualmente instaladas da biblioteca. Além disso, vale verificar se a versão do pip é requisito mínimo de suporte para a versão requisitada do OpenCV a ser inclusa no sistema.[14]
Aplicações em Ktor
[editar | editar código]
A posteriori, outro método de integração é via as dependências do Ktor. Para isso, é necessário baixar o pacote oficial da própria biblioteca do OpenCV https://opencv.org/releases/ e instalar o .zip do código fonte. Após a descompressão, é importante seguir os seguintes passos.
path/to/opencv-version-source-code$ mkdir build
path/to/opencv-version-source-code$ cd build
A seguir, é necessário configurar o CMake.
cmake -DBUILD_SHARED_LIBS=ON -DBUILD_opencv_java=ON -DCMAKE_BUILD_TYPE=Release ..
Após a conclusão desta configuração, é necessário compilar os arquivos.
make -j$(nproc)
No diretório path/to/opencv-version-source-code/build/bin/, haverá o compilado opencv-version-source-code.jar e em path/to/opencv-version-source-code/build/lib/, libopencv-version-source-code.so. Eles serão importantes na integração do projeto em Ktor à biblioteca do OpenCV.
Na aplicação em Ktor, a garantia de integração, depende de alguns passos. A inserção do compilado .jar no diretório ktor-application-name/build/libs/, e a do .so em ktor-application-name/build/native/ (este será necessário ser criado, se ainda não existir). Além disso, no arquivo, build.gradle.kts, deve haver a adição da dependência.
dependencies {
// ... Outras dependências ...
+ implementation(files("build/libs/opencv-version-source-code.jar"))
}
Na main do projeto (Normalmente, em Application.kt), é importante assegurar que o .so seja carregado diretamente com o System.load(). Isso evita problemas com java.library.path no Linux
import org.opencv.core.Core
fun main(args: Array<String>) {
System.load("${System.getProperty("user.dir")}/build/native/libopencv-version-source-code.so")
io.ktor.server.netty.EngineMain.main(args)
}
Finalmente, para se utilizar a biblioteca em classes do projeto em Ktor.
Classe em Kotlin (Image.kt)
[editar | editar código]package backend.classes
import java.awt.*
import java.io.*
import javax.swing.*
import java.nio.file.Paths
import java.io.File
import java.io.BufferedReader
import java.io.InputStreamReader
import kotlinx.serialization.Serializable
import kotlinx.serialization.*
import kotlinx.serialization.Contextual
/*
É essencial essas importações para um uso adequado do OpenCV
*/
import org.opencv.core.*
import org.opencv.imgcodecs.Imgcodecs
import org.opencv.imgproc.Imgproc
@Serializable
data class Image(private val path: String) {
@Contextual private val imageFile : File
private val outputPath : String
init {
try {
val libPath = "${System.getProperty("user.dir")}/build/native/libopencv-version-source-code.so"
System.load(libPath)
} catch (e: UnsatisfiedLinkError) {
println("Error to load OpenCV ${e.message}")
}
this.imageFile = File(this.path)
// Error
if (!imageFile.exists()) {
println("Image not found!")
}
this.outputPath = "path/to/output/file/directory"
}
fun Gaussian() : String {
val src = Imgcodecs.imread(this.path)
val blurred = Mat()
Imgproc.GaussianBlur(src, blurred, Size(15.0, 15.0), 0.0)
val result = Imgcodecs.imwrite("${this.outputPath}/output/file", blurred)
if (result) {
println("Sucess transformation in ${this.outputPath}")
}
else {
println("Failed to save transformation")
}
val outputFile = "${this.outputPath}/output/file"
return outputFile
}
}
Esta segunda forma é potencialmente melhor, a fim de evitar problemas de confiltos de dependências de pacotes em Pyhton (que poderia ser resolvido com ambientes conda[15] ou uv[16]).
Referências
[editar | editar código]- ↑ https://opencv.org/about/
- ↑ https://medium.com/@mohinisaxena/which-programming-languages-are-officially-supported-by-opencv-5ac17401dc0d
- ↑ https://docs.opencv.org/4.x/index.html
- ↑ https://www.unite.ai/pt/what-is-noise-in-image-processing-a-primer/
- ↑ https://link.springer.com/article/10.1007/s00371-022-02723-8
- ↑ https://cse442-17f.github.io/Sobel-Laplacian-and-Canny-Edge-Detection-Algorithms/
- ↑ https://www.datacamp.com/pt/tutorial/face-detection-python-opencv
- ↑ https://medium.com/%40odil.tokhirov/how-i-built-a-hand-gesture-recognition-model-in-python-part-1-db378cf196e6
- ↑ https://opencv.org/android/
- ↑ https://developer.android.com/studio/install?hl=pt-br
- ↑ https://docs.oracle.com/javase/8/docs/api/java/lang/ProcessBuilder.html
- ↑ https://docs.oracle.com/javase/8/docs/api/java/io/BufferedReader.html
- ↑ https://docs.oracle.com/javase/8/docs/api/java/io/InputStream.html
- ↑ https://pypi.org/project/opencv-python/
- ↑ https://docs.conda.io/projects/conda/en/stable/index.html
- ↑ https://astral.sh/blog/uv












