Python kullanarak OpenCV'de bir görüntü nasıl kırpılır


234

OpenCV kullanarak daha önce PIL'de yaptığım gibi görüntüleri nasıl kırpabilirim.

PIL üzerinde çalışma örneği

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

Ama bunu OpenCV'de nasıl yapabilirim?

Ben denedim:

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)

Ama işe yaramıyor.

Sanırım yanlış kullandım getRectSubPix. Bu durumda, lütfen bu işlevi nasıl doğru bir şekilde kullanabileceğimi açıklayın.

Yanıtlar:


528

Çok basit. Numpy dilimleme kullanın.

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 ... Ama kırpma görüntüsünü değişkene nasıl kaydedebilirim?
Nolik

56
hatırlamak bu x ve y çevrileceği. Bunu özledim.
markroxor

10
Alternatif olarak, bir kırpma payı tanımladıysanız, şunları yapabilirsinizcrop_img = img[margin:-margin, margin:-margin]
Rufus

39
Bu harika, sadece crop_img değiştirmenin img'yi değiştireceğini unutmayın. Aksi takdirde, kırpma_img = img [y: y + h, x: x + w] .copy ()
user1270710 27:18 '

1
@javadba numpy uygulama detayı. Numpy satır, col yerine col gösterimi, satır kullanır
Froyo

121

Bu soruyu sordum ve burada başka bir cevap buldum: ilgilendiğiniz bölgeyi kopyalayın

(0,0) 'ı soldan imsağa x yönü, yukarıdan aşağıya y yönü olarak adlandırılan görüntünün sol üst köşesi olarak değerlendirirsek. ve sol üst köşede (x1, y1) ve bu görüntüdeki bir dikdörtgen bölgenin sağ alt köşesinde (x2, y2) var, sonra:

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

burada numpy dizi indeksleme ve dilimleme hakkında size bir görüntünün bir kısmını kırpma gibi şeyler hakkında daha fazla bilgi verebilecek kapsamlı bir kaynak verilmiştir . görüntüler opencv2 içinde bir numpy dizi olarak saklanır.

:)


Merhaba, Koşullar altında "roi = im [y1: y2 + 1, x1: x2 + 1]" olmamalı mı? Çünkü numpy dilimlemek için hariç tutulan bölgeyi kullanır.
Scott Yang

@ samkhan13, bu formülü kullanarak kırpma yaptığımda, tüm ürünlerimin şekli var (0, genişlik, kanallar). Yani. Hiç boyut elde
edemiyorum

@ mLstudent33 muhtemelen görüntünün imdoğru okunmamış ve boş. kodunuzu adım adım teşhis etmek için kesme noktalarına sahip bir IDE kullanmayı deneyin. kod blokları oluşturmak için google colab'ı kullanabilir ve birileri yardım almak için stackoverflow python sohbet odasında jupytor not defterinizi paylaşabilirsiniz .
samkhan13

@ samkhan13 aslında Github Opencv Sorunları üzerine yayınladığım garip bir sorunum var: github.com/opencv/opencv/issues/15406 Ben de sohbet kontrol edeceğim. Teşekkürler!
mLstudent33

16

Görüntü dilimleme bir kopyasını oluşturarak değil, unutmayın cropped imageama oluşturarak pointeriçin roi. Çok fazla görüntü yüklüyorsanız, görüntülerin ilgili bölümlerini dilimleyerek kırpıp bir listeye ekliyorsanız, bu büyük bir bellek kaybı olabilir.

Her birine N resim yüklediğinizi >1MPve yalnızca 100x100sol üst köşeden bölgeye ihtiyacınız olduğunu varsayalım .

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.

Alternatif olarak, ilgili parçayı kopyalayabilirsiniz .copy(), böylece çöp toplayıcı kaldırılır 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.

Bunu bulduktan sonra, user1270710 tarafından yapılan yorumlardan birini fark ettim , ancak bunu bulmam oldukça uzun sürdü (yani, hata ayıklama vb.). Bu yüzden bahsetmeye değer.



Kullanılan bellek alanı anlamında, ilgilendiğim bölgeyi kopyalamanın en iyi şey olduğunu anlıyorum, ama zaman alıcı ne olacak? copy()YG'yi dilimlemeye kıyasla yaparsam sonuç ne olur? Ayrıca, tmpbilgisayarımdan yüklediğim her resmi sakladığım bir değişkenim varsa , dilimlemenin hafızam üzerinde kötü bir etkisi olmamalı, değil mi? Açıkladığınız sorun, yalnızca tüm görüntüleri yüklediğinizde ve daha sonra, hem orijinal hem de ROI'ye sahip olan ROI'yi tekrar depoladığınızda ne olacağı ile ilgilidir . Doğru anladıysam lütfen bana bildirin.
Cătălina Sîrbu

Dediğim durumda kopyalama işlemi ihmal edilebilir bir zaman olacaktır. Büyük resimleri çok fazla kopyalamazsanız, zaman farkınız olmaz. Kodumda, etki kırpma başına 1ms'den az olacak. Sorun, büyük resmi ve bir işaretçiyi (yalnızca birkaç bayt olan ROI) depolamanız veya küçük bir resmi bellekte (benim durumumda) depolamanızdır. Bunu birkaç kez yaparsanız, sorun değil. Ancak bunu binlerce kez yaparsanız, bellek kullanımı dilimleme ile çılgın olacaktır. Eğer birkaç resim yüklerseniz tüm hafızayı doldurursanız dilimleme yaparsanız. MBs
smttsp

12

bu kod bir görüntüyü x = 0, y = 0 konumundan h = 100, w = 200 olarak kırpır

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, yani yükseklik 100 piksel "aşağıda" y = 0 değil mi? Numpy dizisinin 101. satırı mı? Ve genişlik x = 0 sağındaki 200 piksel doğru mu?
mLstudent33

4

Bir görüntüyü kırpmanın yolu aşağıdadır.

image_path: Düzenlenecek görüntünün yolu

coords: x / y koordinatlarından oluşan bir demet (x1, y1, x2, y2) [resmi mspaint'te aç ve koordinatları görmek için görünüm sekmesindeki "cetveli" kontrol et]

saved_location : Kesilen görüntüyü kaydetme yolu

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

Opencv kopyalama kenarlık fonksiyonlu sağlam kırpma:

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

Burada ne BBOX olduğunu ve ne ben, benim üzerinde hata veriyor geçmesine çalışıyorum değer çünkü neyi değeri vermek gerekiyor lütfen açıklayabilir x1,y1,x2,y2 = bbox derken:TypeError: 'int' object is not iterable
Sabah

3

İşte daha sağlam imcrop için bazı kod (biraz matlab gibi)

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

Alternatif olarak, kırpma için tensorflow'u ve görüntüden bir dizi yapmak için openCV'yi kullanabilirsiniz.

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

Şimdi imgbir (imageheight, imagewidth, 3) şekil dizisidir. Diziyi tensorflow ile kırpın:

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
)

Görüntüyü tf.keras ile yeniden birleştirin, böylece işe yaradıysa bakabiliriz:

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

Bu, resmi bir not defterinde yazdırır (Google Colab'da test edilmiştir).


Tüm kod birlikte:

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
)
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.