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.

Nolik
la source

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)
Froyo
la source
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.

:)

samkhan13
la source
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é.

smttsp
la source
jetez un oeil à ceci: stackoverflow.com/q/60359398/7644562
Abdul Rehman
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) 
m.hatami
la source
@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')
Sanyal
la source
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
belgraviton
la source
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
Dan Erez
la source
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
)
zabop
la source