Je souhaite recadrer une zone en forme de rectangle à partir d'une image à l'aide de Pillow en python. Le problème est que le rectangle n'est pas nécessairement parallèle aux marges de l'image, donc je ne peux pas utiliser la fonction .crop ((gauche, haut, droite, bas)).
Y a-t-il un moyen d'y parvenir avec Pillow? (en supposant que nous connaissons les coordonnées des 4 points du rectangle) Sinon, comment cela peut-il être fait en utilisant une autre bibliothèque Python?
3 Réponses :
Vous pouvez utiliser le rectangle pivoté min dans OpenCV:
rect = cv2.minAreaRect(cnt) box = cv2.boxPoints(rect) box = np.int0(box)
En conséquence, vous avez: coordonnées du centre (x, y), largeur, hauteur, angle de rotation du rectangle. Vous pouvez faire pivoter l'image entière avec l'angle de ce rectangle. Votre image va maintenant pivoter:
Vous pouvez calculer de nouvelles coordonnées de quatre sommets de rectangle (vous avez un angle). Ensuite, calculez simplement le rectangle normal pour ces points (rectangle normal = non minimal, sans aucune rotation). Avec ce rect, vous pouvez recadrer votre image pivotée. Dans cette image recadrée sera ce que vous voulez si je vous comprends correctement. Quelque chose comme ça:
Vous n'avez donc besoin que d'Opencv. Il existe peut-être une bibliothèque avec laquelle vous pouvez le faire plus facilement.
Voici une solution basée sur scikit-image (pas Pillow) que vous pourriez trouver utile.
Vous pouvez passer les sommets de la région que vous souhaitez rogner à la fonction skimage.draw.polygon
puis utilisez le fichier récupéré coordonnées en pixels pour masquer l'image d'origine (par exemple, via le canal alpha).
import numpy as np from skimage import io, draw img = io.imread('https://i.stack.imgur.com/x5Ym4.png') vertices = np.asarray([[150, 140], [300, 240], [210, 420], [90, 320], [150, 150]]) rows, cols = draw.polygon(vertices[:, 0], vertices[:, 1]) crop = img.copy() crop[:, :, -1] = 0 crop[rows, cols, -1] = 255 io.imshow(crop)
J'ai adapté ce opencv Solution basée sur
( sub_image
) à utiliser avec PIL
. Il prend un rect (center, size, theta)
que j'obtiens de cv2.minAreaRect
, mais pourrait être construit de manière mathématique à partir de points, etc.
I ' J'ai vu quelques autres solutions mais ils ont laissé des artefacts étranges.
def crop_tilted_rect(image, rect): """ crop rect out of image, handing rotation rect in this case is a tuple of ((center_x, center_y), (width, height), theta), which I get from opencv's cv2.minAreaRect(contour) """ # Get center, size, and angle from rect center, size, theta = rect width, height = [int(d) for d in size] if 45 < theta <= 90: theta = theta - 90 width, height = height, width theta *= math.pi / 180 # convert to rad v_x = (math.cos(theta), math.sin(theta)) v_y = (-math.sin(theta), math.cos(theta)) s_x = center[0] - v_x[0] * (width / 2) - v_y[0] * (height / 2) s_y = center[1] - v_x[1] * (width / 2) - v_y[1] * (height / 2) mapping = np.array([v_x[0],v_y[0], s_x, v_x[1],v_y[1], s_y]) return image.transform((width, height), Image.AFFINE, data=mapping, resample=0, fill=1, fillcolor=(255,255,255))
Veuillez fournir les coordonnées que vous avez vraisemblablement pour les coins. À quoi vous attendez-vous du résultat?
À quoi vous attendriez-vous? l'image avec des pixels noirs à l'extérieur du rectangle? ou une image rectifiée?