Ubah Ukuran / Skala Ulang Gambar Numpy

98

Saya ingin mengambil gambar dan mengubah skala gambar, sementara itu adalah larik numpy.

Misalnya saya punya gambar botol coca-cola: botol-1

Yang diterjemahkan menjadi array bentuk numpy (528, 203, 3)dan saya ingin mengubah ukurannya untuk mengatakan ukuran gambar kedua ini: bottle-2

Yang memiliki bentuk (140, 54, 3).

Bagaimana cara mengubah ukuran gambar ke bentuk tertentu dengan tetap mempertahankan gambar aslinya? Jawaban lain menyarankan untuk menghapus setiap baris lain atau ketiga, tetapi yang ingin saya lakukan pada dasarnya adalah mengecilkan gambar seperti yang Anda lakukan melalui editor gambar tetapi dalam kode python. Apakah ada perpustakaan untuk melakukan ini di numpy / SciPy?

Brian Hamill
sumber
dapatkah Anda menunjukkan kode untuk array numpy Anda?
ShpielMeister
2
@sascha Tidak berlaku lagi, menurut halaman yang Anda tautkan.
Paul Panzer
@ShpielMeister Saya tidak bisa meminta IntelliJ untuk mencetak array numpy sepenuhnya, untuk beberapa alasan ketika outputnya besar itu menempatkan ... sepanjang waktu, jadi saya hanya dapat melihat sebagian dari output array di konsol
Brian Hamill

Jawaban:

123

Ya, Anda dapat menginstal opencv(ini adalah pustaka yang digunakan untuk pemrosesan gambar, dan computer vision), dan menggunakan cv2.resizefungsinya. Dan misalnya penggunaan:

import cv2
import numpy as np

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

Berikut imgadalah larik numpy yang berisi gambar asli, sedangkan reslarik numpy berisi gambar yang diubah ukurannya . Aspek penting adalah interpolationparameter: ada beberapa cara bagaimana mengubah ukuran gambar. Terutama karena Anda memperkecil gambar, dan ukuran gambar asli bukanlah kelipatan dari ukuran gambar yang diubah ukurannya. Skema interpolasi yang mungkin adalah:

  • INTER_NEAREST - interpolasi tetangga terdekat
  • INTER_LINEAR - interpolasi bilinear (digunakan secara default)
  • INTER_AREA- pengambilan sampel ulang menggunakan relasi area piksel. Ini mungkin metode yang lebih disukai untuk penghancuran gambar, karena memberikan hasil bebas moire. Tetapi ketika gambar diperbesar, itu mirip dengan INTER_NEARESTmetode.
  • INTER_CUBIC - interpolasi bikubik pada lingkungan piksel 4x4
  • INTER_LANCZOS4 - interpolasi Lanczos di lingkungan piksel 8x8

Seperti kebanyakan opsi, tidak ada opsi "terbaik" dalam arti bahwa untuk setiap skema pengubahan ukuran, terdapat skenario di mana satu strategi dapat lebih disukai daripada yang lain.

Willem Van Onsem
sumber
5
Saya baru saja mencoba kode ini dan berhasil! Hanya satu perubahan yang dsizeharus dilakukan dsize=(54, 140)dengan x lalu y, di mana array numpy menunjukkan bentuk sebagai y lalu x (y adalah jumlah baris dan x adalah jumlah kolom)
Brian Hamill
6
Saya mencoba untuk menghindari cv2, itu menukar dimensi dan beban dalam format saluran BGR. Saya lebih suka skimage.io.imread('image.jpg')dan skimage.transform.resize(img). scikit-image.org/docs/dev/install.html
Eduardo Pignatelli
1
@EduardoPignatelli Saya menghindari skimage.transform.resize karena Anda tidak memiliki kendali atas algoritma interpolasi yang digunakannya. Tapi, itu mungkin tidak penting, tergantung pada kasus penggunaan orang.
Decker
2
@Decker skimage.transform.resize menyediakan beberapa kontrol melalui parameter 'order'. orde = 0 adalah tetangga terdekat, 1 = bi-linear, 2 = bi-kuadrat, 3 = bi-kubik, dll. Tidak ada rata-rata area atau interpolasi lanczos.
Tapio
1
@TapioFriberg ahh ya, saya berdiri dikoreksi; Saya melihat algoritma yang didefinisikan di bawah dokumentasi untuk parameter 'order' skimage.transform.warp. Pada titik tertentu mungkin berguna untuk memperbarui dokumen untuk menyertakan referensi untuk jenis, "Bi-quartic", misalnya, tidak didefinisikan di mana pun dalam dokumentasi, (mulai 10 Des 2019) - satu baris mungkin bermanfaat bagi pengguna di masa mendatang.
Decker
67

Meskipun dimungkinkan untuk menggunakan numpy sendiri untuk melakukan ini, operasinya tidak built-in. Meskipun demikian, Anda dapat menggunakan scikit-image(yang dibangun di atas numpy) untuk melakukan manipulasi gambar semacam ini.

Dokumentasi penskalaan Scikit-Image ada di sini .

Misalnya, Anda dapat melakukan hal berikut dengan gambar Anda:

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

Ini akan menangani hal-hal seperti interpolasi, anti-aliasing, dll. Untuk Anda.

jakevdp.dll
sumber
2
Terima kasih! Jawaban ini juga berhasil! Meskipun saya mendapatkan beberapa masalah dengan anti_aliasingbenderanya, sepertinya itu telah dihapus dari versi terbaru 0.13.1
Brian Hamill
8
Ini mengembalikan gambar sebagai float ndarray meskipun gambar asli Anda adalah uint8
sziraqui
3
Ini adalah teknik yang bagus karena berfungsi dengan sejumlah saluran. Saya mencoba ini dengan data rgb yang dikombinasikan dengan data cloud titik kedalaman dan mempertahankan hubungan seperti yang saya inginkan.
Darth Egregious
@DarthEgregious, jakevdp -> itu membuat data gangguan acak saya menjadi satu warna ketika saya mengubah ukuran (137,236,3) array menjadi (64,64) seperti metode yang Anda jelaskan. Apakah ini normal karena sepertinya semua informasi telah hilang?
Deshwal
1
Bukankah seharusnya (64,64,3)
Darth Egregious
15

Bagi orang yang datang ke sini dari Google mencari cara cepat untuk menurunkan sampel gambar dalam numpyarray untuk digunakan dalam aplikasi Machine Learning, berikut adalah metode super cepat (diadaptasi dari sini ). Metode ini hanya berfungsi jika dimensi masukan adalah kelipatan dari dimensi keluaran.

Contoh berikut menurunkan sampel dari 128x128 menjadi 64x64 (ini dapat dengan mudah diubah).

Saluran pemesanan terakhir

# 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)

Saluran pemesanan pertama

# 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)

Untuk gambar grayscale ubah saja 3menjadi 1seperti ini:

Saluran pemesanan pertama

# 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)

Metode ini menggunakan penggabungan maksimal yang setara. Ini cara tercepat untuk melakukan ini yang saya temukan.

Waylon Flinn
sumber
4
large_image [:, :: 2, :: 2] mengembalikan gambar dengan resolusi yang dibelah dua.
L. Kärkkäinen
1
@ LasseKärkkäinen tetapi tidak menurunkan sampel, ini hanya memilih setiap piksel lainnya. Perbedaannya adalah bahwa fungsi akhir 'max' dapat diubah untuk memilih atau menghitung piksel dengan cara yang sedikit lebih baik (misalnya menggunakan 'min' atau 'mean'). Metode Anda berguna (dan lebih cepat), jika itu tidak masalah.
Waylon Flinn
@ L.Kärkkäinen apa kebalikan dari resolusi ganda ini?
rayzinnz
2
@rayzinnznp.repeat(np.repeat(a, 2, axis=0), 2, axis=1)
L. Kärkkäinen
11

Jika ada yang datang ke sini mencari metode sederhana untuk menskalakan / mengubah ukuran gambar dengan Python, tanpa menggunakan pustaka tambahan, berikut adalah fungsi pengubahan ukuran gambar yang sangat sederhana:

#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)]

Contoh penggunaan: mengubah ukuran gambar (30 x 30) menjadi (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))

Keluaran: gambar berskala

Ini berfungsi untuk mengecilkan / menskalakan gambar, dan berfungsi dengan baik dengan array numpy.

Romwell
sumber
4

imresize()Metode SciPy adalah metode pengubahan ukuran lainnya, tetapi akan dihapus mulai dengan SciPy v 1.3.0. SciPy mengacu pada metode pengubahan ukuran gambar PIL :Image.resize(size, resample=0)

size - Ukuran yang diminta dalam piksel, sebagai 2-tuple: (lebar, tinggi).
resample - Filter resampling opsional. Ini dapat berupa salah satu PIL.Image.NEAREST (gunakan tetangga terdekat), PIL.Image.BILINEAR (interpolasi linier), PIL.Image.BICUBIC (interpolasi spline kubik), atau PIL.Image.LANCZOS (filter downsampling berkualitas tinggi ). Jika dihilangkan, atau jika gambar memiliki mode "1" atau "P", ini disetel PIL.Image.NEAREST.

Tautkan di sini: https://pillow.readthedocs.io/en/3.1.x/reference/Image.html#PIL.Image.Image.resize

cemsazara
sumber
3
Sayangnya, imresize () tidak digunakan lagi, itu akan dihapus di SciPy 1.3.0
MiniQuark
1

Apakah ada perpustakaan untuk melakukan ini di numpy / SciPy

Tentu. Anda dapat melakukan ini tanpa OpenCV, scikit-image atau PIL.

Pengubahan ukuran gambar pada dasarnya memetakan koordinat setiap piksel dari gambar asli ke posisinya yang diubah ukurannya.

Karena koordinat gambar harus berupa bilangan bulat (anggap saja sebagai matriks), jika koordinat yang dipetakan memiliki nilai desimal, Anda harus menginterpolasi nilai piksel untuk memperkirakannya ke posisi bilangan bulat (misalnya mendapatkan piksel terdekat ke posisi itu diketahui sebagai interpolasi tetangga terdekat ).

Yang Anda butuhkan hanyalah fungsi yang melakukan interpolasi ini untuk Anda. SciPy memiliki interpolate.interp2d.

Anda dapat menggunakannya untuk mengubah ukuran gambar dalam larik numpy, katakanlah arr, sebagai berikut:

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))

Tentu saja, jika gambar Anda RGB, Anda harus melakukan interpolasi untuk setiap saluran.

Jika Anda ingin memahami lebih lanjut, saya sarankan menonton Resizing Images - Computerphile .

fabda01
sumber
Mungkin tidak berfungsi berdasarkan jawaban ini: 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
sumber
4
Selamat datang di StackOverflow. Senang sekali Anda ingin membantu orang lain dengan menjawab pertanyaan mereka. Namun, saya tidak melihat bagaimana jawaban Anda menambah nilai dibandingkan dengan jawaban yang ada yang sudah menggunakan cv2dan menggunakan fungsi pengubahan ukuran yang tepat alih-alih menerapkan ulang fungsi pengubahan ukuran "sub-optimal" yang lebih buruk daripada interpolasi tetangga terdekat.
NOhs