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

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

【C#】DispatcherTimerを即起動させる

10秒毎に○○したいという時DispatcherTimerを使うわけですが、Startメソッドを呼んで 10秒経ってからイベントが開始されます。そうではなくて、Startメソッドを呼んだ時に イベント開始となってほしかったのですが、以下のやり方でOKです。

// Intervalを0に指定で即起動する
timer.Interval = new TimeSpan(0, 0, 0);
timer.Start();

void timer_Tick(object sender, EventArgs e)
{
    // ここで本来の間隔に指定しなおす
    ((Timer)sender).Interval = new TimeSpan(0, 0, 10);
}

stackoverflow.com

【Python】pillow-simdによる画像読み込みの高速化

ディープラーニングのモデルをネットから拾ってくると、画像読み込みにopencvが使われているケースやpillowが使われているケースがあります。自分は使い慣れている/速いという理由でopencvを使ってもらえると助かるのですが、pillowが使われているケースもあり、pillowはopencvと比較して画像読み込み時間が長く、学習に時間がかかって困る時があります。

pillow部分をopencvに書き直す手もありますが、それが面倒という場合もあり、pillow-simdというライブラリを使えばプログラムを書き換えることなく画像読み込みを高速化できると聞いたので試してみます。しかし、pillow-simdはWindowsをサポートしていないのか、インストールでエラーが出ましたが、issueに回避策を見つけた人がいるのでそれに倣いひとまずインストールして試します。

pip uninstall pillow
pip install --upgrade pillow-simd --global-option="build_ext" --global-option="--disable-jpeg" --global-option="--disable-zlib"

4000×3000の画像を読み込んでみたところ、むしろopencvのimreadよりpillow-simdの方が速そうです。

import cv2
from PIL import Image
import time

start = time.time()
image = Image.open('pic.bmp') # Case pillow-simd: time = 78msec
#image = cv2.imread('pic.bmp') # Case opencv: time = 93msec
end = time.time()
print(str(end - start))

【Python】画像データ拡張ライブラリAlbumentationsを使ってみる

PyTorch版のYOLO v3を作っている人がいたので試してみようと思っています。 github.com

ただ、Trainにデータ拡張が入っていないのでデータ拡張ロジックを追加したいと思ったところ、 Albumentationsというライブラリを見つけました。 github.com

物体検出やセグメンテーションにも利用可能そうなので早速試してみました。 使い方は以下を実施すれば良さげです。

  1. Composeを作って、Composeの中に実施したいデータ拡張を記述
  2. Composeに画像、ラベル、クラスIDを含むディクショナリを投入

以下はComposeを作るコードになります。

from albumentations import Compose
from albumentations.augmentations.transforms import Resize, HorizontalFlip, RandomSizedCrop, HueSaturationValue

def get_compose(crop_min_max, image_height, image_width, hue_shift, saturation_shift, value_shift):
    # Resize image to (image_height, image_width) with 100% probability
    # Flip LR with 50% probability
    # Crop image and resize image to (image_height, image_width) with 100% probability
    # Change HSV from -hue_shift to +hue_shift and so on with 100% probability
    # Format 'pascal_voc' means label is given like [x_min, y_min, x_max, y_max]
    return Compose([Resize(image_height, image_width, p=1.0),
                     HorizontalFlip(p=0.5),
                     RandomSizedCrop(crop_min_max, image_height, image_width, p=1.0),
                     HueSaturationValue(hue_shift, saturation_shift, value_shift, p=1.0)],
                    bbox_params={'format':'pascal_voc', 'label_fields':['category_id']})

このComposeは以下のように使います。

# Image size for YOLO
image_size = 416
# Crop 80 - 100% of image
crop_min = image_size*80//100
crop_max = image_size
crop_min_max = (crop_min, crop_max)
# HSV shift limits
hue_shift = 10
saturation_shift = 10
value_shift = 10
# Get compose
compose = get_compose(crop_min_max, image_size, image_size, hue_shift, saturation_shift, value_shift)
# image: numpy array like return value of cv2.imread
# labels: bounding box lists like [[366.7, 80.84, 132.8, 181.84], [5.66, 138.95, 147.09, 164.88]]
# classes: class of each bounding box like [0, 1]
annotation = {'image': image, 'bboxes': labels, 'category_id': classes}
# Do augmentation
augmented = compose(**annotation)
augmented_image = augmented['image']
augmented_labels = augmented['bboxes']

f:id:ni4muraano:20181120235803j:plain:w300 f:id:ni4muraano:20181121000404j:plain:w200

他にも様々な拡張が用意されているっぽい。API Referenceはここ

【C#】ToList()の挙動についての勘違い

ToList()が思ってた動作と違ったのでメモしておきます。

↓のようなクラスを作ります。

class MyClass
{
    public MyClass()
    {
        var random = new Random();
        Value = random.Next(100);
    }

    public int Value { get; set; }
}

上記クラスを使って以下のようなコードを書いてしまっていました。 ToList()は新しいリストを作成するので、中身もコピーされると勝手に勘違いしてました。

var myClasses = new List<MyClass>();
for (int i = 0; i < 1000; ++i)
{
    myClasses.Add(new MyClass());
}
// リストからある条件を満たすものを取り出し、コピーしたつもりになっていた
var selectedMyClasses = myClasses.Where(i => i.Value >= 50).ToList();
// 値を書き換えると
selectedMyClasses[0].Value = 10000;
// 元の中身が書き換わってるので、countは1と表示される
int count = myClasses.Count(i => i.Value == 10000);
Console.WriteLine(count);

中身もコピーしたいのであれば、MyClassにコピー用メソッドを加えて

class MyClass : ICloneable
{
    public MyClass()
    {
        var random = new Random();
        Value = random.Next(100);
    }

    public int Value { get; set; }

    public object Clone()
    {
        return MemberwiseClone();
    }
}

↓こんな感じに書くのでしょうか。。。

var myClasses = new List<MyClass>();
for (int i = 0; i < 1000; ++i)
{
    myClasses.Add(new MyClass());
}
var selectedMyClasses = myClasses.Where(i => i.Value >= 50).Select(i => (MyClass)i.Clone()).ToList();
selectedMyClasses[0].Value = 10000;
int count = myClasses.Count(i => i.Value == 10000);
Console.WriteLine(count);

当たり前という突っ込みを受けそうですが、他に同じ質問している人いたので若干ほっとしました。

stackoverflow.com

【C#】System.IO.Compression.ZipFileクラスのCreateFromDirectoryメソッドはデフォルト設定だと日本語フォルダ名に非対応

アプリケーションでエラーが起きたら必要なログを全てフォルダに集めて、そのフォルダをzip化するということを やろうとしたら、フォルダ名に日本語が入っていると文字化けすることが分かりました。 解決策は以下に書かれていて、encodingにEncoding.GetEncoding("sjis")を指定すれば大丈夫でした。 System.IO.Compression.ZipFileクラスにて、日本語のディレクトリ名やファイル名を文字化けさせずに圧縮処理 · GitHub

【Visual Studio】NuGetを実行したら「'xxxxx' にはすでに 'NETStandard.Library' に対して定義された依存関係があります。」というエラーが出るときの対処法

下記記事にあるようにNuGetをアップデートするのが正解。 というか何故こんな意味不明なエラーメッセージ。。。 qiita.com

【OpenCV】OpenCV3以降でDense SIFTを使いたい

画像から特徴量抽出する方法として、Dense SIFTを使いたいと思ったのですが、何故かOpenCV2のあるバージョンで削除されてしまったようです。ただ幸いkeypointを指定することでDense SIFTを実施できるようなのでメモします(とはいえSIFTの特許の問題があるので2019年3月までは様子見、もしくは別の抽出器を利用でしょうか)。

# dense_feature_detector.py

import cv2

class DenseFeatureDetector(object):
    def __init__(self, detector, step, scale, start):
        self._detector = detector
        self._step = step
        self._scale = scale
        self._start = start
        pass

    def detect(self, image):
        # Convert image to gray if it is color
        if len(image.shape) == 3:
            gray_image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
        else:
            gray_image = image

        # Create dense keypoints
        keypoints = self._create_keypoints(gray_image)
        _, features = self._detector.compute(image, keypoints)
        return keypoints, features

    def _create_keypoints(self, gray_image):
        keypoints = []
        rows, cols = gray_image.shape
        for y in range(self._start, rows, self._step):
            for x in range(self._start, cols, self._step):
                keypoints.append(cv2.KeyPoint(float(x), float(y), self._scale))
        return keypoints


# main.py

import cv2
from dense_feature_detector import DenseFeatureDetector

if __name__ == '__main__':
    image = cv2.imread('lenna.jpg')
    sift = cv2.xfeatures2d.SIFT_create()
    detector = DenseFeatureDetector(sift, step=15, scale=15, start=15)
    keypoints, features = detector.detect(image)
    dense_keypoints_on_image = cv2.drawKeypoints(image, keypoints, None)
    cv2.imwrite('dense.png', dense_keypoints_on_image)


f:id:ni4muraano:20180914232002p:plain