Cara memotong gambar di OpenCV menggunakan Python


234

Bagaimana saya bisa memotong gambar, seperti yang saya lakukan sebelumnya di PIL, menggunakan OpenCV.

Contoh kerja pada PIL

im = Image.open('0.png').convert('L')
im = im.crop((1, 1, 98, 33))
im.save('_0.png')

Tetapi bagaimana saya bisa melakukannya di OpenCV?

Inilah yang saya coba:

im = cv.imread('0.png', cv.CV_LOAD_IMAGE_GRAYSCALE)
(thresh, im_bw) = cv.threshold(im, 128, 255, cv.THRESH_OTSU)
im = cv.getRectSubPix(im_bw, (98, 33), (1, 1))
cv.imshow('Img', im)
cv.waitKey(0)

Tapi itu tidak berhasil.

Saya pikir saya salah digunakan getRectSubPix. Jika ini masalahnya, tolong jelaskan bagaimana saya dapat menggunakan fungsi ini dengan benar.

Jawaban:


528

Ini sangat sederhana. Gunakan irisan numpy.

import cv2
img = cv2.imread("lenna.png")
crop_img = img[y:y+h, x:x+w]
cv2.imshow("cropped", crop_img)
cv2.waitKey(0)

9
Hmm ... Tapi bagaimana saya bisa menyimpan gambar crop ke variabel?
Nolik

56
ingat bahwa x dan y dibalik. Saya melewatkan ini.
markroxor

10
Atau, jika Anda telah menetapkan margin panen, Anda dapat melakukannyacrop_img = img[margin:-margin, margin:-margin]
Rufus

39
Ini hebat, perlu diketahui bahwa mengubah crop_img akan mengubah img. Jika tidak, Anda harus memotong_img = img [y: y + h, x: x + w] .copy ()
user1270710

1
@javadba detail implementasi numpy. Numpy menggunakan baris, notasi col alih-alih col, baris
Froyo

121

Saya punya pertanyaan ini dan menemukan jawaban lain di sini: salin wilayah yang diminati

Jika kita menganggap (0,0) sebagai sudut kiri atas gambar disebut imdengan kiri-ke-kanan sebagai arah x dan atas-ke-bawah sebagai arah y. dan kami memiliki (x1, y1) sebagai verteks kiri-atas dan (x2, y2) sebagai verteks kanan-bawah dari wilayah persegi panjang dalam gambar itu, maka:

roi = im[y1:y2, x1:x2]

di sini adalah sumber daya yang komprehensif pada pengindeksan array dan numpy array yang dapat memberitahu Anda lebih banyak tentang hal-hal seperti memotong bagian dari gambar gambar akan disimpan sebagai array numpy di opencv2.

:)


Hai, bukankah seharusnya `roi = im [y1: y2 +1, x1: x2 +1]` dalam keadaan Anda? Karena numpy menggunakan wilayah yang dikecualikan untuk mengiris.
Scott Yang

@ samkhan13, ketika saya memotong menggunakan rumus ini, semua tanaman saya memiliki bentuk (0, lebar, saluran). Yaitu. Saya tidak mendapatkan dimensi sama sekali
mLstudent33

@ mLstudent33 kemungkinan gambar imbelum dibaca dengan benar dan kosong. coba gunakan IDE dengan breakpoint untuk mendiagnosis kode Anda langkah demi langkah. Anda dapat menggunakan google colab untuk membuat blok kode dan dapat berbagi buku catatan jupytor Anda di ruang obrolan python stackoverflow untuk mendapatkan bantuan seseorang.
samkhan13

@ samkhan13 sebenarnya saya memiliki masalah aneh yang saya posting di Masalah Pembukaan Github: github.com/opencv/opencv/issues/15406 Saya akan memeriksa obrolan juga. Terima kasih!
mLstudent33

16

Perhatikan bahwa, mengiris gambar tidak membuat salinan cropped imagetetapi membuat pointerke roi. Jika Anda memuat begitu banyak gambar, memotong bagian yang relevan dari gambar dengan mengiris, lalu menambahkannya ke dalam daftar, ini mungkin merupakan pemborosan memori yang sangat besar.

Misalkan Anda memuat N gambar masing-masing >1MPdan Anda hanya perlu 100x100wilayah dari sudut kiri atas.

Slicing:

X = []
for i in range(N):
    im = imread('image_i')
    X.append(im[0:100,0:100]) # This will keep all N images in the memory. 
                              # Because they are still used.

Atau, Anda dapat menyalin bagian yang relevan dengan .copy(), sehingga pengumpul sampah akan menghapus im.

X = []
for i in range(N):
    im = imread('image_i')
    X.append(im[0:100,0:100].copy()) # This will keep only the crops in the memory. 
                                     # im's will be deleted by gc.

Setelah mengetahui ini, saya menyadari salah satu komentar oleh user1270710 menyebutkan bahwa tetapi butuh beberapa waktu untuk mengetahuinya (yaitu, debugging dll). Jadi, saya pikir itu layak disebutkan.



Dalam arti ruang memori yang ditempati, saya mengerti bahwa menyalin wilayah yang diminati adalah hal terbaik untuk dilakukan, tetapi bagaimana dengan memakan waktu? Jika saya melakukan copy()ROI, dibandingkan dengan mengiris, apa hasilnya? Juga, jika saya memiliki variabel tmpdi mana saya menyimpan setiap gambar yang saya muat dari komputer saya, pengirisan seharusnya tidak berdampak buruk pada memori saya, kan? Masalah yang Anda jelaskan hanya terkait dengan apa yang terjadi ketika Anda memuat semua gambar dan kemudian Anda menyimpannya kembali ROI, memiliki yang asli dan ROI . Tolong beri tahu saya jika saya benar mengerti.
Cătălina Sîrbu

Menyalin akan diabaikan waktu dalam kasus yang saya katakan. Kecuali Anda menyalin gambar besar berkali-kali, Anda tidak akan memiliki perbedaan waktu. Dalam kode saya, efeknya akan seperti kurang dari 1 ms per tanam. Masalahnya adalah Anda menyimpan gambar besar dan pointer (ROI yang hanya beberapa byte) atau Anda menyimpan gambar kecil di memori (dalam kasus saya). Jika Anda melakukan ini beberapa kali, tidak apa-apa. Namun jika Anda melakukan ini ribuan kali, penggunaan memori akan gila dengan mengiris. Seperti Anda mengisi seluruh memori setelah beberapa jika ribuan memuat gambar jika Anda mengiris. Sedangkan kode saya masih akan dipesan jika MB
smttsp

12

kode ini memotong gambar dari x = 0, y = 0 posisi ke h = 100, w = 200

import numpy as np
import cv2

image = cv2.imread('download.jpg')
y=0
x=0
h=100
w=200
crop = image[y:y+h, x:x+w]
cv2.imshow('Image', crop)
cv2.waitKey(0) 

@hatami, jadi tingginya 100 piksel "di bawah" y = 0 kan? Ini adalah baris ke-101 dari array numpy? Dan lebarnya 200 piksel di sebelah kanan x = 0 benar?
mLstudent33

4

Di bawah ini adalah cara untuk memotong gambar.

image_path: Jalur ke gambar untuk diedit

coords: Sebuah tuple koordinat x / y (x1, y1, x2, y2) [buka gambar dalam mspaint dan periksa "penggaris" di tab tampilan untuk melihat koordinat]

Saved_location : Path untuk menyimpan gambar yang dipangkas

from PIL import Image
    def crop(image_path, coords, saved_location:
        image_obj = Image.open("Path of the image to be cropped")
            cropped_image = image_obj.crop(coords)
            cropped_image.save(saved_location)
            cropped_image.show()


if __name__ == '__main__':
    image = "image.jpg"
    crop(image, (100, 210, 710,380 ), 'cropped.jpg')

3

Pangkas yang kuat dengan fungsi batas salinan opencv:

def imcrop(img, bbox):
   x1, y1, x2, y2 = bbox
   if x1 < 0 or y1 < 0 or x2 > img.shape[1] or y2 > img.shape[0]:
        img, x1, x2, y1, y2 = pad_img_to_fit_bbox(img, x1, x2, y1, y2)
   return img[y1:y2, x1:x2, :]

def pad_img_to_fit_bbox(img, x1, x2, y1, y2):
    img = cv2.copyMakeBorder(img, - min(0, y1), max(y2 - img.shape[0], 0),
                            -min(0, x1), max(x2 - img.shape[1], 0),cv2.BORDER_REPLICATE)
   y2 += -min(0, y1)
   y1 += -min(0, y1)
   x2 += -min(0, x1)
   x1 += -min(0, x1)
   return img, x1, x2, y1, y2

Bisakah Anda jelaskan apa itu bbox di sini dan apa yang seharusnya kami berikan nilainya karena nilai apa pun yang saya coba sampaikan, itu membuat saya keliru x1,y1,x2,y2 = bbox ketika mengatakan:TypeError: 'int' object is not iterable
Sabah

3

di sini adalah beberapa kode untuk imcrop yang lebih kuat (sedikit seperti di matlab)

def imcrop(img, bbox): 
    x1,y1,x2,y2 = bbox
    if x1 < 0 or y1 < 0 or x2 > img.shape[1] or y2 > img.shape[0]:
        img, x1, x2, y1, y2 = pad_img_to_fit_bbox(img, x1, x2, y1, y2)
    return img[y1:y2, x1:x2, :]

def pad_img_to_fit_bbox(img, x1, x2, y1, y2):
    img = np.pad(img, ((np.abs(np.minimum(0, y1)), np.maximum(y2 - img.shape[0], 0)),
               (np.abs(np.minimum(0, x1)), np.maximum(x2 - img.shape[1], 0)), (0,0)), mode="constant")
    y1 += np.abs(np.minimum(0, y1))
    y2 += np.abs(np.minimum(0, y1))
    x1 += np.abs(np.minimum(0, x1))
    x2 += np.abs(np.minimum(0, x1))
    return img, x1, x2, y1, y2

1

Atau, Anda bisa menggunakan tensorflow untuk memotong dan openCV untuk membuat array dari gambar.

import cv2
img = cv2.imread('YOURIMAGE.png')

Sekarang imgadalah array bentuk (imageheight, imagewidth, 3). Pangkas array dengan tensorflow:

import tensorflow as tf
offset_height=0
offset_width=0
target_height=500
target_width=500
x = tf.image.crop_to_bounding_box(
    img, offset_height, offset_width, target_height, target_width
)

Pasang kembali gambar dengan tf.keras, sehingga kita dapat melihatnya jika berhasil:

tf.keras.preprocessing.image.array_to_img(
    x, data_format=None, scale=True, dtype=None
)

Ini mencetak pic di buku catatan (diuji di Google Colab).


Seluruh kode bersama-sama:

import cv2
img = cv2.imread('YOURIMAGE.png')

import tensorflow as tf
offset_height=0
offset_width=0
target_height=500
target_width=500
x = tf.image.crop_to_bounding_box(
    img, offset_height, offset_width, target_height, target_width
)

tf.keras.preprocessing.image.array_to_img(
    x, data_format=None, scale=True, dtype=None
)
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.