Linux安裝prebuild函式庫以OpenCV為例

我的部落格文章轉錄–Linux環境撰寫Shared Library有詳細介紹如何製作和安裝Shared Library,如果想要了解更多Shared Library安裝和製作方式可以參考這篇。
{: .prompt-tip }

在這篇我們想直接使用OpenCV預編譯的函式庫,省去自己編譯函式庫的時間,首先我們先到官網提到的Third-party packages的System packages in popular Linux distributions,找到自己的Linux distributions,在這裡我們是使用Ubuntu22,而我們要下載的是development files for opencv也就是libopencv-dev_4.5.4+dfsg-9ubuntu4_amd64.deb這個連結,在Install Howto的地方可以看到安裝指令。

如果你不想弄亂你的環境,建議你可以用Vagrant建立一台測試環境來測試一下安裝後的結果,或是做一些實驗。
安裝Vagrant的方法在安裝vagrant製作測試環境
{: .prompt-tip }

1
2
sudo apt-get update
sudo apt-get install libopencv-dev

Files的地方可以看到他幫我們裝了什麼東西以及他們被安裝的位置。在Requires的地方可以看到他還幫我們安裝了哪些相依套件。
在這裡我們比較一下libopencv-core-devlibopencv-core4.5d這兩個函式庫。在這兩個package的Files的地方可以發現libopencv-core-dev幫我們在/usr/include/opencv4多裝了很多標頭檔(*.hpp),因為如果要在我們自己的C++中使用OpenCV,必須要include OpenCV的標頭檔,而dev套件已經幫我們幫把標頭檔都放在/usr/include/opencv4讓我們可以引用了。而在編寫OpenCV的C++專案的時候,要記得把這個include資料夾放到你的專案裡。

pkg-config幫我們列出全部的標頭檔位置和opencv的名稱

標頭檔路徑只需要加上g++選項-I/usr/include/opencv4就可以了,不過如果要把所有用到的library都手動寫出來實在很麻煩,這時候pkg-config可以幫我們把全部的opencv library全部列出來,我們可以試看看在終端機輸入下面指令pkg-config --libs --cflags opencv4(如果安裝的是opencv 2.x或3.x要輸入pkg-config --libs --cflags opencv),終端機的回應應該會長的像下面這樣。

1
2
$ pkg-config --libs --cflags opencv4
-I/usr/include/opencv4 -lopencv_stitching -lopencv_alphamat -lopencv_aruco -lopencv_barcode -lopencv_bgsegm -lopencv_bioinspired -lopencv_ccalib -lopencv_dnn_objdetect -lopencv_dnn_superres -lopencv_dpm -lopencv_face -lopencv_freetype -lopencv_fuzzy -lopencv_hdf -lopencv_hfs -lopencv_img_hash -lopencv_intensity_transform -lopencv_line_descriptor -lopencv_mcc -lopencv_quality -lopencv_rapid -lopencv_reg -lopencv_rgbd -lopencv_saliency -lopencv_shape -lopencv_stereo -lopencv_structured_light -lopencv_phase_unwrapping -lopencv_superres -lopencv_optflow -lopencv_surface_matching -lopencv_tracking -lopencv_highgui -lopencv_datasets -lopencv_text -lopencv_plot -lopencv_ml -lopencv_videostab -lopencv_videoio -lopencv_viz -lopencv_wechat_qrcode -lopencv_ximgproc -lopencv_video -lopencv_xobjdetect -lopencv_objdetect -lopencv_calib3d -lopencv_imgcodecs -lopencv_features2d -lopencv_dnn -lopencv_flann -lopencv_xphoto -lopencv_photo -lopencv_imgproc -lopencv_core

g++的指令是長這樣

1
/usr/bin/g++ -g main.cpp -o main `pkg-config --libs --cflags opencv4`

你可以到linux下設定vscode-cmake-gcc-gdb來開發c-專案查看如何用VSCode連接和編譯OpenCV library。

參考:
https://blog.gtwang.org/programming/ubuntu-linux-install-opencv-cpp-python-hello-world-tutorial/
https://pkgs.org/search/?q=opencv
https://ubuntu.pkgs.org/22.04/ubuntu-universe-amd64/libopencv-dev_4.5.4+dfsg-9ubuntu4_amd64.deb.html

文章轉錄--Linux環境撰寫Shared Library

撰寫範例程式

1
bool isPalindrome(char* word);

{: file=”pal.h” }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include "pal.h"
#include <string.h>

bool isPalindrome(char* word)
{
bool ret = true;

char *p = word;
int len = strlen(word);
char *q = &word[len-1];

for (int i = 0 ; i < len ; ++i, ++p, --q)
{
if (*p != *q)
{
ret = false;
}
}

return ret;
}

{: file=”pal.cpp” }

編譯shared library

在終端機輸入以下GCC指令,注意這裡我們有-c選項,這告訴GCC不要進行linking stage,如果沒加GCC就會報錯,因為一個應用程式一定會有main()函式,但是Library不需要main()函式。這行指令將會產生一個pal.o

1
g++ -fPIC -c -Wall pal.cpp

接下來我們要用pal.o檔和以下指令產生真正的library。ld是linker program,通常會被g++呼叫。-shared告訴ld製作一個shared object,以pal.o為輸入輸出名為libpal.so。在Linux通常shared libraries的副檔名都是.so。

1
ld -shared pal.o -o libpal.so

使用shared library

首先建立一個main.cpp來呼叫我們的library的isPalindrome函式。在程式中我們include函式庫的標頭檔pal.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include "pal.h"
#include <iostream>

using namespace std;

int main()
{
while (1)
{
char buffer[64] = {0};
cin >> buffer;

if (isPalindrome(buffer))
{
cout << "Word is a palindrome" << endl;
}
else
{
cout << "Word is not a palindrome" << endl;
}
}

return 0;
}

{: file=”main.cpp” }

接下來我們可能直接用g++ -Wall main.cpp -lpal這行指令連接我們的shared library,c但是如果這麼做我們會得到下面錯誤訊息。這是因為ld在預設的搜尋路徑下找不到libpal.so

1
2
/usr/bin/ld: cannot find -lpal: No such file or directory
collect2: error: ld returned 1 exit status

其中一種解法是用g++告入ld函式庫的位置

1
g++ -Wall -L<libpal.so的路徑> -Wl,-rpath=<libpal.so的路徑> main.cpp -lpal

例如

1
g++ -Wall -L/home/faye -Wl,-rpath=/home/faye/ main.cpp -lpal

其中:

  • -Wall是用來檢查所有編譯警告的
  • L式shared library的路徑,讓ld知道要去那裡尋找
  • -Wl是一連串用逗號分隔的linker指令,在這裡有
    • -rpath表示library 的路徑會被嵌入到主程式的執行檔,因此loader在執行主程式的時候可以找到library

-L-rpath的區別是:

  • -L是給linker用的
  • -rpath是被嵌入到執行檔給loader看的

最後g++會幫我們產生a.out執行檔,執行方式和結果如下

1
2
3
4
5
$ ./a.out 
ada
Word is a palindrome
team
Word is not a palindrome

安裝自己開發的Shared Libraries

用剛剛的方式連接Shared Libraries的方法有個缺點,也就是你的編譯指令直接給其他人的話可能會出錯,因為Shared Libraries在每個人的電腦上的位置可能都不一樣。因此rpath-L選項的路徑也會不一樣。而我們的解決方法之一就是LD_LIBRARY_PATH

LD_LIBRARY_PATH

如果我們直接在終端機輸入指令echo $LD_LIBRARY_PATHLD_LIBRARY_PATH的值,他應該會是空的,除非你以前曾經設定過他。要是用LD_LIBRARY_PATH我們只需要以下指令

1
export LD_LIBRARY_PATH=<Shared Library所在資料夾>:$LD_LIBRARY_PATH

例如

1
export LD_LIBRARY_PATH=/home/faye:$LD_LIBRARY_PATH

這時在輸入一次echo $LD_LIBRARY_PATH應該就會輸出/home/faye:。而這時候我們編譯main.cpp的時候就算沒有-rpath選項編譯出來的執行檔也不會找不到libpal.so

1
g++ -Wall -L/home/faye/sotest main.cpp -lpal

你可以先嘗試看看在還沒設定LD_LIBRARY_PATH之前或是利用unset LD_LIBRARY_PATH指令清空LD_LIBRARY_PATH變數,所編譯出來的執行檔會出現什麼問題。
你應該會發現編譯的過程沒有任何錯誤訊息,但是一旦你執行編譯出來的執行檔a.out就會跳出錯誤訊息./a.out: error while loading shared libraries: libpal.so: cannot open shared object file: No such file or directory,這表示執行檔loader找不到libpal.so
{: .prompt-tip }

不過LD_LIBRARY_PATH其實只適合在開發階段拿來測試library用,因為它不需要root權限,但是函式庫發布給大家使用的時候,要求每個人都去設定LD_LIBRARY_PATH並不是個好方法。

ldconfig : 安裝Shared Libraries正統方法

首先我們先清除上一個教學的LD_LIBRARY_PATH設定,可以用unset LD_LIBRARY_PATH指令來清除。
接下來我們必須把我們的函式庫複製到/usr/lib資料夾。複製函式庫到/usr/lib必須擁有root權限,因此我們用sudo來幫助我們。

1
sudo mv <函式庫所在資料夾>/libpal.so /usr/lib

例如

1
sudo mv /home/faye/libpal.so /usr/lib

接下來更新系統中儲存可用libraries的cache。

1
sudo ldconfig

你可以用下面指令來確定cache已經被更新了而且系統可以找到你的函式庫

1
ldconfig -p | grep libpal

系統應該會回應你

1
libpal.so (libc6,x86-64) => /lib/libpal.so

接下來你就可以用下面指令編譯我們的執行檔了,而且這次我們不需要-rpath,也不需要-L選項!!因為現在我們的函式庫已經位於系統預設的函式庫搜尋路徑下了。

1
g++ -Wall main.cpp -lpal

參考:
https://www.fayewilliams.com/2015/07/07/creating-a-shared-library/
https://www.fayewilliams.com/2015/07/14/installing-and-accessing-shared-libraries/

相關文章:
Linux安裝prebuild函式庫以OpenCV為例

Linux下設定VScode、CMake、GCC、GDB來開發C++專案

安裝套件

  • cmake extension

  • c++ extension
    cmake extension{: w=”700” h=”400” }
    c++ extension{: w=”700” h=”400” }

  • 安裝編譯器GCC、除錯器DBG

    1
    2
    3
    sudo apt-get update
    sudo apt-get install build-essential gdb
    gcc -v #確認GCC安裝成功

注意:
GDB 12.09 版本在VSCode除錯的時候會有錯誤,可以參考下面方式將GDB升級到12.10以上版本
升級GDB
GDB issue
{: .prompt-warning }

  • 安裝CMake
    直接用sudo apt install cmake,安裝的版本會比較舊,因此如果想用CMake最新的功能可以按照官方網站的安裝方式,安裝後我們可以測試一下CMake安裝成功,在這裡我們CMake的版本至少要大於3.15
    1
    cmake --version

編譯C++檔

用以下指令建立一個專案資料夾,最後一行code .會直接打開一個新的VScode並且以這個資料夾作為工作目錄

1
2
3
4
5
mkdir projects
cd projects
mkdir helloworld
cd helloworld
code .

在helloworld資料夾建立helloworld.cpp檔案並且寫入以下程式碼,然後按下編譯按鈕,並且選擇g++作為編譯器(如下圖所示)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <iostream>
#include <vector>
#include <string>

using namespace std;

int main()
{
vector<string> msg {"Hello", "C++", "World", "from", "VS Code", "and the C++ extension!"};

for (const string& word : msg)
{
cout << word << " ";
}
cout << endl;
}

c++ run compiler{: w=”700” h=”400” }
c++ chose compiler{: w=”700” h=”400” }

成功編譯後,你會在Terminal看到程式成功輸出文字
c++ run success{: w=”700” h=”400” }

第一次按下執行compiler後,VScode會幫你建立一個.vscode資料夾和一個,tasks.json。或是你可以自己建立一個.vscode資料夾並且放入tasks.json

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
{
"tasks": [
{
"type": "cppbuild",
"label": "C/C++: gcc-9 build active file",
"command": "/usr/bin/gcc-9",
"args": [
"-fdiagnostics-color=always",
"-g",
"${file}",
"-o",
"${fileDirname}/${fileBasenameNoExtension}"
],
"options": {
"cwd": "${fileDirname}"
},
"problemMatcher": [
"$gcc"
],
"group": {
"kind": "build",
"isDefault": true
},
"detail": "Task generated by Debugger."
}
],
"version": "2.0.0"
}

{: file=’tasks.json’}

在這個資料夾下可以有三種檔案,每個檔案各有自己的用處,關於tasks.json詳細設定可以參考這裡

  • tasks.json (compiler build settings)
  • launch.json (debugger settings)
  • c_cpp_properties.json (compiler path and IntelliSense settings)

tasks.json裡面有幾個的比較重要的點

  • args是給GCC的參數,要符合GCC參數的順序
  • 在這裡我們用${file}這個變數告訴GCC目前打開的檔案讓他編譯
  • ${fileDirname}這個變數告訴GCC目前的資料夾位置,讓他在這個位置產生我們的執行檔
  • ${fileBasenameNoExtension}變數取出目前開啟的檔名但是不包含副檔名,我們用這個名字作為我們的執行檔名,也就是helloworld
  • label會顯示在task清單 detail 會顯示在task清單的詳細描述。先按下Ctrl+P並且輸入task (task後面有空白),就會顯示task清單,包含我們建立的task(如下圖所示)
  • 如果有多個task,可以利用group裡面isDefault屬性設定預設task

c++ task list{: w=”700” h=”400” }

除錯、設中斷點

  • 在程式碼下一個中斷點,並且點及旁邊的執行按鈕並且選擇Debug就可以開始除錯了
    c++ breakpoint{: w=”700” h=”400” }
    c++ run debug{: w=”700” h=”400” }
    c++ selece run debug{: w=”350” h=”200” }

設定在啟動程式時傳入參數給程式

如果像在啟動程式時傳入一些參數,可以利用launch.json,要建立launch.json只需要案價旁邊的齒輪並選擇G++,VSCode就會自動幫你建立一份
c++ add launch{: w=”350” h=”200” }
c++ add launch{: w=”700” h=”400” }

launch.json裡面有幾個的比較重要的點

  • program是要執行的執行檔名稱,也就是我們編譯後產生的helloworld
  • args是執行時要傳給執行檔的參數

C/C++的其他設定

通常編寫C/C++的時候也會用到很多函式庫,我們可以指定這些函式庫的路徑讓VSCode的Intelligence Scope懂這些函式庫。
首先按下Ctrl+Shift+P並且輸入C/C++,按下C/C++: Edit Configurations (UI),後VSCode會幫我們產生一個c_cpp_properties.json
Include path的地方加入我們要包含的路徑。或者我們也可以在c_cpp_properties.jsonconfigurations下的includePath直接做修改

c++ config{: w=”700” h=”400” }

g++ include OpenCV函式庫

首先我們先按照Linux安裝prebuild函式庫以OpenCV為例這篇文章安裝prebuild的OpenCV library。
接下來建立一個ShowImage.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <opencv2/core.hpp>
#include <opencv2/imgcodecs.hpp>
#include <opencv2/highgui.hpp>
#include <iostream>
using namespace cv;
int main()
{
std::string image_path = samples::findFile("starry_night.jpg");
Mat img = imread(image_path, IMREAD_COLOR);
if(img.empty())
{
std::cout << "Could not read the image: " << image_path << std::endl;
return 1;
}
imshow("Display window", img);
int k = waitKey(0); // Wait for a keystroke in the window
if(k == 's')
{
imwrite("starry_night.png", img);
}
return 0;
}

{: file=’ShowImage.cpp’}

我們可以發現VSCode的Intelligence scope找不到OpenCV標頭檔而顯示紅色虛線。
opencv not found{: w=”700” h=”400” }

這時候我們需要修改前面步驟提到的c_cpp_properties.jsoninclude OpenCV的路徑就可以了,而因為我們是安裝prebuild的OpenCV,所以我們的標頭檔已經被安裝在/usr/include/opencv4/裡面,因此修改後的c_cpp_properties.json如下。我們在includePath的清單中加入了/usr/include/opencv4/**,之後你就可以看到Intelligence scope成功認出OpenCV,而且OpenCV的函示可以順利顯示說明文字

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
{
"configurations": [
{
"name": "Linux",
"includePath": [
"${workspaceFolder}/**",
"/usr/include/opencv4/**"
],
"defines": [],
"compilerPath": "/usr/bin/gcc",
"cStandard": "gnu17",
"cppStandard": "gnu++17",
"intelliSenseMode": "linux-gcc-x64"
}
],
"version": 4
}

{: file=’c_cpp_properties.json’}
opencv show usuage{: w=”700” h=”400” }

不過這時候g++依然不知道OpenCV的標頭檔在哪裡,所以如果直接編譯還是會出錯。

pkg-config幫我們列出全部的標頭檔位置和opencv的名稱

標頭檔路徑只需要加上g++選項-I/usr/include/opencv4就可以了,不過如果要把所有用到的library都手動寫出來實在很麻煩,這時候pkg-config可以幫我們把全部的opencv library全部列出來,我們可以試看看在終端機輸入下面指令pkg-config --libs --cflags opencv4(如果安裝的是opencv 2.x或3.x要輸入pkg-config --libs --cflags opencv),終端機的回應應該會長的像下面這樣。

1
2
$ pkg-config --libs --cflags opencv4
-I/usr/include/opencv4 -lopencv_stitching -lopencv_alphamat -lopencv_aruco -lopencv_barcode -lopencv_bgsegm -lopencv_bioinspired -lopencv_ccalib -lopencv_dnn_objdetect -lopencv_dnn_superres -lopencv_dpm -lopencv_face -lopencv_freetype -lopencv_fuzzy -lopencv_hdf -lopencv_hfs -lopencv_img_hash -lopencv_intensity_transform -lopencv_line_descriptor -lopencv_mcc -lopencv_quality -lopencv_rapid -lopencv_reg -lopencv_rgbd -lopencv_saliency -lopencv_shape -lopencv_stereo -lopencv_structured_light -lopencv_phase_unwrapping -lopencv_superres -lopencv_optflow -lopencv_surface_matching -lopencv_tracking -lopencv_highgui -lopencv_datasets -lopencv_text -lopencv_plot -lopencv_ml -lopencv_videostab -lopencv_videoio -lopencv_viz -lopencv_wechat_qrcode -lopencv_ximgproc -lopencv_video -lopencv_xobjdetect -lopencv_objdetect -lopencv_calib3d -lopencv_imgcodecs -lopencv_features2d -lopencv_dnn -lopencv_flann -lopencv_xphoto -lopencv_photo -lopencv_imgproc -lopencv_core

我們可以發現回傳的文字可以直接當成g++的選項來用,因此我們修改tasks.json,在給g++的參數args的list加入

1
"`pkg-config --libs --cflags opencv4`"

改好後tasks.json如下,注意pkg-config --libs --cflags opencv4被兩個引號包圍,因為原本g++的指令是長這樣

1
/usr/bin/g++ -g main.cpp -o main `pkg-config --libs --cflags opencv4`
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
{
"tasks": [
{
"type": "cppbuild",
"label": "C/C++: g++ build active file",
"command": "/usr/bin/g++",
"args": [
"-fdiagnostics-color=always",
"-g",
"${file}",
"-o",
"${fileDirname}/${fileBasenameNoExtension}",
"`pkg-config --libs --cflags opencv4`"
],
"options": {
"cwd": "${fileDirname}"
},
"problemMatcher": [
"$gcc"
],
"group": {
"kind": "build",
"isDefault": true
},
"detail": "Task generated by Debugger."
}
],
"version": "2.0.0"
}

{: file=’tasks.json’}

接下來VSCode就可以順利編譯程式並產生執行檔了。

用CMake管理跨Windows和Linux的C++專案以Darknet為例

如果開發團隊中有人用Windows OS開發,有人用Linux OS開發,而Windows的人只熟悉Visual Studio。這時候要一起合作完成專案在編譯的時候就會遇到困難。CMake跨平台的專案產生器就是專門解決這個問題。只要寫好一次CMake檔,他就可以在Windows幫你產生Visual Studio專案檔.sln,或是在Linux產生Makefile。很多時候我們會想要直接編譯別人的開源程式,這裡我們將以Darknet作為範例。開始這個範例之前你必須先在電腦上安裝好OPenCV

下載Darknet

1
2
git clone git@github.com:AlexeyAB/darknet.git
cd darknet

建立CMake的task.json

按下ctrl + shift + p並且輸入task,選擇Tasks: Configure task,然後再選擇CMake: build,就task設定就會產生在${workspaceFolder}/.vscode/tasks.json
config cmake tasks

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
{
"version": "2.0.0",
"tasks": [
{
"type": "cmake",
"label": "CMake: build",
"command": "build",
"targets": [
"all"
],
"group": "build",
"problemMatcher": [],
"detail": "CMake template build task"
}
]
}

{: file=’task.json’}
這個檔案會編譯所有的CMake Targets,你也可以指定你想編譯的targets。

設定build參數

首先我們先查看Darknet有哪些CMake項可以設定,輸入cmake -S . -B build -LH就可以看到選項。在這個範例不想要使用CUDA,所以要把ENABLE_CUDA:BOOL=ON設為OFF。我們可以建立一個設定檔在${workspaceFolder}/.vscode/settings.json,並且輸入以下內容。更多其他的CMake設定可以在這裡查到。另外因為Darknet已經存在build資料夾,所以我們指定其他資料夾作為build資料夾cmake.buildDirectory": "${workspaceFolder}/local-build,不過通常我們可以直接用預設值,並不需要另外設定cmake.buildDirectory

1
2
3
4
5
6
7
{
"cmake.configureArgs": [
"-DENABLE_CUDA=OFF",
"-DVCPKG_BUILD_OPENCV_WITH_CUDA=OFF"
],
"cmake.buildDirectory": "${workspaceFolder}/local-build"
}

CMake Config

首先我們要先跑一次CMake Config,按下ctrl + shift + p輸入cmake選擇CMake:Configure,在Output的地方會看到有沒有錯誤,如果出現錯誤歡迎在下面留言板留言。
run cmake config

建立launch.json

接下來要設定啟動,先建立一個launch.json位於${workspaceFolder}/.vscode/launch.json,並且複製以下內容。在這裡用到許多CMake extension提供的變數,可以在這裡查詢更多的變數。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
{
"version": "0.2.0",
"configurations": [
{
"name": "(gdb) Launch",
"type": "cppdbg",
"request": "launch",
// Resolved by CMake Tools:
"program": "${command:cmake.launchTargetPath}",
"args": [],
"stopAtEntry": false,
"cwd": "${workspaceFolder}",
"environment": [
{
// add the directory where our target was built to the PATHs
// it gets resolved by CMake Tools:
"name": "PATH",
"value": "${env:PATH}:${command:cmake.getLaunchTargetDirectory}"
},
{
"name": "OTHER_VALUE",
"value": "Something something"
}
],
"console": "externalTerminal",
"MIMode": "gdb",
"setupCommands": [
{
"description": "Enable pretty-printing for gdb",
"text": "-enable-pretty-printing",
"ignoreFailures": true
}
]
}
]
}

{= file:’launch.json’}

設定啟動時參數

首先要先下載yolov1.cfgyolov1.weights並且放到cfg資料夾
Darknet執行檔啟動時需要參數,我們可以在launch.jsonargs設定。

1
"args": ["detect", "cfg/yolov1.cfg", "cfg/yolov1.weights", "data/dog.jpg"],

執行

參考:

Build with CMake Tools
https://github.com/microsoft/vscode-cmake-tools/blob/f4804bcd2d376b4ad850c537d6ebdae46cfdcf3c/docs/build.md

https://code.visualstudio.com/docs/cpp/config-linux

Display an image in an OpenCV window
https://docs.opencv.org/4.x/db/deb/tutorial_display_image.html

pkg-config尋找opencv函式庫
https://answers.opencv.org/question/227890/using-l-in-g-command-line/

Get started with CMake Tools on Linux
https://code.visualstudio.com/docs/cpp/cmake-linux

CMake Tools for Visual Studio Code documentation
https://github.com/microsoft/vscode-cmake-tools/blob/main/docs/README.md

Guide: “A modern, open source C++ dev environment with Visual Studio Code, vcpkg, and CMake”
https://www.reddit.com/r/cpp/comments/j1dh9w/guide_a_modern_open_source_c_dev_environment_with/

Ubuntu NTP 校時

timedatectl 時間管理工具

  • 顯示目前的設定狀態
    1
    2
    # 顯示目前狀態
    timedatectl
1
2
3
4
5
6
7
               Local time: 三 2023-04-26 11:00:22 CST
Universal time: 三 2023-04-26 03:00:22 UTC
RTC time: 三 2023-04-26 03:00:22
Time zone: Asia/Taipei (CST, +0800)
System clock synchronized: yes
NTP service: active
RTC in local TZ: no
  • 啟動網路校時
    輸入下面指令後,就會打開網路校時功能

    1
    2
    # 啟用 NTP 校時
    timedatectl set-ntp yes

    Ubuntu使用的是systemd-timesyncd,因此等一下要設定systemd-timesyncd

  • 檢查systemd-timesyncd服務狀態

    1
    2
    # 檢查 systemd-timesyncd 服務狀態
    systemctl status systemd-timesyncd
1
2
3
4
5
6
7
8
9
10
● systemd-timesyncd.service - Network Time Synchronization
Loaded: loaded (/lib/systemd/system/systemd-timesyncd.service; enabled; vendor preset: enabled)
Active: active (running) since Wed 2023-04-26 10:58:34 CST; 1min 19s ago
Docs: man:systemd-timesyncd.service(8)
Main PID: 221565 (systemd-timesyn)
Status: "Synchronized to time server 91.189.89.198:123 (ntp.ubuntu.com)."
Tasks: 2 (limit: 38317)
Memory: 1.2M
CGroup: /system.slice/systemd-timesyncd.service
└─221565 /lib/systemd/systemd-timesyncd
  • 設定校時伺服器
    要設定校時伺服器可以用root權限編輯以下檔案/etc/systemd/timesyncd.conf
1
2
3
4
5
6
[Time]
# NTP 伺服器(以空白分隔多個伺服器)
NTP=tw.pool.ntp.org jp.pool.ntp.org

# 備用 NTP 伺服器(以空白分隔多個伺服器)
FallbackNTP=sg.pool.ntp.org ntp.ubuntu.com
  • 重新啟動systemd-timesyncd服務
    重啟服務讓更改生效

    1
    2
    # 重新啟動 systemd-timesyncd 服務
    systemctl restart systemd-timesyncd
  • 檢查一下是不是有跟NTP校時了
    用指令systemctl status systemd-timesyncd看一下目前服務狀態,如果有更新成功會顯示出來

錯誤排除

  • 出現Server has too large root distance. Disconnecting.訊息
    表示機器跟ntp server之間回應的時間太久,因此可以去修改/etc/systemd/timesyncd.conf並加入RootDistanceMaxSec=,通常30秒已經很夠用了
    1
    2
    3
    4
    5
    6
    7
    # See timesyncd.conf(5) for details.
    [Time]
    NTP=10.10.1.30
    #FallbackNTP=
    RootDistanceMaxSec=30
    #PollIntervalMinSec=32
    #PollIntervalMaxSec=2048

timedatectl的NTP service為active的時候查看目前使用的是哪一個NTP服務

  • systemd-timesyncd
    1
    systemctl status systemd-timesyncd.service

ntpd 或 chronyd

1
ps -e | grep ntp
1
ps -e | grep chrony

chronyc說明書

https://chrony-project.org/doc/4.6.1/chronyc.html

詳細說明如下
https://unix.stackexchange.com/a/655489

參考:
https://www.cnblogs.com/pipci/p/12833228.html
https://officeguide.cc/ubuntu-linux-timedatectl-time-synchronization-tutorial/
https://www.digitalocean.com/community/tutorials/how-to-set-up-time-synchronization-on-ubuntu-20-04
https://www.tenable.com/audits/items/CIS_Ubuntu_18.04_LTS_Server_v2.1.0_L1.audit:26286d27c59292cfdb9c7b04593edbed
https://serverfault.com/questions/1024770/ubuntu-20-04-time-sync-problems-and-possibly-incorrect-status-information

FastAPI 印出422 unprocessable entity詳細資料

1
2
3
4
5
6
7
8
9
10
11
12
13
import logging
from fastapi import FastAPI, Request, status
from fastapi.exceptions import RequestValidationError
from fastapi.responses import JSONResponse

app = FastAPI()

@app.exception_handler(RequestValidationError)
async def validation_exception_handler(request: Request, exc: RequestValidationError):
exc_str = f'{exc}'.replace('\n', ' ').replace(' ', ' ')
logging.error(f"{request}: {exc_str}")
content = {'status_code': 10422, 'message': exc_str, 'data': None}
return JSONResponse(content=content, status_code=status.HTTP_422_UNPROCESSABLE_ENTITY)

參考:
https://github.com/tiangolo/fastapi/issues/3361#issuecomment-1002120988

X11 Server顯示遠端GUI

X Window 的Client和Server角色

一般來說如果遠端連線到一台電腦,通常遠端那台電腦是Server,但是對於X Window來說,顯示畫面的才是Server,因此如果你要遠端觀看遠端的電腦畫面,你手上的電腦是X Server,遠端的電腦是X Client

X Server的Port

當你啟動X Server的時候,他會開始監聽6000port,更詳細的來說X Server會給定display number,而每個display number所監聽的port為6000+display number

X11 forwdarding

如果你的電腦在防火牆後,這時候6000port不一定有打開,而X11 forwdarding不只幫打開一條通道讓遠端電腦可以連到你的6000port,同時還幫你處理好X serve連線的事情。

step1. Ubuntu 安裝x11套件

1
sudo apt install x11-apps

step2. 設定遠端電腦開啟x11

修改遠端電腦的/etc/ssh/sshd_config檔案,確認檔案內X11Forwarding yes
enable x11{: w=”350” h=”200” }

step3. 檢查x11設定

用以下指令確認設定正確

1
sudo cat /etc/ssh/sshd_config |grep -i X11Forwarding

在Windows下安裝VcXsrv Windows X Server

在windows下必須安裝x server,可以到以下連結下載
https://sourceforge.net/projects/vcxsrv/

問題排除

  1. Warning: untrusted X11 forwarding setup failed: xauth key data not generated

https://serverfault.com/a/355986

測試

Docker container 透過ssh x11 forwarding傳送影像到遠端電腦

以DeepStream container 為例

1
docker run --gpus all -it --rm --net=host --privileged -v /tmp/.X11-unix:/tmp/.X11-unix -e DISPLAY=$DISPLAY --volume="$HOME/.Xauthority:/root/.Xauthority:rw" -w /opt/nvidia/deepstream/deepstream-6.1 nvcr.io/nvidia/deepstream:6.1.1-devel 

參考:
X window解說
https://www.cs.odu.edu/~zeil/cs252/latest/Public/xtrouble/index.html

What You Need to Know About X11 Forwarding
https://goteleport.com/blog/x11-forwarding/

How to enable X11 forwarding from Red Hat Enterprise Linux (RHEL), Amazon Linux, SUSE Linux, Ubuntu server to support GUI-based installations from Amazon EC2
https://aws.amazon.com/tw/blogs/compute/how-to-enable-x11-forwarding-from-red-hat-enterprise-linux-rhel-amazon-linux-suse-linux-ubuntu-server-to-support-gui-based-installations-from-amazon-ec2/

Built-in SSH X11 forwarding in PowerShell or Windows Command Prompt
https://x410.dev/cookbook/built-in-ssh-x11-forwarding-in-powershell-or-windows-command-prompt/

https://dreamanddead.github.io/post/ssh-x11-forward/

https://juejin.cn/post/7009593663894323231

https://stackoverflow.com/questions/65468655/vs-code-remote-x11-cant-get-display-while-connecting-to-remote-server

https://zhuanlan.zhihu.com/p/461378596

X11 Forwarding on Windows with Cygwin
https://www.csusb.edu/sites/default/files/cse_x11_forwarding_on_windows_with_cygwin.pdf

Cygwin/X
https://x.cygwin.com/

ssh option
https://www.microfocus.com/documentation/rsit-server-client-unix/8-4-0/unix-guide/ssh_options_ap.html

What exactly is X/Xorg/X11?
https://www.reddit.com/r/linuxquestions/comments/3uh9n9/what_exactly_is_xxorgx11/

SSH X11 Forwarding Of Gnome-Boxes Under Wayland & Mixed Wayland & X11 Environments
https://www.dbts-analytics.com/notesxfwdgb.html

  • X11 forwarding docker container裡面的影像
    docker run 指令要多加 –volume=”$HOME/.Xauthority:/root/.Xauthority:rw”

X11 forwarding of a GUI app running in docker
https://stackoverflow.com/a/51209546

Run X application in a Docker container reliably on a server connected via SSH without “–net host”
https://stackoverflow.com/questions/48235040/run-x-application-in-a-docker-container-reliably-on-a-server-connected-via-ssh-w

git 筆記

  • git alias

    1
    git config --global alias.slog "log --graph --all --topo-order --pretty='format:%h %ai %s%d (%an)'"
  • 移動git tag 位置

    1
    2
    git tag --force <tag名稱>
    git push origin <tag名稱> --force #強迫推送到遠端

Python呼叫C++系列(二)scikit-build

第一篇連結

本篇重點

我們將建立一個可安裝的c++ extension套件,可以用pip install 安裝,本篇教學將延續第一篇的範例example.cpp

準備環境

  • 安裝所需套件和scikit-build

  • 安裝編譯所需套件

    1
    sudo apt install build-essential
  • 安裝CMake

    1
    sudo apt install cmake

使用方式

  • 建立一個setup.py檔並且放入下面這行

  • 新增一個CMakeLists.txt並且放入下面內容

  • 新增一個pyproject.toml並且放入下面內容

  • 測試安裝套件
    pip install -v

Debug