Impacto das diferenças de operações de imagem entre OpenCV e Pillow no deep learning

A maioria dos modelos de aprendizado profundo treinados com PyTorch utiliza Image.open do Pillow para carregar imagens e os transforms do torchvision, devido à ótima integração. Essa abordagem funciona bem em ambientes acadêmicos, mas expõe problemas críticos em cenários de produção industrial. Como a maioria das implantações em C++ ainda depende do OpenCV para processamento de imagens, observamos que a precisão do modelo pode cair de 10% a 60% ao usar OpenCV para redimensionamento (resize) em comparação com o pré‑procesasmento feito com Pillow. Essa enorme lacuna nos leva a investigar as diferenças pixel a pixel entre os dois frameworks.

Primeiro, comparamos a leitura de uma mesma imagem nos dois frameworks:

import cv2
from PIL import Image
import numpy as np

# Leitura com Pillow e conversão para uint8
pil_img = Image.open("test.jpg").convert("RGB")
arr_pil = np.array(pil_img, dtype=np.uint8)

# Leitura com OpenCV e ajuste de BGR para RGB
cv_img = cv2.imread("test.jpg")
cv_img_rgb = cv2.cvtColor(cv_img, cv2.COLOR_BGR2RGB)
arr_cv = np.array(cv_img_rgb, dtype=np.uint8)

# Erro médio absoluto na leitura
erro_leitura = np.mean(np.abs(arr_pil - arr_cv))
print(f"Erro médio de leitura: {erro_leitura}")

A diferença média observada é de aproximadamente 0,0003 por pixel (em valor absoluto), o que é desprezível. Confirmamos que o erro de leitura não é a causa principal da perda de precisão: ao usar a imagem lida pelo OpenCV convertida para o formato do Pillow e processá‑la com o pré‑processamento do Pillow, a inferência retorna o mesmo resultado que a leitura direta pelo Pillow.

Em seguida, analisamos a operação de redimensionamento utilizando o mesmo tipo de interpolação (bicúbica):

# Redimensionamento com Pillow
pil_resized = pil_img.resize((300, 300), Image.BICUBIC)
arr_pil_res = np.array(pil_resized, dtype=np.uint8)

# Redimensionamento com OpenCV (interpolação cúbica)
cv_resized = cv2.resize(cv_img_rgb, (300, 300), interpolation=cv2.INTER_CUBIC)
arr_cv_res = np.array(cv_resized, dtype=np.uint8)

# Erro médio no resize
erro_resize = np.mean(np.abs(arr_pil_res - arr_cv_res))
print(f"Erro médio de resize: {erro_resize}")

O erro médio obtido foi de aproximadamente 77 no valor de pixel (intervalo 0‑255), ou seja, cerca de um terço da faixa dinâmica. Isso evidencia que as implementações internas do algoritmo de redimensionamento são significativamente diferentes entre Pillow e OpenCV, mesmo usando o mesmo tipo de interpolação. Essa discrepância é a responsável pela queda drástica de precisão ao implantar um modelo treinado com Pillow em um ambiente que utiliza OpenCV.

Essa questão foi reportada em 2017 no repositório oficial do Pillow, mas os mantenedores não consideraram o comportamento incorreto e não realizaram alterações. Para evitar esse problema, recomenda‑se que, durante o treinamento de modelos, não se utilize o Pillow nem os transforms nativos do torchvision, mas sim o mesmo pipeline de pré‑processamento que será usado em produção (por exemplo, OpenCV). Dessa forma, garante‑se consistência entre treinamento e implantação, eliminando a lacuna causada por diferenças de framework.

Tags: OpenCV Pillow resize deep-learning-deployment Pytorch

Publicado em 7-23 07:22