Jak przyciąć obraz w OpenCV za pomocą Pythona


233

Jak mogę przycinać obrazy, tak jak zrobiłem to wcześniej w PIL, używając OpenCV.

Przykład roboczy na PIL

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

Ale jak mogę to zrobić na OpenCV?

Oto, co próbowałem:

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)

Ale to nie działa.

Myślę, że źle użyłem getRectSubPix. W takim przypadku wyjaśnij, w jaki sposób mogę poprawnie korzystać z tej funkcji.

Odpowiedzi:


527

To jest bardzo proste. Użyj krojenia numerycznego.

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 ... Ale jak mogę zapisać obraz przycięcia w zmiennej?
— Nolik

56
pamiętaj, że xiy są odwrócone. Tęskniłem za tym.
— markroxor

10
Alternatywnie, jeśli zdefiniowałeś margines uprawy, możesz to zrobićcrop_img = img[margin:-margin, margin:-margin]
— Rufus

39
To świetnie, pamiętaj tylko, że zmiana crop_img zmieni img. W przeciwnym razie powinieneś crop_img = img [y: y + h, x: x + w] .copy ()
— user1270710

1
@javadba numpy szczegóły implementacji. Numpy używa wiersza, notacji col zamiast col, row
— Froyo

121

Miałem to pytanie i znalazłem tutaj inną odpowiedź: skopiuj region zainteresowania

Jeśli weźmiemy pod uwagę (0,0) jako lewy górny róg obrazu wywoływany imz lewej do prawej jako kierunek x, a z góry na dół jako kierunek y. i mamy (x1, y1) jako lewy górny wierzchołek i (x2, y2) jako prawy dolny wierzchołek regionu prostokąta na tym obrazie, a następnie:

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

oto obszerne źródło informacji na temat indeksowania i dzielenia tablic numpy, które może powiedzieć ci więcej na temat przycinania części obrazu. obrazy byłyby przechowywane w tablicy numpy w opencv2.

:)


Cześć, czy nie powinno być w twoich okolicznościach `roi = im [y1: y2 + 1, x1: x2 + 1]? Ponieważ numpy używa wycinanego regionu.
— Scott Yang,

@ samkhan13, kiedy uprawiam przy użyciu tej formuły, wszystkie moje uprawy mają kształt (0, szerokość, kanały). To znaczy. W ogóle nie dostaję żadnego wymiaru
— mLstudent33

@ mLstudent33 jest prawdopodobne, że obraz imnie został poprawnie odczytany i jest pusty. spróbuj użyć IDE z punktami przerwania, aby zdiagnozować kod krok po kroku. możesz użyć google colab do tworzenia bloków kodu i możesz udostępnić swój notatnik jupytor w pokoju czatowym Python, aby uzyskać pomoc.
— samkhan13,

@ samkhan13 faktycznie mam dziwny problem, który opublikowałem na Github Opencv Problemy: github.com/opencv/opencv/issues/15406 Sprawdzę też czat. Dzięki!
— mLstudent33

16

Zauważ, że krojenie obrazu nie tworzy kopii, cropped imageale tworzy pointerdo roi. Jeśli ładujesz tak wiele zdjęć, przycinasz odpowiednie części obrazów za pomocą krojenia, a następnie dołączasz do listy, może to być ogromne marnotrawstwo pamięci.

Załóżmy, że wczytasz N obrazów >1MPi potrzebujesz tylko 100x100regionu z lewego górnego rogu.

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.

Alternatywnie możesz skopiować odpowiednią część .copy(), więc moduł usuwania śmieci usunie 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.

Po odkryciu tego zdałem sobie sprawę jedną z uwag przez user1270710 wymienionych to, ale zajęło mi trochę czasu, aby dowiedzieć się (tzn debugowania itp). Myślę, że warto o tym wspomnieć.



Rozumiem, że jeśli chodzi o zajmowaną przestrzeń pamięci, najlepiej jest skopiować obszar zainteresowania, ale co z zajmowaniem czasu? Jeśli dokonam copy()zwrotu z inwestycji w porównaniu do krojenia, jaki byłby wynik? Ponadto, jeśli mam zmienną, tmpw której przechowuję każde zdjęcie ładowane z mojego komputera, krojenie nie powinno mieć złego wpływu na moją pamięć, prawda? Opisany problem dotyczy tylko tego, co dzieje się, gdy załadujesz wszystkie obrazy, a następnie zapiszesz je ponownie ROI, mając zarówno oryginały, jak i ROI . Daj mi znać, jeśli dobrze zrozumiałem.
— Cătălina Sîrbu

W przypadku, o którym mówiłem, kopiowanie będzie miało znikomy czas. Chyba że kopiujesz duże obrazy tyle razy, nie będziesz miał różnicy czasu. W moim kodzie efekt będzie mniejszy niż 1 ms na kadrowanie. Problem polega na tym, że albo przechowujesz duży obraz i wskaźnik (ROI, który jest tylko kilka bajtów), albo przechowujesz mały obraz w pamięci (w moim przypadku). Jeśli zrobisz to kilka razy, będzie dobrze. Jednak jeśli zrobisz to tysiące razy, użycie pamięci będzie szalone z krojeniem. Tak jakbyś wypełnił całą pamięć po kilku ładowaniach tysiąca zdjęć, jeśli wykonujesz krojenie. Podczas gdy mój kod będzie nadal w porządku, jeśli MB
— smttsp

12

ten kod przycina obraz z x = 0, y = 0 pozycji do 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, więc wysokość wynosi 100 pikseli „poniżej” y = 0 prawda? To jest 101. wiersz tablicy numpy? A szerokość wynosi 200 pikseli na prawo od x = 0, prawda?
— mLstudent33

4

Poniżej znajduje się sposób przycięcia obrazu.

ścieżka_obrazu: ścieżka do obrazu do edycji

Coords: krotka współrzędnych x / y (x1, y1, x2, y2) [otwórz obraz w mspaint i zaznacz „linijkę” w zakładce widoku, aby zobaczyć współrzędne]

zapisane_lokalizacja : Ścieżka do zapisania przyciętego obrazu

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

Solidny kadr z funkcją kopiowania ramki 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

Czy możesz wyjaśnić, co to jest bbox i co mamy nadać jego wartości, ponieważ niezależnie od wartości, którą próbuję przekazać, daje mi błąd x1,y1,x2,y2 = bbox , mówiąc:TypeError: 'int' object is not iterable
— Sabah

3

oto kod dla bardziej niezawodnego imcropa (trochę jak w Matlabie)

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

Alternatywnie możesz użyć tensorflow do kadrowania i openCV do utworzenia tablicy z obrazu.

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

Teraz imgjest tablica kształtu (wysokość obrazu, szerokość obrazu, 3). Przytnij tablicę za pomocą 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
)

Ponownie złóż obraz za pomocą tf.keras, abyśmy mogli na niego spojrzeć, jeśli zadziałał:

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

To wydrukuje zdjęcie w notatniku (testowane w Google Colab).


Cały kod razem:

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
)
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.