【料理】 ズッキーニのシャキッとマヨ炒め
ここを見て作りました。
材料 (2人分)
- ズッキーニ 1本
- エノキ 1/3袋
- ベーコン 2枚
- マヨネーズ大匙 1と1/2
- しょうゆ小匙 1弱
- 塩少々
作り方
- ズッキーニを厚さ8ミリくらいの輪切りにする。 エノキは根元を切って、小房に分ける。 ベーコンは一口大に切る。
フライパンにマヨネーズ大さじ1と1/2を入れて、火をつける。 マヨネーズが溶けたら、ズッキーニを入れる。
ズッキーニをひっくり返してから、塩少々をふる。 ベーコンとエノキを入れる。
エノキに火が通ったら、しょうゆを回しかける。 サッと混ぜたら完成!
【ディープラーニング】 セマンティックセグメンテーション手法のまとめ
ディープラーニングを利用したセマンティックセグメンテーションについてまとめてあるページを見つけたのでメモします(A 2017 Guide to Semantic Segmentation with Deep Learning)。
2017年12月10日追記 上記リンクを日本語訳した記事があったため、下記リンクを参照した方が良いです。 postd.cc
セマンティックセグメンテーションとは?
セマンティックセグメンテーションは各ピクセルを各クラスに割り当てること
VOC2012(The PASCAL Visual Object Classes Challenge 2012 (VOC2012))とMOSCOCO(COCO - Common Objects in Context)が有名なデータセット
どんなアプローチがあるの?
初期の頃は"patch classification"が用いられており、全結合層があるため入力画像のサイズは固定だった
FCNが発表されてからはFCNがメジャーになった。FCNにより任意の画像サイズを扱うことが出来るようになり、"patch classification"よりも非常に高速という特徴がある
CNNをセグメンテーションに用いることの欠点としてプーリング層の存在がある。プーリング層があることでピクセルの細かい位置情報に捉われることがなくなるが、セグメンテーションは位置情報が重要
位置情報の問題に対処するために2つのアーキテクチャがある。 一つはエンコーダー・デコーダー型でU-Netが有名。もう一つはdilated/atrous convolutions
conditional random fieldによる後処理と併用すると、スコアが1~2%上昇する
読むべき論文
[1411.4038] Fully Convolutional Networks for Semantic Segmentation
[1511.00561] SegNet: A Deep Convolutional Encoder-Decoder Architecture for Image Segmentation
[1511.07122] Multi-Scale Context Aggregation by Dilated Convolutions
[1412.7062] Semantic Image Segmentation with Deep Convolutional Nets and Fully Connected CRFs
[1611.06612] RefineNet: Multi-Path Refinement Networks for High-Resolution Semantic Segmentation
[1703.02719] Large Kernel Matters -- Improve Semantic Segmentation by Global Convolutional Network
[1706.05587] Rethinking Atrous Convolution for Semantic Image Segmentation
読むべき論文追記(精度は劣るが実行スピードが速い)
【Python】 KerasでU-Net構造ネットワークによるセグメンテーションをする
ここ(Daimler Pedestrian Segmentation Benchmark)から取得できるデータセットを使って、写真から人を抽出するセグメンテーション問題を解いてみます。U-Netはここ( U-Net: Convolutional Networks for Biomedical Image Segmentation )で初めて発表された構造と思いますが、セグメンテーション問題にMax Poolingを使うのは良くないといった話があったり、Batch Normalization等も使いたいということで、pix2pixのGeneratorとして利用されているU-Net構造のネットワークを利用します。
実行環境は以下になります。
- Windows10 64bit
- Python3.5.2
- Keras2.0.4
- BackendはTensorflow CPU版
まずはU-Netモデルを作るクラスです(unet.py)。
#!/usr/bin/env python # -*- coding: utf-8 -*- from keras.models import Model from keras.layers import Input from keras.layers.convolutional import Conv2D, ZeroPadding2D, Conv2DTranspose from keras.layers.merge import concatenate from keras.layers import LeakyReLU, BatchNormalization, Activation, Dropout class UNet(object): def __init__(self, input_channel_count, output_channel_count, first_layer_filter_count): self.INPUT_IMAGE_SIZE = 256 self.CONCATENATE_AXIS = -1 self.CONV_FILTER_SIZE = 4 self.CONV_STRIDE = 2 self.CONV_PADDING = (1, 1) self.DECONV_FILTER_SIZE = 2 self.DECONV_STRIDE = 2 # (256 x 256 x input_channel_count) inputs = Input((self.INPUT_IMAGE_SIZE, self.INPUT_IMAGE_SIZE, input_channel_count)) # エンコーダーの作成 # (128 x 128 x N) enc1 = ZeroPadding2D(self.CONV_PADDING)(inputs) enc1 = Conv2D(first_layer_filter_count, self.CONV_FILTER_SIZE, strides=self.CONV_STRIDE)(enc1) # (64 x 64 x 2N) filter_count = first_layer_filter_count*2 enc2 = self._add_encoding_layer(filter_count, enc1) # (32 x 32 x 4N) filter_count = first_layer_filter_count*4 enc3 = self._add_encoding_layer(filter_count, enc2) # (16 x 16 x 8N) filter_count = first_layer_filter_count*8 enc4 = self._add_encoding_layer(filter_count, enc3) # (8 x 8 x 8N) enc5 = self._add_encoding_layer(filter_count, enc4) # (4 x 4 x 8N) enc6 = self._add_encoding_layer(filter_count, enc5) # (2 x 2 x 8N) enc7 = self._add_encoding_layer(filter_count, enc6) # (1 x 1 x 8N) enc8 = self._add_encoding_layer(filter_count, enc7) # デコーダーの作成 # (2 x 2 x 8N) dec1 = self._add_decoding_layer(filter_count, True, enc8) dec1 = concatenate([dec1, enc7], axis=self.CONCATENATE_AXIS) # (4 x 4 x 8N) dec2 = self._add_decoding_layer(filter_count, True, dec1) dec2 = concatenate([dec2, enc6], axis=self.CONCATENATE_AXIS) # (8 x 8 x 8N) dec3 = self._add_decoding_layer(filter_count, True, dec2) dec3 = concatenate([dec3, enc5], axis=self.CONCATENATE_AXIS) # (16 x 16 x 8N) dec4 = self._add_decoding_layer(filter_count, False, dec3) dec4 = concatenate([dec4, enc4], axis=self.CONCATENATE_AXIS) # (32 x 32 x 4N) filter_count = first_layer_filter_count*4 dec5 = self._add_decoding_layer(filter_count, False, dec4) dec5 = concatenate([dec5, enc3], axis=self.CONCATENATE_AXIS) # (64 x 64 x 2N) filter_count = first_layer_filter_count*2 dec6 = self._add_decoding_layer(filter_count, False, dec5) dec6 = concatenate([dec6, enc2], axis=self.CONCATENATE_AXIS) # (128 x 128 x N) filter_count = first_layer_filter_count dec7 = self._add_decoding_layer(filter_count, False, dec6) dec7 = concatenate([dec7, enc1], axis=self.CONCATENATE_AXIS) # (256 x 256 x output_channel_count) dec8 = Activation(activation='relu')(dec7) dec8 = Conv2DTranspose(output_channel_count, self.DECONV_FILTER_SIZE, strides=self.DECONV_STRIDE)(dec8) dec8 = Activation(activation='sigmoid')(dec8) self.UNET = Model(input=inputs, output=dec8) def _add_encoding_layer(self, filter_count, sequence): new_sequence = LeakyReLU(0.2)(sequence) new_sequence = ZeroPadding2D(self.CONV_PADDING)(new_sequence) new_sequence = Conv2D(filter_count, self.CONV_FILTER_SIZE, strides=self.CONV_STRIDE)(new_sequence) new_sequence = BatchNormalization()(new_sequence) return new_sequence def _add_decoding_layer(self, filter_count, add_drop_layer, sequence): new_sequence = Activation(activation='relu')(sequence) new_sequence = Conv2DTranspose(filter_count, self.DECONV_FILTER_SIZE, strides=self.DECONV_STRIDE, kernel_initializer='he_uniform')(new_sequence) new_sequence = BatchNormalization()(new_sequence) if add_drop_layer: new_sequence = Dropout(0.5)(new_sequence) return new_sequence def get_model(self): return self.UNET
次に前処理関連の関数です(main.py)。
IMAGE_SIZE = 256 # 値を-1から1に正規化する関数 def normalize_x(image): image = image/127.5 - 1 return image # 値を0から1に正規化する関数 def normalize_y(image): image = image/255 return image # 値を0から255に戻す関数 def denormalize_y(image): image = image*255 return image # インプット画像を読み込む関数 def load_X(folder_path): import os, cv2 image_files = os.listdir(folder_path) image_files.sort() images = np.zeros((len(image_files), IMAGE_SIZE, IMAGE_SIZE, 3), np.float32) for i, image_file in enumerate(image_files): image = cv2.imread(folder_path + os.sep + image_file) image = cv2.resize(image, (IMAGE_SIZE, IMAGE_SIZE)) images[i] = normalize_x(image) return images, image_files # ラベル画像を読み込む関数 def load_Y(folder_path): import os, cv2 image_files = os.listdir(folder_path) image_files.sort() images = np.zeros((len(image_files), IMAGE_SIZE, IMAGE_SIZE, 1), np.float32) for i, image_file in enumerate(image_files): image = cv2.imread(folder_path + os.sep + image_file, cv2.IMREAD_GRAYSCALE) image = cv2.resize(image, (IMAGE_SIZE, IMAGE_SIZE)) image = image[:, :, np.newaxis] images[i] = normalize_y(image) return images
最後にこれらを利用するメイン関数です(main.py)。これを動作させるためには以下のファイル/フォルダ構造を想定しています。 - main.py - unet.py - trainingData(トレーニング用データを格納するフォルダ) - testData(テスト用データを格納するフォルダ) 更にtrainingDataフォルダ、testDataフォルダには最初に紹介したデータセットにあるleft_imagesフォルダとleft_groundTruthフォルダがそれぞれ置かれているものとします。
- 2017/08/10 関数predictのモデル生成部の不具合を修正
- 2017/11/08 import osを書き忘れている不具合を修正
import os import numpy as np from keras.optimizers import Adam import keras.backend as K from keras.callbacks import ModelCheckpoint, EarlyStopping from unet import UNet # ダイス係数を計算する関数 def dice_coef(y_true, y_pred): y_true = K.flatten(y_true) y_pred = K.flatten(y_pred) intersection = K.sum(y_true * y_pred) return 2.0 * intersection / (K.sum(y_true) + K.sum(y_pred) + 1) # ロス関数 def dice_coef_loss(y_true, y_pred): return 1.0 - dice_coef(y_true, y_pred) # U-Netのトレーニングを実行する関数 def train_unet(): # trainingDataフォルダ配下にleft_imagesフォルダを置いている X_train, file_names = load_X('trainingData' + os.sep + 'left_images') # trainingDataフォルダ配下にleft_groundTruthフォルダを置いている Y_train = load_Y('trainingData' + os.sep + 'left_groundTruth') # 入力はBGR3チャンネル input_channel_count = 3 # 出力はグレースケール1チャンネル output_channel_count = 1 # 一番初めのConvolutionフィルタ枚数は64 first_layer_filter_count = 64 # U-Netの生成 network = UNet(input_channel_count, output_channel_count, first_layer_filter_count) model = network.get_model() model.compile(loss=dice_coef_loss, optimizer=Adam(), metrics=[dice_coef]) BATCH_SIZE = 12 # 20エポック回せば十分 NUM_EPOCH = 20 history = model.fit(X_train, Y_train, batch_size=BATCH_SIZE, epochs=NUM_EPOCH, verbose=1) model.save_weights('unet_weights.hdf5') # 学習後のU-Netによる予測を行う関数 def predict(): import cv2 # testDataフォルダ配下にleft_imagesフォルダを置いている X_test, file_names = load_X('testData' + os.sep + 'left_images') input_channel_count = 3 output_channel_count = 1 first_layer_filter_count = 64 network = UNet(input_channel_count, output_channel_count, first_layer_filter_count) model = network.get_model() model.load_weights('unet_weights.hdf5') BATCH_SIZE = 12 Y_pred = model.predict(X_test, BATCH_SIZE) for i, y in enumerate(Y_pred): # testDataフォルダ配下にleft_imagesフォルダを置いている img = cv2.imread('testData' + os.sep + 'left_images' + os.sep + file_names[i]) y = cv2.resize(y, (img.shape[1], img.shape[0])) cv2.imwrite('prediction' + str(i) + '.png', denormalize_y(y)) if __name__ == '__main__': train_unet() predict()
上手く抽出出来ている例(元画像/正解画像/予測画像)
上手くいかなかった例(元画像/正解画像/予測画像)
上手くいっていない例を見ると、学習データに類似パターンが無さそうなケースでした。学習していないことを予測できないのは仕方無いので、学習用データに十分なバリエーションを持たせて学習させることが重要と思いました。
【WPF】 簡易動画プレーヤーを作成する
今回は動画のプレーヤーを作ってみます(簡易的なものですが)。調べてみるとWPFにはMediaElementというものがあって、これを使ってみると良さそうです。 まずはxamlが以下のようになります。
<Window x:Class="CSMediaPlayer.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:CSMediaPlayer" mc:Ignorable="d" Title="MainWindow" Height="350" Width="525"> <Grid> <Grid.RowDefinitions> <RowDefinition Height="6*"/> <RowDefinition Height="1*"/> </Grid.RowDefinitions> <MediaElement Name="MediaElementMovie" Grid.Row="0" Stretch="Fill" LoadedBehavior="Manual" UnloadedBehavior="Manual"/> <Grid Grid.Row="1"> <Grid.ColumnDefinitions> <ColumnDefinition Width="1*"/> <ColumnDefinition Width="1*"/> <ColumnDefinition Width="1*"/> <ColumnDefinition Width="4*"/> </Grid.ColumnDefinitions> <Button Name="ButtonPlay" Grid.Column="0" Content="▶" Click="ButtonPlay_Click"/> <Button Name="ButtonPause" Grid.Column="1" Content="||" Click="ButtonPause_Click"/> <Button Name="ButtonStop" Grid.Column="2" Content="■" Click="ButtonStop_Click"/> <Grid Grid.Column="3"> <Slider Name="SliderMoviePosition" VerticalAlignment="Center" Minimum="0" Maximum="100" ValueChanged="SliderMoviePosition_ValueChanged"/> </Grid> </Grid> </Grid> </Window>
上記のXAMLでこんな感じの見た目になります。あとはボタンやスライダーにイベントを割り振っていきます。
using System; using System.Windows; using System.Windows.Threading; namespace CSMediaPlayer { /// <summary> /// MainWindow.xaml の相互作用ロジック /// </summary> public partial class MainWindow : Window { // 動画の相対パス private readonly string _videoPath = "video.avi"; // スライダーを動かすためのタイマー private DispatcherTimer _timer = new DispatcherTimer(); // タイマー起動間隔(1秒) private const int _timerInterval = 1; // 動画経過時間 private int _elapsedSec = 0; // スライダーをユーザーが動かしたかどうか判別するフラグ private bool _sliderValueChangedByProgram = false; public MainWindow() { InitializeComponent(); _timer.Interval = new TimeSpan(0, 0, _timerInterval); _timer.Tick += dispatcherTimer_Tick; } private void dispatcherTimer_Tick(object sender, EventArgs e) { // 動画経過時間に合わせてスライダーを動かす _elapsedSec += _timerInterval; double totalSec = MediaElementMovie.NaturalDuration.TimeSpan.TotalSeconds; SliderMoviePosition.Value = _elapsedSec / totalSec * SliderMoviePosition.Maximum; _sliderValueChangedByProgram = true; } private void ButtonPlay_Click(object sender, RoutedEventArgs e) { // 動画を再生 _timer.Start(); MediaElementMovie.Source = new Uri(_videoPath, UriKind.Relative); MediaElementMovie.Play(); } private void ButtonPause_Click(object sender, RoutedEventArgs e) { // 動画を一次停止 _timer.Stop(); MediaElementMovie.Pause(); } private void ButtonStop_Click(object sender, RoutedEventArgs e) { // 動画を停止 _timer.Stop(); _elapsedSec = 0; SliderMoviePosition.Value = 0; MediaElementMovie.Stop(); MediaElementMovie.Source = null; } private void SliderMoviePosition_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e) { if (!_sliderValueChangedByProgram) { // スライダーを動かした位置に合わせて動画の再生箇所を更新する double totalSec = MediaElementMovie.NaturalDuration.TimeSpan.TotalSeconds; double sliderValue = SliderMoviePosition.Value; int targetSec = (int)(sliderValue * totalSec / SliderMoviePosition.Maximum); _elapsedSec = targetSec; TimeSpan ts = new TimeSpan(0, 0, 0, targetSec); MediaElementMovie.Position = ts; } _sliderValueChangedByProgram = false; } } }
参考にしたサイトはここです(方法 : MediaElement (再生、一時停止、停止、ボリューム、および速度) を制御する )。サイトでは様々なイベントを定義しているので、きちんと作るのであれば上記コードは改善しないといけなさそうです。。。
【WPF】 スクリーンショットを撮り続けて動画に保存する
タイトルにあるようにスクリーンショットを撮り続けて動画に保存する方法のメモをします。 まずはスクリーンショットを撮る部分ですが、スクリーンショットをそのまま動画にすると容量がすごいことになるので、スクリーンショットを撮る⇒縮小するまでを作成します。
// Bitmapクラスを使うため参照の追加が必要 using System.Drawing; private Bitmap GetScreenShot(int width, int height) { var resizedBmp = new Bitmap(width, height); using (var bmp = new Bitmap((int)SystemParameters.PrimaryScreenWidth, (int)SystemParameters.PrimaryScreenHeight)) using (Graphics g = Graphics.FromImage(bmp)) using (Graphics resizedG = Graphics.FromImage(resizedBmp)) { // スクリーンショットを撮る g.CopyFromScreen(new System.Drawing.Point(0, 0), new System.Drawing.Point(0, 0), bmp.Size); // 動画サイズを減らすためリサイズする resizedG.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.Bilinear; resizedG.DrawImage(bmp, 0, 0, width, height); } return resizedBmp; }
一定時間ごとに上記メソッドを呼び出す必要があるので、DispatcherTimerクラスを利用します。動画保存はAccord.Video.VFWのAVIWriterクラスを利用します。Accord.Video.VFWはNuGETを使ってインストールします。
using System; using System.Collections.Generic; using System.Threading.Tasks; using System.Windows; using Accord.Video.VFW; using System.Drawing; using System.Windows.Threading; namespace CSMovie { /// <summary> /// MainWindow.xaml の相互作用ロジック /// </summary> public partial class MainWindow : Window { private readonly DispatcherTimer _timer = new DispatcherTimer(); private readonly AVIWriter _aviWriter = new AVIWriter(); // フレームレート private readonly int _frameRate = 15; // 動画の横サイズ private readonly int _videoWidth = 640; // 動画の縦サイズ private readonly int _videoHeight = 480; private readonly List<Task> _tasks = new List<Task>(); public MainWindow() { InitializeComponent(); InitializeDispatcherTimer(); InitializeVideo(); } private void InitializeDispatcherTimer() { // フレームレートに合わせて起動時間を設定する int interval_msec = 1000 / _frameRate; _timer.Interval = new TimeSpan(0, 0, 0, 0, interval_msec); _timer.Tick += dispatcherTimer_Tick; } private void dispatcherTimer_Tick(object sender, EventArgs e) { // 時間がかかる処理なのでバックグラウンドで実行する _tasks.Add( Task.Run(() => { // スクリーンショットを撮る using (Bitmap bmp = GetScreenShot(_videoWidth, _videoHeight)) { // フレームに追加する _aviWriter.AddFrame(bmp); } })); } private void InitializeVideo() { _aviWriter.FrameRate = _frameRate; _aviWriter.Open("video.avi", _videoWidth, _videoHeight); } private void Window_ContentRendered(object sender, EventArgs e) { _timer.Start(); } private Bitmap GetScreenShot(int width, int height) { var resizedBmp = new Bitmap(width, height); using (var bmp = new Bitmap((int)SystemParameters.PrimaryScreenWidth, (int)SystemParameters.PrimaryScreenHeight)) using (Graphics g = Graphics.FromImage(bmp)) using (Graphics resizedG = Graphics.FromImage(resizedBmp)) { // スクリーンショットを撮る g.CopyFromScreen(new System.Drawing.Point(0, 0), new System.Drawing.Point(0, 0), bmp.Size); // 動画サイズを減らすためリサイズする resizedG.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.Bilinear; resizedG.DrawImage(bmp, 0, 0, width, height); } return resizedBmp; } private void Window_Closed(object sender, EventArgs e) { // タイマーを止める _timer.Stop(); // 動作中のタスクがあれば待つ foreach (var task in _tasks) { task.Wait(); } if (_aviWriter != null) { _aviWriter.Close(); _aviWriter.Dispose(); } } } }
ちなみにスクリーンショットを撮る部分はここを参考にしました。
【Python】 PythonでC#のFileSystemWatcher相当のクラスを作成する
C#側がファイルを作成し、それをトリガとしてpythonが起動するようにしたいと思っています。そのため、C#のFileSystemWatcherに相当するようなクラスが欲しいと思ったのですが、以下のサイトに書かれていました。 qiita.com
import time from watchdog.observers import Observer from watchdog.events import PatternMatchingEventHandler class MyFileEventHandler(PatternMatchingEventHandler): def __init__(self, patterns): super(MyHandler, self).__init__(patterns=patterns) def on_moved(self, event): # ファイルが移動した時に実行されるメソッド print('file moved') def on_created(self, event): # ファイルが作成された時に実行されるメソッド print('file created') def on_deleted(self, event): # ファイルが削除された時に実行されるメソッド print('file deleted') def on_modified(self, event): # ファイルが更新された時に実行されるメソッド print('file modified') # MyFileEventHandlerの使い方例 if __name__ == '__main__': # イベントを発生させるファイルパターン file_pattern_to_watch = '*.txt' # MyFileEventHandlerを生成して event_handler = MyFileEventHandler(file_pattern_to_watch) # Observerに登録する file_system_watcher = Observer() file_system_watcher.schedule(event_handler, directory_to_watch, recursive=False) # 監視を開始 file_system_watcher.start() # 指定時間待つ time_out_sec = 30 file_system_watcher.join(time_out_sec) # タイムアウトが起きたかどうか調べる if file_system_watcher.isAlive(): # メモ:実際にタイムアウトが起きたらkillするしかない? print('timeout happened')
【WPF】 モニターの解像度を取得する
で取得できます。
以下のサイトがよくまとまった情報源になります。