【Mixing】在 MATLAB 中調用 Python 解析 QR code

  • 1053
  • 0
  • 2019-01-20

許多人總是拿著 MATLAB 和 Python 互相比較,誰好誰壞該用誰之類的。
在夏恩看來:好用的東西就該「合在一起用」,截長補短嘛!

首先來介紹一下夏恩的工作環境:

作業系統:Microsoft Windows 10 專業版
軟體版本:
MATLAB      Version 9.5      (R2018b)
Python         Version 3.5      (Based on anaconda)
  - pyzbar     Version 0.1.7   (Install from pip)

安裝 pyzbar 的參考網站

本章節中,最複雜的地方在於兩種環境的資料交換。
雖然有點繁瑣,若能耐著性子搞懂它,上手之後就很輕鬆囉~

用 Python 解析 QR code 

在 Python 中,有個解析 QR code 的套件非常好用:pyzbar
由於在 MATLAB 中找不到這個功能,餵狗之後也得到一些奇怪且艱澀的結果,
最後夏恩只好自己從 Python 挖套件過來用了。

這邊先不講混合編程,僅單純的看看在 Python 環境中該如何解析 QR code 就好。

# -*- coding: utf-8 -*-
"""
Created on Sat Jan 19 2019
@author: Shayne
"""

import numpy as np
import pyzbar.pyzbar as zbar

def detect(I): 
    # 偵測所有的 QR code
    barcodes = zbar.decode(I)
    
    # 逐一解碼,回傳位置與結果
    bbox = []; msg = [];
    for i, barcode in enumerate(barcodes):
        bbox.append(np.array(barcode.rect))
        msg.append(barcode.data.decode('utf-8'))

    return bbox, msg

根據上述程式碼,使用 zbar 偵測後,可以得到影像中所有 QR code 的位置。
此時的結果是被打包起來的 List 陣列,無法直接閱讀。

因此我們得在偵測之後使用迴圈,
逐一取得每個被偵測的 QR code,再調用 decode 語句還原結果。

看完解析函數,我們先載入一張測試影像(備註1),接著使用主程式來呼叫一次試試。

# -*- coding: utf-8 -*-
"""
Created on Sat Jan 19 2019
@author: Shayne
"""

import cv2
import numpy as np
import pyzbar.pyzbar as zbar
import matplotlib.pyplot as plt

def detect(I): 
    # 偵測所有的 QR code
    barcodes = zbar.decode(I)
    
    # 逐一解碼,回傳位置與結果
    bbox = []; msg = [];
    for i, barcode in enumerate(barcodes):
        bbox.append(np.array(barcode.rect))
        msg.append(barcode.data.decode('utf-8'))

    return bbox, msg


I = cv2.imread('qr_image.jpg')

plt.imshow(I)
plt.show()
bbox, msg = detect(I)
for rect in bbox:
    c, r, w, h = rect
    cv2.rectangle(I, (c, r), (c+w, r+h), (0, 255, 0), 5)

print(msg)

plt.imshow(I)
plt.show()
['https://dotblogs.com.tw/shaynling/', '夏恩的程式筆記']

看起來效果良好,正確地偵測到所有的 QR code。
若這部分沒有問題,那麼就是 MATALB 登場的時候了!

備註 1:測試圖片

設定 MATLAB 工作環境

目前 MATLAB 所支援的 Python 版本為:2.7 / 3.5 / 3.6 / 3.7。
在執行 MATLAB 的函數前,須先將 Python 安裝完成

接著,先在 MATLAB 的主視窗內輸入指令:pyversion
此時系統就會自行連接主機內的 Python,並回傳工作環境結果。

若要指定工作環境的話,在 pyversion 後面代入工作環境的路徑即可。
(請注意:若要再次切換須重新啟動 MATLAB。)

牛刀小試

環境設定完成後,咱們可以來試試看在 MATLAB 環境中使用一些 Python 語句。

1. 資料型態

請注意 py.list 只能輸入一維陣列,內容不限,想放什麼都可以;若輸入二維以上的陣列會報錯。

2. 陣列運算

若想不受限制地計算二維或高維陣列,可使用 py.numpy.array。

3. 讀取檔案

閒來無事也可以試試看使用 pandas 的讀檔功能。

若想使用什麼工作箱,就先在 Python 環境中安裝好就可以用。

但也不是毫無限制,有些工具箱會出現讀取失敗的問題,
在 anaconda 中,有許多工具箱是用 conda 來安裝而非使用 pip,
經過夏恩測試,使用 conda 安裝的套件更容易有調用失敗的問題,原因目前還沒有頭緒。

這部分請使用者多加留意。

主程式登場

使用 MATLAB 之前,夏恩先把剛才測試正常的 Python 程式存檔成 myfcn.py。
自行撰寫的函數與其它內建的函數的調用方法相同。

以本例來說,檔名加上函數名,就這樣呼叫:py.myfcn.detect。

若我們沒有特別指定輸入格式,則資料交換時默認使用 List 送過去。
而 Python 接收端會用 array.array 的格式來承接數據。

使用 List 有諸多限制,其中一項為「僅能傳送一維陣列」。
若要傳送高維陣列的話,必須先壓扁成一維陣列,當資料抵達 Python 時再還原回來。

用想的就覺得很不友善,更何況要動手實作?

最讓人傷腦筋的還有一點:

MATLAB 的陣列是 column-major;而 Python 預設則是 row-major。

在兩種系統之間「壓扁及還原」高維陣列是一件非常有挑戰性的事情。
(夏恩幹過這種蠢事,花了好幾個小時呢...)

最簡單的方法就是在 MATLAB 端直接把資料用 numpy.array 的格式包起來,
想怎麼傳就怎麼傳,一秒鐘就就搞定了!

請參考主程式如下:

% MATLAB 調用 Python 範例程式
%
% Shayne, 2019.01.19

% 讀檔
I = imread('qr_image.jpg');

% 轉換成 numpy.array 的型態
I1 = py.numpy.array(I);

% 呼叫 Python 函數
result = py.myfcn.detect(I1);
disp(result)

關於回傳值的部分,也值得單獨拿出來講,請先仔細看一下上圖結果。

在本範例中,回傳值有兩個:bbox 和 msg。
分別出現在 result(1) 和 result(2) 內。

此外,每個 result 都包含兩個辨識結果,那是因為有兩個 QR code。

在承接 Python 函數回傳值的時候,有幾個限制:

1. 不論回傳值有幾個,在 MATLAB 中只能寫一個。
2. 不論回傳值是什麼型態,統一規定為 tuple

當然,若沒有回傳值,就不用管這麼多了~

把握上述兩個要點,問題看起來就會簡單許多。
最難也就這樣,沒什麼好大驚小怪的。

在 MATLAB 中,解析 tulpe array 的方法須使用 cell() 來達成。
轉成 cell 後,再對應不同的欄位轉換成 double 或 char array。

解析 tulpe array 的程式碼如下:

% 解析結果
R = cell(result);

% 計算回傳值中包含多少資料,不能直接寫 length(R)
N = length(R{1});

bbox = zeros(N, 4);
msg  = cell(N, 1);

for i = 1:N
    
    % 使用 double() 轉換 array List 為 double
    bbox(i, :) = double( R{1}{i} );
    
    % 使用 char() 轉換 string List 為 char array
    msg{i} = char( R{2}{i} );
    
end

% 出圖
I2 = insertObjectAnnotation(I, 'rectangle', bbox, msg, ...
                               'Color', 'g', ...
                               'LineWidth', 5, ...
                               'TextBoxOpacity', 0.9, ...
                               'FontSize', 18 );
imshow(I2)

小結

使用 MATLAB 的優點是工具箱封裝完整,種類齊全,不會有套件衝突的問題。
逐行解析 debug 又快又好,說明文件詳細易懂。

而最常遇到的問題是:
一旦遇到系統沒有提供的工具箱,也找不到其他相關的資源,開發環境相對封閉。

最重要,也最讓人詬病的一點:MATLAB 很貴呀!!!
主程式 15 萬,各個工具箱 5~30 萬不等,每年還要維護費呢!(翻桌)
理所當然地,使用者人數少,開發資源自然也多不起來。

那 Python 剛好顛倒,由於全世界都在用,因此可以輕鬆找到各種套件。
有非常多的討論、非常多的社群可以幫助我們開發系統。

而缺點是人多嘴雜,以本次解析 QR code 來看,
相似的套件有:qrcode, pyqrcode, zbar, pyzbar, python-barcode...

相似功能有多種套件,該用哪一種?
每種套件可能都有相容性的問題,該怎麼解決?
歡迎來到 Python 的日常~

我們總是期待著可以擁有完美的開發工具,但那也許只存在於未來。
此時此刻,適當地結合多種開發工具來解決問題,才是更實際的方法。

踏入混編的門扉之後,那遠在天邊的地平線,似乎更加深邃了。