なになれ

IT系のことを記録していきます

コードが公開されているAngularの使用例をまとめてみた

モチベーション

Vue.jsのドキュメントにある使用例(https://jp.vuejs.org/v2/examples/)の内容が魅力的だったので、Angularにもそういうものがないかを探してみました。
実際に動くものを見ると、どんなことができるのかイメージできるので良いと思っています。

使用例の紹介

Tour of Heroes

stackblitz.com

Angular公式ドキュメントのチュートリアルです。まずはこれでAngularの基本を学びつつ、どんな風に作って、どんなことが出来るかを理解できると思います。

Angular+Firebaseでチャットアプリを作る

qiita.com

AngularからFirebaseを使うための内容が細かく解説されています。Firebaseを使うことやNgRxの説明など一通りAngularの基本的なことを学んだ人の次のステップとして学んでおくと良いことが色々載っていると思います。
チュートリアル形式で学べるようになっています。2018年12月15日の時点でまだ未完のようです。

Angular RealWorld Example App

github.com

Mediumのクローンアプリです。モジュールの分け方などAngular公式のスタイルガイドに則って作られているちゃんとしたアプリなので学べるものが多いです。
Tour of Heroesを一通り実施した人が実用的なアプリを作りたい時に参考になるアプリだと思います。認証処理が参考になります。

Angular NgRx Material Starter

github.com

Angular+NgRx+Angular Materialを使って作成されたスタータープロジェクトです。
NgRxが全面的に使われていてNgRxを学びたい人にとっては参考になる内容だと思います。
以下の内容が盛り込まれています。

  • カスタムテーマのサポート (4つのテーマがある)
  • フィーチャーモジュールの遅延ロード
  • ローカルストレージでの画面状態の永続化
  • @ngrx/effects によるAPIリクエストの発行
  • レスポンシブデザイン対応
  • angular-material and custom components in SharedModule
  • Angular Materialと SharedModule内にカスタムコンポーネントを配置
  • CypressによるE2Eテスト
  • プロダクション向けDockerfileによるアプリ公開

E2Eテストが用意されているのは珍しいと思います。

Hackernews clone using Angular 5

github.com

シンプルかつ実用的なアプリになっているのが良いです。Angular5なので若干古いです。

TuneIn

github.com

おしゃれなアプリです。こちらもシンプルかつ実用的なアプリです。Angular6なので比較的新しいです。

Angular公式サイト

github.com

Angularの公式サイトですが、実はAngularで作られています。コードも見ることができます。
流石に作り込まれている感じでパッと見ではよく分かりません。
dgeniというドキュメントを生成するツールが使われているようです。

Angular Console

github.com

Angular CLIの機能+αをGUIで使えるようにしたプロダクトです。
Nxという大規模組織向けのAngularプロジェクト構成ツールが使われているため、通常のAngularプロジェクト構成とは異なるので理解しにくいところがあります。
NxはAngular Consoleを作った会社が作っているライブラリのようです。
ElectronやGraphqlを利用しているのが注目ポイントだと思います。

まとめ

とりあえずアプリケーションっぽいものを集めました。コードが公開されているものを前提にしたのでだいぶ限定されました。
Angularが使用されているところを調べてみると色々なところで使われているんだなぁということが分かりました。

https://www.madewithangular.com/

whouse.angular.jp

サンプルやUI部品での使用例を集めるのも良さそうです。

【初心者向け】Pythonによる画像処理について(人物認識)

2018年にやったこととしてPythonによる画像処理に取り組みました。
顔検出、顔認識、人物検出、人物追跡といった「人」が画像処理の対象でした。
そこで得られた知識をまとめたいと思います。

筆者は主にWeb領域のソフトウェアエンジニアで画像処理を専門に取り組んできたわけではないです。
本投稿は、そこそこプログラムが書ける立場で画像処理に取り組んだ経験を元にした内容になっています。
Pythonに関してもほぼ経験がない状態でした。

画像処理における前提

  • 画像処理にはPythonが良い
    • OpenCVが手軽に使える
    • 計算処理が簡単にできる
    • そのほか機械学習系のライブラリが充実している

OpenCVは画像処理に必要な機能が豊富なライブラリです。
OpenCV-Pythonチュートリアルとは — OpenCV-Python Tutorials 1 documentation

顔検出の機能もあります。
Haar Cascadesを使った顔検出 — OpenCV-Python Tutorials 1 documentation

Pythonの環境構築

Anaconda

Anacondaという諸々のライブラリが入ったPythonパッケージを使うのが良かったです。
www.python.jp

Jupyter Notebookでインタラクティブな開発環境を作ることができますし、NumPyなど計算処理向けのライブラリが同梱されています。

opencv-python

非公式ながらOpenCVを簡単にインストール可能なPythonパッケージがあります。
pypi.org

OpenCVを使う時にはとりあえず使っています。

画像処理向けのWebAPIサービス

画像処理をするにあたって、手始めに画像処理サービスをいくつか使いました。
この手のサービスでは、WebAPIを実行するだけで画像の処理を行うことが可能になっていて非常に手軽に利用できるので、入門としては適当だと思います。

Cognitive Services

Microsoftが提供するAIのWebAPIサービスです。

FaceAPIを使いました。顔を検出して、年齢、性別といった属性情報を取得できます。
azure.microsoft.com

Amazon Rekognition

Amazonが提供するAIのWebAPIサービスです。

顔の検出や分析の機能を利用しました。
aws.amazon.com Cognitive Servicesとの違いとしては以下のような点がありました。

  • 動画をそのまま扱える
  • 得られる年齢の幅が広い
  • 横顔の顔検出にも対応できている

こういったWebAPIで利用できるAIのサービスには大きな違いはないというのが印象です。

オープンな手法を活用した実装

実際の状況に当てはめると画像処理サービスでは対応できないことが多いため、自分たちで実装を行う必要があります。
特に実際の状況に応じて画像処理の精度を高めたい場合には作りこみが必要になります。
OpenCVなどを活用しながら実装を進めます。

今回は物体認識という分野の画像処理になります。
対象とする物体は「人」ということになります。
昨今の物体認識ではディープラーニングを元にした手法が主流になっていて、その中でも代表的な以下の手法があります。

  • SSD(Single Shot Multibox Detector)
  • YOLO(You only look once)
  • R-CNN(Region CNN)

Mask R-CNNという手法は画像から物体の領域を特定することが可能になっていて、物体の検出精度も高い手法になっています。

github.com

ただ物体の検出は処理時間とのトレードオフになっています。 精度が高い手法はその分だけ処理時間もかかってしまうという問題を抱えていて、ユースケースに応じて使用する手法を選ぶ必要があります。
リアルタイムでの処理が必要な場合、処理時間が短い手法を選ぶ必要がありそうです。
R-CNN系の手法に比べて、SSDやYOLOは処理時間が短い手法になっています。

SSD
GitHub - weiliu89/caffe at ssd

YOLO
YOLO: Real-Time Object Detection

人物検出および人物追跡

検出は追跡に比べて時間がかかります。また、物体に識別子をつけることで同じ人物を誤認識することを回避できます。
物体に識別子をつけるという処理は追跡でしか出来ないために人物検出と人物追跡を組み合わせる必要があります。
この点に関して、こちらの内容が参考になりました。

www.pyimagesearch.com SSDと物体追跡の手法を組み合わせたものになります。

物体追跡では、OpenCVにもいくつかの手法があります。

www.pyimagesearch.com

追跡には様々な手法が提供されていますが根本的な問題があります。
それは追跡では物体の重なりによって追跡対象が切り替わる可能性があることです。これは追跡の根本的な欠陥です。
追跡の手法にリソースをかけても根本的な解決にはならないので、処理時間が短く精度がそれなりに高い手法を選ぶのが良いと思います。
本当に正確な追跡を行いたい場合、3Dのデータを活用して物体の奥行きを把握しなければなりません。

まとめ

Pythonによる画像処理という課題を通して、画像処理の現状を知ることが出来ました。
取り組んでみての所感としては、画像処理というのはまだまだ専門的な領域であるということです。
この領域ではディープラーニングを元にした多くの手法が短期間で提案されて発展しています。
このような状況をいちソフトウェアエンジニアがキャッチアップすることは難しく、画像処理の専門家が必要な状況です。
今回は実施していないのですが、精度を求めていくとその状況に応じたデータを用意してモデルを作成しなければ難しいという結論になり、そのための専門的なスキルが新たに必要になります。

機械学習を簡単に実施可能なライブラリがオープンに利用できるような状況になっていて、いちソフトウェアエンジニアが機械学習の範囲でやれることが増えたことは間違いないと思います。ただし、その領域を本格的に実施する場合には、ただライブラリを利用できるだけでは難しく、その分野での専門的なスキルが必要だということを感じました。

Docker Composeによる開発環境の構築(TypeScript,Node.js,Redis,Elasticsearch)

TypeScriptで作ってみるシリーズの作業ログです。
今回はDockerおよびでDocker ComposeでTypeScriptの開発環境を作ります。
TypeScriptによるNode.jsアプリの他にRedis、Elasticsearchを使います。

前回はこちらです。

hi1280.hatenablog.com

Node.jsアプリの作成

Expressを使ってWebAPI化します。

index.ts

import bodyParser from 'body-parser';
import compression from 'compression';
import cors from 'cors';
import express from 'express';
import helmet from 'helmet';
import { main } from './main';

const app = express();

app.use(compression());
app.use(helmet());
app.use(cors());
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));

app.get('/', async (_, res) => {
  const data = await main();
  return res.json(data);
});

const port = process.env.PORT || '3000';
app.listen(port, () => console.info(`API running on localhost:${port}`));

特に難しいことはしていないので、説明は省略します。

Redisのセットアップ

公式のDockerイメージを使います。

docker pull redis:5.0.3-alpine3.8
docker run --rm -d -p 6379:6379/tcp redis:5.0.3-alpine3.8

npmモジュールをインストールします。

npm install --save redis
npm install --save-dev @types/redis

Node.jsでRedisが使えるようになります。

redis.ts

import redis from 'redis';
const client = redis.createClient();

client.on('error', (err: any) => {
  console.log('Error ' + err);
});

client.set('string key', 'string val', redis.print);
client.hset('hash key', 'hashtest 1', 'some value', redis.print);
client.hset('hash key', 'hashtest 2', 'some other value', redis.print);
client.hkeys('hash key', (_, replies: any) => {
  console.log(replies.length + ' replies:');
  replies.forEach((reply: any, i: any) => {
    console.log('    ' + i + ': ' + reply);
  });
  client.quit();
});

Elasticsearchのセットアップ

ElasticsearchとKibanaを起動するため、Docker Composeを使います。

docker-compose.yml

version: '2'
services:
  elasticsearch:
    image: elasticsearch:6.5.3
    volumes:
        - elasticsearch-data:/usr/share/elasticsearch/data
    ports:
        - 9200:9200
    expose:
        - 9300
  kibana:
    image: kibana:6.5.3
    ports:
        - 5601:5601
volumes:
    elasticsearch-data:
        driver: local
docker-compose up

npmモジュールをインストールします。

npm install --save elasticsearch
npm install --save-dev @types/elasticsearch

Node.jsでElasticsearchが使えるようになります。

elasticsearch.ts

import elasticsearch from 'elasticsearch';
const client = new elasticsearch.Client({
  host: 'localhost:9200',
  log: 'trace',
});

client.ping(
  {
    requestTimeout: 30000,
  },
  function(error: any) {
    if (error) {
      console.error('elasticsearch cluster is down!');
    } else {
      console.log('All is well');
    }
  },
);

client.index({
  body: {
    text: 'Hello World!',
  },
  index: 'myindex',
  type: 'mytype',
});

client.search({
    body: {
      query: {
        match_all: {},
      },
    },
  })
  .then(
    function(resp: any) {
      const hits = resp.hits.hits;
    },
    function(err: any) {
      console.trace(err.message);
    },
  );

Node.jsアプリのDockerfile作成

TypeScriptによるNode.jsアプリのDockerfileを作成します。

Dockerfile

# Build
FROM node:10.14.2-alpine AS build-stage
WORKDIR /usr/src/app
COPY package*.json ./
RUN npm install --silent
COPY . .
RUN npm run build
# Release
FROM node:10.14.2-alpine AS release-stage
WORKDIR /usr/src/app
COPY package*.json ./
RUN npm install --production --silent
COPY --from=build-stage /usr/src/app/dist /usr/src/app/dist
EXPOSE 3000
ENV NODE_ENV production
CMD [ "node", "dist/index.js" ]

Multi-Stage Buildを使って、TypeScriptからJavaScriptへのトランスパイル処理とNode.jsの実行を分けています。
これによって、Node.jsの実行には不要なTypeScript関係のモジュールを排除してDockerイメージのサイズを削減しています。

Docker Composeのセットアップ

Elasticsearch,Redis,Kibanaが起動するようにDocker Composeを構成します。

docker-compose.yml

version: '2'
services:
  elasticsearch:
    image: elasticsearch:6.5.3
    volumes:
        - elasticsearch-data:/usr/share/elasticsearch/data
    ports:
        - 9200:9200
    expose:
        - 9300
  kibana:
    image: kibana:6.5.3
    ports:
        - 5601:5601
  redis:
    image: redis:5.0.3-alpine3.8
    ports:
        - 6379:6379
  ts-example-api:
    build: .
    ports:
      - 3000:3000
    environment:
      - ES_HOST=elasticsearch
      - ES_PORT=9200
      - REDIS_HOST=redis
      - REDIS_PORT=6379
    depends_on:
      - elasticsearch
      - kibana
      - redis
volumes:
    elasticsearch-data:
        driver: local

Node.jsアプリのソースはこちらです。
main.ts

import axios from 'axios';
import elasticsearch from 'elasticsearch';
import redis from 'redis';
import { promisify } from 'util';
import { Qiita } from './qiita';

const esclient = new elasticsearch.Client({
  host: `${process.env.ES_HOST}:${process.env.ES_PORT}`,
});

export async function main() {
  const client = redis.createClient({
    host: process.env.REDIS_HOST,
    port: parseInt(process.env.REDIS_PORT!, 10),
  });
  const getAsync = promisify(client.get).bind(client);
  const items = await getAsync('items');
  let returned = null;
  if (items) {
    returned = JSON.parse(items);
  } else {
    const res = await get();
    client.set('items', JSON.stringify(res.data), 'EX', 1800);
    const body: Array<{}> = [];
    res.data.forEach(d => {
      body.push({
        index: {
          _id: d.id,
        },
      });
      body.push(d);
    });
    esclient.bulk({
      body,
      index: 'qiita',
      type: 'items',
    });
    returned = res.data;
  }
  client.quit();
  return returned;
}

export function get() {
  return axios.get<Qiita[]>('https://qiita.com/api/v2/items');
}

KibanaでもElasticsearchの内容が見れます。
f:id:hi1280:20181215235832p:plain

まとめ

ここまでの成果物

github.com

プログラム実行に複数のミドルウェアが関係する場合、Docker Composeでコンテナ環境を作ってしまうのが開発が楽になります。

参考

Redis

Elasticsearch

Docker