Cómo recortar una imagen en OpenCV usando Python

234

¿Cómo puedo recortar imágenes, como lo hice antes en PIL, usando OpenCV?

Ejemplo de trabajo en PIL

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

¿Pero cómo puedo hacerlo en OpenCV?

Esto es lo que probé:

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)

Pero no funciona.

Creo que lo usé incorrectamente getRectSubPix. Si este es el caso, explique cómo puedo usar esta función correctamente.

Nolik
fuente

Respuestas:

529

Es muy simple. Usa rebanadas 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)
Froyo
fuente
99
Hmm ... ¿Pero cómo puedo guardar la imagen recortada en variable?
Nolik
56
recuerda que x e y se voltean. Me perdí esto.
markroxor
10
Alternativamente, si ha definido un margen de cultivo, puede hacerlocrop_img = img[margin:-margin, margin:-margin]
Rufus
39
Esto es genial, solo ten en cuenta que cambiar crop_img cambiará img. De lo contrario, debe crop_img = img [y: y + h, x: x + w] .copy ()
usuario1270710
1
@javadba detalle de implementación numpy. Numpy usa fila, notación de columna en lugar de columna, fila
Froyo
121

Tenía esta pregunta y encontré otra respuesta aquí: copiar región de interés

Si consideramos (0,0) como la esquina superior izquierda de la imagen llamada imcon de izquierda a derecha como dirección xy de arriba hacia abajo como dirección y. y tenemos (x1, y1) como el vértice superior izquierdo y (x2, y2) como el vértice inferior derecho de una región rectangular dentro de esa imagen, entonces:

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

Aquí hay un recurso completo sobre indexación y corte de matriz numpy que puede brindarle más información sobre cosas como recortar una parte de una imagen. las imágenes se almacenarían como una matriz numpy en opencv2.

:)

samkhan13
fuente
Hola, ¿no debería ser `roi = im [y1: y2 + 1, x1: x2 + 1]` bajo tus circunstancias? Porque numpy usa la región excluida para cortar.
Scott Yang
@ samkhan13, cuando recorte con esta fórmula, todos mis cultivos tienen forma (0, ancho, canales). Es decir. No estoy obteniendo ninguna dimensión en absoluto
mLstudent33
@ mLstudent33 es probable que la imagen imno se haya leído correctamente y esté vacía. intente usar un IDE con puntos de interrupción para diagnosticar su código paso a paso. puede usar google colab para crear bloques de código y puede compartir su cuaderno jupytor en la sala de chat de python stackoverflow para obtener ayuda de alguien.
samkhan13
@ samkhan13 en realidad tengo un problema extraño que publiqué en Github Opencv Issues: github.com/opencv/opencv/issues/15406 También revisaré el chat. ¡Gracias!
mLstudent33
16

Tenga en cuenta que, rebanar la imagen no es la creación de una copia de la cropped imagepero la creación de una pointera laroi . Si está cargando tantas imágenes, recorta las partes relevantes de las imágenes con un corte y luego las agrega a una lista, esto podría ser un gran desperdicio de memoria.

Suponga que carga N imágenes cada una >1MPy solo necesita una 100x100región desde la esquina superior izquierda.

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.

Alternativamente, puede copiar la parte relevante por .copy(), para que el recolector de basura se elimine 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.

Después de descubrir esto, me di cuenta de que uno de los comentarios del usuario1270710 mencionaba eso, pero me llevó bastante tiempo descubrirlo (es decir, depuración, etc.). Entonces, creo que vale la pena mencionarlo.

smttsp
fuente
eche un vistazo a esto: stackoverflow.com/q/60359398/7644562
Abdul Rehman
En cuanto al espacio de memoria ocupado, entiendo que copiar la región de interés es lo mejor que se puede hacer, pero ¿qué pasa con el tiempo? Si hago copy()el ROI, en comparación con el corte, ¿cuál sería el resultado? Además, si tengo una variable tmpen la que almaceno cada imagen que cargo desde mi computadora, el corte no debería tener un impacto negativo en mi memoria, ¿verdad? El problema que describe solo está relacionado con lo que sucede cuando carga todas las imágenes y luego las almacena de nuevo ROI, teniendo tanto los originales como el ROI . Por favor, avíseme si entendí bien.
Cătălina Sîrbu
Copiar será un tiempo insignificante en el caso que dije. A menos que copie imágenes grandes tantas veces, no tendrá una diferencia horaria. En mi código, el efecto será como menos de 1 ms por recorte. El problema es que usted almacena la imagen grande y un puntero (ROI que solo tiene unos pocos bytes) o almacena una imagen pequeña en la memoria (en mi caso). Si haces esto varias veces, está bien. Sin embargo, si hace esto miles de veces, el uso de la memoria será una locura con los cortes. Al igual que llenas toda la memoria después de cargar un par de miles de imágenes si haces un corte. Mientras que mi código todavía estará en la orden si MBs
smttsp
12

este código recorta una imagen de x = 0, y = 0 posición 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) 
m.hatami
fuente
@hatami, entonces la altura es de 100 píxeles "debajo" y = 0 ¿verdad? ¿Es la fila 101 de la matriz numpy? ¿Y el ancho es de 200 píxeles a la derecha de x = 0 correcto?
mLstudent33
4

A continuación se muestra la forma de recortar una imagen.

ruta de la imagen: la ruta a la imagen para editar

coords: una tupla de coordenadas x / y (x1, y1, x2, y2) [abra la imagen en mspaint y verifique la "regla" en la pestaña de vista para ver las coordenadas]

ubicación_ guardada : ruta para guardar la imagen recortada

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
fuente
3

Recorte robusto con la función de borde de 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
belgraviton
fuente
¿Puede explicar qué es bbox aquí y qué se supone que debemos dar en su valor porque sea cual sea el valor que estoy tratando de pasar, me está dando un error x1,y1,x2,y2 = bbox al decir:TypeError: 'int' object is not iterable
Sabah
3

Aquí hay un código para un imcrop más robusto (un poco como en 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
Dan Erez
fuente
1

Alternativamente, puede usar el flujo de tensor para el recorte y openCV para hacer una matriz a partir de la imagen.

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

Ahora imges una matriz de formas (altura de imagen, ancho de imagen, 3). Recorte la matriz 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
)

Vuelva a montar la imagen con tf.keras, para que podamos verla si funcionó:

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

Esto imprime la imagen en un cuaderno (probado en Google Colab).


Todo el código juntos:

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
fuente