J'ai créé un outil de recadrage d'images en utilisant OpenCV. Coupez l'objet, projetez-le, façonnez-le et enregistrez chacun d'eux.
L'objet est supposé être un film avec l'image suivante. Même si ce n'est pas le cas, je pense qu'il peut être utilisé lors de l'extraction d'un rectangle.
<img width="200", alt="sample.jpg ", src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/779817/f532ad11-9c6d-e7df-844b-a30e6a2851ea.jpeg "> <img width="100", alt="sample2.png ", src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/779817/528427a2-9619-e2d1-b189-8a513cf87c64.jpeg ">
De plus, comme je suis tombé sur la conversion de projection lors de la création, le contenu et la solution sont décrits à la fin. Détails
Mac OS python 3.8.5
opencv-python 4.4.0.44 numpy 1.19.2 tqdm 4.50.2
python
pip install opencv-python
pip install tqdm
J'importe tqdm pour utiliser la barre de progression.
C'est un processus pour corriger l'objet comme s'il avait été pris de face. Je l'ai utilisé parce que je voulais que l'image recadrée soit à angle droit.
<img width="350", alt="射影変換後.jpeg ", src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/779817/5b69d421-8b17-d09c-b6d4-e7843473ccb2.jpeg ">
Article de référence pour la conversion de projection La correction d'image est facile avec la transformation de projection Python / OpenCV! | WATLAB -Python, traitement du signal, AI- Essayez la conversion par projection d'images en utilisant OpenCV avec Python --Qiita
Je me réfère aux articles suivants pour rendre les chemins japonais compatibles avec la lecture d'images. À propos du traitement des problèmes lors de la gestion des chemins de fichiers, y compris le japonais dans Python OpenCV cv2.imread et cv2.imwrite --Qiita
La méthode de fonctionnement est
Le résultat sera enregistré sous "./ dossier de résultats / nom du fichier image / nom du fichier image_0, ...". Si le même dossier que le nom de fichier existe, le processus est ignoré.
Si vous ne pouvez pas le couper correctement, essayez de changer thresh_value ou minimum_area. [Traitement des seuils d'image - documentation OpenCV-Python Tutorials 1](http://labs.eecs.tottori-u.ac.jp/sd/Member/oyamada/OpenCV/html/py_tutorials/py_imgproc/py_thresholding/py_thresholding. html) [Fonctionnalités de zone (contour) - documentation OpenCV-Python Tutorials 1](http://labs.eecs.tottori-u.ac.jp/sd/Member/oyamada/OpenCV/html/py_tutorials/py_imgproc/py_contours/py_contour_features/ py_contour_features.html)
python
import os, shutil, time
from pathlib import Path
import cv2
import numpy as np
from tqdm import tqdm
thresh_value = 240 #Valeur limite lors de la binarisation,Si la valeur du pixel est plus petite que cela, rendez-le blanc(max:255)
minimum_area = 10000 #Ne traitez pas d'objets plus petits que cela lors de l'acquisition du contour(Pour lorsque des points autres que l'objet cible sont détectés)
def imread(filename, flags=cv2.IMREAD_COLOR, dtype=np.uint8):
try:
n = np.fromfile(filename, dtype)
img = cv2.imdecode(n, flags)
return img
except Exception as e:
print(e)
return None
def imwrite(filename, img, params=None):
try:
ext = os.path.splitext(filename)[1]
result, n = cv2.imencode(ext, img, params)
if result:
with open(filename, mode='w+b') as f:
n.tofile(f)
return True
else:
return False
except Exception as e:
print(e)
return False
def calculate_width_height(pts, add):
"""
Largeur de la forme détectée,Trouvez la hauteur en utilisant trois carrés
S'il est trop incliné, la forme changera lors de la conversion de la projection.
:parameter
------------
pts: numpy.ndarray
Coordonnées de 4 points de la forme extraite, shape=(4, 1, 2)
add: int
Correction car les coordonnées du point de départ diffèrent selon la forme
:return
------------
width: int
Largeur calculée
height: int
Hauteur calculée
"""
top_left_cood = pts[0 + add][0]
bottom_left_cood = pts[1 + add][0]
bottom_right_cood = pts[2 + add][0]
width = np.int(np.linalg.norm(bottom_left_cood - bottom_right_cood))
height = np.int(np.linalg.norm(top_left_cood - bottom_left_cood))
return width, height
def img_cut():
"""
Images dans le dossier de ressources(jpg, png)Pour obtenir le contour de l'objet
Coupez l'objet, projetez-le et amenez-le à l'avant
1.dossier,Lire le fichier
2.Lecture d'image,Binarisation(Noir et blanc)En traitement
3.Obtenez un contour
4.Conversion de projection
5.production
6.Déplacer le fichier de ressources vers le résultat
:return: None
"""
# 1.dossier,Lire le fichier
resource_folder = Path(r'./resource')
result_folder = Path(r'./result')
#Créer si le dossier de résultats n'existe pas
if not result_folder.exists():
result_folder.mkdir()
img_list1 = list(resource_folder.glob('*.jpg')) #Liste des chemins des fichiers jpg dans le dossier
img_list2 = list(resource_folder.glob('*.jpeg'))
img_list3 = list(resource_folder.glob('*.png'))
img_list = img_list1 + img_list2 + img_list3
for img in img_list:
img_name, img_suffix = img.stem, img.suffix #Obtenir le nom et l'extension de l'image
#Créez un dossier avec le nom du fichier image dans le dossier de résultats,Ignorer la conversion si le même dossier existe déjà
result_img_folder = Path(r'./result/{}'.format(img_name))
if not result_img_folder.exists():
result_img_folder.mkdir()
else:
print('{}Ne peut pas être converti car un dossier portant le même nom que celui existant dans result'.format(img_name))
continue
# 2.Lecture d'image,Binarisation(Noir et blanc)En traitement
read_img = imread(str(img))
gray_img = cv2.cvtColor(read_img, cv2.COLOR_BGR2GRAY)
ret, thresh_img = cv2.threshold(gray_img, thresh_value, 255, cv2.THRESH_BINARY_INV)
# --------------------------------------------
#Pour la confirmation d'image binarisée
# cv2.namedWindow('final', cv2.WINDOW_NORMAL)
# cv2.imshow('final', thresh_img)
# cv2.waitKey(0)
# cv2.destroyAllWindows()
# --------------------------------------------
# 3.Obtenez un contour
# cv2.RETR_EXTERNAL:Extraire uniquement le contour le plus extérieur des contours détectés->Ignorer les contours même s'ils sont à l'intérieur du contour
# cv2.CHAIN_APPROX_SIMPLE:Obtenez seulement 4 coins, pas les bords du contour
contours, hierarchy = cv2.findContours(thresh_img, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
process_cnt = [] #Liste des contours à couper réellement
for cnt in contours:
if cv2.contourArea(cnt) < minimum_area: #Ne coupez pas d'objets dont la zone de contour est trop petite
continue
process_cnt.append(cnt)
num = 0
for p_cnt in tqdm(process_cnt[::-1], desc='{}'.format(img_name)): #Pour une raison quelconque, le processus commence à partir de l'image du bas, alors coupez-la en sens inverse.(Vers le haut)Correction de
x, y, w, h = cv2.boundingRect(p_cnt) #En haut à gauche du contour x,coordonnée y&largeur,Obtenez de la hauteur
img_half_width = x + w / 2
# cv2.arcLength:Longueur périphérique du contour,True signifie que le contour est fermé
# cv2.approPolyDP:Forme approximative détectée
epsilon = 0.1 * cv2.arcLength(p_cnt, True)
approx = cv2.approxPolyDP(p_cnt, epsilon, True)
try:
# 4.Conversion de projection
pts1 = np.float32(approx)
if pts1[0][0][0] < img_half_width: #Si le point de départ des coordonnées stockées dans pts est le coin supérieur gauche
width, height = calculate_width_height(pts1, 0)
pts2 = np.float32([[0, 0], [0, height], [width, height], [width, 0]])
else:
width, height = calculate_width_height(pts1, 1)
pts2 = np.float32([[width, 0], [0, 0], [0, height], [width, height]])
except IndexError:
continue
M = cv2.getPerspectiveTransform(pts1, pts2)
dst = cv2.warpPerspective(read_img, M, (width, height))
result_img_name = img_name + '_{}.{}'.format(num, img_suffix)
imwrite(str(result_img_folder) + '/' + result_img_name, dst)
num += 1
# 6.Déplacer le fichier de ressources vers le résultat
shutil.move(str(img), result_img_folder)
if __name__ == '__main__':
img_cut()
print('Fin d'exécution')
time.sleep(3)
python
# cv2.arcLength:Longueur périphérique du contour,True signifie que le contour est fermé
# cv2.approPolyDP:Forme approximative détectée
epsilon = 0.1 * cv2.arcLength(p_cnt, True)
approx = cv2.approxPolyDP(p_cnt, epsilon, True)
try:
# 4.Conversion de projection
pts1 = np.float32(approx)
Les coordonnées des 4 coins de l'objet sont stockées dans pts1. ex) [[[6181. 598.]]
[[ 145. 656.]]
[[ 135. 3499.]]
[[6210. 3363.]]]
En amenant ces quatre points aux coins de l'image, l'image semble avoir été vue de face.
python
if pts1[0][0][0] < img_half_width: #Si le point de départ des coordonnées stockées dans pts est le coin supérieur gauche
width, height = calculate_width_height(pts1, 0)
pts2 = np.float32([[0, 0], [0, height], [width, height], [width, 0]])
else:
width, height = calculate_width_height(pts1, 1)
pts2 = np.float32([[width, 0], [0, 0], [0, height], [width, height]])
Il détermine où se trouve le point de départ de pts1. Étant donné que les quatre points de coordonnées stockés sont stockés dans le sens inverse des aiguilles d'une montre à partir du point supérieur (l'axe des y est petit), le point de départ de pts1 change en fonction de l'inclinaison de l'image. Il est jugé et fait correspondre aux coordonnées pts2 après conversion de projection. <img width="400", alt="pts座標.png ", src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/779817/f6c5e9a7-d49c-45ca-191e-05efa952f14a.png ">
La méthode de jugement est basée sur le fait que la coordonnée x du point de départ se trouve à gauche ou à droite du centre de l'image.
De plus, comme je veux garder la forme de l'objet autant que possible, j'ai calculé la largeur et la hauteur en utilisant trois carrés et j'ai sorti une image recadrée avec cette taille.
Je ne savais pas que le point de départ de pts1 allait changer et au début, je produisais une image dénuée de sens. Je ne pouvais pas le trouver facilement même si je cherchais sur le net, et à la fin j'étais dans un état où je l'ai finalement découvert en regardant l'image. J'espère que cela sera utile lorsqu'il y a des gens qui ont des problèmes similaires.
Didacticiel [Traitement des seuils d'image - documentation OpenCV-Python Tutorials 1](http://labs.eecs.tottori-u.ac.jp/sd/Member/oyamada/OpenCV/html/py_tutorials/py_imgproc/py_thresholding/py_thresholding. html) Plan: Première étape - documentation des didacticiels OpenCV-Python 1 [Fonctionnalités de zone (contour) - documentation OpenCV-Python Tutorials 1](http://labs.eecs.tottori-u.ac.jp/sd/Member/oyamada/OpenCV/html/py_tutorials/py_imgproc/py_contours/py_contour_features/ py_contour_features.html)
Référence OpenCV (contour, rognage d'image, conversion de projection) Bases de la détection de contour à partir d'images avec OpenCV (par fonction findContours) | North Building Tech.com [Obtenir et rogner des objets dans une image en utilisant contours-python, image, opencv-contour](https://living-sun.com/ja/python/725302-getting-and-cropping-object-in] -images-utilisant-des-contours-python-image-opencv-contour.html) Essayez la conversion par projection d'images en utilisant OpenCV avec Python --Qiita
Prise en charge de la lecture des pass japonais OpenCV À propos du traitement des problèmes lors de la gestion des chemins de fichiers, y compris le japonais dans Python OpenCV cv2.imread et cv2.imwrite --Qiita
référence de calcul numpy trois carrés [Calculer la distance euclidienne](http://harmonizedai.com/article/%E3%83%A6%E3%83%BC%E3%82%AF%E3%83%AA%E3%83%83%E3% 83% 89% E8% B7% 9D% E9% 9B% A2% E3% 82% 92% E6% B1% 82% E3% 82% 81% E3% 82% 8B /)
Référence de la barre de progression Afficher la barre de progression avec tqdm --Qiita
Recommended Posts