開心的程序猿@NXP 本文專為參加今年大學(xué)生智能車競(jìng)賽AI視覺(jué)組的同學(xué)們而寫,也很適合其他對(duì)MCU上AI應(yīng)用感興趣的朋友。 讀過(guò)之前兩篇的童鞋們,想來(lái)已經(jīng)開始著手開發(fā)屬于自己的AI視覺(jué)應(yīng)用了,當(dāng)然,手中還沒(méi)有OpenART套件的朋友們,也不用著急,可以先參照'智能車大賽AI視覺(jué)組參考答案'在PC上先試訓(xùn)一下AI模型,然后結(jié)合AI視覺(jué)組仙人一步之模型量化,熟悉一下模型部署流程。 在智能車大賽AI視覺(jué)組參考答案推文中,小編介紹了如何借助OpenART所提供的python接口,在openMV IDE中編寫腳本以運(yùn)行AI模型。Python以其簡(jiǎn)單易用的特點(diǎn),可以說(shuō)極大的減輕了我們的開發(fā)負(fù)荷。 但是,想必高級(jí)玩家已經(jīng)不滿足止步于只使用python了吧。 了解我們的OpenART套件的童鞋們肯定知道,套件本身是集成了RT-Thread這款RTOS的,何不嘗試點(diǎn)新玩法?挑戰(zhàn)點(diǎn)高難度,嘗試下用C來(lái)直接寫code,在小伙伴面前炫耀一下? 也正式因?yàn)橛辛薘TOS的加持,每一個(gè)任務(wù)本身在RTOS中都作為一個(gè)獨(dú)立線程存在,想要為項(xiàng)目添加新功能的話,只需要為其創(chuàng)建一個(gè)新的線程即可,無(wú)需更改已有裸機(jī)代碼。 本文的目的就是展示給大家,如何以線程的方式在工程中添加一個(gè)新的功能,以實(shí)現(xiàn)對(duì)于tflite micro推理引擎的直接調(diào)用。 那么,話不多說(shuō),直接開整。 首先介紹一下如何在OpenART中創(chuàng)建一個(gè)新的線程(OpenART中集成了RT-Thread操作系統(tǒng),因此如下操作都是以RT-Thread為基礎(chǔ)的): 下面開始編寫線程主函數(shù),主要包括以下內(nèi)容: 一、初始化攝像頭:因?yàn)槲覀冃枰ㄟ^(guò)攝像頭讀取指令,由于我們已經(jīng)不再使用python腳本,攝像頭初始化部分需要手動(dòng)進(jìn)行,到了這里,是不是要?jiǎng)裢艘恍┬』锇榱四兀縿e急,還有更厲害的在后面,感受C語(yǔ)言的魅力吧! 初始化流程和python調(diào)用流程類似,只不過(guò)我們這次要直接進(jìn)行函數(shù)調(diào)用,來(lái)實(shí)現(xiàn)攝像頭模塊的初始化,這里要感謝RT-Thread所提供的設(shè)備管理框架,能夠讓我們能夠方便的實(shí)現(xiàn)這一功能: 二、讀取圖像:大家可能注意到了這個(gè)fb_alloc,可能有同學(xué)會(huì)問(wèn),這個(gè)不是Micropython中的嗎?這里還能用嗎,當(dāng)然,大家可以把它理解成一個(gè)內(nèi)存管理器,是可以用C來(lái)直接進(jìn)行函數(shù)調(diào)用的: 這個(gè)image_t結(jié)構(gòu)體,實(shí)際上它包含了圖像的所有信息,就可以直接進(jìn)行處理了。 三、模型推理:至此,代碼中所涉及到的圖像采集部分就到此為止了,下面開始介紹模型推理部分。 不過(guò),要提前說(shuō)明的是,在這里我們沒(méi)有保留python API所實(shí)現(xiàn)的圖像輸入部分,即可以通過(guò)設(shè)置窗口滑動(dòng)步長(zhǎng)和窗口放縮比,來(lái)實(shí)現(xiàn)多尺度下的圖像識(shí)別,我們假設(shè)要識(shí)別的物體充滿整個(gè)屏幕,即識(shí)別區(qū)域?yàn)檎睌z像頭采集的圖像,而這就喪失了多目標(biāo)識(shí)別能力,有興趣的玩家可以自行實(shí)現(xiàn)。 那么如何利用tflite micro進(jìn)行模型推斷呢? ⊙ 首先第一步是讀取模型,model_data就是以二進(jìn)制方式讀取的tflite模型,在OpenART中,我們采用DFS文件系統(tǒng)來(lái)進(jìn)行模型文件的讀取,full_path根據(jù): ⊙ 之后是實(shí)例化操作解析器和模型解釋器,其中,解析器用來(lái)訪問(wèn)Tensorflow的操作,可以擴(kuò)展此類以向項(xiàng)目中添加自定義操作。解釋器是用來(lái)對(duì)模型進(jìn)行結(jié)構(gòu)上的解析,以進(jìn)行模型求解。 ⊙ 運(yùn)算之前,我們需要預(yù)先為輸入、輸出以及中間數(shù)組分配一定的內(nèi)存。因此,用戶需要預(yù)分配一個(gè)大小為tensor_arena_size的uint8_t的數(shù)組,傳遞給模型解析器。 ⊙ 隨后還需要調(diào)用函數(shù)對(duì)預(yù)分配的tensor arena進(jìn)行分配: ⊙ 至此,萬(wàn)事俱備,只欠東風(fēng)了,而我們的東風(fēng)就是待識(shí)別的圖像,tflite模型的輸入,是通過(guò)對(duì)input tensor進(jìn)行賦值實(shí)現(xiàn)的,實(shí)現(xiàn)方法也很簡(jiǎn)單,我們只需要將第二步讀取到的圖像數(shù)據(jù),按照模型輸入的要求進(jìn)行處理后,直接對(duì)其賦值即可: ![]() 這里我們假設(shè)image是已經(jīng)按照模型出入要求處理好的圖像數(shù)據(jù),size是其大小,實(shí)際情況下,可以隨機(jī)應(yīng)變,宗旨就是要對(duì)model_input->data.data這個(gè)變量進(jìn)行遍歷賦值。 讓我們的模型解釋器開始干活,還需要最后一步: ![]() ⊙ 最后,怎樣獲取結(jié)果呢?和設(shè)置input tensor大同小異,我們這次要獲取output tensor, 需要注意的是這里的model_output->data.data是個(gè)void*類型,需要根據(jù)model_output->type進(jìn)行指針的強(qiáng)制類型轉(zhuǎn)化: ![]() 至此,運(yùn)行tflite模型的所有所需代碼均已介紹完畢,當(dāng)然,為了方便使用,小編已經(jīng)為大家親手烹制了對(duì)應(yīng)的庫(kù),已經(jīng)集成了這些函數(shù),可以直接調(diào)用。 ![]() ⊙ 這里面比較重要的是兩個(gè)callback函數(shù),要注意的是,這兩個(gè)函數(shù)會(huì)在函數(shù)內(nèi)部自動(dòng)調(diào)用,用戶需要傳入input_callback_data和output_callback_data,下面進(jìn)行一一說(shuō)明。
![]()
![]() 四、導(dǎo)出:最后一步是導(dǎo)出到msh命令行,這樣才可以在系統(tǒng)啟動(dòng)后,在RT-Thread的命令窗口中進(jìn)行調(diào)用,這里我們通過(guò)EXPORT_MSH_CMD()這個(gè)特殊的宏進(jìn)行命令的聲明: ![]() 這里包含兩部分的內(nèi)容,以逗號(hào)進(jìn)行分割,第一部分既代表命令行中所對(duì)應(yīng)的命令,又代表當(dāng)輸入tflite_micro這一命令時(shí)所調(diào)用的函數(shù)名,沒(méi)錯(cuò),我們就是要把我們創(chuàng)建tflite線程的函數(shù)tflite_micro_main放到這里面,這樣,我們就導(dǎo)出了一個(gè)叫做tflite_micro的命令,當(dāng)我們運(yùn)行這一命令時(shí),所創(chuàng)建的tflite micro線程就會(huì)被調(diào)用。第二部分,是對(duì)于指令的描述。 ![]() 至此,我們的菜就全部備齊了,這里略過(guò)1萬(wàn)字的代碼調(diào)試以及下載過(guò)程,在出現(xiàn)的msh命令行窗口中輸入tflite_micro,將待識(shí)別的圖像對(duì)準(zhǔn)攝像頭,即可進(jìn)行物體的識(shí)別: 運(yùn)行結(jié)果: ![]() 不過(guò),由于我們是直接以線程的形式調(diào)用tflite micro的推理引擎,因此沒(méi)有辦法在使用openmv ide進(jìn)行圖像的預(yù)覽了,不過(guò),如果擁有一塊LCD的小伙伴,就可以不用慌張了,可以顯示在LCD屏幕上一睹芳容。 本期,小編為大家?guī)?lái)了如何在底層以新建線程的方式直接調(diào)用tflite micro推理引擎,參考代碼位于https:///crist_xu/tflite_thread_call.git,不過(guò),代碼并不包含OpenART代碼包,同學(xué)們可以參考下實(shí)現(xiàn),等到OpenART軟件包正式和大家見(jiàn)面之后再進(jìn)行動(dòng)手。 |
|
來(lái)自: 西北望msm66g9f > 《編程》