0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫(xiě)文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

MCU部署OpenCV的“進(jìn)階篇”

恩智浦MCU加油站 ? 來(lái)源:恩智浦MCU加油站 ? 作者:恩智浦MCU加油站 ? 2022-11-24 11:16 ? 次閱讀

本文是一個(gè)小系列的第四篇,MCU部署OpenCV的“進(jìn)階篇”,已經(jīng)發(fā)表了“先跑篇”、"配置篇"和“實(shí)戰(zhàn)篇”稍后會(huì)陸續(xù)還有“優(yōu)化篇”,帶您牽手OpenCV,進(jìn)入OpenCV的廣闊世界。

上一期小編帶著大家簡(jiǎn)單過(guò)了一下OpenCV的API的基本使用方法,并最終在MCU上實(shí)際跑了下。相信大家看的一定不是很過(guò)癮,本期小編將和大家一起完全從0開(kāi)始構(gòu)建一個(gè)基于MCUXPresso的OpenCV測(cè)試工程,并部署到MCU上。

提到這兒,可能有朋友會(huì)提出質(zhì)疑:上期不是說(shuō)過(guò)了,基于HelloWorld例程,把那5個(gè)可愛(ài)的庫(kù)一股腦放進(jìn)去就可以了嗎?

可能事情并不是這么簡(jiǎn)單喲!不然,小編可就有湊字+湊篇幅的嫌疑了啊。還請(qǐng)聽(tīng)小編娓娓道來(lái)。

我們知道OpenCV是基于C++編寫(xiě)的項(xiàng)目,而我們所常用的Hello_World例程實(shí)際上是一個(gè)C工程。這樣一來(lái),原生的Hello_World的C工程是無(wú)法兼容,所編譯出的OpenCV庫(kù)工程的,或者說(shuō),工程本身不能支持構(gòu)建C++工程。

因此,在開(kāi)始部署之前,要針對(duì)性地對(duì)工程本身進(jìn)行一些小小的改造,以添加對(duì)于C++的支持。

具體步驟如下:

所謂站在巨人的肩膀看得遠(yuǎn),先做一些準(zhǔn)備工作:

  1. SDK代碼包:2.11.0 for i.MX RT1170

  2. MCUXPresso IDE:11.5.0

  3. 選取參考例程:SDK_rootoardsevkmimxrt1170demo_appshello_world_demo_cm7

  4. 準(zhǔn)備好5個(gè)靜態(tài)庫(kù):libopencv_world, libopenjp2, libjpeg-turbo, libpng, zlib

通過(guò)Quickstart Panel導(dǎo)入hello world例程:

034ad6c2-6b91-11ed-8abf-dac502259ad0.png

犀利的操作:添加C++支持,找到工程目錄下的.project文件,并添加:

038687c6-6b91-11ed-8abf-dac502259ad0.png

修改好之后,使用MCUXPresso IDE重新打開(kāi)工程即可開(kāi)啟C++屬性。

此時(shí)的C++工程屬性還是空的,首先是針對(duì)MCU C++ Compiler的配置,包括頭文件以及預(yù)編譯符號(hào)的添加:

03d29be8-6b91-11ed-8abf-dac502259ad0.png

頭文件路徑如下:

"${workspace_loc:/${ProjName}/drivers}"
"${workspace_loc:/${ProjName}/board}"
"${workspace_loc:/${ProjName}/source}"
"${workspace_loc:/${ProjName}/utilities}"
"${workspace_loc:/${ProjName}/drivers}"
"${workspace_loc:/${ProjName}/device}"
"${workspace_loc:/${ProjName}/component/uart}"
"${workspace_loc:/${ProjName}/component/lists}"
"${workspace_loc:/${ProjName}/startup}"
"${workspace_loc:/${ProjName}/xip}"
"${workspace_loc:/${ProjName}/CMSIS}"
"${workspace_loc:/${ProjName}/utilities}"
"${workspace_loc:/${ProjName}/device}"
"your_cv_pathopencvuild"
" your_cv_path opencvinclude"
" your_cv_path opencvmodulescoreinclude"
" your_cv_path opencvmodulesimgcodecsinclude"
" your_cv_path opencvmodulesimgprocinclude"
" your_cv_path opencvmodulesworldinclude"
" your_cv_path opencvmoduleshighguiinclude"
" your_cv_path opencvmodulesfeatures2dinclude"
" your_cv_path opencvmodulesmlinclude"
" your_cv_path opencvmodulesvideoinclude"
預(yù)編譯符號(hào):
OPENCV_DISABLE_THREAD_SUPPORT=1
__NEWLIB__
CPU_MIMXRT1176DVMAA
CPU_MIMXRT1176DVMAA_cm7
XIP_BOOT_HEADER_DCD_ENABLE=1
USE_SDRAM
DATA_SECTION_IS_CACHEABLE=1
SDK_DEBUGCONSOLE=1
XIP_EXTERNAL_FLASH=1
XIP_BOOT_HEADER_ENABLE=1
PRINTF_FLOAT_ENABLE=0
SCANF_FLOAT_ENABLE=0
PRINTF_ADVANCED_ENABLE=0
SCANF_ADVANCED_ENABLE=0
FSL_SDK_DRIVER_QUICK_ACCESS_ENABLE=1
MCUXPRESSO_SDK
CR_INTEGER_PRINTF
__MCUXPRESSO
__USE_CMSIS
DEBUG

接下來(lái)是MCU C++ Linker的配置,包括所引用的庫(kù)名字以及庫(kù)搜索路徑:

03fa2000-6b91-11ed-8abf-dac502259ad0.png

要注意,庫(kù)的搜索路徑就是存放上面那5個(gè)庫(kù)的位置。

開(kāi)啟C++編程模式,問(wèn):C文件切換成C++文件需要幾步?答:只需一步!重命名hello_world.c->hello_world.cc即可。內(nèi)容可以保存不變。

導(dǎo)入測(cè)試數(shù)據(jù),包括壓縮格式(jpeg,PNG)或是其他未經(jīng)壓縮的原始數(shù)據(jù)。

考慮到MCU平臺(tái)一般沒(méi)有片上的文件系統(tǒng),或者說(shuō)沒(méi)有集成文件系統(tǒng)。那么我們的測(cè)試數(shù)據(jù)就要以RO data的形式直接集成到鏡像中。

為了實(shí)現(xiàn)這一需求,介紹給大家一個(gè)很好用匯編指令:.incbin,顧名思義,指令本身就好像在隱隱地告訴我們,我就是用來(lái)include bin文件的,快點(diǎn)用我。

既然是匯編指令,就要新建一個(gè)匯編文件到我們的工程中。新建匯編文件放到哪里,沒(méi)有特殊要求,但是最好放到和hello_world.cc文件同一級(jí)目錄下:

041fad0c-6b91-11ed-8abf-dac502259ad0.png

添枝加葉:

.global img_start
.global img_end

img_start:
 .incbin "data/lena.jpg"
img_end:

具體測(cè)試圖片的名字可以任意指定,只不過(guò)要注意一點(diǎn)。如果想要使用相對(duì)路徑的話,要保證存儲(chǔ)圖片的位置要和hello_world.cc的位置一致。

至此,MCUXPresso工程就準(zhǔn)備完畢了,下一步就是編寫(xiě)測(cè)試代碼。

要注意,因?yàn)?/span>我們已經(jīng)切換到了C++編程模式,就要順著C++的脾氣來(lái)。

比較重要的一條是:如果不想重命名hello_world.c,說(shuō):我就看.c尾綴舒服。沒(méi)問(wèn)題,但是請(qǐng)不要忘了,在聲明函數(shù)的時(shí)候,不要忘了用extern “C”來(lái)修飾。否則,會(huì)有千千萬(wàn)萬(wàn)個(gè)link error向你撲面而來(lái)。

接下來(lái)開(kāi)始正式編寫(xiě)測(cè)試代碼

包含頭文件,你只需要一行即可,如此和諧友善:

#include "opencv2opencv.hpp"

OpenCV使用cv::Mat來(lái)表征數(shù)據(jù),首先我們要聲明并初始化cv::Mat實(shí)例。

考慮到?jīng)]有片上文件系統(tǒng)的支持,上文也提到直接使用.incbin導(dǎo)入圖片數(shù)據(jù)。這里,我們就可以使在匯編文件中所定義的符號(hào)對(duì)這些數(shù)據(jù)進(jìn)行訪問(wèn)。如果是壓縮后的圖片,需要首先進(jìn)行解碼操作;如果是源數(shù)據(jù)的話,可以直接使用:
extern uint8_t img_start[];
extern uint8_t img_end[];
#define IMG_LEN (img_end - img_start)
// compressed data
std::vector data(img_start, img_start + IMG_LEN);
cv::Mat img = cv::Mat(data), IMREAD_UNCHANGED);
// raw data, need to aware the shape, and also the depth, such as rgb == CV_8UC3, equal to 
// each pixel has 3 items, and each item is 8bits
Mat img(Size(480, 360), CV_8UC3);
memcpy(img.data, img_start, IMG_LEN);

尋找物體輪廓并畫(huà)出:

vector> contours;
vector hierarchy;
findContours(dst, contours, hierarchy, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);
// To display the contours
Mat resultImage = Mat ::zeros(dst.size(),CV_8U);
drawContours(resultImage, contours, -1, Scalar(255, 0, 255));

接下來(lái)是一個(gè)更加復(fù)雜的任務(wù),尋找矩形:

參考代碼如下:

// returns sequence of squares detected on the image.
static void findSquares( const Mat& image, vector >& squares )
{
    squares.clear();
    Mat pyr, timg, gray0(image.size(), CV_8U), gray;
    // down-scale and upscale the image to filter out the noise
    pyrDown(image, pyr, Size(image.cols/2, image.rows/2));
    pyrUp(pyr, timg, image.size());
    vector > contours;
    for( int c = 0; c < 3; c++ )
    {
        int ch[] = {c, 0};
        mixChannels(&timg, 1, &gray0, 1, ch, 1);
        // try several threshold levels
        for( int l = 0; l < N; l++ )
        {
            if( l == 0 )
            {
                Canny(gray0, gray, 0, thresh, 5);
                dilate(gray, gray, Mat(), Point(-1,-1));
            }
            else
            {
                gray = gray0 >= (l+1)*255/N;
            }
            findContours(gray, contours, RETR_LIST, CHAIN_APPROX_SIMPLE);
            vector approx;

            // test each contour
            for( size_t i = 0; i < contours.size(); i++ )
            {
                // approximate contour with accuracy proportional to the contour perimeter
                approxPolyDP(contours[i], approx, arcLength(contours[i], true)*0.02, true);
                // square contours should have 4 vertices after approximation
                if( approx.size() == 4 &&
                    fabs(contourArea(approx)) > 1000 &&
                    isContourConvex(approx) )
                {
                    double maxCosine = 0;

                    for( int j = 2; j < 5; j++ )
                    {
                        // find the maximum cosine of the angle between joint edges
                        double cosine = fabs(angle(approx[j%4], approx[j-2], approx[j-1]));
                        maxCosine = MAX(maxCosine, cosine);
                    }
                    if( maxCosine < 0.3 )
                        squares.push_back(approx);
                }
            }
        }
    }
}

編碼圖像,這里我們選擇利用調(diào)試器將編碼后的數(shù)據(jù)從內(nèi)存download到我們的PC上。關(guān)于如何在MCUXPress中進(jìn)行數(shù)據(jù)保存的操作,在上一篇文章中已有介紹。

std::vector decoded_img;
cv::imencode(".jpeg", img, decoded_img);
uchar *data = decoded_img.data();

不過(guò),這里有個(gè)小坑要提醒給大家,在傳遞編碼格式時(shí)候,請(qǐng)不要忘記那個(gè)人見(jiàn)人愛(ài)的句點(diǎn)“.” 。也就是說(shuō),編碼格式要寫(xiě)成.jpeg而不是jpeg。小編可是在這上面吃過(guò)虧的。

至此,本期小編就給大家介紹了如何從0開(kāi)始新建一個(gè)MCUXPresso工程,并編寫(xiě)OpenCV測(cè)試代碼進(jìn)行測(cè)試。感興趣的小伙伴們快動(dòng)起手來(lái)吧!

審核編輯 :李倩


聲明:本文內(nèi)容及配圖由入駐作者撰寫(xiě)或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問(wèn)題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • mcu
    mcu
    +關(guān)注

    關(guān)注

    146

    文章

    16667

    瀏覽量

    347834
  • C++
    C++
    +關(guān)注

    關(guān)注

    21

    文章

    2085

    瀏覽量

    73304
  • OpenCV
    +關(guān)注

    關(guān)注

    29

    文章

    622

    瀏覽量

    41091

原文標(biāo)題:這個(gè)秋天,OpenCV和MCU更配喲(進(jìn)階篇)

文章出處:【微信號(hào):NXP_SMART_HARDWARE,微信公眾號(hào):恩智浦MCU加油站】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。

收藏 人收藏

    評(píng)論

    相關(guān)推薦

    【《大語(yǔ)言模型應(yīng)用指南》閱讀體驗(yàn)】+ 俯瞰全書(shū)

    松。 入門(mén)主要偏應(yīng)用,比如大語(yǔ)言模型的三種交互方式,分析了提示工程、工作記憶和長(zhǎng)短期記憶,此最后講了ChatGPT的接口和擴(kuò)展功能應(yīng)用,適合大語(yǔ)言模型應(yīng)用技術(shù)人員閱讀。 進(jìn)階篇就非常專(zhuān)業(yè)了,適合專(zhuān)業(yè)
    發(fā)表于 07-21 13:35

    opencv-python和opencv一樣嗎

    不一樣。OpenCV(Open Source Computer Vision Library)是一個(gè)開(kāi)源的計(jì)算機(jī)視覺(jué)和機(jī)器學(xué)習(xí)軟件庫(kù),它提供了大量的圖像和視頻處理功能。OpenCV
    的頭像 發(fā)表于 07-16 10:38 ?449次閱讀

    嵌入式學(xué)習(xí)-飛凌ElfBoard ELF 1板卡 - 如何在Ubuntu中編譯OpenCV庫(kù)

    在之前發(fā)布的文章中探討了交叉編譯OpenCV部署至嵌入式系統(tǒng)的方法。然而,在調(diào)試階段,我們發(fā)現(xiàn)在 Ubuntu 環(huán)境下將 OpenCV 編譯為 X86 架構(gòu)可能更加方便和高效。 通過(guò)在主機(jī)上編譯并
    發(fā)表于 06-07 09:21

    ELF 1技術(shù)貼|如何在Ubuntu中編譯OpenCV庫(kù)

    在之前發(fā)布的文章中探討了交叉編譯OpenCV部署至嵌入式系統(tǒng)的方法。然而,在調(diào)試階段,我們發(fā)現(xiàn)在Ubuntu環(huán)境下將OpenCV編譯為X86架構(gòu)可能更加方便和高效。通過(guò)在主機(jī)上編譯并使用X86架構(gòu)
    的頭像 發(fā)表于 05-31 16:41 ?1034次閱讀
    ELF 1技術(shù)貼|如何在Ubuntu中編譯<b class='flag-5'>OpenCV</b>庫(kù)

    FPGA布局布線優(yōu)化進(jìn)階篇

    邏輯復(fù)制在布局過(guò)程的早期發(fā)生,為了扇出到其他邏輯元件的結(jié)構(gòu),這些元件不可以(由于任何理由)存在于相同的近鄰。
    發(fā)表于 03-27 12:26 ?658次閱讀
    FPGA布局布線優(yōu)化<b class='flag-5'>進(jìn)階篇</b>

    基于OpenCV DNN實(shí)現(xiàn)YOLOv8的模型部署與推理演示

    基于OpenCV DNN實(shí)現(xiàn)YOLOv8推理的好處就是一套代碼就可以部署在Windows10系統(tǒng)、烏班圖系統(tǒng)、Jetson的Jetpack系統(tǒng)
    的頭像 發(fā)表于 03-01 15:52 ?1032次閱讀
    基于<b class='flag-5'>OpenCV</b> DNN實(shí)現(xiàn)YOLOv8的模型<b class='flag-5'>部署</b>與推理演示

    RT-Thread驅(qū)動(dòng)開(kāi)發(fā)指南進(jìn)階篇-動(dòng)手驅(qū)動(dòng)先楫未適配的外設(shè)LCD

    經(jīng)過(guò)上一的《《RT-Thread設(shè)備驅(qū)動(dòng)開(kāi)發(fā)指南》基礎(chǔ)--以先楫bsp的hwtimer設(shè)備為例》闡述,可以大致了解到RT-thread設(shè)備驅(qū)動(dòng)開(kāi)發(fā)的方法步驟,開(kāi)發(fā)指南中的進(jìn)階篇外設(shè)主要是比基礎(chǔ)
    的頭像 發(fā)表于 02-25 11:04 ?1871次閱讀
    RT-Thread驅(qū)動(dòng)開(kāi)發(fā)指南<b class='flag-5'>進(jìn)階篇</b>-動(dòng)手驅(qū)動(dòng)先楫未適配的外設(shè)LCD

    RK3568驅(qū)動(dòng)指南|驅(qū)動(dòng)基礎(chǔ)進(jìn)階篇-進(jìn)階5 自定義實(shí)現(xiàn)insmod命令實(shí)驗(yàn)

    RK3568驅(qū)動(dòng)指南|驅(qū)動(dòng)基礎(chǔ)進(jìn)階篇-進(jìn)階5 自定義實(shí)現(xiàn)insmod命令實(shí)驗(yàn)
    的頭像 發(fā)表于 02-20 14:10 ?489次閱讀
    RK3568驅(qū)動(dòng)指南|驅(qū)動(dòng)基礎(chǔ)<b class='flag-5'>進(jìn)階篇</b>-<b class='flag-5'>進(jìn)階</b>5 自定義實(shí)現(xiàn)insmod命令實(shí)驗(yàn)

    RK3568驅(qū)動(dòng)指南|驅(qū)動(dòng)基礎(chǔ)進(jìn)階篇-進(jìn)階8 內(nèi)核運(yùn)行ko文件總結(jié)

    RK3568驅(qū)動(dòng)指南|驅(qū)動(dòng)基礎(chǔ)進(jìn)階篇-進(jìn)階8 內(nèi)核運(yùn)行ko文件總結(jié)
    的頭像 發(fā)表于 01-31 14:58 ?955次閱讀
    RK3568驅(qū)動(dòng)指南|驅(qū)動(dòng)基礎(chǔ)<b class='flag-5'>進(jìn)階篇</b>-<b class='flag-5'>進(jìn)階</b>8 內(nèi)核運(yùn)行ko文件總結(jié)

    【米爾-TIAM62開(kāi)發(fā)板-接替335x-試用評(píng)測(cè)】OPENCV和NCNN交叉編譯

    目錄 1. 前言 2. OPENCV交叉編譯 3. NCNN交叉編譯 4. OPENCV和NCNN移植 1. 前言 介紹了OpenCV和NCNN庫(kù)的交叉編譯過(guò)程,并在TIAM62開(kāi)發(fā)板上部署
    發(fā)表于 12-16 23:16

    ESP32的接口不能隨便用,如何快速?gòu)腁rduino過(guò)渡到ESP32的新手指南-進(jìn)階篇

    進(jìn)階篇 接續(xù)如何快速?gòu)腁rduino過(guò)渡到ESP32的新手指南-基礎(chǔ),繼續(xù)了解ESP32的各種引腳接口吧! 內(nèi)容介紹 1.?SPI引腳 2.?I2C引腳 3.?UART引腳 4.?RTC引腳 5.
    的頭像 發(fā)表于 12-12 15:54 ?2.1w次閱讀
    ESP32的接口不能隨便用,如何快速?gòu)腁rduino過(guò)渡到ESP32的新手指南-<b class='flag-5'>進(jìn)階篇</b>

    逆變器的調(diào)制方法進(jìn)階篇—空間矢量調(diào)制SVM

    這篇文章將介紹一種新的調(diào)制方法,空間矢量調(diào)制 (Space Vector Modulation),簡(jiǎn)稱 SVM。
    的頭像 發(fā)表于 11-09 14:14 ?1489次閱讀
    逆變器的調(diào)制方法<b class='flag-5'>進(jìn)階篇</b>—空間矢量調(diào)制SVM

    Ubuntu系統(tǒng)下編譯OpenCV4.8源碼記錄

    很多人經(jīng)常會(huì)問(wèn)我是否有在Ubuntu系統(tǒng)化下開(kāi)發(fā)OpenCV C++應(yīng)用的教程,其實(shí)我一直沒(méi)有,然后我有幾塊開(kāi)發(fā)板都是基于Linux的,有Jetson系列的開(kāi)發(fā)板,所以我以前寫(xiě)過(guò)一文章如何在
    的頭像 發(fā)表于 10-27 16:07 ?1366次閱讀
    Ubuntu系統(tǒng)下編譯<b class='flag-5'>OpenCV</b>4.8源碼記錄

    想在STM32 MCU部署機(jī)器學(xué)習(xí)模型?這份入門(mén)教程,讓你一學(xué)就會(huì)~

    想在STM32 MCU部署機(jī)器學(xué)習(xí)模型?這份入門(mén)教程,讓你一學(xué)就會(huì)~
    的頭像 發(fā)表于 10-18 17:45 ?3982次閱讀
    想在STM32 <b class='flag-5'>MCU</b>上<b class='flag-5'>部署</b>機(jī)器學(xué)習(xí)模型?這份入門(mén)教程,讓你一學(xué)就會(huì)~

    OpenCV 如何加載圖片

    之前也寫(xiě)過(guò)一些 OpenCV 的文章,最近正好在考慮 寫(xiě)一個(gè) OpenCV相關(guān)的工具,目前還是在開(kāi)發(fā)過(guò)程中,邊開(kāi)發(fā)邊更新。預(yù)計(jì)會(huì)持續(xù)一段時(shí)間。目前的想法是把 OpenCV 的 API 在這個(gè)工具上都
    的頭像 發(fā)表于 10-09 15:01 ?605次閱讀