Python 安裝 OpenCV 模組

本篇 ShengYu 將介紹如何在 Python 環境中安裝 OpenCV 模組。

以下 Python 安裝 OpenCV 模組的內容分為這幾部分,

  • Python 安裝 OpenCV 模組
  • Python 安裝 OpenCV contrib 模組

Python 安裝 OpenCV 模組

遇到 ImportError: No module named 'cv2' 這個錯誤訊息的話,請安裝 Python 的 OpenCV 模組。

官方文件:https://docs.opencv.org/master/
PyPI:https://pypi.org/project/opencv-python/
Github:https://github.com/skvark/opencv-python

輸入下列指令安裝 OpenCV 模組,以下列出 python 2 與 python 3 的方法︰
Python 2 安裝方法
以下為 pip 的安裝 opencv 方式,

1
$ pip install opencv-python

Ubuntu 的話還可以用 apt-get 來安裝,

1
$ sudo apt-get install python-opencv

Python 3 安裝方法
以下為 pip3 的安裝 opencv 方式,

1
$ pip3 install opencv-python

Ubuntu 的話還可以用 apt-get 來安裝,

1
$ sudo apt-get install python3-opencv

macOS 安裝 OpenCV 方法
macOS 用 pip 安裝如果遇到權限問題,可以加 --user 參數,macOS 更多 pip 細節可以看這篇

1
$ pip3 install opencv-python --user

Python 安裝 OpenCV contrib 模組

pip 安裝 OpenCV contrib 模組指令如下,

1
$ pip install opencv-contrib-python

其它相關文章推薦
Python 新手入門教學懶人包
Python pip install 如何安裝指定版本的套件
macOS 使用 pip 安裝 opencv

Python http web server 快速建立網頁伺服器

本篇介紹如何使用 Python 來快速地建立一個網頁伺服器 http web server,如果你不想架設 Apache,只需要一個簡單的網頁伺服器或者檔案伺服器分享檔案用途的話,那麼 Python 可以幫你達成這目的,千萬不要錯過接下來的內容,Python 2 跟 Python 3 都會一起介紹。利用 Python 2 內建 SimpleHTTPServer 模組,而 Python 3 是 http.server 模組,下一個簡單的指令,馬上就建立好一個網頁目錄伺服器/檔案伺服器,其他人就可以很簡單地用瀏覽器瀏覽目錄裡的檔案,這功能在分享檔案時非常有幫助!除此之外,也可以顯示目錄裡靜態網頁唷!

以下建立 Python http server 內容分為以下幾部分,

  • Python 2.x 使用指令建立 SimpleHTTPServer
  • Python 3.x 使用指令建立 http.server
  • 寫一個簡單 Python 2 的本機 http server
  • 寫一個簡單 Python 3 的本機 http server

Python 2.x 使用指令建立 SimpleHTTPServer

Python 內建 http server,透過下列指令,你可以快速地建立目錄檔案伺服器。
以下用 Python 2 的 SimpleHTTPServer 模組快速建立一個簡單網頁伺服器(Web Server)
關於 Python 2.7 的 SimpleHTTPServer 實作細節可以看 2.7/Lib/SimpleHTTPServer.py 原始碼。

1
2
$ cd /home/somedir
$ python -m SimpleHTTPServer

Python 3.x 使用指令建立 http.server

同樣的功能在 Python 3 改名叫做 http.server,透過下列指令,你可以快速地建立目錄檔案伺服器。
以下用 Python 3 的 http.server 模組快速建立一個簡單網頁伺服器(Web Server)
關於 Python 3.5 的 http.server 實作細節可以看 3.5/Lib/http/server.py 原始碼。

1
2
3
$ cd /home/somedir
$ python3 -m http.server
Serving HTTP on 0.0.0.0 port 8000 ...

看到以上輸出表示伺服器已經啟動好了,預設 port 是 8000,接下來就可以開起 http://127.0.0.1:8000/http://localhost:8000/ 網頁看看效果,

如果想要更改伺服器所使用的 port 的話直接在後面指定 port number,例如我想改成 7000 的話就可以這樣下指令

1
2
$ python3 -m http.server 7000
Serving HTTP on 0.0.0.0 port 7000 ...

以上的作法是本機的位址(localhost)的作法,別台電腦是無法連進來的,
假設我想讓別台電腦或手機裝置連到我的這台電腦的話,就需要將這個伺服器 bind 在真實的網路介面上,
假如我的電腦主機內網IP為 192.168.0.2,那麼指令就要輸入,

1
2
$ python3 -m http.server -b 192.168.0.2
Serving HTTP on 192.168.10.180 port 8000 ...

這樣就會啟動一個網頁伺服器在 192.168.0.2 這個網路介面上,內網的裝置就可以透過這個IP連到我的電腦的網頁伺服器 web server(假如你電腦的防火牆有正確的設定的話),如果是要讓外網的電腦連進來的話,就改換成你電腦的真實對外IP即可。

寫一個簡單 Python 2 的本機 http server

如果想寫一個客製化的 Python 程式實現內建模組沒有的功能的話,
Python 2 寫法如下,不過 Python 2 即將不支援了,稍後也會示範一下 Python 3 的範例

python2-httpserver.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#!/usr/bin/python
# -*- coding: utf-8 -*-
import sys
import BaseHTTPServer
from SimpleHTTPServer import SimpleHTTPRequestHandler

HandlerClass = SimpleHTTPRequestHandler
ServerClass = BaseHTTPServer.HTTPServer
Protocol = "HTTP/1.0"

if sys.argv[1:]:
port = int(sys.argv[1])
else:
port = 8000
server_address = ('127.0.0.1', port)

HandlerClass.protocol_version = Protocol
httpd = ServerClass(server_address, HandlerClass)

sa = httpd.socket.getsockname()
print("Serving HTTP on " + str(sa[0]) + " port " + str(sa[1]) + "...")
httpd.serve_forever()

寫一個簡單 Python 3 的本機 http server

Python 2 即將不支援了,趕緊跟上寫一下 Python 3 的範例吧!
Python 3 的範例如下,

python3-httpserver.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import sys
import http.server
from http.server import SimpleHTTPRequestHandler

HandlerClass = SimpleHTTPRequestHandler
ServerClass = http.server.HTTPServer
Protocol = "HTTP/1.0"

if sys.argv[1:]:
port = int(sys.argv[1])
else:
port = 8000
server_address = ('127.0.0.1', port)

HandlerClass.protocol_version = Protocol
httpd = ServerClass(server_address, HandlerClass)

sa = httpd.socket.getsockname()
print("Serving HTTP on", sa[0], "port", sa[1], "...")
httpd.serve_forever()

Python http web sever 背景執行

Python 執行 http server 後程式會在前景執行,用 & 可以將程式放到背景執行,如果要結束的話再用 kill 指令停止程式。

1
2
3
4
5
# python 2
$ python -m SimpleHTTPServer &

# python 3
$ python3 -m http.server &

但是用 ssh 或 telnet 再執行 python 的 http server 時,遇到 ssh 登出的話就會結束程式,因為 ssh 斷線登出時會發出 SIGNUP 訊號,而 Python 收到 SIGNUP 訊號後就會結束程式。這時可以使用 nohup 這個指令來解決這個問題,nohup 解決指令如下,nohup 會將後面的執行程式標準輸出導向 nohup.out 檔案裡,

1
2
3
4
5
# python 2
$ nohup python -m SimpleHTTPServer &

# python 3
$ nohup python3 -m http.server &

另外也可以用 tmux 或 screen 開啟 terminal,這樣 ssh 登出後 terminal 不會因此結束可以繼續執行,之後再重新連上 tmux 或 screen 的 session 即可。

本篇介紹的方法用作檔案伺服器的話只能下載不能上傳,下一篇就來介紹 Python SimpleHTTPServerWithUpload 上傳檔案功能

以上就是 Python http web server 快速建立網頁伺服器的介紹,
如果你覺得我的文章寫得不錯、對你有幫助的話記得 Facebook 按讚支持一下!

其他參考
[1] Tech Tip: Really Simple HTTP Server with Python | Linux Journal
https://www.linuxjournal.com/content/tech-tip-really-simple-http-server-python
[2] 用Python 的SimpleHTTPServer 模組快速建立一個臨時網頁伺服器(Web Server) - G. T. Wang
https://blog.gtwang.org/web-development/python-simplehttpserver-web-server/
[3] Python 3快速建立網頁伺服器Web server | Funny_DotBlog - 點部落
https://dotblogs.com.tw/funny_dotblog/2019/05/16/python_webserver
[4] Python: Let’s Create a Simple HTTP Server (Tutorial) - Afternerd
https://www.afternerd.com/blog/python-http-server/
[5] a minimal http server in python. Responds to GET, HEAD, POST requests, but will fail on anything else.
https://gist.github.com/bradmontgomery/2219997

其它相關文章推薦
如果你想學習 Python 相關技術,可以參考看看下面的文章,
Python SimpleHTTPServerWithUpload 上傳檔案功能
[Python小專案] Tornado+PyAutoGUI 多媒體控制播放的網頁
Python 新手入門教學懶人包
Python str 字串用法與範例
Python list 串列用法與範例
Python set 集合用法與範例
Python dict 字典用法與範例
Python tuple 元組用法與範例

Python PyAutoGUI 使用教學

本篇 ShengYu 將介紹如何用 Python 搭配 PyAutoGUI 模組來模擬鍵盤、模擬滑鼠,Python 如何模擬鍵盤按下按鍵與模擬滑鼠移動滑鼠與點擊,將在以下教學內容解釋。

以下 Python PyAutoGUI 將分為這幾部分介紹,

  • Python PyAutoGUI 模擬鍵盤
  • Python PyAutoGUI 模擬滑鼠
  • Python PyAutoGUI 螢幕截圖
  • 其他
  • Python PyAutoGUI 常用 API 列表

那我們開始吧!

在執行 Python 如果遇到 ImportError: No module named ‘pyautogui’ 這錯誤訊息就是需要安裝 pyautogui,或參考這篇安裝吧!

1
pip install pyautogui

Python PyAutoGUI 模擬鍵盤

這邊介紹 Python PyAutoGUI 模擬鍵盤的按鍵、組合鍵的範例,
用 PyAutoGUI 模擬按下按鍵的話,例如按下 a 鍵跟 enter 鍵,

1
2
3
4
import pyautogui

pyautogui.press('a') # 按 a
pyautogui.press('enter') # 按 enter

用 PyAutoGUI 模擬按下組合鍵的話,以複製貼上為例,

1
2
3
4
5
6
7
import pyautogui

pyautogui.hotkey('ctrl', 'c') # 複製
pyautogui.hotkey('ctrl', 'v') # 貼上

pyautogui.hotkey('command', 'c') # macOS 複製
pyautogui.hotkey('command', 'v') # macOS 貼上

用 PyAutoGUI 模擬輸入 hello world 的話,用 pyautogui.typewrite() 或者 pyautogui.write() 都可以,

1
2
3
4
5
6
7
import pyautogui

pyautogui.typewrite('hello world')
pyautogui.typewrite('hello world', interval=0.5) # 每個字的按鍵間隔 0.5 秒

pyautogui.write('hello world')
pyautogui.write('hello world', interval=0.5) # 每個字的按鍵間隔 0.5 秒

不是所有的鍵都很容易用單個文字字元來表示。例如 Shift 鍵或左箭頭鍵,PyAutoGUI 要組合輸入這些特殊鍵的話可以這樣寫,

1
2
3
import pyautogui

pyautogui.typewrite(['a', 'b', 'left', 'left', 'X', 'Y'])

常用的一些按鍵整理如下表:

用法 說明
press(‘a’) a
press(‘enter’) 回車鍵
press(‘esc’) Esc
press(‘space’) space 空白鍵
press(‘backspace’) 返回鍵
press(‘up’)
press(‘down’)
press(‘left’)
press(‘right’)
press(‘pgup’) Page Up
press(‘pgdn’) Page Down
press(‘playpause’) 播放/暫停
press(‘volumeup’) 大聲
press(‘volumedown’) 小聲
press(‘volumemute’) 靜音
press(‘printscreen’) Print Screen
press(‘ctrl’) Ctrl
press(‘shift’) Shift
press(‘alt’) Alt
press(‘option’) Option

更多按鍵資訊可參考
https://pyautogui.readthedocs.io/en/latest/keyboard.html

Python PyAutoGUI 模擬滑鼠

這邊介紹 Python PyAutoGUI 來模擬滑鼠的移動、拖曳、點擊。這邊需要解釋一下螢幕上的座標軸,這樣接下來才清楚滑鼠要往哪邊移動。

以下範例為 1920 x 1080 的螢幕解析度示意。

1
2
3
4
5
6
7
8
9
10
(0, 0)        X 增加 -->    (1919, 0)
+---------------------------+
| |
| | Y 增加
| 1920 x 1080 screen | |
| | |
| | V
| |
+---------------------------+
(0, 1079) (1919, 1079)

取得螢幕解析度

1
2
3
>>> import pyautogui
>>> width, height = pyautogui.size()
(1920, 1080)

取得滑鼠當前的座標位置

1
2
3
>>> import pyautogui
>>> pyautogui.position()
(300, 200)

移動滑鼠到絕對位置
語法:moveTo(width, height, duration)
移動到(300, 200)絕對位置

1
2
3
4
5
6
import pyautogui

pyautogui.moveTo(300, 200) # 移動到(300, 200)
pyautogui.moveTo(None, 400) # 移動到(300, 400)
pyautogui.moveTo(500, None) # 移動到(500, 200)
pyautogui.moveTo(300, 200, duration = 1.5) # 花1.5秒移動到(300, 200)

移動滑鼠到相對位置

語法:moeRel(width, height, duration)
移到相對位置,以當前滑鼠位置為基準點

1
2
3
4
5
6
7
import pyautogui

pyautogui.moveRel(0, 100) # 往下移 100
pyautogui.moveRel(-100, 0) # 往左移 100

pyautogui.moveRel(0, 100, duration=0.5) # 花1.5秒逐漸往下移 100
pyautogui.moveRel(-100, 0, duration=0.5) # 花1.5秒逐漸往左移 100

滑鼠拖曳

1
2
3
4
5
import pyautogui

pyautogui.dragTo(100, 200, button='left') # drag mouse to (100, 200) while holding down left mouse button
pyautogui.dragTo(300, 400, 2, button='left') # drag mouse to (300, 400) over 2 seconds while holding down left mouse button
pyautogui.drag(30, 0, 2, button='right') # drag the mouse left 30 pixels over 2 seconds while holding down the right mouse button

滑鼠點擊
語法:click(width, height, button)

1
2
3
4
5
6
7
import pyautogui

button = 'left', 'middle', 'right'

pyautogui.click() #在當前位置點擊(預設左鍵)
pyautogui.click(width, height) #在(width, height)點擊滑鼠(預設左鍵)
pyautogui.click(width, height, button='left') #點擊滑鼠左鍵

更多滑鼠資訊可參考 https://pyautogui.readthedocs.io/en/latest/mouse.html

Python PyAutoGUI 螢幕截圖

這邊介紹 PyAutoGUI 螢幕快照截圖功能,除此之外,PyAutoGUI 還可以比對圖片找出圖片在螢幕的那個位置,在搭配滑鼠或鍵盤的模擬操作,進行一連串的自動化操作。
要將目前的螢幕畫面擷取下來,可以使用 PyAutoGUI 的 screenshot(),

1
2
3
4
import pyautogui

image1 = pyautogui.screenshot() # 擷取螢幕畫面
image2 = pyautogui.screenshot('screenshot.png') # 擷取螢幕畫面,同時儲存檔案

其他

暫停幾秒鐘

1
2
3
import pyautogui

pyautogui.PAUSE = 1.5

Fail-Safes (預設false)
當滑鼠一道螢幕左上角時,觸發 pyautogui 的FailSafeException 異常

1
2
3
4
import pyautogui

pyautogui.FAILSAFE = True # enables the fail-safe
pyautogui.FAILSAFE = False # disables the fail-safe

詳情請看https://pyautogui.readthedocs.io/en/latest/introduction.html#fail-safes

Python PyAutoGUI 常用 API 列表

這邊列出 Python PyAutoGUI 常用 API 列表,方便快速查詢使用,

moveTo(x, y):將滑鼠移動到指定的 x、y 坐標。
moveRel(xOffset, yOffset):相對於當前位置移動滑鼠。
dragTo(x, y):按下左鍵並移動滑鼠。
dragRel(xOffset, yOffset):按下左鍵,相對於當前位置移動滑鼠。
click(x, y, button):模擬點擊(預設是左鍵)。
rightClick():模擬右鍵點擊。
doubleClick():模擬左鍵雙擊。
middleClick():模擬中鍵點擊。
mouseDown(x, y, button):模擬在 x、y 處按下指定滑鼠按鍵。
mouseUp(x, y, button):模擬在 x、y 處釋放指定滑鼠按鍵。
scroll(units):模擬滾動滾輪。正參數表示向上滾動,負參數表示向下滾動。
typewrite(message):逐一輸入給定的字串。
typewrite([key1, key2, key3]):輸入給定鍵字串。
press(key):按下並釋放給定鍵。
keyDown(key):模擬按下給定鍵。
keyUp(key):模擬釋放給定鍵。
hotkey([key1, key2, key3]):模擬按順序按下給定鍵字串,然後以相反的順序釋放。
screenshot():回傳螢幕快照的 Image。
screenshot('screenshot.png'):螢幕快照並存檔且回傳螢幕快照的 Image。

另外 pyautogui 還有 Message BoxScreenshot 改天再研究吧!
對 Python 自動化有興趣可以參考《Python 自動化的樂趣》這本書中的自動化專題實作章節。

參考
PyAutoGUI : 使用Python控制電腦 - Yanwei Liu - Medium
https://medium.com/@yanweiliu/662cc3b18b80
Python学习笔记——用GUI自动化控制键盘和鼠标 - 简书
https://www.jianshu.com/p/41463c82ec8f

其它相關文章推薦
Python 安裝 PyAutoGUI 模組

Python 安裝 PyAutoGUI 模組

本篇 ShengYu 將介紹如何在 Python 3 環境中安裝 PyAutoGUI 模組。

安裝 pyautogui

以下指紀錄 python 3 的安裝方法,python 2 即將不支援就算了吧。
遇到 ImportError: No module named ‘pyautogui’ 這錯誤訊息就是需要安裝 pyautogui,參考下列篇幅安裝吧!
官方文件:https://pyautogui.readthedocs.io/en/latest/
PyPI:https://pypi.org/project/PyAutoGUI/

Windows 安裝

以下安裝方式在 Windows 7, 10 下 Python 3.7.5 測試過

1
$ pip install pyautogui

Ubuntu 安裝

最新 pyautogui 的版本會要求至少 python3.6 以上, 因為我的 ubuntu 16.04 的 python 只有 3.5
在不升級python3的情況下,只好降版安裝指定的版本,
pip 預設會抓最新的版本,目前 pyautogui 最新的版本為 0.9.48(2019/10/10釋出)
我試到 pyautogui 0.9.44(2019/05/31釋出)才可以安裝,安裝方法如下:

1
$ sudo -H pip3 install pyautogui==0.9.44

如果遇到 ImportError: No module named Xlib.display 這錯誤訊息就是需要安裝 Xlib。

1
$ sudo apt-get install python3-xlib

Mac OSX 安裝

以下安裝方式在 macOS 10.13.4 下 Python 2.7.10 測試過,(Python 3 還沒試)

1
$ pip install pyautogui

參考
[1] Installation — PyAutoGUI 1.0.0 documentation
https://pyautogui.readthedocs.io/en/latest/install.html
[2] Python學習筆記——用GUI自動化控制鍵盤和鼠標 - 簡書
https://www.jianshu.com/p/41463c82ec8f
[3] module - Problems importing python-Xlib - Stack Overflow
https://stackoverflow.com/questions/5892297/problems-importing-python-xlib

其它相關文章推薦
Python PyAutoGUI 使用教學

OpenCV trace VideoCapture 流程

本篇紀錄一下 OpenCV 的 VideoCapture 的啟動流程。

Trace 的 OpenCV 版本為:3.4.8 / 4.1.2
以下範例為 Python 的開啟 VideoCapture 範例,C++版本就先不列出來了,
以下紀錄為 VideoCapture 建構流程,以及每幀frame讀取出來的流程。

1
2
3
4
5
6
7
8
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import cv2

cap = cv2.VideoCapture(0)
while(True):
ret, frame = cap.read()
cv2.imshow('frame', frame)

VideoCapture 建構流程

VideoCapture::VideoCapture()建構子
從 VideoCapture::VideoCapture() 開始吧!
https://github.com/opencv/opencv/blob/master/modules/videoio/src/cap.cpp

modules/videoio/src/cap.cpplink
1
2
3
4
5
6
7
8
9
VideoCapture::VideoCapture(const String& filename, int apiPreference) : throwOnFail(false)
{
open(filename, apiPreference); // 1-1
}

VideoCapture::VideoCapture(int index, int apiPreference) : throwOnFail(false)
{
open(index, apiPreference); // 2-1
}

VideoCapture::open()
VideoCapture::open() 有兩種,一種是帶字串的,另一種是帶整數的。
帶字串的會呼叫 getAvailableBackends_CaptureByFilename
帶整數的會呼叫 getAvailableBackends_CaptureByIndex

modules/videoio/src/cap.cpplink
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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
bool VideoCapture::open(const String& filename, int apiPreference) // 1-1
{
if (isOpened()) release();

const std::vector<VideoBackendInfo> backends =
cv::videoio_registry::getAvailableBackends_CaptureByFilename(); // 1-2

for (size_t i = 0; i < backends.size(); i++)
{
const VideoBackendInfo& info = backends[i];
if (apiPreference == CAP_ANY || apiPreference == info.id)
{
const Ptr<IBackend> backend = info.backendFactory->getBackend(); // 1-9 backendFactory
if (!backend.empty())
{
try
{
icap = backend->createCapture(filename); // 1-10
if (!icap.empty())
{
if (icap->isOpened())
return true;
icap.release();
}
} catch(...) {
// ...
}
}
}
}

return false;
}

bool VideoCapture::open(int cameraNum, int apiPreference) // 2-1
{
if (isOpened()) release();

if (apiPreference == CAP_ANY)
{
// interpret preferred interface (0 = autodetect)
int backendID = (cameraNum / 100) * 100;
if (backendID)
{
cameraNum %= 100;
apiPreference = backendID;
}
}

const std::vector<VideoBackendInfo> backends =
cv::videoio_registry::getAvailableBackends_CaptureByIndex(); // 2-2
for (size_t i = 0; i < backends.size(); i++)
{
const VideoBackendInfo& info = backends[i];
if (apiPreference == CAP_ANY || apiPreference == info.id)
{
const Ptr<IBackend> backend = info.backendFactory->getBackend();
if (!backend.empty())
{
try
{
icap = backend->createCapture(cameraNum);
if (!icap.empty())
{
if (icap->isOpened())
return true;
icap.release();
}
} catch(...) {
// ...
}
}
}
}

return false;
}

IBackend 與 IBackendFactory 的關係,IBackendFactory 就是專門生產 IBackend 的物件。
常見的 Singleton 設計模型 static VideoBackendRegistry& getInstance

https://github.com/opencv/opencv/blob/master/modules/videoio/src/videoio_registry.cpp

modules/videoio/src/videoio_registry.cpplink
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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
#define DECLARE_DYNAMIC_BACKEND(cap, name, mode) \
{ \
cap, (BackendMode)(mode), 1000, name, createPluginBackendFactory(cap, name) \
}

#define DECLARE_STATIC_BACKEND(cap, name, mode, createCaptureFile, createCaptureCamera, createWriter) \
{ \
cap, (BackendMode)(mode), 1000, name, createBackendFactory(createCaptureFile, createCaptureCamera, createWriter) \
}

// 1-5
static const struct VideoBackendInfo builtin_backends[] =
{
// ...
// Windows
#ifdef HAVE_DSHOW
DECLARE_STATIC_BACKEND(CAP_DSHOW, "DSHOW", MODE_CAPTURE_BY_INDEX, 0, create_DShow_capture, 0),
#endif

// Linux, some Unix
#if defined HAVE_CAMV4L2 // 1-6
DECLARE_STATIC_BACKEND(CAP_V4L2, "V4L2", MODE_CAPTURE_ALL, create_V4L_capture_file, create_V4L_capture_cam, 0),
#elif defined HAVE_VIDEOIO
DECLARE_STATIC_BACKEND(CAP_V4L, "V4L_BSD", MODE_CAPTURE_ALL, create_V4L_capture_file, create_V4L_capture_cam, 0),
#endif
// ...
};

class VideoBackendRegistry
{
protected:
std::vector<VideoBackendInfo> enabledBackends;
VideoBackendRegistry() // 1-4
{
const int N = sizeof(builtin_backends)/sizeof(builtin_backends[0]);
enabledBackends.assign(builtin_backends, builtin_backends + N); // 1-5 builtin_backends
for (int i = 0; i < N; i++)
{
VideoBackendInfo& info = enabledBackends[i];
info.priority = 1000 - i * 10;
}

// ...
std::sort(enabledBackends.begin(), enabledBackends.end(), sortByPriority); // 排序
}

// ...
static VideoBackendRegistry& getInstance() // 1-3, 2-3 常見的 instance 技巧!
{
static VideoBackendRegistry g_instance; // 1-4 VideoBackendRegistry()
return g_instance;
}
// ...
inline std::vector<VideoBackendInfo> getAvailableBackends_CaptureByIndex() const // 2-4
{
std::vector<VideoBackendInfo> result;
for (size_t i = 0; i < enabledBackends.size(); i++)
{
const VideoBackendInfo& info = enabledBackends[i];
if (info.mode & MODE_CAPTURE_BY_INDEX)
result.push_back(info);
}
return result;
}
inline std::vector<VideoBackendInfo> getAvailableBackends_CaptureByFilename() const // 1-8
{
std::vector<VideoBackendInfo> result;
for (size_t i = 0; i < enabledBackends.size(); i++)
{
const VideoBackendInfo& info = enabledBackends[i];
if (info.mode & MODE_CAPTURE_BY_FILENAME)
result.push_back(info);
}
return result;
}

};

namespace videoio_registry {

std::vector<VideoBackendInfo> getAvailableBackends_CaptureByIndex() // 2-2
{
// 2-3 getInstance, 2-4 getAvailableBackends_CaptureByIndex
const std::vector<VideoBackendInfo> result = VideoBackendRegistry::getInstance().getAvailableBackends_CaptureByIndex();
return result;
}
std::vector<VideoBackendInfo> getAvailableBackends_CaptureByFilename() // 1-2
{
// 1-3 getInstance, 1-8 getAvailableBackends_CaptureByFilename
const std::vector<VideoBackendInfo> result = VideoBackendRegistry::getInstance().getAvailableBackends_CaptureByFilename();
return result;
}
}

https://github.com/opencv/opencv/blob/master/modules/videoio/src/videoio_registry.hpp

modules/videoio/src/videoio_registry.hpplink
1
2
3
4
5
6
7
8
9
struct VideoBackendInfo {
VideoCaptureAPIs id;
BackendMode mode;
int priority; // 1000-<index*10> - default builtin priority
// 0 - disabled (OPENCV_VIDEOIO_PRIORITY_<name> = 0)
// >10000 - prioritized list (OPENCV_VIDEOIO_PRIORITY_LIST)
const char* name;
Ptr<IBackendFactory> backendFactory; // 1-9
};

https://github.com/opencv/opencv/blob/master/modules/videoio/src/backend.hpp

modules/videoio/src/backend.hpplink
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class IBackend
{
public:
virtual ~IBackend() {}
virtual Ptr<IVideoCapture> createCapture(int camera) const = 0;
virtual Ptr<IVideoCapture> createCapture(const std::string &filename) const = 0; // 1-10
virtual Ptr<IVideoWriter> createWriter(const std::string &filename, int fourcc, double fps, const cv::Size &sz, bool isColor) const = 0;
};

class IBackendFactory
{
public:
virtual ~IBackendFactory() {}
virtual Ptr<IBackend> getBackend() const = 0; // 要看是實作哪種 IBackendFactory
};

https://github.com/opencv/opencv/blob/master/modules/videoio/src/cap_interface.hpp

modules/videoio/src/cap_interface.hpplink
1
2
3
4
5
Ptr<IVideoCapture> create_DShow_capture(int index);
// ...
Ptr<IVideoCapture> create_V4L_capture_cam(int index); // 1-6, 1-7
// ...
Ptr<IVideoCapture> create_Images_capture(const std::string &filename);

https://github.com/opencv/opencv/blob/master/modules/videoio/src/cap_v4l.cpp

modules/videoio/src/cap_v4l.cpplink
1
2
3
4
5
6
7
8
9
10
Ptr<IVideoCapture> create_V4L_capture_cam(int index) // 1-7
{
cv::CvCaptureCAM_V4L* capture = new cv::CvCaptureCAM_V4L(); // 下一篇再介紹

if (capture->open(index))
return makePtr<LegacyCapture>(capture);

delete capture;
return NULL;
}

其它相關文章推薦
OpenCV FileStorage 用法與 YAML 檔案讀取寫入範例
如何看OpenCV當初編譯的編譯參設定
怎麼查詢 OpenCV 的版本

如何看OpenCV當初編譯的編譯參數設定

本篇介紹如何看 OpenCV 當初編譯 build 的編譯參數設定,有時可能會有這種需求,可能是當初自己build的但原始碼已經刪了,可能是下載別人編譯好的,等等多種情形下想要知道OpenCV當初編譯的參數,以下將列出幾種方式去查詢。

方法一:執行 opencv_version 程式來查看

opencv_version 這隻程式會印出你的opencv版本號之外,還可以令它輸出當初 OpenCV 編譯時編譯參數設定,
opencv_version 這支程式在 apps/version/opencv_version.cpp 下可以找到原始碼,不是samples/cpp/opencv_version.cpp 這支唷!
具體指令為

1
$ opencv_version -v

輸出如下︰

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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
General configuration for OpenCV 3.4.8 =====================================
Version control: unknown

Platform:
Timestamp: 2019-12-02T23:50:14Z
Host: Linux 4.10.0-40-generic x86_64
CMake: 3.5.1
CMake generator: Unix Makefiles
CMake build tool: /usr/bin/make
Configuration: Release

CPU/HW features:
Baseline: SSE SSE2 SSE3
requested: SSE3
Dispatched code generation: SSE4_1 SSE4_2 FP16 AVX AVX2 AVX512_SKX
requested: SSE4_1 SSE4_2 AVX FP16 AVX2 AVX512_SKX
SSE4_1 (15 files): + SSSE3 SSE4_1
SSE4_2 (2 files): + SSSE3 SSE4_1 POPCNT SSE4_2
FP16 (1 files): + SSSE3 SSE4_1 POPCNT SSE4_2 FP16 AVX
AVX (6 files): + SSSE3 SSE4_1 POPCNT SSE4_2 AVX
AVX2 (28 files): + SSSE3 SSE4_1 POPCNT SSE4_2 FP16 FMA3 AVX AVX2
AVX512_SKX (6 files): + SSSE3 SSE4_1 POPCNT SSE4_2 FP16 FMA3 AVX AVX2 AVX_512F AVX512_COMMON AVX512_SKX

C/C++:
Built as dynamic libs?: YES
C++ Compiler: /usr/bin/c++ (ver 5.4.0)
C++ flags (Release): -fsigned-char -W -Wall -Werror=return-type -Werror=non-virtual-dtor -Werror=address -Werror=sequence-point -Wformat -Werror=format-security -Wmissing-declarations -Wundef -Winit-self -Wpointer-arith -Wshadow -Wsign-promo -Wuninitialized -Winit-self -Wno-delete-non-virtual-dtor -Wno-comment -fdiagnostics-show-option -Wno-long-long -pthread -fomit-frame-pointer -ffunction-sections -fdata-sections -msse -msse2 -msse3 -fvisibility=hidden -fvisibility-inlines-hidden -O3 -DNDEBUG -DNDEBUG
C++ flags (Debug): -fsigned-char -W -Wall -Werror=return-type -Werror=non-virtual-dtor -Werror=address -Werror=sequence-point -Wformat -Werror=format-security -Wmissing-declarations -Wundef -Winit-self -Wpointer-arith -Wshadow -Wsign-promo -Wuninitialized -Winit-self -Wno-delete-non-virtual-dtor -Wno-comment -fdiagnostics-show-option -Wno-long-long -pthread -fomit-frame-pointer -ffunction-sections -fdata-sections -msse -msse2 -msse3 -fvisibility=hidden -fvisibility-inlines-hidden -g -O0 -DDEBUG -D_DEBUG
C Compiler: /usr/bin/cc
C flags (Release): -fsigned-char -W -Wall -Werror=return-type -Werror=non-virtual-dtor -Werror=address -Werror=sequence-point -Wformat -Werror=format-security -Wmissing-declarations -Wmissing-prototypes -Wstrict-prototypes -Wundef -Winit-self -Wpointer-arith -Wshadow -Wuninitialized -Winit-self -Wno-comment -fdiagnostics-show-option -Wno-long-long -pthread -fomit-frame-pointer -ffunction-sections -fdata-sections -msse -msse2 -msse3 -fvisibility=hidden -O3 -DNDEBUG -DNDEBUG
C flags (Debug): -fsigned-char -W -Wall -Werror=return-type -Werror=non-virtual-dtor -Werror=address -Werror=sequence-point -Wformat -Werror=format-security -Wmissing-declarations -Wmissing-prototypes -Wstrict-prototypes -Wundef -Winit-self -Wpointer-arith -Wshadow -Wuninitialized -Winit-self -Wno-comment -fdiagnostics-show-option -Wno-long-long -pthread -fomit-frame-pointer -ffunction-sections -fdata-sections -msse -msse2 -msse3 -fvisibility=hidden -g -O0 -DDEBUG -D_DEBUG
Linker flags (Release): -Wl,--gc-sections
Linker flags (Debug): -Wl,--gc-sections
ccache: NO
Precompiled headers: NO
Extra dependencies: dl m pthread rt
3rdparty dependencies:

OpenCV modules:
To be built: calib3d core dnn features2d flann highgui imgcodecs imgproc ml objdetect photo python2 python3 shape stitching superres ts video videoio videostab
Disabled: world
Disabled by dependency: -
Unavailable: cudaarithm cudabgsegm cudacodec cudafeatures2d cudafilters cudaimgproc cudalegacy cudaobjdetect cudaoptflow cudastereo cudawarping cudev java js viz
Applications: tests perf_tests apps
Documentation: NO
Non-free algorithms: NO

GUI:
GTK+: YES (ver 2.24.30)
GThread : YES (ver 2.48.2)
GtkGlExt: NO
VTK support: NO

Media I/O:
ZLib: /usr/lib/x86_64-linux-gnu/libz.so (ver 1.2.8)
JPEG: /usr/lib/x86_64-linux-gnu/libjpeg.so (ver 80)
WEBP: build (ver encoder: 0x020e)
PNG: /usr/lib/x86_64-linux-gnu/libpng.so (ver 1.2.54)
TIFF: /usr/lib/x86_64-linux-gnu/libtiff.so (ver 42 / 4.0.6)
JPEG 2000: /usr/lib/x86_64-linux-gnu/libjasper.so (ver 1.900.1)
OpenEXR: /usr/lib/x86_64-linux-gnu/libImath.so /usr/lib/x86_64-linux-gnu/libIlmImf.so /usr/lib/x86_64-linux-gnu/libIex.so /usr/lib/x86_64-linux-gnu/libHalf.so /usr/lib/x86_64-linux-gnu/libIlmThread.so (ver 2.2.0)
HDR: YES
SUNRASTER: YES
PXM: YES

Video I/O:
DC1394: YES (ver 2.2.4)
FFMPEG: YES
avcodec: YES (ver 56.60.100)
avformat: YES (ver 56.40.101)
avutil: YES (ver 54.31.100)
swscale: YES (ver 3.1.101)
avresample: NO
GStreamer: NO
libv4l/libv4l2: NO
v4l/v4l2: linux/videodev2.h

Parallel framework: pthreads

Trace: YES (with Intel ITT)

Other third-party libraries:
Intel IPP: 2019.0.0 Gold [2019.0.0]
at: /home/shengyu/build_opencv348/opencv-3.4.8/build/3rdparty/ippicv/ippicv_lnx/icv
Intel IPP IW: sources (2019.0.0)
at: /home/shengyu/build_opencv348/opencv-3.4.8/build/3rdparty/ippicv/ippicv_lnx/iw
Lapack: NO
Eigen: YES (ver 3.2.92)
Custom HAL: NO
Protobuf: build (3.5.1)

OpenCL: YES (no extra features)
Include path: /home/shengyu/build_opencv348/opencv-3.4.8/3rdparty/include/opencl/1.2
Link libraries: Dynamic load

Python 2:
Interpreter: /usr/bin/python2.7 (ver 2.7.12)
Libraries: /usr/lib/x86_64-linux-gnu/libpython2.7.so (ver 2.7.12)
numpy: /home/shengyu/.local/lib/python2.7/site-packages/numpy/core/include (ver 1.13.3)
install path: lib/python2.7/dist-packages/cv2/python-2.7

Python 3:
Interpreter: /usr/bin/python3 (ver 3.5.2)
Libraries: /usr/lib/x86_64-linux-gnu/libpython3.5m.so (ver 3.5.2)
numpy: /home/shengyu/.local/lib/python3.5/site-packages/numpy/core/include (ver 1.15.4)
install path: lib/python3.5/dist-packages/cv2/python-3.5

Python (for build): /usr/bin/python2.7

Java:
ant: NO
JNI: /usr/lib/jvm/default-java/include /usr/lib/jvm/default-java/include/linux /usr/lib/jvm/default-java/include
Java wrappers: NO
Java tests: NO

Install to: /usr/local
-----------------------------------------------------------------

方法二:寫程式呼叫 getBuildInformation() 函式

C/C++ 的寫法如下所示:

1
2
3
4
5
#include <opencv2/opencv.hpp>

int main(void) {
std::cout << cv::getBuildInformation() << std::endl;
}

Python 的寫法如下所示:

1
2
import cv2
print(cv2.getBuildInformation())

方法三:查看安裝到系統的 cvconfig.h

查看 opencv2/cvconfig.h
Ubuntu 安裝通常會安裝在 /usr/local/include/
所以可以查看這個路徑下,指令為:

1
$ cat /usr/local/include/opencv2/cvconfig.h

輸出如下︰

/usr/local/include/opencv2/cvconfig.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
25
26
27
28
29
30
31
32
33
34
35
36
#ifndef OPENCV_CVCONFIG_H_INCLUDED
#define OPENCV_CVCONFIG_H_INCLUDED

/* OpenCV compiled as static or dynamic libs */
#define BUILD_SHARED_LIBS

/* OpenCV intrinsics optimized code */
#define CV_ENABLE_INTRINSICS

/* OpenCV additional optimized code */
/* #undef CV_DISABLE_OPTIMIZATION */

/* Compile for 'real' NVIDIA GPU architectures */
#define CUDA_ARCH_BIN ""

/* Create PTX or BIN for 1.0 compute capability */
/* #undef CUDA_ARCH_BIN_OR_PTX_10 */

/* NVIDIA GPU features are used */
#define CUDA_ARCH_FEATURES ""

/* Compile for 'virtual' NVIDIA PTX architectures */
#define CUDA_ARCH_PTX ""

/* AVFoundation video libraries */
/* #undef HAVE_AVFOUNDATION */

/* V4L capturing support */
/* #undef HAVE_CAMV4L */

/* V4L2 capturing support */
#define HAVE_CAMV4L2

//...

#endif // OPENCV_CVCONFIG_H_INCLUDED

參考
[1] How to view OpenCV Build Information - Stack Overflow
https://stackoverflow.com/questions/19106755/how-to-view-opencv-build-information
[2] Get OpenCV Build Information ( getBuildInformation ) | Learn OpenCV
https://www.learnopencv.com/get-opencv-build-information-getbuildinformation/

其它相關文章推薦
OpenCV FileStorage 用法與 YAML 檔案讀取寫入範例
OpenCV trace VideoCapture 流程
怎麼查詢 OpenCV 的版本

Python 安裝 pyserial 模組

本篇 ShengYu 將介紹如何在 Python 環境中安裝 pyserial 模組。

安裝 pyserial

遇到 ImportError: No module named ‘serial’ 這個錯誤訊息的話,請安裝 python 的 pyserial 模組。

官方文件:https://pythonhosted.org/pyserial/
PyPI:https://pypi.org/project/pyserial/
Github:https://github.com/pyserial/pyserial

輸入下列指令安裝 pyserial 模組,以下列出 python 2 與 python 3 的方法︰
Python 2 安裝方法

1
2
3
$ pip install pyserial
# or (Ubuntu)
$ apt-get install python-serial

Python 3 安裝方法

1
2
3
$ pip3 install pyserial
# or (Ubuntu)
$ apt-get install python3-serial

其它相關文章推薦
Python 新手入門教學懶人包

C++ std::shared_ptr 用法與範例

本篇 ShengYu 將介紹 C++ 的 std::shared_ptr 用法,std::shared_ptr 是可以讓多個 std::shared_ptr 共享一份記憶體,並且在最後一個 std::shared_ptr 生命週期結束時時自動釋放記憶體,本篇一開始會先介紹原始指標與智慧型指標寫法上的差異,再來介紹如何開始使用智慧型指標,並提供一些範例參考。

需要引入的標頭檔<memory>,編譯需要支援 C++11

範例1. 原始指標宣告與智慧型指標宣告的比較

我們先來看看原始指標是怎麼寫跟怎麼用,如 UseRawPointer() 內容所示,
另外有的對應的智慧型指標的版本,如 UseSmartPointer() 內容所示,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
void UseRawPointer() {
// 使用原始指標
Song* pSong = new Song("Just The Way You Are", "Bruno Mars");

// Use pSong...
pSong->DoSomething();

// 別忘了要 delete!
delete pSong;
}

void UseSmartPointer() {
// 使用智慧型指標
shared_ptr<Song> song2(new Song("Just The Way You Are", "Bruno Mars"));

// Use song2...
song2->DoSomething();

} // song2 在這裡自動地被 deleted

範例2. 開始使用智慧型指標

以下為 C++ shared_ptr 幾種初始化寫法,盡可能地使用 make_shared 而不是用 new,範例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 盡可能地使用 make_shared
auto sp1 = make_shared<Song>("The Beatles", "Hey Jude");
// 或者不使用 auto 像這樣寫
shared_ptr<Song> sp1 = make_shared<Song>("The Beatles", "Hey Jude");

// 下面這樣的寫法也可以,但會有一些副作用,記憶體配置上不連續,以及分開記憶體配置效能相對低弱
// Note: Using new expression as constructor argument
// creates no named variable for other code to access.
shared_ptr<Song> sp2(new Song("Lady Gaga", "Poker Face"));

// When initialization must be separate from declaration, e.g. class members,
// initialize with nullptr to make your programming intent explicit.
shared_ptr<Song> sp5(nullptr);
//等於: shared_ptr<Song> sp5;
sp5 = make_shared<Song>("Avril Lavigne", "What The Hell");

在 Scott Meyers 大神的《Effective Modern C++》書裡的條款 21 也提到:「盡量用 std::make_shared 取代直接使用 new」

範例3. 手動釋放記憶體

std::shared_ptr 如果需要手動釋放記憶體的話,可以使用智慧型指標裡的 reset() 函式,這邊要注意的是使用”“來存取 std::shared_ptr.reset(),範例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
void SmartPointerDemo2() {
// Create the object and pass it to a smart pointer
std::shared_ptr<LargeObject> pLarge(new LargeObject());

// Call a method on the object
pLarge->DoSomething();

// 在離開函式前手動釋放記憶體
pLarge.reset();

// ...

}

重複釋放記憶體問題

不建議使用下列這種寫法,用原始指標變數(下例中的p)去建立 shared_ptr 這種寫法容易造成重複釋放記憶體問題,因為 new Object 完後有個原始指標變數 p 指向這個物件,容易造成其它地方再把 p 建立 shared_ptr,
之後 sp 釋放完該物件後(delete),sp2 又再次釋放完該物件,造成重複釋放記憶體,

1
2
3
4
5
6
{
Object *p = new Object();
std::shared_ptr<Object> sp(p);
// ...
std::shared_ptr<Object> sp2(p);
} // sp 釋放,表示 delete 該物件,再換 sp2 釋放,造成釋放重複記憶體

應該改成下列這種寫法,直接在 shared_ptr 建構子裡 new Object,而避免用原始指標變數去建立 shared_ptr,

1
2
3
{
std::shared_ptr<Object> sp(new Object());
} // sp 釋放

範例4. 存取 std::shared_ptr 的原始指標

透過std::shared_ptr.get()可以取得原始指標,大概有兩種情況會使用到,一種是需要呼叫傳統的api,會需要傳遞原始指標,另一種是直接用原始指標,範例如下:

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
37
38
// g++ std-shared-ptr.cpp -o a.out -std=c++11
#include <iostream>
#include <string>
#include <memory>

class LargeObject {
public:
void DoSomething() {
printf("DoSomething\n");
}
};

void LegacyLargeObjectFunction(LargeObject *lo) {
printf("LegacyLargeObjectFunction\n");
lo->DoSomething();
}

int main() {
printf("===1===\n");
// Create the object and pass it to a smart pointer
std::shared_ptr<LargeObject> pLarge(new LargeObject());

printf("===2===\n");
// Call a method on the object
pLarge->DoSomething();

printf("===3===\n");
// 傳遞原始指標給 legacy API
LegacyLargeObjectFunction(pLarge.get());

printf("===4===\n");
// 用原始指標去接
LargeObject *p = pLarge.get();
LegacyLargeObjectFunction(p);

printf("===5===\n");
return 0;
}

輸出如下

1
2
3
4
5
6
7
8
9
10
===1===
===2===
DoSomething
===3===
LegacyLargeObjectFunction
DoSomething
===4===
LegacyLargeObjectFunction
DoSomething
===5===

ShengYu 探索實驗1. 生命週期

看看生命週期以及印出這兩種的記憶體位置

std-shared-ptr-exp1.cpp
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
37
38
39
40
41
// g++ std-shared-ptr-exp1.cpp -o a.out -std=c++11
#include <iostream>
#include <string>
#include <memory>

class LargeObject {
public:
LargeObject() {
printf("LargeObject::LargeObject()\n");
}

~LargeObject() {
printf("LargeObject::~LargeObject()\n");
}

void DoSomething() {
printf("DoSomething, x=%d\n", x);
}

int x = 10;
};

int main() {
printf("===1===\n");
LargeObject *p = new LargeObject();
p->DoSomething();
printf("%p\n", p);

printf("===2===\n");
std::shared_ptr<LargeObject> pLarge(p);
pLarge->DoSomething();
printf("%p\n", pLarge.get());
printf("%p\n", p);

printf("===3===\n");

//delete p; // double free or corruption

printf("===4===\n");
return 0;
}

輸出如下

1
2
3
4
5
6
7
8
9
10
11
===1===
LargeObject::LargeObject()
DoSomething, x=10
0x1524030
===2===
DoSomething, x=10
0x1524030
0x1524030
===3===
===4===
LargeObject::~LargeObject()

範例5. 判斷兩個 shared_ptr 是不是都是指到同一個物件

1
2
3
4
5
6
7
auto song1 = new Song("Village People", "YMCA");
auto song2 = new Song("Village People", "YMCA");
shared_ptr<Song> p1(song1);
shared_ptr<Song> p2(song2);
shared_ptr<Song> p3(p2);
cout << "p1 == p2 = " << std::boolalpha << (p1 == p2) << endl;
cout << "p3 == p2 = " << std::boolalpha << (p3 == p2) << endl;

unique_ptr 轉 shared_ptr

shared_ptr 轉換成 unique_ptr 是不允許的,但是可以反過來 unique_ptr 轉成 shared_ptr,
unique_ptr 轉 shared_ptr 有兩種方式,一種是

1
shared_ptr<Point> p = make_unique<Point>();

另一種是使用 move,

1
2
unique_ptr<Point> p1 = make_unique<Point>();
shared_ptr<Point> p2 = move(p1);

下一篇將會介紹 std::unique_ptr 用法

參考
[1] std::shared_ptr - cppreference.com
https://en.cppreference.com/w/cpp/memory/shared_ptr
[2] How to: Create and Use shared_ptr instances
https://docs.microsoft.com/zh-tw/cpp/cpp/how-to-create-and-use-shared-ptr-instances?view=vs-2019
圖表一與圖表二把 “參考計數器 ref count” 解釋地很好
[3] 智慧型指標 (新式 C++)
https://docs.microsoft.com/zh-tw/cpp/cpp/smart-pointers-modern-cpp?view=vs-2019

其它相關文章推薦
C/C++ 新手入門教學懶人包
std::unique_ptr 用法與範例
std::thread 用法與範例
std::deque 用法與範例
std::find 用法與範例
std::mutex 用法與範例
std::unordered_map 用法與範例
std::sort 用法與範例
std::random_shuffle 產生不重複的隨機亂數
std::async 用法與範例

std::atomic_flag 用法與範例

本篇介紹 C++ 的 std::atomic_flag 用法,並提供一些範例。

atomic_flag 一個原子 bool 類型,只支援兩種函式呼叫,test-and-set 和 clear。std::atomic_flag 它保證是 lock-free 無鎖的。atomic_flag 跟 std::atomic 不同的地方在於 std::atomic_flag 不提供讀取與儲存的操作。

std::atomic_flag 請使用 ATOMIC_FLAG_INIT 定義來進行初始化,這樣可以保證 std::atomic_flag 處於 clear 的狀態。

std::atomic_flag::test_and_set() 介紹

std::atomic_flag::test_and_set() 會原子地設定 flag 的旗標為 true,並回傳 flag 之前的設定值。

std::atomic_flag::clear() 介紹

std::atomic_flag::clear() 會原子地清除 flag 旗標,設定旗標為 false,這樣會使得下一次呼叫 std::atomic_flag::test_and_set 回傳 false。

使用範例

以下範例是開3個執行緒,內部迴圈各跑15次,每次都試圖去索取 lock(atomic_flag),如果索取到的話印出該執行緒號碼與cnt第幾次,
如果沒有索取到的話,進入自旋狀態,一直 while 迴圈嘗試索取到 lock 成功為止,
test_and_set 上鎖成功會回傳 false
test_and_set 上鎖失敗會回傳 true

std-atomic_flag.cpp
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
// g++ std-atomic_flag.cpp -o a.out -std=c++11 -pthread
#include <thread>
#include <vector>
#include <iostream>
#include <atomic>

std::atomic_flag lock = ATOMIC_FLAG_INIT;

void f(int n)
{
for (int cnt = 0; cnt < 15; ++cnt) {
while (lock.test_and_set(std::memory_order_acquire)) // acquire lock
; // spin 自旋
std::cout << "Output from thread " << n << ", cnt = " << cnt << std::endl;
lock.clear(std::memory_order_release); // release lock
}
}

int main()
{
std::vector<std::thread> v;
for (int n = 0; n < 3; ++n) {
v.emplace_back(f, n);
}
for (auto& t : v) {
t.join();
}

return 0;
}

輸出如下,每次執行結果不同:

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
37
38
39
40
41
42
43
44
45
Output from thread 0, cnt = 0
Output from thread 2, cnt = 0
Output from thread 0, cnt = 1
Output from thread 1, cnt = 0
Output from thread 2, cnt = 1
Output from thread 1, cnt = 1
Output from thread 2, cnt = 2
Output from thread 1, cnt = 2
Output from thread 2, cnt = 3
Output from thread 1, cnt = 3
Output from thread 2, cnt = 4
Output from thread 0, cnt = 2
Output from thread 1, cnt = 4
Output from thread 2, cnt = 5
Output from thread 0, cnt = 3
Output from thread 1, cnt = 5
Output from thread 2, cnt = 6
Output from thread 0, cnt = 4
Output from thread 2, cnt = 7
Output from thread 1, cnt = 6
Output from thread 0, cnt = 5
Output from thread 2, cnt = 8
Output from thread 0, cnt = 6
Output from thread 1, cnt = 7
Output from thread 2, cnt = 9
Output from thread 0, cnt = 7
Output from thread 2, cnt = 10
Output from thread 1, cnt = 8
Output from thread 2, cnt = 11
Output from thread 0, cnt = 8
Output from thread 2, cnt = 12
Output from thread 1, cnt = 9
Output from thread 2, cnt = 13
Output from thread 1, cnt = 10
Output from thread 2, cnt = 14
Output from thread 0, cnt = 9
Output from thread 1, cnt = 11
Output from thread 0, cnt = 10
Output from thread 1, cnt = 12
Output from thread 0, cnt = 11
Output from thread 1, cnt = 13
Output from thread 0, cnt = 12
Output from thread 1, cnt = 14
Output from thread 0, cnt = 13
Output from thread 0, cnt = 14

參考
std::atomic_flag - cppreference.com
https://en.cppreference.com/w/cpp/atomic/atomic_flag
C++11 並發指南六(atomic 類型詳解一 atomic_flag 介紹) - Haippy - 博客園
https://www.cnblogs.com/haippy/p/3252056.html
C++11中的atomic | YuanguoBlog
http://www.yuanguohuo.com/2019/07/08/cpp11-atomic/
並行編程中的lock free技術
https://blog.csdn.net/pmt123456/article/details/72844029

其它相關文章推薦
C/C++ 新手入門教學懶人包
std::thread 用法與範例
std::deque 用法與範例
std::find 用法與範例
std::mutex 用法與範例
std::unordered_map 用法與範例
std::sort 用法與範例
std::random_shuffle 產生不重複的隨機亂數
std::shared_ptr 用法與範例
std::async 用法與範例

Python if else elif 條件判斷用法與範例

本篇 ShengYu 將介紹如何使用 Python if 條件判斷用法與範例,以下教學將介紹 python if else 單一條件判斷用法與 if elif else 多重條件判斷的用法,並提供一些範例。

以下內容分為這個部份,

  • Python if else 單一條件判斷
  • Python if elif else 多重條件判斷
  • Python if 條件式範例練習

Python if else 單一條件判斷

跟 C 和 Java 語言不同,Python 判斷式都不用加括號,判斷式後也不用用大括弧包起,而是用冒號跟縮排決定。

Python if 用法範例如下,如果 if 條件判斷判斷結果為 True 則執行程式碼區塊,如果判斷結果為 False 則不執行程式碼區塊A,接著離開條件判斷繼續執行程式碼區塊B,

1
2
3
if 條件判斷:
#程式碼區塊A
#程式碼區塊B

Python 使用內縮方式來區分 if 語句的程式碼區塊,你可以使用 tab 或者 4 個空白字元來內縮,告訴 Python 直譯器這是 if 語句的程式碼區塊,同一份 Python 原始碼裡 tab 與 4 個空白字元請二擇一統一使用,不要一下用 tab 一下用 4 個空白字元。

Python if else 用法範例如下,如果 if 條件判斷判斷結果為 True 則執行程式碼區塊A,如果判斷結果為 False 則執行程式碼區塊B,接著離開條件判斷繼續執行程式碼區塊C,

1
2
3
4
5
if 條件判斷:
#程式碼區塊A
else:
#程式碼區塊B
#程式碼區塊C

數字比對

1
2
3
num = 1
if num == 1:
print('num is 1')

字串比對

1
2
3
4
5
string = 'ok'
if string == 'ok':
print('match')
else:
print('not match')

Python if elif else 多重條件判斷

Python if elif else 用法範例如下,如果 if 條件判斷1判斷結果為 True 則執行程式碼區塊A,接著離開條件判斷繼續執行程式碼區塊D,
否則執行 elif 條件判斷2判斷結果為 True 則執行程式碼區塊B,接著離開條件判斷繼續執行程式碼區塊D,
否則 else 執行程式碼區塊C,接著離開條件判斷繼續執行程式碼區塊D,
這邊的 elif 條件判斷是可以一直擴充的,依照實際程式的需求自行發揮,

1
2
3
4
5
6
7
if 條件判斷1:
#程式碼區塊A
elif 條件判斷2:
#程式碼區塊B
else:
#程式碼區塊C
#程式碼區塊D

數字比對的多重條件

1
2
3
4
5
6
7
grade = 60
if grade >= 90:
print('great')
elif grade >= 60:
print('good')
else:
print('bad')

Python if 條件式範例練習

綜合上述的介紹,以下為 Python if 條件式完整範例,

python-if.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

num = 1
if num == 1:
print('num is 1')

string = 'ok'
if string == 'ok':
print('match')
else:
print('not match')

grade = 60
if grade >= 90:
print('great')
elif grade >= 60:
print('good')
else:
print('bad')

輸出如下

1
2
3
num is 1
match
good

下一篇將介紹 for 迴圈的用法

以上就是 Python if else elif 條件判斷用法與範例介紹,
如果你覺得我的文章寫得不錯、對你有幫助的話記得 Facebook 按讚支持一下!

其它相關文章推薦
如果你想學習 Python 相關技術,可以參考看看下面的文章,
Python 新手入門教學懶人包
Python 取得鍵盤輸入 input
Python for 迴圈
Python str 字串用法與範例
Python list 串列用法與範例
Python set 集合用法與範例
Python dict 字典用法與範例
Python tuple 元組用法與範例
Python sort 排序
Python 建立多執行緒 thread
Python 讀檔,讀取 txt 文字檔
Python PIL 讀取圖片並顯示
Python OpenCV resize 圖片縮放