1. 在單晶片上運行電腦視覺應用
在電腦視覺中,影像辨識所需耗費的算力是非常龐大的,在加速處理的思路上可分為從硬體及軟體(算法、模型)下手,大致可分為下列幾種作法:
- 降低影像色彩數及解析度
- 降低每秒處理影格數(Frames, FPS)
- 增加晶片工作時脈(Clock, MHz)
- 增加單位時間平行運算能力(如SIMD, Vector指令集、多核)
- 選擇複雜度(如層數、寬度、網路結構)較低的模型
- 對模型進行推論速度、儲存空間、記憶體使用優化(量化、減枝、壓縮、蒸餾等)
在前一篇「MCU攜手NPU讓tinyML邁向新里程碑」中已有幫大家介紹過各種MCU+NPU的硬體加速計算方式,但由於MCU儲存程式碼、模型網路結構、權重參數的FLASH容量及計算過程所需的SRAM記憶體容量非常少,所以第一件事是要把模型縮到塞得進MCU中,才有資格談模型加速運算及維持推論精度,尤其在電腦視覺應用中更是首先必須要解決的問題。
傳統AIoT的應用開發,MCU重點在擷取多種感測器信號及加上通訊將資料傳到雲端,並不直接在MCU上進行AI相關運算,所以工作時脈不用太快,只要數十MHz就足夠,而Flash、SRAM有個數KB到數十KB就夠用了,甚至連像ARM MBED或RTOS這樣的MCU作業系統都不用。
隨著微型攝影機模組(Camera Module)的普及,越來越多的有線、無線網路攝影機(IP Cam)方案被提出,AIoT的應用也開始加入了影像監控的領域。為解決影像基本處理及編解碼,因此單晶片也開始提高工作時脈到數百MHz、增加Flash、SRAM到數百KB到數MB,甚至加入數位信號處理器(Digital Signal Processor, DSP),使得有足夠能力可以運作微型作業系統或MicroPython這類的開發環境。不過此時若想在擷取到的串流影音上進行AI相關應用時,幾乎都還是送回雲端處理。
最近隨著各種微型攝影機模組(如I/O Bus, MIPI, SPI界面)越來越容易取得,支援TinyML技術的硬體逐漸成熟,相關開發工具(如TensorFlow Lite for Microcontroller, CMSIS-NN)和平台(如Edge Impulse, SensiML)也逐漸普及,因此在沒有網路及雲端服務下,在MCU上直接跑微型電腦視覺AI應用也開始變得沒這麼困難。接下來就市售20款開發板分成三大類依序幫大家介紹,而開發工具、平台、模型優化等就留待下次再幫大家介紹。
2. Arm Based TinyML開發板
大家都知道Arm Cortex-M系列是目前市面上使用最廣的MCU內核了。它的硬體跨度非常大,隨著搭配的指令集可提供各種不同的加速運算能力,以下就簡單介紹一下。更完整的指令集介紹可參考「WIKI Pedia-ARM Cortex-M」。
- Cortex-M3(2004)
ARMv7-M (Thumb1,Thumb2) - Cortex-M4(2010)
ARMv7E-M (Thumb1,Thumb2,DSP,VFPv4-SP) - Cortex-M0+(2012)
ARMv6-M (Thumb1) - Cortex-M7(2014)
ARMv7E-M (Thumb1,Thumb2,DSP,VFPv5) - Cortex-M33(2016)
ARMv8-M (Thumb1,Thumb2,DSP,VFPv5,TrustZone) - Cortex-M35P(2018)
ARMv8-M (Thumb1,Thumb2,DSP,VFPv5,TrustZone) - Cortex-M55(2020)
ARMv8.1-M (Thumb1,Thumb2,DSP,VFPv5,TrustZone,MVE)
Arm v6指令集主要考量為低功耗、價格低為考量,所以工作速度不高,32bit硬體整數乘法也要32個週期才能完成。v7指令集讓32bit整數乘法可在1個週期完成,亦開始提供12週期的硬體整數除法及飽和運算。v7E指令集開始加入DSP指令及可選配的浮點數運算器(如Cortex-M4F),32bit乘法及乘積累加(MAC)在1個週期就能完成,同時開始支援單指令多資料(SIMD)運算,讓32bit MAC可拆解成多個8/16bit的運算,加快運算速度,故TinyML應用開發板多半選用v7E(如Cortex-M4)以上指令集核心。最新的v8.1指令集提供了更高效的計算能力,包括在Cortex-A系列才有的Helium(M-Profile Vector Extension, MVE)指令集,同時對單/雙精度浮點數的計算也有更快的效能。
以下就以核心架構為分類,為大家一一介紹。
2.1 Cortex-M0+
目前市售Cortex-M0及M0+的開發板通常工作時脈只有32M ~ 64MHz,Flash及SRAM也只有數十到數百KB。2021年1月樹莓派基金會推出Raspberry Pi Pico開發板,主要晶片為RP2040,內建雙Cortex-M0+核心,且工作時脈一口氣拉高到133MHz,還配有2MB Flash, 264KB SRAM,並可運行MicroPython。
2.1.1 Arducam Pico4ML
Arducam看上Pi Pico能力,首先推出Pico4ML,不僅搭載QVGA解析度(320x240)的攝影機,更配有一片0.96吋(160x80)的LCD,方便觀看拍攝的影像及顯示電腦視覺推論結果。另外板子上也有搭配麥克風(Mic)及運動感測器(IMU),方便開發智慧感測器應用。
完整介紹:https://www.arducam.com/pico4ml-an-rp2040-based-platform-for-tiny-machine-learning/
2.1.2 Arducam Pico4ML-BLE
Pico4ML-BLE為Pico4ML的升級版,加入U6161 BLE 5.0通訊模組,其餘部份不變。
完整介紹:https://www.arducam.com/arducam-pico4ml-ble-version/
2.2 Cortex-M4
2.2.1 Arduino Nano 33 BLE Sense
目前想要開始玩TinyML應用,通常都會推薦大家使用Arduino Nano 33 BLE Sense這塊開發板,它的主要晶片為Nordic nRF52840 (Cortex-M4 @64MHz),搭配有1MB Flash及256KB SRAM,並支援DSP(SIMD)加速運算指令集及安裝Arm MBED作業系統,有利運行小型卷積神經網路(CNN)模型。另外開發板上面搭載了各種感測器,包括麥克風、九軸運動(IMU)、手勢近接光照、氣壓、溫濕度等感測器,很方便做為智慧感測器應用開發。不過由於它沒有搭載攝影機模組,因此Arduino幫它出了一組Tiny Machine Learning Kit,透過擴充底板將開發板上的IO腳位和OV7675攝影機模組(640x480像素)連接在一起,省去手工接線的麻煩,方便完成電腦視覺相關應用。更完整規格如下所示。
開發板完整介紹:https://docs.arduino.cc/hardware/nano-33-ble-sense
教學套件完整介紹:https://store-usa.arduino.cc/products/arduino-tiny-machine-learning-kit
套件相關教學文:https://www.edgeimpulse.com/blog/announcing-support-for-the-arduino-tiny-machine-learning-kit
2.2.2 Sony Spresense
Sony Spresense主板(CXD5602PWBMAIN1)的主要晶片CXD5602為Sony自行開發,它內建6個Cortex-M4 @156MHz 核心,搭配有8MB Flash及1.5MB SRAM,非常適合多工運行不同應用甚至不同AI模型。板上留有專屬攝影機界面,透過軟排可連接兩款專用攝影機,分別是CXD5602PWBCAM1 2608x1960(5M)及CXD5602PWBCAM2W 1280x960(1.3M)。目前這塊開發板並沒有加上任何感測器,如果需要可透過擴充底板及標準界面(DIO, AIO, I2C, SPI)進行連接。更完整規格如下所示。
完整介紹:https://developer.sony.com/develop/spresense/
2.3 Cortex-M7
Arm Cortex-M7由於算力較高,所以很早就被OpenMV這家公司拿去開發小型視覺應用產品,不過當時並沒有TinyML相關應用,因此該公司自行開發一些簡易視覺函式庫(類似OpenCV)給開發者使用,近年來陸續推出新產品也開始支援一些TinyML開發平台。另外為了讓MCU有更高效能的表現同時兼具更低功耗表現,因此近年來推出的MCU常會整合兩種核心Cortex-M7+Cortex-M4,而這類晶片自然也成為小型電腦視覺AI應用的首選。因此Arduino也採用此類晶片推出了兩個系列的高階開發板Protenta H7及Nicla系列。
2.3.1 OpenMV Cam H7 / H7 R2 / H7 Plus
OpenMV目前三款產品都是使用的單核心Cortex-M7,工作時脈480MHz,搭配2MB Flash, 1MB SRAM。H7 / H7 R2使用VGA(640x480)解析度的攝影機,而H7 Plus則採用5MP(2592x1944)攝影機。另外H7 Plus加上外加32MB Flash及32MB SDRAM,讓影像處理記憶體空間更多,可以適用更多的電腦視覺AI模型。
Cam H7完整介紹: https://openmv.io/products/openmv-cam-h7
Cam H7 R2完整介紹: https://openmv.io/products/openmv-cam-h7-r2
Cam H7 Plus完整介紹: https://openmv.io/products/openmv-cam-h7-plus
2.3.2 Arduino Protenta H7 + Vision Shield
一般Arduino的開發板多半是很基礎的MCU開發板,近年來他們也開始推高階應用(PRO系列)的開發板,像是2.2.1節提及的Nano 33 BLE Sense就是其中一種,而Portenta家族就是最高階的開發板。Portenta H7開發板使用STM32H747XI作為主晶片,內含兩個核心,一組Cortex-M7 480MHz,另一組Cortex-M4 240MHz,搭配 2MB Flash及1MB SRAM。板上另外擴充有16MB Flash及8MB SDRAM。雖然這塊板子有很強的MCU,但並沒有搭配任何感測器,因此影音部份就要搭配另一塊Vision Shield的板子,這塊板子有搭配Himax HM-01B0 QVGA解析度(320x240)攝影機模組及二組麥克風。這塊擴充板可依連網需求,可選擇有線(Ethernet)或無線(LoRa)模組。
Portenta H7完整介紹: https://www.arduino.cc/pro/hardware-product-portenta-h7
Vision Shield完整介紹: https://www.arduino.cc/pro/hardware-product-portenta-vision-shield
2.3.3 Arduino Nicla Vision
Arduino Nicla系列也是屬於PRO系列,它有兩款產品,Nicla ME用於一般物聯網智慧感測器,而Nicla Vision則有帶攝影機模組可用於電腦視覺應用。其主要使用STM32H747AII6,內含兩個核心,一組Cortex-M7 480MHz,另一組Cortex-M4 240MHz,搭配 2MB Flash及1MB SRAM。另外於板上擴充有16MB的Flash。在感測器方面,除了攝影機模組GC2145(1616x1232)外,還有搭配6軸運動(IMU)、飛時測距(ToF)及麥克風等感測器。在通訊方面也支援WiFi和BLE4.2。
完整介紹:https://www.arduino.cc/pro/hardware-product-nicla-vision
2.4 Cortex-M33/M35P
Arm Cortex-M33/M35P是最先導入ARMv8-M指令集的MCU,還開始支援TrustZone,讓MCU程式有更好的保護性。由於支援VFP5.0指令集,所以在浮點數運算上效能更好。在硬體除法上僅需211個週期,較Cortex-M7需320個週期來的快。目前M33已有相關產品上市,M35屬M33的進階版但目前仍較少見。
2.4.1 Silicon Labs xG24 Dev Kit
這塊開發板使用Silicon Labs EFR32MG24B310F1536IM48,其核心為Cortex-M33,工作時脈78MHz,搭配有1536KB Flash及256KB SRAM。另外開發板上還有溫濕度、壓力、運動、環境光、霍爾(磁場)及麥克風等感測器,但沒有攝影機模組,因此須另搭配Arducam Mini 2MP Plus SPI界面之攝影機模組,才能運行電腦視覺相關應用。
開發板完整介紹:https://www.silabs.com/development-tools/wireless/efr32xg24-dev-kit
攝影機完整介紹:https://www.arducam.com/product/arducam-2mp-spi-camera-b0067-arduino/
連接攝影機教學:https://docs.edgeimpulse.com/docs/tutorials/hardware-specific-tutorials/object-detection-xg24-devkit
2.4.2 Realtek AMB82-MINI (Ameba Pro 2)
瑞昱(Realtek)這款AMB82-MINI(又稱Ameba Pro 2)開發板採用自家最新的RTL8735B晶片,主要核心技術並未公開,但有提及是採用32bit v8M指令集,工作時脈500MHz,因此猜測為Cortex-M33或M35P內核。當然也有可能是v8.1M指令集的Cortex-M55+Ethos-U55,因為它還有提供一組具有0.4TOPS算力的NPU。該晶片除內建的768KB Flash和512KB的SRAM外,另外掛64MB Flash及128MB SDRAM來輔助儲存擷取到的影像和運算AI模型所需的參數及記憶體。目前板子上只有一組電容式麥克風,若想接入其它感測器信號,則需使用標準界面(如I2C, SPI, AIO, DIO)連接。另外這顆晶片本身就有支援Ethernet, WiFi, BLE 4.2及Audio, H.264, H.265 Codec,所以非常適合用於具電腦視覺功能的邊緣智能網路攝影機。現僅能支援原廠提供的SDK來轉換模型,不支援Edge Impulse這類雲端TinyML開發平台。
完整介紹:https://www.amebaiot.com/zh/amebapro2/#rtk_amb82_mini
2.5 Cortex-M55
Cortex-M55是目前已上市MCU產品中效能最高的,支援v8.1M指令集,對於浮點運算及平行、向量加速運算亦有更好的表現。若再搭配NPU Ethos-U55則算力會大幅提升,更適合用於電腦視覺相關應用。
2.5.1 Alif Ensemble E7 Development Kit
Cortex-M55及Ethos-U55 IP於2020年初就被推出,但受疫情影響一直遲遲未看到相關實體晶片上市。2022年10月才看到ALIF將相關開發板推廣上市。目前ALIF Ensemble系列共有四個產品E7, E5, E3, E1,分別使用不同數量的MPU(Cortex-A53)、MCU(Cortex-M55)、NPU(Ethos-U55)組合而成。其中E7為最高性能,有兩組MPU、兩組MCU及兩組NPU,內建5.5MB MRAM(類似Flash屬不可揮發記憶體)及13.5MB SRAM。為方便後續開發者開發產品及替換,故將其設計成SOM形式模組板及一組擴充底板。現有推出的E7開發板及擴充底板上並沒有任何感測器,但有保留MIPI界面未來可連接攝影機模組。依官方測試使用一組M55加一組U55在影像分類上,推論速度較前一代M7快700倍,亦比單純只使用M55快了78倍,由此可知未來此類晶片非常適合用於小型電腦視覺AI應用。
晶片及開發板完整介紹:https://alifsemi.com/ensemble/
開發板使用教學介紹:https://docs.edgeimpulse.com/docs/development-platforms/officially-supported-mcu-targets/alif-ensemble-e7
3. RISC-V Based TinyML開發板
MCU除了Arm Cortex-M系列外,近年來RISC-V指令集的MCU也直起直追,若再加上向量(V)及SIMD§擴充指令集,則晶片算力大幅提升,亦可作為神經網路加速計算用。部份廠商亦會採取多核心或者搭配NPU的作法來提升算力。
3.1 Sipeed MAIX Bit
矽遞(Sipeed)推出的MAIX Bit是採用嘉楠(Canaan)的堪智(Kendryte)K210晶片,內含二組64bit RISC-V,工作時脈400MHz,搭配16MB Flash和8MB SRAM,另外自帶NPU提供1TOPS的算力。目前開發板上除OV2640(1600x1200)攝影機模組外,及保留MIPI LCD界面外,並未提供其它感測器。板子預裝FreeRTOS作業系統,能支援的AI開發框架較有限,僅TensorFlow, Keras, Darknet(YOLOv2),開發上較依賴傳統SDK,而非常用的TensorFlow Lite Micro或Edge Impulse TinyML開發平台。
開發板完整介紹:https://wiki.sipeed.com/soft/maixpy/zh/develop_kit_board/maix_bit.html
晶片完整介紹:https://canaan-creative.com/product/kendryteai
4. DSP Based TinyML開發板
早期MCU計算能力較弱,當遇到像影音處理中的快速傅立葉轉換(FFT)時,幾乎無法作到即時運算,因此出現專用的數位信號處理器(Digital Signal Processor, DSP)。DSP主要提供了「乘法累加(MAC)」指令處理像「D=AxB+C」這類的運算,還有提供浮點數四則運算能力,當然也包括提供多組可平行計算的MAC(如超長指令集VLIW)及飽和運算。在Arm Cortex-M3出現後,開始有支援一週期整數的乘法指令及一組多週期的乘加指令。到了Cortex-M4開始支援SIMD運算(俗稱DSP指令集),可大幅增加計算速度,從此開始有一些應用就從DSP轉到高階MCU上。不過由於Cortex-M4/M7能提供的平行乘加數量還是不多,所以仍有部份DSP採用高平行度乘加來提供影音處理使用,包括一些小型電腦視覺AI應用。
4.1 Espressif ESP32
ESP32主要採用了Tensilica Xtensa架構的DPU,它是一種基於DSP的內核,提供了很多DSP相關指令集及硬體,如硬體乘法器、除法器、乘加器(MAC)、單精度/雙精度浮點數加速、HiFi Audio Engine、IVPEP(SIMD) Image Engine、ConnX DSP engines、BBE DSP engines、FLIX(即超長指令字Very Long Instruction Word, VLIW)等。另外也提供豐富的通訊界面,如WiFi, BLE 4.2, Ethernet,很適合用於IP Cam,並可加入少量電腦視覺運算(如人臉辨識),現有多款市售產品皆採用這個解決方案。
4.1.1 Espressif ESP-EYE
ESP-EYE是基於ESP32晶片的開發板,內含兩組Xtensa LX6核心,當時脈運行在240MHz時可提供994.26 CoreMark(4.14 CoreMark/MHz)的算力,除內置448K Flash和536KB SRAM,另有外置4MB Flash及8MB PSRAM。另外板上有提供一組電容式麥克風方便作為IP Cam或邊緣智慧影音AI應用。
開發板完整介紹:https://www.espressif.com/zh-hans/products/devkits/esp-eye/overview
晶片完整介紹:https://www.espressif.com/zh-hans/products/socs/esp32
4.1.2 Ai-thinker ESP32-CAM
安信可(AI-Thinker)的ESP32-CAM,也是依此ESP-EYE(ESP32)架構製作,可透過官方SDK開發電腦視覺AI相關應用。目前部份雲端TinyML開發平台只支援ESP-EYE而不支援ESP32-CAM,可參考第5章支援清單。
開發板完整介紹:http://www.ai-thinker.com/pro_view-24.html
開發文件完整介紹:https://docs.ai-thinker.com/esp32-cam
4.1.3 LILYGO T-Camera S3
這款產品採用ESP32-S3作為主晶片,其效能較ESP32高出許多,內含兩組Xtensa LX7核心,當時脈運行在240MHz時可提供1181.6 CoreMark(4.92 CoreMark/MHz)的算力,除內置384K Flash和512KB SRAM,另有外置16MB Flash及8MB PSRAM,並具有WiFi, BLE 5.0等通訊能力。除此之外,內部還有一組超低功耗(Ultra-Low-Power, ULP)的RISC-V MCU及一組有限狀態機(Finite State Machine, FSM)共同處理器。開發板上搭配有OV2640攝影機模組(1600x1200)、麥克風、PIR感測器及SSD1306 0.96吋OLED,方便電腦視覺AI應用開發。由於目前剛推出,相關TinyML平台都尚未支援,只能採用較傳統SDK方式開發AI應用。
另外Lilygo先前亦有推出類似ESP-EYE產品,TTGO T-Camera Plus,TTGO T-Journal ESP32 Camera Module,可自行參考其官網介紹。
4.2 Eta Compute ECM3532 AI Vision
ECM3532內核採用Arm Cortex-M3 MCU及NXP CoolFlux DSP16 DSP,而主要加速計算還是由DSP負責,故歸類於此。其內部搭配有512KB Flash和352KB SRAM,外置8MB Flash。其DSP共有兩組16x16的MAC,工作時脈在100MHz,約有800MOPS算力。晶片內部亦有配置BLE 4.2電路,方便通訊。板上除Himax HM0360攝影機模組(640x480),另外有環境光(ALS)、麥克風(Mic)及運動(IMU)等感測器,方便開發智慧感測器應用。由於此晶片算力不高,所以在視覺應用上僅適合用於人臉、人員偵測等小型AI應用。
開發板完整介紹:https://media.digikey.com/pdf/Data Sheets/Eta Compute PDFs/ECM3532-AI-Vision-Product-Brief-1.0.pdf
晶片完整介紹:https://en.wikichip.org/wiki/eta_compute/ecm353x/ecm3532
4.3 Himax HX6537-A
台灣奇景光電(Himax)為世界知名影像感測器供應商,近年來也積極投入邊緣智慧電腦視覺晶片開發,其中HX6537-A更被多家廠商採用,除自家的WE-I Plus EVB開發板外,另外像Seeed Studio及TinyML知名大神Pete Warden創立的Useful Sensors也都有開發出實用的開發板。
HX6537-A晶片,其核心為Synopsys ARC EM9D DSP,工作時脈為400MHz,內含2MB Flash和2MB SRAM。EM9D是基於ARCv2DSP指令集架構,有超過100條DSP指令,由於具有32x32 MUL/MAC單元,且支持定點DSP數據類型和向量/SIMD(單指令多數據)操作,其運算速度大約有1.81 DMIPS/MHz和4.42 CoreMark/MHz,因此很適合作為矩陣加速運算及AI神經網路模型計算。
4.3.1 Himax WE-I Plus EVB
Himax WE-I Plus (I是羅馬數字的1) 開發板就是採用自家的HX6537-A晶片,板子上除了有一組自家的HM0360攝影機模組(640x480)外,還有兩組麥克風及一組運動感測器(IMU)。由於功耗很低,因此可用於持續開啟(Always On)的視覺應用。
開發板完整介紹:https://www.himax.com.tw/zh/products/intelligent-sensing/always-on-smart-sensing/developing-tools/
晶片完整介紹:https://www.himax.com.tw/zh/products/intelligent-sensing/always-on-smart-sensing/
4.3.2 Seeed Studio Grove Vision AI Module
Seeed Studio設計生產許多單晶片開發板及功能性擴充板(Grove),其中也有使用HX6537-A晶片開發了一款視覺智慧模組,大致上設計和WE-I Plus EVB類似,只是將攝影機模組升級到OV2640(1600x1200),並增加一組BLE 5.0通訊模型,讓使用上更加方便。
開發板完整介紹:https://www.seeedstudio.com/Grove-Vision-AI-Module-p-5457.html
教學文件完整介紹:https://wiki.seeedstudio.com/Grove-Vision-AI-Module/
4.3.3 UsefulSensors Person Sensor
Useful Sensors是TinyML知名大神Pete Warden於2022年底成立,其主要產品Person Sensor就是採用HX6537-A晶片,而其設計就是參考WE-I Plus而移除運動感測器並取消USB接頭,將其縮小至只有20mm x 20mm,只透過I2C來傳遞命令及結果,讓使用者不用擔心影像被外流。目前主要用於人臉、人員、手勢偵測等不需要太大算力的視覺應用。初步公開的數據表示檢測一次速度大約在200ms內,對於大部份室內應用勉強能滿足。
開發板完整介紹:https://usefulsensors.com/person-sensor/
教學文件完整介紹:https://github.com/usefulsensors/person_sensor_docs/blob/main/README.md
5. TinyML開發平台支援清單
目前常見的TinyML工具及開發平台可以支援的開發板清單可參考下列連結。
- Google TensorFlow Lite for Microcontrollers
https://www.tensorflow.org/lite/microcontrollers - Edge Impulse - MCU Target
https://docs.edgeimpulse.com/docs/development-platforms/officially-supported-mcu-targets - Edge Impulse - Community boards
https://docs.edgeimpulse.com/docs/development-platforms/community-boards - SensiML Toolkit Documentation - Supported Devices
https://sensiml.com/documentation/index.php
小結
本文簡單地介紹了各種可支援TinyML做為電腦視覺的開發板,包含基於Arm Cortex-M、RISC-V及DSP的解決方案,雖然目前已可使用MCU來開發微型電腦視覺AI應用,但還有很多使用及開發的限制未能突破,相信隨著硬體及算法的持續進步,不久的將來高效能、高隱私、低延遲、低成本的邊緣智能電腦視覺應用就能普及到日常生活中。
參考文獻
- 許哲豪,“MCU攜手NPU讓tinyML邁向新里程碑”
https://omnixri.blogspot.com/2022/10/mcunputinyml.html - WIKI Pedia, “ARM Cortex-M”
https://zh.m.wikipedia.org/zh-hant/ARM_Cortex-M
Very good article. Thanks.
回覆刪除