Come ritagliare un'immagine in OpenCV usando Python


234

Come posso ritagliare le immagini, come ho già fatto in PIL, usando OpenCV.

Esempio di lavoro su PIL

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

Ma come posso farlo su OpenCV?

Questo è quello che ho provato:

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)

Ma non funziona

Penso di aver usato in modo errato getRectSubPix. In tal caso, spiegare come posso utilizzare correttamente questa funzione.

Risposte:


529

È molto semplice. Usa affettare intorpidito.

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 ... Ma come posso salvare l'immagine di ritaglio in variabile?
Nolik,

56
ricorda che xey sono capovolti. Mi è mancato questo.
markroxor,

10
In alternativa, se hai definito un margine di ritaglio, puoi farlocrop_img = img[margin:-margin, margin:-margin]
Rufus,

39
Questo è fantastico, tieni presente che cambiando crop_img cambierà img. Altrimenti, dovresti crop_img = img [y: y + h, x: x + w] .copy ()
user1270710

1
Dettagli dell'implementazione numpy di @javadba. Numpy usa row, notazione invece di col, riga
Froyo,

121

ho avuto questa domanda e ho trovato un'altra risposta qui: copia regione di interesse

Se consideriamo (0,0) come angolo in alto a sinistra dell'immagine chiamato imda sinistra a destra come direzione x e dall'alto verso il basso come direzione y. e abbiamo (x1, y1) come vertice in alto a sinistra e (x2, y2) come vertice in basso a destra di una regione rettangolare all'interno di quell'immagine, quindi:

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

ecco una risorsa completa sull'indicizzazione e lo sbriciolamento dell'array numpy che può dirti di più su cose come ritagliare una parte di un'immagine. le immagini verrebbero archiviate come array numpy in opencv2.

:)


Ciao, non dovrebbe essere `roi = im [y1: y2 + 1, x1: x2 + 1]` sotto di te? Perché numpy usa la regione esclusa per tagliare.
Scott Yang,

@ samkhan13, quando coltivo usando questa formula, tutte le mie colture hanno forma (0, larghezza, canali). Vale a dire. Non ottengo affatto una dimensione
mLstudent33

@ mLstudent33 è probabile che l'immagine imnon sia stata letta correttamente ed è vuota. prova a utilizzare un IDE con punti di interruzione per diagnosticare il tuo codice passo dopo passo. puoi usare google colab per creare blocchi di codice e condividere il tuo quaderno jupytor nella chat room python stackoverflow per ottenere aiuto da qualcuno.
samkhan13,

@ samkhan13 in realtà ho uno strano problema che ho pubblicato su Github Opencv Issues: github.com/opencv/opencv/issues/15406 Verificherò anche la chat. Grazie!
mLstudent33,

16

Si noti che la suddivisione in immagini non sta creando una copia di cropped imagema creando pointeraroi . Se stai caricando così tante immagini, ritagliando le parti rilevanti delle immagini con lo slicing, quindi aggiungendole a un elenco, questo potrebbe essere un enorme spreco di memoria.

Supponiamo di caricare N immagini ciascuna >1MPe che è necessaria solo la 100x100regione dall'angolo in alto a sinistra.

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.

In alternativa, è possibile copiare la parte pertinente in .copy()modo da rimuovere Garbage Collector 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.

Dopo averlo scoperto, mi sono reso conto di uno dei commenti di user1270710 che lo menzionava, ma mi ci è voluto un po 'di tempo per scoprirlo (es. Debug, ecc.). Quindi, penso che valga la pena menzionarlo.



Nel mezzo dello spazio di memoria occupato, capisco che copiare la regione di interesse è la cosa migliore da fare, ma che dire del tempo? Se eseguissi copy()il ROI, rispetto all'affettatura, quale sarebbe il risultato? Inoltre, se ho una variabile tmpin cui memorizzo ogni immagine che carico dal mio computer, il taglio non dovrebbe avere un impatto negativo sulla mia memoria, giusto? Il problema che descrivi è legato solo a ciò che accade quando carichi tutte le immagini e poi le memorizzi di nuovo ROI, avendo sia gli originali che il ROI . Per favore fatemi sapere se ho capito bene.
Cătălina Sîrbu,

La copia sarà un tempo trascurabile nel caso in cui lo dicessi. A meno che non copiate immagini di grandi dimensioni così tante volte, non avrete differenze di tempo. Nel mio codice, l'effetto sarà come meno di 1 ms per ritaglio. Il problema è che o memorizzi l'immagine grande e un puntatore (ROI che è solo pochi byte) o memorizzi una piccola immagine in memoria (nel mio caso). Se lo fai alcune volte, va bene. Tuttavia, se lo fai migliaia di volte, l'utilizzo della memoria sarà pazzo con lo slicing. Come se riempissi l'intera memoria dopo un paio se migliaia di immagini si caricano se fai il taglio. Considerando che il mio codice sarà ancora in ordine se
MBs

12

questo codice ritaglia un'immagine da x = 0, y = 0 posizione a 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, quindi l'altezza è di 100 pixel "sotto" y = 0 giusto? È la 101a fila dell'array numpy? E la larghezza è di 200 pixel a destra di x = 0 corretta?
mLstudent33,

4

Di seguito è riportato il modo di ritagliare un'immagine.

percorso_immagine: il percorso dell'immagine da modificare

coords: una tupla di coordinate x / y (x1, y1, x2, y2) [apri l'immagine in mspaint e controlla il "righello" nella scheda di visualizzazione per vedere le coordinate]

saved_location : percorso per salvare l'immagine ritagliata

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

Ritaglio robusto con funzione di bordo copia 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

Puoi per favore spiegare cosa è bbox qui e cosa dovremmo dare nel suo valore perché qualunque valore sto cercando di trasmettere, mi sta dando un errore x1,y1,x2,y2 = bbox mentre dice:TypeError: 'int' object is not iterable
Sabah

3

ecco un po 'di codice per imcrop più robusto (un po' come in 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

In alternativa, è possibile utilizzare tensorflow per il ritaglio e openCV per creare un array dall'immagine.

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

Ora imgè un array di forme (imageheight, imagewidth, 3). Ritaglia l'array con 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
)

Riassembla l'immagine con tf.keras, così possiamo guardarla se ha funzionato:

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

Questo stampa l'immagine in un taccuino (testato su Google Colab).


L'intero codice insieme:

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
)
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.