Introdução aos dados de imagem
- 10 minutos
Em visão computacional, normalmente resolvemos um dos seguintes problemas:
- A Classificação de Imagens é a tarefa mais simples, quando precisamos de classificar uma imagem numa de muitas categorias pré-definidas, por exemplo, distinguir um gato de um cão numa fotografia, ou reconhecer um dígito manuscrito.
- A deteção de objetos é uma tarefa um pouco mais difícil, na qual precisamos de encontrar objetos conhecidos na imagem e localizá-los, ou seja, devolver a caixa delimitadora para cada um dos objetos reconhecidos.
- A segmentação é semelhante à deteção de objetos, mas em vez de fornecer uma caixa delimitadora, precisamos de devolver um mapa exato de píxeis que delineie cada um dos objetos reconhecidos.
Imagem do Curso CS231n de Stanford
Imagens como tensores
A Visão por Computador funciona com Imagens. Como provavelmente sabes, as imagens consistem em píxeis, por isso podem ser vistas como uma coleção retangular (matriz) de píxeis.
Na primeira parte deste módulo, vamos abordar o reconhecimento de dígitos manuscritos. Vamos usar o conjunto de dados MNIST, que consiste em imagens em tons de cinzento de dígitos manuscritos, 28x28 píxeis. Cada imagem pode ser representada como um array de 28x28, e os elementos deste array denotariam a intensidade do píxel correspondente – ou na escala de 0 a 1 (nesse caso são usados números de ponto flutuante), ou de 0 a 255 (inteiros). Uma biblioteca popular em Python chamada numpy é frequentemente usada em tarefas de visão computacional, porque permite operar eficazmente com arrays multidimensionais.
Para lidar com imagens a cores, precisamos de alguma forma de representar as cores. Na maioria dos casos, representamos cada píxel por 3 valores de intensidade, correspondentes aos componentes Vermelho (R), Verde (G) e Azul (B). Esta codificação de cor chama-se RGB, e assim a imagem a cores de tamanho W×H será representada como um array de tamanho H×W×3 (por vezes a ordem dos componentes pode ser diferente, mas a ideia é a mesma). Na representação por array, a altura (número de linhas) vem antes da largura (número de colunas), o que é o oposto da convenção comum de imagem W×H.
Matrizes multidimensionais também são chamados tensores. Usar tensores para representar imagens também tem uma vantagem, porque podemos usar uma dimensão extra para armazenar uma sequência de imagens. Por exemplo, para representar um fragmento de vídeo composto por 200 fotogramas com dimensão 800x600 (largura × altura), podemos usar o tensor de tamanho 200x600x800x3. Lembre-se que as dimensões tensoriais usam a ordem H×W (linha maior), e não a convenção W×H comum em editores de imagens. A ordem aqui é composta por frames × altura (600) × largura (800) × canais. Esta ordenação é conhecida como channels_last e é o padrão no TensorFlow; alguns outros frameworks colocam canais antes da altura e largura (channels_first).
import tensorflow as tf
import keras
import matplotlib.pyplot as plt
import numpy as np
# Prints the installed TensorFlow version
print(tf.__version__)
Vamos usar a estrutura Keras para as nossas experiências. Ao longo deste módulo, usamos import keras (o estilo de importação independente do Keras 3), que requer o TensorFlow 2.16 ou posterior (ou uma instalação autónoma via pip install keras>=3.0). Se estiveres a usar uma versão antiga do TensorFlow 2.x, substitui import keras por from tensorflow import keras.
(x_train, y_train), (x_test, y_test) = keras.datasets.mnist.load_data()
# Output: (60000, 28, 28) (60000,)
print(x_train.shape, y_train.shape)
# Output: (10000, 28, 28) (10000,)
print(x_test.shape, y_test.shape)
Visualize o conjunto de dados de dígitos
Agora que descarregámos o conjunto de dados, podemos visualizar alguns dos dígitos:
fig, ax = plt.subplots(1, 7)
for i in range(7):
ax[i].imshow(x_train[i])
ax[i].set_title(y_train[i])
ax[i].axis('off')
# Displays a row of seven handwritten digit images with their labels
Estrutura do conjunto de dados
Temos um total de 60.000 imagens de treino e 10.000 imagens de teste, e cada imagem tem um tamanho de 28×28 píxeis:
print('Training samples:', len(x_train))
print('Test samples:', len(x_test))
print('Tensor size:', x_train[0].shape)
print('First 10 digits are:', y_train[:10])
print('Type of data is ', type(x_train))
# Output:
# Training samples: 60000
# Test samples: 10000
# Tensor size: (28, 28)
# First 10 digits are: [5 0 4 1 9 2 1 3 1 4]
# Type of data is <class 'numpy.ndarray'>
Como pode ver, o tipo de dados é um numpy array. Cada intensidade de píxel é representada por um valor inteiro entre 0 e 255:
print('Min intensity value: ', x_train.min())
print('Max intensity value: ', x_train.max())
# Output:
# Min intensity value: 0
# Max intensity value: 255
A razão pela qual está entre 0 e 255 é porque cada píxel é representado por um inteiro de 8 bits. Em muitos casos, especialmente ao trabalhar com redes neurais, é mais conveniente escalar todos os valores para o intervalo [0, 1] dividindo por 255. Este processo chama-se normalização:
x_train = x_train.astype(np.float32) / 255.0
x_test = x_test.astype(np.float32) / 255.0
# Pixel values are now floating point numbers in the range [0, 1]
Agora temos os dados e estamos prontos para começar a treinar a nossa primeira rede neural!