辞書型の要素に関数をいれると便利だった
プログラミングをしているときに、たくさん条件分岐したいっていう時ないですか?僕はあります。
それで例えばこんな感じにifとelifで繋げるとするじゃないですか
def f_a(): return 1 def f_b(): return 2 def f_c(): return 3 def f_d(): return 4 def f_e(): return 5 def f_f(): return 6 tmp_str = 'a' if tmp_str == 'a': print(f_a()) elif tmp_str == 'b': print(f_b()) elif tmp_str == 'c': print(f_c()) elif tmp_str == 'd': print(f_d()) elif tmp_str == 'f': print(f_f())
だらだらとしていて、見苦しいなーっと思いました。それでふと辞書にすればスッキリするんじゃないかと思って関数を要素にすればいいじゃんと気づきました。
関数ポインタを使ってこんな感じに書き直してみました。
def f_a(): return 1 def f_b(): return 2 def f_c(): return 3 def f_d(): return 4 def f_e(): return 5 def f_f(): return 6 tmp_str = 'a' func_tmp = {'a':f_a, 'b':f_b, 'c':f_c, 'd':f_d, 'e':f_e, 'f':f_f} print(func_tmp[tmp_str]())
結構スッキリした気がします。ただあまりこの書き方見ない気がするので、もしかすると良くないのかもしれない…そもそも凄い数の条件分岐がある時点でだめなんだろうなと思ったりした。
python + opencvで個人認識 その4
GW最終日、明日から海の日までしばらく祝日がないのでテンションが下がっています。テンションが下がりつつ、個人認識の続きを書きました。
今回は前回学習したモデルを用いて、自分の顔を認識させたいと思います。そして何番煎じか分かりませんが、認識させたとき自分の顔を笑い男の画像と置き換えたいと思います。
流れとしては以下を実装しました。
- USBカメラから画像を読み込む
- カスケード分類器を用いて、顔の候補領域を取り出す
- 候補領域を学習した識別器にかけ、本人か識別する
- 本人ならば笑い男の画像を合成する
画像の読み込みはその1で書いてあるため飛ばします。
顔の候補領域はopencvにあるカスケード分類器を用います。カスケード分類器に関しては以下のサイトを参考にしました。
http://opencv.jp/opencv-2.2/c/objdetect_cascade_classification.html
カスケード分類器に関するコードはこちらになります。デフォルトで用意されている特徴分類器を用います。
今回は正面顔と横顔用の特徴分類器を用いました。
def cascade_init(self): profilecascade_path = 'haarcascade_profileface.xmlのパス' frontalcascade_path = 'haarcascade_frontalface_alt2.xmlのパス' self.profile_cascade = cv2.CascadeClassifier(profilecascade_path) self.frontal_cascade = cv2.CascadeClassifier(frontalcascade_path) def execute_cascade(self, frame): profile_facerect = self.profile_cascade.detectMultiScale(frame, scaleFactor=1.1, minNeighbors=2, minSize=(1, 1)) frontal_facerect = self.frontal_cascade.detectMultiScale(frame, scaleFactor=1.1, minNeighbors=2, minSize=(1, 1)) return profile_facerect, frontal_facerect
次に候補領域から取り出した画像をcnnで自分の顔かどうかを識別させます。
CNNの設定でsimple_predictで画像一枚を入力として、識別結果を返すように設定していました。また今回は自分の顔と認識した中で最も大きい領域を自分の顔の領域としました。これは自分の顔周辺をいくつか候補として出した場合、自分の顔と識別する結果が複数得られるためです。
def cnn_init(self): TRAIN_BATCH = 100 TEST_BATCH = 90 self.cnn_net = TensorCNN(TRAIN_BATCH, TEST_BATCH, self.IMG_WIDTH, self.IMG_HEIGHT) self.cnn_net.net_init() def load_cnnmodel(self, path): self.cnn_net.load_model(path) def rect_cnn(self, imgs, rects): get_area = lambda tmprect: (tmprect[2] - tmprect[0]) * (tmprect[3] - tmprect[1]) myimg_rect = [] rect_area = [] for img,rect in zip(imgs,rects): res = self.predict_cnn(img) print(res) if np.argmax(res) == self.MYIMG_LABEL: self.exit_prob += 1 myimg_rect.append(rect) rect_area.append(get_area(rect)) return myimg_rect, rect_area def predict_cnn(self, img): input_img = cv2.resize(img,(self.IMG_WIDTH, self.IMG_HEIGHT)) input_img = np.reshape(input_img,(1,self.IMG_HEIGHT,self.IMG_WIDTH,self.CH)) predict_dict = {self.cnn_net.predict_input: input_img, self.cnn_net.keep_prob: 1.0} return self.cnn_net.sess.run(self.cnn_net.simple_predict, feed_dict=predict_dict)
最後に自分の顔画像があればそれを笑い男の画像に取り替えます。透過領域があるpng画像からマスクを生成して、合成しました。
def combine_img(self, frame, img, x,y): height_over_check = lambda x: self.HEIGHT if x > self.HEIGHT else x width_over_check = lambda x: self.WIDTH if x > self.WIDTH else x img = cv2.resize(img, (int(self.WIDTH/1.2), int(self.HEIGHT/1.2))) height,width = img.shape[:2] ex = width_over_check(x+width) ey = height_over_check(y+height) mask = img[:,:,3] #これでアルファチャンネルのみの行列が抽出。 mask = cv2.cvtColor(mask, cv2.COLOR_GRAY2BGR) mask = mask/255.0 img = img[:,:,:3] frame_float = frame.astype(np.float64) frame_float[y:ey, x:ex] *= 1 - mask[:(ey-y),:(ex-x)] frame_float[y:ey, x:ex] += img[:(ey-y),:(ex-x)] * mask[:(ey-y),:(ex-x)] return frame_float
これらを組み合わせると、自分の顔画像が見つかったときに笑い男の画像を貼り付けます。

ただ、今回過学習しているためか変な領域を自分の顔と認識したり、カメラからの距離に応じて認識精度が変わってしまいました。
対策としてはシンプルに画像量を増やすか、顔認識で優秀なモデルを借りて、追加で自分の顔を認識させるとかが考えられます。
また顔候補領域が顔の向きによっては出にくい、処理が重くてカクカクするなどの問題もありました。
やっぱり、顔認識とかの論文を読んだ方が良さそうだなというのが素直な感想です。
一応以下に全コードをあげておきます。
import cv2 import time import numpy as np from cnn import TensorCNN class FaceRecognition: def __init__(self, size): self.IMG_HEIGHT = 64 self.IMG_WIDTH = 64 self.CH = 3 self.MARGIN = 0 self.MYIMG_LABEL = 1 self.WIDTH = size[0] self.HEIGHT = size[1] self.exit_prob = 0 self.judge = 0 def cnn_init(self): TRAIN_BATCH = 100 TEST_BATCH = 90 self.cnn_net = TensorCNN(TRAIN_BATCH, TEST_BATCH, self.IMG_WIDTH, self.IMG_HEIGHT) self.cnn_net.net_init() self.cnn_recoglog = [] def load_cnnmodel(self, path): self.cnn_net.load_model(path) print('CNN model downloaded is success') def rect_cnn(self, imgs, rects): get_area = lambda tmprect: (tmprect[2] - tmprect[0]) * (tmprect[3] - tmprect[1]) myimg_rect = [] rect_area = [] for img,rect in zip(imgs,rects): res = self.predict_cnn(img) print(res) if np.argmax(res) == self.MYIMG_LABEL: self.exit_prob += 1 myimg_rect.append(rect) rect_area.append(get_area(rect)) return myimg_rect, rect_area def predict_cnn(self, img): input_img = cv2.resize(img,(self.IMG_WIDTH, self.IMG_HEIGHT)) input_img = np.reshape(input_img,(1,self.IMG_HEIGHT,self.IMG_WIDTH,self.CH)) predict_dict = {self.cnn_net.predict_input: input_img, self.cnn_net.keep_prob: 1.0} return self.cnn_net.sess.run(self.cnn_net.simple_predict, feed_dict=predict_dict) #profile 横顔 #frontalface 正面顔 def cascade_init(self): profilecascade_path = 'haarcascade_profileface.xmlへのパス' frontalcascade_path = 'haarcascade_frontalface_alt2.xmlへのパス' self.profile_cascade = cv2.CascadeClassifier(profilecascade_path) self.frontal_cascade = cv2.CascadeClassifier(frontalcascade_path) self.pro_cascade_log = [] self.front_cascade_log = [] print('Cascade init succeded...') def execute_cascade(self, frame): profile_facerect = self.profile_cascade.detectMultiScale(frame, scaleFactor=1.1, minNeighbors=2, minSize=(20, 20)) frontal_facerect = self.frontal_cascade.detectMultiScale(frame, scaleFactor=1.1, minNeighbors=2, minSize=(20, 20)) return profile_facerect, frontal_facerect #return frontal_facerect def set_cascade_log(self, pro_facerect, fro_facerect): #if len(pro_facerect) > 0: # self.pro_cascade_log = [rect for rect in pro_facerect] if len(fro_facerect) > 0: self.front_cascade_log = [rect for rect in fro_facerect] def set_cnn_rectlog(self, rects, areas): self.judge += 1 if len(rects) > 0: self.exit_prob += 1 self.cnn_recoglog = [] index = np.argmax(areas) self.cnn_recoglog = rects[index] #候補領域周辺から取得する def sample_img(self, frame): img_list = [] rect_list = [] if len(self.pro_cascade_log) > 0: for rect in self.pro_cascade_log: img_list.append(self.rect2img(rect, frame)) rect_list.append(rect) if len(self.front_cascade_log) > 0: for rect in self.front_cascade_log: img_list.append(self.rect2img(rect, frame)) rect_list.append(rect) if len(self.cnn_recoglog) > 0: img_list.append(self.rect2img(self.cnn_recoglog, frame)) rect_list.append(rect) return img_list,rect_list def rect2img(self,rect,frame): zero_check = lambda x: 0 if x < 0 else x height_over_check = lambda x: self.HEIGHT if x > self.HEIGHT else x width_over_check = lambda x: self.WIDTH if x > self.WIDTH else x sx = zero_check(rect[0] - self.MARGIN) sy = zero_check(rect[1] - self.MARGIN) ex = width_over_check(rect[0] + rect[2] + self.MARGIN) ey = height_over_check(rect[1] + rect[3] + self.MARGIN) return frame[sy:ey,sx:ex] def combine_img(self, frame, img, x,y): zero_check = lambda x: 0 if x < 0 else x height_over_check = lambda x: self.HEIGHT if x > self.HEIGHT else x width_over_check = lambda x: self.WIDTH if x > self.WIDTH else x bias = 200 x = zero_check(x-bias) y = zero_check(y-bias) img = cv2.resize(img, (int(self.WIDTH/1.2), int(self.HEIGHT/1.2))) height,width = img.shape[:2] ex = width_over_check(x+width) ey = height_over_check(y+height) mask = img[:,:,3] #これでアルファチャンネルのみの行列が抽出。 mask = cv2.cvtColor(mask, cv2.COLOR_GRAY2BGR) #maskを3色分にした。0 ->(B0, G0, R0)になる。 mask = mask/255.0 #正規化 img = img[:,:,:3] frame_float = frame.astype(np.float64) frame_float[y:ey, x:ex] *= 1 - mask[:(ey-y),:(ex-x)] frame_float[y:ey, x:ex] += img[:(ey-y),:(ex-x)] * mask[:(ey-y),:(ex-x)] return frame_float def capture_camera(self,size=None): cap_flag = False cv2.namedWindow("Get_faceimg") cap = cv2.VideoCapture(0) #-1をつけることでαチャネルを読み込む waraiotoko = cv2.imread('warai_flat.png',-1) check_rate = 0 while True: # retは画像を取得成功フラグ ret, frame = cap.read() frame = cv2.resize(frame, (self.WIDTH,self.HEIGHT)) check_rate += 1 #毎フレーム処理すると重いので適当なタイミングで検出処理を行う if check_rate > 3: image_gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) pro_facerect,fro_facerect = self.execute_cascade(image_gray) self.set_cascade_log(pro_facerect,fro_facerect) check_rate = 0 imgs,rects = self.sample_img(frame) rects, areas = self.rect_cnn(imgs, rects) self.set_cnn_rectlog(rects, areas) if len(self.cnn_recoglog) > 0 and self.exit_prob/self.judge > 0.8: frame = self.combine_img(frame,waraiotoko, self.cnn_recoglog[0],self.cnn_recoglog[1]) frame= frame.astype(np.uint8) cv2.imshow('camera capture', frame) k = cv2.waitKey(10) # 10msec待つ if k == 27: # ESCキーで終了 break if k == 0x73: cv2.imwrite('result/' + 'res' + '.png',frame) # キャプチャを解放する cap.release() cv2.destroyAllWindows() if __name__ == '__main__': face_recog = FaceRecognition(size = (800,600)) face_recog.cascade_init() face_recog.cnn_init() face_recog.load_cnnmodel('models/') face_recog.capture_camera()
python + opencvで個人認識 その3
前回、前々回で学習データの取得を行いました。実は前回、前々回の方法だと大量のデータを取ってくるのには向いていないんですよね。自画像の方はムービーにしてひたすら撮りまくれば増やせるんですが、スクレイピングの方が一度に20枚しか取れませんでした。google画像検索で何か対策されているっぽいんですよね。スクレイピングを改良してデータを増やすのもいいと思ったんですが、折角学習させるということで今回はデータを水増しさせます。
確かその1で水増しはしないと言っていましたが、あれは嘘です。なので今回はデータを水増しさせて、それで学習を行い自分の顔を認識できるのかを確認していきたいと思います。
今回水増し処理として、ガウス分布に基づくノイズ、ごま塩ノイズ、平滑化処理、アフィン変換を行いました。
水増しを行う処理はopencvで以下のように書きました。
import cv2 import numpy as np class DataPadder: def padding_gammmanoise(self, img, mean=0, sigma=15): src = img row,col,ch= src.shape gauss = np.random.normal(mean,sigma,(row,col,ch)) gauss = gauss.reshape(row,col,ch) gauss_img = src + gauss return gauss_img def padding_saltpappernoise(self, img, s_vs_p=0.5, amount=0.004): src = img row,col,ch = src.shape sp_img = src.copy() # 塩モード num_salt = np.ceil(amount * src.size * s_vs_p) coords = [np.random.randint(0, i-1 , int(num_salt)) for i in src.shape] sp_img[coords[:-1]] = (255,255,255) # 胡椒モード num_pepper = np.ceil(amount* src.size * (1. - s_vs_p)) coords = [np.random.randint(0, i-1 , int(num_pepper)) for i in src.shape] sp_img[coords[:-1]] = (0,0,0) return sp_img def padding_smoothing(self, img, mask): src = img blur_img = cv2.blur(src, mask) return blur_img def padding_affine(self, img, rotate, scale=1.0): src = img row,col,ch = src.shape sp_img = src.copy() center = (row/2,col/2) matrix = cv2.getRotationMatrix2D(center, rotate, scale) affine = cv2.warpAffine(sp_img, matrix, (row,col)) return affine
それでこれを用いて画像を水増しします。こんな風に
from datapadder import DataPadder from dataread import DataRead import cv2 import time dr = DataRead() padder = DataPadder() dr.write_pathlabel('dataset','path.txt') imgdatas = dr.read_imgdata('path.txt',(64,64),2) myface_img = [] face_img = [] rotates = [45,90,135,180,225,270,315] #ノイズ、ごま塩、回転(45,90,135,180,225,270,315) for img,label in imgdatas: if label[1] == 1: myface_img.append(padder.padding_gammmanoise(img)) myface_img.append(padder.padding_saltpappernoise(img)) myface_img.append(padder.padding_smoothing(img, (5,5))) for rotate in rotates: myface_img.append(padder.padding_affine(img, rotate)) else: face_img.append(padder.padding_gammmanoise(img)) face_img.append(padder.padding_saltpappernoise(img)) face_img.append(padder.padding_smoothing(img, (5,5))) for rotate in rotates: face_img.append(padder.padding_affine(img, rotate)) now = int(time.time()) for i,img in enumerate(myface_img): cv2.imwrite('paddimg/myface/' + str(now) + str(i) + '.png',img) for i,img in enumerate(face_img): cv2.imwrite('paddimg/face/' + str(now) + str(i) + '.png',img)
そうすると、こんな感じの画像たちから

こんなのができます。

いきなりインド人が現れていますが、写ってないだけで元の画像にちゃんといます。
さて、これでようやく学習を行うためのデータが集まりました。待ちに待った学習を行っていきます。
今回はTensorflowでCNNを学習させます。
二層のcnnと全結合層をtensorflowで実装しました。以下、cnnの実装です。
import tensorflow as tf import numpy as np class TensorCNN: def __init__(self, train_batch, test_batch, width, height): # Set model parameters self.trainbatch_size = train_batch self.testbatch_size = test_batch self.learning_rate = 0.01 self.image_width = width self.image_height = height self.target_size = 2 self.num_channels = 3 # greyscale = 1 channel self.conv1_features = 20 self.conv1_ksize = 4 self.conv2_features = 30 self.conv2_ksize = 4 self.max_pool_size1 = 2 # NxN window for 1st max pool layer self.max_pool_size2 = 2 # NxN window for 2nd max pool layer self.fully_connected_size1 = 100 def save_model(self,path): saver = tf.train.Saver() saver.save(self.sess, path) def load_model(self,path): saver = tf.train.Saver() saver.restore(self.sess,path) def net_init(self): self.sess = tf.Session() with tf.variable_scope("net") as scope: self.val_init() self.weight_init() self.model_init() self.loss_init() self.predict_init() self.optimizer_init() # Initialize Variables init = tf.global_variables_initializer() self.sess.run(init) def end(self): self.sess.close() def train_run(self,batch_x,batch_t): train_dict = {self.x_input:batch_x,self.y_target:batch_t,self.keep_prob:0.5} self.sess.run(self.train_step,feed_dict=train_dict) temp_train_loss, temp_train_preds = self.sess.run([self.loss, self.prediction], feed_dict=train_dict) temp_train_acc = self.get_accuracy(temp_train_preds, batch_t) return temp_train_acc,temp_train_loss def test_run(self,batch_testx,batch_testt): test_dict = {self.eval_input: batch_testx, self.eval_target: batch_testt,self.keep_prob:1.0} temptest_loss,temptest_preds = self.sess.run([self.test_loss,self.test_prediction], feed_dict=test_dict) temp_test_acc = self.get_accuracy(temptest_preds, batch_testt) return temp_test_acc,temptest_loss def val_init(self): x_input_shape = (self.trainbatch_size, self.image_width, self.image_height, self.num_channels) self.x_input = tf.placeholder(tf.float32, x_input_shape) self.y_target = tf.placeholder(tf.int32, (self.trainbatch_size,self.target_size)) eval_input_shape = (self.testbatch_size, self.image_width, self.image_height, self.num_channels) self.eval_input = tf.placeholder(tf.float32, shape=eval_input_shape) self.eval_target = tf.placeholder(tf.int32, shape=(self.testbatch_size, self.target_size)) predict_input_shape = (1, self.image_width, self.image_height, self.num_channels) self.predict_input = tf.placeholder(tf.float32, shape=predict_input_shape) self.predict_target = tf.placeholder(tf.int32, shape=(1,self.target_size)) def weight_init(self): # Convolutional layer variables conv1_initval = np.sqrt(1/self.image_height * self.image_width) self.conv1_weight = tf.Variable(tf.truncated_normal([ self.conv1_ksize,self.conv1_ksize, self.num_channels, self.conv1_features], stddev=conv1_initval, dtype=tf.float32)) self.conv1_bias = tf.Variable(tf.zeros( [self.conv1_features], dtype=tf.float32)) num_units = ((self.image_height - self.conv1_ksize)/2) ** 2 conv2_initval = np.sqrt(1/num_units) self.conv2_weight = tf.Variable(tf.truncated_normal( [self.conv2_ksize,self.conv2_ksize, self.conv1_features, self.conv2_features], stddev=conv2_initval, dtype=tf.float32)) self.conv2_bias = tf.Variable( tf.zeros([self.conv2_features], dtype=tf.float32)) # fully connected variables resulting_width = self.image_width // (self.max_pool_size1 + self.max_pool_size2) resulting_height = self.image_height // (self.max_pool_size1 + self.max_pool_size2) full1_input_size = resulting_width * resulting_height * self.conv2_features full1_initval = np.sqrt( 1/((full1_input_size)/2 + 1) ) self.full1_weight = tf.Variable(tf.truncated_normal( [full1_input_size, self.fully_connected_size1], stddev=full1_initval, dtype=tf.float32)) self.full1_bias = tf.Variable(tf.truncated_normal( [self.fully_connected_size1], stddev=0.1, dtype=tf.float32)) self.full2_weight = tf.Variable(tf.truncated_normal( [self.fully_connected_size1, self.target_size], stddev=np.sqrt(1/self.fully_connected_size1), dtype=tf.float32)) self.full2_bias = tf.Variable( tf.truncated_normal([self.target_size], stddev=0.1, dtype=tf.float32)) #dropout self.keep_prob = tf.placeholder(tf.float32) # Initialize Model Operations def my_conv_net(self,input_data): # First Conv-ReLU-MaxPool Layer conv1 = tf.nn.conv2d(input_data, self.conv1_weight, strides=[1, 1, 1, 1], padding='SAME') relu1 = tf.nn.relu(tf.nn.bias_add(conv1, self.conv1_bias)) max_pool1 = tf.nn.max_pool(relu1, ksize=[1, self.max_pool_size1, self.max_pool_size1, 1], strides=[1, 2, 2, 1], padding='SAME') # Second Conv-ReLU-MaxPool Layer conv2 = tf.nn.conv2d( max_pool1, self.conv2_weight, strides=[1, 1, 1, 1], padding='SAME') relu2 = tf.nn.relu(tf.nn.bias_add(conv2, self.conv2_bias)) max_pool2 = tf.nn.max_pool( relu2, ksize=[1, self.max_pool_size2, self.max_pool_size2, 1], strides=[1, 2, 2, 1], padding='SAME') self.max_pool2 = max_pool2 # Transform Output into a 1xN layer for next fully connected layer final_conv_shape = max_pool2.get_shape().as_list() final_shape = final_conv_shape[1] * final_conv_shape[2] * final_conv_shape[3] flat_output = tf.reshape(max_pool2, [final_conv_shape[0], final_shape]) # First Fully Connected Layer fully_connected1 = tf.nn.relu(tf.add(tf.matmul(flat_output, self.full1_weight), self.full1_bias)) fully_connected1 = tf.nn.dropout(fully_connected1,self.keep_prob) # Second Fully Connected Layer final_model_output = tf.add( tf.matmul(fully_connected1, self.full2_weight), self.full2_bias) self.fc2 = final_model_output return(final_model_output) def model_init(self): self.model_output = self.my_conv_net(self.x_input) self.test_model_output = self.my_conv_net(self.eval_input) self.predict_model_output = self.my_conv_net(self.predict_input) def loss_init(self): # Declare Loss Function (softmax cross entropy) self.loss = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(logits=self.model_output, labels=self.y_target)) self.test_loss = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(logits=self.test_model_output, labels=self.eval_target)) def predict_init(self): # Create a prediction function self.prediction = tf.nn.softmax(self.model_output) self.test_prediction = tf.nn.softmax(self.test_model_output) self.simple_predict = tf.nn.softmax(self.predict_model_output) # Create accuracy function def get_accuracy(self,logits, targets): batch_predictions = np.argmax(logits, axis=1) num_correct = np.sum(np.equal(batch_predictions, np.argmax(targets, axis=1))) return(100. * num_correct/batch_predictions.shape[0]) def optimizer_init(self): my_optimizer = tf.train.MomentumOptimizer(self.learning_rate, 0.9) self.train_step = my_optimizer.minimize(self.loss)
このcnnのクラスを用いて学習させた結果を下図に載せます。それぞれ、正答率と損失関数になります。


収束しているみたいです。ただ、元々の画像の量が少なかったので実際に使ってみて個人認識ができるのかどうか気になるところです。
一応学習させ、プロットしたときのコードを載せておきます。
import matplotlib.pyplot as plt import numpy as np from cnn import TensorCNN from dataread import DataRead IMG_HEIGHT = 64 IMG_WIDTH = 64 CH_NUM = 3 TARGET_SIZE = 2 TRAIN_BATCH = 100 TEST_BATCH = 90 EPOCHS = 30 dr = DataRead() dr.write_pathlabel('dataset','path.txt') dr.import_data('path.txt',(IMG_HEIGHT,IMG_WIDTH),TARGET_SIZE) data = dr.get_data() TRAIN_DATA_SIZE = int(len(data) * 0.8) TRAIN_DATA_SET = data[:TRAIN_DATA_SIZE] TEST_DATA_SET = data[TRAIN_DATA_SIZE:] cnn_net = TensorCNN(TRAIN_BATCH, TEST_BATCH, IMG_WIDTH, IMG_HEIGHT) cnn_net.net_init() def deivide_datasets(data_sets): data_set = np.array(data_sets) img_data_set = data_set[:int(len(data_set)), :1].flatten() label_data_set = data_set[:int(len(data_set)), 1:].flatten() image_ndarray = np.empty((0, IMG_HEIGHT, IMG_WIDTH, CH_NUM)) label_ndarray = np.empty((0,TARGET_SIZE)) for (img, label) in zip(img_data_set, label_data_set) : image_ndarray = np.append(image_ndarray, np.reshape(img, [1, IMG_HEIGHT, IMG_WIDTH, CH_NUM]), axis=0) label_ndarray = np.append(label_ndarray, np.reshape(label, (1, TARGET_SIZE)), axis=0) return image_ndarray, label_ndarray def get_batch_dataset(img_dataset, label_dataset, indexes): img_len = IMG_HEIGHT * IMG_WIDTH * CH_NUM img_ndarray = np.empty((0, IMG_HEIGHT, IMG_WIDTH, CH_NUM)) label_ndarray = np.empty((0, TARGET_SIZE)) for index in indexes: img_ndarray = np.append(img_ndarray, np.reshape(img_dataset[index], [1, IMG_HEIGHT, IMG_WIDTH, CH_NUM]), axis=0) label_ndarray = np.append(label_ndarray, np.reshape(label_dataset[index], [1,TARGET_SIZE]), axis=0) return img_ndarray, label_ndarray train_imgdata, train_label = deivide_datasets(TRAIN_DATA_SET) test_imgdata, test_label = deivide_datasets(TEST_DATA_SET) epochlogs = [] for i in range(EPOCHS): epochres = [] #ミニバッチ学習 train_perm = np.random.permutation(len(train_imgdata)) test_perm = np.random.permutation(len(test_imgdata)) train_accuracy = 0 train_loss = 0 test_accuracy = 0 test_loss_ = 0 cnt = 0 for j in range(0,(len(train_imgdata) - TRAIN_BATCH),TRAIN_BATCH): batch_x, batch_t = get_batch_dataset(train_imgdata, train_label, train_perm[j:j+TRAIN_BATCH]) temp_train_acc,temp_train_loss = cnn_net.train_run(batch_x,batch_t) train_loss += temp_train_loss train_accuracy += temp_train_acc cnt += 1 print('%d epoch Accuracy = %.2f%% Loss = %.3e'%(i,train_accuracy/cnt,train_loss/cnt)) epochres.append(train_accuracy/cnt) epochres.append(train_loss/cnt) cnt = 0 for j in range(0,len(test_imgdata) - TEST_BATCH,TEST_BATCH): batch_testx, batch_testt = get_batch_dataset(test_imgdata, test_label, test_perm[j:j+TEST_BATCH]) temp_test_acc,temp_test_loss = cnn_net.test_run(batch_testx,batch_testt) test_loss_ += temp_test_loss test_accuracy += temp_test_acc cnt += 1 print('%d epoch TestAccuracy = %.2f%% TestLoss = %.3e'%(i,test_accuracy/cnt,test_loss_/cnt)) epochres.append(test_accuracy/cnt) epochres.append(test_loss_/cnt) epochlogs.append(epochres) #cnn_net.save_model('models/') import matplotlib.ticker as ticker trainresult = np.array(epochlogs) x_epochs = np.array(np.arange(len(epochlogs))) plt.plot(x_epochs,trainresult[:,0],label='main',color='b') plt.plot(x_epochs,trainresult[:,2],label='validation',color='g') plt.ylabel('accuracy[%]') plt.xlabel('epochs') plt.gca().xaxis.set_major_locator(ticker.MaxNLocator(integer=True)) plt.legend() plt.show() plt.plot(trainresult[:,1],label='main',color='b') plt.plot(trainresult[:,3],label='validation',color='g') plt.ylabel('loss') plt.xlabel('epochs') plt.gca().xaxis.set_major_locator(ticker.MaxNLocator(integer=True)) plt.legend() plt.show()
次回は保存した学習モデルを使って、カメラの映像から個人認識をしてみたいと思います。
espをpythonで動かすのが一瞬だったお話
組み込みでpython使えたら楽なんだけどなーっと思ってたらmicropythonという代物があるらしい。(何を今更という人がたくさんいると思う)
何とespとstmマイコン系列で動くらしい、まじかよとなった。実際に本当であり、何と数分で動かせて感動しました。
ちなみにファームウェアと動かせるファームウェアはmicropythonの公式にあるっぽい。
micropython.org
早速動かしてみようと思った。ちなみに私はEsp32のdevkitを用いました。
akizukidenshi.com
環境はwindowsでいけます、ということはMacやlinuxでもいけます。
まずファームウェアを先ほどのサイトから拾ってきます。次にファームを書き込むためにesptoolをインストールします。
pip install esptool
接続して、windowsの人はデバイスマネージャーからespのシリアルの設定をボーレートを115200に設定しましょう。
(これしなくてもいいかも)
それでダウンロードしてきたファームウェアのバイナリを書き込みます。
esptool --chip esp32 --port COM6 --baud 115200 write_flash -z 0x1000 esp32-20180423-v1.9.3-552-gb5ee3b2f.bin
ではteratermでシリアル通信しましょう。ボーレートは115200です。そうすると対話モードが立ち上がりpythonが叩けます、すげーー

ちなみにmicropythonのドキュメントがこちらになります。使えるライブラリとかも書いてあります、結構標準ライブラリ使えるんですね。
http://docs.micropython.org/en/latest/esp8266/esp8266/tutorial/pins.html
micropythonすごかったのですが、まだあまり理解していないのでまずはドキュメントとかを読んでいきたいと思います。
python + opencvで個人認識 その2
前回は自分の顔をopencvで保存しました。
今回はcnnを使う前にデータセットの自作とスクレイピングで画像を集めたいと思います。
データセットの自作ではtensorflow等で取り扱えるような形にしたいと思います。今回はデータの水増し等はやらないですが、そのうち水増しのコードも書きたいと思います。
スクレイピングでは自分の顔以外の適当な画像データを集めます。学習データが自分の顔だけより、他の画像データを持ってくることでcnnの汎化性能が上がる気がしたからです。まあ、個人認識の論文とか読んでないので分からないですので間違ってたら教えてくれると嬉しいです。
まずはデータセットの自作からです。こちらのサイトを参考にしました。
qiita.com
各クラスのディレクトリを収納した親ディレクトリのパスとクラス数と画像のサイズを指定するとデータセットを作成します。
以下ソースコードになります。
import cv2 import os import random import numpy as np class DataRead: def write_pathlabel(self, path,pathfile): dir_list = os.listdir(path) with open(pathfile,"w") as file: for y,dir_name in enumerate(dir_list): files = os.listdir(path+"\\"+dir_name) f_list = [path + "\\" + dir_name + "\\" + f + " " + str(y) + "\n" for f in files if os.path.isfile(os.path.join(path+"\\"+dir_name,f))] file.writelines(f_list) def read_data(self, pathfile): with open(pathfile,"r") as file: self.PATH_AND_LABEL = [(line.rstrip()).split() for line in file] random.shuffle(self.PATH_AND_LABEL) def import_data(self, pathfile, imgshapes, numclass): self.read_data(pathfile) self.DATA_SET = [] for path_label in self.PATH_AND_LABEL: if len(path_label) !=2: continue img = cv2.imread(path_label[0]) img = cv2.resize(img,imgshapes) img = img.flatten().astype(np.float32)/255.0 label_ary = np.zeros(numclass, dtype = 'float64') label_ary[int(path_label[1])] = 1 self.DATA_SET.append([img,label_ary]) def get_data(self): return self.DATA_SET if __name__ == "__main__": dr = DataRead() dr.write_pathlabel('test','path.txt') dr.import_data('path.txt',(64,64),2) print(dr.get_data())
次にスクレイピングで画像を取得していきます。BeautifulSoup4とrequestsを用いています。
ページ内のimgタグを取得してきて、requestsを用いて画像をダウンロードします。
import requests import urllib import os from bs4 import BeautifulSoup class Scraper: def get_imglinks(self,url): self.imgs = [] res = requests.get(url) content = res.content soup = BeautifulSoup(content, 'html.parser') links = soup.find_all("img") img_links = [link.get("src") for link in links] return img_links def download(self,keyword,urls): if os.path.exists(keyword) == False: os.mkdir(keyword) for i,url in enumerate(urls): fn,ext = os.path.splitext(url) res = requests.get(url, allow_redirects=False) if res.status_code != 200: print(res.status_code) continue if 'image' not in res.headers["content-type"]: print(res.headers["content-type"]) continue with open(keyword + "/" + str(i) + ext, "wb" ) as f: f.write(res.content) if __name__ == "__main__": scraper = Scraper() links = scraper.get_imglinks('https://www.google.co.jp/search?q=rwby&source=lnms&tbm=isch&sa=X&ved=0ahUKEwivruumkM3aAhXDqJQKHZixBLUQ_AUICygC&biw=1812&bih=954') scraper.download('rwby',links)
これでデータセットの作成と学習用のデータを集められるようになったので次回こそ学習させる予定です。
Django2.0 ファイルアップロードについて
Django2.0でファイルアップロードの仕方で少しだけ詰まったのでメモしておきます。
まず最初にディレクトリ構成はこんな感じです。
├─.vscode
├─apps
│ ├─media
│ │ └─thumbnail
│ ├─products
│ │ └─migrations
│ ├─static
│ │ └─vendor
│ │ └─bootstrap
│ │ ├─css
│ │ ├─img
│ │ └─js
│ └─templates
│ ├─accounts
│ └─products
├─config
└─docs
今回はmediaの中のthumbnailに画像を保存することを目的としました。
まずmodel.pyから
from django.db import models class Product(models.Model): title = models.CharField(max_length=140) thumbnail = models.FileField(upload_to='thumbnail/', blank=True, null=True) sentence = models.TextField()
次にform.py
class ProductForm(forms.ModelForm): class Meta: model = Product fields = ("title","thumbnail","sentence")
views.py
request.FILESというディレク処理の中にファイルのデータが入っているので、フォームへデータを受け渡します。
https://docs.djangoproject.com/ja/2.0/topics/http/file-uploads/
from django.shortcuts import render, redirect from django.views.generic import CreateView from .models import Product from .forms import ProductForm from django.urls import reverse, reverse_lazy class ProductCreateView(CreateView): template_name = "products/product_create_form.html" form_class = ProductForm model = Product success_url = reverse_lazy('products') def post(self, request, *args, **kwargs): form = self.form_class(request.POST, request.FILES) if form.is_valid(): print('VALID') form.save() return redirect(reverse('products')) print('Not valid') return redirect(reverse('post'))
そしてフォームのproduct_create_form.htmlですが、enctype="multipart/form-data"を入れ忘れないようにしましょう。
私はこれを入れ忘れてかれこれ3時間くらい格闘する羽目になりました...
mugenup-tech.hatenadiary.com
{% extends "base.html" %}
{%block body%}
<h1>新規作成</h1>
<form action="" method="post" enctype="multipart/form-data" data-ajax="false">
{% csrf_token %}
<table class="table">
{{ form }}
</table>
<button class="btn btn-primary" type="submit">送信</button>
</form>
{% endblock %}
そしてsettings.pyに以下を追加します。
MEDIA_ROOT = os.path.join(BASE_DIR,'carpediem','media') MEDIA_URL = '/carpediem/media/'
これでファイルをアップロードできるようになりました。
python+opencvで個人認識 その1
個人認識したい
ちょっと個人認識したいなと思ったので、何はともあれ学習データを集めるところから始める。
使ったものは
取りあえず自分を認識させたいので自分の顔画像を集めるところから始める。
最初は自宅の背景を

良い感じに消して顔写真を撮りまくろうと思った。
元々撮影した背景と各フレームごとのデータを引き算してあげればいい感じになるのではと思って
def delete_background(frame,bcg): src_img = frame - bcg src_img[src_img <= 100] = 0 print(src_img) return src_img
こんな関数を作った。ただ、

こんな感じになってしまい、綺麗に消せなかった。
だから結局は適当な範囲を抽出して画像を保存することにした。
以下ソースコード、
import cv2 import time import numpy as np def delete_background(frame,bcg): src_img = frame - bcg src_img[src_img <= 100] = 0 print(src_img) return src_img def capture_camera(mirror=False, size=None): img_no = 1 cv2.namedWindow("Get_faceimg") cap = cv2.VideoCapture(0) background = cv2.imread('img/bcg.png',cv2.IMREAD_COLOR) while True: now = int(time.time()) # retは画像を取得成功フラグ ret, frame = cap.read() # 鏡のように映るか否か if mirror is True: frame = frame[:,::-1] # フレームをリサイズ # sizeは例えば(800, 600) if size is not None and len(size) == 2: frame = cv2.resize(frame, size) width = frame.shape[1] height = frame.shape[0] rect_startx = int(width/4) rect_endx = int(3*width/4) rect_starty = int(height/8) rect_endy = int(7*height/8) cv2.rectangle(frame, (rect_startx-1, rect_starty-1), (rect_endx+1, rect_endy+1), (0, 0, 255), 1) #frame = frame - background #frame = delete_background(frame,background) # フレームを表示する cv2.imshow('camera capture', frame) cv2.imshow('save capture', frame[rect_starty:rect_endy,rect_startx:rect_endx]) k = cv2.waitKey(10) # 10msec待つ if k == 27: # ESCキーで終了 break elif k == 0x73: cv2.imwrite('faceimg/' + str(now) + '.png',frame[rect_starty:rect_endy,rect_startx:rect_endx]) elif k == 32: delete_background(frame,background) #cv2.imwrite('img/bcg.png',frame) # キャプチャを解放する cap.release() cv2.destroyAllWindows() if __name__ == '__main__': capture_camera(size = (800,600))
背景を消すところはrgbじゃなくてhsvで処理すべきだったかもしれない。久しぶりに画像を扱うので色々とセオリーを忘れている感がいなめない。
次辺りは適当に画像データを集めて、今回の顔写真を使って識別器を作っていきたい。svmか流行りのcnnのどちらかを使おうと思っている。