[PYTHON] Extraire uniquement la partie chat de l'image du chat (segmentation mat / semantig)

Aperçu

À partir de l'image du chat collectée sur PIXTA, j'ai essayé d'extraire uniquement la partie du chat avec le moins d'effort possible.

Extraction de premier plan

Le processus de séparation du premier plan et de l'arrière-plan de l'image est appelé matage. Cela semble être une fonction majeure dans Photoshop, et dans le cas de gimp, la fonction nommée "Foreground Extraction" lui correspond. Il semble que le comportement général consiste à diviser l'image en trois types, "premier plan", "arrière-plan" et "l'un ou l'autre", et à déterminer la limite en regardant les caractéristiques de l'image dans chaque zone.

Il semble difficile d'automatiser ce processus, et dans le cadre de mes recherches, je n'ai pu trouver qu'une méthode appelée Deep Automatic Portrait Matting, qui extrait uniquement des personnes à partir de photographies de portrait.

En dehors de cela, il y avait un article sur une méthode de génération automatique d'informations (trimap) utilisée pour le matage.

Cependant, le matage automatique n'est pas toujours possible si un trimap approprié peut être généré. En fait, lors du traitement avec gimp, etc., il existe de nombreuses situations dans lesquelles le jugement de premier plan / arrière-plan n'est pas classé comme prévu et un ajustement fin est nécessaire.

Segmentation sémantique

Comme autre approche, j'ai envisagé d'utiliser la segmentation sémantique. La segmentation sémantique discrimine divers objets pixel par pixel pour une seule image d'entrée.

Dans l'article précédemment écrit, j'ai écrit que ChainerCV supporte également la segmentation sémantique, mais celui préparé comme modèle de pré-apprentissage est CamVid. Il est basé sur des données d'entraînement appelées ensemble de données et prend en charge 11 classes telles que le ciel, la route, la voiture et les piétons. Il n'y a aucune information sur le chat que je souhaite gérer cette fois.

Lorsque j'ai recherché d'autres implémentations et ensembles de données de segmentation sémantique, l'implémentation Keras de DilatedNet a été trouvée dans [PASCAL VOC2011 challange](http: //host.robots.ox). Je l'ai distribué avec un modèle entraîné en utilisant un jeu de données basé sur le jeu de données .ac.uk / pascal / VOC / voc2012 /), j'ai donc décidé de l'utiliser cette fois.

installer

Vous pouvez l'utiliser en clonant le code, en installant tous les modules python qui satisfont aux dépendances requirements.txt et en téléchargeant le modèle de pré-apprentissage. Puisque nous allons utiliser l'ancien backend Keras et TensorFlow (0.12.1), il est judicieux d'avoir un environnement virtualenv dédié.

$ pip install -r requirements.txt
$ curl -L https://github.com/nicolov/segmentation_keras/releases/download/model/nicolov_segmentation_model.tar.gz \
 | tar xvf -
$ python predict.py --weights_path \ 
 conversion/converted/dilation8_pascal_voc.npy \
 images/cat.jpg

dilation8_pascal_voc.npy sera le fichier de modèle de pré-entraînement. Le traitement des images / cat.jpg incluses générera un fichier appelé images / cat_seg.png.

S'applique aux images inconnues

En fait, utilisons ce modèle pour traiter une image inconnue. Malheureusement, cette implémentation a une limite sur la taille de l'image d'entrée, et il semble qu'elle ne peut être traitée que si la largeur et la hauteur sont inférieures à environ 500. Après avoir redimensionné l'image à une taille qui respecte cette limite à l'avance, j'ai essayé le traitement.

Image à traiter: サイズ修正猫画像

Sortie de segmentation セグメンテーション

La zone identifiée comme une image de chat est identifiée par la couleur # 400000 (R: 0x40, G: 0x00, B: 0x00). À première vue, cela semble fonctionner. Après cela, tout en se référant à chaque pixel de cette image, il semble que vous puissiez obtenir le résultat souhaité en remplissant la partie jugée autre que l'image du chat à partir de l'image originale.

Cependant, si vous regardez de près les limites, elles ne sont pas exactement divisées en fonction des valeurs numériques.

セグメンテーション拡大

Pour une comparaison de valeur absolue

Comme vous pouvez le voir, les valeurs autour des bords sont légèrement floues, donc si vous comparez simplement les valeurs absolues des pixels de segmentation pour déterminer s'il s'agit d'une image de premier plan, cette partie de l'image sera manquante.

絶対値比較の結果

Voir les normes entre les espaces colorimétriques

À la suite de diverses réflexions et essais, j'ai utilisé linalg.norm de numpy pour calculer la distance entre la couleur du pixel de segmentation et # 400000, et si elle se situe dans un certain nombre, il est déterminé qu'il s'agit d'un pixel d'image de chat. J'ai fait. Le résultat de considérer la plage correspondant à 32, qui est la moitié de 64, comme valide est le suivant.

ノルムによる処理結果

Ça fait du bien. Le code pour afficher ce résultat est indiqué ci-dessous.

# -*- coding: utf-8 -*-

import argparse
import os, sys
from PIL import Image
import numpy as np

def get_args():
    p = argparse.ArgumentParser()
    p.add_argument("--contents-dir", '-c', default=None)
    p.add_argument("--segments-dir", '-s', default=None)
    p.add_argument("--output-dir", '-o', default=None)
    p.add_argument("--cat-label-vals", '-v', default="64,0,0")
    args = p.parse_args()
    if args.contents_dir is None or args.segments_dir is None or args.output_dir is None:
        p.print_help()
        sys.exit(1)
    
    return args

def cat_col_array(val_str):
    vals = val_str.split(',')
    vals = [int(i) for i in vals]
    return vals

def cmpary(a1, a2):
    v1 = np.asarray(a1)
    v2 = np.asarray(a2)
    norm = np.linalg.norm(v1-v2)
    if norm <= 32:
        return True
    return False

def make_images(cont_dir, seg_dir, out_dir, cat_vals):
    files = []
    for fname in os.listdir(cont_dir): # make target file list
        seg_fname = os.path.join(seg_dir, fname)
        if os.path.exists(seg_fname):
            files.append(fname)
    for fname in files:
        print("processing: %s" % fname)
        c_fname = os.path.join(cont_dir, fname)
        cont = np.asarray(Image.open(c_fname)).copy()
        s_fname = os.path.join(seg_dir, fname)
        seg = np.asarray(Image.open(s_fname))
        width, height, _ = seg.shape
        for y in range(height):
            for x in range(width):
                vals = seg[x, y]
                if not cmpary(vals, cat_vals):
                    cont[x, y] = [255, 255, 255]
        out = Image.fromarray(cont)
        o_fname = os.path.join(out_dir, fname)
        out.save(o_fname)

def main():
    args = get_args()
    cat_vals = cat_col_array(args.cat_label_vals)
    make_images(args.contents_dir, args.segments_dir, args.output_dir,
                cat_vals)
    
if __name__ == '__main__':
    main()

Tâche

Lorsque le modèle utilisé cette fois-ci a été appliqué à toutes les images collectées sur Pixabay et sélectionnées, il n'a pas toujours montré les résultats attendus à 100%. Il y a des cas où il est jugé qu'il existe des caractéristiques de l'un des 19 types restants autres que les chats.

Une partie du corps est jugée comme un chien

Lorsque l'image suivante a été traitée, une partie de la partie du corps a été identifiée comme un chien.

処理対象

セグメンテーション結果

La teinte violette (# 400080) est la zone identifiée comme un chien. Certes, je pense que les chiens avec ce genre de couleur sont relativement chers. Si vous donnez ce résultat au script précédent, il ressemblera à ceci:

マッティング結果

Étant donné que l'image préparée cette fois-ci ne contient presque pas de chiens, il est possible de quitter la zone considérée comme un chien.

Traitement des chats noirs

Les chats noirs sont difficiles à capturer proprement dans l'image, il semble donc que des problèmes soient susceptibles de se produire en termes de discrimination.

黒猫画像

セグメンテーション結果

La partie ombre est devenue une partie du chat, et la partie du corps qui n'a l'air que noire a disparu.

マッティング結果

base de données

Cette fois, les URL vers les données ciblées pour la segmentation / mise en correspondance sont répertoriées et publiées sur github.

1100 Ce sera une petite image.

à partir de maintenant

Je pense que les bords2cats de pix2pix peuvent être reproduits dans une certaine mesure même avec ce niveau de données, donc je vais vraiment l'apprendre.

En outre, j'aimerais lire l'article pour approfondir ma compréhension de la structure de SegNet qui peut être utilisée avec DilatedNet et ChainerCV utilisé cette fois. Je voudrais également essayer de former les données de formation utilisées dans l'implémentation DilatedNet Keras avec SegNet de ChainerCV.

Recommended Posts

Extraire uniquement la partie chat de l'image du chat (segmentation mat / semantig)
Extrait uniquement complet du résultat de Trinity
Supprimer le cadre de l'image
Vision par ordinateur: segmentation sémantique, partie 2 - segmentation sémantique en temps réel
Coupons le visage de l'image
Récupérez uniquement le texte du formulaire Django.
Utilisez Pillow pour rendre l'image transparente et en superposer une partie seulement
Utilisez PIL en Python pour extraire uniquement les données souhaitées d'Exif