TensorFlow IOS調(diào)用

iOS相比Android移植TensorFlow沒那么方便,要用C++來編寫,接下來講一下iOS調(diào)用TensorFlow的過程洽沟。

  • 引入依賴

在Podfile中加入pod 'TensorFlow-experimental'攘宙,再在terminal中cd進(jìn)項(xiàng)目目錄輸入pod install即可安裝依賴。

  • 復(fù)制PB文件

快速開發(fā)的話直接把PB文件放在data文件夾里就行划煮,如果正式上線的時(shí)候覺得PB文件一起打包較大的話可以放在服務(wù)器送丰,打開APP的時(shí)候提示下載再?gòu)?fù)制進(jìn)去就好。

  • 引入頭文件弛秋、命名空間
#import <opencv2/imgcodecs/ios.h>
#include "tensorflow/cc/ops/const_op.h"
#include "tensorflow/core/framework/op_kernel.h"
#include "tensorflow/core/public/session.h"
#include <tensorflow/core/kernels/reshape_op.h>
#include <tensorflow/core/kernels/argmax_op.h>

using namespace tensorflow;
using namespace tensorflow::core;
  • 處理數(shù)據(jù)

圖像處理相比于Android的bitmap操作還是較為麻煩器躏,iOS需要用到opencv,所以還需要引入opencv的依賴蟹略,通過cv的UIImageToMat方法吧UIImage轉(zhuǎn)成cv::Mat再進(jìn)行矩陣操作(類似:灰度處理登失、歸一化、平展)

UIImage *image = [UIImage imageNamed:@"OOLU8095571.jpg"];
    self.preImageView.contentMode = UIViewContentModeRedraw;
    UIImageToMat(image,cvMatImage);
    cvMatImage.convertTo(cvMatImage, CV_32F, 1.0/255., 0);//歸一化
    cv::Mat reshapeMat= cvMatImage.reshape(0,1);//reshape
    NSString* inference_result = RunModel(reshapeMat);
    self.urlContentTextView.text = inference_result;

RunModel(reshapeMat)就是把處理過的數(shù)據(jù)傳遞給TensorFlow去運(yùn)算了挖炬。

  • 定義常量

這里跟Android差不多揽浙,定義一些必要的常量,輸入輸出節(jié)點(diǎn)茅茂,輸出輸出節(jié)點(diǎn)數(shù)據(jù)捏萍,圖像尺寸、通道等

    std::string input_layer = "inputs/X";
    std::string output_layer = "output/predict";
    tensorflow::Tensor x(
                         tensorflow::DT_FLOAT,
    tensorflow::TensorShape({wanted_height*wanted_width}));
    std::vector<tensorflow::Tensor> outputs;    
    const int wanted_width = 256;
    const int wanted_height = 64;
    const int wanted_channels = 1;
  • 創(chuàng)建session

這里跟Android不同空闲,需要手動(dòng)創(chuàng)建session

    tensorflow::SessionOptions options;
    
    tensorflow::Session* session_pointer = nullptr;
    tensorflow::Status session_status = tensorflow::NewSession(options, &session_pointer);
    if (!session_status.ok()) {
        std::string status_string = session_status.ToString();
        return [NSString stringWithFormat: @"Session create failed - %s",
                status_string.c_str()];
    }
    std::unique_ptr<tensorflow::Session> session(session_pointer);
  • 載入graph
    tensorflow::GraphDef tensorflow_graph;
    NSString* network_path = FilePathForResourceName(@"rounded_graph", @"pb");
    PortableReadFileToProto([network_path UTF8String], &tensorflow_graph);
    
    tensorflow::Status s = session->Create(tensorflow_graph);
    if (!s.ok()) {
        LOG(ERROR) << "Could not create TensorFlow Graph: " << s;
        return @"";
    }

其中FilePathForResourceName是返回graph的地址

NSString* FilePathForResourceName(NSString* name, NSString* extension) {
    NSString* file_path = [[NSBundle mainBundle] pathForResource:name ofType:extension];
    if (file_path == NULL) {
        LOG(FATAL) << "Couldn't find '" << [name UTF8String] << "."
           << [extension UTF8String] << "' in bundle.";
    }
    return file_path;
}

PortableReadFileToProto是把graph讀到內(nèi)存中并賦值給tensorflow_graph令杈,并使用session->Create(tensorflow_graph)把graph載入到session中。

  • 輸入數(shù)據(jù)的類型轉(zhuǎn)換

輸入到TensorFlow的數(shù)據(jù)不能是mat類型的所以進(jìn)行mat轉(zhuǎn)vector操作

    vector<float> Vmat;
    Vmat.assign ( ( float* )ImageMat.datastart, ( float* )ImageMat.dataend );
    auto dst = x.flat<float>().data();
    auto img = Vmat;
    std::copy_n(img.begin(), wanted_width*wanted_height, dst);
  • run session
tensorflow::Status run_status = session->Run({{input_layer, x}},
                                                 {output_layer}, {}, &outputs);
    if (!run_status.ok()) {
        LOG(ERROR) << "Running model failed: " << run_status;
        tensorflow::LogAllRegisteredKernels();
        result = @"Error running model";
        return result;
    }
  • 數(shù)據(jù)變換

使用operator方法獲取到tensor中的每一個(gè)元素值碴倾,重新賦值給array逗噩。

auto outputMatrix = outputs[0].flat<int64>();
    array<long,11> outputArray;
    for(int i=0;i<11;i++){
        outputArray[i]=outputMatrix.operator()(i);
    }
NSString *predictionstr = vec2text(outputArray);

獲取完之后需要對(duì)數(shù)據(jù)進(jìn)行處理掉丽,比如我們做的vector轉(zhuǎn)text。

NSString* vec2text(array<long,11> outputArray) {
    std::stringstream ss;
    ss.precision(12);
    ss <<"Prediction:";
    for(int i=0;i<11;i++){
        long char_idx=outputArray[i];
        long char_code = 0;
        if (char_idx<10){
            char_code = char_idx + int('0');
        }
        else if (char_idx<36){
            char_code = char_idx-10 + int('A');
        }
        else if (char_idx<62){
            char_code = char_idx + int('a');
        }
        ss << char(char_code);
    }
    tensorflow::string predictions = ss.str();
    NSString* result = [NSString stringWithFormat: @"%s",
              predictions.c_str()];
    return result;
}

iOS調(diào)用TensorFlow的基礎(chǔ)運(yùn)用就這樣异雁,高級(jí)用法可以使用MemoryMappedModel捶障,這種方法會(huì)比較節(jié)省內(nèi)存,更加優(yōu)雅纲刀。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末项炼,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子示绊,更是在濱河造成了極大的恐慌锭部,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,402評(píng)論 6 499
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件面褐,死亡現(xiàn)場(chǎng)離奇詭異拌禾,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)展哭,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,377評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門湃窍,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人匪傍,你說我怎么就攤上這事您市。” “怎么了析恢?”我有些...
    開封第一講書人閱讀 162,483評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵墨坚,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我映挂,道長(zhǎng),這世上最難降的妖魔是什么盗尸? 我笑而不...
    開封第一講書人閱讀 58,165評(píng)論 1 292
  • 正文 為了忘掉前任柑船,我火速辦了婚禮,結(jié)果婚禮上泼各,老公的妹妹穿的比我還像新娘鞍时。我一直安慰自己,他們只是感情好扣蜻,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,176評(píng)論 6 388
  • 文/花漫 我一把揭開白布逆巍。 她就那樣靜靜地躺著,像睡著了一般莽使。 火紅的嫁衣襯著肌膚如雪锐极。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,146評(píng)論 1 297
  • 那天芳肌,我揣著相機(jī)與錄音灵再,去河邊找鬼肋层。 笑死,一個(gè)胖子當(dāng)著我的面吹牛翎迁,可吹牛的內(nèi)容都是我干的栋猖。 我是一名探鬼主播,決...
    沈念sama閱讀 40,032評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼汪榔,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼蒲拉!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起痴腌,我...
    開封第一講書人閱讀 38,896評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤全陨,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后衷掷,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體辱姨,經(jīng)...
    沈念sama閱讀 45,311評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,536評(píng)論 2 332
  • 正文 我和宋清朗相戀三年戚嗅,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了雨涛。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,696評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡懦胞,死狀恐怖替久,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情躏尉,我是刑警寧澤蚯根,帶...
    沈念sama閱讀 35,413評(píng)論 5 343
  • 正文 年R本政府宣布,位于F島的核電站胀糜,受9級(jí)特大地震影響颅拦,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜教藻,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,008評(píng)論 3 325
  • 文/蒙蒙 一距帅、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧括堤,春花似錦碌秸、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至轧抗,卻和暖如春恩敌,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背鸦致。 一陣腳步聲響...
    開封第一講書人閱讀 32,815評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工潮剪, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留涣楷,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,698評(píng)論 2 368
  • 正文 我出身青樓抗碰,卻偏偏與公主長(zhǎng)得像狮斗,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子弧蝇,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,592評(píng)論 2 353

推薦閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,072評(píng)論 25 707
  • TensorFlow對(duì)Android、iOS两芳、樹莓派都提供移動(dòng)端支持摔寨。 移動(dòng)端應(yīng)用原理。移動(dòng)端怖辆、嵌入式設(shè)備應(yīng)用深度...
    利炳根閱讀 2,877評(píng)論 1 13
  • 一丶逆向作用 1.分析目標(biāo)程序,拿到關(guān)鍵信息,可以歸類于安全相關(guān)的逆向工程;2.借鑒他人的程序功能來開發(fā)自己的軟件...
    丶納涼閱讀 262評(píng)論 0 1
  • 知行合一一直掛在嘴邊卻沒有去細(xì)想它真正的含義是复,簡(jiǎn)單地想就是知道的和行動(dòng)的要一致,其實(shí)相去甚遠(yuǎn)竖螃,這里面有一個(gè)問題是知...
    朵朵頤閱讀 323評(píng)論 0 2
  • 薛姓是中國(guó)一重姓氏淑廊。始祖奚仲,因分封在薛國(guó)而得姓特咆,以封邑名為氏季惩,后人有以國(guó)名為氏。薛姓發(fā)源地有山東濟(jì)寧腻格,滕...
    薛舒陽閱讀 925評(píng)論 0 0