上篇文章演示了使用飛凌OK3568-C開發(fā)板外接USB攝像頭進(jìn)行AI物品識別,并進(jìn)行了視頻演示,本篇來介紹下代碼實(shí)現(xiàn)。
01
SSD模型介紹
SSD,全稱為Single Shot MultiBox Detector,是Wei Liu在ECCV 2016上提出的一種目標(biāo)檢測算法,屬于一階段One Stage方法,SSD 模型利用不同尺度的特征圖進(jìn)行目標(biāo)的檢測,其模型結(jié)構(gòu)圖如下:
![8d4b2dbe-bfd2-11ed-bfe3-dac502259ad0.png](https://file1.elecfans.com//web2/M00/99/AC/wKgaomTnc9WAJFulAAE7yAOXjQ4942.png)
SSD具有如下主要特點(diǎn):
-
從YOLO中繼承了將detection轉(zhuǎn)化為regression的思路,同時一次即可完成網(wǎng)絡(luò)訓(xùn)練
-
基于Faster RCNN中的anchor,提出了相似的prior box
-
加入基于特征金字塔(Pyramidal Feature Hierarchy)的檢測方式,相當(dāng)于半個FPN思路
SSD網(wǎng)絡(luò)結(jié)構(gòu)圖如下:
![8d6790da-bfd2-11ed-bfe3-dac502259ad0.png](https://file1.elecfans.com//web2/M00/99/AC/wKgaomTnc9WAeVBcAAFAfKd-KfA646.png)
其算法步驟為:
-
將圖像輸入預(yù)訓(xùn)練好的分類網(wǎng)絡(luò)(基于VGG16-Atrous)得到不同大小的特征映射
-
分別提取Conv4_3、Conv7、Conv8_2、Conv9_2、Conv10_2、Conv11_2層的特征映射feature map,在每個特征映射的每個點(diǎn)構(gòu)造6個不同大小尺度的bounding box,進(jìn)行檢測和分類來生成一些列bounding box
-
采用NMS處理不同特征映射的bounding box,刪掉部分重疊或者不正確的bounding box,得到最終的檢測框
OK3568-C開發(fā)板中自帶了已訓(xùn)練好的AI模型,位于/userdata/model目錄下的ssd_inception_v2.rknn,我們直接用就可以了。
02
USB攝像頭實(shí)現(xiàn)物品識別代碼
先來看下整個代碼的項(xiàng)目結(jié)構(gòu),然后再來分別介紹各個功能模塊。
-
imageutil.h:圖像類型轉(zhuǎn)換相關(guān)函數(shù)
-
myvideosourceface.cpp/h:用于USB攝像頭圖像顯示
-
qtcamera.cpp/h:qt界面
-
rknn_ssd_process.cpp/h:用于SSD模型進(jìn)行AI物品識別的接口函數(shù)
-
rknn_ssd.cpp/h:SSD模型相關(guān)函數(shù)
![8d815498-bfd2-11ed-bfe3-dac502259ad0.png](https://file1.elecfans.com//web2/M00/99/AC/wKgaomTnc9aAaxQnAAMRTgr9iSA862.png)
03
按幀獲取USB攝像頭圖像
3.1 改為自己的Viewfinder
之前測試USB攝像頭顯示時,使用的是Qt的QCameraViewfinder用來顯示攝像頭圖像,為了能獲取到每一幀的圖像,可以自己實(shí)現(xiàn)一個Viewfinder,然后在m_camera->setViewfinder時設(shè)置為自己的,并添加槽函數(shù)rcvFrame,當(dāng)獲取到一幀圖像時,會觸發(fā)此函數(shù)。
void qtCamera::on_cameraClick()
{
//創(chuàng)建攝像頭對象
m_camera = new QCamera(m_cameraInfo);
m_camera->unload();
//配置攝像頭的模式--捕獲靜止圖像
m_camera->setCaptureMode(QCamera::CaptureStillImage);
//設(shè)置默認(rèn)攝像頭參數(shù)
QCameraViewfinderSettings set;
set.setResolution(640, 480); //設(shè)置顯示分辨率
set.setMaximumFrameRate(25); //設(shè)置幀率
//自己用QPainter將每一幀視頻畫出來
myvideosurface *surface = new myvideosurface(this);
//設(shè)置取景器顯示
m_camera->setViewfinder(surface);
connect(surface, SIGNAL(frameAvailable(QVideoFrame)), this, SLOT(rcvFrame(QVideoFrame)), Qt::DirectConnection);
connect(this,SIGNAL(sendOneQImage(QImage)), this, SLOT(recvOneQImage(QImage)));
//啟動攝像頭
m_camera->start();
}
(左右移動查看全部內(nèi)容)
接收到一幀圖像后,其原始圖像格式是QVideoFrame類型的,需要先轉(zhuǎn)為QImage類型,然后就可以進(jìn)行顯示或進(jìn)行圖像處理了,這里觸發(fā)一個sendOneQImage信號來通知進(jìn)行圖像處理:
void qtCamera::rcvFrame(QVideoFrame m_currentFrame)
{
m_currentFrame.map(QAbstractVideoBuffer::ReadOnly);
QImage videoImg = QImage(m_currentFrame.bits(),
m_currentFrame.width(),
m_currentFrame.height(),
QVideoFrame::imageFormatFromPixelFormat(m_currentFrame.pixelFormat())).copy();
m_currentFrame.unmap();
QWidget::update();
emit sendOneQImage(videoImg); //發(fā)送信號
}
(左右移動查看全部內(nèi)容)
3.2 自定義Viewfinder的實(shí)現(xiàn)
參考網(wǎng)上的一些代碼實(shí)現(xiàn),其主要邏輯如下:
bool myvideosurface::present(const QVideoFrame &frame)
{
if (frame.isValid())
{
QVideoFrame cloneFrame(frame);
emit frameAvailable(cloneFrame);
return true;
}
stop();
return false;
}
//這些虛函數(shù),會自動被調(diào)用,start檢測圖像是否可以對等轉(zhuǎn)換,每一幀有沒有
bool myvideosurface::start(const QVideoSurfaceFormat &videoformat)
{
if(QVideoFrame::imageFormatFromPixelFormat(videoformat.pixelFormat()) != QImage::Format_Invalid && !videoformat.frameSize().isEmpty())
{
QAbstractVideoSurface::start(videoformat);
return true;
}
return false;
}
void myvideosurface::stop()
{
QAbstractVideoSurface::stop();
}
//將視頻流中像素格式轉(zhuǎn)換成格式對等的圖片格式,若無對等的格式,返回QImage::Format_Invalid
bool myvideosurface::isFormatSupported(const QVideoSurfaceFormat &videoformat) const
{
//imageFormatFromPixelFormat()-----返回與視頻幀像素格式等效的圖像格式
//pixelFormat()-----返回視頻流中幀的像素格式
return QVideoFrame::imageFormatFromPixelFormat(videoformat.pixelFormat()) != QImage::Format_Invalid;
}
//支持的像素格式
QListmyvideosurface::supportedPixelFormats(QAbstractVideoBuffer::HandleType handleType) const
{
if(handleType == QAbstractVideoBuffer::NoHandle){
return QList() << QVideoFrame::Format_RGB32
<< QVideoFrame::Format_ARGB32
<< QVideoFrame::Format_ARGB32_Premultiplied
<< QVideoFrame::Format_RGB565
<< QVideoFrame::Format_RGB555;
qDebug() << QList() << QVideoFrame::Format_RGB32;
}
else
{
return QList();
}
}
(左右移動查看全部內(nèi)容)
對應(yīng)的頭文件類定義:
class myvideosurface : public QAbstractVideoSurface
{
Q_OBJECT
public:
explicit myvideosurface(QObject *parent = nullptr);
~myvideosurface() Q_DECL_OVERRIDE;
bool present(const QVideoFrame &) Q_DECL_OVERRIDE; //每一幀畫面將回到這里處理
bool start(const QVideoSurfaceFormat &) Q_DECL_OVERRIDE; //只有攝像頭開,就會調(diào)用
void stop() Q_DECL_OVERRIDE; //出錯就停止了
bool isFormatSupported(const QVideoSurfaceFormat &) const Q_DECL_OVERRIDE;
QListsupportedPixelFormats(QAbstractVideoBuffer::HandleType type = QAbstractVideoBuffer::NoHandle) const Q_DECL_OVERRIDE;
private:
QVideoFrame m_currentFrame; //視頻幀
signals:
void frameAvailable(QVideoFrame); //將捕獲的每一幀視頻通過信號槽方式發(fā)出去
};
(左右移動查看全部內(nèi)容)
04
圖像類型的轉(zhuǎn)換與顯示
4.1 QImage轉(zhuǎn)Mat
Qt是QCamera創(chuàng)建的USB攝像頭,獲取到的圖片格式是QImage類型,而使用OpenCV進(jìn)行圖像處理,需要轉(zhuǎn)換為cv::Mat類型,轉(zhuǎn)換的方式如下:
cv::Mat QImageToMat(QImage image)
{
image = image.convertToFormat(QImage::Format_RGB888);
cv::Mat tmp(image.height(), image.width(), CV_8UC3, (uchar *)image.bits(), image.bytesPerLine());
cv::Mat result; // deep copy just in case (my lack of knowledge with open cv)
cvtColor(tmp, result, CV_BGR2RGB);
return result;
}
(左右移動查看全部內(nèi)容)
4.2 Mat轉(zhuǎn)QImage
OpenCV進(jìn)行圖像處理完成后,比如進(jìn)行AI物品識別完成,并將識別的信息標(biāo)記到圖像上后,需要再轉(zhuǎn)成QImage的類型用于在Qt中顯示出來,轉(zhuǎn)換的方式如下:
QImage MatToQImage(cv::Mat mat)
{
cv::cvtColor(mat, mat, CV_BGR2RGB);
QImage qim((const unsigned char *)mat.data, mat.cols, mat.rows, mat.step,
QImage::Format_RGB888);
return qim;
}
(左右移動查看全部內(nèi)容)
4.3 QImage轉(zhuǎn)QPixmap
QImage在Qt中還不能直接顯示出來,還需要再轉(zhuǎn)為QPixmap類型,轉(zhuǎn)換的方式如下:
QImage qImage;
QPixmap tempPixmap = QPixmap::fromImage(qImage);
(左右移動查看全部內(nèi)容)
4.4 圖像的顯示
這里創(chuàng)建一個QLabel用于顯示圖像,調(diào)用setPixmap方法即可將圖像顯示出來,最后的adjustSize用來自動調(diào)整大小。
//創(chuàng)建一個label用于顯示圖像
m_lableShowImg = new QLabel();
m_lableShowImg->setPixmap(tempPixmap);
m_lableShowImg->adjustSize();
(左右移動查看全部內(nèi)容)
05
RKNN例程移植
飛凌OK3568-C開發(fā)板資料中,自帶了ssd模型的測試程序,代碼位置如下,ssd的測試代碼是這3個文件:
![8da65f68-bfd2-11ed-bfe3-dac502259ad0.png](https://file1.elecfans.com//web2/M00/99/AC/wKgaomTnc9aANyeZAAA-CY7gz4Q901.png)
測試代碼,需要在執(zhí)行時,輸入模型的目錄位置和測試圖片的位置,AI物品識別之后會產(chǎn)生一個輸出圖片,需要再使用圖片查看器查看結(jié)果。
為了方便功能的調(diào)用,這里將fltest_opencv_rknn_ssd_main.cc改寫為rknn_ssd_process.cpp,并將具體功能進(jìn)行拆分,封裝為C++的形式。
5.1 按功能封裝為C++形式
自己封裝的RknnSsdModel類定義:
class RknnSsdModel
{
public:
RknnSsdModel(){};
~RknnSsdModel(){};
int RknnInit(const char *model_path);
int RknnDeInit();
unsigned char *LoadModel(const char *filename, int *model_size);
int DoRknnSsd(cv::Mat &src, cv::Mat &res);
private:
unsigned char *m_pModel = nullptr;
rknn_context m_rknnCtx;
rknn_input_output_num m_rknnIoNum;
};
(左右移動查看全部內(nèi)容)
5.1.1 RKNN初始化
主要功能是根據(jù)傳入的rknn模型進(jìn)行相關(guān)的初始化
int RknnSsdModel::RknnInit(const char *model_path)
{
int ret = 0;
int model_len = 0;
// Load RKNN Model
printf("Loading model ...
");
m_pModel = LoadModel(model_path, &model_len);
printf("rknn_init ...
");
ret = rknn_init(&m_rknnCtx, m_pModel, model_len, 0, NULL);
if (ret < 0)
{
printf("rknn_init fail! ret=%d
", ret);
return -1;
}
// Get Model Input Output Info
ret = rknn_query(m_rknnCtx, RKNN_QUERY_IN_OUT_NUM, &m_rknnIoNum, sizeof(m_rknnIoNum));
if (ret != RKNN_SUCC)
{
printf("rknn_query fail! ret=%d
", ret);
return -1;
}
printf("model input num: %d, output num: %d
", m_rknnIoNum.n_input, m_rknnIoNum.n_output);
printf("input tensors:
");
rknn_tensor_attr input_attrs[m_rknnIoNum.n_input];
memset(input_attrs, 0, sizeof(input_attrs));
for (int i = 0; i < m_rknnIoNum.n_input; i++)
{
input_attrs[i].index = i;
ret = rknn_query(m_rknnCtx, RKNN_QUERY_INPUT_ATTR, &(input_attrs[i]), sizeof(rknn_tensor_attr));
if (ret != RKNN_SUCC)
{
printf("rknn_query fail! ret=%d
", ret);
return -1;
}
printRKNNTensor(&(input_attrs[i]));
}
printf("output tensors:
");
rknn_tensor_attr output_attrs[m_rknnIoNum.n_output];
memset(output_attrs, 0, sizeof(output_attrs));
for (int i = 0; i < m_rknnIoNum.n_output; i++)
{
output_attrs[i].index = i;
ret = rknn_query(m_rknnCtx, RKNN_QUERY_OUTPUT_ATTR, &(output_attrs[i]), sizeof(rknn_tensor_attr));
if (ret != RKNN_SUCC)
{
printf("rknn_query fail! ret=%d
", ret);
return -1;
}
printRKNNTensor(&(output_attrs[i]));
}
return ret;
}
(左右移動查看全部內(nèi)容)
5.1.2 RKNN運(yùn)行
傳入一張Mat格式的圖片(一幀視頻圖像),經(jīng)過AI識別,并將識別的信息標(biāo)注到圖片上后,將識別結(jié)果也以Mat格式傳出:
int RknnSsdModel::DoRknnSsd(cv::Mat &src, cv::Mat &res)
{
const int img_width = 300;
const int img_height = 300;
const int img_channels = 3;
int ret = 0;
cv::Mat img = src.clone();
if (src.cols != img_width || src.rows != img_height)
{
printf("resize %d %d to %d %d
", src.cols, src.rows, img_width, img_height);
cv::resize(src, img, cv::Size(img_width, img_height), (0, 0), (0, 0), cv::INTER_LINEAR);
}
// Set Input Data
rknn_input inputs[1];
memset(inputs, 0, sizeof(inputs));
inputs[0].index = 0;
inputs[0].type = RKNN_TENSOR_UINT8;
inputs[0].size = img.cols * img.rows * img.channels();
inputs[0].fmt = RKNN_TENSOR_NHWC;
inputs[0].buf = img.data;
ret = rknn_inputs_set(m_rknnCtx, m_rknnIoNum.n_input, inputs);
if (ret < 0)
{
printf("rknn_input_set fail! ret=%d
", ret);
return -1;
}
// Run
printf("rknn_run
");
ret = rknn_run(m_rknnCtx, nullptr);
if (ret < 0)
{
printf("rknn_run fail! ret=%d
", ret);
return -1;
}
// Get Output
rknn_output outputs[2];
memset(outputs, 0, sizeof(outputs));
outputs[0].want_float = 1;
outputs[1].want_float = 1;
ret = rknn_outputs_get(m_rknnCtx, m_rknnIoNum.n_output, outputs, NULL);
if (ret < 0)
{
printf("rknn_outputs_get fail! ret=%d
", ret);
return -1;
}
// Post Process
detect_result_group_t detect_result_group;
postProcessSSD((float *)(outputs[0].buf), (float *)(outputs[1].buf), src.cols, src.rows, &detect_result_group);
// Release rknn_outputs
rknn_outputs_release(m_rknnCtx, 2, outputs);
// Draw Objects
for (int i = 0; i < detect_result_group.count; i++)
{
detect_result_t *det_result = &(detect_result_group.results[i]);
printf("%s @ (%d %d %d %d) %f
",
det_result->name,
det_result->box.left, det_result->box.top, det_result->box.right, det_result->box.bottom,
det_result->prop);
int x1 = det_result->box.left;
int y1 = det_result->box.top;
int x2 = det_result->box.right;
int y2 = det_result->box.bottom;
rectangle(src, Point(x1, y1), Point(x2, y2), Scalar(255, 0, 0, 255), 3);
putText(src, det_result->name, Point(x1, y1 - 12), 1, 4, Scalar(0, 255, 0, 255), 4);
}
res = src;
return 0;
}
(左右移動查看全部內(nèi)容)
5.2 AI識別調(diào)用
OK3568-C開發(fā)板中自帶了已訓(xùn)練好的AI模型,位于/userdata/model目錄下的ssd_inception_v2.rknn,在程序初始化時需要用到。
![8dbb9a40-bfd2-11ed-bfe3-dac502259ad0.png](https://file1.elecfans.com//web2/M00/99/AC/wKgaomTnc9aAenNDAAAan_1bs7k977.png)
AI識別的代碼邏輯為:先在qtCamera初始化時調(diào)用RKNN的初始化,然后打開USB攝像頭,USB獲取到每幀圖像后, 調(diào)用DoRknnSsd進(jìn)行AI物品識別,最后將識別的結(jié)果通過setPixmap方法展示出來
//先在qtCamera初始化時調(diào)用RKNN的初始化
std::string ssd_model = "/userdata/model/ssd_inception_v2.rknn";
m_rknnModel.RknnInit(ssd_model.c_str());
//USB獲取到每幀圖像后, 調(diào)用DoRknnSsd進(jìn)行AI物品識別
void qtCamera::recvOneQImage(QImage qImage)
{
cv::Mat srcImg = ImageUtil::QImageToMat(qImage);
cv::Mat dstImg;
m_rknnModel.DoRknnSsd(srcImg, dstImg);
QImage qDstImage = ImageUtil::MatToQImage(dstImg);
QPixmap tempPixmap = QPixmap::fromImage(qDstImage);
m_lableShowImg->setPixmap(tempPixmap);
m_lableShowImg->adjustSize();
}
(左右移動查看全部內(nèi)容)
5.3 編譯
需要注意下Qt工程的配置文件,要把opencv的一些庫鏈接進(jìn)去
qcamera.pri
INCLUDEPATH += $$PWD/src
HEADERS +=
$$PWD/src/qtcamera.h
$$PWD/src/myvideosurface.h
$$PWD/src/rknn_ssd.h
$$PWD/src/rknn_ssd_process.h
$$PWD/src/imageutil.h
SOURCES +=
$$PWD/src/qtcamera.cpp
$$PWD/src/myvideosurface.cpp
$$PWD/src/rknn_ssd.cpp
$$PWD/src/rknn_ssd_process.cpp
(左右移動查看全部內(nèi)容)
qcamera.pro
TARGET = USBCameraSSD
TEMPLATE = app
QT += widgets multimedia multimediawidgets
SOURCES += main.cpp
include($$PWD/qcamera.pri)
LIBS+=-lopencv_core -lopencv_objdetect -lopencv_highgui -lopencv_videoio -lopencv_imgproc -lopencv_imgcodecs -lrknn_api -lOpenCL -lpthread
DESTDIR = $$PWD/app_bin
MOC_DIR = $$PWD/build/qcamera
OBJECTS_DIR = $$PWD/build/qcamera
(左右移動查看全部內(nèi)容)
最后的編譯腳本還和之前的一樣:
#! /bin/bash
mkdir -p build
cd build
export PATH=/home/xxpcb/myTest/OK3568/sourcecode/OK3568-linux-source/buildroot/output/OK3568/host/bin:$PATH
qmake .. && make
(左右移動查看全部內(nèi)容)
06
總結(jié)
本篇介紹了在飛凌OK3568-C開發(fā)板中,外接USB攝像頭,利用Qt和RKNN進(jìn)行AI物品識別,通過已訓(xùn)練好的SSD模型,進(jìn)行攝像頭畫面的實(shí)時AI物品檢查的代碼實(shí)現(xiàn)原理。
![8dc969c2-bfd2-11ed-bfe3-dac502259ad0.png](https://file1.elecfans.com//web2/M00/99/AC/wKgaomTnc9aAa3I7AASvXwa2KoY723.png)
更多熱點(diǎn)文章閱讀
- OS內(nèi)核及視窗分論壇詳解之OpenHarmony 3D顯示支持
- 應(yīng)用模型開發(fā)指南上新介紹
- 技術(shù)構(gòu)筑萬物智聯(lián),第一屆OpenHarmony技術(shù)峰會圓滿舉行
- OpenHarmony L1(3.0)串口功能開發(fā)
-
小白指南:手把手教你用低代碼開發(fā)一個應(yīng)用頁面
提示:本文由電子發(fā)燒友論壇發(fā)布,轉(zhuǎn)載請注明來源。如需社區(qū)合作及入群交流,請?zhí)砑游⑿臙EFans0806,或者發(fā)郵箱liuyong@huaqiu.com。
原文標(biāo)題:RK3568開發(fā)板:USB攝像頭實(shí)時AI物品識別代碼實(shí)現(xiàn)
文章出處:【微信公眾號:電子發(fā)燒友開源社區(qū)】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
-
電子發(fā)燒友
+關(guān)注
關(guān)注
33文章
556瀏覽量
33106 -
開源社區(qū)
+關(guān)注
關(guān)注
0文章
94瀏覽量
485
原文標(biāo)題:RK3568開發(fā)板:USB攝像頭實(shí)時AI物品識別代碼實(shí)現(xiàn)
文章出處:【微信號:HarmonyOS_Community,微信公眾號:電子發(fā)燒友開源社區(qū)】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
發(fā)布評論請先 登錄
相關(guān)推薦
[迅為RK3568開發(fā)板]非科班也能玩轉(zhuǎn)Android應(yīng)用,體驗(yàn)QT跨平臺能力
![[迅為<b class='flag-5'>RK3568</b><b class='flag-5'>開發(fā)板</b>]非科班也能玩轉(zhuǎn)Android應(yīng)用,體驗(yàn)QT跨平臺能力](https://file1.elecfans.com/web3/M00/02/F1/wKgZO2diirGAPoENAACpt0CV3xc346.png)
Android11修改攝像頭前后置方法,觸覺智能RK3568開發(fā)板演示
![Android11修改<b class='flag-5'>攝像頭</b>前后置方法,觸覺智能<b class='flag-5'>RK3568</b><b class='flag-5'>開發(fā)板</b>演示](https://file1.elecfans.com/web3/M00/00/4E/wKgZO2dIGryATAP6AAB_IgRfxw8866.png)
基于迅為RK3568開發(fā)板全國產(chǎn)平臺,快速實(shí)現(xiàn)APP開機(jī)自啟動技術(shù)分享
![基于迅為<b class='flag-5'>RK3568</b><b class='flag-5'>開發(fā)板</b>全國產(chǎn)平臺,快速<b class='flag-5'>實(shí)現(xiàn)</b>APP開機(jī)自啟動技術(shù)分享](https://file1.elecfans.com/web2/M00/0C/FB/wKgaomc-y_CAM0OEAACic4bprx8519.png)
能力再次提升! 迅為RK3588/RK3568開發(fā)板&amp;核心板新增定制分區(qū)鏡像
![能力再次提升! 迅為<b class='flag-5'>RK</b>3588/<b class='flag-5'>RK3568</b><b class='flag-5'>開發(fā)板</b>&amp;核心<b class='flag-5'>板</b>新增定制分區(qū)鏡像](https://file1.elecfans.com/web2/M00/0B/E0/wKgaomcrFqCAPVkfAAC-6O-KM4Q713.png)
迅為RK3568開發(fā)板支持銀河麒麟和開放麒麟系統(tǒng)
迅為RK3568開發(fā)板/核心板助力實(shí)時系統(tǒng)!
國產(chǎn)核心板全面進(jìn)攻-RK3568開發(fā)板評測
![國產(chǎn)核心<b class='flag-5'>板</b>全面進(jìn)攻-<b class='flag-5'>RK3568</b><b class='flag-5'>開發(fā)板</b>評測](https://file.elecfans.com/web2/M00/08/64/pYYBAGDwFEGADIPWAAFlJOlmLxg664.jpg)
基于迅為RK3568/RK3588開發(fā)板的AI圖像識別方案
鴻蒙OpenHarmony南向:【RK3568開發(fā)板介紹】
![鴻蒙OpenHarmony南向:【<b class='flag-5'>RK3568</b><b class='flag-5'>開發(fā)板</b>介紹】](https://file1.elecfans.com/web2/M00/D8/1F/wKgZomYpEAGALxyGABBtbKfhj8Q460.jpg)
評論