Numpy Resize / Rescale Image

95

Eu gostaria de tirar uma imagem e mudar a escala da imagem, embora seja uma matriz numpy.

Por exemplo, tenho esta imagem de uma garrafa de coca-cola: garrafa-1

O que se traduz em uma matriz numpy de forma (528, 203, 3)e eu quero redimensionar para dizer o tamanho desta segunda imagem: garrafa-2

Que tem uma forma de (140, 54, 3).

Como altero o tamanho da imagem para uma determinada forma, mantendo a imagem original? Outras respostas sugerem remover todas as outras ou terceiras linhas, mas o que eu quero fazer é basicamente reduzir a imagem como você faria por meio de um editor de imagens, mas em código python. Existe alguma biblioteca para fazer isso no numpy / SciPy?

Brian Hamill
fonte
você pode mostrar o código para sua matriz numpy?
ShpielMeister
2
@sascha Obsoleto, de acordo com a página vinculada.
Paul Panzer
@ShpielMeister Não consigo fazer com que o IntelliJ imprima totalmente o array numpy, por alguma razão quando as saídas são grandes ele coloca ... o tempo todo, então eu só posso ver parte da saída do array no console
Brian Hamill

Respostas:

118

Sim, você pode instalar opencv(esta é uma biblioteca usada para processamento de imagens e visão computacional) e usar a cv2.resizefunção. E, por exemplo, use:

import cv2
import numpy as np

img = cv2.imread('your_image.jpg')
res = cv2.resize(img, dsize=(54, 140), interpolation=cv2.INTER_CUBIC)

Aqui imgestá, portanto, um array numpy contendo a imagem original, enquanto resum array numpy contendo a imagem redimensionada . Um aspecto importante é o interpolationparâmetro: existem várias maneiras de redimensionar uma imagem. Especialmente porque você reduz a escala da imagem e o tamanho da imagem original não é um múltiplo do tamanho da imagem redimensionada. Os esquemas de interpolação possíveis são:

  • INTER_NEAREST - uma interpolação do vizinho mais próximo
  • INTER_LINEAR - uma interpolação bilinear (usada por padrão)
  • INTER_AREA- reamostragem usando relação de área de pixel. Pode ser um método preferido para dizimação de imagens, pois fornece resultados sem moiré. Mas quando a imagem é ampliada, é semelhante ao INTER_NEARESTmétodo.
  • INTER_CUBIC - uma interpolação bicúbica sobre a vizinhança de pixels de 4x4
  • INTER_LANCZOS4 - uma interpolação Lanczos sobre a vizinhança de 8x8 pixels

Como a maioria das opções, não existe uma opção "melhor" no sentido de que, para cada esquema de redimensionamento, há cenários em que uma estratégia pode ser preferida em vez de outra.

Willem Van Onsem
fonte
5
Acabei de experimentar este código e funciona! Apenas uma mudança é que dsizedeve ser uma dsize=(54, 140)vez que leva x e depois y, onde uma matriz numpy mostra a forma como y, então x (y é o número de linhas ex é o número de colunas)
Brian Hamill
5
Tento evitar cv2, ele troca dimensões e carrega no formato de canal BGR. Eu prefiro skimage.io.imread('image.jpg')e skimage.transform.resize(img). scikit-image.org/docs/dev/install.html
Eduardo Pignatelli
1
@EduardoPignatelli Eu evito skimage.transform.resize porque você não tem controle sobre o algoritmo de interpolação que ele usa. Mas, isso pode não ser importante, dependendo dos casos de uso das pessoas.
Decker de
1
@Decker skimage.transform.resize fornece algum controle por meio do parâmetro 'ordem'. ordem = 0 é o vizinho mais próximo, 1 = bi-linear, 2 = bi-quadrático, 3 = bi-cúbico etc. No entanto, nenhuma média de área ou interpolação de lanczos.
Tapio Friberg
1
@TapioFriberg ahh sim, estou corrigido; Vejo os algoritmos definidos na documentação do parâmetro 'pedido' de skimage.transform.warp. Em algum ponto, pode ser útil atualizar os documentos para incluir referências para os tipos, "Bi-quártico", por exemplo, não está definido em nenhum outro lugar da documentação (em 10 de dezembro de 2019) - um one-liner pode ser benéfico para futuros usuários.
Decker de
64

Embora seja possível usar o numpy sozinho para fazer isso, a operação não é embutida. Dito isso, você pode usar scikit-image(que é baseado no numpy) para fazer esse tipo de manipulação de imagem.

A documentação de redimensionamento do Scikit-Image está aqui .

Por exemplo, você pode fazer o seguinte com sua imagem:

from skimage.transform import resize
bottle_resized = resize(bottle, (140, 54))

Isso cuidará de coisas como interpolação, anti-aliasing, etc. para você.

jakevdp
fonte
2
Obrigado! Essa resposta também funciona! Embora eu esteja tendo alguns problemas com a anti_aliasingsinalização, parece que ela foi removida da versão mais recente do 0.13.1
Brian Hamill
8
Isso retorna a imagem como float ndarray mesmo se sua imagem original for uint8
sziraqui
3
Esta é uma boa técnica porque funciona com qualquer número de canais. Tentei fazer isso com dados rgb combinados com dados de nuvem de pontos de profundidade e preservou o relacionamento como eu queria.
Darth Egregious
@DarthEgregious, jakevdp -> ele transformou meus dados de ruído aleatório em uma única cor quando redimensionei o array (137,236,3) para (64,64) como o método que você descreveu. Isso é normal porque parece que perdeu todas as informações?
Deshwal
1
Não deveria ser (64,64,3)
Darth Egregious
14

Para as pessoas que vêm do Google e procuram uma maneira rápida de reduzir a resolução de imagens em numpyarrays para uso em aplicativos de aprendizado de máquina, aqui está um método super rápido (adaptado daqui ). Este método só funciona quando as dimensões de entrada são um múltiplo das dimensões de saída.

Os exemplos a seguir reduzem a resolução de 128x128 para 64x64 (isso pode ser facilmente alterado).

Último pedido de canais

# large image is shape (128, 128, 3)
# small image is shape (64, 64, 3)
input_size = 128
output_size = 64
bin_size = input_size // output_size
small_image = large_image.reshape((output_size, bin_size, 
                                   output_size, bin_size, 3)).max(3).max(1)

Primeira ordem dos canais

# large image is shape (3, 128, 128)
# small image is shape (3, 64, 64)
input_size = 128
output_size = 64
bin_size = input_size // output_size
small_image = large_image.reshape((3, output_size, bin_size, 
                                      output_size, bin_size)).max(4).max(2)

Para imagens em tons de cinza, apenas altere o 3para 1assim:

Primeira ordem dos canais

# large image is shape (1, 128, 128)
# small image is shape (1, 64, 64)
input_size = 128
output_size = 64
bin_size = input_size // output_size
small_image = large_image.reshape((1, output_size, bin_size,
                                      output_size, bin_size)).max(4).max(2)

Este método usa o equivalente ao max pooling. É a maneira mais rápida de fazer isso que encontrei.

Waylon Flinn
fonte
4
imagem_grande [:, :: 2, :: 2] retorna a imagem com a resolução dividida pela metade.
L. Kärkkäinen
1
@ LasseKärkkäinen, mas não reduz a resolução, apenas seleciona todos os outros pixels. A diferença é que a função final 'max' pode ser alterada para selecionar ou calcular pixels de maneiras ligeiramente melhores (usando 'min' ou 'média', por exemplo). Seu método é útil (e mais rápido), se isso não importa.
Waylon Flinn
@ L.Kärkkäinen qual é o oposto disso à resolução dupla?
rayzinnz
2
@rayzinnznp.repeat(np.repeat(a, 2, axis=0), 2, axis=1)
L. Kärkkäinen
8

Se alguém veio aqui em busca de um método simples para dimensionar / redimensionar uma imagem em Python, sem usar bibliotecas adicionais, aqui está uma função de redimensionamento de imagem muito simples:

#simple image scaling to (nR x nC) size
def scale(im, nR, nC):
  nR0 = len(im)     # source number of rows 
  nC0 = len(im[0])  # source number of columns 
  return [[ im[int(nR0 * r / nR)][int(nC0 * c / nC)]  
             for c in range(nC)] for r in range(nR)]

Exemplo de uso: redimensionando uma imagem (30 x 30) para (100 x 200):

import matplotlib.pyplot as plt

def sqr(x):
  return x*x

def f(r, c, nR, nC):
  return 1.0 if sqr(c - nC/2) + sqr(r - nR/2) < sqr(nC/4) else 0.0

# a red circle on a canvas of size (nR x nC)
def circ(nR, nC):
  return [[ [f(r, c, nR, nC), 0, 0] 
             for c in range(nC)] for r in range(nR)]

plt.imshow(scale(circ(30, 30), 100, 200))

Resultado: imagem em escala

Isso funciona para reduzir / dimensionar imagens e funciona bem com matrizes entorpecidas.

Romwell
fonte
4

O imresize()método SciPy era outro método de redimensionamento, mas será removido a partir do SciPy v 1.3.0. SciPy se refere ao método de redimensionamento de imagem PIL :Image.resize(size, resample=0)

size - O tamanho solicitado em pixels, como uma tupla de 2: (largura, altura).
resample - Um filtro opcional de reamostragem. Pode ser um de PIL.Image.NEAREST (usar o vizinho mais próximo), PIL.Image.BILINEAR (interpolação linear), PIL.Image.BICUBIC (interpolação de spline cúbica) ou PIL.Image.LANCZOS (um filtro de downsampling de alta qualidade ) Se omitido, ou se a imagem tiver modo “1” ou “P”, é definido PIL.Imagem.NEAREST.

Link aqui: https://pillow.readthedocs.io/en/3.1.x/reference/Image.html#PIL.Image.Image.resize

Cemsazara
fonte
3
Infelizmente, imresize () está obsoleto, ele será removido no SciPy 1.3.0
MiniQuark
1

Há alguma biblioteca para fazer isso no numpy / SciPy

Certo. Você pode fazer isso sem OpenCV, scikit-image ou PIL.

O redimensionamento da imagem é basicamente mapear as coordenadas de cada pixel da imagem original para sua posição redimensionada.

Uma vez que as coordenadas de uma imagem devem ser inteiros (pense nisso como uma matriz), se a coordenada mapeada tem valores decimais, você deve interpolar o valor do pixel para aproximá-lo da posição do inteiro (por exemplo, obter o pixel mais próximo dessa posição é conhecido como interpolação de vizinho mais próximo ).

Tudo que você precisa é uma função que faça essa interpolação para você. SciPy tem interpolate.interp2d.

Você pode usá-lo para redimensionar uma imagem em uma matriz numpy, digamos arr, da seguinte maneira:

W, H = arr.shape[:2]
new_W, new_H = (600,300)
xrange = lambda x: np.linspace(0, 1, x)

f = interp2d(xrange(W), xrange(H), arr, kind="linear")
new_arr = f(xrange(new_W), xrange(new_H))

Claro, se sua imagem é RGB, você deve fazer a interpolação para cada canal.

Se você gostaria de entender mais, sugiro assistir Resizing Images - Computerphile .

yellow01
fonte
Pode não funcionar com base nesta resposta: stackoverflow.com/questions/37872171/…
random_dsp_guy
0
import cv2
import numpy as np

image_read = cv2.imread('filename.jpg',0) 
original_image = np.asarray(image_read)
width , height = 452,452
resize_image = np.zeros(shape=(width,height))

for W in range(width):
    for H in range(height):
        new_width = int( W * original_image.shape[0] / width )
        new_height = int( H * original_image.shape[1] / height )
        resize_image[W][H] = original_image[new_width][new_height]

print("Resized image size : " , resize_image.shape)

cv2.imshow(resize_image)
cv2.waitKey(0)
M. Farzalizadeh
fonte
4
Bem-vindo ao StackOverflow. Ótimo que você queira ajudar outras pessoas respondendo às suas perguntas. No entanto, não vejo como sua resposta agrega valor em comparação com a resposta existente que já usa cv2e usa uma função de redimensionamento adequada em vez de reimplementar uma função de redimensionamento "abaixo do ideal" que se sai pior do que a interpolação do vizinho mais próximo.
NOhs de