深度學習之Matlab 轉C++在iOS上測試CNN手型識別

1 前言

在上一篇Blog,我介紹了在iOS上運行CNN的一些方法。但是,一般來說,我們需要一個性能強勁的機器來跑CNN,我們只不過需要將得到的結果用於移動端。之前在Matlab使用UFLDL的代碼修改後跑了手型識別的3層CNN,這裏我們就考慮將Matlab轉C之後移植到xcode中。

Step 1:Matlab 轉c

首先要保證代碼可以跑,可以運行,比如我這邊,如下測試cnn識別手型:
手型圖片

>> parameters = load('./opt_parameters/opttheta_8epoches_cnn.mat');
cnnPredict(imread('./data/test_five1 (1).bmp'),parameters.opttheta)
ans =

     5

大家可以看到,我識別出來是5個手指。OK,CNN沒有問題。現在就是要將cnnPredict函數轉c,這裏大家可以看到這個函數包含了輸入數據和已訓練的參數。

function labels = cnnPredict(images,opttheta)

基本方式是使用Matlab自帶的工具:coder。
在Command窗口輸入coder:
這裏寫圖片描述
新建一個項目:
這裏寫圖片描述
這裏我已經導入了我要轉的文件cnnPredict.m,裏面有兩個輸入變量,我需要定義其變量類型,這裏我使用autodefine types,就是寫一個腳本運行這個函數,就行。也就是我一開始貼的代碼,識別出來後是這樣:
這裏寫圖片描述
這裏大家可以看到我這邊CNN的參數並不是很多,也就是19萬個參數而已。
接下來就是build了,這裏選擇c/c++ static library,並且只輸出c code:
這裏寫圖片描述
build結果如下:
這裏寫圖片描述
有可能你會build失敗,這個時候可能是數據類型問題,可以根據具體情況進行修改到成功爲止。
生成的code在文件夾的codegen文件夾中:
這裏寫圖片描述

Step 2:將.Mat參數導出爲.txt格式

在訓練的時候,我們的cnn參數是存儲在.mat中,因此,爲了能在xcode中使用,我們需要將參數導出,這裏我選擇導出爲.txt格式。
導出方法非常簡單,一條代碼;

>> save('opttheta.txt','opttheta','-ASCII'); %將opt theta參數保存爲opttheta.txt

這裏寫圖片描述

Step 3: 新建iOS工程,導入cnnPredict代碼

這一步很簡單,把整個文件夾拉進來就OK了。
注意cnnPredict.h代碼,我們要用的也就是這裏面的函數了:

/*
 * File: cnnPredict.h
 *
 * MATLAB Coder version            : 2.7
 * C/C++ source code generated on  : 16-Jul-2015 16:22:01
 */

#ifndef __CNNPREDICT_H__
#define __CNNPREDICT_H__

/* Include Files */
#include <math.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include "rt_nonfinite.h"
#include "rtwtypes.h"
#include "cnnPredict_types.h"

/* Function Declarations */
extern double cnnPredict(const double images[9216], const double opttheta[195245]);

#endif

/*
 * File trailer for cnnPredict.h
 *
 * [EOF]
 */

注意的是導入運行裏面有個interface文件夾會導致運行失敗,應刪除之,不會影響其他。

Step 4 在Xcode中導入參數

這一步就是讀取txt文件中的數據並轉存爲double的數組,直接貼代碼:

NSString *filePath = [[NSBundle mainBundle] pathForResource:@"opttheta" ofType:@"txt"];
    NSString *testString = [NSString stringWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:nil];
    NSMutableArray *thetaString = (NSMutableArray *)[testString componentsSeparatedByString:@"\n"];
    [thetaString removeLastObject];
    NSLog(@"Theta1 count:%lu",(unsigned long)thetaString.count);
    for (int i = 0; i < thetaString.count; i++) {
        NSString *data = [thetaString objectAtIndex:i];
        theta[i] = [data doubleValue];
    }

從代碼中可以看到,就是用’\n’來分割數據,道理非常簡單。

Step 5 將圖片轉換爲double數組

爲了使用函數,我們必須將圖片轉換爲數組。我們這裏顯然是使用灰度圖片,轉換的代碼如下:

UIImage *image = [UIImage imageNamed:@"one.bmp"];


CGImageRef imageRef = [image CGImage];
CGDataProviderRef provider = CGImageGetDataProvider(imageRef);
NSData *data = (id)CFBridgingRelease(CGDataProviderCopyData(provider));
NSLog(@"image:%lu",(unsigned long)data.length);
const uint8_t *bytes = [data bytes];

這裏就轉換爲uint8的數組了,接下來我這邊根據需要對圖片的灰度矩陣需要進行轉置:

double newBytes[9216];

    for (int y = 0; y < 96 ; y++) {
        for (int x = 0; x < 96; x++) {
            newBytes[x*96 + y] = bytes[y*96 + x];
        }
    }

Step 6: 運行cnn

有了上面的處理,這一步直接運行cnnPredict

double result = cnnPredict(newBytes, theta);

NSLog(@"result:%f",result);

直接就輸出結果了:
這裏寫圖片描述
大家看到了嗎?識別出的結果爲1,就是大拇指的意思。
其實看到這裏,我自己都是有點激動的。特別爽是不是,iOS上運行的CNN直接識別手勢,雖然這邊的圖片是黑白的比較簡單一點。

小結

本文總結了如何將CNN的MATLAB代碼轉換爲C++代碼然後在iOS上直接運行的方法。希望對同道中人有啓發!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章