Como cortar uma imagem no OpenCV usando Python

234

Como cortar imagens, como fiz anteriormente no PIL, usando o OpenCV.

Exemplo de trabalho no PIL

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

Mas como eu posso fazer isso no OpenCV?

Isto é o que eu tentei:

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)

Mas isso não funciona.

Eu acho que usei incorretamente getRectSubPix. Se for esse o caso, explique como posso usar corretamente esta função.

Nolik
fonte

Respostas:

528

É muito simples. Use fatias 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
fonte
9
Hmm ... Mas como posso salvar a imagem de corte em variável?
Nolik 23/03
56
lembre-se de que xey são invertidos. Eu perdi isso.
MarkRoxor #
10
Alternativamente, se você definiu uma margem de culturas, você pode fazercrop_img = img[margin:-margin, margin:-margin]
Rufus
39
Isso é ótimo, basta estar ciente de que alterar crop_img mudará img. Caso contrário, você deve cortar_img = img [y: y + h, x: x + w] .copy ()
user1270710 27/10/18
1
@javadba detalhes de implementação numpy. Numpy usa linha, notação col vez de col, row
Froyo
121

Eu tive essa pergunta e encontrei outra resposta aqui: copiar região de interesse

Se considerarmos (0,0) o canto superior esquerdo da imagem chamado imcom a esquerda para a direita como direção x e de cima para baixo como direção y. e temos (x1, y1) como o vértice superior esquerdo e (x2, y2) como o vértice inferior direito de uma região retangular dentro dessa imagem;

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

aqui está um recurso abrangente sobre indexação e fatiamento de matrizes numpy, que pode informar mais sobre coisas como cortar parte de uma imagem. as imagens seriam armazenadas como uma matriz numpy no opencv2.

:)

samkhan13
fonte
Oi, Não deveria ser `roi = im [y1: y2 + 1, x1: x2 + 1]` sob suas circunstâncias? Porque numpy usa a região excluída para cortar.
Scott Yang
@ samkhan13, quando corto usando essa fórmula, todas as minhas culturas têm forma (0, largura, canais). Ou seja. Eu não estou obtendo uma dimensão de todo
mLstudent33 #
@ mLstudent33 é provável que a imagem imnão tenha sido lida corretamente e esteja vazia. tente usar um IDE com pontos de interrupção para diagnosticar seu código passo a passo. você pode usar o google colab para criar blocos de código e compartilhar seu notebook jupytor na sala de bate-papo stackoverflow python para obter ajuda de alguém.
precisa saber é o seguinte
@ samkhan13 Na verdade, tenho um problema estranho que publiquei no Github Opencv Issues: github.com/opencv/opencv/issues/15406 Também vou conferir o bate-papo. Obrigado!
precisa saber é o seguinte
16

Observe que, o fatiamento da imagem não está criando uma cópia do, cropped imagemas criando um pointerpara oroi . Se você estiver carregando tantas imagens, cortando as partes relevantes das imagens com fatias e anexando-as a uma lista, isso pode ser um enorme desperdício de memória.

Suponha que você carregue N imagens cada uma >1MPe precise apenas da 100x100região no canto superior esquerdo.

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.

Como alternativa, você pode copiar a parte relevante .copy(), para que o coletor de lixo seja removido 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.

Depois de descobrir isso, percebi que um dos comentários do user1270710 mencionou isso, mas levei algum tempo para descobrir (por exemplo, depuração etc). Então, acho que vale a pena mencionar.

smttsp
fonte
Dê uma olhada no seguinte: stackoverflow.com/q/60359398/7644562
Abdul Rehman
Em relação ao espaço ocupado na memória, entendo que copiar a região de interesse é a melhor coisa a fazer, mas e consumir tempo? Se eu fizer copy()o ROI, comparado ao fatiamento, qual seria o resultado? Além disso, se eu tiver uma variável tmpna qual guardo cada imagem que carrego do meu computador, a fatia não deve ter um impacto ruim na minha memória, certo? O problema que você descreve está relacionado apenas ao que acontece quando você carrega todas as imagens e, em seguida, armazena novamente o ROI, tendo os originais e o ROI . Por favor, deixe-me saber se eu entendi direito.
Cătălina Sîrbu
Copiar será um tempo insignificante no caso que eu disse. A menos que você copie imagens grandes tantas vezes, não terá diferença de horário. No meu código, o efeito será menor que 1ms por corte. O problema é que você armazena a imagem grande e um ponteiro (ROI de apenas alguns bytes) ou armazena uma imagem pequena na memória (no meu caso). Se você fizer isso algumas vezes, tudo bem. No entanto, se você fizer isso milhares de vezes, o uso da memória ficará louco com o fatiamento. Como você preenche toda a memória depois de algumas centenas, se carregar milhares de imagens, se você fatiar. Considerando que meu código ainda estará no pedido se MBs
smttsp
12

esse código recorta uma imagem da posição x = 0, y = 0 até 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
fonte
@hatami, então a altura é de 100 pixels "abaixo" y = 0, certo? É a 101ª linha da matriz numpy? E a largura é de 200 pixels à direita de x = 0, correto?
precisa saber é o seguinte
4

Abaixo está o caminho para cortar uma imagem.

image_path: o caminho para a imagem a editar

coords: Uma tupla de coordenadas x / y (x1, y1, x2, y2) [abra a imagem no mspaint e verifique a "régua" na guia Visualizar para ver as coordenadas]

saved_location : caminho para salvar a imagem cortada

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

Recorte robusto com função de borda de cópia 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
fonte
Pode me explicar o que é bbox aqui eo que é que vamos dar no seu valor porque o valor que eu estou tentando passar, ele está me dando erro no x1,y1,x2,y2 = bbox enquanto dizia:TypeError: 'int' object is not iterable
Sabah
3

aqui está um código para imcrop mais robusto (um pouco como no 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
fonte
1

Como alternativa, você pode usar o tensorflow para o recorte e o openCV para criar uma matriz a partir da imagem.

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

Agora imgé uma matriz de forma (altura da imagem, largura da imagem, 3). Corte a matriz com fluxo tensor:

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
)

Remonte a imagem com tf.keras, para que possamos ver se funcionou:

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

Isso imprime a foto em um notebook (testado no Google Colab).


O código inteiro junto:

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
fonte