Comment recadrer une image dans OpenCV en utilisant Python


234

Comment puis-je recadrer des images, comme je l'ai déjà fait dans PIL, en utilisant OpenCV.

Exemple de travail sur PIL

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

Mais comment puis-je le faire sur OpenCV?

Voici ce que j'ai essayé:

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)

Mais ça ne marche pas.

Je pense que j'ai mal utilisé getRectSubPix. Si tel est le cas, veuillez expliquer comment je peux utiliser correctement cette fonction.

Réponses:


529

C'est très simple. Utilisez un découpage 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)

9
Hmm ... Mais comment puis-je enregistrer l'image de recadrage en variable?
Nolik

56
rappelez - vous que x et y sont inversés. J'ai manqué ça.
markroxor

10
Alternativement, si vous avez défini une marge de récolte, vous pouvez le fairecrop_img = img[margin:-margin, margin:-margin]
Rufus

39
C'est génial, sachez simplement que changer crop_img changera img. Sinon, vous devez crop_img = img [y: y + h, x: x + w] .copy ()
user1270710

1
Détail de l'implémentation de @javadba numpy. Numpy utilise la notation row, col au lieu de col, row
Froyo

121

j'ai eu cette question et j'ai trouvé une autre réponse ici: copier la région d'intérêt

Si l'on considère (0,0) comme le coin supérieur gauche de l'image appelé imavec de gauche à droite comme direction x et de haut en bas comme direction y. et nous avons (x1, y1) comme sommet en haut à gauche et (x2, y2) comme sommet en bas à droite d'une région rectangulaire dans cette image, puis:

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

voici une ressource complète sur l' indexation et le découpage de tableaux numpy qui peut vous en dire plus sur des choses comme le recadrage d'une partie d'une image. les images seraient stockées sous forme de tableau numpy dans opencv2.

:)


Salut, cela ne devrait-il pas être `roi = im [y1: y2 + 1, x1: x2 + 1]` dans vos circonstances? Parce que numpy utilise la région exclue pour trancher.
Scott Yang

@ samkhan13, lorsque je recadre en utilisant cette formule, tous mes recadrages ont une forme (0, largeur, canaux). C'est à dire. Je n'obtiens pas du tout une dimension ay
mLstudent33

@ mLstudent33 il est probable que l'image imn'ait pas été lue correctement et soit vide. essayez d'utiliser un IDE avec des points d'arrêt pour diagnostiquer votre code étape par étape. vous pouvez utiliser google colab pour créer des blocs de code et partager votre bloc-notes jupytor sur la salle de discussion stackoverflow python pour obtenir de l'aide.
samkhan13

@ samkhan13 en fait, j'ai un problème étrange que j'ai publié sur Github Opencv Issues: github.com/opencv/opencv/issues/15406 Je vais également consulter le chat. Merci!
mLstudent33

16

Notez que le découpage d'image ne crée pas une copie de cropped imagemais crée un pointervers le roi. Si vous chargez autant d'images, recadrez les parties pertinentes des images avec des tranches, puis ajoutez-les dans une liste, cela pourrait être un énorme gaspillage de mémoire.

Supposons que vous chargiez N images chacune >1MPet que vous n'ayez besoin que d'une 100x100région dans le coin supérieur gauche.

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.

Alternativement, vous pouvez copier la partie appropriée par .copy(), afin que le garbage collector soit supprimé 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.

Après avoir découvert cela, je me suis rendu compte que l' un des commentaires de user1270710 le mentionnait, mais cela m'a pris un certain temps pour le découvrir (c'est-à-dire le débogage, etc.). Donc, je pense que cela mérite d'être mentionné.



En termes d'espace mémoire occupé, je comprends que la copie de la région d'intérêt est la meilleure chose à faire, mais qu'en est-il du temps? Si je fais copy()le retour sur investissement, par rapport au tranchage, quel serait le résultat?. De plus, si j'ai une variable tmpdans laquelle je stocke chaque image que je charge depuis mon ordinateur, le découpage ne devrait pas avoir un mauvais impact sur ma mémoire, non? Le problème que vous décrivez est uniquement lié à ce qui se passe lorsque vous chargez toutes les images et que vous les enregistrez à nouveau ROI, ayant à la fois les originaux et le ROI . Veuillez me faire savoir si j'ai bien compris.
Cătălina Sîrbu

La copie sera un temps négligeable dans le cas que j'ai dit. Si vous ne copiez pas de grandes images autant de fois, vous n'aurez pas de décalage horaire. Dans mon code, l'effet sera comme moins de 1 ms par recadrage. Le problème est que vous stockez la grande image et un pointeur (ROI qui n'est que de quelques octets) ou que vous stockez une petite image en mémoire (dans mon cas). Si vous faites cela plusieurs fois, c'est très bien. Cependant, si vous le faites des milliers de fois, l'utilisation de la mémoire sera folle avec le découpage. Comme si vous remplissiez la mémoire entière après quelques milliers d'images, si vous chargez des tranches. Alors que mon code sera toujours sur la commande si
MB

12

ce code recadre une image de x = 0, y = 0 à 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, donc la hauteur est de 100 pixels "en dessous" y = 0, n'est-ce pas? C'est la 101ème ligne du tableau numpy? Et la largeur est de 200 pixels à droite de x = 0 correct?
mLstudent33

4

Vous trouverez ci-dessous la façon de recadrer une image.

chemin_image: chemin d'accès à l'image à modifier

coords: Un tuple de coordonnées x / y (x1, y1, x2, y2) [ouvrez l'image dans mspaint et vérifiez la "règle" dans l'onglet vue pour voir les coordonnées]

saved_location : chemin pour enregistrer l'image recadrée

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

Recadrage robuste avec fonction de bordure de copie ouverte:

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

Pouvez-vous s'il vous plaît expliquer ce qu'est la bbox ici et ce que nous sommes censés donner dans sa valeur car quelle que soit la valeur que j'essaie de passer, cela me donne une erreur x1,y1,x2,y2 = bbox en disant:TypeError: 'int' object is not iterable
Sabah

3

voici du code pour une imcrop plus robuste (un peu comme dans 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

Alternativement, vous pouvez utiliser tensorflow pour le recadrage et openCV pour créer un tableau à partir de l'image.

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

Il imgs'agit maintenant d' un tableau de formes (imageheight, imagewidth, 3). Recadrez le tableau avec 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
)

Remontez l'image avec tf.keras, afin que nous puissions la regarder si cela a fonctionné:

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

Cela imprime la photo dans un cahier (testé dans Google Colab).


L'ensemble du code ensemble:

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
)
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.