ハードウェア技術者のスキルアップ日誌

某家電メーカーの技術者がスキルアップのために勉強したことを記録するブログです

FaceNet(顔認証)を使って自動撮影カメラを作ってみた

前回、GITHUBで公開されているFaceNetを動かしてみました。
今回はこれを使って登録した人の顔を自動で撮影するおもちゃを作ってみたいと思います。

masaeng.hatenablog.com

 

ソースコードの修正

FaceNetのcompare.pyを修正して、USBカメラで撮影した映像に対して、
FaceNetで顔認証を行うスクリプトを作成しました。


①ライブラリインポートとMain関数、引数取得用の関数

from __future__ import absolute_import
from __future__ import division
from __future__ import print_function

from scipy import misc
import tensorflow as tf
import numpy as np
import sys
import os
import cv2
import copy
import glob
import argparse
import facenet
import align.detect_face
from timeit import default_timer as timer

minsize = 20  # minimum size of face
fd_threshold = [ 0.6, 0.7, 0.7 ]  # three steps's threshold
factor = 0.709  # scale factor
input_image_size = 160
fr_threshold = 1.2

def main(args):
  margin = args.margin
  with tf.Graph().as_default():
    #gpu_options = tf.GPUOptions(per_process_gpu_memory_fraction=args.gpu_memory_fraction)
    gpu_options = tf.GPUOptions(allow_growth=True) # GPUのメモリ割り当て方法を変更
    sess = tf.Session(config=tf.ConfigProto(gpu_options=gpu_options,
log_device_placement=False)) with sess.as_default():
# 顔検出のネットワーク作成 MTCNN pnet, rnet, onet = align.detect_face.create_mtcnn(sess, None) image_paths = glob.glob(args.reg_paths) # 登録済み画像のフォルダ nrof_images = len(image_paths) #登録済み画像の数(only one person) # 登録済み画像から顔のみを抽出したリストを作成 images = load_and_align_data(image_paths, nrof_images, pnet, rnet, onet, args) nrof_images = len(images) #登録に成功した顔の数(only one person) # Load the model facenet.load_model(args.model) # Get input and output tensors images_placeholder = tf.get_default_graph().get_tensor_by_name("input:0") embeddings = tf.get_default_graph().get_tensor_by_name("embeddings:0") phase_train_placeholder = tf.get_default_graph().get_tensor_by_name("phase_train:0") embedding_size = embeddings.get_shape()[1] # Run forward pass to calculate embeddings feed_dict = { images_placeholder: images, phase_train_placeholder:False } emb_reg = sess.run(embeddings, feed_dict=feed_dict) # 登録済み画像の特徴ベクトル抽出 # カメラ映像/動画ファイルの取得 video_capture = cv2.VideoCapture(0) # camera input print('Start Recognition') #fps計算 初期化 frame_num = 1 accum_time =0 curr_fps = 0 prev_time = timer() fps = "FPS: ??" while True: ret, frame = video_capture.read() if ret == False: break if frame.ndim == 2: frame = facenet.to_rgb(frame) frame = frame[:, :, 0:3] #frame = cv2.resize(frame, (640, 352)) # 入力画像をリサイズ bounding_boxes, _ = align.detect_face.detect_face(frame, minsize,
pnet, rnet, onet, fd_threshold, factor) nrof_faces = bounding_boxes.shape[0] print('Detected_FaceNum: %d' % nrof_faces, end='') if nrof_faces > 0: #顔を検出した場合 det = bounding_boxes[:, 0:4] frame_size = np.asarray(frame.shape)[0:2] cropped = [] scaled = [] scaled_reshape = [] v_bb = np.zeros((nrof_faces,4), dtype=np.int32) for i in range(nrof_faces): emb_array = np.zeros((1, embedding_size)) v_bb[i][0] = np.maximum(det[i][0]-margin/2, 0) # 左上 x(横) v_bb[i][1] = np.maximum(det[i][1]-margin/2, 0) # 左上 y(縦) v_bb[i][2] = np.minimum(det[i][2]+margin/2, frame_size[1]) # 右下 x(横) v_bb[i][3] = np.minimum(det[i][3]+margin/2, frame_size[0]) # 右下 y(縦) cropped.append(frame[v_bb[i][1]:v_bb[i][3], v_bb[i][0]:v_bb[i][2], :]) cropped[i] = facenet.flip(cropped[i], False) scaled.append(misc.imresize(cropped[i],
(input_image_size, input_image_size), interp='bilinear')) scaled[i] = cv2.resize(scaled[i], (input_image_size,input_image_size), interpolation=cv2.INTER_CUBIC) scaled[i] = facenet.prewhiten(scaled[i]) scaled_reshape.append(scaled[i].reshape(-1,input_image_size,input_image_size,3)) cv2.rectangle(frame, (v_bb[i][0], v_bb[i][1]), (v_bb[i][2], v_bb[i][3]), (0, 255, 0), 2) feed_dict = {images_placeholder: scaled_reshape[i],
phase_train_placeholder: False} emb_array[0, :] = sess.run(embeddings, feed_dict=feed_dict) # 特徴ベクトルの抽出 # 識別(登録済み画像の特徴ベクトルとのユークリッド距離を計算) dist_ave = cal_distance(emb_reg, emb_array, nrof_images) print(' %1.4f ' % dist_ave, end='') if dist_ave < fr_threshold: # 認識のしきい値 #plot result idx under box text_x = v_bb[i][0] text_y = v_bb[i][3] + 20 print('Find registered person', end='') cv2.rectangle(frame, (v_bb[i][0], v_bb[i][1]),
(v_bb[i][2], v_bb[i][3]), (0, 0, 255), 2) else: print('', end='') else: #顔非検出の場合 print(' Alignment Failure', end='') print('') #frame_num表示 cv2.putText(frame, str(frame_num), (3,30), cv2.FONT_HERSHEY_SIMPLEX,
0.50, (255, 0, 0), thickness=2) frame_num += 1 #fps計算 curr_time = timer() exec_time = curr_time - prev_time prev_time = curr_time accum_time = accum_time + exec_time curr_fps = curr_fps + 1 if accum_time > 1: accum_time = accum_time - 1 fps = "FPS: " + str(curr_fps) curr_fps = 0 cv2.putText(frame, fps, (3,15), cv2.FONT_HERSHEY_SIMPLEX, 0.50, (255, 0, 0), thickness=2) cv2.imshow('Video', frame) if cv2.waitKey(1) & 0xFF == ord('q'): break video_capture.release() cv2.destroyAllWindows() def parse_arguments(argv): parser = argparse.ArgumentParser() parser.add_argument('model', type=str, help='Could be either a directory containing the meta_file and ckpt_file or a model protobuf (.pb) file') # parser.add_argument('image_files', type=str, nargs='+', help='Images to compare') parser.add_argument('reg_paths', type=str, help='The path of registered human faces') parser.add_argument('--image_size', type=int, help='Image size (height, width) in pixels.', default=160) parser.add_argument('--margin', type=int, help='Margin for the crop around the bounding box (height, width) in pixels.', default=44) parser.add_argument('--gpu_memory_fraction', type=float, help='Upper bound on the amount of GPU memory that will be used by the process.', default=1.0) return parser.parse_args(argv)

 

②MTCNNで顔検出し、顔画像のリストを作成

def load_and_align_data(image_paths, nrof_images,pnet, rnet, onet, args):
  img_list = []
  for image in image_paths:
    img = misc.imread(os.path.expanduser(image), mode='RGB') # 画像読み込み RGB形式

    img_size = np.asarray(img.shape)[0:2]
    bounding_boxes, _ = align.detect_face.detect_face(img, minsize,
                     pnet, rnet, onet, fd_threshold, factor) # 顔検出 if len(bounding_boxes) < 1: # 顔が検出されなかった場合 print("can't detect face", image) continue det = np.squeeze(bounding_boxes[0,0:4]) #顔の検出ポイント cropped = cropped_face(det, img, img_size, args) img_list.append(cropped) if nrof_images > 1 : images = np.stack(img_list) # 登録済み画像から顔のみ抽出したリスト else : images = img_list return images

 

③顔領域のクロッピング

def cropped_face(det, img, img_size, args):
  margin = args.margin
  bb = np.zeros(4, dtype=np.int32)
  bb[0] = np.maximum(det[0]-margin/2, 0)   # 左上 x(横)
  bb[1] = np.maximum(det[1]-margin/2, 0)   # 左上 y(縦)
  bb[2] = np.minimum(det[2]+margin/2, img_size[1])   # 右下 x(横)
  bb[3] = np.minimum(det[3]+margin/2, img_size[0])   # 右下 y(縦)

  cropped = img[bb[1]:bb[3],bb[0]:bb[2],:] # bounding boxの場所指定
  aligned = misc.imresize(cropped, 
    (input_image_size, input_image_size), interp='bilinear') # クロッピングしてリサイズ aligned = facenet.prewhiten(aligned) return aligned

 

④各顔のユークリッド距離を計算

def cal_distance(emb_reg, emb_video, nrof_images):
  dist = np.zeros(nrof_images, dtype=np.float64)
  dist_ave = 0.
  cnt = 1
  for j in range(nrof_images):
    dist[j] = np.sqrt(np.sum(np.square(np.subtract(emb_reg[j,:],
                        emb_video[0, :])))) #ユークリッド距離計算 dist.sort() #距離が短い順に並び替え for x in range(3): # kNN, k=3 dist_ave += dist[x] cnt += 1 if cnt > len(dist): break dist_ave = dist_ave / float(cnt-1) # 登録済み画像とのユークリッド距離(最近点3個) return dist_ave

 

実行時には以下のように引数を指定します。

$ python [重みファイルのパス] [登録する顔画像のパス] \
$ --image_size 160 --margin 32 --gpu_memory_fraction 0

登録したい顔画像をフォルダに入れて、引数でそのパスを指定します。
簡単のため、登録できる人数は一人だけとしていますが、
画像は複数枚入れてもOKです。
パス指定は data/registered/*のようにワイルドカードで複数枚の指定が可能です。

その他の引数は元のcompare.pyと同様です。

 

処理内容を図にすると以下のような感じです。
登録顔画像と未知の顔の距離を計算し、閾値比較をします。
顔画像を4枚以上登録した場合は距離が近い方から3点の距離の平均値を
閾値比較に使用しています。

f:id:masashi_k:20190804003948p:plain

閾値は fr_thresholdという変数で定義しています。
上記ソースコードでは閾値は1.2ですが、カメラの撮影条件によって
適切な値に設定してください。

 

ラズパイ上で動作させる

このスクリプトをさらに応用して、登録した顔がカメラに写ったら
その画像をJPGで保存するプログラムを作ります。
さらに、これをラズパイ上で動作させ、撮影したことが分かるように
撮影したらLEDを1秒間点灯させるようにしたいと思います。
(ラズパイで動かす意味はあまりありませんが・・・)

以前勉強した、ラズパイでLEDを点灯させる処理を上記のソースコードに追加します。

masaeng.hatenablog.com

 

追加したソースコード

import RPi.GPIO as GPIO
import time
import subprocess

LED_PIN = 26          # 36pin

def main():
    num = 1
    GPIO.setmode(GPIO.BCM)
    GPIO.setup(LED_PIN ,GPIO.OUT)

    if dist_ave < fr_threshold: # 認識のしきい値
      #plot result idx under box
      text_x = v_bb[i][0]
      text_y = v_bb[i][3] + 20
      print('Find registered person', end='')
      cv2.rectangle(frame, (v_bb[i][0], v_bb[i][1]), 
                                 (v_bb[i][2], v_bb[i][3]), (0, 0, 255), 2)

      GPIO.output(LED_PIN , GPIO.HIGH)
      cv2.imwrite("picture{0:03d}.jpg".format(num), frame)  #保存先を指定
      time.sleep(1)
      GPIO.output(LED_PIN , GPIO.LOW)
      num = num + 1

 

回路図は前回同様こちらです。38,40ピンは今回使いません。

f:id:masashi_k:20190706235403p:plain

 

作成したスクリプト実行させてみるとこのように登録顔を見つけたときにLEDが点灯します。リード線で接続している緑色のLEDがGPIO16 36ピンにつながっています。

f:id:masashi_k:20190809233831j:plain

息子の写真1枚登録し、1~2時間ほどスクリプトを走らせてみた結果、このように写真が撮れています。(リモートからラズパイにアクセスしています。)

f:id:masashi_k:20190809232558p:plain

全部で47枚の写真が撮れており、正しく息子を判別して写真撮影できたのはわずか7枚(正解率約15%)でした。そもそも顔でないものを顔と誤判別していたり、親子である私と息子の識別ができていないなど、性能はまだまだでした。登録顔が1枚だけだったので、いろいろな角度の顔画像を登録すればもう少し性能が上がるのではと思います。 

まとめ

 GITHUBで公開されているFaceNetの顔認識を使って、登録した人の写真を自動で撮影する機器をラズベリーパイで作ってみました。登録顔が1枚だけだったので、精度が今一つでしたが、FaceNetの概要を理解でき、また、所望の機能をプログラムで実現することができました。今後もディープラーニングを使った機器のアイデアを考えて、実際に作っていけたらと考えています。

 

 

【読書記録】- THE TEAM 5つの法則

 今回は読書メモ。リンクアンドモチベーション取締役 麻野耕司さんのTHE TEAMという本を読んでみたので、学んだことを記録します。

今の仕事は個人作業の要素が強いのですが、一方で、メンバーを信頼し、協力しながら困難に立ち向かうといったチームで進める仕事に憧れがあります。
将来そんなチームを作れるようにチームの成功の法則を理解しておきたいと思います。

THE TEAM 5つの法則 (NewsPicks Book)

THE TEAM 5つの法則 (NewsPicks Book)

 

 

5つの法則 

チームの法則は以下の5つが紹介されています。
頭文字をとるとABCDEで覚えやすいです。

f:id:masashi_k:20190731221759p:plain

 

Aim - 目標設定

目標には行動レベル、成果レベル、意義レベルの3つのレベルがある。
行動レベルの目標は取るべきアクションがわかりやすい、意義レベルの目標は何をするかの自由度が高く、メンバーが自ら何をするかを考える必要がある。
メンバーの能力に応じて目標のレベルを変える。
ブレイクスルーは意義レベルの目標を立てないと起こせない。

 

Boarding - 人員選定

ビジネスの環境変化が激しいケースでは人員の流動性、メンバーの多様性が重要。
いろいろなスキルを持った人をメンバーに入れる必要あり。

 

Communication - 意思疎通

ルールを定めることで時間的なコミュニケーションコストを減らせる。
しかし、細かすぎるルールはNG。 環境変化が大きいとすぐ使えなくなる。
誰が言うか、どのように言うかで伝わり方が変わる。
メンバーの経験、感覚、思考、能力を考慮した声掛けが必要。
心理的に安全な場を作る。(何を言っても大丈夫、発言を責めないなど)

 

Decision - 意思決定

合議をする場合:
 選択肢を選ぶ基準とその優先順位を決める
 その上で選択肢を考える

リーダーが決める場合:
 どの案もメリット、デメリットがある 五分五分のことも多いので早く決める
 決めた後はメンバーが着実に実行する 決定を正解にするのはメンバー

 

Engagement - 共感創造

理念・方針/活動・成長/人材・風土/待遇・特権
これらいずれかでメンバーのモチベーションを高める

 Engagement = 報酬の魅力(WILL) x 達成可能性(CAN) x 危機感(MUST)

近年感情報酬(やりがい、仲間とのつながり、貢献など)を重視する傾向が強い。

 

その他

当事者意識をメンバーに持たせるには・・・
 ・人員の余剰を減らす
 ・責任範囲を明確化する
 ・参画感を持たせる 

FaceNet(顔認識)を動かしてみた

YOLOやSSDなどディープラーニングのネットワークをいくつか試してきましたが、
今回は顔認識のニューラルネットワークであるFaceNetを動かしてみましたので
手順を記録しておきます。

FaceNetの概要

FaceNetは2015年にGoogleが発表した顔認証用のニューラルネットワークです。

FaceNetの論文はこちらから参照できます。
https://arxiv.org/pdf/1503.03832.pdf

 

Siamese Network

FaceNetはSiamese(シャム) Networkをベースにしており、
入力画像がどのクラスに属するかではなく、
画像同士が似ているか似てないかを表す距離を学習します。

   f:id:masashi_k:20190725220116p:plain

クラスに分類する場合、候補となる人のどれに当たるかを分類することになるため、
各人の画像を大量に収集して学習する必要があります。
さらに、候補者が増えた場合は、再学習が必要です。
一方、Siamese Networkのように画像間の距離を算出する場合は、
その距離により2つの顔画像が同一人物かどうかを判断できるので
少ない画像で判別が可能となります。
画像は1枚でも判定が可能でそのような学習タスクをOne Shot Learningと呼びます。

 

Triplet Loss

Siamese Networkでは2つの画像のペアで学習を行いますが、
FaceNetでは3つの画像の組み合わせで学習を行います。
その時に使用する損失関数がTriplet Lossと呼ばれるもので、
基準となるAnchorに対して、同一ラベルのものを近くに、異なるラベルのものを
遠くに置くように学習します。

f:id:masashi_k:20190725222629p:plain

 

FaceNetのネットワーク構成

ネットワーク構成は以下のようになっています。
顔のみを切り出し、正規化した画像を入力します。
DEEP ARCHITECTUREの部分が特徴量を抽出するCNNで、
論文上ではZeiler&Fergus、Inceptionを使用しています。
他のCNNを使用することも可能と思われます。
その出力CNNのに対してL2ノルムを取り、Triplet Lossを計算します。

f:id:masashi_k:20190725222937p:plain

 

OpenFace

オープンソースの顔認識技術としてOpenFaceというものもあります。http://cmusatyalab.github.io/openface/

こちらはFaceNet論文を元にしたオープンソースの実装です。
DNN(Deep Neural Network)を用い、顔から128次元の特徴ベクトルを抽出します。

f:id:masashi_k:20190725223903j:plain

         OpenFaceのサイト(上記URL)から引用

 

FaceNetを動かしてみた

GITHUBで公開されているこちらのレポジトリを試してみました。

github.com

FaceNetは顔認識のニューラルネットワークですが、実際に認識をさせるためには
写真から顔の部分を切り出す必要があります。
このため、facenetではMTCNNという顔検知のニューラルネットワークを前段で
使用しており、その結果をfacenetに入力する形になっています。

MTCNNの論文
https://kpzhang93.github.io/MTCNN_face_detection_alignment/paper/spl.pdf

 

ネットワークの動かし方はこちらのサイトがとても参考になりました。

appliedmachinelearning.blog


Face_IDという別のレポジトリに関するものですが、
facenet(小文字表記はレポジトリ名)をベースに修正されているようで、
共通している部分が多いです。

上記サイトでは2つの命題について記載されています。
①Face verification:登録された顔が画像の中にあるかを判別
②Face identification:あらかじめ登録された顔のデータベースから
          どれに当てはまるかを識別
今回は①を試してみました 

環境

 OS:Windows 10 Home (64bit)
 Python 3.5
 Anaconda 4.2.0
 Tensorflow 1.12.0

 

動かし方

GITHUBからレポジトリをCloneして任意の場所に解凍します。
https://github.com/davidsandberg/facenet

②以下のURLから学習済みの重みをダウンロードし、facenetの下に保存します。
 データセットCASIA-WebFaceとVGGFace2の2種類あります。
https://drive.google.com/file/d/1R77HmFADxe87GmoLwzfgMu_HY0IhcyBz/view
https://drive.google.com/file/d/1EXPBSXwTaqrSC0OhUdXNmKSh9qJUQ55-/view

③Anacondaプロンプトを起動し、プロジェクトディレクトリに移動します。
Requirements.txtに記載されているライブラリをインストールします。
必要なライブラリは以下の通りです。

  Tensorflow
  scipy
  scikit-learn
  opencv-python
  h5py 
  matplotlib
  Pillow
  requests
  psutil

※以下のコマンドで自動で必要なライブラリをインストールできますが、
Tensorflowのバージョンが1.7.0になってしまうので、ダウングレードが不要であれば、
Requirements.txtを修正のうえ、実行してください。
私の環境では1.12.0でも問題なく動作しました。

$ pip install -r requirements.txt

 

④サンプルスクリプトcompare.pyを実行

$ python [重みファイルのパス] [比較したい画像ファイルパス(2つ以上も可)] \
$ --image_size 160 --margin 32 --gpu_memory_fraction 0

重みファイルのパス:②で取得したファイルのパスを指定
比較したい画像パス:顔の類似度を計算したい画像ファイルを指定
 手元に画像がない場合はサンプルとしてfacenet/data/imagesの中に画像があります。
--image_size:画像サイズ デフォルトは160pixel
--margin:顔検知した際、検知した場所から何pixel外側をクロップするか
     デフォルト 44pixel
--gpu_memory_fraction:GPUメモリ使用量の設定

具体的にはこのようになります。

$ python src/compare.py src/20180402-114759 \
$ data/images/Anthony_Hopkins_0001.jpg \
$ data/images/Anthony_Hopkins_0002.jpg \
$ --image_size 160 --margin 32 --gpu_memory_fraction 0

結果は以下の通りとなります。

Images:
0: data\images\Anthony_Hopkins_0001.jpg
1: data\images\Anthony_Hopkins_0002.jpg

Distance matrix
     0            1
0   0.0000   0.8516
1   0.8516   0.0000

Anthony_Hopkinsの画像を二枚入力し、0と1の画像の距離が0.856となっています。
距離が短いほど2つの顔が類似しているということになります。

同一人物だけだと面白くないので別の顔画像で試した結果も示します。
Face_IDのレポジトリにはもう少しサンプル画像が入っていますので、
それを使って試しました。

結果はこちら。

Images:
0: facenet/dataset/test-images/mark1.jpeg
1: facenet/dataset/test-images/mark.jpeg
2: facenet/dataset/test-images/bradley.jpeg
3: facenet/dataset/test-images/hritik.jpeg

 

Distance matrix
        0         1         2         3

0    0.0000    0.6942    1.4113    1.4643
1    0.6942    0.0000    1.4722    1.4246
2    1.4113    1.4722    0.0000    1.1274
3    1.4643    1.4246    1.1274    0.0000

0,1がマークザッカーバーグで同一人物、2,3は別の人物です。
結果を見ると0-1の距離が約0.7と短く、それ以外の距離はすべて1以上です。
FaceNetの論文では1.1が閾値としてよいとされているので、
確かに同一人物では画像間の距離が1.1以下となっていることを確認できました。

まとめ

GITHUBに公開されているFaceNetのレポジトリを実際に動かしてみて、
顔同士の距離が算出され、それが人の類似度になっていることを確認しました。
次は、このサンプルコードを使って、ラズパイでなにか作ってみたいと思います。

参考サイト

https://qiita.com/koshian2/items/554b3cbef6aeb1aab10d

https://www.slideshare.net/kaorunasuno/20150611-nasuno

https://blog.imind.jp/entry/2019/05/01/142232

https://blog.imind.jp/entry/2019/05/06/192603

【読書記録】- VISION DRIVEN 直感と論理をつなぐ思考法

今回はVISION DRIVENという本を読んでみました。

私は経営者やリーダーは細かい指示をする前にビジョンを語るべきだと思っていて、
人を動かすビジョンや、それを伝えるヒントが得られればと思い、手に取りました。

いつものように要約と自分の気づきをまとめます。

直感と論理をつなぐ思考法 VISION DRIVEN

直感と論理をつなぐ思考法 VISION DRIVEN

 

 

ビジョン思考とは?

以下はこの本の内容を1枚に要約したチャート。

f:id:masashi_k:20190721224825p:plain

思考法には上記の4つがある。

カイゼン思考 ー PDCAを回し、今までの同じことを如何に効率的にやるか
          VUCAの時代、これまでの延長線上をたどっていっても生き残れない

②戦略思考 - ニッチな領域など、勝てる領域を探し、そこで戦う
        優秀な人だけが生き残る戦場 しかし疲弊し、長続きは難しい

③デザイン思考 ー モノを作り、手を動かしながら考える アジャイル開発
          生活者の課題をみんなで解決する

④ビジョン思考 ー 他人を気にせず自分の中の「妄想」を起点にする
          直感を論理に、妄想を戦略に落とし込む

この中で著者が勧めるのはタイトルにもあるように「ビジョン思考」。

仕事に振り回されながら生きていると、「他人モード」になっており、
自分が本当にやりたいことを見失ってしまう。

そこで、「自分モード」を取り戻し、こうなったら面白い!というものを探す、
妄想をする時間を確保する。

成功の陰には根拠のない「妄想」があり、これを論理→戦略へと落とし込んでいく。
決して、論理や戦略から考え始めないこと。

ビジョン思考:直感から思考をはじめ、それを駆動力にしながら同時に
「直感」を「論理」につなぎ、「妄想」を「戦略」に落とし込むといった思考モード

 

ビジョン思考を習慣化するには?

次の2つのものが必要となる。

 ①時間的な余白
   他人モードではなく自分モードで考える時間
   モレスキンを買い、自分の1日のスケジュールの中の15分を抑える

 ②ビジョン思考のメソッド
   妄想→知覚→組替→表現のサイクルを回す

f:id:masashi_k:20190721231700p:plain

 妄想

■感情アウトプット
  決まった時間に決まったページだけその時感じたことをノートに吐き出す
  最低一ヶ月続けると他人モードから自分モードを取り戻せる
  「もし~なら?」、「子供のころの夢は?」のような問い掛けを通して
  妄想を膨らませる ワクワク感が大切
   ※メモの魔力の自己分析1000問をやることもよさそう
masaeng.hatenablog.com

知覚

感じたこと、気づいたことに対して意味づけをする(センスメイキング)
以下の3プロセスを日々実践

 ①感知ーありのままに見る
 ②解釈ーインプットを絵の状態で考え、フレームにまとめ
 ③意味付けーまとめあげた考えに意味を与える
       ビジュアルと言語を行ったり来たりしながら考える

組替

ここまでのアイデアは平凡でよい
イデアは出してからどう磨き上げるかが勝負

 組替 = 分解 + 再構築

分解:当たり前の要素に分け、その中から違和感を探し、
   その当たり前の逆を考える
   物事の構造を可視化すると、アイデアは考えやすい
再構築:分解した構成要素のアナロジー(似たもの)を考え、
    その構図をアイデアに取り入れる

表現

プロトタイプを人に見せ、フィードバックをもらうというサイクルを
いかに早く回せるか
自分のビジョンを一目で理解できる「絵」にし、説明する
ストーリーを作ることも効果的
プロトタイピングの最終目的は自分のビジョンに共感してもらい、
聞いてもらった人を動かすこと

 

感想

自分のスキルを上げたいというざっくりしたビジョンはあるものの、
世の中をこうしたい、こんなサービスを作りたいという具体的なビジョンが
描けていなかった自分にとっては、そのビジョンの作り方を紹介した内容であり、
まさに自分にぴったりの書籍でした。

ビジョンを持つには何より自分と向き合うことが大切で、振り返ってみれば、
著者が指摘するように自分についてじっくり考える時間はなかなか取れていません。

まずはモレスキンを買い、手帳で考える時間を確保して、生い立ちを振り返ったり、
ニュースから将来を想像するなどして妄想を膨らませていこうと思います。

 

また、平行して読んだ濱口秀̪司氏のSHIFT:イノベーションの作法とも
共通するところがあり、こちらも合わせて読むと理解が深まると思いますので、
ご紹介しておきます。

SHIFT:イノベーションの作法

SHIFT:イノベーションの作法

 

 

 

ラズパイでGPIO制御をする

今までソフトウェア中心の勉強でしたが、ハードウェアのスキルの幅を
広げるためにもラズベリーパイを使って電子回路を制御し、
いろんなものを作っていきたいと思っています。
そのためにラズパイでハードウェアを制御する方法を勉強していきます。
初回はGPIO制御です。LEDの点灯、消灯を実際にやってみたいと思います。

ラズパイのピンレイアウト

私が持っているラズパイはRaspberry Pi 3B+ですが、
ピンレイアウトはこのようになっています。
いくつかのピンはGPIOだけでなく、I2C, SPI, UARTとしても使用できます。

f:id:masashi_k:20190706222932j:plain  f:id:masashi_k:20190706222941p:plain

コマンドでも$gpio readallで確認できます。

GPIOポートの使い方

ラズパイのポートをGPIOとして使用する方法はいくつかあるようですが、
ディープラーニングPythonを使い慣れていることもあり、RPi.GPIOという
ライブラリを使ってみます。

まず、ライブラリのインストールです。

$ sudo apt-get install python-rpi.gpio


 Pythonスクリプト上でRPi.GPIOを使用するためにライブラリをインポートします。

import RPi.GPIO as GPIO

 

RPi.GPIOではポートの指定方法に2種類あります。
1つはピン番号1-40(上図のNo.の列の番号)で指定する方法 GPIO.BOARD
もう一つはGPIOの番号(上図のGPIO列の番号)で指定する方法 GPIO.BCM
どちらの表記で記載するかを以下のようにGPIO.setmode()で指定します。

GPIO.setmode(GPIO.BOARD)    # ボードのピン番号で指定
GPIO.setmode(GPIO.BCM)      # GPIO番号で指定

 

GPIOポートの入出力設定はGPIO.setup()で行います。
例えば、GPIO2を出力、GPIO3を入力ポートで使用する場合は以下のようになります。

GPIO.setmode(GPIO.BCM)      # GPIO番号で指定
GPIO.setup(2, GPIO.OUT)     # GPIO2:出力ポート
GPIO.setup(3, GPIO.IN)      # GPIO3:入力ポート

 

出力設定したポートのH/L出力を指定するにはGPIO.output()を使います。
GPIO2のポートをH/L制御する場合は以下のようになります。

GPIO.output(2, GPIO.HIGH)   # 出力H
GPIO.output(2, GPIO.LOW)    # 出力L

 

入力設定したポートの値を読み出すにはGPIO.input()を使います。
この関数の戻り値がポートの値となっています。

print GPIO.input(3)   # GPIO3の値を読み出し
# 結果:1 or 0

 

GPIO入力をトリガに割り込み処理を実行

プッシュスイッチなどポートの値の変化をトリガにして処理を行いたいケースが
あったので割り込み処理の作り方を調べてみました。

callback関数を定義し、ポートの値が変化したらcallback関数を呼び出す
add_event_detect()というメソッドを使用します。

def callback(gpio_pin):
    #関数内処理

GPIO.add_event_detect("ピン名", "トリガエッジ", 
                       callback=callback, bouncetime=300)

ピン名:ピン番号かGPIO名を指定 入力ポートに設定しておく必要あり
トリガエッジ:立ち上がりエッジ GPIO.RISING, 立下りエッジ GPIO.FALLING
                         両方 GPIO.BOTH
callback:コールバック関数の指定
bouncetime:チャタリング防止のため、指定時間の間次のイベントを呼び出さない
       単位msec

サンプル

最後に上記内容を使って作成したコードを上げておきます。
「スイッチを押すとLEDが点灯し、ラズパイに接続したUSBカメラで写真を撮る」
というものです。

回路図は以下の通り

f:id:masashi_k:20190706235403p:plain

38pinがトリガでL->Hとなったときに36pinがHとなり、LEDが点灯
同時にUSBカメラのシャッターを切る制御が動作するという仕組みです。
38pinはスイッチにつなぎたかったのですが、スイッチがなかったため、
40pinの出力結果を入力するようにしました。

# coding: utf-8
import RPi.GPIO as GPIO
import time
import subprocess

SHUTTER_PIN = 21      # 40pin
SHUTTER_TRIGGER = 20  # 38pin
LED_PIN = 26          # 36pin

def main():
    # cameraの設定
    cmd = 'fswebcam capture.jpg'

    GPIO.setmode(GPIO.BCM)
    GPIO.setup(SHUTTER_PIN,GPIO.OUT)
    GPIO.setup(SHUTTER_TRIGGER ,GPIO.IN)
    GPIO.setup(LED_PIN ,GPIO.OUT)
    # callback登録(GIO.RISING:立ち上がりエッジ検出、bouncetime:300ms)
    GPIO.add_event_detect(SHUTTER_PIN, GPIO.RISING, callback=callback, bouncetime=300)

    try:
        while(True):
            time.sleep(0.1)

    # Ctrl + Cで抜ける
    except KeyboardInterrupt:
        print("break")
        GPIO.output(TRIGGER_PIN, GPIO.LOW)  # LEDを消して終了
        GPIO.cleanup()

def callback(gpio_pin):
  print(gpio_pin)
  GPIO.output(LED_PIN , GPIO.HIGH)
  ret = subprocess.check_output(cmd, shell=True) # カメラ撮影

if __name__ == "__main__":
    main()

カメラの撮影はfswebcamというパッケージを使用しています。
もしインストールされていない場合は
  $ sudo apt-get install fswebcam
でインストールできます。

40pinのトリガーはこちらのスクリプトで発生させています。

import RPi.GPIO as GPIO

SHUTTER_PIN= 21 # 40pin

def main():
    GPIO.setmode(GPIO.BCM)
    GPIO.setup(SHUTTER_PIN,GPIO.OUT)
    GPIO.output(SHUTTER_PIN, GPIO.HIGH)

if __name__ == "__main__":
    main()

 

このスクリプトで写真撮影はできていましたが、fswebcamを使うと
トリガー発生時から約1sec後にシャッターを切るようです。
リモートからのシャッター制御には使えないですね。
トリガーが発生したら即撮影するいい方法があったら教えてください。

【読書記録】ー GIVE & TAKE

久しぶりの読書記録になりました。

今回は友人に勧められて読んでみたGIVE & TAKEという本について、学んだことを記録します。 

GIVE & TAKE 「与える人」こそ成功する時代 (単行本)

GIVE & TAKE 「与える人」こそ成功する時代 (単行本)

 

 

人間は3種類に分類される

f:id:masashi_k:20190630235417p:plain

人間の性格として3つのタイプがある。

 ・ギバー:人に惜しみなく与える人
 ・テイカー:真っ先に自分の利益を優先させる人
 ・マッチャー:損得のバランスを考える人

この中で最も成功するのはギバー。ギバーになれ!
ただし、最も生産性が低いのもギバーである。
その違いは「他者志向性」があるか。

 

ギバーが持つ考えや行動

自分の利益より顧客の利益を考える

一時的に損をするかもしれないが、顧客の視点に立ち、顧客の利益を考えて
行動することは長期的には自分にメリットになる

 

弱いつながりを大事にする

弱いつながり:過去に同じ仕事をした、同じコミュニティにいたなどの
       ちょっとした知り合いという人間関係
困ったときに休眠状態のつながりをリコネクトして助けを求めると
現在の自分の周りにはない価値ある情報や手助けを得られる可能性がある

 

ギブは価値の交換ではなく、パイを大きくすること

ギバーは見返りを求めず、コミュニティに恩を送る
与えることでネットワーク全体の価値を増やし、全体の利益につなげる

 

頼る≠弱さ

頼ることは強さの源
多くの人のスキルを大きな利益のために活用できる

 

部下、周りのメンバーに期待を抱く

可能性を信じていることをはっきり伝えることがチームとしての成功に重要
 ⇒本人がそれに見合った姿になろうとする
ダメなところを見るのではなく、その人の一番いいところを引き出す
「才能」より粘り強さ、やる気が大切

 

ゆるいコミュニケーション

強引なコミュニケーションではなく、人の意見を聞き、アドバイスを受け入れる
自己開示し、自分の弱みをさらけ出す(ただし、仕事ができる人でないと逆効果)
アドバイスを求めることは相手を自分側につけること

 

他者志向性をもつ

自己利益と他者の利益追求は別の軸のもの 同時に追求できる
自分にとって意義のあることを行い、その中で他者のためになることをする
自分を捨てて他者のために行動することは「自己犠牲」 これでは成功できない
燃え尽きないためには与えたことを認められる、周囲からのサポートがあることが必要

 

感想

最も成功できるのは「ギバー」という明確なメッセージがあり、
ギバーを目指していこうと思わされる内容でした。

この本を読んでから日々の生活の中でギバーとして行動できているか
ということを節々で意識するようになりました。

ギバーとは本質的には自分視点ではなく、他人の視点になって考える人ということだと思います。自分は他人に対して関心が薄いという自覚があるので、まずそこから改善していくことが第一歩でしょうか。

そして、自分が相手のために力になれることは何かを考えて、それを行動に移すように
日々意識していきたいと思っています。

Linuxコマンドまとめ⑥ - GIT

Linuxの勉強も6回目になりました。一応今回で一区切りにしたいと思います。
今回のテーマはGIT(バージョン管理システム)です。
コマンドだけでなく概念的なところまで勉強したので、まとめを残しておきます。

インストールと初期設定

$ sudo apt-get install git-core                  # GITのインストール
$ git config --global user.name '名前'           # 名前の登録
$ git config --global user.email 'メールアドレス' # メールアドレスの登録
$ git config --global color.ui auto              # カラー出力を有効に

設定は~/.gitconfigに保存される

 

基本的な使い方

$ mkdir -p ~/git/work         # 作業ディレクトリを作成
$ cd ~/git/work
$ git init                         # ディレクトリの初期化(リポジトリの作成)

$ git add <ファイル名>           # ファイルをコミット対象として登録
$ git add -u               # 変更したファイルをすべてadd
$ git add -A               # 変更したファイル+新規作成ファイルすべてをadd
$ git commit -m '変更に対するコメント'  # リポジトリに登録

$ git status          # ワークツリーの状態表示
$ git diff            # ワークツリーとインデックスの差分表示
$ git diff --cached      # インデックスとレポジトリの差分表示
$ git diff HEAD # ワークツリーとレポジトリの差分表示

$ git log # コミットの履歴を確認
$ git log -p # コミットごとの差分を表示

$ git checkout HEAD .               # ワークツリーをレポジトリの状態に戻す $ git revert <取り消したいコミットのオブジェクト名> # コミットの取り消し

 

主な処理の実行イメージ

f:id:masashi_k:20190629234543p:plain

 

ブランチを使う

一つのリビジョンから複数のコミットを派生させることができる

$ git branch                         # ブランチの一覧表示
$ git branch <ブランチ名>             # 新しいブランチの作成
$ git checkout <移動先ブランチ名>      # ブランチの切り替え
$ git merge <マージするブランチ名>     # ブランチのマージ
  ※ マージ先ブランチ(残す方)を選択した状態でgit mergeを実行
$ git branch -d <ブランチ名>          # ブランチの削除

 

f:id:masashi_k:20190629235927p:plain

 

リモートレポジトリ

ソフト開発など複数人で作業をする場合にはローカルのレポジトリとは別にサーバーを立てて、そこにリモートのレポジトリを作成する。

ローカルで作業した内容をリモートに反映させたり、リモートの内容を取得することもできる。

$ git remote add <リポジトリ名> <リポジトリパス>     # リポジトリパスの名前を付ける
$ git push <リモートリポジトリ名> <ブランチ名>       # ローカルリポジトリの変更をリモートリポジトリに送信
$ git pull <リモートリポジトリ名> <ブランチ名>       # リモートリポジトリの内容をローカルリポジトリに反映

$ git clone <リモートリポジトリ名> # リモートリポジトリの内容を複製する

 

   f:id:masashi_k:20190630003004p:plain

 

参考文献

以上6回の内容は以下の書籍を使って勉強しました。

新しいLinuxの教科書

新しいLinuxの教科書