旅行好きなソフトエンジニアの備忘録

プログラミングや技術関連のメモを始めました

【Python】 OSに依存しないフォルダ・ファイルパスの書き方

ディープラーニング関連の論文あるネットワークを試してみたい時、誰かが既に作成してくれていることが多くて助かります。ただ、ソースを自分の環境に落として動かそうとするとエラーになることも多く、原因の一部はOSに依存した書き方があったため、ということもあります。最近あったのはLinuxWindowsでのパスの書き方の違いによるものだったので、対処法をメモします。

元のソースの例

weight_file_path = 'folder_weight\\weights.hdf5'
model.load_weight(weight_file_path)


変更例1

import os

weight_file_path = os.path.join('folder_weight', 'weights.hdf5')
model.load_weight(weight_file_path)


変更例2

import os

weight_file_path = 'folder_weight' + os.sep + 'weights.hdf5'
model.load_weight(weight_file_path)

【OpenCV】 輪郭を強調する

先鋭化フィルタを適用して輪郭を強調する例をメモします。OpenCVには用意されていない関数ですが、フィルタを自作して適用することで簡単に作成することが出来ます。作成するフィルタは以下の通りです(http://www.wakayama-u.ac.jp/~chen/education/image/2012/L8.pdf)。

4近傍

$$ \left( \begin{array}{ccc} 0 & -1 & 0 \\ -1 & 5 & -1 \\ 0 & -1 & 0 \\ \end{array} \right) $$

8近傍

$$ \left( \begin{array}{ccc} -1 & -1 & -1 \\ -1 & 9 & -1 \\ -1 & -1 & -1 \\ \end{array} \right) $$

// 画像を読み込む
Mat source;
imread("picture.jpg").copyTo(source);
if (source.empty())
{
    throw runtime_error("Failed to open image");
}

// 先鋭化フィルタを作成する
const float k = -1.0;
Mat sharpningKernel4 = (Mat_<float>(3, 3) << 0.0, k, 0.0, k, 5.0, k, 0.0, k, 0.0);
Mat sharpningKernel8 = (Mat_<float>(3, 3) << k, k, k, k, 9.0, k, k, k, k);

// 先鋭化フィルタを適用する
Mat destination;
cv::filter2D(source, destination, source.depth(), sharpningKernel8);

imshow("source", source);
imshow("destination", destination);

f:id:ni4muraano:20170415170026j:plain:w200 f:id:ni4muraano:20170415170032j:plain:w200

【WPF】 二次元配列をWriteableBitmapに変換する

二次元配列をWriteableBitmapに変換する方法をメモします。グレースケール画像をbyteの二次元配列で表現しており、これをImageクラスに表示するためWriteableBitmapに変換したかった、というのが動機になります。

using System.Windows.Media;
using System.Windows.Media.Imaging;

private WriteableBitmap ToWriteableBitmap(byte[,] image)
{
    int width = image.GetLength(1);
    int height = image.GetLength(0);
    var bitmap = new WriteableBitmap(width, height, 96.0, 96.0,
                                     PixelFormats.Gray8,
                                     BitmapPalettes.Gray256);
    int stride = width*1;
    byte[] pixels = image.Cast<byte>().ToArray();
    bitmap.WritePixels(new Int32Rect(0, 0, width, height), pixels, stride, 0);
    
    return bitmap;
}

上記のToWriteableBitmapの返り値をImageのSourceプロパティに代入することで画像を表示することが出来ます。

【C#】 二次元配列を一次元配列に変換する

タイトルの通り二次元配列を一次元配列に変換する方法です。

using System.Linq;

var array2dim = new byte[,] { { 0, 1, 2 },
                              { 3, 4, 5 },
                              { 6, 7, 8 } };
// array1dimは{0, 1, 2, 3, 4, 5, 6, 7, 8}となる
byte[] array1dim = array2dim.Cast<byte>().ToArray();

【OpenCV】 穴を塗りつぶす

learnopencv.comのブログ(Filling holes in an image using OpenCV ( Python / C++ ) | Learn OpenCV)のメモになります。タイトルにあるように、OpenCVを使って穴を塗りつぶす方法についてブログに書かれており、MATLABにはimfillという関数があるけど、OpenCVには無いからどうする?という問いかけがされています。

f:id:ni4muraano:20170329234450j:plain:w150 ⇒ f:id:ni4muraano:20170329234507j:plain:w150

// Step1:画像の読み込み
Mat image = imread("nickel.jpg", IMREAD_GRAYSCALE);

// Step2:二値化処理を行う(二値化処理が上手くいかなかったため、ソース元と少し閾値を変更した)
Mat binary_image;
threshold(image, binary_image, 200, 255, THRESH_BINARY_INV);

// Step3:コインの外側の色を反転させる
Mat floodfilled_image = binary_image.clone();
floodFill(floodfilled_image, cv::Point(0, 0), Scalar(255));

// Step4:全体を反転させる
Mat inverted_floodfilled_image;
bitwise_not(floodfilled_image, inverted_floodfilled_image);

// Step5:コインの中を塗りつぶす
Mat foreground = (binary_image | inverted_floodfilled_image);

floodFillというメソッドがOpenCVにはあることも勉強になったので、覚えておきたいと思います。最後にソースコード中のステップ1からステップ5までの画像を並べます。

f:id:ni4muraano:20170329234450j:plain:w100 ⇒ f:id:ni4muraano:20170329235330j:plain:w100 ⇒ f:id:ni4muraano:20170329235340j:plain:w100 ⇒ f:id:ni4muraano:20170329235351j:plain:w100 ⇒ f:id:ni4muraano:20170329234507j:plain:w100

【Python】 KerasでResNet等のショートカット構造を実装する

Kerasでは学習済みのResNetが利用できるため、ResNetを自分で作ることは無いと思います。ただ、ResNet以外にも下の写真のようなショートカット構造を持つネットワークがあり、これらを実装したい時にどのように作成するかをメモします。
f:id:ni4muraano:20170325221400p:plain

単純なネットワークの場合、KerasではSequentialを生成して、レイヤーをaddしていくのが通常ですが(Sequentialモデルのガイド - Keras Documentation)、少し複雑なネットワークを作成する場合はFunctional APIを利用します(Functional APIのガイド - Keras Documentation)。
Functional APIはSequentialを利用するプログラミングと比較した場合は多少直観性に劣りますが、別段難しいものではありません。例えば下図のようにInput⇒Convolutionというシーケンスは以下のように書くことができます。

from keras.layers import Input, Convolution2D

# 入力の形状を指定する(CIFAR-10のような32×32×3の画像を想定)
in_ = Input((32, 32, 3))
# Input⇒Convolution2D
in_conv = Convolution2D(10, 3, 3, border_mode='same')(in_)

f:id:ni4muraano:20170325223257p:plain

Input⇒Convolutionに更にConvolutionを接続するのも一行追加するだけです。

from keras.layers import Input, Convolution2D

# 入力の形状を指定する(CIFAR-10のような32×32×3の画像を想定)
in_ = Input((32, 32, 3))
# Input⇒Convolution2D
in_conv = Convolution2D(10, 3, 3, border_mode='same')(in_)
# Input⇒Convolution2D⇒Convolution2D
in_conv_conv = Convolution2D(10, 3, 3, border_mode='same')(in_conv)

f:id:ni4muraano:20170325224113p:plain

Input⇒Convolution2Dをショートカットさせます。これはInput⇒Convolution2DとInput⇒Convolution2D⇒Convolution2Dをマージさせることで実現できます。keras.layersにはMergeとmergeがあるのですが、mergeを利用します。間違えてMergeをimportしないよう注意して下さい。

from keras.layers import Input, Convolution2D, merge

# 入力の形状を知らせる(ここではCIFAR-10のような32×32×3としている)
in_ = Input((32, 32, 3))
# Input⇒Convolution2D
in_conv = Convolution2D(10, 3, 3, border_mode='same')(in_)
# Input⇒Convolution2D⇒Convolution2D
in_conv_conv = Convolution2D(10, 3, 3, border_mode='same')(in_conv)
# Input⇒Convolution2DとInput⇒Convolution2D⇒Convolution2Dをマージする
merged = merge([in_conv_conv, in_conv], mode='sum')

f:id:ni4muraano:20170325224731p:plain

以上でショートカット構造を実現できました。最後に、最初に示した図の構造を実現するためのコードを下記にまとめます。

from keras.layers import Input, Convolution2D, merge
from keras.models import Model
from keras.utils.visualize_util import plot

# 入力の形状を知らせる(ここではCIFAR-10のような32×32×3としている)
in_ = Input((32, 32, 3))
# Input⇒Convolution2D
in_conv = Convolution2D(10, 3, 3, border_mode='same')(in_)
# Input⇒Convolution2D⇒Convolution2D
in_conv_conv = Convolution2D(10, 3, 3, border_mode='same')(in_conv)
# Input⇒Convolution2DとInput⇒Convolution2D⇒Convolution2Dをマージする
merged = merge([in_conv_conv, in_conv], mode='sum')
# 更にConvolution2Dを接続する
merged_conv = Convolution2D(10, 3, 3, border_mode='same')(merged)
# input, outputを指定してモデルを作成する
model = Model(input=in_, output=merged_conv)
# モデルを図示する
plot(model, 'shortcut_structure_example.png')

【WPF】 画像の差分を計算してWPFのImageに表示したい

タイトルの通り画像の差分を計算してWPFのImageに表示する必要があったためメモします。画像の差分を取るためにはOpenCvC#用ラッパーであるOpenCvSharpを利用すればすぐに済むため、結局OpenCvSharpのMatクラスをWPFのImage.Sourceにどう入れるか、という話になります。

手順
  1. NuGetを利用してOpenCvSharpを導入する
  2. “参照の追加"からSystem.Drawingを追加する
  3. usingに"using OpenCvSharp;“ "using OpenCvSharp.Extensions;"を追加する
  4. 下記コードを記述する
// 画像1を読み込む
using (Mat image1 = Cv2.ImRead("lenna1.jpg"))
// 画像2を読み込む
using (Mat image2 = Cv2.ImRead("lenna2.jpg"))
// 差分画像を保存する領域を確保する
using (Mat diff = new Mat(new OpenCvSharp.Size(image1.Cols, image1.Rows), MatType.CV_8UC3))
{
    // 画像1と画像2の差分をとる
    Cv2.Absdiff(image1, image2, diff);
    // BitmapSourceConverterを利用するとMatをBitmapSourceに変換できる
    BitmapSource bitmap = BitmapSourceConverter.ToBitmapSource(diff);
    // Sourceに画像を割り当てる
    ImageToShowDiffImage.Source = bitmap;
}