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.

Nolik
źródło

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)
Froyo
źródło
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.

:)

samkhan13
źródło
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ć.

smttsp
źródło
spójrz na to: stackoverflow.com/q/60359398/7644562
Abdul Rehman
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) 
m.hatami
źródło
@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')
Sanyal
źródło
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
belgrawiton
źródło
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
Dan Erez
źródło
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
)
zabop
źródło