資料介紹
描述
介紹
增強現(xiàn)實 (AR) 是真實世界環(huán)境的實時視圖,已通過計算機生成的圖形進行了增強。AR有兩種實現(xiàn)方式:
- 基于標記的增強現(xiàn)實
- 無標記 AR
Marker-less AR 更加復雜,并且依賴于真實環(huán)境來確定參考點。這通常涉及識別物體和/或背景線索,例如地板和墻壁。
基于標記的 AR 更易于實現(xiàn),并且依賴于場景中的方形標記。
該項目將描述如何使用 OpenCV 在 Ultra96-V2 上實現(xiàn)基于標記的 AR。
讓我們開始吧 !
靈感
在開始之前,我想分享一下這個項目的靈感和動力。
我受到以下創(chuàng)新產(chǎn)品/教程的啟發(fā):
動機
我實現(xiàn)類似功能的動機是能夠使用標記自動觸發(fā)某種校準,例如:
- 白平衡,使用白色參考圖表
- 立體校準,使用棋盤參考圖
為了實現(xiàn)這一點,我創(chuàng)建了以下三個圖表(使用 Microsoft Word)進行實驗。
在這項目中,我會檢測這些圖表的存在,并根據(jù)圖表進行額外的處理:
- 圖表 1 - 在棋盤圖案周圍畫一個綠色框
- 圖表 2 - 測量標記內(nèi)區(qū)域中 B、GR 像素的平均值,并在圖表上顯示帶有值的條形圖
- 圖表 2 - 測量標記內(nèi)區(qū)域的顏色直方圖,并在圖表上顯示直方圖
有關生成這些標記的更多信息,請參閱以下優(yōu)秀教程:
- Adrian Rosebrock,使用 OpenCV 和 Python 生成 ArUco 標記,PyImageSearch,
檢測場景中的標記
定義好目標并創(chuàng)建帶有標記的圖表后,我們就可以開始實施了。
我選擇用 C++ 實現(xiàn)這個項目,作為一個 gstreamer 插件。我需要感謝Tom Simpson為創(chuàng)建 gstreamer 插件奠定了基礎。
第一步是檢測場景中的標記。這是通過 OpenCV 完成的。
/* Aruco Markers */
#include
...
//
// Detect ARUCO markers
// ref : https://docs.opencv.org/master/d5/dae/tutorial_aruco_detection.html
//
std::vector markerIds;
std::vector<std::vector<cv::Point2f>> markerCorners, rejectedCandidates;
cv::Ptr<cv::aruco::DetectorParameters> parameters = cv::aruco::DetectorParameters::create();
cv::Ptr<cv::aruco::Dictionary> dictionary = cv::aruco::getPredefinedDictionary(cv::aruco::DICT_ARUCO_ORIGINAL);
cv::aruco::detectMarkers(img, dictionary, markerCorners, markerIds, parameters, rejectedCandidates);
if ( markerIds.size() > 0 )
{
cv::aruco::drawDetectedMarkers(img, markerCorners, markerIds);
}
此時,您會注意到我正在使用 DICT_ARUCO_ORIGINAL 系列標記。標記包含一個由黑色邊框包圍的 5x5 方格。
對于每個檢測到的標記,OpenCV API 返回以下信息:
- markerIds : 每個標記的標識
- markerCorners : 每個標記的坐標,格式如下: [0] top-left [1] top-right [2] bottom-right [3] bottom-left
下表和圖像說明了我創(chuàng)建的圖表中使用了哪些標記。
我使用 switch case 狀態(tài)掃描檢測到的標記,并識別感興趣的標記。
...
if (markerIds.size() >= 4 )
{
int tl_id = 0;
int tr_id = 0;
int bl_id = 0;
int br_id = 0;
cv::Point2f tl_xy, tr_xy, bl_xy, br_xy;
for ( unsigned i = 0; i < markerIds.size(); i++ )
{
switch ( markerIds[i] )
{
case 923:
tl_id = markerIds[i];
tl_xy = markerCorners[i][3]; // bottom left corner of top left marker
break;
case 1001:
case 1002:
case 1003:
case 1004:
case 1005:
case 1006:
tr_id = markerIds[i];
tr_xy = markerCorners[i][2]; // bottom right corner of top right marker
break;
case 1007:
bl_id = markerIds[i];
bl_xy = markerCorners[i][0]; // top left corner of bottom left marker
break;
case 241:
br_id = markerIds[i];
br_xy = markerCorners[i][1]; // top right corner of bottom right marker
break;
default:
break;
}
}
...
}
...
下圖說明了我保留哪些 ID 和 X/Y 坐標來定義感興趣區(qū)域 (ROI) 以進行附加處理。
檢測特定圖表是通過以下簡單的條件語句完成的。
// Chart 1 - Checkerboard (9x7)
if ( (tl_id==923) && (tr_id==1001) && (bl_id==1007) && (br_id==241) )
{
...
}
// Chart 2 - White Reference
if ( (tl_id==923) && (tr_id==1002) && (bl_id==1007) && (br_id==241) )
...
}
{
// Chart 3 - Histogram
if ( (tl_id==923) && (tr_id==1003) && (bl_id==1007) && (br_id==241) )
{
...
}
將計算機生成的圖形添加到場景中
對于“Chart 1 - CheckerBoard (9x7)”,繪制了一個綠色矩形來標識感興趣的區(qū)域。
// Extract ROI (area, ideally within 4 markers)
std::vector<cv::Point> polygonPoints;
polygonPoints.push_back(cv::Point(tl_xy.x,tl_xy.y));
polygonPoints.push_back(cv::Point(tr_xy.x,tr_xy.y));
polygonPoints.push_back(cv::Point(br_xy.x,br_xy.y));
polygonPoints.push_back(cv::Point(bl_xy.x,bl_xy.y));
// Draw border around "checkerboard"
cv::polylines(img, polygonPoints, true, cv::Scalar (0, 255, 0), 2, 16);
對于“圖表 2 - 白色參考”,感興趣區(qū)域用于計算藍色 (B)、綠色 (G) 和紅色 (R) 分量中的每一個的平均值。下面的代碼使用了一個掩碼,它支持一個不是完美矩形的 ROI。
//
// Calculate color gains
// ref : https://stackoverflow.com/questions/32466616/finding-the-average-color-within-a-polygon-bound-in-opencv
//
cv::Point pts[1][4];
pts[0][0] = cv::Point(tl_xy.x,tl_xy.y);
pts[0][1] = cv::Point(tr_xy.x,tr_xy.y);
pts[0][2] = cv::Point(br_xy.x,br_xy.y);
pts[0][3] = cv::Point(bl_xy.x,bl_xy.y);
const cv::Point* points[1] = {pts[0]};
int npoints = 4;
// Create the mask with the polygon
cv::Mat1b mask(img.rows, img.cols, uchar(0));
cv::fillPoly(mask, points, &npoints, 1, cv::Scalar(255));
// Calculate mean in masked area
auto bgr_mean = cv::mean( img, mask );
double b_mean = bgr_mean(0);
double g_mean = bgr_mean(1);
double r_mean = bgr_mean(2);
計算出平均值后,將創(chuàng)建一個 plotImage,其中包含一個帶有 B、G 和 R 像素平均值的條形圖圖像。
// Draw bars
int plot_w = 100, plot_h = 100;
cv::Mat plotImage( plot_h, plot_w, CV_8UC3, cv::Scalar(255,255,255) );
int b_bar = int((b_mean/256.0)*80.0);
int g_bar = int((g_mean/256.0)*80.0);
int r_bar = int((r_mean/256.0)*80.0);
// layout of bars : |<-10->|<---20-->|<-10->|<---20-->|<-10->|<---20-->|<-10->|
cv::rectangle(plotImage, cv::Rect(10,(80-b_bar),20,b_bar), cv::Scalar(255, 0, 0), cv::FILLED, cv::LINE_8);
cv::rectangle(plotImage, cv::Rect(40,(80-g_bar),20,g_bar), cv::Scalar(0, 255, 0), cv::FILLED, cv::LINE_8);
cv::rectangle(plotImage, cv::Rect(70,(80-r_bar),20,r_bar), cv::Scalar(0, 0, 255), cv::FILLED, cv::LINE_8);
std::stringstream b_str;
std::stringstream g_str;
std::stringstream r_str;
b_str << int(b_mean);
g_str << int(g_mean);
r_str << int(r_mean);
cv::putText(plotImage, b_str.str(), cv::Point(10,90), cv::FONT_HERSHEY_PLAIN, 0.75, cv::Scalar(255,0,0), 1, cv::LINE_AA);
cv::putText(plotImage, g_str.str(), cv::Point(40,90), cv::FONT_HERSHEY_PLAIN, 0.75, cv::Scalar(0,255,0), 1, cv::LINE_AA);
cv::putText(plotImage, r_str.str(), cv::Point(70,90), cv::FONT_HERSHEY_PLAIN, 0.75, cv::Scalar(0,0,255), 1, cv::LINE_AA);
最后,使用以下 OpenCV 函數(shù)將此繪圖圖像扭曲到感興趣區(qū)域:
- findHomography :計算源坐標和目標坐標之間的變換矩陣
- warpPerspective :將源圖像扭曲到目標坐標
然后使用掩碼將扭曲的條形圖圖像與實時圖像組合。
// Calculate transformation matrix
std::vector<cv::Point2f> srcPoints;
std::vector<cv::Point2f> dstPoints;
srcPoints.push_back(cv::Point( 0, 0)); // top left
srcPoints.push_back(cv::Point(plot_w-1, 0)); // top right
srcPoints.push_back(cv::Point(plot_w-1,plot_h-1)); // bottom right
srcPoints.push_back(cv::Point( 0,plot_h-1)); // bottom left
dstPoints.push_back(tl_xy);
dstPoints.push_back(tr_xy);
dstPoints.push_back(br_xy);
dstPoints.push_back(bl_xy);
cv::Mat h = cv::findHomography(srcPoints,dstPoints);
// Warp plot image onto video frame
cv::Mat img_temp = img.clone();
cv::warpPerspective(plotImage, img_temp, h, img_temp.size());
cv::Point pts_dst[4];
for( int i = 0; i < 4; i++)
{
pts_dst[i] = dstPoints[i];
}
cv::fillConvexPoly(img, pts_dst, 4, cv::Scalar(0), cv::LINE_AA);
img = img + img_temp;
對于“圖表 3 - 直方圖”,使用了與“圖表 2”類似的技術(shù)。不是顯示顏色平均值的條形圖,而是顯示每個顏色分量的直方圖。
了解了所有理論之后,是時候在 Ultra96-V2 上進行嵌入式實現(xiàn)了。
第 0 步 - 打印圖表
為了運行此示例,您將需要三個圖表,這些圖表已在“原理圖”部分以 PDF 格式提供。
- 圖 1 - 棋盤 (9x7)
- 圖表 2 - 白色參考
- 圖表 2 - 直方圖
第 1 步 - 創(chuàng)建 SD 卡
為以下 Avnet 平臺提供了預構(gòu)建的 Vitis-AI 1.3 SD 卡映像:
- u96v2_sbc_base : Ultra96-V2 開發(fā)板
- uz7ev_evcc_base:UltraZed-EV SOM (7EV) + FMC 載卡
- uz3eg_iocc_base:UltraZed-EG SOM (3EG) + IO 載卡
可在此處找到預構(gòu)建 SD 卡映像的下載鏈接:
- 適用于 Avnet Vitis 平臺的 Vitis-AI 1.3 流程:https ://avnet.me/vitis-ai-1.3-project
下載并解壓后,.img 文件可以編程到 16GB 微型 SD 卡。
0.解壓壓縮包得到.img文件
1. 將開發(fā)板特定的 SD 卡映像編程到 16GB(或更大)的 micro SD 卡
一個。在 Windows 機器上,使用 Balena Etcher 或 Win32DiskImager(免費開源軟件)
灣。在 linux 機器上,使用 Balena Etcher 或使用 dd 實用程序
$ sudo dd bs=4M if=Avnet-{platform}-Vitis-AI-1-3-{date}.img of=/dev/sd{X} status=progress conv=fsync
其中 {X} 是一個小寫字母,用于指定 SD 卡的設備。您可以使用“df -h”來確定您的 SD 卡對應的設備。
第 2 步 - 克隆源代碼存儲庫
本項目中使用的源代碼可以從以下存儲庫中獲?。?/font>
如果您有活動的互聯(lián)網(wǎng)連接,您可以簡單地將存儲庫克隆到嵌入式平臺的根目錄:
$ cd ~
$ git clone
第 3 步 - 編譯和安裝 gstreamer 插件
gstreamer 插件可以在 Ultra96-V2 嵌入式平臺上使用 make 命令構(gòu)建:
$ cd vitis_ai_gstreamer_plugins
$ cd markerdetect
$ make
編譯完成后,gstreamer 插件可以按如下方式安裝:
$ cp libgstmarkerdetect.so /usr/lib/gstreamer-1.0/.
可以使用 gst-inspect-1.0 實用程序驗證 gstreamer 插件的安裝:
$ gst-inspect-1.0 | grep markerdetect
markerdetect: markerdetect: Marker detection using the OpenCV Library
$ gst-inspect-1.0 markerdetect
Factory Details:
Rank none (0)
Long-name Marker detection using the OpenCV Library
Klass Video Filter
Description Marker Detection
Author FIXME
Plugin Details:
Name markerdetect
Description Marker detection using the OpenCV Library
Filename /usr/lib/gstreamer-1.0/libgstmarkerdetect.so
Version 0.0.0
License LGPL
Source module markerdetect
Binary package OpenCV Library
Origin URL http://avnet.com
GObject
+----GInitiallyUnowned
+----GstObject
+----GstElement
+----GstBaseTransform
+----GstVideoFilter
+----GstMarkerDetect
Pad Templates:
SRC template: 'src'
Availability: Always
Capabilities:
video/x-raw
format: { (string)BGR }
width: [ 1, 1920 ]
height: [ 1, 1080 ]
framerate: [ 0/1, 2147483647/1 ]
SINK template: 'sink'
Availability: Always
Capabilities:
video/x-raw
format: { (string)BGR }
width: [ 1, 1920 ]
height: [ 1, 1080 ]
framerate: [ 0/1, 2147483647/1 ]
Element has no clocking capabilities.
Element has no URI handling capabilities.
Pads:e--
SINK: 'sink'
Pad Template: 'sink'
SRC: 'src'
Pad Template: 'src'
Element Properties:
name : The name of the object
flags: readable, writable
String. Default: "markerdetect0"
parent : The parent of the object
flags: readable, writable
Object of type "GstObject"
qos : Handle Quality-of-Service events
flags: readable, writable
Boolean. Default: true
第 4 步 - 使用實時視頻源執(zhí)行示例
為了便于啟動示例,請在您的嵌入式平臺上創(chuàng)建以下啟動腳本 (launch_usb_markerdetect.sh):
#!/bin/sh
gst-launch-1.0 \
v4l2src device=/dev/video0 io-mode=4 ! \
video/x-raw, width=640, height=480, format=YUY2, framerate=30/1 ! \
videoconvert ! \
video/x-raw, format=BGR ! \
queue ! markerdetect ! queue ! \
videoconvert ! \
fpsdisplaysink sync=false text-overlay=false fullscreen-overlay=true \
\
-v
在啟動示例之前,我們要定義我們的 DISPLAY 環(huán)境變量,并配置我們的 DisplayPort 顯示器的分辨率。
$ export DISPLAY=:0.0
$ xrandr --output DP-1 --mode 800x600
該示例可以使用我們剛剛創(chuàng)建的腳本啟動:
$ ./launch_usb_markerdetect.sh
我第一次使用 USB 攝像頭執(zhí)行這個“markerdetect”gstreamer 插件讓我感到驚訝。我使用的 USB 相機是具有自動白平衡功能的羅技 C720,所以我希望看到藍色、綠色和紅色的平均值大致相同。
事實證明,DisplayPort 監(jiān)視器正在生成一種藍色色調(diào),該色調(diào)被圖表拾取,并略微扭曲了結(jié)果。
我跑了同樣的測試,這次遠離顯示器,結(jié)果更符合我的預期。
這一次,藍色、綠色和紅色的平均值大致相同。
結(jié)論
我希望本教程能激發(fā)您在 Ultra96-V2 上嘗試增強現(xiàn)實 (AR)。
你能想到其他應用程序會使用這些類型的標記嗎?
如果您還想看到任何其他相關內(nèi)容,請在下面的評論中分享您的想法。
?
- Ultra96硬件用戶指南
- Ultra96 CSI-2視頻輸出到Raspberry Pi攝像頭輸入
- Ultra96上的實時攝像頭饋送網(wǎng)頁
- 使用PYNQ的Ultra96面部識別鎖栓
- 使用Tensil、TF-Lite和PYNQ在Ultra96板上運行YOLO v4 Tiny
- Ultra96-V2上的頭部姿勢估計
- 在Ultra96 V2平臺上用Python實現(xiàn)人臉檢測和人臉跟蹤
- 用于Ultra96的夾層板96AnalogXperience
- Ultra96 FPGA上的Live NYC Subway Monitor應用程序
- 關于Ultra96的Xilinx DDS編譯器IP教程
- 與Ultra96聯(lián)網(wǎng)端口轉(zhuǎn)發(fā)
- 使用Ultra96 PYNQ測定織物GSM
- Ultra96皮膚癌AI構(gòu)建
- 2018.2 Ultra96:從 Matchbox 桌面關斷 PetaLinux BSP,無法關斷電路板
- 一起玩Ultra96之GPIO操作
- 大模型系列:Flash Attention V2整體運作流程 1296次閱讀
- PCB上那些米色標記/圖標/符號是什么?如何將其去除? 877次閱讀
- PINE64便攜烙鐵Pinecil V2版本 3398次閱讀
- 虛擬現(xiàn)實和增強現(xiàn)實存在什么潛在的問題 2721次閱讀
- dfrobotGravity: 模擬pH計V2介紹 2007次閱讀
- dfrobotGravity:模擬電導率計V2 (K=1)介紹 1440次閱讀
- 微雪電子ST-LINK/V2 STM32仿真器簡介 4285次閱讀
- 微雪電子迷你ST-LINK/V2 STM32仿真器 2985次閱讀
- 微雪電子ST-LINK/V2 STM32仿真器簡介 5772次閱讀
- 亞太天能科技V2指紋鎖簡介 1651次閱讀
- 微雪電子 樹莓派原裝攝像頭V2兼容模組介紹 2021次閱讀
- 微雪電子樹莓派原裝攝像頭V2簡介 2540次閱讀
- 基于Arm技術(shù)的16nm MPSoC開發(fā)套件Ultra96 6099次閱讀
- 增強現(xiàn)實技術(shù)的應用分析 8637次閱讀
- 增強現(xiàn)實技術(shù)和vr差異在哪 1787次閱讀
下載排行
本周
- 1山景DSP芯片AP8248A2數(shù)據(jù)手冊
- 1.06 MB | 532次下載 | 免費
- 2RK3399完整板原理圖(支持平板,盒子VR)
- 3.28 MB | 339次下載 | 免費
- 3TC358743XBG評估板參考手冊
- 1.36 MB | 330次下載 | 免費
- 4DFM軟件使用教程
- 0.84 MB | 295次下載 | 免費
- 5元宇宙深度解析—未來的未來-風口還是泡沫
- 6.40 MB | 227次下載 | 免費
- 6迪文DGUS開發(fā)指南
- 31.67 MB | 194次下載 | 免費
- 7元宇宙底層硬件系列報告
- 13.42 MB | 182次下載 | 免費
- 8FP5207XR-G1中文應用手冊
- 1.09 MB | 178次下載 | 免費
本月
- 1OrCAD10.5下載OrCAD10.5中文版軟件
- 0.00 MB | 234315次下載 | 免費
- 2555集成電路應用800例(新編版)
- 0.00 MB | 33566次下載 | 免費
- 3接口電路圖大全
- 未知 | 30323次下載 | 免費
- 4開關電源設計實例指南
- 未知 | 21549次下載 | 免費
- 5電氣工程師手冊免費下載(新編第二版pdf電子書)
- 0.00 MB | 15349次下載 | 免費
- 6數(shù)字電路基礎pdf(下載)
- 未知 | 13750次下載 | 免費
- 7電子制作實例集錦 下載
- 未知 | 8113次下載 | 免費
- 8《LED驅(qū)動電路設計》 溫德爾著
- 0.00 MB | 6656次下載 | 免費
總榜
- 1matlab軟件下載入口
- 未知 | 935054次下載 | 免費
- 2protel99se軟件下載(可英文版轉(zhuǎn)中文版)
- 78.1 MB | 537798次下載 | 免費
- 3MATLAB 7.1 下載 (含軟件介紹)
- 未知 | 420027次下載 | 免費
- 4OrCAD10.5下載OrCAD10.5中文版軟件
- 0.00 MB | 234315次下載 | 免費
- 5Altium DXP2002下載入口
- 未知 | 233046次下載 | 免費
- 6電路仿真軟件multisim 10.0免費下載
- 340992 | 191187次下載 | 免費
- 7十天學會AVR單片機與C語言視頻教程 下載
- 158M | 183279次下載 | 免費
- 8proe5.0野火版下載(中文版免費下載)
- 未知 | 138040次下載 | 免費
評論
查看更多