一、前言
最近需要在 Linux 平臺下開發一個人臉識別相關的應用,用到了虹軟的人臉識別 SDK。之前在 Windows 平臺用過,感覺不錯,SDK 裡面還帶了 Demo 可以快速看到效果。開啟 Linux 版本的 SDK 裡面沒有發現 Demo,於是想著把 Windows 的 Demo 移植到 Linux。這篇文章記錄了移植的過程,Linux 用的是 Ubuntu 20.04(使用虛擬機器 VMware Workstation 15 Player)。
二、配置依賴
2.1 ArcFace SDK
到虹軟官網下載人臉識別 SDK 3.1 Linux 增值版本 解壓到合適的目錄,並從官網獲取 APP_ID、SDK_KEY 和 ACTIVE_KEY,用於寫到配置檔案用來啟用 SDK。
2.2 OpenCV
到 OpenCV 官網下載原始碼,我用的版本是 3.4.9。可以按照官網的教程 Installation in Linux 自行編譯,我參考官網教程使用下面的這些命令在 GCC 9.3.0(Ubuntu 20.04 自帶的編譯器) 上編譯成功。
sudo apt update
sudo apt install build-essential
sudo apt install cmake git libgtk2.0-dev pkg-config libavcodec-dev libavformat-dev libswscale-dev
cd <OpenCV 原始碼目錄>
mkdir build
cd build
cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=<自定義目錄> ..
make -j3 # 可以使用核心數 - 1 個執行緒來編譯
sudo make install
2.3 Qt
Qt 使用的是 5.14.2 版本。
三、專案檔案
3.1 .pro 檔案
原 Windows Demo 使用的是 Visual Studio 2015,在 Linux 下我這裡用到了 Qt Creator 進行開發,因此需要編寫 .pro 檔案,包括以下幾個方面:
- 用到的 Qt 模組
- 編譯出來的程式名
- 用到的標頭檔案、原始檔和資原始檔
- 依賴庫的標頭檔案及庫名
更具體的內容檢視文末提供的原始碼。
3.2 檔案編碼
原原始碼檔案使用的是 GBK 編碼,需要轉換為 UTF-8 編碼。將下面的命令儲存到 convert.sh
檔案中並用 chmod u+x convert.sh
賦予可執行許可權。
#!/bin/bash
for i in "$@"; do
desc=$(file "$i")
if $(echo $desc | grep -i "UTF-8 Unicode" > /dev/null); then
if $(echo $desc | grep -i "(with BOM)" > /dev/null); then
echo "Remove UTF-8 BOM: " $i
sed -i "1s/^\xef\xbb\xbf//" "$i"
fi
elif $(echo $desc | grep -i "ISO-8859" > /dev/null); then
echo "GBK --> UTF-8 : " $i
temp=temp.txt
iconv -f gbk -t utf-8 -o "$temp" "$i"
mv "$temp" "$i"
fi
done
在原始碼根目錄下執行 find . -type f \( -name "*.h" -o -name "*.cpp" \) | xargs -I{} ./convert.sh "{}"
將所有的檔案的編碼從 GBK 轉為 UTF-8,並去除現有 UTF-8 檔案的 BOM 頭。
四、程式碼修改
到這裡已經可以用 Qt Creator 開啟專案了,但在程式碼中還存在一些問題,一方面是原始碼使用了一些 Windows 平臺特有的 API,一方面是有些程式碼在 Linux 有相容性問題。先去除 Windows 特有的依賴到編譯通過,再補充必要的依賴,最後解決相容性問題。
4.1 修改報錯直至編譯通過
直接進行編譯,逐步解決編譯錯誤,通過下面的方式可以解決編譯錯誤:
- 刪除 Utils.cpp 中報錯的標頭檔案、GUID 巨集、listDevices 函式的主體、UTF8_To_string 和 string_To_UTF8 函式。
- 將所有包含的
qDebug
改為QDebug
。 - 將
Sleep(milli)
改為std::this_thread::sleep_for(std::chrono::milliseconds(milli))
。 - 刪除 MSVC 的連結庫的編譯指令
#pragma comment ...
。 - 將原來使用 OpenCV 2 的介面遷移到目前的 OpenCV 3。
- IplImage 到 cv::Mat 的轉換由
cv::Mat mat(ipl, false)
改成cv::Mat mat = cv::cvarrToMat(ipl)
。 - cv::Mat 到 IplImage 的轉換由
IplImage(mat)
改成cvIplImage(mat)
,在原來的程式碼裡 cv::Mat 轉為 IplImage 後有個取地址,對右值取地址是不安全的,需要用一個變數儲存轉換後的值再對這個變數取地址。 - 在呼叫 cvRectangle 時將
CV_RGB
改成cvScalar
。 - 使用
cv::cvtColor
需要額外包含標頭檔案opencv2/imgproc.hpp
。 - 使用
cv::VideoCapture
需要額外包含標頭檔案opencv2/videoio.hpp
- IplImage 到 cv::Mat 的轉換由
- 將
strcpy_s
改成strncpy
,僅有引數位置上的改變。 - 將
TRUE
改為true
,將FALSE
改為false
。
改了編譯錯誤後,忽略警告已經可以編譯通過了,接下來是補充剛才刪除的一些必要依賴及解決相容性問題。
因為環境差異,可能出現錯誤的順序不一致,但基本上是上面提到的錯誤之一。
4.2 重新實現獲取攝像頭列表的函式
原 Windows Demo 使用了 Windows 特有的 dshow 來查詢攝像頭,在這裡直接用 cv::VideoCapture 嘗試開啟來獲取攝像頭的索引:
auto list = std::vector<int>();
for (auto i = 0; i != 10; ++i)
{
auto cap = cv::VideoCapture(i);
if (cap.isOpened()) { list.emplace_back(i); }
cap.release();
}
Demo 可以只開啟一個RGB攝像頭,也可以同時開啟一個RGB攝像頭和一個IR攝像頭。原始碼儲存獲取攝像頭的名稱,僅用來統計數量,具體開啟哪個攝像頭是通過settings.ini
檔案來配置的。在改變探測攝像頭存在的數量的方式後,順帶改變了開啟攝像頭的邏輯,僅一個攝像頭就認為是僅開啟普通攝像頭。在settings.ini
檔案中配置兩種攝像頭的索引,如果索引為 -1,則自動把小的索引認為是普通攝像頭,大的索引認為是紅外攝像頭,如果和真實情況不一致可手動指定攝像頭索引。
settings.ini
檔案在後面執行 Demo 時會有更多的說明。
4.3 修復彈出檔案選擇框失敗的相容性問題
在 Ubuntu 20.04 下,Qt 的 QFileDialog::getOpenFileName 和 QFileDialog::getExistingDirectory 存在一些問題,在開啟時會卡死介面,通過將最後一個引數設定為 QFileDialog::DontUseNativeDialog
可以解決這個問題。
五、執行 Demo
5.1 介面預覽
5.2 配置
- 配置檔案已經隨原始碼打包好了,在執行時需要移動到可執行程式所在的同級目錄下。
- 在配置檔案中填入官網獲取的 APP_ID、SDK_KEY 和 ACTIVE_KEY。
- 編譯並執行。