1.はじめに
今まで、画像のセグメンテーションは、YolactEdge や Mask R-CNN のような物体を検出して形を推定するものをご紹介しましたが、今回ご紹介するのはセマンティック・セグメンテーションと呼ばれるタスクで、画像をピクセル単位でカテゴリを分類する PSPNet です。
*この論文は、2017.4に提出されました。
2.PSPNetとは?
下記に、PSPNetの概要を示します。
まず、Input Image(入力画像)を CNN に入力し Feature Map(特徴マップ)を取得します。次に、プーリングを通して Pyramid Pooling Module で、様々なスケールでの領域表現を収集したものを、先程の Feature Map とCONCAT(連結)させます。最後に、畳み込み層を通して Final Prediction(最終的なピクセル毎の予測を)取得します。
早速、コードを動かしてみましょう。
3.コード
コードはGoogle Colabで動かす形にしてGithubに上げてありますので、それに沿って説明して行きます。自分で動かしてみたい方は、この「リンク」をクリックし表示されたノートブックの先頭にある「Colab on Web」ボタンをクリックすると動かせます。
まず、セットアップを行います。
1 2 3 4 5 6 7 8 |
%%bash # Colab-specific setup !(stat -t /usr/local/lib/*/dist-packages/google/colab > /dev/null 2>&1) && exit pip install yacs 2>&1 >> install.log git init 2>&1 >> install.log git remote add origin https://github.com/CSAILVision/semantic-segmentation-pytorch.git 2>> install.log git pull origin master 2>&1 >> install.log DOWNLOAD_ONLY=1 ./demo_test.sh 2>> install.log |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
# System libs import os, csv, torch, numpy, scipy.io, PIL.Image, torchvision.transforms # Our libs from mit_semseg.models import ModelBuilder, SegmentationModule from mit_semseg.utils import colorEncode colors = scipy.io.loadmat('data/color150.mat')['colors'] names = {} with open('data/object150_info.csv') as f: reader = csv.reader(f) next(reader) for row in reader: names[int(row[0])] = row[5].split(";")[0] def visualize_result(img, pred, index=None): # filter prediction class if requested if index is not None: pred = pred.copy() pred[pred != index] = -1 print(f'{names[index+1]}:') # colorize prediction pred_color = colorEncode(pred, colors).astype(numpy.uint8) # aggregate images and save im_vis = numpy.concatenate((img, pred_color), axis=1) display(PIL.Image.fromarray(im_vis)) |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
# Network Builders net_encoder = ModelBuilder.build_encoder( arch='resnet50dilated', fc_dim=2048, weights='ckpt/ade20k-resnet50dilated-ppm_deepsup/encoder_epoch_20.pth') net_decoder = ModelBuilder.build_decoder( arch='ppm_deepsup', fc_dim=2048, num_class=150, weights='ckpt/ade20k-resnet50dilated-ppm_deepsup/decoder_epoch_20.pth', use_softmax=True) crit = torch.nn.NLLLoss(ignore_index=-1) segmentation_module = SegmentationModule(net_encoder, net_decoder, crit) segmentation_module.eval() segmentation_module.cuda() |
次に、サンプル画像を読み込みます。自分の画像を使用したい場合は、カレントディレクトリに自分の画像をドラッグ&ドロップでアップロードし、pil_image = PIL.Image.open(‘***.jpg’).convert(‘RGB’)の’***.jpg’の部分を変更して下さい。
1 2 3 4 5 6 7 8 9 10 11 12 |
# Load and normalize one image as a singleton tensor batch pil_to_tensor = torchvision.transforms.Compose([ torchvision.transforms.ToTensor(), torchvision.transforms.Normalize( mean=[0.485, 0.456, 0.406], # These are RGB mean+std values std=[0.229, 0.224, 0.225]) # across a large photo dataset. ]) pil_image = PIL.Image.open('ADE_val_00001519.jpg').convert('RGB') img_original = numpy.array(pil_image) img_data = pil_to_tensor(pil_image) singleton_batch = {'img_data': img_data[None].cuda()} output_size = img_data.shape[1:] |
読み込んだ画像にセマンティック・セグメンテーションを掛けて、表示させます。
1 2 3 4 5 6 7 8 |
# Run the segmentation at the highest resolution. with torch.no_grad(): scores = segmentation_module(singleton_batch, segSize=output_size) # Get the predicted scores for each pixel _, pred = torch.max(scores, dim=1) pred = pred.cpu()[0].numpy() visualize_result(img_original, pred) |
画像を構成するピクセル毎に色分けされ、カテゴリ分類されているのが分かります。
それでは、カテゴリー分類した結果のTOP15を1つづつ表示させます。
1 2 3 4 |
# Top classes in answer predicted_classes = numpy.bincount(pred.flatten()).argsort()[::-1] for c in predicted_classes[:15]: visualize_result(img_original, pred, c) |
——————————————– 以下略 ———————————————-
それでは、今度は動画でやってみましょう。まず、サンプルビデオをダウンロードします。
1 2 3 |
# サンプルビデオをダウンロード import gdown gdown.download('https://drive.google.com/uc?id=1cfa4R-0Zwd2Te5-qBWe9oNRKQ_pEUr0z', 'road.mp4', quiet=False) |
サンプルビデオから静止画を切り出し、連番のjpg画像として images フォルダーに保存します。設定は、3フレーム毎に切り出す形(interval = 3)にしています。
ご自分のビデオを使う場合は、自分のPCからカレントディレクトリへビデオをドラッグ&ドロップでアップロードし、video_file = の部分を変更して下さい。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
# サンプルビデオを静止画に変換 import os import shutil import cv2 def video_2_images(video_file= './road.mp4', # ビデオ指定 image_dir='./images/', image_file='%s.jpg'): # Initial setting i = 0 interval = 3 length = 600 # 最大フレーム数 cap = cv2.VideoCapture(video_file) while(cap.isOpened()): flag, frame = cap.read() if flag == False: break if i == length*interval: break if i % interval == 0: cv2.imwrite(image_dir+image_file % str(int(i/interval)).zfill(6), frame) i += 1 cap.release() # imagesフォルダーリセット if os.path.isdir('images'): shutil.rmtree('images') os.makedirs('images', exist_ok=True) # ビデオを静止画に変換 video_2_images() |
images フォルダーにある連番のjpg画像をセマンティック・セグメンテーションしたものに置き換えます。ファイル名は変更しません。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
# 静止画をセグメンテーションに変換 # 正規化データをロード pil_to_tensor = torchvision.transforms.Compose([ torchvision.transforms.ToTensor(), torchvision.transforms.Normalize( mean=[0.485, 0.456, 0.406], # These are RGB mean+std values std=[0.229, 0.224, 0.225]) # across a large photo dataset. ]) # imagesフォルダーの静止画を1枚づつ処理 from tqdm import tqdm import glob files = glob.glob('./images/*.jpg') files.sort() for file in tqdm(files): pil_image = PIL.Image.open(file).convert('RGB') img_original = numpy.array(pil_image) img_data = pil_to_tensor(pil_image) singleton_batch = {'img_data': img_data[None].cuda()} output_size = img_data.shape[1:] # セグメンテーションの実行 with torch.no_grad(): scores = segmentation_module(singleton_batch, segSize=output_size) # 予測結果の処理 _, pred = torch.max(scores, dim=1) pred = pred.cpu()[0].numpy() pred_color = colorEncode(pred, colors).astype(numpy.uint8) im_vis = numpy.concatenate((img_original, pred_color), axis=1) # オリジナルと横連結 #im_vis = numpy.concatenate((pred_color, img_original), axis=0) # オリジナルと縦連結 PIL.Image.fromarray(im_vis).save(file) |
ffmpeg を使って、images フォルダーにある連番の jpg 画像を動画(output.mp4)に変換します。
1 2 3 4 5 6 |
# output.mp4をリセット if os.path.exists('./output.mp4'): os.remove('./output.mp4') # 実写+セグメンテーションをmp4動画に変換 !ffmpeg -r 10 -i images/%06d.jpg -vcodec libx264 -pix_fmt yuv420p output.mp4 |
1 2 3 4 5 6 7 8 9 10 |
# mp4動画の再生 from IPython.display import HTML from base64 import b64encode mp4 = open('./output.mp4', 'rb').read() data_url = 'data:video/mp4;base64,' + b64encode(mp4).decode() HTML(f""" <video width="100%" height="100%" controls> <source src="{data_url}" type="video/mp4"> </video>""") |
では、また。
(オリジナルgithub)https://github.com/CSAILVision/semantic-segmentation-pytorch