Computer VisionEP.10

OpenCV XY Vision Automation
智慧化視覺辨識與自動化操作系統

透過 OpenCV 視覺辨識與座標轉換,讓傳統依賴固定 XY 座標的自動化測試,
升級為可動態辨識 UI 的智慧化操作系統。

Joseph Chen

2024 - 2025
13 min read
智慧自動化

這篇文章要解決什麼問題

在消費性電子與手機出廠自動化測試中,軟體腳本通常必須透過實體機械手臂或 XY 軸點擊器 (Physical XY Clicker) 來模擬人手觸控螢幕。

過去,自動化點擊完全依賴「硬連結座標 (Hardcoded Coordinates)」—— 例如,命令手臂在 `X=450, Y=1200` 的像素位置進行點擊。這種作法看似直觀,卻存在極為脆弱的硬傷:一旦測試手機換了型號、螢幕解析度改變、APP UI 發生微小改版,或者是系統更新彈出了系統通知,原本的座標點擊就會直接落空。

工程師們不得不花費大量工作天重新校正數以千計的座標點。為了解決這個問題,我開發了一套基於 **OpenCV 電腦視覺辨識與空間座標映射** 的智慧導航系統。

我們要解決的核心問題是:如何利用相機鏡頭對手機螢幕進行即時畫面擷取,使用 OpenCV 自動找出 UI 按鈕的像素位置,並透過仿射與透視矩陣轉換,計算出機械手臂在實體物理空間的真實運動座標,實現免除手動校正的閉環自動化系統?


真實案例與 Joseph 的現場經驗

在工廠自動化測試的現場,最大的敵人往往不是代碼 Bug,而是實體環境的物理變化。

我曾遇過一個棘手案例:我們需要對一系列不同解析度(從 iPhone SE 到 Pro Max 尺寸)的手機進行 APP 點擊測試。由於固定座標在版面微調後會直接失效,起初團隊只能為每款手機寫一套專屬座標配置文件。更糟糕的是,因為相機架設在測試機台上方,容易受到現場日光燈反光的折射干擾,或者因為機台運轉震動導致相機镜头發生微小的角度偏斜。

💡 現場血淚經驗:演算法只是起點,環境魯棒性才是關鍵

我一開始也以為這不過是跑個 cv2.matchTemplate 罷了。但到了生產線當天就遭遇滑鐵盧:工廠上空的日光燈反光,導致按鈕在相機畫面中形成一片白斑,模板比對直接失敗。

後來,我對輸入的影像進行了動態直方圖等化(CLAHE)與 Canny 邊緣檢測,消除反光色差的影響;並設計了「三點動態物理校正法 (Three-point Physical Calibration)」:開局時讓機械手點擊螢幕上三個基準點,透過觸控事件取得手機內部座標,再比對相機拍到的像素座標,當場求解出 Perspective Matrix(透視變換矩陣)。這讓我們的點擊精準度達到了 ±0.5mm 以內。


核心概念解釋:影像處理與座標空間變換

這套自動化系統的背後是一套嚴謹的影像處理與數學變換閉環。以下是從相機輸入到機械臂點擊的視覺閉環流程圖:

OpenCV 智慧定位與實體映射 Pipeline

Step 01 / 圖像採集

Camera Grab

工業相機即時擷取測試手機畫面,轉換為 OpenCV 灰階圖,並使用高斯模糊去除鏡頭噪點。

Step 02 / 模板匹配

Template Matching

使用 cv2.matchTemplate 匹配目標按鈕邊緣,並以 MinMaxLoc 找出匹配係數大於 0.85 的像素中心點 (u, v)。

Step 03 / 座標映射與點擊

Coordinate Transform

藉由透視變換矩陣,將 (u, v) 映射為實體物理空間的機械手座標 (X, Y),隨後透過 Serial Port 發送點擊指令。

我們使用了三個核心影像技術來確保辨識率與穩定性:

  • 直方圖均衡化 (Equalization):特別是限制對比度自適應直方圖均衡化 (CLAHE),能大幅減少工廠局部陰影與燈光反光的影響。
  • 透視變換 (Perspective Transformation):由於相機架設難以保證絕對垂直,畫面通常存在傾斜。透視矩陣可校正梯形變形,將偏斜的像素面轉換為正投影實體面。
  • 匹配閾值卡點 (Confidence Thresholding):僅在相似度高於設定閾值時觸發,避免在背景紋理複雜時產生誤判點擊。

程式碼實戰:影像處理與實體點擊

以下是我們在專案中應用的核心代碼。第一段是傳統的 RGB 色值區間硬比對(容易因亮度稍微改變而完全抓不到點);第二段是 OpenCV 模板比對配合矩陣映射的完整實現。

錯誤的寫法:直覺的固定像素 RGB 比對(光影或按鈕微小變色即失效)

# ❌ 舊型自動化腳本片段:RGB 色值硬比對
import cv2
import numpy as np

def find_button_naive(screenshot_path):
    img = cv2.imread(screenshot_path)
    # 嘗試尋找畫面中接近特定藍色 (R:0, G:120, B:255) 的區域
    # 在工廠日光燈反光或變色主題下,色值稍微偏差 5 個單位就完全失效
    lower_blue = np.array([115, 50, 50])
    upper_blue = np.array([125, 255, 255])
    
    hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
    mask = cv2.inRange(hsv, lower_blue, upper_blue)
    
    # 尋找輪廓並點擊幾何中心
    contours, _ = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    if contours:
        largest = max(contours, key=cv2.contourArea)
        M = cv2.moments(largest)
        if M["m00"] != 0:
            cX = int(M["m10"] / M["m00"])
            cY = int(M["m01"] / M["m00"])
            return cX, cY # 返回像素坐標,沒有與手臂座標校準映射
    return None

改良後寫法:OpenCV 模板匹配與透視變換座標計算

# 🚀 現代化視覺定位與實體映射模組
import cv2
import numpy as np

class VisionLocator:
    def __init__(self, perspective_matrix_path=None):
        # 載入預先校正好的 3x3 透視變換矩陣
        if perspective_matrix_path and os.path.exists(perspective_matrix_path):
            self.M = np.load(perspective_matrix_path)
        else:
            # 預設單位矩陣 (不做轉換)
            self.M = np.eye(3, dtype=np.float32)

    def set_calibration(self, src_pts: np.ndarray, dst_pts: np.ndarray):
        """傳入三個/四個基準像素坐標(src)與對應手臂坐標(dst),動態計算轉換矩陣"""
        self.M, _ = cv2.findHomography(src_pts, dst_pts)

    def locate_and_convert(self, scene_img_path, template_img_path, threshold=0.85):
        # 1. 讀取並轉為灰階
        scene = cv2.imread(scene_img_path)
        template = cv2.imread(template_img_path)
        
        scene_gray = cv2.cvtColor(scene, cv2.COLOR_BGR2GRAY)
        template_gray = cv2.cvtColor(template, cv2.COLOR_BGR2GRAY)
        
        # 2. 進行限制對比度直方圖等化 (CLAHE) 以消除反光
        clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8, 8))
        scene_gray = clahe.apply(scene_gray)
        template_gray = clahe.apply(template_gray)

        # 3. 模板匹配
        w, h = template_gray.shape[::-1]
        res = cv2.matchTemplate(scene_gray, template_gray, cv2.TM_CCOEFF_NORMED)
        min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(res)

        # 判斷匹配度是否合規
        if max_val < threshold:
            print(f"⚠️ 辨識置信度不足: {max_val:.2f} < {threshold}")
            return None

        # 取得匹配中心點像素坐標 (u, v)
        pixel_x = max_loc[0] + w // 2
        pixel_y = max_loc[1] + h // 2
        
        # 4. 關鍵步驟:利用 Homography Matrix 將像素坐標轉換為物理空間坐標
        pixel_vector = np.array([pixel_x, pixel_y, 1.0], dtype=np.float32)
        physical_vector = np.dot(self.M, pixel_vector)
        
        # 歸一化齊次坐標
        physical_x = physical_vector[0] / physical_vector[2]
        physical_y = physical_vector[1] / physical_vector[2]
        
        return (pixel_x, pixel_y), (float(physical_x), float(physical_y))

if __name__ == "__main__":
    locator = VisionLocator()
    
    # 模擬動態校正過程:定義手機畫面上的三個角點像素坐標,以及對應實體點擊器的 XY 坐標 (mm)
    pixel_points = np.array([[100, 100], [900, 100], [500, 1800]], dtype=np.float32)
    physical_points = np.array([[10.0, 15.0], [90.0, 15.0], [50.0, 190.0]], dtype=np.float32)
    locator.set_calibration(pixel_points, physical_points)
    
    # 執行定位與映射
    # result = locator.locate_and_convert("scene.jpg", "confirm_btn.jpg")
    # if result:
    #     pixel_pos, robot_pos = result
    #     print(f"🎯 成功識別按鈕像素: {pixel_pos} -> 機械手實體座標: {robot_pos} mm")

視覺與點擊自動化方案比較

對於不同預算、速度與精度要求的生產線,常見的自動化定位方案如下:

定位方案優點缺點適合場景
硬編碼固定座標點擊執行速度極快(微秒級)、程式碼最簡單對版面與機台震動的容錯率為零、需要频繁重新校正治具與手機完全固定、UI 永不變更的封閉測試
OpenCV 傳統視覺辨識不需大算力顯卡、位置定位精準度高 (±0.5mm)、適應 UI 位移對環境光線敏感、影像形變過大或角度偏差時可能誤判APP 介面微調、機台存在微小震動、多解析度適配測試
深度學習目標檢測 (YOLO / SAM)對角度變形與反光的容錯率極高、能識別複雜不規則物件需高階 GPU 硬體、需要預先標註訓練資料、單次推論耗時較長非標準化實體包裝外觀檢查、多變性極高的動態 3D UI 辨識

常見錯誤與踩坑實錄

🔴

模板圖片與相機拍攝解析度不匹配

開發時,我們直接將 APP UI 設計圖上的按鈕切圖作為比對 Template。但現場相機拍到的按鈕,因為鏡頭物理對焦、距離遠近,其像素面積比原始設計圖縮小了將近 30%,導致比對相似度直接跌破 0.5。
正解: 必須在現場相機就定位後,實地擷取一張「真實拍到的」按鈕作為 Template,或在代碼中進行多尺度 (Multi-scale) 模板匹配。

🔴

誤把單純的縮放比例當成座標變換

一開始我們圖方便,只用簡易的比例公式轉換:`手臂X = 像素X * 比例`。但實際上,由於相機架設存在微小的傾斜角(Pitch & Roll),會產生遠小近大的透視投影形變。這導致螢幕中心的點擊很準,但邊角的點擊偏移了將近 3mm,弄壞了測試探針。
正解: 必須嚴格使用 `cv2.findHomography` 求解 3x3 透視變換矩陣,利用齊次矩陣乘法計算點擊位置。


實務操作建議

加裝相機專用物理遮光罩

演算法寫得再好,也比不上優質的物理光源輸入。在鏡頭外圍加裝 LED 環形光源與遮光罩,將反射干擾降低 90% 以上,是讓 OpenCV 相似度常年穩定在 0.95 以上的最省力方法。

開機自檢動態校正

機台運行時間久了難免有物理位移。建議在測試流程開頭,設計一段 30 秒的自檢程序:讓手臂點擊螢幕邊角的校正圓點,自動重新計算並更新 Homography Matrix,實現零人工維護成本。


與本系列其他文章的關聯

視覺自動化是智慧工廠大版圖的關鍵一環,你可以進一步了解以下相關技術:


總結

「固定座標」是自動化測試的初級階段,而「動態視覺導航」則是走向智慧工廠的必然選擇。

透過 OpenCV 模板匹配、CLAHE 反光消除以及 Homography 透視矩陣,我們成功為機械手臂裝上了「眼睛」,讓它能夠在 UI 變動與複雜的光影干擾中,精準無誤地完成點擊測試。這不僅消除了無謂的重複校正人力,更為接下來導入基於 AI 的異常檢測打下了穩固的底座。


延伸閱讀:更多 AI 技術分享

上一篇

EP.09 — Test Management System (TMS)

下一篇

更多精彩實戰,敬請期待...